235 lines
28 KiB
JavaScript
235 lines
28 KiB
JavaScript
#!/usr/bin/env node
|
|
// proba-conformance.mjs: the probe of the conformance suite itself. Three states per row: VERDE (as required), ROSU (not), STRICAT /
|
|
// NEMASURAT (the row could not be measured: counted, never passed); INFO rows are measurements printed beside the gates, not counted. Exit 0 all green, 1 a red or broken row, 2 only unmeasured rows.
|
|
//
|
|
// node proba-conformance.mjs [--online] [--acceptate] the suite is derived, whole, single-place, two implementations pass it,
|
|
// and malformed artifacts are refused by name (--acceptate lists what a
|
|
// verifier still accepts when damaged: the optional and the unread)
|
|
// node proba-conformance.mjs --control-negativ [--online] [--doar <rule id prefix>]
|
|
// every rule planted out of its verifier, one at a time: the vectors that
|
|
// name the rule, and only they, must fail; plus the controls of the
|
|
// runner, of the coverage gate and of the rule derivation
|
|
// node proba-conformance.mjs --pe-loc a MEASUREMENT, not a gate: each check site planted out alone; how many
|
|
// of the sites the vectors reach
|
|
//
|
|
// The independent implementation needs Python 3 (AERE_CONFORMANCE_PYTHON=<command>, default python3 then python); the envelope
|
|
// vectors need the AIP-23 reference verifier (AERE_VERIFY_PROOF=<verify-proof.mjs>, with `npm install` done beside it); --online
|
|
// reads testnet 28001 (nothing is sent).
|
|
import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { spawn, spawnSync } from 'node:child_process'; import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
import { deriva, hartaPublica, VERIFICATOARE, MODULE, VERIFY_PROOF, FINALITY_SITES, incarca, marcajul } from './reguli.mjs';
|
|
import { incarcaVectorii, ruleazaSuita } from './conformance.mjs';
|
|
import { aplica, plicuriKinds } from './genereaza-vectori.mjs';
|
|
|
|
const AICI = path.dirname(fileURLToPath(import.meta.url));
|
|
const a = process.argv.slice(2); const online = a.includes('--online'); const control = a.includes('--control-negativ'); const peLoc = a.includes('--pe-loc');
|
|
const REF = `node "${path.join(AICI, 'implementare-aere.mjs')}"`;
|
|
let verzi = 0, rosii = 0, nemas = 0;
|
|
const rand = (stare, text) => { console.log(` [${stare.padEnd(9)}] ${text}`); if (stare === 'VERDE') verzi++; else if (stare === 'ROSU') rosii++; else if (stare !== 'INFO') nemas++; };
|
|
const cer = (cond, text, detaliu = '') => rand(cond ? 'VERDE' : 'ROSU', text + (detaliu ? ': ' + detaliu : ''));
|
|
const canon = (x) => (x === null || typeof x !== 'object' ? JSON.stringify(x) : Array.isArray(x) ? '[' + x.map(canon).join(',') + ']' : '{' + Object.keys(x).sort().map((k) => JSON.stringify(k) + ':' + canon(x[k])).join(',') + '}');
|
|
/** the JSON paths at which two values differ (a subtree added, removed or of another type is one path) */
|
|
function difera(x, y, p = []) {
|
|
if (canon(x) === canon(y)) return [];
|
|
const ob = (v) => v !== null && typeof v === 'object';
|
|
if (!ob(x) || !ob(y) || Array.isArray(x) !== Array.isArray(y)) return [p];
|
|
return [...new Set([...Object.keys(x), ...Object.keys(y)])].flatMap((k) => (k in x && k in y ? difera(x[k], y[k], [...p, Array.isArray(x) ? Number(k) : k]) : [[...p, Array.isArray(x) ? Number(k) : k]]));
|
|
}
|
|
const prefix = (pre, p) => pre.length <= p.length && pre.every((k, i) => String(k) === String(p[i]));
|
|
|
|
if (!fs.existsSync(VERIFY_PROOF)) { console.log('NEMASURAT: the AIP-23 reference verifier is not at ' + VERIFY_PROOF + ' (AERE_VERIFY_PROOF=<verify-proof.mjs>)'); process.exit(2); }
|
|
const S = incarcaVectorii(); if (S.eroare) { console.log('ROSU: the vector set is not whole: ' + S.eroare); process.exit(1); }
|
|
const vectori = S.vectori; const HARTA = hartaPublica(); const TOATE = Object.keys(HARTA);
|
|
const cuRegula = (r, lista = vectori) => lista.filter((v) => v.expected.failed.includes(r)).map((v) => v.id);
|
|
/** coverage: every rule has a vector that expects it; returns the rules without one */
|
|
const neacoperite = (reguli, lista = vectori) => reguli.filter((r) => !cuRegula(r, lista).length);
|
|
const python = () => { for (const c of [process.env.AERE_CONFORMANCE_PYTHON, 'python3', 'python'].filter(Boolean)) { const r = spawnSync(c, ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); if (r.status === 0 && /Python 3/.test(r.stdout + r.stderr)) return c; } return null; };
|
|
const numar = (s, st) => s.rezultate.filter((r) => r.stare === st);
|
|
|
|
// ---------- planting ----------
|
|
const PLANT = "|| String(1) === '1' /*PLANTAT*/";
|
|
/** exact-once plantings of the reference verifier (a published program without markers): rule -> [text, planted text]. The rule is
|
|
* taken out whole: its level then passes whatever it is shown. */
|
|
const PLANT_VP = {
|
|
form: ["const formOk = obj(env) && obj(env.statement)", "const formOk = String(1) === '1' || obj(env) && obj(env.statement)"],
|
|
integrity: ["const ok = h === sh.toLowerCase();", "const ok = h === sh.toLowerCase() || String(1) === '1';"],
|
|
signature: [" const s = env.signature;\n", " const s = env.signature;\n if (String(1) === '1') return level('signature', 'PASSED', 'planted out');\n"],
|
|
finality: ["env.notarization ? 'FAILED' : 'ABSENT'", "(env.notarization && String(1) === '2') ? 'FAILED' : 'ABSENT'"],
|
|
};
|
|
/** Writes the planted copy of a rule's verifier beside the original; returns { copie, env } or { stricat }. `loc` plants one site only. */
|
|
function planteaza(verifier, sursa, loc = null) {
|
|
const d = VERIFICATOARE[verifier];
|
|
if (d.sursa === 'verify-proof') {
|
|
const src = fs.readFileSync(VERIFY_PROOF, 'utf8'); const [de, cu] = PLANT_VP[sursa] || []; const n = de ? src.split(de).length - 1 : 0;
|
|
if (n !== 1) return { stricat: `the planting anchor of ${sursa} appears ${n} times in the reference verifier` };
|
|
const copie = path.join(path.dirname(VERIFY_PROOF), `.plantat-conf-${process.pid}-${sursa}.mjs`); fs.writeFileSync(copie, src.replace(de, () => cu));
|
|
return { copie, env: { AERE_VERIFY_PROOF: copie, AERE_CONF_VERIFY_PROOF_DEP: VERIFY_PROOF } };
|
|
}
|
|
const p = fileURLToPath(MODULE[d.sursa].url); const src = fs.readFileSync(p, 'utf8'); const ap = marcajul(d.sursa);
|
|
if (ap) { // the rule is named where the verifier refuses: its one refusal helper, in the copy, drops the refusals of this rule
|
|
const text = ap.planteaza(src, sursa); if (text === null || loc !== null) return { stricat: loc !== null ? 'no single sites to plant' : 'the refusal helper is not where the planting expects it' };
|
|
const copie = path.join(path.dirname(p), `_plantat-conf-${process.pid}-${sursa}.mjs`); fs.writeFileSync(copie, text); return { copie, env: { [MODULE[d.sursa].env]: copie } };
|
|
}
|
|
const bucati = src.split(`/*PAZA:${sursa}*/`); const n = bucati.length - 1;
|
|
if (n < 1) return { stricat: `no /*PAZA:${sursa}*/ marker in ${path.basename(p)}` };
|
|
if (loc !== null && loc >= n) return { stricat: 'site ' + loc + ' of ' + n };
|
|
const text = loc === null ? bucati.join(PLANT) : bucati.map((b, i) => (i === bucati.length - 1 ? b : b + (i === loc ? PLANT : `/*PAZA:${sursa}*/`))).join('');
|
|
const copie = path.join(path.dirname(p), `_plantat-conf-${process.pid}-${sursa}${loc === null ? '' : '-' + loc}.mjs`); fs.writeFileSync(copie, text);
|
|
return { copie, env: { [MODULE[d.sursa].env]: copie } };
|
|
}
|
|
/** the suite, run as a separate program (several plantings run side by side) */
|
|
function suitaAsync(env, { online: on = false, doar = null, artefacte = null } = {}) {
|
|
return new Promise((gata) => {
|
|
const args = [path.join(AICI, 'conformance.mjs'), '--impl', REF, '--json']; if (on) args.push('--online'); if (doar) args.push('--only', doar); if (artefacte) args.push('--artifacts', artefacte.join(','));
|
|
const c = spawn(process.execPath, args, { env: { ...process.env, ...env }, stdio: ['ignore', 'pipe', 'pipe'] }); let out = '', err = '';
|
|
c.stdout.on('data', (d) => { out += d; }); c.stderr.on('data', (d) => { err += d; });
|
|
c.on('close', () => { try { gata({ rezultate: JSON.parse(out).results }); } catch { gata({ eroare: (out + err).trim().split('\n').slice(-1)[0].slice(0, 200) }); } });
|
|
});
|
|
}
|
|
async function inParalel(sarcini, n = 4) { const rez = new Array(sarcini.length); let k = 0; await Promise.all(Array.from({ length: Math.min(n, sarcini.length) }, async () => { while (k < sarcini.length) { const i = k++; rez[i] = await sarcini[i](); } })); return rez; }
|
|
const ramase = () => [...Object.values(MODULE).map((m) => path.dirname(fileURLToPath(m.url))), path.dirname(VERIFY_PROOF)].flatMap((d) => fs.readdirSync(d).filter((f) => /^[._]plantat-conf-/.test(f)).map((f) => path.join(d, f)));
|
|
|
|
/**
|
|
* Every path of every offline vector that is not INVALID, removed or replaced by null, a string, a number, a list, an object, and given
|
|
* to the module verifiers directly. Left out, and said: the policy of a payment (a policy that cannot be defined is refused by
|
|
* definePolicy by throwing, by design: nothing is judged under it), the public task of a market job (each replay costs seconds), and
|
|
* the ai-trust bundle (its verifier runs the reference verifier, a program, several times per case).
|
|
* @returns {{ cazuri: number, aruncate: string[], acceptate: string[] }} acceptate = the paths (without array positions) whose damage was accepted
|
|
*/
|
|
async function malformate() {
|
|
const R = await incarca('risc'), C = await incarca('chitanta'), P = await incarca('piata'), B = await incarca('baza'), SC = await incarca('sidecar'), AR = await incarca('arbore'), L = await incarca('ledger'); const clona = (x) => JSON.parse(JSON.stringify(x));
|
|
const J = { 'payment-risk': (i) => R.judeca(i.policy, i.inputs).decision === 'ALLOW', 'payment-receipt': (i) => C.verificaChitanta(i.receipt, { policy: i.policy, inputs: i.inputs, tx: i.tx, receipt: i.txReceipt }).verdict === 'VALID', 'market-timeline': (i) => P.judeca(i).verdict === 'VALID',
|
|
'dataset-record': (i) => B.verificaInregistrare(i.record, i.commitment).ok, 'dataset-answer': (i) => B.verificaRaspuns(i.answer, i.commitment).verdict === 'VALID', 'dataset-consistency': (i) => B.verificaConsistentaVersiuni(i.proof, i.older, i.newer).ok,
|
|
// the verifiers enrolled after the first four (2026-10-05), called the way the adapter calls them; ai-trust is not here: each case would run the reference verifier several times
|
|
'audit-log': (i) => (i.head !== undefined ? SC.verificaFataDeCap(i.log, i.head, { semnatar: i.signer || null }) : SC.verificaJurnal(i.log)).ok,
|
|
'audit-inclusion': (i) => AR.verificaIncludere(i.proof, { cap: i.head || null, semnatar: i.signer || null }).ok,
|
|
'audit-consistency': (i) => AR.verificaConsistenta(i.proof, { vechi: i.older, nou: i.newer, semnatar: i.signer || null }).ok,
|
|
'agent-ledger': (i) => { const o = { policy: i.policy, policyHash: i.policyHash, anchors: i.anchors || [], notBefore: i.notBefore === undefined ? null : i.notBefore, revocations: i.revocations || [] }; if (i.maxTime !== undefined) o.maxTime = i.maxTime; return L.verifyLedger(i.ledger, o).ok; } };
|
|
const cai = (x, p = []) => (x !== null && typeof x === 'object' ? [p, ...Object.keys(x).flatMap((k) => cai(x[k], [...p, Array.isArray(x) ? Number(k) : k]))] : [p]).filter((q) => q.length);
|
|
const VAL = [null, 'x', 7, [], {}]; let cazuri = 0; const aruncate = [], acceptate = [];
|
|
for (const v of vectori) {
|
|
if (!J[v.artifact] || v.expected.verdict === 'INVALID' || v.requires.length) continue;
|
|
const inp = clona(v.input); if (v.artifact === 'market-timeline') delete inp.spec;
|
|
for (const p of cai(inp)) { if (p[0] === 'policy' && v.artifact.startsWith('payment-')) continue;
|
|
for (const op of ['remove', 0, 1, 2, 3, 4]) { const c = clona(inp); let o = c; for (const k of p.slice(0, -1)) o = o[k]; const k = p[p.length - 1];
|
|
if (op === 'remove') { if (Array.isArray(o)) o.splice(k, 1); else delete o[k]; } else { if (JSON.stringify(o[k]) === JSON.stringify(VAL[op])) continue; o[k] = clona(VAL[op]); }
|
|
cazuri++; try { if (J[v.artifact](c)) acceptate.push(v.artifact + ':' + p.filter((x) => typeof x !== 'number').join('.')); } catch (e) { aruncate.push(v.id + ' ' + p.join('.') + ' <- ' + (op === 'remove' ? 'removed' : JSON.stringify(VAL[op])) + ': ' + String(e.message).slice(0, 60)); } } }
|
|
}
|
|
return { cazuri, aruncate, acceptate };
|
|
}
|
|
|
|
if (!control && !peLoc) {
|
|
console.log('A. the rules and the vectors are derived, and nothing is typed in their place');
|
|
const D = await deriva();
|
|
cer(!D.probleme.length && !D.nemasurat.length, `the rules derive from the code of ${Object.keys(D.tabel.verifiers).length} verifiers: declared == marked == mapped (${TOATE.length} rules)`, [...D.probleme, ...D.nemasurat].join(' | '));
|
|
const rj = path.join(AICI, 'REGULI.json'); cer(fs.existsSync(rj) && fs.readFileSync(rj, 'utf8').replace(/\r\n/g, '\n') === JSON.stringify(D.tabel, null, 1) + '\n', 'REGULI.json is what the code derives now');
|
|
const g = spawnSync(process.execPath, [path.join(AICI, 'genereaza-vectori.mjs'), '--verifica'], { encoding: 'utf8' });
|
|
cer(g.status === 0, 'the vectors on disk are what the generator derives now, byte for byte', g.status === 0 ? g.stdout.trim().split('\n').slice(-1)[0] : (g.stdout + g.stderr).trim().split('\n').slice(-3).join(' | '));
|
|
const dev = path.join(AICI, '..', 'aere-proof-protocol', 'genereaza-vectori-kinds.mjs');
|
|
if (fs.existsSync(dev)) { const G = await import(pathToFileURL(dev).href); const lor = new Map(G.totiVectorii().map((x) => [x.nume, canon(x.plic)])); const ale = plicuriKinds();
|
|
cer(ale.length === lor.size && ale.every(([k, p]) => lor.get(k) === canon(p)), `the ${ale.length} kind envelopes are the published AIP-23 kind vectors (same producers, same fixed inputs)`); }
|
|
else rand('INFO', 'the comparison with the published AIP-23 kind vectors is made in the development repository (their generator is not part of this copy)');
|
|
|
|
console.log('B. coverage and single place');
|
|
const fara = neacoperite(TOATE); cer(!fara.length, `every rule a verifier names has a vector that expects it (${TOATE.length} of ${TOATE.length})`, fara.length ? 'without a vector: ' + fara.join(', ') : '');
|
|
const artifacte = Object.values(VERIFICATOARE).flatMap((v) => v.artifacts); const faraValid = artifacte.filter((x) => !vectori.some((v) => v.artifact === x && v.expected.verdict === 'VALID'));
|
|
cer(!faraValid.length, `every artifact kind has a VALID vector (${artifacte.length} kinds)`, faraValid.join(', '));
|
|
const kinds = plicuriKinds().map(([k]) => k); const kLipsa = kinds.filter((k) => !['valid', 'form', 'integrity'].every((s) => vectori.some((v) => v.id === `envelope/${k}.${s}`)));
|
|
cer(!kLipsa.length, `every AIP-23 proof kind (${kinds.length}) has its VALID vector and one INVALID vector per offline envelope rule`, kLipsa.join(', '));
|
|
let unLoc = 0, prod = 0, alte = 0; const rele = [];
|
|
for (const v of vectori) {
|
|
if (!v.base) { if (v.expected.verdict === 'INVALID') rele.push(v.id + ': INVALID without a base'); continue; }
|
|
const b = vectori.find((x) => x.id === v.base); if (!b) { rele.push(v.id + ': base missing'); continue; }
|
|
if (b.expected.verdict === 'INVALID') rele.push(v.id + ': its base is INVALID');
|
|
if (v.mutation && v.mutation.op) { let m; try { m = aplica(b.input, v.mutation); } catch (e) { rele.push(v.id + ': ' + e.message); continue; } if (canon(m) !== canon(v.input)) rele.push(v.id + ': the input is not the base with the one operation applied'); else if (v.expected.verdict === 'INVALID') unLoc++; else alte++; }
|
|
else if (v.mutation && v.mutation.producer) { const dif = difera(b.input, v.input); const afara = dif.filter((p) => !v.mutation.changed.some((c) => prefix(c, p))); const neatinse = v.mutation.changed.filter((c) => !dif.some((p) => prefix(c, p)));
|
|
if (!dif.length || afara.length || neatinse.length) rele.push(v.id + ': differs outside what it declares (' + afara.map((p) => p.join('.')).join(', ') + ') or declares paths that did not move (' + neatinse.map((p) => p.join('.')).join(', ') + ')'); else prod++; }
|
|
else rele.push(v.id + ': a base without a mutation');
|
|
}
|
|
const inv = vectori.filter((v) => v.expected.verdict === 'INVALID').length;
|
|
cer(!rele.length && unLoc + prod === inv, `every INVALID vector (${inv}) is its base with ONE operation applied (${unLoc}), or one change at the producer with the paths that moved declared (${prod}); ${alte} more vectors that are not INVALID are also one operation from their base`, rele.slice(0, 4).join(' | '));
|
|
const multe = vectori.filter((v) => v.expected.failed.length > 1); const fin = cuRegula('envelope.finality');
|
|
rand('INFO', `measured, not a gate: ${multe.length} vector(s) expect more than one rule (rules that guard the same field: ${multe.map((v) => v.id.split('/')[1]).join(', ')}); of the ${FINALITY_SITES.length} refusal sites of the reference verifier's finality level the vectors reach ${fin.length ? FINALITY_SITES.filter((x) => x.reachable).length : 0} (the others need an endpoint that lies)`);
|
|
|
|
console.log('C. two implementations, one runner');
|
|
const r1 = ruleazaSuita({ impl: REF, online });
|
|
if (r1.eroare) rand('STRICAT', 'the reference verifiers: ' + r1.eroare);
|
|
else { const p = numar(r1, 'PASS').length, deRulat = vectori.filter((v) => online || !v.requires.length).length;
|
|
cer(p === deRulat && !numar(r1, 'FAIL').length && !numar(r1, 'BROKEN').length && !numar(r1, 'NOT_COVERED').length, `the published verifiers behind the one verdict form pass ${p} of ${deRulat} vectors${online ? ' (testnet 28001 read)' : ' (offline)'}`, [...numar(r1, 'FAIL'), ...numar(r1, 'BROKEN')].slice(0, 3).map((x) => x.id + ' ' + x.detaliu).join(' | '));
|
|
if (!online) rand('NEMASURAT', `${numar(r1, 'SKIPPED_ONLINE').length} vectors need testnet 28001 and were not run (--online)`); }
|
|
const py = python();
|
|
if (!py) rand('NEMASURAT', 'the independent implementation needs Python 3 (AERE_CONFORMANCE_PYTHON=<command>)');
|
|
else { const r2 = ruleazaSuita({ impl: `${py} "${path.join(AICI, 'implementare-integritate.py')}"`, online: false });
|
|
if (r2.eroare) rand('STRICAT', 'the independent implementation: ' + r2.eroare);
|
|
else { const p = numar(r2, 'PASS').length, nc = numar(r2, 'NOT_COVERED').length; const revendica = vectori.filter((v) => r2.caps.artifacts.includes(v.artifact) && v.exercises.every((x) => r2.caps.rules.includes(x)) && !v.requires.length).length;
|
|
cer(p === revendica && p > 0 && !numar(r2, 'FAIL').length && !numar(r2, 'BROKEN').length && p + nc === vectori.length, `an independent implementation in another language (${r2.caps.name}) passes all ${p} vectors it claims; the other ${nc} are reported NOT COVERED, never passed`, [...numar(r2, 'FAIL'), ...numar(r2, 'BROKEN')].slice(0, 3).map((x) => x.id + ' ' + x.detaliu).join(' | ')); } }
|
|
|
|
console.log('D. a malformed artifact is refused by name, never thrown at the reader');
|
|
{ const m = await malformate();
|
|
cer(m.cazuri > 1000 && !m.aruncate.length, `${m.cazuri} malformed inputs (every path removed or replaced) given to the module verifiers: each got a verdict, none threw`, m.aruncate.slice(0, 3).join(' | ') + (m.aruncate.length > 3 ? ' ... ' + m.aruncate.length + ' in all' : ''));
|
|
const cai = [...new Set(m.acceptate)].sort(); const peFel = {}; for (const c of cai) { const k = c.split(':')[0]; peFel[k] = (peFel[k] || 0) + 1; }
|
|
rand('INFO', `measured, not a gate: ${m.acceptate.length} of them were still accepted, at ${cai.length} distinct paths (${Object.entries(peFel).map(([k, n]) => k + ' ' + n).join(', ')}): parts a verifier treats as optional (the transaction facts of a receipt, the envelope of a timeline) and fields no rule reads; --acceptate lists them`);
|
|
if (a.includes('--acceptate')) for (const c of cai) console.log(' ' + c); }
|
|
} else if (control) {
|
|
console.log('CONTROL: every rule planted out of its verifier, one at a time; the vectors that expect the rule, and only they, must fail');
|
|
// Under a planting the suite runs the vectors of every artifact kind that can load the planted verifier (the kinds whose vectors
|
|
// exercise a rule of that verifier); the other kinds never load it: the adapter reaches a planted copy only through that verifier's
|
|
// own variable, and a verifier that another one imports is imported from its real file.
|
|
const atinse = (verif) => [...new Set(vectori.filter((x) => x.exercises.some((r) => HARTA[r] && HARTA[r].verifier === verif)).map((x) => x.artifact))];
|
|
// a rule whose vectors are ALL chain vectors can be controlled only with --online; with --online a rule that has chain vectors is run with them
|
|
const toate = []; for (const [v, d] of Object.entries(VERIFICATOARE)) for (const [src, [id]] of Object.entries(d.reguli)) { const ai = vectori.filter((x) => x.expected.failed.includes(id)); toate.push({ v, src, id, numaiOnline: ai.length > 0 && ai.every((x) => x.requires.length), online: online && ai.some((x) => x.requires.length) }); }
|
|
const filtru = a.includes('--doar') ? a[a.indexOf('--doar') + 1] : null; const cazuri = toate.filter((c) => !filtru || c.id.startsWith(filtru));
|
|
if (filtru) console.log(` (FILTERED: --doar ${filtru}: ${cazuri.length} of ${toate.length} rules; the others are NOT controlled by this run)`);
|
|
const deRulat = cazuri.filter((c) => !c.numaiOnline || online); let bune = 0;
|
|
try {
|
|
const rez = await inParalel(deRulat.map((c) => async () => { const p = planteaza(c.v, c.src); if (p.stricat) return { stricat: p.stricat }; try { return await suitaAsync(p.env, { online: c.online, artefacte: atinse(c.v) }); } finally { fs.rmSync(p.copie, { force: true }); } }));
|
|
deRulat.forEach((c, k) => {
|
|
const r = rez[k]; const asteptate = cuRegula(c.id, vectori.filter((v) => c.online || !v.requires.length)).sort();
|
|
if (r.stricat || r.eroare) return rand('STRICAT', `${c.id}: ${r.stricat || r.eroare}`);
|
|
const cazute = r.rezultate.filter((x) => x.stare === 'FAIL').map((x) => x.id).sort(); const stricate = r.rezultate.filter((x) => x.stare === 'BROKEN').map((x) => x.id);
|
|
const ok = asteptate.length > 0 && cazute.join() === asteptate.join() && !stricate.length; if (ok) bune++;
|
|
rand(ok ? 'VERDE' : stricate.length ? 'STRICAT' : 'ROSU', `${c.id} planted out: ${cazute.length} vector(s) fail` + (ok ? `, exactly the ${asteptate.length} that expect it` : `; expected exactly [${asteptate.join(', ')}], failed [${cazute.join(', ')}]` + (stricate.length ? `, broken [${stricate.join(', ')}]` : '')));
|
|
});
|
|
} finally { for (const f of ramase()) fs.rmSync(f, { force: true }); }
|
|
for (const c of cazuri.filter((x) => x.numaiOnline && !online)) rand('NEMASURAT', `${c.id}: its vectors need testnet 28001 (--online)`);
|
|
if (filtru) rand('NEMASURAT', `${toate.length - cazuri.length} rules left out by --doar`);
|
|
console.log('CONTROL of the instruments');
|
|
{ // the runner can fail: an implementation that calls everything VALID
|
|
const stub = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'aere-conf-stub-')), 'stub.mjs');
|
|
fs.writeFileSync(stub, `import fs from 'node:fs'; const a = process.argv.slice(2); const V = JSON.stringify({ verdict: 'VALID', failed: [], levels: [{ level: 'integrity', state: 'PASSED' }, { level: 'authenticity', state: 'NOT_MEASURED' }, { level: 'anchoring', state: 'NOT_MEASURED' }, { level: 'finality', state: 'NOT_MEASURED' }] });\nif (a.includes('--capabilities')) console.log(JSON.stringify({ artifacts: ${JSON.stringify(Object.values(VERIFICATOARE).flatMap((v) => v.artifacts))}, rules: ${JSON.stringify(TOATE)}, online: false, batch: true }));\nelse for (const x of JSON.parse(fs.readFileSync(a[a.indexOf('--batch') + 1], 'utf8'))) console.log(V);\n`);
|
|
const r = ruleazaSuita({ impl: `node "${stub}"` }); fs.rmSync(path.dirname(stub), { recursive: true, force: true });
|
|
const off = vectori.filter((v) => !v.requires.length); const trebuie = off.filter((v) => !(v.expected.verdict === 'VALID' && Object.entries(v.expected.levels).every(([l, s]) => s === (l === 'integrity' ? 'PASSED' : 'NOT_MEASURED')))).map((v) => v.id).sort();
|
|
const cazute = r.eroare ? [] : numar(r, 'FAIL').map((x) => x.id).sort();
|
|
cer(!r.eroare && cazute.join() === trebuie.join() && trebuie.length > 0, `the runner fails an implementation that calls everything VALID on ${trebuie.length} of ${off.length} offline vectors (every one that is not plainly VALID)`, r.eroare || ''); }
|
|
{ const lista = vectori.filter((v) => !v.expected.failed.includes('dataset.order')); const f = neacoperite(TOATE, lista);
|
|
cer(f.join() === 'dataset.order', 'the coverage gate goes red when the vector of one rule is taken away (dataset.order)', f.join(', ')); }
|
|
{ // a rule added to a verifier and not mapped in the suite is refused by the derivation
|
|
const p = fileURLToPath(MODULE.baza.url); const copie = path.join(path.dirname(p), `_plantat-conf-${process.pid}-regula-noua.mjs`); const src = fs.readFileSync(p, 'utf8'); const anc = "'consistenta', 'esantion'];";
|
|
try { if (src.split(anc).length !== 2) rand('STRICAT', 'the anchor of the new-rule planting is not in the dataset module');
|
|
else { fs.writeFileSync(copie, src.replace(anc, "'consistenta', 'esantion', 'noua'];") + '\n/*PAZA:noua*/\n'); const D = await deriva({ cai: { baza: copie } });
|
|
cer(D.probleme.length === 1 && /names rule noua and the suite does not map it/.test(D.probleme[0]), 'a rule added to a verifier and not mapped in the suite is refused by the derivation (a new rule without a vector cannot pass unseen)', D.probleme.join(' | ')); }
|
|
} finally { fs.rmSync(copie, { force: true }); } }
|
|
{ // the malformed-input row can go red: the market judge without its form check and without its catch throws again, and the row counts it
|
|
const p = fileURLToPath(MODULE.piata.url); const copie = path.join(path.dirname(p), `_plantat-conf-${process.pid}-arunca.mjs`); const src = fs.readFileSync(p, 'utf8'); const anc = "catch (e) { return { verdict: 'INVALID', motive: ['malformed timeline: '";
|
|
try { if (src.split(anc).length !== 2) rand('STRICAT', 'the anchor of the throwing planting is not in the market judge');
|
|
else { fs.writeFileSync(copie, src.replace(anc, "catch (e) { throw e; return { verdict: 'INVALID', motive: ['malformed timeline: '").split('/*PAZA:forma*/').join(PLANT)); process.env[MODULE.piata.env] = copie; const m = await malformate(); delete process.env[MODULE.piata.env];
|
|
cer(m.aruncate.length > 0 && m.aruncate.every((x) => x.startsWith('market-timeline/')), `the malformed-input row goes red when a judge throws again (the market judge without its form check and its catch: ${m.aruncate.length} of ${m.cazuri} inputs thrown)`); }
|
|
} finally { delete process.env[MODULE.piata.env]; fs.rmSync(copie, { force: true }); } }
|
|
const r = ramase(); cer(!r.length, 'no planted copy is left behind', r.join(', '));
|
|
console.log(`CONTROL: ${bune} of ${cazuri.length} rules proven load-bearing through their vectors`);
|
|
} else {
|
|
console.log('MEASUREMENT (not a gate): each check site planted out alone, the vectors of its artifact kinds run');
|
|
const locuri = []; for (const [v, d] of Object.entries(VERIFICATOARE)) { if (d.sursa === 'verify-proof') continue; const src = fs.readFileSync(fileURLToPath(MODULE[d.sursa].url), 'utf8');
|
|
for (const [s, [id]] of Object.entries(d.reguli)) { const n = src.split(`/*PAZA:${s}*/`).length - 1; for (let k = 0; k < n; k++) locuri.push({ v, src: s, id, k, n, doar: d.artifacts[0].split('-')[0] }); } }
|
|
let atinse = 0;
|
|
try {
|
|
const rez = await inParalel(locuri.map((c) => async () => { const p = planteaza(c.v, c.src, c.k); if (p.stricat) return { stricat: p.stricat }; try { return await suitaAsync(p.env, { doar: c.doar }); } finally { fs.rmSync(p.copie, { force: true }); } }));
|
|
const peRegula = {};
|
|
locuri.forEach((c, k) => { const r = rez[k]; const cazute = r.rezultate ? r.rezultate.filter((x) => x.stare === 'FAIL').length : 0; if (cazute) atinse++; (peRegula[c.id] || (peRegula[c.id] = [])).push(r.stricat || r.eroare ? '?' : cazute ? 'reached' : 'NOT reached'); });
|
|
for (const [id, l] of Object.entries(peRegula)) console.log(` ${id}: ${l.filter((x) => x === 'reached').length} of ${l.length} site(s) reached` + (l.includes('NOT reached') ? ' (sites ' + l.map((x, i) => (x === 'NOT reached' ? i + 1 : null)).filter(Boolean).join(', ') + ' have no vector)' : ''));
|
|
} finally { for (const f of ramase()) fs.rmSync(f, { force: true }); }
|
|
console.log(`SITES: the vectors reach ${atinse} of ${locuri.length} check sites of the module verifiers (the reference verifier's own sites are counted in REGULI.json)`);
|
|
process.exit(0);
|
|
}
|
|
console.log(`\nconformance suite${control ? ' (control)' : ''}: ${verzi} VERDE, ${rosii} ROSU, ${nemas} NEMASURAT/STRICAT`);
|
|
process.exitCode = rosii ? 1 : nemas ? 2 : 0;
|