🎉 init: 小龙的工作空间
This commit is contained in:
@@ -0,0 +1,332 @@
|
||||
/**
|
||||
* PR-36 — Software Factory Core Layer 测试
|
||||
*
|
||||
* 覆盖:
|
||||
* A — Factory Registry
|
||||
* B — Factory Contract
|
||||
* C — Extensibility
|
||||
*/
|
||||
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
// ─── PR-36 Software Factory Core ──────────────────
|
||||
import {
|
||||
FACTORY_ARTIFACT_KIND, METRIC_KIND, REPORT_TYPE, DEPENDENCY_KIND,
|
||||
createFactoryInput, createFactoryOutput,
|
||||
createFactoryArtifact, createFactoryMetric,
|
||||
createFactoryReport, createFactoryDependency,
|
||||
FACTORY_STATUS, createFactoryRegistry,
|
||||
} from "../src/software-factory-core/index.mjs";
|
||||
|
||||
// ═════════════════════════════════════════════════════
|
||||
// Part A — Factory Registry
|
||||
// ═════════════════════════════════════════════════════
|
||||
|
||||
describe("PR-36 Part A — Factory Registry", () => {
|
||||
|
||||
it("registers all 15 factories (SF-01 ~ SF-15)", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
if (i === 3) continue;
|
||||
const id = `SF-${String(i).padStart(2, "0")}`;
|
||||
const f = registry.get(id);
|
||||
assert.ok(f, `Factory ${id} should exist`);
|
||||
assert.equal(f.id, id);
|
||||
assert.ok(f.name, `Factory ${id} should have a name`);
|
||||
assert.ok(f.domain, `Factory ${id} should have a domain`);
|
||||
}
|
||||
});
|
||||
|
||||
it("returns null for unknown factory", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
assert.equal(registry.get("SF-99"), null);
|
||||
});
|
||||
|
||||
it("lists factories by domain", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
const archFactories = registry.list({ domain: "architecture" });
|
||||
assert.equal(archFactories.length, 1);
|
||||
assert.equal(archFactories[0].id, "SF-05");
|
||||
});
|
||||
|
||||
it("getDependencies returns correct upstream dependencies", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
const deps = registry.getDependencies("SF-07");
|
||||
assert.ok(deps.length >= 2, "SF-07 should have at least 2 dependencies");
|
||||
|
||||
const fromIds = deps.map(d => d.from);
|
||||
assert.ok(fromIds.includes("SF-05"), "SF-07 should depend on SF-05");
|
||||
assert.ok(fromIds.includes("SF-04"), "SF-07 should depend on SF-04");
|
||||
});
|
||||
|
||||
it("getDependents returns downstream dependents", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
const dependents = registry.getDependents("SF-01");
|
||||
assert.ok(dependents.length >= 1);
|
||||
const depIds = dependents.map(d => d.id);
|
||||
assert.ok(depIds.includes("SF-02"), "SF-02 should depend on SF-01");
|
||||
});
|
||||
|
||||
it("getTopologicalOrder returns valid order", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
const order = registry.getTopologicalOrder();
|
||||
assert.ok(order.length === 14, `Expected 14, got ${order.length}`);
|
||||
|
||||
// SF-01 should come before SF-02
|
||||
const idx01 = order.indexOf("SF-01");
|
||||
const idx02 = order.indexOf("SF-02");
|
||||
assert.ok(idx01 < idx02, `SF-01 (${idx01}) should precede SF-02 (${idx02})`);
|
||||
});
|
||||
|
||||
it("getParallelBatches returns valid batches", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
const batches = registry.getParallelBatches();
|
||||
assert.ok(batches.length >= 1, "Should have at least 1 batch");
|
||||
|
||||
// First batch should contain SF-01 (no dependencies)
|
||||
assert.ok(batches[0].includes("SF-01"), "First batch should include SF-01");
|
||||
|
||||
// SF-01 should not appear in later batches
|
||||
for (let i = 1; i < batches.length; i++) {
|
||||
assert.ok(!batches[i].includes("SF-01"), "SF-01 should only be in batch 0");
|
||||
}
|
||||
});
|
||||
|
||||
it("registerImplementation activates factory", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
const mockImpl = (_input) => createFactoryOutput({ factoryId: "SF-01", status: "passed" });
|
||||
|
||||
registry.registerImplementation("SF-01", mockImpl);
|
||||
|
||||
const f = registry.get("SF-01");
|
||||
assert.equal(f.status, FACTORY_STATUS.ACTIVE);
|
||||
assert.ok(f.implementationId);
|
||||
assert.ok(f.activatedAt);
|
||||
});
|
||||
|
||||
it("execute calls registered implementation", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
registry.registerImplementation("SF-01", (input) => {
|
||||
return createFactoryOutput({
|
||||
factoryId: "SF-01",
|
||||
status: "passed",
|
||||
artifacts: [createFactoryArtifact({ kind: "requirement", name: "Strategy Doc", factoryId: "SF-01" })],
|
||||
});
|
||||
});
|
||||
|
||||
const input = createFactoryInput({ factoryId: "SF-01" });
|
||||
const output = registry.execute("SF-01", input);
|
||||
|
||||
assert.equal(output.status, "passed");
|
||||
assert.equal(output.artifacts.length, 1);
|
||||
assert.equal(output.artifacts[0].name, "Strategy Doc");
|
||||
});
|
||||
|
||||
it("throws when executing without implementation", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
assert.throws(() => {
|
||||
registry.execute("SF-01", createFactoryInput({ factoryId: "SF-01" }));
|
||||
}, /No implementation registered/);
|
||||
});
|
||||
|
||||
it("throws when registering implementation for unknown factory", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
assert.throws(() => {
|
||||
registry.registerImplementation("SF-99", () => ({}));
|
||||
}, /Unknown factory/);
|
||||
});
|
||||
|
||||
it("stats returns correct counts", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
const s = registry.stats();
|
||||
assert.equal(s.total, 14);
|
||||
assert.equal(s.domains.length, 14);
|
||||
});
|
||||
|
||||
it("setStatus changes factory status", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
registry.setStatus("SF-01", "active");
|
||||
assert.equal(registry.get("SF-01").status, FACTORY_STATUS.ACTIVE);
|
||||
|
||||
registry.setStatus("SF-01", "deprecated");
|
||||
assert.equal(registry.get("SF-01").status, FACTORY_STATUS.DEPRECATED);
|
||||
});
|
||||
});
|
||||
|
||||
// ═════════════════════════════════════════════════════
|
||||
// Part B — Factory Contract
|
||||
// ═════════════════════════════════════════════════════
|
||||
|
||||
describe("PR-36 Part B — Factory Contract", () => {
|
||||
|
||||
describe("FactoryInput", () => {
|
||||
it("creates with required field", () => {
|
||||
const input = createFactoryInput({ factoryId: "SF-01" });
|
||||
assert.ok(input.id);
|
||||
assert.equal(input.factoryId, "SF-01");
|
||||
assert.equal(input.upstreamArtifacts.length, 0);
|
||||
});
|
||||
|
||||
it("throws without factoryId", () => {
|
||||
assert.throws(() => createFactoryInput({}));
|
||||
});
|
||||
|
||||
it("accepts optional fields", () => {
|
||||
const input = createFactoryInput({
|
||||
factoryId: "SF-01",
|
||||
parameters: { key: "val" },
|
||||
constraints: { timeBudgetMs: 5000 },
|
||||
});
|
||||
assert.equal(input.factoryId, "SF-01");
|
||||
assert.equal(input.parameters.key, "val");
|
||||
assert.equal(input.constraints.timeBudgetMs, 5000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("FactoryOutput", () => {
|
||||
it("creates with passed status", () => {
|
||||
const output = createFactoryOutput({ factoryId: "SF-01", status: "passed" });
|
||||
assert.equal(output.status, "passed");
|
||||
});
|
||||
|
||||
it("throws on invalid status", () => {
|
||||
assert.throws(() => createFactoryOutput({ factoryId: "SF-01", status: "unknown" }));
|
||||
});
|
||||
|
||||
it("accepts all valid statuses", () => {
|
||||
for (const s of ["passed", "failed", "blocked", "partial"]) {
|
||||
const o = createFactoryOutput({ factoryId: "SF-01", status: s });
|
||||
assert.equal(o.status, s);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("FactoryArtifact", () => {
|
||||
it("creates with all fields", () => {
|
||||
const art = createFactoryArtifact({
|
||||
kind: "code",
|
||||
name: "auth.module.ts",
|
||||
factoryId: "SF-07",
|
||||
productId: "pm-1",
|
||||
version: 2,
|
||||
dependencies: ["fa-1", "fa-2"],
|
||||
path: "/src/auth/module.ts",
|
||||
metadata: { language: "TypeScript" },
|
||||
});
|
||||
assert.equal(art.kind, "code");
|
||||
assert.equal(art.name, "auth.module.ts");
|
||||
assert.equal(art.factoryId, "SF-07");
|
||||
assert.equal(art.productId, "pm-1");
|
||||
assert.equal(art.version, 2);
|
||||
assert.deepEqual(art.dependencies, ["fa-1", "fa-2"]);
|
||||
});
|
||||
|
||||
it("throws on invalid kind", () => {
|
||||
assert.throws(() => createFactoryArtifact({ kind: "invalid", name: "x", factoryId: "SF-01" }));
|
||||
});
|
||||
});
|
||||
|
||||
describe("FactoryMetric", () => {
|
||||
it("creates with required fields", () => {
|
||||
const m = createFactoryMetric({
|
||||
factoryId: "SF-07",
|
||||
name: "code.coverage",
|
||||
value: 85.5,
|
||||
kind: "percentage",
|
||||
target: 80,
|
||||
threshold: 70,
|
||||
});
|
||||
assert.equal(m.name, "code.coverage");
|
||||
assert.equal(m.value, 85.5);
|
||||
assert.equal(m.kind, "percentage");
|
||||
assert.equal(m.target, 80);
|
||||
assert.equal(m.threshold, 70);
|
||||
});
|
||||
|
||||
it("throws if value is not a number", () => {
|
||||
assert.throws(() => createFactoryMetric({ factoryId: "SF-01", name: "x", value: "not-a-number" }));
|
||||
});
|
||||
});
|
||||
|
||||
describe("FactoryReport", () => {
|
||||
it("creates factory report", () => {
|
||||
const r = createFactoryReport({
|
||||
type: "factory",
|
||||
title: "SF-07 Execution Report",
|
||||
factoryId: "SF-07",
|
||||
recommendations: ["Increase test coverage"],
|
||||
});
|
||||
assert.equal(r.type, "factory");
|
||||
assert.equal(r.title, "SF-07 Execution Report");
|
||||
assert.deepEqual(r.recommendations, ["Increase test coverage"]);
|
||||
});
|
||||
|
||||
it("throws on invalid type", () => {
|
||||
assert.throws(() => createFactoryReport({ type: "invalid", title: "Test" }));
|
||||
});
|
||||
});
|
||||
|
||||
describe("FactoryDependency", () => {
|
||||
it("creates hard dependency", () => {
|
||||
const d = createFactoryDependency({
|
||||
from: "SF-01",
|
||||
to: "SF-02",
|
||||
kind: "hard",
|
||||
description: "Strategy feeds into requirements",
|
||||
});
|
||||
assert.equal(d.from, "SF-01");
|
||||
assert.equal(d.to, "SF-02");
|
||||
assert.equal(d.kind, "hard");
|
||||
assert.equal(d.required, true);
|
||||
});
|
||||
|
||||
it("creates soft dependency", () => {
|
||||
const d = createFactoryDependency({ from: "SF-02", to: "SF-05", kind: "soft", required: false });
|
||||
assert.equal(d.kind, "soft");
|
||||
assert.equal(d.required, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ═════════════════════════════════════════════════════
|
||||
// Part C — Product Model
|
||||
// ═════════════════════════════════════════════════════
|
||||
|
||||
describe("PR-36 Part I — Extensibility", () => {
|
||||
|
||||
it("no hardcoded factory implementations in registry", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
// All factories should initially have no implementation
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
if (i === 3) continue;
|
||||
const id = `SF-${String(i).padStart(2, "0")}`;
|
||||
assert.equal(registry.getImplementation(id), null, `SF-${String(i).padStart(2, "0")} should have no hardcoded implementation`);
|
||||
}
|
||||
});
|
||||
|
||||
it("can add custom factory after SF-14", () => {
|
||||
// Factory Registry only registers SF-01~SF-15, but can we extend with a custom one?
|
||||
// This validates the extensibility: the registry is not locked to 14 factories.
|
||||
const registry = createFactoryRegistry();
|
||||
|
||||
// Future SF-16 could be registered via a separate module
|
||||
// For now, verify the existing get API correctly handles unknown IDs
|
||||
assert.equal(registry.get("SF-16"), null);
|
||||
|
||||
// getImplementation for unknown should return null
|
||||
assert.equal(registry.getImplementation("SF-16"), null);
|
||||
});
|
||||
|
||||
it("factory registry supports custom implementation registration", () => {
|
||||
const registry = createFactoryRegistry();
|
||||
// Register implementation for existing factory
|
||||
registry.registerImplementation("SF-07", (input) => {
|
||||
return createFactoryOutput({ factoryId: "SF-07", status: "passed" });
|
||||
});
|
||||
assert.equal(typeof registry.getImplementation("SF-07"), "function");
|
||||
|
||||
const output = registry.execute("SF-07", createFactoryInput({ factoryId: "SF-07" }));
|
||||
assert.equal(output.status, "passed");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user