mirror your GitHub repos to tangled.org automatically
1

Configure Feed

Select the types of activity you want to include in your feed.

at main 3.5 kB View raw
1import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' 2import { RemoteRejectedError } from '../../server/utils/git-wire/errors' 3import { fetchAdvertisement, fetchPack } from '../../server/utils/git-wire/upload-pack' 4import { fakeGithubFetch, GitFixture } from '../utils/git-wire' 5 6const PACK_MAGIC = Buffer.from('PACK') 7 8async function drain(gen: AsyncGenerator<Buffer>): Promise<Buffer> { 9 const parts: Buffer[] = [] 10 for await (const c of gen) parts.push(c) 11 return Buffer.concat(parts) 12} 13 14describe('upload-pack (against real git-upload-pack)', () => { 15 let fx: GitFixture 16 let realFetch: typeof globalThis.fetch 17 18 beforeEach(() => { 19 fx = new GitFixture() 20 realFetch = globalThis.fetch 21 }) 22 23 afterEach(() => { 24 globalThis.fetch = realFetch 25 fx.cleanup() 26 vi.restoreAllMocks() 27 }) 28 29 function wire(repos: Map<string, string>) { 30 globalThis.fetch = fakeGithubFetch(repos) as unknown as typeof globalThis.fetch 31 } 32 33 it('resolves a ref name to a SHA via the advertisement', async () => { 34 const bare = fx.initBare('gh.git') 35 const work = fx.initWork('work') 36 const sha = fx.commit(work, 'a.txt', 'hello') 37 fx.pushTo(work, bare, 'HEAD:refs/heads/main') 38 wire(new Map([['owner/repo', bare]])) 39 40 const adv = await fetchAdvertisement('owner/repo', 'tok') 41 expect(adv.refs.get('refs/heads/main')).toBe(sha) 42 expect(adv.capabilities.has('thin-pack')).toBe(true) 43 }) 44 45 it('fetches a full pack on first sync (no haves)', async () => { 46 const bare = fx.initBare('gh.git') 47 const work = fx.initWork('work') 48 const sha = fx.commit(work, 'a.txt', 'hello') 49 fx.pushTo(work, bare, 'HEAD:refs/heads/main') 50 wire(new Map([['owner/repo', bare]])) 51 52 const { pack } = await fetchPack({ repoFullName: 'owner/repo', token: 'tok', want: sha, haves: [], maxBytes: 1 << 30 }) 53 const bytes = await drain(pack) 54 expect(bytes.subarray(0, 4)).toEqual(PACK_MAGIC) 55 expect(bytes.length).toBeGreaterThan(0) 56 }) 57 58 it('sends a smaller pack when the knot tip is offered as a have', async () => { 59 const bare = fx.initBare('gh.git') 60 const work = fx.initWork('work') 61 const first = fx.commit(work, 'a.txt', 'a'.repeat(5000)) 62 fx.pushTo(work, bare, 'HEAD:refs/heads/main') 63 const second = fx.commit(work, 'b.txt', 'b'.repeat(5000)) 64 fx.pushTo(work, bare, 'HEAD:refs/heads/main') 65 wire(new Map([['owner/repo', bare]])) 66 67 const full = await drain((await fetchPack({ repoFullName: 'owner/repo', token: 'tok', want: second, haves: [], maxBytes: 1 << 30 })).pack) 68 const incremental = await drain((await fetchPack({ repoFullName: 'owner/repo', token: 'tok', want: second, haves: [first], maxBytes: 1 << 30 })).pack) 69 70 expect(incremental.subarray(0, 4)).toEqual(PACK_MAGIC) 71 expect(incremental.length).toBeLessThan(full.length) 72 }) 73 74 it('aborts with too-big once the byte cap is exceeded', async () => { 75 const bare = fx.initBare('gh.git') 76 const work = fx.initWork('work') 77 const sha = fx.commit(work, 'a.txt', 'x'.repeat(10_000)) 78 fx.pushTo(work, bare, 'HEAD:refs/heads/main') 79 wire(new Map([['owner/repo', bare]])) 80 81 const { pack } = await fetchPack({ repoFullName: 'owner/repo', token: 'tok', want: sha, haves: [], maxBytes: 10 }) 82 await expect(drain(pack)).rejects.toMatchObject({ 83 constructor: RemoteRejectedError, 84 reason: 'too-big', 85 }) 86 }) 87 88 it('throws on a 404 from github', async () => { 89 wire(new Map()) 90 await expect(fetchAdvertisement('missing/repo', 'tok')).rejects.toThrow(/404/) 91 }) 92})