File size: 3,889 Bytes
d197cf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Configured hook tests cover the closed allowlist and open discovery decisions.
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { resolveInternalHookSelection } from "./configured.js";

const readConfigMachineStateMock = vi.hoisted(() => vi.fn());

vi.mock("../state/config-machine-state.js", () => ({
  readConfigMachineState: readConfigMachineStateMock,
}));

describe("resolveInternalHookSelection", () => {
  beforeEach(() => {
    readConfigMachineStateMock.mockReset();
    readConfigMachineStateMock.mockReturnValue(undefined);
  });

  it.each([
    [{}, false],
    [{ hooks: { internal: { entries: { selected: { enabled: true } } } } }, true],
    [{ hooks: { internal: { entries: { selected: { enabled: false } } } } }, false],
    [{ hooks: { internal: { load: { extraDirs: ["/tmp/hooks"] } } } }, true],
  ] satisfies Array<[OpenClawConfig, boolean]>)(
    "reports whether %j selects discovery",
    (config, configured) => {
      expect(resolveInternalHookSelection(config).configured).toBe(configured);
    },
  );

  it("retains explicit and installed names while extra roots keep discovery open", () => {
    readConfigMachineStateMock.mockReturnValue({ pack: { source: "path", hooks: ["installed"] } });
    expect(
      resolveInternalHookSelection({
        hooks: {
          internal: {
            entries: { disabled: { enabled: false } },
            load: { extraDirs: ["/tmp/hooks"] },
          },
        },
      }),
    ).toEqual({ configured: true, names: null, declaredNames: new Set(["disabled", "installed"]) });
  });

  it("keeps CLI-shaped named entries closed when the master flag is enabled", () => {
    expect(
      resolveInternalHookSelection({
        hooks: {
          internal: {
            enabled: true,
            entries: {
              enabled: { enabled: true },
              disabled: { enabled: false },
            },
          },
        },
      }).names,
    ).toEqual(new Set(["enabled"]));

    expect(
      resolveInternalHookSelection({
        hooks: {
          internal: {
            enabled: true,
            entries: { disabled: { enabled: false } },
          },
        },
      }).names,
    ).toEqual(new Set());
  });

  it("keeps a bare master enable open for broad discovery", () => {
    expect(
      resolveInternalHookSelection({
        hooks: { internal: { enabled: true } },
      }).names,
    ).toBeNull();
  });

  it("keeps extra directories open-ended even with named entries", () => {
    expect(
      resolveInternalHookSelection({
        hooks: {
          internal: {
            enabled: true,
            entries: { named: { enabled: true } },
            load: { extraDirs: ["/opt/openclaw/hooks"] },
          },
        },
      }).names,
    ).toBeNull();
  });

  it("uses declared install hook names as an allowlist", () => {
    readConfigMachineStateMock.mockReturnValue({
      pack: { source: "path", hooks: ["installed-one", "installed-two"] },
    });

    expect(resolveInternalHookSelection({}).names).toEqual(
      new Set(["installed-one", "installed-two"]),
    );
  });

  it("keeps installs with unknown dynamic hook names open-ended", () => {
    readConfigMachineStateMock.mockReturnValue({ pack: { source: "path" } });

    expect(resolveInternalHookSelection({}).names).toBeNull();
  });

  it("lets explicit disablement override every discovery surface", () => {
    readConfigMachineStateMock.mockReturnValue({ pack: { source: "path" } });
    const config = {
      hooks: {
        internal: {
          enabled: false,
          entries: { named: { enabled: true } },
          load: { extraDirs: ["/opt/openclaw/hooks"] },
        },
      },
    } satisfies OpenClawConfig;

    expect(resolveInternalHookSelection(config).names).toEqual(new Set());
  });
});