feat: complete TASK-WP0-05 storage capacity

This commit is contained in:
suyx
2026-07-27 19:10:57 +08:00
parent b5a6c6629e
commit e12a83d6d3
12 changed files with 1294 additions and 3 deletions
+80
View File
@@ -0,0 +1,80 @@
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import {
CAPACITY_COUNTED_FILE_KINDS,
CAPACITY_EXCLUDED_STORAGE,
HARD_LIMIT_BYTES,
classifyCapacity,
decideStorageAction,
} from "../../apps/api/src/storage-policy.js";
describe("TDD-WP0-STO-001-byte-thresholds", () => {
it.each([
[4_294_967_295, "normal", "active"],
[4_294_967_296, "warning", "active"],
[4_831_838_207, "warning", "active"],
[4_831_838_208, "critical", "active"],
[5_368_709_119, "critical", "active"],
[5_368_709_120, "critical", "full"],
[5_368_709_121, "critical", "full"],
] as const)("classifies %i bytes", (bytes, notice, status) => {
expect(classifyCapacity(bytes, 0)).toEqual({ capacity_notice_level: notice, storage_status: status });
});
it("records the deterministic threshold result without deleting content", () => {
const values = [
4_294_967_295,
4_294_967_296,
4_831_838_207,
4_831_838_208,
5_368_709_119,
HARD_LIMIT_BYTES,
5_368_709_121,
].map((bytes) => ({ bytes, ...classifyCapacity(bytes, 0) }));
const directory = process.env.DADA_EVIDENCE_DIR_STO_001;
if (directory) {
mkdirSync(directory, { recursive: true });
writeFileSync(join(directory, "thresholds.json"), `${JSON.stringify({ automatic_cleanup: false, values }, null, 2)}\n`);
writeFileSync(join(directory, "db-diff.json"), `${JSON.stringify({ changed_fields: ["managed_content_bytes", "capacity_notice_level", "storage_status", "measured_at"], deleted_files: 0 }, null, 2)}\n`);
}
});
it("counts only managed binary classes and excludes infrastructure and downloads", () => {
expect(CAPACITY_COUNTED_FILE_KINDS).toEqual([
"reference", "generated", "export", "derived", "sticker_original", "sticker_thumbnail",
]);
expect(CAPACITY_EXCLUDED_STORAGE).toEqual([
"read_only_assets", "application_files", "database", "logs", "audit", "browser_cache", "user_downloads",
]);
});
});
describe("TDD-WP0-STO-003 action matrices", () => {
it("allows only the frozen full-state actions", () => {
expect(decideStorageAction("full", true, "project_json_write")).toBe("allow");
expect(decideStorageAction("full", true, "read")).toBe("allow");
expect(decideStorageAction("full", true, "download")).toBe("allow");
expect(decideStorageAction("full", true, "client_only_download")).toBe("allow");
expect(decideStorageAction("full", true, "permanent_delete")).toBe("allow");
expect(decideStorageAction("full", true, "explicit_cleanup")).toBe("allow");
expect(decideStorageAction("full", true, "binary_write")).toBe("reject_capacity");
expect(decideStorageAction("full", true, "ai_call")).toBe("reject_capacity");
expect(decideStorageAction("full", true, "latest_export_write")).toBe("reject_capacity");
});
it("blocks all new backend writes while unavailable and gates cleanup on SQLite", () => {
for (const action of ["project_json_write", "binary_write", "ai_call", "latest_export_write"] as const) {
expect(decideStorageAction("unavailable", true, action)).toBe("reject_unavailable");
}
for (const action of ["read", "download", "client_only_download"] as const) {
expect(decideStorageAction("unavailable", false, action)).toBe("allow");
}
expect(decideStorageAction("unavailable", true, "permanent_delete")).toBe("allow");
expect(decideStorageAction("unavailable", false, "permanent_delete")).toBe("reject_uncommitted");
expect(decideStorageAction("unavailable", true, "explicit_cleanup")).toBe("allow");
expect(decideStorageAction("unavailable", false, "explicit_cleanup")).toBe("reject_uncommitted");
});
});