morphit/apps/indexer/scripts/handler-test-stand-in-meta-assertion-smoke.ts

164 lines
6 KiB
TypeScript

#!/usr/bin/env tsx
/**
* handler-test-stand-in-meta-assertion-smoke.
*
* Part 122 cp49 STRUCTURAL DEFENSE (LL #53 / O-2). Closes the
* cp47-A1 recurring class permanently for vitest unit-test files
* (the cp48-O1 closure covered only standalone smoke scripts;
* the vitest test path was an unprotected sibling).
*
* Recurring class history:
* cp33 — handler tests used 'DOGE' as the "unknown asset"
* stand-in; cp33 added DOGE; tests silently passed because
* they ran before the asset list was rechecked.
* cp39 — same pattern with 'ZEC'.
* cp47 — same pattern with 'ETH'. Vitest tests SILENTLY BROKE
* (handler returned ok:true; expectation was
* asset_invalid) but the unit-test path was NOT part of
* run-smokes.sh, so the breakage went undetected for
* 2 checkpoints (cp47, cp48).
* cp49 — surfaced via deep-deep A-2 grep; fixed inline AND
* structural defense added here.
*
* Defense: any time a vitest test file contains a hardcoded
* asset ticker literal in a "rejects unknown asset" or
* "asset_invalid" context, that literal must NOT be in the
* canonical ASSET_TICKERS set. Synthetic non-ticker like
* '__UNKNOWN__' (or any value with chars outside the canonical
* ticker regex /^[A-Z]+$/) is acceptable.
*
* Mutation-test verification: M-111 in cp49 deep-deep. Swapping
* '__UNKNOWN__' back to a real ticker like 'XRP' fires:
* "handler-test-stand-in-meta-assertion FAILED:
* apps/indexer/test/handlers/order.test.ts uses 'XRP' as
* asset_invalid stand-in — XRP is a real ticker. Pick a
* synthetic non-ticker like '__UNKNOWN__'."
*
* Also pins cp48-O1's UNKNOWN_STANDIN constant in
* asset-registry-smoke.ts — confirms that defense is still
* intact at cp49.
*/
import { readFileSync, readdirSync, statSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { ASSET_TICKERS } from '../../../packages/asset-registry/src/index';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ASSET_TICKERS_SET = new Set(ASSET_TICKERS as readonly string[]);
let failed = 0;
let passed = 0;
function pass(name: string): void {
console.log(`${name}`);
passed++;
}
function fail(name: string, detail: string): void {
console.error(`${name}`);
console.error(` ${detail}`);
failed++;
}
console.log('\n── handler-test-stand-in meta-assertion smoke (cp49 LL #53) ─\n');
// ── Walk vitest test files recursively ──
function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
const st = statSync(full);
if (st.isDirectory()) {
if (entry !== 'node_modules' && entry !== 'dist' && entry !== '.svelte-kit') {
walk(full, out);
}
} else if (entry.endsWith('.test.ts') || entry.endsWith('.test.tsx')) {
out.push(full);
}
}
return out;
}
const ROOTS = [
join(__dirname, '..', '..', '..', 'apps', 'indexer', 'test'),
join(__dirname, '..', '..', '..', 'apps', 'relay', 'test'),
join(__dirname, '..', '..', '..', 'apps', 'web', 'test'),
join(__dirname, '..', '..', '..', 'packages', 'asset-registry', 'test')
];
const files: string[] = [];
for (const r of ROOTS) {
try { walk(r, files); } catch { /* dir missing OK */ }
}
console.log(`Scanned ${files.length} vitest test files\n`);
// Find any hardcoded literal `asset: 'XXX'` or `asset: "XXX"` near
// `asset_invalid` or `unknown asset`.
const ASSET_LITERAL_RE = /asset:\s*['"]([A-Z_]{2,16})['"]/g;
const REJECTION_CONTEXT_RE = /asset_invalid|unknown asset|invalid asset/i;
const violations: Array<{ file: string; line: number; ticker: string; context: string }> = [];
for (const f of files) {
const content = readFileSync(f, 'utf-8');
const lines = content.split('\n');
// For each occurrence, check if there's a rejection-context
// match within ±5 lines.
for (let i = 0; i < lines.length; i++) {
const line = lines[i] ?? '';
ASSET_LITERAL_RE.lastIndex = 0;
let m: RegExpExecArray | null;
while ((m = ASSET_LITERAL_RE.exec(line)) !== null) {
const ticker = m[1] ?? '';
if (!ASSET_TICKERS_SET.has(ticker)) continue; // synthetic OK
// Look in nearby context for rejection
const start = Math.max(0, i - 5);
const end = Math.min(lines.length - 1, i + 5);
const context = lines.slice(start, end + 1).join('\n');
if (REJECTION_CONTEXT_RE.test(context)) {
violations.push({ file: f, line: i + 1, ticker, context: lines[i] ?? '' });
}
}
}
}
if (violations.length === 0) {
pass(`no vitest test file uses a real ticker as 'asset_invalid' stand-in (${files.length} files scanned)`);
} else {
for (const v of violations) {
fail(
`stand-in violation in ${v.file.split('/morphit-cp49/')[1] ?? v.file}:${v.line}`,
`uses '${v.ticker}' as asset_invalid stand-in — '${v.ticker}' is a real ticker. ` +
`Pick a synthetic non-ticker like '__UNKNOWN__'. Context: ${v.context.trim()}`
);
}
}
// ── Also pin cp48-O1's UNKNOWN_STANDIN integrity ──
const indexerSmokePath = join(__dirname, '..', '..', '..', 'apps', 'indexer', 'scripts', 'asset-registry-smoke.ts');
try {
const c = readFileSync(indexerSmokePath, 'utf-8');
const m = /const UNKNOWN_STANDIN = '([^']+)';/.exec(c);
if (!m) {
fail("cp48-O1's UNKNOWN_STANDIN constant intact", "no `const UNKNOWN_STANDIN = '...'` found in asset-registry-smoke.ts");
} else {
const v = m[1] ?? '';
if (ASSET_TICKERS_SET.has(v.toUpperCase())) {
fail("cp48-O1's UNKNOWN_STANDIN intact", `UNKNOWN_STANDIN '${v}' is now a real ticker`);
} else {
pass(`cp48-O1's UNKNOWN_STANDIN ('${v}') intact and remains non-ticker`);
}
}
} catch (e) {
fail('cp48-O1 inspection', `could not read asset-registry-smoke.ts: ${e}`);
}
const total = passed + failed;
console.log(`\n${passed} passed, ${failed} failed (${total} total)`);
if (failed > 0) {
console.error('\nhandler-test-stand-in meta-assertion smoke FAILED');
process.exit(1);
}
console.log(`✓ all ${total} stand-in scenarios passed`);