import { afterEach, describe, expect, test } from 'bun:test'; import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { controlFromDebName, repreproPoolPath, stageDebInPool } from './apt-pool-path'; describe('repreproPoolPath', () => { test('a source named lib* is filed under its first four letters', () => { // The path apt.celilo.computer actually serves, measured for celilo#1505. // The flat pool/ every caller assumed returns 404. expect( repreproPoolPath({ package: 'libsignal-jni', version: '0.96.3', architecture: 'arm64' }), ).toBe('pool/main/libs/libsignal-jni/libsignal-jni_0.96.3_arm64.deb'); }); test('any other source is filed under its first letter', () => { expect(repreproPoolPath({ package: 'celilo', version: '1.2.3', architecture: 'amd64' })).toBe( 'pool/main/c/celilo/celilo_1.2.3_amd64.deb', ); expect( repreproPoolPath({ package: 'celilo-bootstrap', version: '1.2.3', architecture: 'all' }), ).toBe('pool/main/c/celilo-bootstrap/celilo-bootstrap_1.2.3_all.deb'); }); test('the directory follows Source, the file follows Package', () => { expect( repreproPoolPath({ package: 'libfoo-dev', source: 'foo', version: '2.0', architecture: 'arm64', }), ).toBe('pool/main/f/foo/libfoo-dev_2.0_arm64.deb'); }); test('an epoch is not part of the file name', () => { expect(repreproPoolPath({ package: 'celilo', version: '1:2.0-1', architecture: 'amd64' })).toBe( 'pool/main/c/celilo/celilo_2.0-1_amd64.deb', ); }); test('the component is part of the path', () => { expect( repreproPoolPath({ package: 'celilo', version: '1.0', architecture: 'all' }, 'contrib'), ).toBe('pool/contrib/c/celilo/celilo_1.0_all.deb'); }); }); describe('controlFromDebName', () => { test('reads package, version and architecture from a .deb file name', () => { expect(controlFromDebName('libsignal-jni_0.96.3_arm64.deb')).toEqual({ package: 'libsignal-jni', version: '0.96.3', architecture: 'arm64', }); }); test('refuses a name that is not __.deb', () => { expect(() => controlFromDebName('celilo.deb')).toThrow(/celilo\.deb/); expect(() => controlFromDebName('celilo_1.0_amd64.tar')).toThrow(); }); }); describe('stageDebInPool', () => { const dirs: string[] = []; afterEach(() => { for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }); }); test('stages a .deb where reprepro would file it, and not flat in pool/', () => { const work = mkdtempSync(join(tmpdir(), 'apt-pool-')); dirs.push(work); const deb = join(work, 'libsignal-jni_0.96.3_arm64.deb'); writeFileSync(deb, 'bytes'); const cache = join(work, '.apt-repo-cache'); const staged = stageDebInPool(cache, deb); expect(staged).toBe(join(cache, 'pool/main/libs/libsignal-jni/libsignal-jni_0.96.3_arm64.deb')); expect(existsSync(staged)).toBe(true); // The simulator must not answer on a path the real repo does not serve. expect(existsSync(join(cache, 'pool', 'libsignal-jni_0.96.3_arm64.deb'))).toBe(false); }); });