Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/the-system/__tests__/documents.test.ts

Raw
import assert from "node:assert/strict";
import {
	mkdirSync,
	mkdtempSync,
	rmSync,
	symlinkSync,
	writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, it } from "vitest";
import {
	discoverDocumentBundles,
	discoverProjectRoot,
	indexBundleDocuments,
	PHASE_BYTE_LIMIT,
	parseAuthoredDocument,
	parseDocumentId,
	parseSystemConfig,
	requireProjectRoot,
	resolveGovernedPath,
	SystemDocumentError,
	systemConfigForInitialization,
	validateBundleCandidate,
	validateDocumentPath,
} from "../documents.ts";

const roots: string[] = [];
const tree = () => {
	const root = mkdtempSync(path.join(tmpdir(), "the-system-documents-"));
	roots.push(root);
	return root;
};
const dir = (root: string, ...parts: string[]) => {
	const target = path.join(root, ...parts);
	mkdirSync(target, { recursive: true });
	return target;
};
const write = (root: string, relative: string, content: string) => {
	const target = path.join(root, relative);
	dir(path.dirname(target));
	writeFileSync(target, content);
};
const source = (
	directory: string,
	id: string,
	slug: string,
	frontmatter: string,
) => ({
	path: `.system/${directory}/${id}-${slug}/index.md`,
	markdown: `---\n${frontmatter}\n---\n\n# ${id}\n`,
});
const spec = (id = "PX-SPEC-ABCDEFGH") =>
	`---\nid: ${id}\ntype: spec\ntitle: Contract\n---\n\n# ${id}\n`;

afterEach(() => {
	for (const root of roots.splice(0))
		rmSync(root, { recursive: true, force: true });
});

describe("project root and schema-0 config", () => {
	it("resolves the nearest .system root", () => {
		const root = tree();
		dir(root, ".system");
		const nested = dir(root, "nested");
		assert.equal(discoverProjectRoot(nested), root);
		dir(nested, ".system");
		assert.equal(discoverProjectRoot(nested), nested);
		assert.equal(resolveGovernedPath(root, "x/../x"), path.join(root, "x"));
		assert.throws(
			() => resolveGovernedPath(root, "../outside"),
			SystemDocumentError,
		);
	});

	it("returns undefined without a .system directory", () => {
		assert.equal(discoverProjectRoot(tree()), undefined);
		assert.throws(() => requireProjectRoot(tree()), SystemDocumentError);
	});

	it("ignores .system files and symbolic links while walking", () => {
		const root = tree();
		dir(root, ".system");
		dir(root, "elsewhere");
		const fileDirectory = dir(root, "file");
		writeFileSync(path.join(fileDirectory, ".system"), "");
		const linkDirectory = dir(root, "link");
		symlinkSync(
			path.join(root, "elsewhere"),
			path.join(linkDirectory, ".system"),
			"dir",
		);
		assert.equal(discoverProjectRoot(fileDirectory), root);
		assert.equal(discoverProjectRoot(linkDirectory), root);
	});

	it("accepts schema 0 and prepares mismatched config for initialization", () => {
		assert.deepEqual(
			parseSystemConfig(
				'{"prefix":"PX","schemaVersion":0,"title":"Project X","currentPhase":"PX-PHASE-PHASE001"}',
			),
			{
				prefix: "PX",
				schemaVersion: 0,
				title: "Project X",
				currentPhase: "PX-PHASE-PHASE001",
			},
		);
		assert.deepEqual(parseSystemConfig('{"prefix":"PX","schemaVersion":0}'), {
			prefix: "PX",
			schemaVersion: 0,
		});
		assert.throws(
			() => parseSystemConfig('{"prefix":"PX","schemaVersion":4}'),
			/does not match 0; run \/system init/,
		);
		assert.deepEqual(
			systemConfigForInitialization(
				'{"prefix":"PX","schemaVersion":4,"title":"Project X"}',
			),
			{
				config: { prefix: "PX", schemaVersion: 0, title: "Project X" },
				sourceSchemaVersion: 4,
			},
		);
	});
});

describe("schema-0 identity and metadata", () => {
	it("parses uppercase token IDs and rejects short numeric IDs", () => {
		assert.deepEqual(parseDocumentId("PX-SPEC-AZ3_P9QK", "PX"), {
			id: "PX-SPEC-AZ3_P9QK",
			prefix: "PX",
			type: "spec",
			token: "AZ3_P9QK",
		});
		assert.throws(
			() => parseDocumentId("PX-SPEC-001", "PX"),
			SystemDocumentError,
		);
		assert.throws(
			() => parseDocumentId("PX-SPEC-az3_p9qk", "PX"),
			SystemDocumentError,
		);
	});

	it("projects known graph fields while preserving unknown frontmatter", () => {
		const parsed = parseAuthoredDocument(
			`---\nid: PX-PLAN-PL4N0001\ntype: plan\ntitle: Delivery\nspec: PX-SPEC-SPEC0001\nstatus: draft\ndepends_on: [PX-PLAN-PL4N0000]\nreview: human\n---\n`,
			"PX",
		);
		assert.deepEqual(parsed.metadata, {
			id: "PX-PLAN-PL4N0001",
			type: "plan",
			title: "Delivery",
			spec: "PX-SPEC-SPEC0001",
			status: "draft",
			depends_on: ["PX-PLAN-PL4N0000"],
		});
		assert.deepEqual(
			[...parsed.frontmatter.keys()],
			["id", "type", "title", "spec", "status", "depends_on", "review"],
		);
	});

	it("parses operating phases and validates their labels and size", () => {
		assert.deepEqual(
			parseAuthoredDocument(
				"---\nid: PX-PHASE-PHASE001\ntype: phase\ntitle: Pre-v1\nlabel: pre-v1\n---\n",
				"PX",
			).metadata,
			{
				id: "PX-PHASE-PHASE001",
				type: "phase",
				title: "Pre-v1",
				label: "pre-v1",
			},
		);
		assert.throws(
			() =>
				parseAuthoredDocument(
					"---\nid: PX-PHASE-PHASE001\ntype: phase\ntitle: Pre-v1\nlabel: label-is-too-long\n---\n",
					"PX",
				),
			/1-12 ASCII/,
		);
		assert.throws(
			() =>
				parseAuthoredDocument(
					`---\nid: PX-PHASE-PHASE001\ntype: phase\ntitle: Pre-v1\nlabel: pre-v1\n---\n\n${"x".repeat(PHASE_BYTE_LIMIT)}`,
					"PX",
				),
			/byte limit/,
		);
	});

	it("parses issue metadata without status and validates approval status", () => {
		assert.deepEqual(
			parseAuthoredDocument(
				"---\nid: PX-ISSUE-ISSUE001\ntype: issue\ntitle: Bug\nspecs: []\n---\n",
				"PX",
			).metadata,
			{
				id: "PX-ISSUE-ISSUE001",
				type: "issue",
				title: "Bug",
				specs: [],
			},
		);
		assert.equal(
			parseAuthoredDocument(
				"---\nid: PX-PLAN-PLAN0001\ntype: plan\ntitle: Build\nspec: PX-SPEC-SPEC0001\nstatus: approved\n---\n",
				"PX",
			).metadata.status,
			"approved",
		);
		assert.throws(
			() =>
				parseAuthoredDocument(
					"---\nid: PX-PLAN-PLAN0001\ntype: plan\ntitle: Build\nspec: PX-SPEC-SPEC0001\nstatus: done\n---\n",
					"PX",
				),
			/status must be draft or approved/,
		);
	});

	it("accepts only canonical bundle paths", () => {
		const metadata = {
			id: "PX-SPEC-AZ3_P9QK",
			type: "spec",
			title: "Login",
		} as const;
		assert.doesNotThrow(() =>
			validateDocumentPath(
				".system/specs/PX-SPEC-AZ3_P9QK-login-flow/index.md",
				metadata,
				"PX",
			),
		);
		for (const item of [
			".system/specs/PX-SPEC-AZ3_P9QK-login-flow.md",
			".system/phases/PX-SPEC-AZ3_P9QK-login-flow/index.md",
			".system/specs/PX-SPEC-AZ3_P9QK-Login/index.md",
			".system/specs/PX-SPEC-AZ3_P9QK-login-flow/other/index.md",
		])
			assert.throws(
				() => validateDocumentPath(item, metadata, "PX"),
				SystemDocumentError,
			);
	});
});

describe("bundle discovery and graph", () => {
	it("returns authored indexes and structural diagnostics separately", () => {
		const root = tree();
		write(
			root,
			".system/specs/PX-SPEC-SPEC0001-contract/index.md",
			spec("PX-SPEC-SPEC0001"),
		);
		write(
			root,
			".system/specs/PX-SPEC-SPEC0001-contract/assets/data.csv",
			"a,b",
		);
		write(root, ".system/plans/loose.md", "bad");
		dir(root, ".system", "specs", "PX-SPEC-SPEC0002-empty");
		assert.equal(discoverDocumentBundles(root).sources.length, 1);
		assert.deepEqual(
			discoverDocumentBundles(root).diagnostics.map(
				(item) => `${item.path} ${item.code}`,
			),
			[
				".system/plans/loose.md bundle.expected-directory",
				".system/specs/PX-SPEC-SPEC0002-empty bundle.missing-index",
			],
		);
	});

	it("reports symlinks without following them", () => {
		const root = tree();
		const outside = tree();
		write(
			root,
			".system/specs/PX-SPEC-SPEC0001-contract/index.md",
			spec("PX-SPEC-SPEC0001"),
		);
		symlinkSync(
			outside,
			path.join(root, ".system/specs/PX-SPEC-SPEC0002-link"),
			"dir",
		);
		symlinkSync(
			outside,
			path.join(root, ".system/specs/PX-SPEC-SPEC0001-contract/assets-link"),
			"dir",
		);
		assert.deepEqual(
			discoverDocumentBundles(root).diagnostics.map((item) => item.code),
			["bundle.symbolic-link", "bundle.symbolic-link"],
		);
	});

	it("keeps phases separate from the spec-plan graph", () => {
		const indexed = indexBundleDocuments(
			[
				source(
					"research",
					"PX-RESEARCH-RESEARCH",
					"notes",
					"id: PX-RESEARCH-RESEARCH\ntype: research\ntitle: Notes",
				),
				source(
					"specs",
					"PX-SPEC-SPEC0001",
					"one",
					"id: PX-SPEC-SPEC0001\ntype: spec\ntitle: One\nresearch: [PX-RESEARCH-RESEARCH]",
				),
				source(
					"specs",
					"PX-SPEC-SPEC0002",
					"two",
					"id: PX-SPEC-SPEC0002\ntype: spec\ntitle: Two",
				),
				source(
					"phases",
					"PX-PHASE-PHASE001",
					"delivery",
					"id: PX-PHASE-PHASE001\ntype: phase\ntitle: Delivery\nlabel: delivery",
				),
				source(
					"plans",
					"PX-PLAN-PLAN0001",
					"build",
					"id: PX-PLAN-PLAN0001\ntype: plan\ntitle: Build\nspec: PX-SPEC-SPEC0002\nstatus: draft",
				),
			],
			"PX",
		);
		assert.deepEqual(indexed.diagnostics, []);
		assert.deepEqual(
			indexed.documents.find((document) => document.metadata.type === "phase")
				?.metadata,
			{
				id: "PX-PHASE-PHASE001",
				type: "phase",
				title: "Delivery",
				label: "delivery",
			},
		);
	});

	it("validates dependency references and cycles", () => {
		const indexed = indexBundleDocuments(
			[
				source(
					"specs",
					"PX-SPEC-SPEC0001",
					"one",
					"id: PX-SPEC-SPEC0001\ntype: spec\ntitle: One",
				),
				source(
					"specs",
					"PX-SPEC-SPEC0002",
					"two",
					"id: PX-SPEC-SPEC0002\ntype: spec\ntitle: Two",
				),
				source(
					"plans",
					"PX-PLAN-PLAN0001",
					"one",
					"id: PX-PLAN-PLAN0001\ntype: plan\ntitle: One\nspec: PX-SPEC-SPEC0001\nstatus: draft\ndepends_on: [PX-PLAN-MISSING1]",
				),
				source(
					"plans",
					"PX-PLAN-PLAN0002",
					"two",
					"id: PX-PLAN-PLAN0002\ntype: plan\ntitle: Two\nspec: PX-SPEC-SPEC0001\nstatus: draft\ndepends_on: [PX-PLAN-PLAN0003]",
				),
				source(
					"plans",
					"PX-PLAN-PLAN0003",
					"three",
					"id: PX-PLAN-PLAN0003\ntype: plan\ntitle: Three\nspec: PX-SPEC-SPEC0001\nstatus: draft\ndepends_on: [PX-PLAN-PLAN0002]",
				),
				source(
					"plans",
					"PX-PLAN-PLAN0004",
					"four",
					"id: PX-PLAN-PLAN0004\ntype: plan\ntitle: Four\nspec: PX-SPEC-SPEC0002\nstatus: draft\ndepends_on: [PX-PLAN-PLAN0002]",
				),
			],
			"PX",
		);
		assert.deepEqual(indexed.diagnostics.map((item) => item.code).sort(), [
			"graph.dangling-reference",
			"graph.dependency-cycle",
			"graph.forbidden-edge",
		]);
	});

	it("candidate validation reports only new candidate findings", () => {
		const findings = validateBundleCandidate(
			[
				source(
					"specs",
					"PX-SPEC-SPEC0001",
					"one",
					"id: PX-SPEC-SPEC0001\ntype: spec\ntitle: One",
				),
			],
			source(
				"plans",
				"PX-PLAN-PLAN0001",
				"delivery",
				"id: PX-PLAN-PLAN0001\ntype: plan\ntitle: Delivery\nspec: PX-SPEC-MISSING1\nstatus: draft",
			),
			"PX",
		);
		assert.deepEqual(
			findings.map((item) => item.code),
			["graph.dangling-reference"],
		);
	});
});