← Source tree

web_demo/test_core.cjs

"use strict";
// Node-only logic/static checks. Not a DOM, rendering, or browser-download test.
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const { createHash } = require("node:crypto");
const { Session, sha256 } = require("./core.js");
require("./fixture.js");
const fixture = globalThis.SYNTHETIC_REPORT;
delete globalThis.SYNTHETIC_REPORT;
const clone = x => JSON.parse(JSON.stringify(x));
const digest = x => createHash("sha256").update(x).digest("hex");
const pending = () => { let resolve; const promise = new Promise(r => { resolve = r; }); return { promise, resolve }; };

test("Web Crypto SHA-256 matches Node for ASCII and Unicode", async () => {
  for (const text of ["abc", "Ўзбекча — 00001"]) assert.equal(await sha256(text), digest(text));
});
test("fixture is exactly the stored Python model output", () => {
  assert.deepEqual(fixture, JSON.parse(fs.readFileSync(path.join(__dirname, "../sample_output.json"), "utf8")));
});
test("original input byte hash is retained", () => {
  assert.equal(fixture.source_sha256, digest(fs.readFileSync(path.join(__dirname, "../sample_input.json"))));
});
test("invalid row counts and review counts are rejected", () => {
  for (const field of ["input_rows", "output_rows", "review_rows"]) {
    const bad = clone(fixture); bad[field] += 1;
    assert.throws(() => new Session(bad), /Invalid demonstration report/);
  }
});
test("malformed source records, flags and row numbers are rejected", () => {
  for (const change of [r => { r.original = []; }, r => { r.flags = "flag"; },
    r => { r.row_number = 7; }, r => { r.needs_review = "false"; }]) {
    const bad = clone(fixture); change(bad.rows[0]);
    assert.throws(() => new Session(bad), /Invalid demonstration report/);
  }
});
test("export and confirmation are refused before review", () => {
  const s = new Session(fixture);
  assert.throws(() => s.export(), /confirmation required/);
  assert.throws(() => s.confirm("fake"), /exact current digest/);
});
test("review retains every row, duplicate ID, ambiguous amount and abstention", async () => {
  const viewed = await new Session(fixture).review();
  assert.deepEqual(viewed.document.review, fixture);
  assert.equal(viewed.document.review.rows.length, 6);
  assert.deepEqual(viewed.document.review.rows.map(r => r.original.record_id), ["00001", "00002", "00003", "00003", "00004", "00005"]);
  assert.equal(viewed.document.review.rows[4].original.amount, "1,234.50");
  assert.equal(viewed.document.review.rows[5].ml.status, "not_scored");
});
test("digest binds exact complete exported UTF-8 document", async () => {
  const s = new Session(fixture), v = await s.review();
  assert.equal(v.state, "awaiting_demo_confirmation");
  assert.equal(s.confirm(v.digest), "demo_confirmed");
  assert.equal(digest(s.export()), v.digest);
  assert.deepEqual(JSON.parse(s.export()).review, fixture);
});
test("wrong digest cannot unlock export", async () => {
  const s = new Session(fixture); await s.review();
  assert.throws(() => s.confirm("0".repeat(64)), /exact current digest/);
  assert.throws(() => s.export(), /confirmation required/);
});
test("mutation of returned review cannot change export", async () => {
  const s = new Session(fixture), v = await s.review();
  v.document.review.rows.pop(); v.document.review.rows[0].original.record_id = "changed";
  s.confirm(v.digest); assert.deepEqual(JSON.parse(s.export()).review, fixture);
});
test("mutation of constructor input cannot change snapshot", async () => {
  const input = clone(fixture), s = new Session(input);
  input.rows.length = 0;
  assert.deepEqual((await s.review()).document.review, fixture);
});
test("another review invalidates earlier confirmation", async () => {
  const s = new Session(fixture), first = await s.review(); s.confirm(first.digest);
  await s.review(); assert.throws(() => s.export(), /confirmation required/);
});
test("reset invalidates digest and export permission", async () => {
  const s = new Session(fixture), v = await s.review(); s.confirm(v.digest);
  assert.equal(s.reset(), "unreviewed");
  assert.throws(() => s.export(), /confirmation required/);
  assert.throws(() => s.confirm(v.digest), /exact current digest/);
});
test("reset during hashing cannot resurrect a stale review", async () => {
  const p = pending(), s = new Session(fixture, () => p.promise);
  const review = s.review(); s.reset(); p.resolve("stale");
  await assert.rejects(review, /Review cancelled/);
  assert.throws(() => s.confirm("stale"), /exact current digest/);
  assert.throws(() => s.export(), /confirmation required/);
});
test("overlapping reviews cannot replace the newest digest", async () => {
  const a = pending(), b = pending(); let call = 0;
  const s = new Session(fixture, () => (++call === 1 ? a.promise : b.promise));
  const first = s.review(), second = s.review();
  b.resolve("newest"); await second; a.resolve("older");
  await assert.rejects(first, /Review cancelled/);
  assert.throws(() => s.confirm("older"), /exact current digest/);
  s.confirm("newest"); assert.deepEqual(JSON.parse(s.export()).review, fixture);
});
test("hash failure fails closed", async () => {
  const s = new Session(fixture, async () => { throw new Error("Web Crypto unavailable"); });
  await assert.rejects(s.review(), /Web Crypto unavailable/);
  assert.throws(() => s.export(), /confirmation required/);
  assert.throws(() => s.confirm("fake"), /exact current digest/);
});
test("static page wiring and default disabled gates are present", () => {
  const html = fs.readFileSync(path.join(__dirname, "index.html"), "utf8");
  for (const id of ["rows", "report", "details", "status", "state", "digest", "review", "reset", "confirm", "export"]) {
    assert.equal((html.match(new RegExp('id="' + id + '"', "g")) || []).length, 1);
  }
  for (const id of ["confirm", "export"]) assert.match(html, new RegExp('<button[^>]*id="' + id + '"[^>]*disabled'));
  assert.ok(html.indexOf('src="fixture.js"') < html.indexOf('src="core.js"'));
  assert.ok(html.indexOf('src="core.js"') < html.indexOf('src="app.js"'));
  assert.match(html, /connect-src 'none'/);
  assert.match(html, /have not yet been verified/);
});
test("browser source uses local assets and no network or HTML injection APIs", () => {
  const files = ["index.html", "app.js", "core.js", "fixture.js", "style.css", "inference.js", "model.js"];
  for (const file of files) {
    const text = fs.readFileSync(path.join(__dirname, file), "utf8");
    assert.doesNotMatch(text, /https?:\/\/|\bfetch\s*\(|XMLHttpRequest|WebSocket|sendBeacon|innerHTML|outerHTML|document\.write|\beval\s*\(/);
  }
});