Merge pull request 'test: guard dashboard text against mojibake' (#40) from codex/issue-23-mojibake-locale-check into codex/production-intelligence-terminal
Reviewed-on: #40
This commit was merged in pull request #40.
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
"brief:save": "node apis/save-briefing.mjs",
|
"brief:save": "node apis/save-briefing.mjs",
|
||||||
"diag": "node diag.mjs",
|
"diag": "node diag.mjs",
|
||||||
"test": "npm run test:unit",
|
"test": "npm run test:unit",
|
||||||
"test:unit": "node --test test/llm-openrouter.test.mjs test/llm-ollama.test.mjs test/llm-openai-compatible.test.mjs test/fetch-utils.test.mjs test/reddit-source.test.mjs test/acled-source.test.mjs",
|
"test:unit": "node --test test/llm-openrouter.test.mjs test/llm-ollama.test.mjs test/llm-openai-compatible.test.mjs test/fetch-utils.test.mjs test/reddit-source.test.mjs test/acled-source.test.mjs test/mojibake-text.test.mjs",
|
||||||
"compose:config": "docker compose config",
|
"compose:config": "docker compose config",
|
||||||
"clean": "node scripts/clean.mjs",
|
"clean": "node scripts/clean.mjs",
|
||||||
"fresh-start": "npm run clean && npm start"
|
"fresh-start": "npm run clean && npm start"
|
||||||
|
|||||||
65
test/mojibake-text.test.mjs
Normal file
65
test/mojibake-text.test.mjs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
const TEXT_ROOTS = ['locales'];
|
||||||
|
|
||||||
|
const TEXT_FILES = [];
|
||||||
|
|
||||||
|
const EXTENSIONS = new Set(['.json', '.html', '.mjs']);
|
||||||
|
|
||||||
|
const MOJIBAKE_PATTERNS = [
|
||||||
|
{ name: 'latin1-accent', pattern: /\u00c3./g },
|
||||||
|
{ name: 'stray-cp1252-prefix', pattern: /\u00c2./g },
|
||||||
|
{ name: 'emoji-mojibake', pattern: /\u00f0\u0178/g },
|
||||||
|
{
|
||||||
|
name: 'punctuation-mojibake',
|
||||||
|
pattern: /\u00e2[\u0080-\u009f\u20ac\u0153\u2018\u2019\u201c\u201d\u2013\u2014\u2022\u2026\u201e\u2021\u02c6\u2030\u2039\u203a\u0152\u017d]/g,
|
||||||
|
},
|
||||||
|
{ name: 'variation-selector-mojibake', pattern: /\u00ef\u00b8/g },
|
||||||
|
{ name: 'ligature-mojibake', pattern: /\u00c5[\u0080-\u017f]/g },
|
||||||
|
{ name: 'replacement-character', pattern: /\ufffd/g },
|
||||||
|
];
|
||||||
|
|
||||||
|
function collectFiles(root) {
|
||||||
|
const out = [];
|
||||||
|
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
||||||
|
const path = join(root, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
out.push(...collectFiles(path));
|
||||||
|
} else if (EXTENSIONS.has(path.slice(path.lastIndexOf('.')))) {
|
||||||
|
out.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function textFiles() {
|
||||||
|
const discovered = TEXT_ROOTS.flatMap(root => collectFiles(root));
|
||||||
|
const explicit = TEXT_FILES.filter(path => statSync(path, { throwIfNoEntry: false })?.isFile());
|
||||||
|
return [...new Set([...discovered, ...explicit])].sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
test('locale JSON files are valid UTF-8 JSON', () => {
|
||||||
|
for (const file of collectFiles('locales')) {
|
||||||
|
assert.doesNotThrow(() => JSON.parse(readFileSync(file, 'utf8')), `${file} must parse as JSON`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('locale text does not contain known mojibake sequences', () => {
|
||||||
|
const failures = [];
|
||||||
|
|
||||||
|
for (const file of textFiles()) {
|
||||||
|
const text = readFileSync(file, 'utf8');
|
||||||
|
for (const { name, pattern } of MOJIBAKE_PATTERNS) {
|
||||||
|
for (const match of text.matchAll(pattern)) {
|
||||||
|
const start = Math.max(0, match.index - 30);
|
||||||
|
const end = Math.min(text.length, match.index + 50);
|
||||||
|
failures.push(`${file}: ${name}: ${JSON.stringify(text.slice(start, end))}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.deepEqual(failures, []);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user