summaryrefslogtreecommitdiffhomepage
path: root/packages/kernel/src/host/dag.test.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-04 23:34:18 +0900
committerAdam Malczewski <[email protected]>2026-06-04 23:34:18 +0900
commitdbcf2193d45b3cd6e51869dc9587b08d26a27f3e (patch)
tree3648b48b0faa8f3da15991844f5ca7572bf6f03e /packages/kernel/src/host/dag.test.ts
parent9b611d614d123462e50492d78202dae696b99aa2 (diff)
downloaddispatch-dbcf2193d45b3cd6e51869dc9587b08d26a27f3e.tar.gz
dispatch-dbcf2193d45b3cd6e51869dc9587b08d26a27f3e.zip
feat(kernel): extension host — discovery, DAG resolve, apiVersion check, activate, HostAPI (wraps bus); 50 tests
Diffstat (limited to 'packages/kernel/src/host/dag.test.ts')
-rw-r--r--packages/kernel/src/host/dag.test.ts107
1 files changed, 107 insertions, 0 deletions
diff --git a/packages/kernel/src/host/dag.test.ts b/packages/kernel/src/host/dag.test.ts
new file mode 100644
index 0000000..1a0431f
--- /dev/null
+++ b/packages/kernel/src/host/dag.test.ts
@@ -0,0 +1,107 @@
+import { describe, expect, it } from "vitest";
+import type { Manifest } from "../contracts/extension.js";
+import { resolveActivationOrder } from "./dag.js";
+
+function manifest(id: string, deps?: readonly string[]): Manifest {
+ const base: Manifest = {
+ id,
+ name: id,
+ version: "1.0.0",
+ apiVersion: "^0.1.0",
+ trust: "bundled",
+ };
+ if (deps !== undefined) {
+ return { ...base, dependsOn: deps };
+ }
+ return base;
+}
+
+describe("resolveActivationOrder", () => {
+ it("returns empty array for no extensions", () => {
+ expect(resolveActivationOrder([])).toEqual([]);
+ });
+
+ it("returns a single extension with no deps", () => {
+ const result = resolveActivationOrder([manifest("a")]);
+ expect(result.map((m) => m.id)).toEqual(["a"]);
+ });
+
+ it("orders a linear chain (A → B → C)", () => {
+ const a = manifest("a");
+ const b = manifest("b", ["a"]);
+ const c = manifest("c", ["b"]);
+
+ const result = resolveActivationOrder([c, b, a]);
+ const ids = result.map((m) => m.id);
+
+ expect(ids.indexOf("a")).toBeLessThan(ids.indexOf("b"));
+ expect(ids.indexOf("b")).toBeLessThan(ids.indexOf("c"));
+ });
+
+ it("orders a diamond (A → B, A → C, B → D, C → D)", () => {
+ const a = manifest("a");
+ const b = manifest("b", ["a"]);
+ const c = manifest("c", ["a"]);
+ const d = manifest("d", ["b", "c"]);
+
+ const result = resolveActivationOrder([d, c, b, a]);
+ const ids = result.map((m) => m.id);
+
+ expect(ids.indexOf("a")).toBeLessThan(ids.indexOf("b"));
+ expect(ids.indexOf("a")).toBeLessThan(ids.indexOf("c"));
+ expect(ids.indexOf("b")).toBeLessThan(ids.indexOf("d"));
+ expect(ids.indexOf("c")).toBeLessThan(ids.indexOf("d"));
+ });
+
+ it("handles independent sets (no deps between them)", () => {
+ const a = manifest("a");
+ const b = manifest("b");
+ const c = manifest("c");
+
+ const result = resolveActivationOrder([a, b, c]);
+ expect(result).toHaveLength(3);
+ expect(result.map((m) => m.id).sort()).toEqual(["a", "b", "c"]);
+ });
+
+ it("throws on a cycle (A → B → A)", () => {
+ const a = manifest("a", ["b"]);
+ const b = manifest("b", ["a"]);
+
+ expect(() => resolveActivationOrder([a, b])).toThrow(/cycle/i);
+ });
+
+ it("throws on a larger cycle (A → B → C → A)", () => {
+ const a = manifest("a", ["c"]);
+ const b = manifest("b", ["a"]);
+ const c = manifest("c", ["b"]);
+
+ expect(() => resolveActivationOrder([a, b, c])).toThrow(/cycle/i);
+ });
+
+ it("throws on a missing dependency", () => {
+ const a = manifest("a", ["nonexistent"]);
+
+ expect(() => resolveActivationOrder([a])).toThrow(/not available/);
+ });
+
+ it("throws on duplicate extension ids", () => {
+ const a1 = manifest("a");
+ const a2 = manifest("a");
+
+ expect(() => resolveActivationOrder([a1, a2])).toThrow(/duplicate/i);
+ });
+
+ it("handles mixed independent and dependent extensions", () => {
+ const a = manifest("a");
+ const b = manifest("b", ["a"]);
+ const c = manifest("c");
+ const d = manifest("d", ["c"]);
+
+ const result = resolveActivationOrder([a, b, c, d]);
+ const ids = result.map((m) => m.id);
+
+ expect(ids.indexOf("a")).toBeLessThan(ids.indexOf("b"));
+ expect(ids.indexOf("c")).toBeLessThan(ids.indexOf("d"));
+ expect(result).toHaveLength(4);
+ });
+});