71 lines
3.6 KiB
JavaScript
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/);
|
|
});
|