build-identity.test.ts102 lines · main
| 1 | /** |
| 2 | * Shared build-identity resolver tests — pin the env→git→"dev" |
| 3 | * fallback chain so every service (apps/api, apps/realtime, |
| 4 | * apps/runtime) gets the same answer for `/info.buildSha`. |
| 5 | */ |
| 6 | |
| 7 | import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; |
| 8 | import { tmpdir } from 'node:os'; |
| 9 | import { join } from 'node:path'; |
| 10 | |
| 11 | import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; |
| 12 | |
| 13 | import { resolveBuildAtFromGit, resolveShaFromGit } from '@briven/shared'; |
| 14 | |
| 15 | describe('resolveShaFromGit', () => { |
| 16 | let tmp: string; |
| 17 | let gitDir: string; |
| 18 | const SHA = 'a42bd57b3c1d8e2f4a6b7c9d0e1f2a3b4c5d6e7f'; |
| 19 | |
| 20 | beforeEach(() => { |
| 21 | tmp = mkdtempSync(join(tmpdir(), 'briven-git-')); |
| 22 | gitDir = join(tmp, '.git'); |
| 23 | mkdirSync(gitDir, { recursive: true }); |
| 24 | }); |
| 25 | |
| 26 | afterEach(() => { |
| 27 | rmSync(tmp, { recursive: true, force: true }); |
| 28 | }); |
| 29 | |
| 30 | test('returns null when .git/HEAD is missing (dev tarball case)', () => { |
| 31 | expect(resolveShaFromGit(gitDir)).toBeNull(); |
| 32 | }); |
| 33 | |
| 34 | test('resolves a detached HEAD (sha written directly)', () => { |
| 35 | writeFileSync(join(gitDir, 'HEAD'), SHA + '\n'); |
| 36 | expect(resolveShaFromGit(gitDir)).toBe(SHA); |
| 37 | }); |
| 38 | |
| 39 | test('resolves a loose ref (refs/heads/main)', () => { |
| 40 | writeFileSync(join(gitDir, 'HEAD'), 'ref: refs/heads/main\n'); |
| 41 | mkdirSync(join(gitDir, 'refs/heads'), { recursive: true }); |
| 42 | writeFileSync(join(gitDir, 'refs/heads/main'), SHA + '\n'); |
| 43 | expect(resolveShaFromGit(gitDir)).toBe(SHA); |
| 44 | }); |
| 45 | |
| 46 | test('falls back to packed-refs when loose ref is absent', () => { |
| 47 | writeFileSync(join(gitDir, 'HEAD'), 'ref: refs/heads/main\n'); |
| 48 | writeFileSync( |
| 49 | join(gitDir, 'packed-refs'), |
| 50 | `# pack-refs with: peeled fully-peeled sorted\n${SHA} refs/heads/main\n`, |
| 51 | ); |
| 52 | expect(resolveShaFromGit(gitDir)).toBe(SHA); |
| 53 | }); |
| 54 | |
| 55 | test('skips comment + peeled lines in packed-refs', () => { |
| 56 | writeFileSync(join(gitDir, 'HEAD'), 'ref: refs/heads/main\n'); |
| 57 | writeFileSync( |
| 58 | join(gitDir, 'packed-refs'), |
| 59 | [ |
| 60 | '# pack-refs with: peeled fully-peeled sorted', |
| 61 | 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef refs/heads/other', |
| 62 | `${SHA} refs/heads/main`, |
| 63 | '^cafebabecafebabecafebabecafebabecafebabe', |
| 64 | ].join('\n') + '\n', |
| 65 | ); |
| 66 | expect(resolveShaFromGit(gitDir)).toBe(SHA); |
| 67 | }); |
| 68 | |
| 69 | test('returns null on malformed sha (corrupted ref)', () => { |
| 70 | writeFileSync(join(gitDir, 'HEAD'), 'ref: refs/heads/main\n'); |
| 71 | mkdirSync(join(gitDir, 'refs/heads'), { recursive: true }); |
| 72 | writeFileSync(join(gitDir, 'refs/heads/main'), 'not-a-sha\n'); |
| 73 | expect(resolveShaFromGit(gitDir)).toBeNull(); |
| 74 | }); |
| 75 | }); |
| 76 | |
| 77 | describe('resolveBuildAtFromGit', () => { |
| 78 | let tmp: string; |
| 79 | let gitDir: string; |
| 80 | |
| 81 | beforeEach(() => { |
| 82 | tmp = mkdtempSync(join(tmpdir(), 'briven-git-at-')); |
| 83 | gitDir = join(tmp, '.git'); |
| 84 | mkdirSync(gitDir, { recursive: true }); |
| 85 | }); |
| 86 | |
| 87 | afterEach(() => { |
| 88 | rmSync(tmp, { recursive: true, force: true }); |
| 89 | }); |
| 90 | |
| 91 | test('returns ISO timestamp derived from HEAD mtime', () => { |
| 92 | writeFileSync(join(gitDir, 'HEAD'), 'ref: refs/heads/main\n'); |
| 93 | const out = resolveBuildAtFromGit(gitDir); |
| 94 | expect(out).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/); |
| 95 | const delta = Math.abs(Date.now() - new Date(out as string).getTime()); |
| 96 | expect(delta).toBeLessThan(5_000); |
| 97 | }); |
| 98 | |
| 99 | test('returns null when HEAD is absent', () => { |
| 100 | expect(resolveBuildAtFromGit(gitDir)).toBeNull(); |
| 101 | }); |
| 102 | }); |