/** * test/worktrees.test.ts — parseWorktrees (pure parser for `git worktree list * --porcelain`). The git wrapper itself is covered via the projects-detail * integration test in projects.test.ts. */ import { describe, it, expect } from 'vitest' import { parseWorktrees } from '../src/http/worktrees.js' const MAIN = [ 'worktree /repo/main', 'HEAD 1234567890abcdef1234567890abcdef12345678', 'branch refs/heads/main', '', ].join('\n') describe('parseWorktrees', () => { it('parses a single main worktree on a branch', () => { const wts = parseWorktrees(MAIN, '/repo/main') expect(wts).toHaveLength(1) expect(wts[0]).toMatchObject({ path: '/repo/main', branch: 'main', head: '12345678', // first 8 of the sha isMain: true, isCurrent: true, }) }) it('marks only the first record as main; flags the current path', () => { const text = [ 'worktree /repo/main', 'HEAD aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'branch refs/heads/main', '', 'worktree /repo/wt/feature', 'HEAD bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 'branch refs/heads/feature/login', '', ].join('\n') const wts = parseWorktrees(text, '/repo/wt/feature') expect(wts).toHaveLength(2) expect(wts[0]).toMatchObject({ path: '/repo/main', branch: 'main', isMain: true, isCurrent: false }) expect(wts[1]).toMatchObject({ path: '/repo/wt/feature', branch: 'feature/login', // refs/heads/ stripped, slashes kept isMain: false, isCurrent: true, }) }) it('handles a detached HEAD (no branch)', () => { const text = ['worktree /repo/x', 'HEAD cccccccccccccccccccccccccccccccccccccccc', 'detached', ''].join('\n') const wts = parseWorktrees(text, '/other') expect(wts[0]?.branch).toBeUndefined() expect(wts[0]?.head).toBe('cccccccc') }) it('captures locked and prunable flags', () => { const text = [ 'worktree /repo/locked', 'HEAD dddddddddddddddddddddddddddddddddddddddd', 'branch refs/heads/wip', 'locked', 'prunable gitdir file points to non-existent location', '', ].join('\n') const wts = parseWorktrees(text, '/x') expect(wts[0]?.locked).toBe(true) expect(wts[0]?.prunable).toBe(true) }) it('parses the final record even without a trailing blank line', () => { const text = ['worktree /repo/main', 'HEAD eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', 'branch refs/heads/main'].join( '\n', ) expect(parseWorktrees(text, '/repo/main')).toHaveLength(1) }) it('returns [] for empty input', () => { expect(parseWorktrees('', '/x')).toEqual([]) }) })