mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
49ff6a4497
* fix(protocol): classify system agents in rosters * fix(ci): align agent kind client gates * fix(ci): keep roster filtering in startup bundle * chore(ui): remove superseded roster module * style(android): satisfy agent kind test formatting * style(linux): format merged agent capability assertion * fix(config): reject reserved system agent ids * chore(i18n): refresh native source inventory * style(android): format agent kind surfaces * fix(gateway): preserve configured agent ownership
278 lines
11 KiB
Swift
278 lines
11 KiB
Swift
import Foundation
|
|
import OpenClawChatUI
|
|
import OpenClawKit
|
|
import OpenClawProtocol
|
|
import Testing
|
|
@testable import OpenClaw
|
|
|
|
struct MacGatewayChatTransportMappingTests {
|
|
@Test func `mac chat advertises typed agent rosters and inline widgets`() {
|
|
#expect(GatewayConnection.operatorClientCaps == [
|
|
OpenClawGatewayClientCapability.agentKind,
|
|
OpenClawGatewayClientCapability.inlineWidgets,
|
|
])
|
|
}
|
|
|
|
@Test func `bare global session target carries normalized selected agent`() {
|
|
let transport = MacGatewayChatTransport(defaultGlobalAgentID: " Agent-A ")
|
|
|
|
#expect(transport.sessionTarget(for: " GLOBAL ") == .init(
|
|
sessionKey: "GLOBAL",
|
|
agentID: "agent-a"))
|
|
#expect(transport.sessionTarget(for: "agent:agent-a:main") == .init(
|
|
sessionKey: "agent:agent-a:main",
|
|
agentID: nil))
|
|
#expect(transport.sessionTarget(for: "main") == .init(
|
|
sessionKey: "main",
|
|
agentID: nil))
|
|
|
|
let snapshotObserverTransport = transport
|
|
snapshotObserverTransport.updateDefaultGlobalAgentID("Agent-B")
|
|
#expect(transport.sessionTarget(for: "global") == .init(
|
|
sessionKey: "global",
|
|
agentID: "agent-b"))
|
|
}
|
|
|
|
@Test func `bare global session target tolerates missing selected agent`() {
|
|
let transport = MacGatewayChatTransport()
|
|
|
|
#expect(transport.sessionTarget(for: "global") == .init(
|
|
sessionKey: "global",
|
|
agentID: nil))
|
|
}
|
|
|
|
@Test func `fixed connection does not inherit app wide cache routing`() async throws {
|
|
let url = try #require(URL(string: "wss://fixed.example"))
|
|
let connection = GatewayConnection(configProvider: {
|
|
(url: url, token: nil, password: nil)
|
|
})
|
|
let transport = MacGatewayChatTransport(
|
|
connection: connection,
|
|
outboxGatewayID: "manual-fixed")
|
|
|
|
#expect(await transport.currentOutboxGatewayMatchesConnection())
|
|
await connection.shutdown()
|
|
}
|
|
|
|
@Test func `session settings request preserves verbosity patch`() {
|
|
let request = MacGatewayChatTransport.sessionSettingsRequest(
|
|
sessionKey: "global",
|
|
agentID: "reviewer",
|
|
patch: OpenClawChatSessionSettingsPatch(
|
|
model: .some("openai/gpt-5.6-sol"),
|
|
thinkingLevel: .some(nil),
|
|
fastMode: .some(.on),
|
|
verboseLevel: .some("full")))
|
|
|
|
#expect(request.method == "sessions.patch")
|
|
#expect(request.params["key"]?.value as? String == "global")
|
|
#expect(request.params["agentId"]?.value as? String == "reviewer")
|
|
#expect(request.params["model"]?.value as? String == "openai/gpt-5.6-sol")
|
|
#expect(request.params["thinkingLevel"]?.value is NSNull)
|
|
#expect(request.params["fastMode"]?.value as? Bool == true)
|
|
#expect(request.params["verboseLevel"]?.value as? String == "full")
|
|
}
|
|
|
|
@Test func `full message request uses generated gateway field names`() throws {
|
|
let request = try MacGatewayChatTransport.fullMessageRequest(
|
|
sessionKey: "global",
|
|
agentID: "reviewer",
|
|
messageID: "msg-42")
|
|
|
|
#expect(request.method == "chat.message.get")
|
|
#expect(request.params["sessionKey"]?.value as? String == "global")
|
|
#expect(request.params["agentId"]?.value as? String == "reviewer")
|
|
#expect(request.params["messageId"]?.value as? String == "msg-42")
|
|
#expect(request.params["maxChars"]?.value as? Int == 500_000)
|
|
}
|
|
|
|
@Test func `message rewind and fork requests map session targets`() {
|
|
let rewind = MacGatewayChatTransport.rewindSessionRequest(
|
|
sessionKey: "global",
|
|
agentID: "reviewer",
|
|
entryId: "msg-42")
|
|
let fork = MacGatewayChatTransport.forkSessionAtMessageRequest(
|
|
sessionKey: "agent:reviewer:main",
|
|
agentID: nil,
|
|
entryId: "msg-43")
|
|
|
|
#expect(rewind.method == "sessions.rewind")
|
|
#expect(rewind.params["sessionKey"]?.value as? String == "global")
|
|
#expect(rewind.params["agentId"]?.value as? String == "reviewer")
|
|
#expect(rewind.params["entryId"]?.value as? String == "msg-42")
|
|
#expect(fork.method == "sessions.fork")
|
|
#expect(fork.params["sessionKey"]?.value as? String == "agent:reviewer:main")
|
|
#expect(fork.params["agentId"] == nil)
|
|
#expect(fork.params["entryId"]?.value as? String == "msg-43")
|
|
}
|
|
|
|
@Test func `legacy trace preference migrates to independent defaults once`() throws {
|
|
let suiteName = "MacGatewayChatTransportMappingTests.\(UUID().uuidString)"
|
|
let defaults = try #require(UserDefaults(suiteName: suiteName))
|
|
defer { defaults.removePersistentDomain(forName: suiteName) }
|
|
defaults.set(false, forKey: OpenClawChatWindowShell.assistantTraceDefaultsKey)
|
|
|
|
#expect(WebChatTracePreferences.displayOptions(defaults: defaults).isEmpty)
|
|
#expect(defaults.object(forKey: OpenClawChatWindowShell.assistantReasoningDefaultsKey) as? Bool == false)
|
|
#expect(defaults.object(forKey: OpenClawChatWindowShell.assistantToolActivityDefaultsKey) as? Bool == false)
|
|
|
|
defaults.set(true, forKey: OpenClawChatWindowShell.assistantReasoningDefaultsKey)
|
|
#expect(WebChatTracePreferences.displayOptions(defaults: defaults) == [.reasoning])
|
|
}
|
|
|
|
@Test func `snapshot maps to health`() {
|
|
let snapshot = Snapshot(
|
|
presence: [],
|
|
health: ["ok": OpenClawProtocol.AnyCodable(false)],
|
|
stateversion: StateVersion(presence: 1, health: 1),
|
|
uptimems: 123,
|
|
configpath: nil,
|
|
statedir: nil,
|
|
sessiondefaults: nil,
|
|
authmode: nil,
|
|
updateavailable: nil)
|
|
|
|
let hello = HelloOk(
|
|
type: "hello",
|
|
_protocol: 2,
|
|
server: [:],
|
|
features: [:],
|
|
snapshot: snapshot,
|
|
controluitabs: nil,
|
|
pluginsurfaceurls: nil,
|
|
auth: [:],
|
|
policy: [:])
|
|
|
|
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.snapshot(hello))
|
|
switch mapped {
|
|
case let .health(ok):
|
|
#expect(ok == false)
|
|
default:
|
|
Issue.record("expected .health from snapshot, got \(String(describing: mapped))")
|
|
}
|
|
}
|
|
|
|
@Test func `health event maps to health`() {
|
|
let frame = EventFrame(
|
|
type: "event",
|
|
event: "health",
|
|
payload: OpenClawProtocol.AnyCodable(["ok": OpenClawProtocol.AnyCodable(true)]),
|
|
seq: 1,
|
|
stateversion: nil)
|
|
|
|
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame))
|
|
switch mapped {
|
|
case let .health(ok):
|
|
#expect(ok == true)
|
|
default:
|
|
Issue.record("expected .health from health event, got \(String(describing: mapped))")
|
|
}
|
|
}
|
|
|
|
@Test func `tick event maps to tick`() {
|
|
let frame = EventFrame(type: "event", event: "tick", payload: nil, seq: 1, stateversion: nil)
|
|
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame))
|
|
#expect({
|
|
if case .tick = mapped {
|
|
return true
|
|
}
|
|
return false
|
|
}())
|
|
}
|
|
|
|
@Test func `sessions changed event maps to authoritative refresh signal`() {
|
|
let payload = OpenClawProtocol.AnyCodable([
|
|
"sessionKey": OpenClawProtocol.AnyCodable("agent:main:main"),
|
|
"agentId": OpenClawProtocol.AnyCodable("main"),
|
|
"reason": OpenClawProtocol.AnyCodable("command-metadata"),
|
|
])
|
|
let frame = EventFrame(
|
|
type: "event",
|
|
event: "sessions.changed",
|
|
payload: payload,
|
|
seq: 1,
|
|
stateversion: nil)
|
|
|
|
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame))
|
|
guard case let .sessionsChanged(change) = mapped else {
|
|
Issue.record("expected .sessionsChanged, got \(String(describing: mapped))")
|
|
return
|
|
}
|
|
#expect(change == .init(
|
|
sessionKey: "agent:main:main",
|
|
agentId: "main",
|
|
reason: "command-metadata"))
|
|
}
|
|
|
|
@Test func `chat event maps to chat`() {
|
|
let payload = OpenClawProtocol.AnyCodable([
|
|
"runId": OpenClawProtocol.AnyCodable("run-1"),
|
|
"sessionKey": OpenClawProtocol.AnyCodable("main"),
|
|
"state": OpenClawProtocol.AnyCodable("final"),
|
|
])
|
|
let frame = EventFrame(type: "event", event: "chat", payload: payload, seq: 1, stateversion: nil)
|
|
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame))
|
|
|
|
switch mapped {
|
|
case let .chat(chat):
|
|
#expect(chat.runId == "run-1")
|
|
#expect(chat.sessionKey == "main")
|
|
#expect(chat.state == "final")
|
|
default:
|
|
Issue.record("expected .chat from chat event, got \(String(describing: mapped))")
|
|
}
|
|
}
|
|
|
|
@Test func `session message event maps to session message`() {
|
|
let payload = OpenClawProtocol.AnyCodable([
|
|
"sessionKey": OpenClawProtocol.AnyCodable("agent:main:main"),
|
|
"messageId": OpenClawProtocol.AnyCodable("msg-1"),
|
|
"messageSeq": OpenClawProtocol.AnyCodable(7),
|
|
"message": OpenClawProtocol.AnyCodable([
|
|
"role": OpenClawProtocol.AnyCodable("user"),
|
|
"content": OpenClawProtocol.AnyCodable([
|
|
OpenClawProtocol.AnyCodable([
|
|
"type": OpenClawProtocol.AnyCodable("text"),
|
|
"text": OpenClawProtocol.AnyCodable("spoken transcript"),
|
|
]),
|
|
]),
|
|
"timestamp": OpenClawProtocol.AnyCodable(1234.5),
|
|
]),
|
|
])
|
|
let frame = EventFrame(type: "event", event: "session.message", payload: payload, seq: 1, stateversion: nil)
|
|
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame))
|
|
|
|
switch mapped {
|
|
case let .sessionMessage(message):
|
|
#expect(message.sessionKey == "agent:main:main")
|
|
#expect(message.messageId == "msg-1")
|
|
#expect(message.messageSeq == 7)
|
|
#expect(message.message?.role == "user")
|
|
#expect(message.message?.content.first?.text == "spoken transcript")
|
|
default:
|
|
Issue.record("expected .sessionMessage from session.message event, got \(String(describing: mapped))")
|
|
}
|
|
}
|
|
|
|
@Test func `unknown event maps to nil`() {
|
|
let frame = EventFrame(
|
|
type: "event",
|
|
event: "unknown",
|
|
payload: OpenClawProtocol.AnyCodable(["a": OpenClawProtocol.AnyCodable(1)]),
|
|
seq: 1,
|
|
stateversion: nil)
|
|
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame))
|
|
#expect(mapped == nil)
|
|
}
|
|
|
|
@Test func `seq gap maps to seq gap`() {
|
|
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.seqGap(expected: 1, received: 9))
|
|
#expect({
|
|
if case .seqGap = mapped {
|
|
return true
|
|
}
|
|
return false
|
|
}())
|
|
}
|
|
}
|