summaryrefslogtreecommitdiffhomepage
path: root/src/features/plugins/logic/cascade.test.ts
blob: 109c08b204fbdacd3120be258ee359552e30923d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import { describe, expect, it } from "vitest";
import { computeCascade } from "./cascade";
import { node } from "./test-fixtures";

describe("computeCascade", () => {
  it("returns [] when the target has no dependents", () => {
    const nodes = [node({ id: "a" }), node({ id: "b" })];
    expect(computeCascade("a", nodes)).toEqual([]);
  });

  it("returns the direct loaded dependents", () => {
    const nodes = [
      node({ id: "heartbeat" }),
      node({ id: "transport-http", dependsOn: ["heartbeat"] }),
    ];
    expect(computeCascade("heartbeat", nodes)).toEqual(["transport-http"]);
  });

  it("is transitive (dependent of dependent)", () => {
    const nodes = [
      node({ id: "a" }),
      node({ id: "b", dependsOn: ["a"] }),
      node({ id: "c", dependsOn: ["b"] }),
    ];
    expect(computeCascade("a", nodes)).toEqual(["b", "c"]);
  });

  it("does NOT traverse through an UNLOADED intermediate (loaded-subgraph closure — server mirror)", () => {
    // c depends on b, b is unloaded: unloading a cascades to b only (b is not
    // collected — unloaded) and does NOT reach c (its dep chain is already unmet).
    const nodes = [
      node({ id: "a" }),
      node({ id: "b", loaded: false, section: "discovered", dependsOn: ["a"] }),
      node({ id: "c", dependsOn: ["b"] }),
    ];
    expect(computeCascade("a", nodes)).toEqual([]);
  });

  it("ignores plugins that merely share a dependency (direction matters)", () => {
    const nodes = [node({ id: "dep" }), node({ id: "x", dependsOn: ["dep"] })];
    expect(computeCascade("x", nodes)).toEqual([]);
  });

  it("handles diamond dependencies without duplicates", () => {
    const nodes = [
      node({ id: "root" }),
      node({ id: "left", dependsOn: ["root"] }),
      node({ id: "right", dependsOn: ["root"] }),
      node({ id: "bottom", dependsOn: ["left", "right"] }),
    ];
    expect(computeCascade("root", nodes)).toEqual(["bottom", "left", "right"]);
  });

  it("returns [] for a target that is not in the list", () => {
    expect(computeCascade("ghost", [node({ id: "a" })])).toEqual([]);
  });

  it("is deterministic (alphabetical order)", () => {
    const nodes = [
      node({ id: "root" }),
      node({ id: "zeta", dependsOn: ["root"] }),
      node({ id: "alpha", dependsOn: ["root"] }),
    ];
    expect(computeCascade("root", nodes)).toEqual(["alpha", "zeta"]);
  });
});