mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-20 21:34:45 +00:00
test: tighten approve command assertions
This commit is contained in:
@@ -25,6 +25,38 @@ vi.mock("../../globals.js", () => ({
|
||||
logVerbose: vi.fn(),
|
||||
}));
|
||||
|
||||
function requireRecord(value: unknown, label: string): Record<string, unknown> {
|
||||
expect(value).toBeTypeOf("object");
|
||||
expect(value).not.toBeNull();
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new Error(`expected ${label} to be an object`);
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function gatewayRequest(callIndex = 0) {
|
||||
const call = callGatewayMock.mock.calls[callIndex] as unknown[] | undefined;
|
||||
expect(call).toBeDefined();
|
||||
if (!call) {
|
||||
throw new Error(`expected gateway call ${callIndex}`);
|
||||
}
|
||||
return requireRecord(call[0], `gateway call ${callIndex} request`);
|
||||
}
|
||||
|
||||
function expectGatewayResolveCall(params: {
|
||||
callIndex?: number;
|
||||
method: string;
|
||||
id: string;
|
||||
decision?: string;
|
||||
}) {
|
||||
const request = gatewayRequest(params.callIndex ?? 0);
|
||||
expect(request.method).toBe(params.method);
|
||||
expect(request.params).toEqual({
|
||||
id: params.id,
|
||||
decision: params.decision ?? "allow-once",
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeDiscordDirectApproverId(value: string | number): string | undefined {
|
||||
const normalized = String(value)
|
||||
.trim()
|
||||
@@ -468,12 +500,7 @@ describe("handleApproveCommand", () => {
|
||||
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(result?.reply?.text).toContain("Approval allow-once submitted");
|
||||
expect(callGatewayMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "exec.approval.resolve",
|
||||
params: { id: "abc", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "exec.approval.resolve", id: "abc" });
|
||||
});
|
||||
|
||||
it("accepts bare approve text for Slack-style manual approvals", async () => {
|
||||
@@ -496,12 +523,7 @@ describe("handleApproveCommand", () => {
|
||||
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(result?.reply?.text).toContain("Approval allow-once submitted");
|
||||
expect(callGatewayMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "exec.approval.resolve",
|
||||
params: { id: "abc", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "exec.approval.resolve", id: "abc" });
|
||||
});
|
||||
|
||||
it("accepts Telegram /approve from configured approvers even when chat access is otherwise blocked", async () => {
|
||||
@@ -516,12 +538,7 @@ describe("handleApproveCommand", () => {
|
||||
const result = await handleApproveCommand(params, true);
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(result?.reply?.text).toContain("Approval allow-once submitted");
|
||||
expect(callGatewayMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "exec.approval.resolve",
|
||||
params: { id: "abc12345", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "exec.approval.resolve", id: "abc12345" });
|
||||
});
|
||||
|
||||
it("honors the configured default account for omitted-account /approve auth", async () => {
|
||||
@@ -563,12 +580,7 @@ describe("handleApproveCommand", () => {
|
||||
const result = await handleApproveCommand(params, true);
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(result?.reply?.text).toContain("Approval allow-once submitted");
|
||||
expect(callGatewayMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "exec.approval.resolve",
|
||||
params: { id: "abc12345", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "exec.approval.resolve", id: "abc12345" });
|
||||
});
|
||||
|
||||
it("accepts Signal /approve from configured approvers even when chat access is otherwise blocked", async () => {
|
||||
@@ -594,12 +606,7 @@ describe("handleApproveCommand", () => {
|
||||
const result = await handleApproveCommand(params, true);
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(result?.reply?.text).toContain("Approval allow-once submitted");
|
||||
expect(callGatewayMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "exec.approval.resolve",
|
||||
params: { id: "abc12345", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "exec.approval.resolve", id: "abc12345" });
|
||||
});
|
||||
|
||||
it("does not treat implicit default approval auth as a bypass for unauthorized senders", async () => {
|
||||
@@ -706,12 +713,7 @@ describe("handleApproveCommand", () => {
|
||||
const result = await handleApproveCommand(params, true);
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(result?.reply?.text).toContain("Approval allow-once submitted");
|
||||
expect(callGatewayMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "exec.approval.resolve",
|
||||
params: { id: "abc12345", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "exec.approval.resolve", id: "abc12345" });
|
||||
});
|
||||
|
||||
it("accepts Telegram /approve from exec target recipients when native approvals are disabled", async () => {
|
||||
@@ -744,12 +746,7 @@ describe("handleApproveCommand", () => {
|
||||
const result = await handleApproveCommand(params, true);
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(result?.reply?.text).toContain("Approval allow-once submitted");
|
||||
expect(callGatewayMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "exec.approval.resolve",
|
||||
params: { id: "abc12345", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "exec.approval.resolve", id: "abc12345" });
|
||||
});
|
||||
|
||||
it("requires configured Discord approvers for exec approvals", async () => {
|
||||
@@ -800,13 +797,11 @@ describe("handleApproveCommand", () => {
|
||||
expect(result?.shouldContinue, testCase.name).toBe(false);
|
||||
expect(result?.reply?.text, testCase.name).toContain(testCase.expectedText);
|
||||
expect(callGatewayMock, testCase.name).toHaveBeenCalledTimes(testCase.expectedGatewayCalls);
|
||||
if ("expectedMethod" in testCase) {
|
||||
expect(callGatewayMock, testCase.name).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: testCase.expectedMethod,
|
||||
params: { id: "abc12345", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
if ("expectedMethod" in testCase && testCase.expectedMethod) {
|
||||
expectGatewayResolveCall({
|
||||
method: testCase.expectedMethod,
|
||||
id: "abc12345",
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -859,13 +854,11 @@ describe("handleApproveCommand", () => {
|
||||
expect(result?.shouldContinue).toBe(false);
|
||||
expect(result?.reply?.text).toContain("Approval allow-once submitted");
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(2);
|
||||
expect(callGatewayMock).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
method: "plugin.approval.resolve",
|
||||
params: { id: "legacy-plugin-123", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({
|
||||
callIndex: 1,
|
||||
method: "plugin.approval.resolve",
|
||||
id: "legacy-plugin-123",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns the underlying not-found error for plugin-only approval routing", async () => {
|
||||
@@ -911,12 +904,7 @@ describe("handleApproveCommand", () => {
|
||||
expect(result?.reply?.text).toContain("Failed to submit approval");
|
||||
expect(result?.reply?.text).toContain("unknown or expired approval id");
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
expect(callGatewayMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "plugin.approval.resolve",
|
||||
params: { id: "abc123", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "plugin.approval.resolve", id: "abc123" });
|
||||
});
|
||||
|
||||
it("requires configured Discord approvers for plugin approvals", async () => {
|
||||
@@ -952,12 +940,7 @@ describe("handleApproveCommand", () => {
|
||||
expect(result?.reply?.text, testCase.name).toContain(testCase.expectedText);
|
||||
expect(callGatewayMock, testCase.name).toHaveBeenCalledTimes(testCase.expectedGatewayCalls);
|
||||
if (testCase.expectedGatewayCalls > 0) {
|
||||
expect(callGatewayMock, testCase.name).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "plugin.approval.resolve",
|
||||
params: { id: "plugin:abc123", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({ method: "plugin.approval.resolve", id: "plugin:abc123" });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1064,12 +1047,11 @@ describe("handleApproveCommand", () => {
|
||||
testCase.expectedGatewayCalls,
|
||||
);
|
||||
if (testCase.expectedGatewayCalls > 0) {
|
||||
expect(callGatewayMock, String(testCase.scopes)).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "exec.approval.resolve",
|
||||
params: { id: "abc", decision: "allow-once" },
|
||||
}),
|
||||
);
|
||||
expectGatewayResolveCall({
|
||||
callIndex: callGatewayMock.mock.calls.length - 1,
|
||||
method: "exec.approval.resolve",
|
||||
id: "abc",
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user