134 lines
5.4 KiB
TypeScript
134 lines
5.4 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import { repoPathExists } from "~/utils/testing/repoPaths";
|
|
|
|
type FeatureManifest = {
|
|
id: string;
|
|
classification: string;
|
|
pruning: {
|
|
action: string;
|
|
};
|
|
};
|
|
|
|
const rootDir = process.cwd();
|
|
const docs = {
|
|
migrationStatus: "docs/architecture-migration-status.md",
|
|
featureInventory: "docs/feature-inventory.md",
|
|
manifestReadme: "docs/feature-manifests/README.md",
|
|
agentEntrypoints: "docs/agent-entrypoints.md",
|
|
} as const;
|
|
|
|
const requiredArchitectureClosureSources = [
|
|
"docs/architecture-migration-status.md",
|
|
"docs/feature-inventory.md",
|
|
"docs/feature-manifests/",
|
|
];
|
|
|
|
const documentedS04Evidence = ["apps/web/app/utils/build/bundleScan.ts", "apps/web/app/utils/build/manualChunks.ts"];
|
|
|
|
const requiredS04Evidence = [...documentedS04Evidence, "apps/web/app/utils/build/viteConfigIntegration.test.ts"];
|
|
|
|
function readProjectFile(filePath: string) {
|
|
return fs.readFileSync(path.join(rootDir, filePath), "utf8");
|
|
}
|
|
|
|
function readManifest(fileName: string) {
|
|
return JSON.parse(readProjectFile(path.join("docs/feature-manifests", fileName))) as FeatureManifest;
|
|
}
|
|
|
|
describe("Architecture Closure closure documentation", () => {
|
|
it("keeps architecture status anchored to boundaries, pruning manifests and S04 evidence", () => {
|
|
const status = readProjectFile(docs.migrationStatus);
|
|
|
|
expect(status).toContain("## Architecture Closure final closure status");
|
|
expect(status).toMatch(/handoff contract/i);
|
|
expect(status).toContain("apps/api");
|
|
expect(status).toContain("apps/worker");
|
|
expect(status).toContain("packages/db");
|
|
expect(status).toContain("packages/mail");
|
|
expect(status).toContain("docs/feature-manifests/");
|
|
expect(status).toContain("docs/feature-inventory.md");
|
|
expect(status).toContain("core`, `plugin-candidate`, `remove-candidate");
|
|
documentedS04Evidence.forEach((evidencePath) => {
|
|
expect(status, evidencePath).toContain(evidencePath);
|
|
});
|
|
});
|
|
|
|
it("keeps the feature inventory aligned with Architecture Closure status and pruning actions", () => {
|
|
const inventory = readProjectFile(docs.featureInventory);
|
|
|
|
expect(inventory).toContain("docs/architecture-migration-status.md");
|
|
expect(inventory).toContain("docs/feature-manifests/");
|
|
expect(inventory).toContain("`core` bleibt die aktive Boilerplate-Basis");
|
|
expect(inventory).toContain("`product-core` beschreibt Blueprint-, Plan-, Custom-Plan-, Tenant-Override- und Tenant-Rollen-Gates");
|
|
expect(inventory).toContain("`plugin-candidate` beschreibt nur noch Extraktionsvertraege");
|
|
expect(inventory).toContain("`remove-candidate` beschreibt Loesch- oder Review-Vertraege");
|
|
expect(inventory).toContain("API-Split");
|
|
expect(inventory).toContain("Worker/API/Mail-Split");
|
|
});
|
|
|
|
it("documents the executor-facing manifest contract and source alignment", () => {
|
|
const readme = readProjectFile(docs.manifestReadme);
|
|
|
|
requiredArchitectureClosureSources.forEach((sourcePath) => {
|
|
expect(readme, sourcePath).toContain(sourcePath);
|
|
});
|
|
expect(readme).toContain("current API, worker, package, pruning, and bundle handoff status");
|
|
expect(readme).toContain("machine-readable Core/Plugin/Removal contract");
|
|
expect(readme).toContain("ownership, dependencies, configuration, provider, worker, UAT, and pruning boundaries");
|
|
expect(readme).toContain("pruning.action");
|
|
expect(readme).toContain("keep`, `extract-plugin`, `remove`, or `review");
|
|
});
|
|
|
|
it("points agents at every canonical Architecture Closure planning source before feature work", () => {
|
|
const entrypoints = readProjectFile(docs.agentEntrypoints);
|
|
|
|
expect(entrypoints).toContain("Architecture Closure closure/status contract");
|
|
expect(entrypoints).toContain("docs/architecture-migration-status.md");
|
|
expect(entrypoints).toContain("docs/feature-inventory.md");
|
|
expect(entrypoints).toContain("docs/feature-manifests/*.json");
|
|
expect(entrypoints).toContain("before planning implementation, extraction, or removal work");
|
|
});
|
|
|
|
it("keeps the complete S04 bundle evidence set present for future verification", () => {
|
|
requiredS04Evidence.forEach((evidencePath) => {
|
|
expect(repoPathExists(evidencePath), evidencePath).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Architecture Closure feature manifest pruning contract", () => {
|
|
const manifestDir = path.join(rootDir, "docs/feature-manifests");
|
|
const manifestFiles = fs
|
|
.readdirSync(manifestDir)
|
|
.filter((file) => file.endsWith(".json") && file !== "schema.json")
|
|
.sort();
|
|
|
|
it("keeps every non-schema feature manifest JSON-parseable", () => {
|
|
expect(manifestFiles.length).toBeGreaterThan(0);
|
|
|
|
manifestFiles.forEach((file) => {
|
|
expect(() => JSON.parse(readProjectFile(path.join("docs/feature-manifests", file))), file).not.toThrow();
|
|
});
|
|
});
|
|
|
|
it("exposes the canonical core/plugin/removal pruning actions", () => {
|
|
expect(readManifest("core.json")).toMatchObject({
|
|
id: "core",
|
|
classification: "core",
|
|
pruning: { action: "keep" },
|
|
});
|
|
expect(readManifest("plugin-candidates.json")).toMatchObject({
|
|
id: "plugin-candidates",
|
|
classification: "plugin-candidate",
|
|
pruning: { action: "extract-plugin" },
|
|
});
|
|
expect(readManifest("remove-candidates.json")).toMatchObject({
|
|
id: "remove-candidates",
|
|
classification: "remove-candidate",
|
|
pruning: { action: "remove" },
|
|
});
|
|
});
|
|
});
|