Files
orca/src/relay/git-handler-utils.test.ts
T
9aeaf0728f Fix git worktree list fallback for older Git (#7235)
* fix git worktree list fallback for localized errors

* Polish worktree -z fallback: clarify comment, add localized + negative tests

Co-authored-by: Orca <help@stably.ai>

* Extend -z fallback to relay/SSH worktree path; tighten comments; add relay tests

Co-authored-by: Orca <help@stably.ai>

* Isolate regex-fallback path in relay test

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: yuqili03 <yuqili03@deeproute.ai>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 12:56:38 -07:00

102 lines
3.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { parseStatusOutput } from './git-status-output-parser'
import { isUnsupportedWorktreeListZError } from './git-handler-utils'
describe('isUnsupportedWorktreeListZError', () => {
it('detects an unknown-switch usage error from stderr when the exit code is absent', () => {
// Isolates the regex fallback: no numeric code, so only the stderr text
// (a runner that dropped the exit code) can classify the rejection.
const error = Object.assign(new Error('worktree list -z'), {
stderr: "error: unknown switch `z'\nusage: git worktree list [<options>]\n"
})
expect(isUnsupportedWorktreeListZError(error)).toBe(true)
})
it('detects a localized (non-English) usage error via exit code 129', () => {
// The SSH remote may run under a non-English locale where the stderr text is
// translated; the numeric exit code must still classify the -z rejection.
const error = Object.assign(new Error('worktree list -z'), {
code: 129,
stderr: 'Fehler: Unbekannter Schalter »z«\nAufruf: git worktree list [<Optionen>]\n'
})
expect(isUnsupportedWorktreeListZError(error)).toBe(true)
})
it('does not classify a fatal (exit 128) error as an unsupported -z rejection', () => {
const error = Object.assign(new Error('fatal'), {
code: 128,
stderr: 'fatal: unable to read tree\n'
})
expect(isUnsupportedWorktreeListZError(error)).toBe(false)
})
})
describe('parseStatusOutput', () => {
it('parses upstream ahead/behind from porcelain v2 branch headers', () => {
const result = parseStatusOutput(
[
'# branch.oid abcdef1234567890',
'# branch.head feature/prompts',
'# branch.upstream origin/feature/prompts',
'# branch.ab +2 -3',
''
].join('\n')
)
expect(result.upstreamStatus).toEqual({
hasUpstream: true,
upstreamName: 'origin/feature/prompts',
ahead: 2,
behind: 3
})
})
it('reports no upstream when porcelain v2 omits branch.upstream', () => {
const result = parseStatusOutput(
['# branch.oid abcdef1234567890', '# branch.head feature/prompts', ''].join('\n')
)
expect(result.upstreamStatus).toEqual({ hasUpstream: false, ahead: 0, behind: 0 })
})
it('parses ignored porcelain records separately from actionable entries', () => {
const result = parseStatusOutput(['! dist/', '! .env', '? scratch.txt', ''].join('\n'))
expect(result.ignoredPaths).toEqual(['dist/', '.env'])
expect(result.entries).toEqual([
{ path: 'scratch.txt', status: 'untracked', area: 'untracked' }
])
})
it('parses rename records with spaces in the paths', () => {
const result = parseStatusOutput(
'2 R. N... 100644 100644 100644 aaaa bbbb R100 src/new name.ts\tsrc/old name.ts\n'
)
expect(result.entries).toEqual([
{ path: 'src/new name.ts', oldPath: 'src/old name.ts', status: 'renamed', area: 'staged' }
])
})
it('parses submodule dirtiness flags from porcelain records', () => {
const result = parseStatusOutput(
'1 AM S..U 000000 160000 160000 0000000000000000000000000000000000000000 7844cb64e631f17a9ca5b548f3500ef7cecd2f17 nested-repo\n'
)
expect(result.entries).toEqual([
{
path: 'nested-repo',
status: 'added',
area: 'staged',
submodule: { commitChanged: false, trackedChanges: false, untrackedChanges: true }
},
{
path: 'nested-repo',
status: 'modified',
area: 'unstaged',
submodule: { commitChanged: false, trackedChanges: false, untrackedChanges: true }
}
])
})
})