124 lines
5.8 KiB
Swift
124 lines
5.8 KiB
Swift
import Testing
|
|
|
|
@testable import NucleicCore
|
|
|
|
/// Provider approval callbacks remain client-reviewed; Nucleic owns policy and presentation.
|
|
@Suite struct CodexAppServerBackendTests {
|
|
@Test func autoModeStillRoutesReviewToNucleic() {
|
|
#expect(CodexAppServerBackend.approvalsReviewer(autoApprove: true) == "user")
|
|
}
|
|
|
|
@Test func resumePreservesPlatformToolCapabilities() {
|
|
let resume = ResumeSpec(
|
|
sessionID: SessionID(rawValue: "resume-tools"), backendSessionID: "thread-1",
|
|
worktree: "/repo", allowHostExec: true, allowMacVMExec: true,
|
|
allowMacVMComputer: true, allowLinuxVMExec: true,
|
|
allowLinuxVMComputer: true, allowAgentContainers: true)
|
|
let run = CodexAppServerBackend.runSpec(resuming: resume)
|
|
#expect(run.allowHostExec)
|
|
#expect(run.allowMacVMExec)
|
|
#expect(run.allowMacVMComputer)
|
|
#expect(run.allowLinuxVMExec)
|
|
#expect(run.allowLinuxVMComputer)
|
|
#expect(run.allowAgentContainers)
|
|
}
|
|
|
|
@Test func commonAutoPolicyNeverApprovesDestructiveUnknownOrQuestions() {
|
|
#expect(NucleicApprovalPolicy.shouldAutoApprove(autoApprove: true, risk: .write))
|
|
#expect(!NucleicApprovalPolicy.shouldAutoApprove(autoApprove: true, risk: .destructive))
|
|
#expect(!NucleicApprovalPolicy.shouldAutoApprove(autoApprove: true, risk: .unknown))
|
|
#expect(!NucleicApprovalPolicy.shouldAutoApprove(
|
|
autoApprove: true, risk: .write, requiresExplicitUserDecision: true))
|
|
#expect(!NucleicApprovalPolicy.shouldAutoApprove(autoApprove: false, risk: .write))
|
|
}
|
|
|
|
@Test func interactiveModeSelectsUserReviewer() {
|
|
#expect(CodexAppServerBackend.approvalsReviewer(autoApprove: false) == "user")
|
|
}
|
|
|
|
@Test func acceptsSupportedMaxAndSolProWireEfforts() {
|
|
#expect(CodexAppServerBackend.acceptedReasoningEffort(
|
|
"max", model: "gpt-5.6-sol", codexPro: false) == "max")
|
|
#expect(CodexAppServerBackend.acceptedReasoningEffort(
|
|
"max", model: "gpt-5.6-terra", codexPro: false) == "max")
|
|
#expect(CodexAppServerBackend.acceptedReasoningEffort(
|
|
"max", model: "gpt-5.5", codexPro: true) == nil)
|
|
#expect(CodexAppServerBackend.acceptedReasoningEffort(
|
|
"ultra", model: "gpt-5.6-sol", codexPro: true) == "ultra")
|
|
#expect(CodexAppServerBackend.acceptedReasoningEffort(
|
|
"ultra", model: "gpt-5.6-sol", codexPro: false) == nil)
|
|
#expect(CodexAppServerBackend.acceptedReasoningEffort(
|
|
"ultra", model: "gpt-5.6-terra", codexPro: true) == nil)
|
|
#expect(CodexAppServerBackend.acceptedReasoningEffort(
|
|
"pro", model: "gpt-5.6-sol", codexPro: true) == nil)
|
|
#expect(CodexAppServerBackend.acceptedReasoningEffort(
|
|
"unknown", model: "gpt-5.6-sol", codexPro: true) == nil)
|
|
}
|
|
|
|
// MARK: - Nucleic-MCP auto-allow (CodexGateOwnership)
|
|
|
|
private func request(_ method: String, _ params: [String: JSONValue])
|
|
-> JSONRPCConnection.InboundRequest
|
|
{
|
|
JSONRPCConnection.InboundRequest(id: .string("1"), method: method, params: .object(params))
|
|
}
|
|
|
|
@Test func topLevelServerAndToolIdentityStillMatch() {
|
|
#expect(CodexAppServerBackend.concernsNucleicMCP(
|
|
request("item/tool/requestApproval", ["server": .string("nucleic")])))
|
|
#expect(CodexAppServerBackend.concernsNucleicMCP(
|
|
request("item/tool/requestApproval", ["toolName": .string("mcp__nucleic__mac_vm_exec")])))
|
|
}
|
|
|
|
/// A guardian permission request identifies the call *nested*, under the requested-permission
|
|
/// entry rather than at the top level — the shape a top-level-only check missed, which is how
|
|
/// Nucleic's own VM tools ended up prompting or failing instead of being auto-allowed.
|
|
@Test func nestedGuardianPermissionPayloadMatches() {
|
|
let nested = request(
|
|
"permissions/requestApproval",
|
|
[
|
|
"threadId": .string("t1"),
|
|
"permissions": .array([
|
|
.object([
|
|
"mcpToolCall": .object([
|
|
"toolName": .string("mcp__nucleic__linux_container"),
|
|
"connectorName": .string("nucleic"),
|
|
])
|
|
])
|
|
]),
|
|
])
|
|
#expect(CodexAppServerBackend.concernsNucleicMCP(nested))
|
|
|
|
// Same shape in the core's snake_case spelling.
|
|
let snake = request(
|
|
"permissions/requestApproval",
|
|
["permissions": .array([.object(["mcp_tool_call": .object([
|
|
"tool_name": .string("mcp__nucleic__mac_vm_computer"),
|
|
"connector_name": .string("nucleic"),
|
|
])])])])
|
|
#expect(CodexAppServerBackend.concernsNucleicMCP(snake))
|
|
}
|
|
|
|
/// The recursive match must not auto-allow an unrelated approval. This project is itself named
|
|
/// "nucleic", so a bare `name: "nucleic"` buried in a command approval's payload must NOT count
|
|
/// — only a server/connector key, or a `mcp__nucleic__`-qualified tool name, may.
|
|
@Test func unrelatedApprovalNamingTheProjectIsNotAutoAllowed() {
|
|
let commandApproval = request(
|
|
"item/commandExecution/requestApproval",
|
|
[
|
|
"itemId": .string("c1"),
|
|
"command": .string("rm -rf build"),
|
|
"workspace": .object(["name": .string("nucleic")]),
|
|
])
|
|
#expect(!CodexAppServerBackend.concernsNucleicMCP(commandApproval))
|
|
|
|
let otherServer = request(
|
|
"permissions/requestApproval",
|
|
["permissions": .array([.object(["mcpToolCall": .object([
|
|
"toolName": .string("mcp__github__create_issue"),
|
|
"connectorName": .string("github"),
|
|
])])])])
|
|
#expect(!CodexAppServerBackend.concernsNucleicMCP(otherServer))
|
|
}
|
|
}
|