Project Files
src / piiModel.test.ts
// Smoke test for the optional ML PII model.
//
// Skipped by default — `RUN_MODEL_TESTS=1 npm test` to enable. The first
// run downloads ~280 MB of weights and takes minutes; subsequent runs reuse
// the HuggingFace transformers.js cache (`~/.cache/huggingface/...`).
import { test } from "node:test";
import { strict as assert } from "node:assert";
import { detectWithModel } from "./piiModel.ts";
const RUN = process.env.RUN_MODEL_TESTS === "1";
test("piiModel: skip without flag", { skip: !RUN }, async () => {
// This whole file is no-op without RUN_MODEL_TESTS=1.
// The skip is wired per-test so the file still parses and type-checks in CI.
});
test(
"piiModel: detects French names with detectNames option",
{ skip: !RUN, timeout: 5 * 60_000 },
async () => {
const text = "Jean Dupont habite à Paris depuis 2020.";
const spans = await detectWithModel(text, { detectNames: true });
assert.ok(
spans.some((s) => s.type === "NOM" && /Jean|Dupont/.test(s.value)),
`expected a NOM span containing Jean/Dupont, got: ${JSON.stringify(spans)}`,
);
},
);
test(
"piiModel: returns empty when no category is enabled",
{ skip: !RUN, timeout: 60_000 },
async () => {
const spans = await detectWithModel("Jean Dupont à Paris.", {});
assert.deepEqual(spans, []);
},
);
test(
"piiModel: addresses gated behind detectAddresses",
{ skip: !RUN, timeout: 5 * 60_000 },
async () => {
const text = "Habite à Lyon, 12 rue Garibaldi.";
const off = await detectWithModel(text, { detectNames: true });
assert.ok(
!off.some((s) => s.type === "ADDRESS"),
"ADDRESS must not appear when detectAddresses is off",
);
const on = await detectWithModel(text, { detectAddresses: true });
assert.ok(
on.some((s) => s.type === "ADDRESS"),
`expected at least one ADDRESS span with detectAddresses, got ${JSON.stringify(on)}`,
);
},
);