Files
ansiblings/packages/nopy/tests/cubes.resolve-hook.test.ts
Benjamin Diedrichsen 6ecb2c366f
Publish snapshot / snapshot (push) Successful in 1m2s
[refactor] moving cubes into own package"
[fix] default parameter run records parameters in session for replay[fix] remove default parameters for several cubes
2026-07-28 12:18:10 +02:00

108 lines
4.0 KiB
TypeScript

/**
* Tests for the manifest resolve hook.
*
* Every case runs in a child `node` process. Vitest resolves a dynamic import
* through vite, which finds `zod` from the project root whether or not the hook
* is installed — so a test run inside the worker passes either way and proves
* nothing. Only real Node resolution can tell the two apart.
*/
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
const CUBES_SRC = fileURLToPath(new URL('../src/cubes', import.meta.url));
const HOOK = pathToFileURL(path.join(CUBES_SRC, 'resolve-hook.mjs')).href;
// Only ever handed to createRequire, which wants a path inside the package and
// never opens it. This is the same URL loader.ts registers with.
const FROM = pathToFileURL(path.join(CUBES_SRC, 'loader.ts')).href;
describe('resolve-hook', () => {
let tmpDir: string;
/** Runs `manifest.mjs` in a fresh Node process and returns its default export. */
const importManifest = (source: string, { withHook } = { withHook: true }) => {
fs.writeFileSync(path.join(tmpDir, 'manifest.mjs'), source);
const manifest = pathToFileURL(path.join(tmpDir, 'manifest.mjs')).href;
const script = [
withHook ? "import module from 'node:module';" : '',
withHook
? `module.register(${JSON.stringify(HOOK)}, ${JSON.stringify(FROM)}, ` +
`{ data: { from: ${JSON.stringify(FROM)} } });`
: '',
`const loaded = await import(${JSON.stringify(manifest)})`,
' .then((m) => m.default, (err) => ({ failed: err.code ?? String(err) }));',
'console.log(JSON.stringify(loaded));',
]
.filter(Boolean)
.join('\n');
return JSON.parse(
execFileSync(process.execPath, ['--input-type=module', '-e', script], { encoding: 'utf-8' })
);
};
beforeEach(() => {
// Under os.tmpdir() precisely because nothing above it links zod: this is a
// cube in a directory the user pointed `cubeDirs` at, nothing more.
tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'nopy-hook-')));
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it('cannot import zod from a bare directory without the hook', () => {
const result = importManifest("import 'zod';\nexport default { ok: true };", {
withHook: false,
});
expect(result).toEqual({ failed: 'ERR_MODULE_NOT_FOUND' });
});
it('resolves zod from the running CLI', () => {
const result = importManifest(
"import { z } from 'zod';\nexport default { ok: typeof z.object === 'function' };"
);
expect(result).toEqual({ ok: true });
});
it('resolves a subpath of a covered package', () => {
const result = importManifest(
"import pkg from '@bitsquare/nopy/package.json' with { type: 'json' };\n" +
'export default { name: pkg.name };'
);
expect(result).toEqual({ name: '@bitsquare/nopy' });
});
it('leaves anything else to fail as it would have', () => {
const result = importManifest("import 'no-such-package';\nexport default { ok: true };");
expect(result).toEqual({ failed: 'ERR_MODULE_NOT_FOUND' });
});
it('prefers a copy the cube can already see', () => {
// The whole point of trying normal resolution first: a consumer with its
// own zod keeps it, so the hook can never introduce version skew.
const stub = path.join(tmpDir, 'node_modules', 'zod');
fs.mkdirSync(stub, { recursive: true });
fs.writeFileSync(
path.join(stub, 'package.json'),
JSON.stringify({ name: 'zod', version: '0.0.0', type: 'module', main: 'index.js' })
);
fs.writeFileSync(path.join(stub, 'index.js'), "export const z = { from: 'the cube' };");
const result = importManifest(
"import { z } from 'zod';\nexport default { from: z.from ?? 'the CLI' };"
);
expect(result).toEqual({ from: 'the cube' });
});
});