Files
nekonest-cloud/tests/privacy-inventory.test.mjs

71 lines
3.6 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import {
CONTROL_PLANE_EXCLUSIONS,
DATA_INVENTORY,
INVENTORIED_TABLES,
} from "../app/privacy/data-inventory.ts";
test("keeps the public data inventory complete against the D1 schema", async () => {
const schema = await readFile(new URL("../db/schema.ts", import.meta.url), "utf8");
const schemaTables = [...schema.matchAll(/sqliteTable\(\s*["']([^"']+)["']/g)]
.map((match) => match[1])
.sort();
const inventoryTables = [...INVENTORIED_TABLES].sort();
assert.equal(new Set(schemaTables).size, schemaTables.length, "schema table names must be unique");
assert.equal(new Set(inventoryTables).size, inventoryTables.length, "inventory table names must be unique");
assert.deepEqual(inventoryTables, schemaTables);
});
test("publishes purpose, retention boundary, and user control for every data group", () => {
assert.ok(DATA_INVENTORY.length >= 7);
for (const group of DATA_INVENTORY) {
assert.ok(group.summary.length > 10, `${group.id} needs a summary`);
assert.ok(group.purpose.length > 10, `${group.id} needs a purpose`);
assert.ok(group.retention.length > 10, `${group.id} needs a retention boundary`);
assert.ok(group.userControl.length > 10, `${group.id} needs a user control statement`);
assert.ok(group.examples.length > 0, `${group.id} needs examples`);
assert.ok(group.tables.length > 0, `${group.id} needs table coverage`);
}
const dormant = DATA_INVENTORY.find((group) => group.id === "dormant-billing");
assert.equal(dormant?.status, "dormant");
assert.match(dormant?.purpose ?? "", /服务端拒绝写入/);
assert.ok(CONTROL_PLANE_EXCLUSIONS.some((entry) => entry.item === "项目文件和任意磁盘目录内容"));
assert.match(
CONTROL_PLANE_EXCLUSIONS.find((entry) => entry.item.includes("明文配对码"))?.boundary ?? "",
/短暂处理,不持久化到 D1/,
);
});
test("keeps the public privacy page honest about unfinished deletion and compliance work", async () => {
const [page, shell, readiness, docs, plan] = await Promise.all([
readFile(new URL("../app/privacy/page.tsx", import.meta.url), "utf8"),
readFile(new URL("../app/components/Shells.tsx", import.meta.url), "utf8"),
readFile(new URL("../app/readiness/page.tsx", import.meta.url), "utf8"),
readFile(new URL("../docs/data-inventory.md", import.meta.url), "utf8"),
readFile(new URL("../docs/implementation-plan.md", import.meta.url), "utf8"),
]);
assert.match(page, /这不是一份拿模板拼出的最终隐私政策/);
assert.match(page, /隐私门禁还没有通过/);
assert.match(page, /不会宣称“已经合规”/);
assert.match(page, /DATA_INVENTORY\.map/);
assert.match(shell, /href="\/privacy"/);
assert.match(readiness, /查看公测数据说明/);
assert.match(docs, /以上项目完成前.*P0 门禁继续保持阻止/);
assert.match(plan, /\[x\].*D1 schema 完整对齐的控制平面数据清单/);
assert.doesNotMatch(`${page}\n${docs}`, /隐私门禁已经通过|已经完成合规/);
});
test("documents retention facts that are enforced by the current pairing code", async () => {
const repository = await readFile(new URL("../db/repository.ts", import.meta.url), "utf8");
assert.match(repository, /const expiresAt = isoAfterMinutes\(10\)/);
assert.match(repository, /pairing_claim_rate_limits WHERE window_start < \?/);
assert.match(repository, /nowMilliseconds - 24 \* 60 \* 60_000/);
assert.match(repository, /pairing_claim_attempts WHERE created_at < \?/);
assert.match(repository, /nowMilliseconds - 30 \* 24 \* 60 \* 60_000/);
});