





















@@ -68,6 +68,27 @@ vi.mock("./client.js", () => ({
68686969const { withOperatorApprovalsGatewayClient } = await import("./operator-approvals-client.js");
707071+const DEFAULT_APPROVAL_CLIENT_DISPLAY_NAME = "Matrix approval (@owner:example.org)";
72+73+async function runOperatorApprovalsGatewayClient(
74+params: { gatewayUrl?: string } = {},
75+callback: Parameters<typeof withOperatorApprovalsGatewayClient>[1] = async () => undefined,
76+) {
77+await withOperatorApprovalsGatewayClient(
78+{
79+config: {} as never,
80+clientDisplayName: DEFAULT_APPROVAL_CLIENT_DISPLAY_NAME,
81+ ...params,
82+},
83+callback,
84+);
85+}
86+87+function expectRuntimeTokenApprovalClient(): void {
88+expect(typeof clientState.options?.approvalRuntimeToken).toBe("string");
89+expect(clientState.options?.deviceIdentity).toBeNull();
90+}
91+7192describe("withOperatorApprovalsGatewayClient", () => {
7293beforeEach(() => {
7394clientState.options = null;
@@ -82,18 +103,12 @@ describe("withOperatorApprovalsGatewayClient", () => {
82103});
8310484105it("waits for hello before running the callback and stops cleanly", async () => {
85-await withOperatorApprovalsGatewayClient(
86-{
87-config: {} as never,
88-clientDisplayName: "Matrix approval (@owner:example.org)",
89-},
90-async (client) => {
91-await client.request("exec.approval.resolve", {
92-id: "req-123",
93-decision: "allow-once",
94-});
95-},
96-);
106+await runOperatorApprovalsGatewayClient({}, async (client) => {
107+await client.request("exec.approval.resolve", {
108+id: "req-123",
109+decision: "allow-once",
110+});
111+});
9711298113expect(clientState.options?.scopes).toEqual(["operator.approvals"]);
99114expect(typeof clientState.options?.approvalRuntimeToken).toBe("string");
@@ -109,134 +124,72 @@ describe("withOperatorApprovalsGatewayClient", () => {
109124bootstrapState.url = "wss://gateway.example/ws";
110125bootstrapState.urlSource = "config gateway.remote.url";
111126112-await withOperatorApprovalsGatewayClient(
113-{
114-config: {} as never,
115-clientDisplayName: "Matrix approval (@owner:example.org)",
116-},
117-async () => undefined,
118-);
127+await runOperatorApprovalsGatewayClient();
119128120129expect(clientState.options).not.toHaveProperty("deviceIdentity", null);
121130expect(clientState.options?.deviceIdentity).toBeUndefined();
122131});
123132124-it("omits approval runtime token for explicit loopback gateway URL overrides", async () => {
125-bootstrapState.urlSource = "cli --url";
126-127-await withOperatorApprovalsGatewayClient(
128-{
129-config: {} as never,
130-gatewayUrl: "ws://127.0.0.1:18789",
131-clientDisplayName: "Matrix approval (@owner:example.org)",
132-},
133-async () => undefined,
134-);
135-136-expect(clientState.options).not.toHaveProperty("approvalRuntimeToken");
137-});
138-139-it("omits approval runtime token for remote explicit gateway URL overrides", async () => {
140-bootstrapState.url = "wss://gateway.example/ws";
141-bootstrapState.urlSource = "cli --url";
142-143-await withOperatorApprovalsGatewayClient(
144-{
145-config: {} as never,
146-gatewayUrl: "wss://gateway.example/ws",
147-clientDisplayName: "Matrix approval (@owner:example.org)",
148-},
149-async () => undefined,
150-);
151-152-expect(clientState.options).not.toHaveProperty("approvalRuntimeToken");
153-});
154-155-it("omits approval runtime token for configured remote loopback gateway URLs", async () => {
156-bootstrapState.url = "ws://127.0.0.1:18789";
157-bootstrapState.urlSource = "config gateway.remote.url";
158-159-await withOperatorApprovalsGatewayClient(
160-{
161-config: {} as never,
162-clientDisplayName: "Matrix approval (@owner:example.org)",
163-},
164-async () => undefined,
165-);
166-167-expect(clientState.options).not.toHaveProperty("approvalRuntimeToken");
168-});
169-170-it("omits approval runtime token for env loopback gateway URL overrides", async () => {
171-bootstrapState.url = "ws://127.0.0.1:18789";
172-bootstrapState.urlSource = "env OPENCLAW_GATEWAY_URL";
173-174-await withOperatorApprovalsGatewayClient(
175-{
176-config: {} as never,
177-clientDisplayName: "Matrix approval (@owner:example.org)",
178-},
179-async () => undefined,
180-);
181-133+it.each([
134+{
135+name: "explicit loopback gateway URL overrides",
136+url: "ws://127.0.0.1:18789",
137+urlSource: "cli --url",
138+gatewayUrl: "ws://127.0.0.1:18789",
139+},
140+{
141+name: "remote explicit gateway URL overrides",
142+url: "wss://gateway.example/ws",
143+urlSource: "cli --url",
144+gatewayUrl: "wss://gateway.example/ws",
145+},
146+{
147+name: "configured remote loopback gateway URLs",
148+url: "ws://127.0.0.1:18789",
149+urlSource: "config gateway.remote.url",
150+},
151+{
152+name: "env loopback gateway URL overrides",
153+url: "ws://127.0.0.1:18789",
154+urlSource: "env OPENCLAW_GATEWAY_URL",
155+},
156+])("omits approval runtime token for $name", async ({ url, urlSource, gatewayUrl }) => {
157+bootstrapState.url = url;
158+bootstrapState.urlSource = urlSource;
159+160+await runOperatorApprovalsGatewayClient(gatewayUrl ? { gatewayUrl } : {});
182161expect(clientState.options).not.toHaveProperty("approvalRuntimeToken");
183162});
184163185164it("keeps approval runtime token for local fallback gateway URLs", async () => {
186165bootstrapState.url = "ws://127.0.0.1:18789";
187166bootstrapState.urlSource = "missing gateway.remote.url (fallback local)";
188167189-await withOperatorApprovalsGatewayClient(
190-{
191-config: {} as never,
192-clientDisplayName: "Matrix approval (@owner:example.org)",
193-},
194-async () => undefined,
195-);
168+await runOperatorApprovalsGatewayClient();
196169197-expect(typeof clientState.options?.approvalRuntimeToken).toBe("string");
198-expect(clientState.options?.deviceIdentity).toBeNull();
170+expectRuntimeTokenApprovalClient();
199171});
200172201173it("omits stored device identity for local runtime-token approval clients without shared auth", async () => {
202174bootstrapState.auth = { token: undefined, password: undefined };
203175204-await withOperatorApprovalsGatewayClient(
205-{
206-config: {} as never,
207-clientDisplayName: "Matrix approval (@owner:example.org)",
208-},
209-async () => undefined,
210-);
176+await runOperatorApprovalsGatewayClient();
211177212-expect(typeof clientState.options?.approvalRuntimeToken).toBe("string");
213-expect(clientState.options?.deviceIdentity).toBeNull();
178+expectRuntimeTokenApprovalClient();
214179});
215180216181it("surfaces close failures before hello", async () => {
217182clientState.startMode = "close";
218183219-await expect(
220-withOperatorApprovalsGatewayClient(
221-{
222-config: {} as never,
223-clientDisplayName: "Matrix approval (@owner:example.org)",
224-},
225-async () => undefined,
226-),
227-).rejects.toThrow("gateway closed (1008): pairing required");
184+await expect(runOperatorApprovalsGatewayClient()).rejects.toThrow(
185+"gateway closed (1008): pairing required",
186+);
228187});
229188230189it("falls back to stop when stopAndWait rejects", async () => {
231190clientState.stopAndWaitSpy.mockRejectedValueOnce(new Error("close failed"));
232191233-await withOperatorApprovalsGatewayClient(
234-{
235-config: {} as never,
236-clientDisplayName: "Matrix approval (@owner:example.org)",
237-},
238-async () => undefined,
239-);
192+await runOperatorApprovalsGatewayClient();
240193241194expect(clientState.stopAndWaitSpy).toHaveBeenCalledTimes(1);
242195expect(clientState.stopSpy).toHaveBeenCalledTimes(1);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。