Files
openclaw/extensions/matrix/src/approval-handler.runtime.test.ts
kakahu d70808433d Add structured Matrix approval metadata (#72432)
Merged via squash.

Prepared head SHA: 0e06533dff
Co-authored-by: kakahu2015 <17962485+kakahu2015@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-27 14:52:02 -04:00

291 lines
7.8 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { matrixApprovalNativeRuntime } from "./approval-handler.runtime.js";
describe("matrixApprovalNativeRuntime", () => {
it("sends versioned Matrix approval content with pending exec approvals", async () => {
const sendSingleTextMessage = vi.fn().mockResolvedValue({
messageId: "$approval",
primaryMessageId: "$approval",
messageIds: ["$approval"],
roomId: "!room:example.org",
});
const reactMessage = vi.fn().mockResolvedValue(undefined);
const pendingPayload = await matrixApprovalNativeRuntime.presentation.buildPendingPayload({
cfg: {} as never,
accountId: "default",
context: { client: {} as never },
request: {
id: "req-1",
request: {
command: "echo hi",
cwd: "/repo",
host: "gateway",
agentId: "agent-1",
},
createdAtMs: 0,
expiresAtMs: 1_000,
},
approvalKind: "exec",
nowMs: 100,
view: {
approvalKind: "exec",
approvalId: "req-1",
phase: "pending",
title: "Exec Approval Required",
description: "A command needs your approval.",
metadata: [],
ask: "on-request",
agentId: "agent-1",
commandText: "echo hi",
commandPreview: "echo hi",
cwd: "/repo",
host: "gateway",
actions: [
{
decision: "allow-once",
label: "Allow Once",
style: "success",
command: "/approve req-1 allow-once",
},
{
decision: "deny",
label: "Deny",
style: "danger",
command: "/approve req-1 deny",
},
],
expiresAtMs: 1_000,
} as never,
});
await matrixApprovalNativeRuntime.transport.deliverPending({
cfg: {} as never,
accountId: "default",
context: {
client: {} as never,
deps: {
sendSingleTextMessage,
reactMessage,
},
},
request: {} as never,
approvalKind: "exec",
preparedTarget: {
to: "room:!room:example.org",
roomId: "!room:example.org",
},
pendingPayload,
});
expect(sendSingleTextMessage).toHaveBeenCalledWith(
"room:!room:example.org",
expect.stringContaining("echo hi"),
expect.objectContaining({
extraContent: {
"com.openclaw.approval": expect.objectContaining({
version: 1,
type: "approval.request",
state: "pending",
id: "req-1",
kind: "exec",
commandText: "echo hi",
cwd: "/repo",
agentId: "agent-1",
allowedDecisions: ["allow-once", "deny"],
}),
},
}),
);
});
it("includes plugin approval fields in Matrix approval content", async () => {
const pendingPayload = await matrixApprovalNativeRuntime.presentation.buildPendingPayload({
cfg: {} as never,
accountId: "default",
context: { client: {} as never },
request: {
id: "plugin:req-1",
request: {
title: "Plugin Approval Required",
description: "Approve the tool call.",
severity: "critical",
toolName: "deploy",
pluginId: "ops",
agentId: "agent-1",
},
createdAtMs: 0,
expiresAtMs: 1_000,
},
approvalKind: "plugin",
nowMs: 100,
view: {
approvalKind: "plugin",
approvalId: "plugin:req-1",
phase: "pending",
title: "Plugin Approval Required",
description: "Approve the tool call.",
metadata: [],
agentId: "agent-1",
pluginId: "ops",
toolName: "deploy",
severity: "critical",
actions: [
{
decision: "allow-once",
label: "Allow Once",
style: "success",
command: "/approve plugin:req-1 allow-once",
},
],
expiresAtMs: 1_000,
} as never,
});
expect(pendingPayload).toMatchObject({
extraContent: {
"com.openclaw.approval": {
version: 1,
type: "approval.request",
state: "pending",
id: "plugin:req-1",
kind: "plugin",
pluginId: "ops",
toolName: "deploy",
agentId: "agent-1",
severity: "critical",
},
},
});
});
it("falls back to chunked Matrix delivery when approval content exceeds one event", async () => {
const sendSingleTextMessage = vi
.fn()
.mockRejectedValue(new Error("Matrix single-message text exceeds limit (5000 > 4000)"));
const sendMessage = vi.fn().mockResolvedValue({
messageId: "$last",
primaryMessageId: "$primary",
messageIds: ["$primary", "$last"],
roomId: "!room:example.org",
});
const reactMessage = vi.fn().mockResolvedValue(undefined);
const pendingPayload = await matrixApprovalNativeRuntime.presentation.buildPendingPayload({
cfg: {} as never,
accountId: "default",
context: { client: {} as never },
request: {
id: "req-1",
request: {
command: "echo hi",
},
createdAtMs: 0,
expiresAtMs: 1_000,
},
approvalKind: "exec",
nowMs: 100,
view: {
approvalKind: "exec",
approvalId: "req-1",
phase: "pending",
title: "Exec Approval Required",
description: "A command needs your approval.",
metadata: [],
commandText: "echo hi",
actions: [
{
decision: "allow-once",
label: "Allow Once",
style: "success",
command: "/approve req-1 allow-once",
},
],
expiresAtMs: 1_000,
} as never,
});
const entry = await matrixApprovalNativeRuntime.transport.deliverPending({
cfg: {} as never,
accountId: "default",
context: {
client: {} as never,
deps: {
sendSingleTextMessage,
sendMessage,
reactMessage,
},
},
request: {} as never,
approvalKind: "exec",
preparedTarget: {
to: "room:!room:example.org",
roomId: "!room:example.org",
},
pendingPayload,
});
expect(sendMessage).toHaveBeenCalledWith(
"room:!room:example.org",
pendingPayload.text,
expect.objectContaining({
accountId: "default",
extraContent: pendingPayload.extraContent,
}),
);
expect(reactMessage).toHaveBeenCalledWith(
"!room:example.org",
"$primary",
expect.any(String),
expect.objectContaining({
accountId: "default",
}),
);
expect(entry).toMatchObject({
roomId: "!room:example.org",
messageIds: ["$primary", "$last"],
reactionEventId: "$primary",
});
});
it("uses a longer code fence when resolved commands contain triple backticks", async () => {
const result = await matrixApprovalNativeRuntime.presentation.buildResolvedResult({
cfg: {} as never,
accountId: "default",
context: {
client: {} as never,
},
request: {
id: "req-1",
request: {
command: "echo hi",
},
createdAtMs: 0,
expiresAtMs: 1_000,
},
resolved: {
id: "req-1",
decision: "allow-once",
ts: 0,
},
view: {
approvalKind: "exec",
approvalId: "req-1",
decision: "allow-once",
commandText: "echo ```danger```",
} as never,
entry: {} as never,
});
expect(result).toEqual({
kind: "update",
payload: [
"Exec approval: Allowed once",
"",
"Command",
"````",
"echo ```danger```",
"````",
].join("\n"),
});
});
});