import { createHash } from 'node:crypto'; import { spawnSync } from 'node:child_process'; import { mkdir, mkdtemp, readFile, rm, symlink, writeFile, } from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { compareCodePoints, collectDirectoryFiles, createThirdPartyLicenseBundle, } from '../scripts/release-archive.mjs'; describe('release archive helpers', () => { it('sorts archive paths by code point', () => { expect(['z', 'a', 'ä'].sort(compareCodePoints)).toEqual(['a', 'z', 'ä']); }); it('creates identical ZIP bytes in different process timezones', () => { const hashes = ['UTC', 'Europe/Berlin', 'America/New_York'].map( (timezone) => { const script = ` import { createHash } from 'node:crypto'; import { createDeterministicZip } from './scripts/release-archive.mjs'; const archive = createDeterministicZip({ 'z.txt': Buffer.from('last'), 'a.txt': Buffer.from('first'), }); process.stdout.write(createHash('sha256').update(archive).digest('hex')); `; const result = spawnSync( process.execPath, ['--input-type=module', '--eval', script], { cwd: process.cwd(), encoding: 'utf8', env: { ...process.env, TZ: timezone }, } ); expect(result.status, result.stderr).toBe(0); return result.stdout; } ); expect(new Set(hashes)).toHaveLength(1); }); it('collects installed runtime license and notice texts', async () => { const bundle = await createThirdPartyLicenseBundle(process.cwd(), [ 'react', 'pdfjs-dist', '@add-ideas/toolbox-contract', ]); const text = bundle.toString('utf8'); expect(text).toContain('react@'); expect(text).toContain('pdfjs-dist@'); expect(text).toContain('@add-ideas/toolbox-contract@'); expect(createHash('sha256').update(bundle).digest('hex')).toMatch( /^[a-f0-9]{64}$/ ); }); it('rejects symbolic links in release input', async () => { const temporaryDirectory = await mkdtemp( path.join(os.tmpdir(), 'pdf-tools-release-') ); try { const outsideFile = path.join(temporaryDirectory, 'outside.txt'); const inputDirectory = path.join(temporaryDirectory, 'input'); await writeFile(outsideFile, 'private'); await mkdir(inputDirectory); await symlink(outsideFile, path.join(inputDirectory, 'linked.txt')); await expect(collectDirectoryFiles(inputDirectory)).rejects.toThrow( 'Unsupported filesystem entry in release input: linked.txt' ); } finally { await rm(temporaryDirectory, { recursive: true, force: true }); } }); it('carries the embedded core-js license used by the PDF.js legacy build', async () => { const license = await readFile( 'licenses/core-js-3.49.0-LICENSE.txt', 'utf8' ); expect(license).toContain('core-js 3.49.0'); expect(license).toContain('CoreJS Company'); expect(license).toContain('Permission is hereby granted'); }); });