// @vitest-environment node import { afterEach, describe, expect, it } from 'vitest'; import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import YAML from 'yaml'; import { assertPackageIdentities, installProductionShrinkwrap, preparePublishedPackageRoot, requiredPackageIdentities, } from '../../scripts/lib/pi-runtime-bundle.mjs'; import { parseBundleArgs } from '../../scripts/bundle-pi-runtime.mjs'; const scratchRoots: string[] = []; async function scratch(prefix: string): Promise { const root = await mkdtemp(join(tmpdir(), prefix)); scratchRoots.push(root); return root; } afterEach(async () => { await Promise.all(scratchRoots.splice(0).map((root) => rm(root, { recursive: true, force: true, maxRetries: 3, }))); }); describe('Pi runtime production bundler', () => { it('turns the published root dev mismatch into a reproducible npm 11 production install', async () => { const root = await scratch('makelore-pi-bundle-mismatch-'); const source = join(root, 'published'); const stage = join(root, 'stage'); await mkdir(source); await writeFile(join(source, 'package.json'), `${JSON.stringify({ name: '@fixture/pi-runtime', version: '1.0.0', devDependencies: { 'missing-from-production-lock': '1.0.0' }, }, null, 2)}\n`); await writeFile(join(source, 'npm-shrinkwrap.json'), `${JSON.stringify({ name: '@fixture/pi-runtime', version: '1.0.0', lockfileVersion: 3, requires: true, packages: { '': { name: '@fixture/pi-runtime', version: '1.0.0' }, }, }, null, 2)}\n`); const prepared = await preparePublishedPackageRoot(source, stage); expect(prepared.omittedDevDependencies).toEqual(['missing-from-production-lock']); expect(JSON.parse(await readFile(join(stage, 'package.json'), 'utf8'))) .not.toHaveProperty('devDependencies'); await expect(installProductionShrinkwrap({ projectRoot: process.cwd(), stageRoot: stage, target: { platform: process.platform, arch: process.arch }, })).resolves.toMatchObject({}); }, 15_000); it('accepts same-version nested-to-root flattening by identity instead of physical lock path', () => { const shrinkwrap = { packages: { 'node_modules/p-retry/node_modules/@types/retry': { version: '0.12.0', }, }, }; const required = requiredPackageIdentities(shrinkwrap, { platform: process.platform, arch: process.arch, }); expect(required.identities).toEqual(['@types/retry@0.12.0']); expect(() => assertPackageIdentities(required.identities, [ '@types/retry@0.12.0', ])).not.toThrow(); }); it('keeps target selection explicit and rejects cross-OS staging', () => { const current = `${process.platform}-${process.arch}`; expect(parseBundleArgs(['--target', current]).targets).toEqual([ expect.objectContaining({ platform: process.platform, arch: process.arch }), ]); const foreign = process.platform === 'win32' ? 'linux-x64' : 'win32-x64'; expect(() => parseBundleArgs(['--target', foreign])).toThrow(/must be staged on/); expect(() => parseBundleArgs(['--target', 'unknown-x64'])).toThrow(/Unsupported/); }); it('wires every package target to its matching staged Pi runtime', async () => { const packageJson = JSON.parse(await readFile('package.json', 'utf8')) as { homepage: string; scripts: Record; }; const builder = YAML.parse(await readFile('electron-builder.yml', 'utf8')) as Record< string, { extraResources?: Array<{ from: string; to: string }> } >; expect(packageJson.scripts.build).toContain('bundle-pi-runtime.mjs --release-targets'); expect(packageJson.scripts.package).toContain('bundle-pi-runtime.mjs --release-targets'); expect(packageJson.scripts['package:stage:win-x64']) .toContain('bundle-pi-runtime.mjs --target win32-x64'); expect(packageJson.scripts['verify:artifact:pi']) .toBe('node scripts/verify-pi-product-artifact.mjs'); expect(packageJson.scripts['smoke:pi:real']) .toBe('node scripts/smoke-pi-real.mjs'); expect(packageJson.scripts['perf:pi:release']) .toBe('node scripts/run-pi-release-performance.mjs'); expect(packageJson.homepage).toBe('https://git.nianxx.cn/wangxuming/makelore'); expect(builder.mac.extraResources).toContainEqual({ from: 'build/pi-runtime/darwin-${arch}', to: 'pi-runtime', filter: ['**/*', '!node_modules{,/**/*}'], }); expect(builder.mac.extraResources).toContainEqual({ from: 'build/pi-runtime/darwin-${arch}/node_modules', to: 'pi-runtime/node_modules', filter: ['**/*'], }); expect(builder.win.extraResources).toContainEqual({ from: 'build/pi-runtime/win32-${arch}', to: 'pi-runtime', filter: ['**/*', '!node_modules{,/**/*}'], }); expect(builder.win.extraResources).toContainEqual({ from: 'build/pi-runtime/win32-${arch}/node_modules', to: 'pi-runtime/node_modules', filter: ['**/*'], }); expect(builder.linux.extraResources).toContainEqual({ from: 'build/pi-runtime/linux-${arch}', to: 'pi-runtime', filter: ['**/*', '!node_modules{,/**/*}'], }); expect(builder.linux.extraResources).toContainEqual({ from: 'build/pi-runtime/linux-${arch}/node_modules', to: 'pi-runtime/node_modules', filter: ['**/*'], }); }); });