mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-29 16:05:13 +00:00
* fix(macos): align ask always approval actions * fix(macos): harden approval prompt decisions * fix(ui): satisfy approval action lint * fix(infra): settle jsonl sockets on close * fix(ui): explain unavailable durable approvals * test(macos): document legacy approval fallback
149 lines
5.4 KiB
Swift
149 lines
5.4 KiB
Swift
import AppKit
|
|
import Testing
|
|
@testable import OpenClaw
|
|
|
|
@Suite(.serialized)
|
|
@MainActor
|
|
struct ExecApprovalPromptLayoutTests {
|
|
@Test func `allowed decisions omit durable approval even when ask allows it`() {
|
|
let decisions = ExecApprovalsPromptPresenter.allowedPromptDecisions(
|
|
ExecApprovalPromptRequest(
|
|
command: "/bin/sh -lc pwd",
|
|
cwd: "/Users/example/projects/openclaw",
|
|
host: "node",
|
|
security: "full",
|
|
ask: "on-miss",
|
|
agentId: "main",
|
|
resolvedPath: "/bin/sh",
|
|
sessionKey: "session-1",
|
|
allowedDecisions: [.allowOnce, .deny]))
|
|
|
|
#expect(decisions == [.allowOnce, .deny])
|
|
}
|
|
|
|
@Test func `ask always prompts omit durable approval when decisions are omitted`() {
|
|
let decisions = ExecApprovalsPromptPresenter.allowedPromptDecisions(
|
|
ExecApprovalPromptRequest(
|
|
command: "/bin/sh -lc pwd",
|
|
cwd: "/Users/example/projects/openclaw",
|
|
host: "node",
|
|
security: "full",
|
|
ask: "always",
|
|
agentId: "main",
|
|
resolvedPath: "/bin/sh",
|
|
sessionKey: "session-1"))
|
|
|
|
#expect(decisions == [.allowOnce, .deny])
|
|
}
|
|
|
|
@Test func `ask on miss prompts keep durable approval when decisions are omitted`() {
|
|
let decisions = ExecApprovalsPromptPresenter.allowedPromptDecisions(
|
|
ExecApprovalPromptRequest(
|
|
command: "/bin/sh -lc pwd",
|
|
cwd: "/Users/example/projects/openclaw",
|
|
host: "node",
|
|
security: "full",
|
|
ask: "on-miss",
|
|
agentId: "main",
|
|
resolvedPath: "/bin/sh",
|
|
sessionKey: "session-1"))
|
|
|
|
#expect(decisions == [.allowOnce, .allowAlways, .deny])
|
|
}
|
|
|
|
@Test func `legacy prompts keep durable approval when policy fields are omitted`() {
|
|
let decisions = ExecApprovalsPromptPresenter.allowedPromptDecisions(
|
|
ExecApprovalPromptRequest(
|
|
command: "/bin/sh -lc pwd",
|
|
cwd: "/Users/example/projects/openclaw",
|
|
host: "node",
|
|
security: "full",
|
|
agentId: "main",
|
|
resolvedPath: "/bin/sh",
|
|
sessionKey: "session-1"))
|
|
|
|
#expect(decisions == [.allowOnce, .allowAlways, .deny])
|
|
}
|
|
|
|
@Test func `unknown ask prompts keep legacy durable approval when decisions are omitted`() {
|
|
let decisions = ExecApprovalsPromptPresenter.allowedPromptDecisions(
|
|
ExecApprovalPromptRequest(
|
|
command: "/bin/sh -lc pwd",
|
|
cwd: "/Users/example/projects/openclaw",
|
|
host: "node",
|
|
security: "full",
|
|
ask: "unexpected",
|
|
agentId: "main",
|
|
resolvedPath: "/bin/sh",
|
|
sessionKey: "session-1"))
|
|
|
|
#expect(decisions == [.allowOnce, .allowAlways, .deny])
|
|
}
|
|
|
|
@Test func `approval request decodes valid allowed decisions only`() throws {
|
|
let data = """
|
|
{
|
|
"command": "/bin/sh -lc pwd",
|
|
"ask": "on-miss",
|
|
"allowedDecisions": ["allow-once", "bad", "deny", 3]
|
|
}
|
|
""".data(using: .utf8)!
|
|
|
|
let request = try JSONDecoder().decode(ExecApprovalPromptRequest.self, from: data)
|
|
|
|
#expect(request.allowedDecisions == [.allowOnce, .deny])
|
|
}
|
|
|
|
@Test func `approval request falls back when allowed decisions has wrong shape`() throws {
|
|
let data = """
|
|
{
|
|
"command": "/bin/sh -lc pwd",
|
|
"ask": "always",
|
|
"allowedDecisions": "allow-once"
|
|
}
|
|
""".data(using: .utf8)!
|
|
|
|
let request = try JSONDecoder().decode(ExecApprovalPromptRequest.self, from: data)
|
|
|
|
#expect(ExecApprovalsPromptPresenter.allowedPromptDecisions(request) == [.allowOnce, .deny])
|
|
}
|
|
|
|
@Test func `modal close does not synthesize deny when deny is unavailable`() {
|
|
let closeResponse = NSApplication.ModalResponse(rawValue: 0)
|
|
|
|
let withoutDeny = ExecApprovalsPromptPresenter.decision(
|
|
forModalResponse: closeResponse,
|
|
decisions: [.allowOnce])
|
|
let withDeny = ExecApprovalsPromptPresenter.decision(
|
|
forModalResponse: closeResponse,
|
|
decisions: [.allowOnce, .deny])
|
|
|
|
#expect(withoutDeny == nil)
|
|
#expect(withDeny == .deny)
|
|
}
|
|
|
|
@Test func `accessory view reserves nonzero alert layout space`() {
|
|
let accessory = ExecApprovalsPromptPresenter.buildAccessoryView(
|
|
ExecApprovalPromptRequest(
|
|
command: "/bin/sh -lc \"hostname; uptime; echo '---'\"",
|
|
cwd: "/Users/example/projects/openclaw",
|
|
host: "node",
|
|
security: "allowlist",
|
|
ask: "on-miss",
|
|
agentId: "main",
|
|
resolvedPath: "/bin/sh",
|
|
sessionKey: "session-1"))
|
|
|
|
#expect(accessory.frame.width >= 380)
|
|
#expect(accessory.frame.height >= 160)
|
|
|
|
let alert = NSAlert()
|
|
alert.messageText = "Allow this command?"
|
|
alert.informativeText = "Review the command details before allowing."
|
|
alert.accessoryView = accessory
|
|
|
|
#expect(alert.accessoryView?.frame.width == accessory.frame.width)
|
|
#expect(alert.accessoryView?.frame.height == accessory.frame.height)
|
|
}
|
|
}
|