mirror your GitHub repos to tangled.org automatically
1import { describe, expect, it } from 'vitest'
2import { classifyNgReason, classifySshStderr } from '../../server/utils/git-wire/errors'
3import { parseAdvertisement, ZERO_SHA } from '../../server/utils/git-wire/refs'
4
5function lines(...ls: string[]): Buffer[] {
6 return ls.map(l => Buffer.from(l))
7}
8
9describe('parseAdvertisement', () => {
10 it('parses a populated repo with capabilities on the first ref line', () => {
11 const adv = parseAdvertisement(lines(
12 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa refs/heads/main\0report-status delete-refs thin-pack agent=git/2.39\n',
13 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb refs/heads/dev\n',
14 ))
15 expect(adv.refs.get('refs/heads/main')).toBe('a'.repeat(40))
16 expect(adv.refs.get('refs/heads/dev')).toBe('b'.repeat(40))
17 expect(adv.capabilities.has('thin-pack')).toBe(true)
18 expect(adv.capabilities.has('delete-refs')).toBe(true)
19 })
20
21 it('skips the smart-HTTP service prelude', () => {
22 const adv = parseAdvertisement(lines(
23 '# service=git-upload-pack\n',
24 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa refs/heads/main\0thin-pack\n',
25 ))
26 expect(adv.refs.get('refs/heads/main')).toBe('a'.repeat(40))
27 expect(adv.capabilities.has('thin-pack')).toBe(true)
28 })
29
30 it('records peeled annotated-tag lines separately', () => {
31 const adv = parseAdvertisement(lines(
32 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa refs/tags/v1\0thin-pack\n',
33 'cccccccccccccccccccccccccccccccccccccccc refs/tags/v1^{}\n',
34 ))
35 expect(adv.refs.get('refs/tags/v1')).toBe('a'.repeat(40))
36 expect(adv.peeled.get('refs/tags/v1')).toBe('c'.repeat(40))
37 })
38
39 it('handles the empty-repo capabilities sentinel without inventing a ref', () => {
40 const adv = parseAdvertisement(lines(
41 `${ZERO_SHA} capabilities^{}\0report-status delete-refs\n`,
42 ))
43 expect(adv.refs.size).toBe(0)
44 expect(adv.capabilities.has('report-status')).toBe(true)
45 })
46})
47
48describe('classifySshStderr', () => {
49 it('classifies repo-gone', () => {
50 expect(classifySshStderr('fatal: repository not found')?.reason).toBe('repo-gone')
51 expect(classifySshStderr('ERROR: does not exist')?.reason).toBe('repo-gone')
52 })
53
54 it('classifies auth-rejected', () => {
55 expect(classifySshStderr('git@host: Permission denied (publickey).')?.reason).toBe('auth-rejected')
56 })
57
58 it('returns null for unrecognised stderr', () => {
59 expect(classifySshStderr('warning: something benign')).toBeNull()
60 })
61})
62
63describe('classifyNgReason', () => {
64 it('classifies a stale compare-and-swap as stale-old-sha', () => {
65 expect(classifyNgReason('non-fast-forward').reason).toBe('stale-old-sha')
66 expect(classifyNgReason('stale info').reason).toBe('stale-old-sha')
67 })
68
69 it('classifies a missing repo', () => {
70 expect(classifyNgReason('repository does not exist').reason).toBe('repo-gone')
71 })
72
73 it('falls back to other', () => {
74 expect(classifyNgReason('funny business').reason).toBe('other')
75 })
76})