/** * @vitest-environment node */ import { describe, expect, it } from 'vitest'; import { branchStatusClass, ghostBranchesByHop, mergeMermaidStyles, pickMergeCandidate, } from './pathTracerTimeline'; import type { PathTraceTimelineBranch } from './pathTracerTimeline'; function branch(partial: Partial): PathTraceTimelineBranch { return { id: 'b1', fork_hop_index: 0, status: 'running', is_ghost: true, ...partial, }; } describe('pathTracerTimeline', () => { it('groups ghost branches by fork hop', () => { const map = ghostBranchesByHop([ branch({ id: 'a', fork_hop_index: 1 }), branch({ id: 'b', fork_hop_index: 1 }), branch({ id: 'c', fork_hop_index: 0 }), ]); expect(map.get(1)?.map((b) => b.id)).toEqual(['a', 'b']); expect(map.get(0)?.map((b) => b.id)).toEqual(['c']); }); it('prefers won branch for merge candidate', () => { const pick = pickMergeCandidate([ branch({ id: 'run', status: 'running' }), branch({ id: 'win', status: 'won', mining_linked: true, hashrate: 120 }), ]); expect(pick?.id).toBe('win'); }); it('skips hospice strains for fork-merge parent selection', () => { const hospice = new Set(['#ab88e4']); const pick = pickMergeCandidate( [ branch({ id: 'hospice-win', status: 'won', spread_lanes: ['winrm'] }), branch({ id: 'ok-run', status: 'running', spread_lanes: ['docker'] }), ], hospice, ); expect(pick?.id).toBe('ok-run'); }); it('maps status to CSS class', () => { expect(branchStatusClass('won')).toBe('won'); expect(branchStatusClass('running')).toBe('running'); expect(branchStatusClass('failed')).toBe('failed'); }); it('appends mermaid classDef styles once', () => { const raw = 'graph TD\n a --> b'; const styled = mergeMermaidStyles(raw); expect(styled).toContain('classDef won'); expect(mergeMermaidStyles(styled)).toBe(styled); }); });