mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
df8fedcc00
* fix(macos): isolate named-profile development project roots * fix(macos): report actual direct gateway connection failures * fix(macos): satisfy SwiftLint remote recovery line limit * fix(macos): sync native IPv6 endpoint localization inventory * fix(macos): keep IPv6 endpoint formatting out of localization inventory
192 lines
6.4 KiB
Swift
192 lines
6.4 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import OpenClaw
|
|
|
|
struct ControlChannelStateDebouncerTests {
|
|
@Test func `terminal states apply immediately`() {
|
|
let start = Date(timeIntervalSince1970: 1000)
|
|
var debouncer = ControlChannelStateDebouncer(interval: 0.5, lastAppliedAt: start)
|
|
|
|
let degradedDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connecting,
|
|
newState: .degraded("gateway unavailable"),
|
|
now: start.addingTimeInterval(0.1))
|
|
#expect(degradedDelay != nil)
|
|
|
|
let connectedDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connecting,
|
|
newState: .connected,
|
|
now: start.addingTimeInterval(0.2))
|
|
#expect(connectedDelay == nil)
|
|
|
|
let afterTerminalDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connected,
|
|
newState: .connecting,
|
|
now: start.addingTimeInterval(0.3))
|
|
#expect(afterTerminalDelay == nil)
|
|
}
|
|
|
|
@Test func `nonterminal states are debounced within interval`() {
|
|
let start = Date(timeIntervalSince1970: 1000)
|
|
var debouncer = ControlChannelStateDebouncer(interval: 0.5, lastAppliedAt: start)
|
|
|
|
let soonDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connecting,
|
|
newState: .degraded("gateway unavailable"),
|
|
now: start.addingTimeInterval(0.1))
|
|
#expect(soonDelay != nil)
|
|
#expect(abs((soonDelay ?? 0) - 0.4) < 0.001)
|
|
|
|
let afterWindowDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connecting,
|
|
newState: .degraded("gateway unavailable"),
|
|
now: start.addingTimeInterval(0.6))
|
|
#expect(afterWindowDelay == nil)
|
|
}
|
|
|
|
@Test func `deferred apply resets debounce window`() {
|
|
let start = Date(timeIntervalSince1970: 1000)
|
|
var debouncer = ControlChannelStateDebouncer(interval: 0.5, lastAppliedAt: start)
|
|
|
|
debouncer.recordDeferredApply(at: start.addingTimeInterval(0.5))
|
|
|
|
let delayAfterDeferredUpdate = debouncer.delayBeforeApplying(
|
|
currentState: .degraded("gateway unavailable"),
|
|
newState: .connecting,
|
|
now: start.addingTimeInterval(0.7))
|
|
#expect(delayAfterDeferredUpdate != nil)
|
|
#expect(abs((delayAfterDeferredUpdate ?? 0) - 0.3) < 0.001)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
struct ControlChannelGatewayMessageTests {
|
|
@Test(arguments: [
|
|
URLError.Code.cannotFindHost,
|
|
URLError.Code.cannotConnectToHost,
|
|
URLError.Code.cancelled,
|
|
URLError.Code.timedOut,
|
|
])
|
|
func `direct gateway failures identify their actual endpoint`(code: URLError.Code) {
|
|
let root: [String: Any] = [
|
|
"gateway": [
|
|
"mode": "remote",
|
|
"remote": [
|
|
"transport": "direct",
|
|
"url": "ws://127.0.0.1:42674",
|
|
],
|
|
],
|
|
]
|
|
|
|
let message = ControlChannel.friendlyGatewayMessage(URLError(code), configRoot: root)
|
|
|
|
#expect(message.contains("127.0.0.1:42674"))
|
|
#expect(!message.contains("SSH"))
|
|
}
|
|
|
|
@Test func `direct gateway diagnostics never expose URL credentials`() {
|
|
let root: [String: Any] = [
|
|
"gateway": [
|
|
"mode": "remote",
|
|
"remote": [
|
|
"transport": "direct",
|
|
"url": "wss://user:secret@gateway.example:9443/path?token=private",
|
|
],
|
|
],
|
|
]
|
|
|
|
let message = ControlChannel.friendlyGatewayMessage(
|
|
URLError(.cannotConnectToHost),
|
|
configRoot: root)
|
|
|
|
#expect(message.contains("gateway.example:9443"))
|
|
#expect(!message.contains("secret"))
|
|
#expect(!message.contains("private"))
|
|
}
|
|
|
|
@Test func `direct secure gateway diagnostics use the default TLS port`() {
|
|
let root: [String: Any] = [
|
|
"gateway": [
|
|
"mode": "remote",
|
|
"remote": [
|
|
"transport": "direct",
|
|
"url": "wss://gateway.example",
|
|
],
|
|
],
|
|
]
|
|
|
|
let message = ControlChannel.friendlyGatewayMessage(
|
|
URLError(.cannotConnectToHost),
|
|
configRoot: root)
|
|
|
|
#expect(message.contains("gateway.example:443"))
|
|
}
|
|
|
|
@Test func `direct IPv6 gateway diagnostics bracket the endpoint host`() {
|
|
let root: [String: Any] = [
|
|
"gateway": [
|
|
"mode": "remote",
|
|
"remote": [
|
|
"transport": "direct",
|
|
"url": "wss://[fd12:3456:789a::1]:9443",
|
|
],
|
|
],
|
|
]
|
|
|
|
let message = ControlChannel.friendlyGatewayMessage(
|
|
URLError(.cannotConnectToHost),
|
|
configRoot: root)
|
|
|
|
#expect(message.contains("[fd12:3456:789a::1]:9443"))
|
|
}
|
|
|
|
@Test func `SSH gateway failures preserve tunnel recovery guidance`() {
|
|
let root: [String: Any] = [
|
|
"gateway": [
|
|
"mode": "remote",
|
|
"remote": ["transport": "ssh"],
|
|
],
|
|
]
|
|
|
|
let message = ControlChannel.friendlyGatewayMessage(
|
|
URLError(.cannotConnectToHost),
|
|
configRoot: root)
|
|
|
|
#expect(message.contains("localhost:"))
|
|
#expect(message.contains("SSH tunnel"))
|
|
}
|
|
|
|
@Test func `direct gateway handshake failures identify the remote endpoint`() {
|
|
let root: [String: Any] = [
|
|
"gateway": [
|
|
"mode": "remote",
|
|
"remote": [
|
|
"transport": "direct",
|
|
"url": "wss://gateway.example:9443",
|
|
],
|
|
],
|
|
]
|
|
let error = NSError(
|
|
domain: "Gateway",
|
|
code: 1,
|
|
userInfo: [NSLocalizedDescriptionKey: "hello failed (unexpected response)"])
|
|
|
|
let message = ControlChannel.friendlyGatewayMessage(error, configRoot: root)
|
|
|
|
#expect(message.contains("gateway.example:9443"))
|
|
#expect(!message.contains("SSH"))
|
|
}
|
|
|
|
@Test func `local gateway failures preserve local recovery guidance`() {
|
|
let root: [String: Any] = ["gateway": ["mode": "local"]]
|
|
|
|
let message = ControlChannel.friendlyGatewayMessage(
|
|
URLError(.cannotConnectToHost),
|
|
configRoot: root)
|
|
|
|
#expect(message.contains("localhost:"))
|
|
#expect(message.contains("ensure the gateway is running"))
|
|
#expect(!message.contains("SSH"))
|
|
}
|
|
}
|