open-standards/aac/aac-rs/tools/symbols/check-no-nc.test.mjs
Transaction Science edd04264eb check-no-nc.mjs: a file named for a set is caught in every folder
The filename check sat inside the content word scan, which skips the
third-party folders, so vendor/arasaac-loader.js passed. Filenames are now
checked in every folder; only the content scan leaves third-party data out
(its dictionaries hold the Italian word "sclera"). Planted: files named for
a set in vendor/, voices/, models/lang/ (a folder) and pkg/ are caught.
prerender-static.sh's comment and the README now describe both this and the
decompressed magic-byte check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 09:08:06 -04:00

141 lines
6.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// node --test tools/symbols/check-no-nc.test.mjs — planted files.
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { randomBytes } from 'node:crypto';
import { copyFileSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { gzipSync, brotliCompressSync } from 'node:zlib';
import { check, PACK_MAGIC } from './check-no-nc.mjs';
function site(files, prefix = '') {
const dir = mkdtempSync(join(tmpdir(), 'check-no-nc-'));
const base = {
'index.html': '<!doctype html><title>AAC</title>',
'pkg/aac-web.0123456789ab.js': 'export default 1;',
'sw.js': `const PRECACHE_URLS = ["/","/pkg/aac-web.0123456789ab.js"];`,
'voices/c/g2p/it/dict.tsv': 'sclera\tˈsklɛra\n',
'voices/c/g2p/fr/verb.csv': 'musclera,muscler\n',
'models/lang/it.0123456789ab.bin': 'sclera scleral',
};
// The usual files sit under the app's prefix; planted ones where they say.
const all = { ...Object.fromEntries(Object.entries(base).map(([k, v]) => [prefix + k, v])), ...files };
for (const [path, content] of Object.entries(all)) {
const p = join(dir, path);
mkdirSync(dirname(p), { recursive: true });
writeFileSync(p, content);
}
return dir;
}
const clean = { packs: [], named: [], precached: [] };
test('a clean build passes, whatever the third-party dictionaries hold', () => {
for (const prefix of ['', 'app/']) {
const dir = site({}, prefix);
assert.deepEqual(check(dir, prefix), clean);
rmSync(dir, { recursive: true });
}
});
test('a file carrying a pack\'s bytes is caught, whatever its name or place', () => {
const dir = site({ 'pkg/x.bin': Buffer.concat([PACK_MAGIC, Buffer.from('rest')]), 'voices/y.dat': PACK_MAGIC });
assert.deepEqual(check(dir).packs.sort(), ['pkg/x.bin', 'voices/y.dat']);
rmSync(dir, { recursive: true });
});
test('a pack compressed as a .br or .gz sidecar is caught in every folder, third-party ones too', () => {
// Incompressible, so each sidecar is larger than the part of it first read.
const pack = Buffer.concat([PACK_MAGIC, randomBytes(200_000)]);
const dir = site({
'voices/c/g2p/it/z.tsv.br': brotliCompressSync(pack),
'models/lang/w.bin.gz': gzipSync(pack),
'pkg/blob.bin.br': brotliCompressSync(pack),
'vendor/v.js.gz': gzipSync(pack),
// Compressed, but not a pack.
'voices/c/g2p/it/ok.tsv.br': brotliCompressSync('ciao\tˈtʃao\n'),
'pkg/ok.js.gz': gzipSync('export default 2;'),
// Named like a sidecar, not compressed, and not a pack.
'ai/raw.br': 'not brotli',
});
assert.deepEqual(check(dir).packs.sort(), ['models/lang/w.bin.gz', 'pkg/blob.bin.br', 'vendor/v.js.gz', 'voices/c/g2p/it/z.tsv.br']);
rmSync(dir, { recursive: true });
});
test('anything under models/symbols/, or any *.pack, is caught', () => {
const dir = site({ 'models/symbols/LICENSE.md': 'licence', 'pkg/a.pack': 'x', 'ai/b.pack.gz': 'x' });
assert.deepEqual(check(dir).packs.sort(), ['ai/b.pack.gz', 'models/symbols/LICENSE.md', 'pkg/a.pack']);
rmSync(dir, { recursive: true });
});
test('the service worker precaching a pack is caught', () => {
const dir = site({ 'sw.js': 'const PRECACHE_URLS = ["/","/models/symbols/x.0123456789ab.pack"];' });
assert.deepEqual(check(dir).precached, ['/models/symbols/x.0123456789ab.pack']);
rmSync(dir, { recursive: true });
});
test('a worker whose precache list cannot be read fails', () => {
const dir = site({ 'sw.js': 'const PRECACHE_URLS = __PRECACHE_URLS__;' });
assert.throws(() => check(dir), /no PRECACHE_URLS list/);
rmSync(dir, { recursive: true });
});
test('an own file naming a set is caught, sidecars decompressed', () => {
const dir = site({
'pkg/aac-web.0123456789ab.wasm': 'xx ARASAAC yy',
'symbols/LICENSES.md': '# Sclera',
'index.html.gz': gzipSync('<p>arasaac</p>'),
'pkg/s.js.br': brotliCompressSync('sclera'),
});
assert.deepEqual(check(dir).named.sort(), ['index.html.gz', 'pkg/aac-web.0123456789ab.wasm', 'pkg/s.js.br', 'symbols/LICENSES.md']);
rmSync(dir, { recursive: true });
});
test('a file named for a set is caught in every folder, third-party ones too', () => {
const dir = site({
'vendor/arasaac-loader.js': 'export {};',
'voices/c/Sclera.txt': '',
'models/lang/arasaac/w.bin': 'x',
'pkg/sclera.js': 'export {};',
});
assert.deepEqual(check(dir).named.sort(), ['models/lang/arasaac/w.bin', 'pkg/sclera.js', 'vendor/arasaac-loader.js', 'voices/c/Sclera.txt']);
rmSync(dir, { recursive: true });
});
test('under /app/, the third-party folders are the app\'s', () => {
const dir = site({ 'app/pkg/x.js': 'sclera', 'app/voices/y.txt': 'sclera' }, 'app/');
assert.deepEqual(check(dir, 'app/').named, ['app/pkg/x.js']);
// The same folders outside the app are not third-party data.
const other = site({ 'voices/x.txt': 'sclera' }, 'app/');
assert.deepEqual(check(other, 'app/').named, ['voices/x.txt']);
rmSync(dir, { recursive: true });
rmSync(other, { recursive: true });
});
test('run as a command, as prerender-static.sh does, from any path, it checks', () => {
const script = fileURLToPath(new URL('./check-no-nc.mjs', import.meta.url));
const tmp = mkdtempSync(join(tmpdir(), 'check-no-nc-cli-'));
const spaced = join(tmp, 'with space');
mkdirSync(spaced);
copyFileSync(script, join(spaced, 'check-no-nc.mjs'));
symlinkSync(dirname(script), join(tmp, 'linked'));
const ways = [
{ name: 'as prerender-static.sh', args: [script] },
{ name: 'from a path with a space', args: [join(spaced, 'check-no-nc.mjs')] },
{ name: 'through a symlinked directory', args: [join(tmp, 'linked', 'check-no-nc.mjs')] },
{ name: 'relative, inside the spaced path', args: ['check-no-nc.mjs'], cwd: spaced },
];
const planted = site({ 'index.html': '<!doctype html><p>ARASAAC</p>' });
const clean = site({});
for (const way of ways) {
const run = (dir) => spawnSync(process.execPath, [...way.args, dir], { cwd: way.cwd, encoding: 'utf8' });
const bad = run(planted);
assert.equal(bad.status, 1, `${way.name}: ${bad.stdout}${bad.stderr}`);
assert.match(bad.stderr, /names them in 1 file\(s\):\n index\.html/, way.name);
const good = run(clean);
assert.equal(good.status, 0, `${way.name}: ${good.stdout}${good.stderr}`);
assert.match(good.stdout, /✓ no symbol pack/, way.name);
}
for (const dir of [tmp, planted, clean]) rmSync(dir, { recursive: true });
});