feedback5 R6: check that sim and match stay off the net and the client
tools/check-imports.mjs lists boundary hits and exits 1 only with --strict. The current hit is the Node data loader's node:fs, node:path and node:url. server/data.js stays allowed.
This commit is contained in:
1 parent
64a7f48efa
commit
014259b75f
3 files changed
+422
-1
No files matched your search
@@ -0,0 +1,110 @@
|
||||
// Boundary checker: fixture rules, plus the real tree's current violation list.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { findSpecifiers, scan } from '../tools/check-imports.mjs';
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
function fixture(files) {
|
||||
const root = mkdtempSync(join(tmpdir(), 'sp-imp-'));
|
||||
for (const [rel, body] of Object.entries(files)) {
|
||||
const abs = join(root, rel);
|
||||
mkdirSync(dirname(abs), { recursive: true });
|
||||
writeFileSync(abs, body);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
test('comments are not imports, and a multiline import is', () => {
|
||||
const specs = findSpecifiers(`
|
||||
// import fs from 'node:fs'
|
||||
/* require('node:path') */
|
||||
import {
|
||||
a,
|
||||
b,
|
||||
} from './tier1.js';
|
||||
export function fromThing() { return 1; }
|
||||
`);
|
||||
assert.deepEqual(specs.map((s) => s.spec), ['./tier1.js']);
|
||||
});
|
||||
|
||||
test('sim and match rules on a fixture tree', () => {
|
||||
const root = fixture({
|
||||
'server/sim/clean.js': `
|
||||
// import fs from 'node:fs'
|
||||
import { a } from './other.js';
|
||||
export function load() { return 1; }
|
||||
export { a };
|
||||
`,
|
||||
'server/sim/bad.js': `
|
||||
import '../match/Match.js';
|
||||
import '../lobby.js';
|
||||
import '../index.js';
|
||||
import '../net.js';
|
||||
import '../../public/js/main.js';
|
||||
import fs from 'node:fs';
|
||||
import 'fs';
|
||||
export { z } from '../match/board.js';
|
||||
const p = await import('node:path');
|
||||
`,
|
||||
'server/match/ok.js': `
|
||||
import '../sim/rng.js';
|
||||
import 'node:fs';
|
||||
import '../data.js';
|
||||
`,
|
||||
'server/match/bad.js': `
|
||||
import '../../public/js/main.js';
|
||||
import 'node:http';
|
||||
import 'ws';
|
||||
import '../net.js';
|
||||
import '../index.js';
|
||||
`,
|
||||
});
|
||||
try {
|
||||
const keys = scan(root).violations.map((v) => `${v.file} ${v.spec} ${v.code}`).sort();
|
||||
assert.deepEqual(keys, [
|
||||
'server/match/bad.js ../../public/js/main.js match-client',
|
||||
'server/match/bad.js ../index.js match-http',
|
||||
'server/match/bad.js ../net.js match-http',
|
||||
'server/match/bad.js node:http match-http',
|
||||
'server/match/bad.js ws match-http',
|
||||
'server/sim/bad.js ../../public/js/main.js sim-client',
|
||||
'server/sim/bad.js ../index.js sim-entry',
|
||||
'server/sim/bad.js ../lobby.js sim-lobby',
|
||||
'server/sim/bad.js ../match/Match.js sim-match',
|
||||
'server/sim/bad.js ../match/board.js sim-match',
|
||||
'server/sim/bad.js ../net.js sim-http',
|
||||
'server/sim/bad.js fs sim-node',
|
||||
'server/sim/bad.js node:fs sim-node',
|
||||
'server/sim/bad.js node:path sim-node',
|
||||
]);
|
||||
assert.equal(scan(root).notes.length, 0);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('this repo: only the Node data loader crosses the sim boundary', () => {
|
||||
const { violations, notes } = scan(ROOT);
|
||||
assert.deepEqual(violations.map((v) => `${v.file}:${v.line}: ${v.spec} [${v.code}]`), [
|
||||
'server/sim/nodeData.js:11: node:fs [sim-node]',
|
||||
'server/sim/nodeData.js:12: node:path [sim-node]',
|
||||
'server/sim/nodeData.js:13: node:url [sim-node]',
|
||||
]);
|
||||
assert.ok(notes.some((n) => n.file === 'server/sim/nodeData.js' && n.resolved === 'server/data.js'));
|
||||
assert.ok(notes.every((n) => n.resolved === 'server/data.js'));
|
||||
});
|
||||
|
||||
test('the CLI lists hits and fails only with --strict', () => {
|
||||
const loose = spawnSync(process.execPath, ['tools/check-imports.mjs'], { cwd: ROOT, encoding: 'utf8' });
|
||||
assert.equal(loose.status, 0);
|
||||
assert.match(loose.stdout, /server\/sim\/nodeData\.js:11: node:fs \[sim-node\]/);
|
||||
const strict = spawnSync(process.execPath, ['tools/check-imports.mjs', '--strict'], { cwd: ROOT, encoding: 'utf8' });
|
||||
assert.equal(strict.status, 1);
|
||||
assert.match(strict.stdout, /node:fs/);
|
||||
});
|
||||
Reference in new issue
Block a user