fix(macos): clean swiftformat pass and sendable warning

This commit is contained in:
Peter Steinberger
2026-03-08 13:22:46 +00:00
parent eb0758e172
commit 53fb317e7f
129 changed files with 505 additions and 512 deletions

View File

@@ -6,14 +6,14 @@ import OpenClawKit
import OSLog import OSLog
actor CameraCaptureService { actor CameraCaptureService {
struct CameraDeviceInfo: Encodable, Sendable { struct CameraDeviceInfo: Encodable {
let id: String let id: String
let name: String let name: String
let position: String let position: String
let deviceType: String let deviceType: String
} }
enum CameraError: LocalizedError, Sendable { enum CameraError: LocalizedError {
case cameraUnavailable case cameraUnavailable
case microphoneUnavailable case microphoneUnavailable
case permissionDenied(kind: String) case permissionDenied(kind: String)

View File

@@ -2,7 +2,7 @@ import Foundation
import OpenClawProtocol import OpenClawProtocol
enum ConfigStore { enum ConfigStore {
struct Overrides: Sendable { struct Overrides {
var isRemoteMode: (@Sendable () async -> Bool)? var isRemoteMode: (@Sendable () async -> Bool)?
var loadLocal: (@MainActor @Sendable () -> [String: Any])? var loadLocal: (@MainActor @Sendable () -> [String: Any])?
var saveLocal: (@MainActor @Sendable ([String: Any]) -> Void)? var saveLocal: (@MainActor @Sendable ([String: Any]) -> Void)?

View File

@@ -1,13 +1,13 @@
import Foundation import Foundation
enum EffectiveConnectionModeSource: Sendable, Equatable { enum EffectiveConnectionModeSource: Equatable {
case configMode case configMode
case configRemoteURL case configRemoteURL
case userDefaults case userDefaults
case onboarding case onboarding
} }
struct EffectiveConnectionMode: Sendable, Equatable { struct EffectiveConnectionMode: Equatable {
let mode: AppState.ConnectionMode let mode: AppState.ConnectionMode
let source: EffectiveConnectionModeSource let source: EffectiveConnectionModeSource
} }

View File

@@ -14,7 +14,7 @@ struct ControlHeartbeatEvent: Codable {
let reason: String? let reason: String?
} }
struct ControlAgentEvent: Codable, Sendable, Identifiable { struct ControlAgentEvent: Codable, Identifiable {
var id: String { var id: String {
"\(self.runId)-\(self.seq)" "\(self.runId)-\(self.seq)"
} }

View File

@@ -226,7 +226,7 @@ struct CronJob: Identifiable, Codable, Equatable {
} }
} }
struct CronEvent: Codable, Sendable { struct CronEvent: Codable {
let jobId: String let jobId: String
let action: String let action: String
let runAtMs: Int? let runAtMs: Int?
@@ -237,7 +237,7 @@ struct CronEvent: Codable, Sendable {
let nextRunAtMs: Int? let nextRunAtMs: Int?
} }
struct CronRunLogEntry: Codable, Identifiable, Sendable { struct CronRunLogEntry: Codable, Identifiable {
var id: String { var id: String {
"\(self.jobId)-\(self.ts)" "\(self.jobId)-\(self.ts)"
} }

View File

@@ -1,6 +1,6 @@
import Foundation import Foundation
struct DevicePresentation: Sendable { struct DevicePresentation {
let title: String let title: String
let symbol: String? let symbol: String?
} }

View File

@@ -7,7 +7,7 @@ actor DiagnosticsFileLog {
private let maxBytes: Int64 = 5 * 1024 * 1024 private let maxBytes: Int64 = 5 * 1024 * 1024
private let maxBackups = 5 private let maxBackups = 5
struct Record: Codable, Sendable { struct Record: Codable {
let ts: String let ts: String
let pid: Int32 let pid: Int32
let category: String let category: String

View File

@@ -84,13 +84,13 @@ enum ExecAsk: String, CaseIterable, Codable, Identifiable {
} }
} }
enum ExecApprovalDecision: String, Codable, Sendable { enum ExecApprovalDecision: String, Codable {
case allowOnce = "allow-once" case allowOnce = "allow-once"
case allowAlways = "allow-always" case allowAlways = "allow-always"
case deny case deny
} }
enum ExecAllowlistPatternValidationReason: String, Codable, Sendable, Equatable { enum ExecAllowlistPatternValidationReason: String, Codable, Equatable {
case empty case empty
case missingPathComponent case missingPathComponent
@@ -104,12 +104,12 @@ enum ExecAllowlistPatternValidationReason: String, Codable, Sendable, Equatable
} }
} }
enum ExecAllowlistPatternValidation: Sendable, Equatable { enum ExecAllowlistPatternValidation: Equatable {
case valid(String) case valid(String)
case invalid(ExecAllowlistPatternValidationReason) case invalid(ExecAllowlistPatternValidationReason)
} }
struct ExecAllowlistRejectedEntry: Sendable, Equatable { struct ExecAllowlistRejectedEntry: Equatable {
let id: UUID let id: UUID
let pattern: String let pattern: String
let reason: ExecAllowlistPatternValidationReason let reason: ExecAllowlistPatternValidationReason
@@ -753,7 +753,7 @@ enum ExecApprovalHelpers {
} }
} }
struct ExecEventPayload: Codable, Sendable { struct ExecEventPayload: Codable {
var sessionKey: String var sessionKey: String
var runId: String var runId: String
var host: String var host: String

View File

@@ -11,7 +11,7 @@ final class ExecApprovalsGatewayPrompter {
private let logger = Logger(subsystem: "ai.openclaw", category: "exec-approvals.gateway") private let logger = Logger(subsystem: "ai.openclaw", category: "exec-approvals.gateway")
private var task: Task<Void, Never>? private var task: Task<Void, Never>?
struct GatewayApprovalRequest: Codable, Sendable { struct GatewayApprovalRequest: Codable {
var id: String var id: String
var request: ExecApprovalPromptRequest var request: ExecApprovalPromptRequest
var createdAtMs: Int var createdAtMs: Int

View File

@@ -5,7 +5,7 @@ import Foundation
import OpenClawKit import OpenClawKit
import OSLog import OSLog
struct ExecApprovalPromptRequest: Codable, Sendable { struct ExecApprovalPromptRequest: Codable {
var command: String var command: String
var cwd: String? var cwd: String?
var host: String? var host: String?

View File

@@ -1,6 +1,6 @@
import Foundation import Foundation
struct ExecCommandResolution: Sendable { struct ExecCommandResolution {
let rawExecutable: String let rawExecutable: String
let resolvedPath: String? let resolvedPath: String?
let executableName: String let executableName: String

View File

@@ -6,7 +6,7 @@ import OSLog
private let gatewayConnectionLogger = Logger(subsystem: "ai.openclaw", category: "gateway.connection") private let gatewayConnectionLogger = Logger(subsystem: "ai.openclaw", category: "gateway.connection")
enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable { enum GatewayAgentChannel: String, Codable, CaseIterable {
case last case last
case whatsapp case whatsapp
case telegram case telegram
@@ -33,7 +33,7 @@ enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
} }
} }
struct GatewayAgentInvocation: Sendable { struct GatewayAgentInvocation {
var message: String var message: String
var sessionKey: String = "main" var sessionKey: String = "main"
var thinking: String? var thinking: String?
@@ -53,7 +53,7 @@ actor GatewayConnection {
typealias Config = (url: URL, token: String?, password: String?) typealias Config = (url: URL, token: String?, password: String?)
enum Method: String, Sendable { enum Method: String {
case agent case agent
case status case status
case setHeartbeats = "set-heartbeats" case setHeartbeats = "set-heartbeats"
@@ -428,9 +428,9 @@ actor GatewayConnection {
// MARK: - Typed gateway API // MARK: - Typed gateway API
extension GatewayConnection { extension GatewayConnection {
struct ConfigGetSnapshot: Decodable, Sendable { struct ConfigGetSnapshot: Decodable {
struct SnapshotConfig: Decodable, Sendable { struct SnapshotConfig: Decodable {
struct Session: Decodable, Sendable { struct Session: Decodable {
let mainKey: String? let mainKey: String?
let scope: String? let scope: String?
} }
@@ -729,7 +729,7 @@ extension GatewayConnection {
// MARK: - Cron // MARK: - Cron
struct CronSchedulerStatus: Decodable, Sendable { struct CronSchedulerStatus: Decodable {
let enabled: Bool let enabled: Bool
let storePath: String let storePath: String
let jobs: Int let jobs: Int

View File

@@ -2,7 +2,7 @@ import ConcurrencyExtras
import Foundation import Foundation
import OSLog import OSLog
enum GatewayEndpointState: Sendable, Equatable { enum GatewayEndpointState: Equatable {
case ready(mode: AppState.ConnectionMode, url: URL, token: String?, password: String?) case ready(mode: AppState.ConnectionMode, url: URL, token: String?, password: String?)
case connecting(mode: AppState.ConnectionMode, detail: String) case connecting(mode: AppState.ConnectionMode, detail: String)
case unavailable(mode: AppState.ConnectionMode, reason: String) case unavailable(mode: AppState.ConnectionMode, reason: String)
@@ -24,14 +24,14 @@ actor GatewayEndpointStore {
] ]
private static let remoteConnectingDetail = "Connecting to remote gateway…" private static let remoteConnectingDetail = "Connecting to remote gateway…"
private static let staticLogger = Logger(subsystem: "ai.openclaw", category: "gateway-endpoint") private static let staticLogger = Logger(subsystem: "ai.openclaw", category: "gateway-endpoint")
private enum EnvOverrideWarningKind: Sendable { private enum EnvOverrideWarningKind {
case token case token
case password case password
} }
private static let envOverrideWarnings = LockIsolated((token: false, password: false)) private static let envOverrideWarnings = LockIsolated((token: false, password: false))
struct Deps: Sendable { struct Deps {
let mode: @Sendable () async -> AppState.ConnectionMode let mode: @Sendable () async -> AppState.ConnectionMode
let token: @Sendable () -> String? let token: @Sendable () -> String?
let password: @Sendable () -> String? let password: @Sendable () -> String?

View File

@@ -3,7 +3,7 @@ import OpenClawIPC
import OSLog import OSLog
/// Lightweight SemVer helper (major.minor.patch only) for gateway compatibility checks. /// Lightweight SemVer helper (major.minor.patch only) for gateway compatibility checks.
struct Semver: Comparable, CustomStringConvertible, Sendable { struct Semver: Comparable, CustomStringConvertible {
let major: Int let major: Int
let minor: Int let minor: Int
let patch: Int let patch: Int

View File

@@ -3,14 +3,14 @@ import Network
import Observation import Observation
import SwiftUI import SwiftUI
struct HealthSnapshot: Codable, Sendable { struct HealthSnapshot: Codable {
struct ChannelSummary: Codable, Sendable { struct ChannelSummary: Codable {
struct Probe: Codable, Sendable { struct Probe: Codable {
struct Bot: Codable, Sendable { struct Bot: Codable {
let username: String? let username: String?
} }
struct Webhook: Codable, Sendable { struct Webhook: Codable {
let url: String? let url: String?
} }
@@ -29,13 +29,13 @@ struct HealthSnapshot: Codable, Sendable {
let lastProbeAt: Double? let lastProbeAt: Double?
} }
struct SessionInfo: Codable, Sendable { struct SessionInfo: Codable {
let key: String let key: String
let updatedAt: Double? let updatedAt: Double?
let age: Double? let age: Double?
} }
struct Sessions: Codable, Sendable { struct Sessions: Codable {
let path: String let path: String
let count: Int let count: Int
let recent: [SessionInfo] let recent: [SessionInfo]

View File

@@ -22,7 +22,7 @@ enum HostEnvSecurityPolicy {
"PS4", "PS4",
"GCONV_PATH", "GCONV_PATH",
"IFS", "IFS",
"SSLKEYLOGFILE" "SSLKEYLOGFILE",
] ]
static let blockedOverrideKeys: Set<String> = [ static let blockedOverrideKeys: Set<String> = [
@@ -50,17 +50,17 @@ enum HostEnvSecurityPolicy {
"OPENSSL_ENGINES", "OPENSSL_ENGINES",
"PYTHONSTARTUP", "PYTHONSTARTUP",
"WGETRC", "WGETRC",
"CURL_HOME" "CURL_HOME",
] ]
static let blockedOverridePrefixes: [String] = [ static let blockedOverridePrefixes: [String] = [
"GIT_CONFIG_", "GIT_CONFIG_",
"NPM_CONFIG_" "NPM_CONFIG_",
] ]
static let blockedPrefixes: [String] = [ static let blockedPrefixes: [String] = [
"DYLD_", "DYLD_",
"LD_", "LD_",
"BASH_FUNC_" "BASH_FUNC_",
] ]
} }

View File

@@ -1,7 +1,7 @@
import Foundation import Foundation
enum Launchctl { enum Launchctl {
struct Result: Sendable { struct Result {
let status: Int32 let status: Int32
let output: String let output: String
} }
@@ -26,7 +26,7 @@ enum Launchctl {
} }
} }
struct LaunchAgentPlistSnapshot: Equatable, Sendable { struct LaunchAgentPlistSnapshot: Equatable {
let programArguments: [String] let programArguments: [String]
let environment: [String: String] let environment: [String: String]
let stdoutPath: String? let stdoutPath: String?

View File

@@ -122,7 +122,7 @@ actor MacNodeBrowserProxy {
} }
} }
let profile = params.profile?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" let profile = params.profile?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if !profile.isEmpty && !queryItems.contains(where: { $0.name == "profile" }) { if !profile.isEmpty, !queryItems.contains(where: { $0.name == "profile" }) {
queryItems.append(URLQueryItem(name: "profile", value: profile)) queryItems.append(URLQueryItem(name: "profile", value: profile))
} }
if !queryItems.isEmpty { if !queryItems.isEmpty {
@@ -172,7 +172,7 @@ actor MacNodeBrowserProxy {
} }
if let text = String(data: data, encoding: .utf8)? if let text = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines), .trimmingCharacters(in: .whitespacesAndNewlines),
!text.isEmpty !text.isEmpty
{ {
return text return text
} }

View File

@@ -1,10 +1,10 @@
import Foundation import Foundation
enum MacNodeScreenCommand: String, Codable, Sendable { enum MacNodeScreenCommand: String, Codable {
case record = "screen.record" case record = "screen.record"
} }
struct MacNodeScreenRecordParams: Codable, Sendable, Equatable { struct MacNodeScreenRecordParams: Codable, Equatable {
var screenIndex: Int? var screenIndex: Int?
var durationMs: Int? var durationMs: Int?
var fps: Double? var fps: Double?

View File

@@ -87,7 +87,7 @@ enum OverlayPanelFactory {
offsetX: CGFloat = 6, offsetX: CGFloat = 6,
offsetY: CGFloat = 6, offsetY: CGFloat = 6,
duration: TimeInterval = 0.16, duration: TimeInterval = 0.16,
completion: @escaping () -> Void) completion: @escaping @MainActor @Sendable () -> Void)
{ {
let target = window.frame.offsetBy(dx: offsetX, dy: offsetY) let target = window.frame.offsetBy(dx: offsetX, dy: offsetY)
NSAnimationContext.runAnimationGroup { context in NSAnimationContext.runAnimationGroup { context in
@@ -96,7 +96,7 @@ enum OverlayPanelFactory {
window.animator().setFrame(target, display: true) window.animator().setFrame(target, display: true)
window.animator().alphaValue = 0 window.animator().alphaValue = 0
} completionHandler: { } completionHandler: {
completion() Task { @MainActor in completion() }
} }
} }
@@ -109,10 +109,8 @@ enum OverlayPanelFactory {
onHidden: @escaping @MainActor () -> Void) onHidden: @escaping @MainActor () -> Void)
{ {
self.animateDismiss(window: window, offsetX: offsetX, offsetY: offsetY, duration: duration) { self.animateDismiss(window: window, offsetX: offsetX, offsetY: offsetY, duration: duration) {
Task { @MainActor in window.orderOut(nil)
window.orderOut(nil) onHidden()
onHidden()
}
} }
} }

View File

@@ -56,7 +56,7 @@ final class PeekabooBridgeHostCoordinator {
private func startIfNeeded() async { private func startIfNeeded() async {
guard self.host == nil else { return } guard self.host == nil else { return }
var allowlistedTeamIDs: Set<String> = ["Y5PE65HELJ"] var allowlistedTeamIDs: Set = ["Y5PE65HELJ"]
if let teamID = Self.currentTeamID() { if let teamID = Self.currentTeamID() {
allowlistedTeamIDs.insert(teamID) allowlistedTeamIDs.insert(teamID)
} }

View File

@@ -15,7 +15,7 @@ actor PortGuardian {
let timestamp: TimeInterval let timestamp: TimeInterval
} }
struct Descriptor: Sendable { struct Descriptor {
let pid: Int32 let pid: Int32
let command: String let command: String
let executablePath: String? let executablePath: String?

View File

@@ -4,13 +4,13 @@ import OpenClawProtocol
import OSLog import OSLog
import SwiftUI import SwiftUI
struct SessionPreviewItem: Identifiable, Sendable { struct SessionPreviewItem: Identifiable {
let id: String let id: String
let role: PreviewRole let role: PreviewRole
let text: String let text: String
} }
enum PreviewRole: String, Sendable { enum PreviewRole: String {
case user case user
case assistant case assistant
case tool case tool
@@ -114,7 +114,7 @@ extension SessionPreviewCache {
} }
#endif #endif
struct SessionMenuPreviewSnapshot: Sendable { struct SessionMenuPreviewSnapshot {
let items: [SessionPreviewItem] let items: [SessionPreviewItem]
let status: SessionMenuPreviewView.LoadStatus let status: SessionMenuPreviewView.LoadStatus
} }

View File

@@ -152,7 +152,7 @@ final class TalkAudioPlayer: NSObject, @preconcurrency AVAudioPlayerDelegate {
} }
} }
struct TalkPlaybackResult: Sendable { struct TalkPlaybackResult {
let finished: Bool let finished: Bool
let interruptedAt: Double? let interruptedAt: Double?
} }

View File

@@ -2,7 +2,7 @@ import AppKit
import Foundation import Foundation
import OSLog import OSLog
enum VoiceWakeChime: Codable, Equatable, Sendable { enum VoiceWakeChime: Codable, Equatable {
case none case none
case system(name: String) case system(name: String)
case custom(displayName: String, bookmark: Data) case custom(displayName: String, bookmark: Data)

View File

@@ -32,7 +32,7 @@ enum VoiceWakeForwarder {
} }
} }
struct ForwardOptions: Sendable { struct ForwardOptions {
var sessionKey: String = "main" var sessionKey: String = "main"
var thinking: String = "low" var thinking: String = "low"
var deliver: Bool = true var deliver: Bool = true

View File

@@ -16,7 +16,7 @@ private enum WebChatSwiftUILayout {
static let anchorPadding: CGFloat = 8 static let anchorPadding: CGFloat = 8
} }
struct MacGatewayChatTransport: OpenClawChatTransport, Sendable { struct MacGatewayChatTransport: OpenClawChatTransport {
func requestHistory(sessionKey: String) async throws -> OpenClawChatHistoryPayload { func requestHistory(sessionKey: String) async throws -> OpenClawChatHistoryPayload {
try await GatewayConnection.shared.chatHistory(sessionKey: sessionKey) try await GatewayConnection.shared.chatHistory(sessionKey: sessionKey)
} }

View File

@@ -374,9 +374,9 @@ public final class GatewayDiscoveryModel {
if let host = gateway.serviceHost? if let host = gateway.serviceHost?
.trimmingCharacters(in: .whitespacesAndNewlines) .trimmingCharacters(in: .whitespacesAndNewlines)
.lowercased(), .lowercased(),
!host.isEmpty, !host.isEmpty,
let port = gateway.servicePort, let port = gateway.servicePort,
port > 0 port > 0
{ {
return "endpoint|\(host):\(port)" return "endpoint|\(host):\(port)"
} }
@@ -674,7 +674,7 @@ public final class GatewayDiscoveryModel {
} }
} }
struct ResolvedGatewayService: Equatable, Sendable { struct ResolvedGatewayService: Equatable {
var txt: [String: String] var txt: [String: String]
var host: String? var host: String?
var port: Int? var port: Int?

View File

@@ -1,7 +1,7 @@
import Foundation import Foundation
import OpenClawKit import OpenClawKit
struct TailscaleServeGatewayBeacon: Sendable, Equatable { struct TailscaleServeGatewayBeacon: Equatable {
var displayName: String var displayName: String
var tailnetDns: String var tailnetDns: String
var host: String var host: String
@@ -13,7 +13,7 @@ enum TailscaleServeGatewayDiscovery {
private static let probeConcurrency = 6 private static let probeConcurrency = 6
private static let defaultProbeTimeoutSeconds: TimeInterval = 1.6 private static let defaultProbeTimeoutSeconds: TimeInterval = 1.6
struct DiscoveryContext: Sendable { struct DiscoveryContext {
var tailscaleStatus: @Sendable () async -> String? var tailscaleStatus: @Sendable () async -> String?
var probeHost: @Sendable (_ host: String, _ timeout: TimeInterval) async -> Bool var probeHost: @Sendable (_ host: String, _ timeout: TimeInterval) async -> Bool
@@ -85,13 +85,13 @@ enum TailscaleServeGatewayDiscovery {
} }
} }
private struct Candidate: Sendable { private struct Candidate {
var dnsName: String var dnsName: String
var displayName: String var displayName: String
} }
private static func collectCandidates(status: TailscaleStatus) -> [Candidate] { private static func collectCandidates(status: TailscaleStatus) -> [Candidate] {
let selfDns = normalizeDnsName(status.selfNode?.dnsName) let selfDns = self.normalizeDnsName(status.selfNode?.dnsName)
var out: [Candidate] = [] var out: [Candidate] = []
var seen = Set<String>() var seen = Set<String>()
@@ -112,7 +112,7 @@ enum TailscaleServeGatewayDiscovery {
out.append(Candidate( out.append(Candidate(
dnsName: dnsName, dnsName: dnsName,
displayName: displayName(hostName: node.hostName, dnsName: dnsName))) displayName: self.displayName(hostName: node.hostName, dnsName: dnsName)))
if out.count >= self.maxCandidates { if out.count >= self.maxCandidates {
break break
@@ -257,7 +257,7 @@ enum TailscaleServeGatewayDiscovery {
operation: { operation: {
while true { while true {
let message = try await task.receive() let message = try await task.receive()
if isConnectChallenge(message: message) { if self.isConnectChallenge(message: message) {
return true return true
} }
} }

View File

@@ -1,7 +1,7 @@
import Foundation import Foundation
import OpenClawKit import OpenClawKit
struct WideAreaGatewayBeacon: Sendable, Equatable { struct WideAreaGatewayBeacon: Equatable {
var instanceName: String var instanceName: String
var displayName: String var displayName: String
var host: String var host: String
@@ -19,7 +19,7 @@ enum WideAreaGatewayDiscovery {
private static let defaultTimeoutSeconds: TimeInterval = 0.2 private static let defaultTimeoutSeconds: TimeInterval = 0.2
private static let nameserverProbeConcurrency = 6 private static let nameserverProbeConcurrency = 6
struct DiscoveryContext: Sendable { struct DiscoveryContext {
var tailscaleStatus: @Sendable () -> String? var tailscaleStatus: @Sendable () -> String?
var dig: @Sendable (_ args: [String], _ timeout: TimeInterval) -> String? var dig: @Sendable (_ args: [String], _ timeout: TimeInterval) -> String?

View File

@@ -3,11 +3,10 @@ import OpenClawProtocol
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite
@MainActor @MainActor
struct AgentEventStoreTests { struct AgentEventStoreTests {
@Test @Test
func appendAndClear() { func `append and clear`() {
let store = AgentEventStore() let store = AgentEventStore()
#expect(store.events.isEmpty) #expect(store.events.isEmpty)
@@ -25,7 +24,7 @@ struct AgentEventStoreTests {
} }
@Test @Test
func trimsToMaxEvents() { func `trims to max events`() {
let store = AgentEventStore() let store = AgentEventStore()
for i in 1...401 { for i in 1...401 {
store.append(ControlAgentEvent( store.append(ControlAgentEvent(

View File

@@ -2,10 +2,9 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite
struct AgentWorkspaceTests { struct AgentWorkspaceTests {
@Test @Test
func displayPathUsesTildeForHome() { func `display path uses tilde for home`() {
let home = FileManager().homeDirectoryForCurrentUser let home = FileManager().homeDirectoryForCurrentUser
#expect(AgentWorkspace.displayPath(for: home) == "~") #expect(AgentWorkspace.displayPath(for: home) == "~")
@@ -14,20 +13,20 @@ struct AgentWorkspaceTests {
} }
@Test @Test
func resolveWorkspaceURLExpandsTilde() { func `resolve workspace URL expands tilde`() {
let url = AgentWorkspace.resolveWorkspaceURL(from: "~/tmp") let url = AgentWorkspace.resolveWorkspaceURL(from: "~/tmp")
#expect(url.path.hasSuffix("/tmp")) #expect(url.path.hasSuffix("/tmp"))
} }
@Test @Test
func agentsURLAppendsFilename() { func `agents URL appends filename`() {
let root = URL(fileURLWithPath: "/tmp/ws", isDirectory: true) let root = URL(fileURLWithPath: "/tmp/ws", isDirectory: true)
let url = AgentWorkspace.agentsURL(workspaceURL: root) let url = AgentWorkspace.agentsURL(workspaceURL: root)
#expect(url.lastPathComponent == AgentWorkspace.agentsFilename) #expect(url.lastPathComponent == AgentWorkspace.agentsFilename)
} }
@Test @Test
func bootstrapCreatesAgentsFileWhenMissing() throws { func `bootstrap creates agents file when missing`() throws {
let tmp = FileManager().temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
@@ -50,7 +49,7 @@ struct AgentWorkspaceTests {
} }
@Test @Test
func bootstrapSafetyRejectsNonEmptyFolderWithoutAgents() throws { func `bootstrap safety rejects non empty folder without agents`() throws {
let tmp = FileManager().temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
@@ -63,7 +62,7 @@ struct AgentWorkspaceTests {
} }
@Test @Test
func bootstrapSafetyAllowsExistingAgentsFile() throws { func `bootstrap safety allows existing agents file`() throws {
let tmp = FileManager().temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
@@ -76,7 +75,7 @@ struct AgentWorkspaceTests {
} }
@Test @Test
func bootstrapSkipsBootstrapFileWhenWorkspaceHasContent() throws { func `bootstrap skips bootstrap file when workspace has content`() throws {
let tmp = FileManager().temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
@@ -91,7 +90,7 @@ struct AgentWorkspaceTests {
} }
@Test @Test
func needsBootstrapFalseWhenIdentityAlreadySet() throws { func `needs bootstrap false when identity already set`() throws {
let tmp = FileManager().temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }

View File

@@ -3,8 +3,8 @@ import OpenClawProtocol
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct AnyCodableEncodingTests { struct AnyCodableEncodingTests {
@Test func encodesSwiftArrayAndDictionaryValues() throws { @Test func `encodes swift array and dictionary values`() throws {
let payload: [String: Any] = [ let payload: [String: Any] = [
"tags": ["node", "ios"], "tags": ["node", "ios"],
"meta": ["count": 2], "meta": ["count": 2],
@@ -19,7 +19,7 @@ import Testing
#expect(obj["null"] is NSNull) #expect(obj["null"] is NSNull)
} }
@Test func protocolAnyCodableEncodesPrimitiveArrays() throws { @Test func `protocol any codable encodes primitive arrays`() throws {
let payload: [String: Any] = [ let payload: [String: Any] = [
"items": [1, "two", NSNull(), ["ok": true]], "items": [1, "two", NSNull(), ["ok": true]],
] ]

View File

@@ -2,15 +2,15 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct AudioInputDeviceObserverTests { struct AudioInputDeviceObserverTests {
@Test func hasUsableDefaultInputDeviceReturnsBool() { @Test func `has usable default input device returns bool`() {
// Smoke test: verifies the composition logic runs without crashing. // Smoke test: verifies the composition logic runs without crashing.
// Actual result depends on whether the host has an audio input device. // Actual result depends on whether the host has an audio input device.
let result = AudioInputDeviceObserver.hasUsableDefaultInputDevice() let result = AudioInputDeviceObserver.hasUsableDefaultInputDevice()
_ = result // suppress unused-variable warning; the assertion is "no crash" _ = result // suppress unused-variable warning; the assertion is "no crash"
} }
@Test func hasUsableDefaultInputDeviceConsistentWithComponents() { @Test func `has usable default input device consistent with components`() {
// When no default UID exists, the method must return false. // When no default UID exists, the method must return false.
// When a default UID exists, the result must match alive-set membership. // When a default UID exists, the result must match alive-set membership.
let uid = AudioInputDeviceObserver.defaultInputDeviceUID() let uid = AudioInputDeviceObserver.defaultInputDeviceUID()

View File

@@ -5,7 +5,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct CLIInstallerTests { struct CLIInstallerTests {
@Test func installedLocationFindsExecutable() throws { @Test func `installed location finds executable`() throws {
let fm = FileManager() let fm = FileManager()
let root = fm.temporaryDirectory.appendingPathComponent( let root = fm.temporaryDirectory.appendingPathComponent(
"openclaw-cli-installer-\(UUID().uuidString)") "openclaw-cli-installer-\(UUID().uuidString)")

View File

@@ -1,14 +1,14 @@
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct CameraCaptureServiceTests { struct CameraCaptureServiceTests {
@Test func normalizeSnapDefaults() { @Test func `normalize snap defaults`() {
let res = CameraCaptureService.normalizeSnap(maxWidth: nil, quality: nil) let res = CameraCaptureService.normalizeSnap(maxWidth: nil, quality: nil)
#expect(res.maxWidth == 1600) #expect(res.maxWidth == 1600)
#expect(res.quality == 0.9) #expect(res.quality == 0.9)
} }
@Test func normalizeSnapClampsValues() { @Test func `normalize snap clamps values`() {
let low = CameraCaptureService.normalizeSnap(maxWidth: -1, quality: -10) let low = CameraCaptureService.normalizeSnap(maxWidth: -1, quality: -10)
#expect(low.maxWidth == 1600) #expect(low.maxWidth == 1600)
#expect(low.quality == 0.05) #expect(low.quality == 0.05)

View File

@@ -2,8 +2,8 @@ import Foundation
import OpenClawIPC import OpenClawIPC
import Testing import Testing
@Suite struct CameraIPCTests { struct CameraIPCTests {
@Test func cameraSnapCodableRoundtrip() throws { @Test func `camera snap codable roundtrip`() throws {
let req: Request = .cameraSnap( let req: Request = .cameraSnap(
facing: .front, facing: .front,
maxWidth: 640, maxWidth: 640,
@@ -24,7 +24,7 @@ import Testing
} }
} }
@Test func cameraClipCodableRoundtrip() throws { @Test func `camera clip codable roundtrip`() throws {
let req: Request = .cameraClip( let req: Request = .cameraClip(
facing: .back, facing: .back,
durationMs: 3000, durationMs: 3000,
@@ -45,7 +45,7 @@ import Testing
} }
} }
@Test func cameraClipDefaultsIncludeAudioToTrueWhenMissing() throws { @Test func `camera clip defaults include audio to true when missing`() throws {
let json = """ let json = """
{"type":"cameraClip","durationMs":1234} {"type":"cameraClip","durationMs":1234}
""" """

View File

@@ -11,7 +11,7 @@ import Testing
return dir return dir
} }
@Test func detectsInPlaceFileWrites() async throws { @Test func `detects in place file writes`() async throws {
let dir = try self.makeTempDir() let dir = try self.makeTempDir()
defer { try? FileManager().removeItem(at: dir) } defer { try? FileManager().removeItem(at: dir) }

View File

@@ -2,8 +2,8 @@ import Foundation
import OpenClawIPC import OpenClawIPC
import Testing import Testing
@Suite struct CanvasIPCTests { struct CanvasIPCTests {
@Test func canvasPresentCodableRoundtrip() throws { @Test func `canvas present codable roundtrip`() throws {
let placement = CanvasPlacement(x: 10, y: 20, width: 640, height: 480) let placement = CanvasPlacement(x: 10, y: 20, width: 640, height: 480)
let req: Request = .canvasPresent(session: "main", path: "/index.html", placement: placement) let req: Request = .canvasPresent(session: "main", path: "/index.html", placement: placement)
@@ -23,7 +23,7 @@ import Testing
} }
} }
@Test func canvasPresentDecodesNilPlacementWhenMissing() throws { @Test func `canvas present decodes nil placement when missing`() throws {
let json = """ let json = """
{"type":"canvasPresent","session":"s","path":"/"} {"type":"canvasPresent","session":"s","path":"/"}
""" """

View File

@@ -7,7 +7,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct CanvasWindowSmokeTests { struct CanvasWindowSmokeTests {
@Test func panelControllerShowsAndHides() async throws { @Test func `panel controller shows and hides`() async throws {
let root = FileManager().temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-canvas-test-\(UUID().uuidString)") .appendingPathComponent("openclaw-canvas-test-\(UUID().uuidString)")
try FileManager().createDirectory(at: root, withIntermediateDirectories: true) try FileManager().createDirectory(at: root, withIntermediateDirectories: true)
@@ -30,7 +30,7 @@ struct CanvasWindowSmokeTests {
controller.close() controller.close()
} }
@Test func windowControllerShowsAndCloses() throws { @Test func `window controller shows and closes`() throws {
let root = FileManager().temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-canvas-test-\(UUID().uuidString)") .appendingPathComponent("openclaw-canvas-test-\(UUID().uuidString)")
try FileManager().createDirectory(at: root, withIntermediateDirectories: true) try FileManager().createDirectory(at: root, withIntermediateDirectories: true)

View File

@@ -41,7 +41,7 @@ private func makeChannelsStore(
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct ChannelsSettingsSmokeTests { struct ChannelsSettingsSmokeTests {
@Test func channelsSettingsBuildsBodyWithSnapshot() { @Test func `channels settings builds body with snapshot`() {
let store = makeChannelsStore( let store = makeChannelsStore(
channels: [ channels: [
"whatsapp": SnapshotAnyCodable([ "whatsapp": SnapshotAnyCodable([
@@ -108,7 +108,7 @@ struct ChannelsSettingsSmokeTests {
_ = view.body _ = view.body
} }
@Test func channelsSettingsBuildsBodyWithoutSnapshot() { @Test func `channels settings builds body without snapshot`() {
let store = makeChannelsStore( let store = makeChannelsStore(
channels: [ channels: [
"whatsapp": SnapshotAnyCodable([ "whatsapp": SnapshotAnyCodable([

View File

@@ -23,7 +23,7 @@ import Testing
return (tmp, pnpmPath) return (tmp, pnpmPath)
} }
@Test func prefersOpenClawBinary() throws { @Test func `prefers open claw binary`() throws {
let defaults = self.makeLocalDefaults() let defaults = self.makeLocalDefaults()
let tmp = try makeTempDirForTests() let tmp = try makeTempDirForTests()
@@ -36,7 +36,7 @@ import Testing
#expect(cmd.prefix(2).elementsEqual([openclawPath.path, "gateway"])) #expect(cmd.prefix(2).elementsEqual([openclawPath.path, "gateway"]))
} }
@Test func fallsBackToNodeAndScript() throws { @Test func `falls back to node and script`() throws {
let defaults = self.makeLocalDefaults() let defaults = self.makeLocalDefaults()
let tmp = try makeTempDirForTests() let tmp = try makeTempDirForTests()
@@ -63,7 +63,7 @@ import Testing
} }
} }
@Test func prefersOpenClawBinaryOverPnpm() throws { @Test func `prefers open claw binary over pnpm`() throws {
let defaults = self.makeLocalDefaults() let defaults = self.makeLocalDefaults()
let tmp = try makeTempDirForTests() let tmp = try makeTempDirForTests()
@@ -84,7 +84,7 @@ import Testing
#expect(cmd.prefix(2).elementsEqual([openclawPath.path, "rpc"])) #expect(cmd.prefix(2).elementsEqual([openclawPath.path, "rpc"]))
} }
@Test func usesOpenClawBinaryWithoutNodeRuntime() throws { @Test func `uses open claw binary without node runtime`() throws {
let defaults = self.makeLocalDefaults() let defaults = self.makeLocalDefaults()
let tmp = try makeTempDirForTests() let tmp = try makeTempDirForTests()
@@ -103,7 +103,7 @@ import Testing
#expect(cmd.prefix(2).elementsEqual([openclawPath.path, "gateway"])) #expect(cmd.prefix(2).elementsEqual([openclawPath.path, "gateway"]))
} }
@Test func fallsBackToPnpm() throws { @Test func `falls back to pnpm`() throws {
let defaults = self.makeLocalDefaults() let defaults = self.makeLocalDefaults()
let (tmp, pnpmPath) = try self.makeProjectRootWithPnpm() let (tmp, pnpmPath) = try self.makeProjectRootWithPnpm()
@@ -116,7 +116,7 @@ import Testing
#expect(cmd.prefix(4).elementsEqual([pnpmPath.path, "--silent", "openclaw", "rpc"])) #expect(cmd.prefix(4).elementsEqual([pnpmPath.path, "--silent", "openclaw", "rpc"]))
} }
@Test func pnpmKeepsExtraArgsAfterSubcommand() throws { @Test func `pnpm keeps extra args after subcommand`() throws {
let defaults = self.makeLocalDefaults() let defaults = self.makeLocalDefaults()
let (tmp, pnpmPath) = try self.makeProjectRootWithPnpm() let (tmp, pnpmPath) = try self.makeProjectRootWithPnpm()
@@ -131,7 +131,7 @@ import Testing
#expect(cmd.suffix(2).elementsEqual(["--timeout", "5"])) #expect(cmd.suffix(2).elementsEqual(["--timeout", "5"]))
} }
@Test func preferredPathsStartWithProjectNodeBins() throws { @Test func `preferred paths start with project node bins`() throws {
let tmp = try makeTempDirForTests() let tmp = try makeTempDirForTests()
CommandResolver.setProjectRoot(tmp.path) CommandResolver.setProjectRoot(tmp.path)
@@ -139,7 +139,7 @@ import Testing
#expect(first == tmp.appendingPathComponent("node_modules/.bin").path) #expect(first == tmp.appendingPathComponent("node_modules/.bin").path)
} }
@Test func buildsSSHCommandForRemoteMode() { @Test func `builds SSH command for remote mode`() {
let defaults = self.makeDefaults() let defaults = self.makeDefaults()
defaults.set(AppState.ConnectionMode.remote.rawValue, forKey: connectionModeKey) defaults.set(AppState.ConnectionMode.remote.rawValue, forKey: connectionModeKey)
defaults.set("openclaw@example.com:2222", forKey: remoteTargetKey) defaults.set("openclaw@example.com:2222", forKey: remoteTargetKey)
@@ -170,13 +170,13 @@ import Testing
} }
} }
@Test func rejectsUnsafeSSHTargets() { @Test func `rejects unsafe SSH targets`() {
#expect(CommandResolver.parseSSHTarget("-oProxyCommand=calc") == nil) #expect(CommandResolver.parseSSHTarget("-oProxyCommand=calc") == nil)
#expect(CommandResolver.parseSSHTarget("host:-oProxyCommand=calc") == nil) #expect(CommandResolver.parseSSHTarget("host:-oProxyCommand=calc") == nil)
#expect(CommandResolver.parseSSHTarget("user@host:2222")?.port == 2222) #expect(CommandResolver.parseSSHTarget("user@host:2222")?.port == 2222)
} }
@Test func configRootLocalOverridesRemoteDefaults() throws { @Test func `config root local overrides remote defaults`() throws {
let defaults = self.makeDefaults() let defaults = self.makeDefaults()
defaults.set(AppState.ConnectionMode.remote.rawValue, forKey: connectionModeKey) defaults.set(AppState.ConnectionMode.remote.rawValue, forKey: connectionModeKey)
defaults.set("openclaw@example.com:2222", forKey: remoteTargetKey) defaults.set("openclaw@example.com:2222", forKey: remoteTargetKey)

View File

@@ -4,7 +4,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct ConfigStoreTests { struct ConfigStoreTests {
@Test func loadUsesRemoteInRemoteMode() async { @Test func `load uses remote in remote mode`() async {
var localHit = false var localHit = false
var remoteHit = false var remoteHit = false
await ConfigStore._testSetOverrides(.init( await ConfigStore._testSetOverrides(.init(
@@ -20,7 +20,7 @@ struct ConfigStoreTests {
#expect(result["remote"] as? Bool == true) #expect(result["remote"] as? Bool == true)
} }
@Test func loadUsesLocalInLocalMode() async { @Test func `load uses local in local mode`() async {
var localHit = false var localHit = false
var remoteHit = false var remoteHit = false
await ConfigStore._testSetOverrides(.init( await ConfigStore._testSetOverrides(.init(
@@ -36,7 +36,7 @@ struct ConfigStoreTests {
#expect(result["local"] as? Bool == true) #expect(result["local"] as? Bool == true)
} }
@Test func saveRoutesToRemoteInRemoteMode() async throws { @Test func `save routes to remote in remote mode`() async throws {
var localHit = false var localHit = false
var remoteHit = false var remoteHit = false
await ConfigStore._testSetOverrides(.init( await ConfigStore._testSetOverrides(.init(
@@ -51,7 +51,7 @@ struct ConfigStoreTests {
#expect(!localHit) #expect(!localHit)
} }
@Test func saveRoutesToLocalInLocalMode() async throws { @Test func `save routes to local in local mode`() async throws {
var localHit = false var localHit = false
var remoteHit = false var remoteHit = false
await ConfigStore._testSetOverrides(.init( await ConfigStore._testSetOverrides(.init(

View File

@@ -4,7 +4,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
struct CoverageDumpTests { struct CoverageDumpTests {
@Test func periodicallyFlushCoverage() async { @Test func `periodically flush coverage`() async {
guard ProcessInfo.processInfo.environment["LLVM_PROFILE_FILE"] != nil else { return } guard ProcessInfo.processInfo.environment["LLVM_PROFILE_FILE"] != nil else { return }
guard let writeProfile = resolveProfileWriteFile() else { return } guard let writeProfile = resolveProfileWriteFile() else { return }
let deadline = Date().addingTimeInterval(4) let deadline = Date().addingTimeInterval(4)

View File

@@ -2,10 +2,9 @@ import AppKit
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite
@MainActor @MainActor
struct CritterIconRendererTests { struct CritterIconRendererTests {
@Test func makeIconRendersExpectedSize() { @Test func `make icon renders expected size`() {
let image = CritterIconRenderer.makeIcon( let image = CritterIconRenderer.makeIcon(
blink: 0.25, blink: 0.25,
legWiggle: 0.5, legWiggle: 0.5,
@@ -19,7 +18,7 @@ struct CritterIconRendererTests {
#expect(image.tiffRepresentation != nil) #expect(image.tiffRepresentation != nil)
} }
@Test func makeIconRendersWithBadge() { @Test func `make icon renders with badge`() {
let image = CritterIconRenderer.makeIcon( let image = CritterIconRenderer.makeIcon(
blink: 0, blink: 0,
legWiggle: 0, legWiggle: 0,
@@ -31,7 +30,7 @@ struct CritterIconRendererTests {
#expect(image.tiffRepresentation != nil) #expect(image.tiffRepresentation != nil)
} }
@Test func critterStatusLabelExercisesHelpers() async { @Test func `critter status label exercises helpers`() async {
await CritterStatusLabel.exerciseForTesting() await CritterStatusLabel.exerciseForTesting()
} }
} }

View File

@@ -15,17 +15,17 @@ struct CronJobEditorSmokeTests {
onSave: { _ in }) onSave: { _ in })
} }
@Test func statusPillBuildsBody() { @Test func `status pill builds body`() {
_ = StatusPill(text: "ok", tint: .green).body _ = StatusPill(text: "ok", tint: .green).body
_ = StatusPill(text: "disabled", tint: .secondary).body _ = StatusPill(text: "disabled", tint: .secondary).body
} }
@Test func cronJobEditorBuildsBodyForNewJob() { @Test func `cron job editor builds body for new job`() {
let view = self.makeEditor() let view = self.makeEditor()
_ = view.body _ = view.body
} }
@Test func cronJobEditorBuildsBodyForExistingJob() { @Test func `cron job editor builds body for existing job`() {
let channelsStore = ChannelsStore(isPreview: true) let channelsStore = ChannelsStore(isPreview: true)
let job = CronJob( let job = CronJob(
id: "job-1", id: "job-1",
@@ -60,12 +60,12 @@ struct CronJobEditorSmokeTests {
_ = view.body _ = view.body
} }
@Test func cronJobEditorExercisesBuilders() { @Test func `cron job editor exercises builders`() {
var view = self.makeEditor() var view = self.makeEditor()
view.exerciseForTesting() view.exerciseForTesting()
} }
@Test func cronJobEditorIncludesDeleteAfterRunForAtSchedule() { @Test func `cron job editor includes delete after run for at schedule`() {
let view = self.makeEditor() let view = self.makeEditor()
var root: [String: Any] = [:] var root: [String: Any] = [:]

View File

@@ -2,7 +2,6 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite
struct CronModelsTests { struct CronModelsTests {
private func makeCronJob( private func makeCronJob(
name: String, name: String,
@@ -26,14 +25,14 @@ struct CronModelsTests {
state: state) state: state)
} }
@Test func scheduleAtEncodesAndDecodes() throws { @Test func `schedule at encodes and decodes`() throws {
let schedule = CronSchedule.at(at: "2026-02-03T18:00:00Z") let schedule = CronSchedule.at(at: "2026-02-03T18:00:00Z")
let data = try JSONEncoder().encode(schedule) let data = try JSONEncoder().encode(schedule)
let decoded = try JSONDecoder().decode(CronSchedule.self, from: data) let decoded = try JSONDecoder().decode(CronSchedule.self, from: data)
#expect(decoded == schedule) #expect(decoded == schedule)
} }
@Test func scheduleAtDecodesLegacyAtMs() throws { @Test func `schedule at decodes legacy at ms`() throws {
let json = """ let json = """
{"kind":"at","atMs":1700000000000} {"kind":"at","atMs":1700000000000}
""" """
@@ -45,21 +44,21 @@ struct CronModelsTests {
} }
} }
@Test func scheduleEveryEncodesAndDecodesWithAnchor() throws { @Test func `schedule every encodes and decodes with anchor`() throws {
let schedule = CronSchedule.every(everyMs: 5000, anchorMs: 10000) let schedule = CronSchedule.every(everyMs: 5000, anchorMs: 10000)
let data = try JSONEncoder().encode(schedule) let data = try JSONEncoder().encode(schedule)
let decoded = try JSONDecoder().decode(CronSchedule.self, from: data) let decoded = try JSONDecoder().decode(CronSchedule.self, from: data)
#expect(decoded == schedule) #expect(decoded == schedule)
} }
@Test func scheduleCronEncodesAndDecodesWithTimezone() throws { @Test func `schedule cron encodes and decodes with timezone`() throws {
let schedule = CronSchedule.cron(expr: "*/5 * * * *", tz: "Europe/Vienna") let schedule = CronSchedule.cron(expr: "*/5 * * * *", tz: "Europe/Vienna")
let data = try JSONEncoder().encode(schedule) let data = try JSONEncoder().encode(schedule)
let decoded = try JSONDecoder().decode(CronSchedule.self, from: data) let decoded = try JSONDecoder().decode(CronSchedule.self, from: data)
#expect(decoded == schedule) #expect(decoded == schedule)
} }
@Test func payloadAgentTurnEncodesAndDecodes() throws { @Test func `payload agent turn encodes and decodes`() throws {
let payload = CronPayload.agentTurn( let payload = CronPayload.agentTurn(
message: "hello", message: "hello",
thinking: "low", thinking: "low",
@@ -73,7 +72,7 @@ struct CronModelsTests {
#expect(decoded == payload) #expect(decoded == payload)
} }
@Test func jobEncodesAndDecodesDeleteAfterRun() throws { @Test func `job encodes and decodes delete after run`() throws {
let job = CronJob( let job = CronJob(
id: "job-1", id: "job-1",
agentId: nil, agentId: nil,
@@ -94,7 +93,7 @@ struct CronModelsTests {
#expect(decoded.deleteAfterRun == true) #expect(decoded.deleteAfterRun == true)
} }
@Test func scheduleDecodeRejectsUnknownKind() { @Test func `schedule decode rejects unknown kind`() {
let json = """ let json = """
{"kind":"wat","at":"2026-02-03T18:00:00Z"} {"kind":"wat","at":"2026-02-03T18:00:00Z"}
""" """
@@ -103,7 +102,7 @@ struct CronModelsTests {
} }
} }
@Test func payloadDecodeRejectsUnknownKind() { @Test func `payload decode rejects unknown kind`() {
let json = """ let json = """
{"kind":"wat","text":"hello"} {"kind":"wat","text":"hello"}
""" """
@@ -112,8 +111,8 @@ struct CronModelsTests {
} }
} }
@Test func displayNameTrimsWhitespaceAndFallsBack() { @Test func `display name trims whitespace and falls back`() {
let base = makeCronJob(name: " hello ", payloadText: "hi") let base = self.makeCronJob(name: " hello ", payloadText: "hi")
#expect(base.displayName == "hello") #expect(base.displayName == "hello")
var unnamed = base var unnamed = base
@@ -121,8 +120,8 @@ struct CronModelsTests {
#expect(unnamed.displayName == "Untitled job") #expect(unnamed.displayName == "Untitled job")
} }
@Test func nextRunDateAndLastRunDateDeriveFromState() { @Test func `next run date and last run date derive from state`() {
let job = makeCronJob( let job = self.makeCronJob(
name: "t", name: "t",
payloadText: "hi", payloadText: "hi",
state: CronJobState( state: CronJobState(
@@ -136,7 +135,7 @@ struct CronModelsTests {
#expect(job.lastRunDate == Date(timeIntervalSince1970: 1_700_000_050)) #expect(job.lastRunDate == Date(timeIntervalSince1970: 1_700_000_050))
} }
@Test func decodeCronListResponseSkipsMalformedJobs() throws { @Test func `decode cron list response skips malformed jobs`() throws {
let json = """ let json = """
{ {
"jobs": [ "jobs": [
@@ -177,7 +176,7 @@ struct CronModelsTests {
#expect(jobs.first?.id == "good") #expect(jobs.first?.id == "good")
} }
@Test func decodeCronRunsResponseSkipsMalformedEntries() throws { @Test func `decode cron runs response skips malformed entries`() throws {
let json = """ let json = """
{ {
"entries": [ "entries": [

View File

@@ -2,8 +2,8 @@ import OpenClawKit
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct DeepLinkAgentPolicyTests { struct DeepLinkAgentPolicyTests {
@Test func validateMessageForHandleRejectsTooLongWhenUnkeyed() { @Test func `validate message for handle rejects too long when unkeyed`() {
let msg = String(repeating: "a", count: DeepLinkAgentPolicy.maxUnkeyedConfirmChars + 1) let msg = String(repeating: "a", count: DeepLinkAgentPolicy.maxUnkeyedConfirmChars + 1)
let res = DeepLinkAgentPolicy.validateMessageForHandle(message: msg, allowUnattended: false) let res = DeepLinkAgentPolicy.validateMessageForHandle(message: msg, allowUnattended: false)
switch res { switch res {
@@ -17,7 +17,7 @@ import Testing
} }
} }
@Test func validateMessageForHandleAllowsTooLongWhenKeyed() { @Test func `validate message for handle allows too long when keyed`() {
let msg = String(repeating: "a", count: DeepLinkAgentPolicy.maxUnkeyedConfirmChars + 1) let msg = String(repeating: "a", count: DeepLinkAgentPolicy.maxUnkeyedConfirmChars + 1)
let res = DeepLinkAgentPolicy.validateMessageForHandle(message: msg, allowUnattended: true) let res = DeepLinkAgentPolicy.validateMessageForHandle(message: msg, allowUnattended: true)
switch res { switch res {
@@ -28,7 +28,7 @@ import Testing
} }
} }
@Test func effectiveDeliveryIgnoresDeliveryFieldsWhenUnkeyed() { @Test func `effective delivery ignores delivery fields when unkeyed`() {
let link = AgentDeepLink( let link = AgentDeepLink(
message: "Hello", message: "Hello",
sessionKey: "s", sessionKey: "s",
@@ -44,7 +44,7 @@ import Testing
#expect(res.channel == .last) #expect(res.channel == .last)
} }
@Test func effectiveDeliveryHonorsDeliverForDeliverableChannelsWhenKeyed() { @Test func `effective delivery honors deliver for deliverable channels when keyed`() {
let link = AgentDeepLink( let link = AgentDeepLink(
message: "Hello", message: "Hello",
sessionKey: "s", sessionKey: "s",
@@ -60,7 +60,7 @@ import Testing
#expect(res.channel == .whatsapp) #expect(res.channel == .whatsapp)
} }
@Test func effectiveDeliveryStillBlocksWebChatDeliveryWhenKeyed() { @Test func `effective delivery still blocks web chat delivery when keyed`() {
let link = AgentDeepLink( let link = AgentDeepLink(
message: "Hello", message: "Hello",
sessionKey: "s", sessionKey: "s",

View File

@@ -1,10 +1,9 @@
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite
struct DeviceModelCatalogTests { struct DeviceModelCatalogTests {
@Test @Test
func symbolPrefersModelIdentifierPrefixes() { func `symbol prefers model identifier prefixes`() {
#expect(DeviceModelCatalog #expect(DeviceModelCatalog
.symbol(deviceFamily: "iPad", modelIdentifier: "iPad16,6", friendlyName: nil) == "ipad") .symbol(deviceFamily: "iPad", modelIdentifier: "iPad16,6", friendlyName: nil) == "ipad")
#expect(DeviceModelCatalog #expect(DeviceModelCatalog
@@ -12,7 +11,7 @@ struct DeviceModelCatalogTests {
} }
@Test @Test
func symbolUsesFriendlyNameForMacVariants() { func `symbol uses friendly name for mac variants`() {
#expect(DeviceModelCatalog.symbol( #expect(DeviceModelCatalog.symbol(
deviceFamily: "Mac", deviceFamily: "Mac",
modelIdentifier: "Mac99,1", modelIdentifier: "Mac99,1",
@@ -28,13 +27,13 @@ struct DeviceModelCatalogTests {
} }
@Test @Test
func symbolFallsBackToDeviceFamily() { func `symbol falls back to device family`() {
#expect(DeviceModelCatalog.symbol(deviceFamily: "Android", modelIdentifier: "", friendlyName: nil) == "android") #expect(DeviceModelCatalog.symbol(deviceFamily: "Android", modelIdentifier: "", friendlyName: nil) == "android")
#expect(DeviceModelCatalog.symbol(deviceFamily: "Linux", modelIdentifier: "", friendlyName: nil) == "cpu") #expect(DeviceModelCatalog.symbol(deviceFamily: "Linux", modelIdentifier: "", friendlyName: nil) == "cpu")
} }
@Test @Test
func presentationUsesBundledModelMappings() { func `presentation uses bundled model mappings`() {
let presentation = DeviceModelCatalog.presentation(deviceFamily: "iPhone", modelIdentifier: "iPhone1,1") let presentation = DeviceModelCatalog.presentation(deviceFamily: "iPhone", modelIdentifier: "iPhone1,1")
#expect(presentation?.title == "iPhone") #expect(presentation?.title == "iPhone")
} }

View File

@@ -59,21 +59,21 @@ struct ExecAllowlistTests {
cwd: nil) cwd: nil)
} }
@Test func matchUsesResolvedPath() { @Test func `match uses resolved path`() {
let entry = ExecAllowlistEntry(pattern: "/opt/homebrew/bin/rg") let entry = ExecAllowlistEntry(pattern: "/opt/homebrew/bin/rg")
let resolution = Self.homebrewRGResolution() let resolution = Self.homebrewRGResolution()
let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution) let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution)
#expect(match?.pattern == entry.pattern) #expect(match?.pattern == entry.pattern)
} }
@Test func matchIgnoresBasenamePattern() { @Test func `match ignores basename pattern`() {
let entry = ExecAllowlistEntry(pattern: "rg") let entry = ExecAllowlistEntry(pattern: "rg")
let resolution = Self.homebrewRGResolution() let resolution = Self.homebrewRGResolution()
let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution) let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution)
#expect(match == nil) #expect(match == nil)
} }
@Test func matchIgnoresBasenameForRelativeExecutable() { @Test func `match ignores basename for relative executable`() {
let entry = ExecAllowlistEntry(pattern: "echo") let entry = ExecAllowlistEntry(pattern: "echo")
let resolution = ExecCommandResolution( let resolution = ExecCommandResolution(
rawExecutable: "./echo", rawExecutable: "./echo",
@@ -84,21 +84,21 @@ struct ExecAllowlistTests {
#expect(match == nil) #expect(match == nil)
} }
@Test func matchIsCaseInsensitive() { @Test func `match is case insensitive`() {
let entry = ExecAllowlistEntry(pattern: "/OPT/HOMEBREW/BIN/RG") let entry = ExecAllowlistEntry(pattern: "/OPT/HOMEBREW/BIN/RG")
let resolution = Self.homebrewRGResolution() let resolution = Self.homebrewRGResolution()
let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution) let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution)
#expect(match?.pattern == entry.pattern) #expect(match?.pattern == entry.pattern)
} }
@Test func matchSupportsGlobStar() { @Test func `match supports glob star`() {
let entry = ExecAllowlistEntry(pattern: "/opt/**/rg") let entry = ExecAllowlistEntry(pattern: "/opt/**/rg")
let resolution = Self.homebrewRGResolution() let resolution = Self.homebrewRGResolution()
let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution) let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution)
#expect(match?.pattern == entry.pattern) #expect(match?.pattern == entry.pattern)
} }
@Test func resolveForAllowlistSplitsShellChains() { @Test func `resolve for allowlist splits shell chains`() {
let command = ["/bin/sh", "-lc", "echo allowlisted && /usr/bin/touch /tmp/openclaw-allowlist-test"] let command = ["/bin/sh", "-lc", "echo allowlisted && /usr/bin/touch /tmp/openclaw-allowlist-test"]
let resolutions = ExecCommandResolution.resolveForAllowlist( let resolutions = ExecCommandResolution.resolveForAllowlist(
command: command, command: command,
@@ -110,7 +110,7 @@ struct ExecAllowlistTests {
#expect(resolutions[1].executableName == "touch") #expect(resolutions[1].executableName == "touch")
} }
@Test func resolveForAllowlistKeepsQuotedOperatorsInSingleSegment() { @Test func `resolve for allowlist keeps quoted operators in single segment`() {
let command = ["/bin/sh", "-lc", "echo \"a && b\""] let command = ["/bin/sh", "-lc", "echo \"a && b\""]
let resolutions = ExecCommandResolution.resolveForAllowlist( let resolutions = ExecCommandResolution.resolveForAllowlist(
command: command, command: command,
@@ -121,7 +121,7 @@ struct ExecAllowlistTests {
#expect(resolutions[0].executableName == "echo") #expect(resolutions[0].executableName == "echo")
} }
@Test func resolveForAllowlistFailsClosedOnCommandSubstitution() { @Test func `resolve for allowlist fails closed on command substitution`() {
let command = ["/bin/sh", "-lc", "echo $(/usr/bin/touch /tmp/openclaw-allowlist-test-subst)"] let command = ["/bin/sh", "-lc", "echo $(/usr/bin/touch /tmp/openclaw-allowlist-test-subst)"]
let resolutions = ExecCommandResolution.resolveForAllowlist( let resolutions = ExecCommandResolution.resolveForAllowlist(
command: command, command: command,
@@ -131,7 +131,7 @@ struct ExecAllowlistTests {
#expect(resolutions.isEmpty) #expect(resolutions.isEmpty)
} }
@Test func resolveForAllowlistFailsClosedOnQuotedCommandSubstitution() { @Test func `resolve for allowlist fails closed on quoted command substitution`() {
let command = ["/bin/sh", "-lc", "echo \"ok $(/usr/bin/touch /tmp/openclaw-allowlist-test-quoted-subst)\""] let command = ["/bin/sh", "-lc", "echo \"ok $(/usr/bin/touch /tmp/openclaw-allowlist-test-quoted-subst)\""]
let resolutions = ExecCommandResolution.resolveForAllowlist( let resolutions = ExecCommandResolution.resolveForAllowlist(
command: command, command: command,
@@ -141,7 +141,7 @@ struct ExecAllowlistTests {
#expect(resolutions.isEmpty) #expect(resolutions.isEmpty)
} }
@Test func resolveForAllowlistFailsClosedOnQuotedBackticks() { @Test func `resolve for allowlist fails closed on quoted backticks`() {
let command = ["/bin/sh", "-lc", "echo \"ok `/usr/bin/id`\""] let command = ["/bin/sh", "-lc", "echo \"ok `/usr/bin/id`\""]
let resolutions = ExecCommandResolution.resolveForAllowlist( let resolutions = ExecCommandResolution.resolveForAllowlist(
command: command, command: command,
@@ -151,7 +151,7 @@ struct ExecAllowlistTests {
#expect(resolutions.isEmpty) #expect(resolutions.isEmpty)
} }
@Test func resolveForAllowlistMatchesSharedShellParserFixture() throws { @Test func `resolve for allowlist matches shared shell parser fixture`() throws {
let fixtures = try Self.loadShellParserParityCases() let fixtures = try Self.loadShellParserParityCases()
for fixture in fixtures { for fixture in fixtures {
let resolutions = ExecCommandResolution.resolveForAllowlist( let resolutions = ExecCommandResolution.resolveForAllowlist(
@@ -169,7 +169,7 @@ struct ExecAllowlistTests {
} }
} }
@Test func resolveMatchesSharedWrapperResolutionFixture() throws { @Test func `resolve matches shared wrapper resolution fixture`() throws {
let fixtures = try Self.loadWrapperResolutionParityCases() let fixtures = try Self.loadWrapperResolutionParityCases()
for fixture in fixtures { for fixture in fixtures {
let resolution = ExecCommandResolution.resolve( let resolution = ExecCommandResolution.resolve(
@@ -180,7 +180,7 @@ struct ExecAllowlistTests {
} }
} }
@Test func resolveForAllowlistTreatsPlainShInvocationAsDirectExec() { @Test func `resolve for allowlist treats plain sh invocation as direct exec`() {
let command = ["/bin/sh", "./script.sh"] let command = ["/bin/sh", "./script.sh"]
let resolutions = ExecCommandResolution.resolveForAllowlist( let resolutions = ExecCommandResolution.resolveForAllowlist(
command: command, command: command,
@@ -191,7 +191,7 @@ struct ExecAllowlistTests {
#expect(resolutions[0].executableName == "sh") #expect(resolutions[0].executableName == "sh")
} }
@Test func resolveForAllowlistUnwrapsEnvShellWrapperChains() { @Test func `resolve for allowlist unwraps env shell wrapper chains`() {
let command = [ let command = [
"/usr/bin/env", "/usr/bin/env",
"/bin/sh", "/bin/sh",
@@ -208,7 +208,7 @@ struct ExecAllowlistTests {
#expect(resolutions[1].executableName == "touch") #expect(resolutions[1].executableName == "touch")
} }
@Test func resolveForAllowlistUnwrapsEnvToEffectiveDirectExecutable() { @Test func `resolve for allowlist unwraps env to effective direct executable`() {
let command = ["/usr/bin/env", "FOO=bar", "/usr/bin/printf", "ok"] let command = ["/usr/bin/env", "FOO=bar", "/usr/bin/printf", "ok"]
let resolutions = ExecCommandResolution.resolveForAllowlist( let resolutions = ExecCommandResolution.resolveForAllowlist(
command: command, command: command,
@@ -220,7 +220,7 @@ struct ExecAllowlistTests {
#expect(resolutions[0].executableName == "printf") #expect(resolutions[0].executableName == "printf")
} }
@Test func matchAllRequiresEverySegmentToMatch() { @Test func `match all requires every segment to match`() {
let first = ExecCommandResolution( let first = ExecCommandResolution(
rawExecutable: "echo", rawExecutable: "echo",
resolvedPath: "/usr/bin/echo", resolvedPath: "/usr/bin/echo",

View File

@@ -2,8 +2,8 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct ExecApprovalHelpersTests { struct ExecApprovalHelpersTests {
@Test func parseDecisionTrimsAndRejectsInvalid() { @Test func `parse decision trims and rejects invalid`() {
#expect(ExecApprovalHelpers.parseDecision("allow-once") == .allowOnce) #expect(ExecApprovalHelpers.parseDecision("allow-once") == .allowOnce)
#expect(ExecApprovalHelpers.parseDecision(" allow-always ") == .allowAlways) #expect(ExecApprovalHelpers.parseDecision(" allow-always ") == .allowAlways)
#expect(ExecApprovalHelpers.parseDecision("deny") == .deny) #expect(ExecApprovalHelpers.parseDecision("deny") == .deny)
@@ -11,7 +11,7 @@ import Testing
#expect(ExecApprovalHelpers.parseDecision("nope") == nil) #expect(ExecApprovalHelpers.parseDecision("nope") == nil)
} }
@Test func allowlistPatternPrefersResolution() { @Test func `allowlist pattern prefers resolution`() {
let resolved = ExecCommandResolution( let resolved = ExecCommandResolution(
rawExecutable: "rg", rawExecutable: "rg",
resolvedPath: "/opt/homebrew/bin/rg", resolvedPath: "/opt/homebrew/bin/rg",
@@ -29,7 +29,7 @@ import Testing
#expect(ExecApprovalHelpers.allowlistPattern(command: [], resolution: nil) == nil) #expect(ExecApprovalHelpers.allowlistPattern(command: [], resolution: nil) == nil)
} }
@Test func validateAllowlistPatternReturnsReasons() { @Test func `validate allowlist pattern returns reasons`() {
#expect(ExecApprovalHelpers.isPathPattern("/usr/bin/rg")) #expect(ExecApprovalHelpers.isPathPattern("/usr/bin/rg"))
#expect(ExecApprovalHelpers.isPathPattern(" ~/bin/rg ")) #expect(ExecApprovalHelpers.isPathPattern(" ~/bin/rg "))
#expect(!ExecApprovalHelpers.isPathPattern("rg")) #expect(!ExecApprovalHelpers.isPathPattern("rg"))
@@ -47,7 +47,7 @@ import Testing
} }
} }
@Test func requiresAskMatchesPolicy() { @Test func `requires ask matches policy`() {
let entry = ExecAllowlistEntry(pattern: "/bin/ls", lastUsedAt: nil, lastUsedCommand: nil, lastResolvedPath: nil) let entry = ExecAllowlistEntry(pattern: "/bin/ls", lastUsedAt: nil, lastUsedCommand: nil, lastResolvedPath: nil)
#expect(ExecApprovalHelpers.requiresAsk( #expect(ExecApprovalHelpers.requiresAsk(
ask: .always, ask: .always,

View File

@@ -1,10 +1,9 @@
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite
@MainActor @MainActor
struct ExecApprovalsGatewayPrompterTests { struct ExecApprovalsGatewayPrompterTests {
@Test func sessionMatchPrefersActiveSession() { @Test func `session match prefers active session`() {
let matches = ExecApprovalsGatewayPrompter._testShouldPresent( let matches = ExecApprovalsGatewayPrompter._testShouldPresent(
mode: .remote, mode: .remote,
activeSession: " main ", activeSession: " main ",
@@ -20,7 +19,7 @@ struct ExecApprovalsGatewayPrompterTests {
#expect(!mismatched) #expect(!mismatched)
} }
@Test func sessionFallbackUsesRecentActivity() { @Test func `session fallback uses recent activity`() {
let recent = ExecApprovalsGatewayPrompter._testShouldPresent( let recent = ExecApprovalsGatewayPrompter._testShouldPresent(
mode: .remote, mode: .remote,
activeSession: nil, activeSession: nil,
@@ -38,7 +37,7 @@ struct ExecApprovalsGatewayPrompterTests {
#expect(!stale) #expect(!stale)
} }
@Test func defaultBehaviorMatchesMode() { @Test func `default behavior matches mode`() {
let local = ExecApprovalsGatewayPrompter._testShouldPresent( let local = ExecApprovalsGatewayPrompter._testShouldPresent(
mode: .local, mode: .local,
activeSession: nil, activeSession: nil,

View File

@@ -5,7 +5,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
struct ExecApprovalsSocketPathGuardTests { struct ExecApprovalsSocketPathGuardTests {
@Test @Test
func hardenParentDirectoryCreatesDirectoryWith0700Permissions() throws { func `harden parent directory creates directory with0700 permissions`() throws {
let root = FileManager().temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: root) } defer { try? FileManager().removeItem(at: root) }
@@ -24,7 +24,7 @@ struct ExecApprovalsSocketPathGuardTests {
} }
@Test @Test
func removeExistingSocketRejectsSymlinkPath() throws { func `remove existing socket rejects symlink path`() throws {
let root = FileManager().temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: root) } defer { try? FileManager().removeItem(at: root) }
@@ -50,7 +50,7 @@ struct ExecApprovalsSocketPathGuardTests {
} }
@Test @Test
func removeExistingSocketRejectsRegularFilePath() throws { func `remove existing socket rejects regular file path`() throws {
let root = FileManager().temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: root) } defer { try? FileManager().removeItem(at: root) }

View File

@@ -17,8 +17,8 @@ struct ExecApprovalsStoreRefactorTests {
} }
@Test @Test
func ensureFileSkipsRewriteWhenUnchanged() async throws { func `ensure file skips rewrite when unchanged`() async throws {
try await self.withTempStateDir { stateDir in try await self.withTempStateDir { _ in
_ = ExecApprovalsStore.ensureFile() _ = ExecApprovalsStore.ensureFile()
let url = ExecApprovalsStore.fileURL() let url = ExecApprovalsStore.fileURL()
let firstWriteDate = try Self.modificationDate(at: url) let firstWriteDate = try Self.modificationDate(at: url)
@@ -32,7 +32,7 @@ struct ExecApprovalsStoreRefactorTests {
} }
@Test @Test
func updateAllowlistReportsRejectedBasenamePattern() async throws { func `update allowlist reports rejected basename pattern`() async throws {
try await self.withTempStateDir { _ in try await self.withTempStateDir { _ in
let rejected = ExecApprovalsStore.updateAllowlist( let rejected = ExecApprovalsStore.updateAllowlist(
agentId: "main", agentId: "main",
@@ -50,7 +50,7 @@ struct ExecApprovalsStoreRefactorTests {
} }
@Test @Test
func updateAllowlistMigratesLegacyPatternFromResolvedPath() async throws { func `update allowlist migrates legacy pattern from resolved path`() async throws {
try await self.withTempStateDir { _ in try await self.withTempStateDir { _ in
let rejected = ExecApprovalsStore.updateAllowlist( let rejected = ExecApprovalsStore.updateAllowlist(
agentId: "main", agentId: "main",
@@ -69,7 +69,7 @@ struct ExecApprovalsStoreRefactorTests {
} }
@Test @Test
func ensureFileHardensStateDirectoryPermissions() async throws { func `ensure file hardens state directory permissions`() async throws {
try await self.withTempStateDir { stateDir in try await self.withTempStateDir { stateDir in
try FileManager().createDirectory(at: stateDir, withIntermediateDirectories: true) try FileManager().createDirectory(at: stateDir, withIntermediateDirectories: true)
try FileManager().setAttributes([.posixPermissions: 0o755], ofItemAtPath: stateDir.path) try FileManager().setAttributes([.posixPermissions: 0o755], ofItemAtPath: stateDir.path)

View File

@@ -3,7 +3,7 @@ import Testing
@testable import OpenClaw @testable import OpenClaw
struct ExecHostRequestEvaluatorTests { struct ExecHostRequestEvaluatorTests {
@Test func validateRequestRejectsEmptyCommand() { @Test func `validate request rejects empty command`() {
let request = ExecHostRequest( let request = ExecHostRequest(
command: [], command: [],
rawCommand: nil, rawCommand: nil,
@@ -23,7 +23,7 @@ struct ExecHostRequestEvaluatorTests {
} }
} }
@Test func evaluateRequiresPromptOnAllowlistMissWithoutDecision() { @Test func `evaluate requires prompt on allowlist miss without decision`() {
let context = Self.makeContext(security: .allowlist, ask: .onMiss, allowlistSatisfied: false, skillAllow: false) let context = Self.makeContext(security: .allowlist, ask: .onMiss, allowlistSatisfied: false, skillAllow: false)
let decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: nil) let decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: nil)
switch decision { switch decision {
@@ -36,7 +36,7 @@ struct ExecHostRequestEvaluatorTests {
} }
} }
@Test func evaluateAllowsAllowOnceDecisionOnAllowlistMiss() { @Test func `evaluate allows allow once decision on allowlist miss`() {
let context = Self.makeContext(security: .allowlist, ask: .onMiss, allowlistSatisfied: false, skillAllow: false) let context = Self.makeContext(security: .allowlist, ask: .onMiss, allowlistSatisfied: false, skillAllow: false)
let decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: .allowOnce) let decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: .allowOnce)
switch decision { switch decision {
@@ -49,7 +49,7 @@ struct ExecHostRequestEvaluatorTests {
} }
} }
@Test func evaluateDeniesOnExplicitDenyDecision() { @Test func `evaluate denies on explicit deny decision`() {
let context = Self.makeContext(security: .full, ask: .off, allowlistSatisfied: true, skillAllow: false) let context = Self.makeContext(security: .full, ask: .off, allowlistSatisfied: true, skillAllow: false)
let decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: .deny) let decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: .deny)
switch decision { switch decision {

View File

@@ -20,7 +20,7 @@ private struct SystemRunCommandContractExpected: Decodable {
} }
struct ExecSystemRunCommandValidatorTests { struct ExecSystemRunCommandValidatorTests {
@Test func matchesSharedSystemRunCommandContractFixture() throws { @Test func `matches shared system run command contract fixture`() throws {
for entry in try Self.loadContractCases() { for entry in try Self.loadContractCases() {
let result = ExecSystemRunCommandValidator.resolve(command: entry.command, rawCommand: entry.rawCommand) let result = ExecSystemRunCommandValidator.resolve(command: entry.command, rawCommand: entry.rawCommand)

View File

@@ -1,8 +1,8 @@
import Foundation import Foundation
import Testing import Testing
@Suite struct FileHandleLegacyAPIGuardTests { struct FileHandleLegacyAPIGuardTests {
@Test func sourcesAvoidLegacyNonThrowingFileHandleReadAPIs() throws { @Test func `sources avoid legacy non throwing file handle read AP is`() throws {
let testFile = URL(fileURLWithPath: #filePath) let testFile = URL(fileURLWithPath: #filePath)
let packageRoot = testFile let packageRoot = testFile
.deletingLastPathComponent() // OpenClawIPCTests .deletingLastPathComponent() // OpenClawIPCTests

View File

@@ -2,8 +2,8 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct FileHandleSafeReadTests { struct FileHandleSafeReadTests {
@Test func readToEndSafelyReturnsEmptyForClosedHandle() { @Test func `read to end safely returns empty for closed handle`() {
let pipe = Pipe() let pipe = Pipe()
let handle = pipe.fileHandleForReading let handle = pipe.fileHandleForReading
try? handle.close() try? handle.close()
@@ -12,7 +12,7 @@ import Testing
#expect(data.isEmpty) #expect(data.isEmpty)
} }
@Test func readSafelyUpToCountReturnsEmptyForClosedHandle() { @Test func `read safely up to count returns empty for closed handle`() {
let pipe = Pipe() let pipe = Pipe()
let handle = pipe.fileHandleForReading let handle = pipe.fileHandleForReading
try? handle.close() try? handle.close()
@@ -21,7 +21,7 @@ import Testing
#expect(data.isEmpty) #expect(data.isEmpty)
} }
@Test func readToEndSafelyReadsPipeContents() { @Test func `read to end safely reads pipe contents`() {
let pipe = Pipe() let pipe = Pipe()
let writeHandle = pipe.fileHandleForWriting let writeHandle = pipe.fileHandleForWriting
writeHandle.write(Data("hello".utf8)) writeHandle.write(Data("hello".utf8))
@@ -31,7 +31,7 @@ import Testing
#expect(String(data: data, encoding: .utf8) == "hello") #expect(String(data: data, encoding: .utf8) == "hello")
} }
@Test func readSafelyUpToCountReadsIncrementally() { @Test func `read safely up to count reads incrementally`() {
let pipe = Pipe() let pipe = Pipe()
let writeHandle = pipe.fileHandleForWriting let writeHandle = pipe.fileHandleForWriting
writeHandle.write(Data("hello world".utf8)) writeHandle.write(Data("hello world".utf8))

View File

@@ -1,13 +1,13 @@
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct GatewayAgentChannelTests { struct GatewayAgentChannelTests {
@Test func shouldDeliverBlocksWebChat() { @Test func `should deliver blocks web chat`() {
#expect(GatewayAgentChannel.webchat.shouldDeliver(true) == false) #expect(GatewayAgentChannel.webchat.shouldDeliver(true) == false)
#expect(GatewayAgentChannel.webchat.shouldDeliver(false) == false) #expect(GatewayAgentChannel.webchat.shouldDeliver(false) == false)
} }
@Test func shouldDeliverAllowsLastAndProviderChannels() { @Test func `should deliver allows last and provider channels`() {
#expect(GatewayAgentChannel.last.shouldDeliver(true) == true) #expect(GatewayAgentChannel.last.shouldDeliver(true) == true)
#expect(GatewayAgentChannel.whatsapp.shouldDeliver(true) == true) #expect(GatewayAgentChannel.whatsapp.shouldDeliver(true) == true)
#expect(GatewayAgentChannel.telegram.shouldDeliver(true) == true) #expect(GatewayAgentChannel.telegram.shouldDeliver(true) == true)
@@ -16,7 +16,7 @@ import Testing
#expect(GatewayAgentChannel.last.shouldDeliver(false) == false) #expect(GatewayAgentChannel.last.shouldDeliver(false) == false)
} }
@Test func initRawNormalizesAndFallsBackToLast() { @Test func `init raw normalizes and falls back to last`() {
#expect(GatewayAgentChannel(raw: nil) == .last) #expect(GatewayAgentChannel(raw: nil) == .last)
#expect(GatewayAgentChannel(raw: " ") == .last) #expect(GatewayAgentChannel(raw: " ") == .last)
#expect(GatewayAgentChannel(raw: "WEBCHAT") == .webchat) #expect(GatewayAgentChannel(raw: "WEBCHAT") == .webchat)

View File

@@ -3,14 +3,14 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
struct GatewayAutostartPolicyTests { struct GatewayAutostartPolicyTests {
@Test func startsGatewayOnlyWhenLocalAndNotPaused() { @Test func `starts gateway only when local and not paused`() {
#expect(GatewayAutostartPolicy.shouldStartGateway(mode: .local, paused: false)) #expect(GatewayAutostartPolicy.shouldStartGateway(mode: .local, paused: false))
#expect(!GatewayAutostartPolicy.shouldStartGateway(mode: .local, paused: true)) #expect(!GatewayAutostartPolicy.shouldStartGateway(mode: .local, paused: true))
#expect(!GatewayAutostartPolicy.shouldStartGateway(mode: .remote, paused: false)) #expect(!GatewayAutostartPolicy.shouldStartGateway(mode: .remote, paused: false))
#expect(!GatewayAutostartPolicy.shouldStartGateway(mode: .unconfigured, paused: false)) #expect(!GatewayAutostartPolicy.shouldStartGateway(mode: .unconfigured, paused: false))
} }
@Test func ensuresLaunchAgentWhenLocalAndNotAttachOnly() { @Test func `ensures launch agent when local and not attach only`() {
#expect(GatewayAutostartPolicy.shouldEnsureLaunchAgent( #expect(GatewayAutostartPolicy.shouldEnsureLaunchAgent(
mode: .local, mode: .local,
paused: false)) paused: false))

View File

@@ -4,7 +4,7 @@ import os
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct GatewayConnectionTests { struct GatewayConnectionTests {
private func makeConnection( private func makeConnection(
session: GatewayTestWebSocketSession, session: GatewayTestWebSocketSession,
token: String? = nil) throws -> (GatewayConnection, ConfigSource) token: String? = nil) throws -> (GatewayConnection, ConfigSource)
@@ -56,7 +56,7 @@ import Testing
} }
} }
@Test func requestReusesSingleWebSocketForSameConfig() async throws { @Test func `request reuses single web socket for same config`() async throws {
let session = self.makeSession() let session = self.makeSession()
let (conn, _) = try self.makeConnection(session: session) let (conn, _) = try self.makeConnection(session: session)
@@ -68,7 +68,7 @@ import Testing
#expect(session.snapshotCancelCount() == 0) #expect(session.snapshotCancelCount() == 0)
} }
@Test func requestReconfiguresAndCancelsOnTokenChange() async throws { @Test func `request reconfigures and cancels on token change`() async throws {
let session = self.makeSession() let session = self.makeSession()
let (conn, cfg) = try self.makeConnection(session: session, token: "a") let (conn, cfg) = try self.makeConnection(session: session, token: "a")
@@ -81,7 +81,7 @@ import Testing
#expect(session.snapshotCancelCount() == 1) #expect(session.snapshotCancelCount() == 1)
} }
@Test func concurrentRequestsStillUseSingleWebSocket() async throws { @Test func `concurrent requests still use single web socket`() async throws {
let session = self.makeSession(helloDelayMs: 150) let session = self.makeSession(helloDelayMs: 150)
let (conn, _) = try self.makeConnection(session: session) let (conn, _) = try self.makeConnection(session: session)
@@ -92,7 +92,7 @@ import Testing
#expect(session.snapshotMakeCount() == 1) #expect(session.snapshotMakeCount() == 1)
} }
@Test func subscribeReplaysLatestSnapshot() async throws { @Test func `subscribe replays latest snapshot`() async throws {
let session = self.makeSession() let session = self.makeSession()
let (conn, _) = try self.makeConnection(session: session) let (conn, _) = try self.makeConnection(session: session)
@@ -109,7 +109,7 @@ import Testing
#expect(snap.type == "hello-ok") #expect(snap.type == "hello-ok")
} }
@Test func subscribeEmitsSeqGapBeforeEvent() async throws { @Test func `subscribe emits seq gap before event`() async throws {
let session = self.makeSession() let session = self.makeSession()
let (conn, _) = try self.makeConnection(session: session) let (conn, _) = try self.makeConnection(session: session)

View File

@@ -3,7 +3,7 @@ import OpenClawKit
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct GatewayChannelConnectTests { struct GatewayChannelConnectTests {
private enum FakeResponse { private enum FakeResponse {
case helloOk(delayMs: Int) case helloOk(delayMs: Int)
case invalid(delayMs: Int) case invalid(delayMs: Int)
@@ -34,7 +34,7 @@ import Testing
}) })
} }
@Test func concurrentConnectIsSingleFlightOnSuccess() async throws { @Test func `concurrent connect is single flight on success`() async throws {
let session = self.makeSession(response: .helloOk(delayMs: 200)) let session = self.makeSession(response: .helloOk(delayMs: 200))
let channel = try GatewayChannelActor( let channel = try GatewayChannelActor(
url: #require(URL(string: "ws://example.invalid")), url: #require(URL(string: "ws://example.invalid")),
@@ -50,7 +50,7 @@ import Testing
#expect(session.snapshotMakeCount() == 1) #expect(session.snapshotMakeCount() == 1)
} }
@Test func concurrentConnectSharesFailure() async throws { @Test func `concurrent connect shares failure`() async throws {
let session = self.makeSession(response: .invalid(delayMs: 200)) let session = self.makeSession(response: .invalid(delayMs: 200))
let channel = try GatewayChannelActor( let channel = try GatewayChannelActor(
url: #require(URL(string: "ws://example.invalid")), url: #require(URL(string: "ws://example.invalid")),

View File

@@ -3,7 +3,7 @@ import OpenClawKit
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct GatewayChannelRequestTests { struct GatewayChannelRequestTests {
private func makeSession(requestSendDelayMs: Int) -> GatewayTestWebSocketSession { private func makeSession(requestSendDelayMs: Int) -> GatewayTestWebSocketSession {
GatewayTestWebSocketSession( GatewayTestWebSocketSession(
taskFactory: { taskFactory: {
@@ -16,7 +16,7 @@ import Testing
}) })
} }
@Test func requestTimeoutThenSendFailureDoesNotDoubleResume() async throws { @Test func `request timeout then send failure does not double resume`() async throws {
let session = self.makeSession(requestSendDelayMs: 100) let session = self.makeSession(requestSendDelayMs: 100)
let channel = try GatewayChannelActor( let channel = try GatewayChannelActor(
url: #require(URL(string: "ws://example.invalid")), url: #require(URL(string: "ws://example.invalid")),

View File

@@ -3,8 +3,8 @@ import OpenClawKit
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct GatewayChannelShutdownTests { struct GatewayChannelShutdownTests {
@Test func shutdownPreventsReconnectLoopFromReceiveFailure() async throws { @Test func `shutdown prevents reconnect loop from receive failure`() async throws {
let session = GatewayTestWebSocketSession() let session = GatewayTestWebSocketSession()
let channel = try GatewayChannelActor( let channel = try GatewayChannelActor(
url: #require(URL(string: "ws://example.invalid")), url: #require(URL(string: "ws://example.invalid")),

View File

@@ -39,14 +39,14 @@ private func makeTestGatewayConnection() -> GatewayConnection {
} }
@Suite(.serialized) struct GatewayConnectionControlTests { @Suite(.serialized) struct GatewayConnectionControlTests {
@Test func statusFailsWhenProcessMissing() async { @Test func `status fails when process missing`() async {
let connection = makeTestGatewayConnection() let connection = makeTestGatewayConnection()
let result = await connection.status() let result = await connection.status()
#expect(result.ok == false) #expect(result.ok == false)
#expect(result.error != nil) #expect(result.error != nil)
} }
@Test func rejectEmptyMessage() async { @Test func `reject empty message`() async {
let connection = makeTestGatewayConnection() let connection = makeTestGatewayConnection()
let result = await connection.sendAgent( let result = await connection.sendAgent(
message: "", message: "",

View File

@@ -3,7 +3,6 @@ import OpenClawDiscovery
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite
struct GatewayDiscoveryHelpersTests { struct GatewayDiscoveryHelpersTests {
private func makeGateway( private func makeGateway(
serviceHost: String?, serviceHost: String?,
@@ -41,23 +40,23 @@ struct GatewayDiscoveryHelpersTests {
#expect(parsed?.port == port) #expect(parsed?.port == port)
} }
@Test func sshTargetUsesResolvedServiceHostOnly() { @Test func `ssh target uses resolved service host only`() {
let gateway = self.makeGateway( let gateway = self.makeGateway(
serviceHost: "resolved.example.ts.net", serviceHost: "resolved.example.ts.net",
servicePort: 18789, servicePort: 18789,
sshPort: 2201) sshPort: 2201)
assertSSHTarget(for: gateway, host: "resolved.example.ts.net", port: 2201) self.assertSSHTarget(for: gateway, host: "resolved.example.ts.net", port: 2201)
} }
@Test func sshTargetAllowsMissingResolvedServicePort() { @Test func `ssh target allows missing resolved service port`() {
let gateway = self.makeGateway( let gateway = self.makeGateway(
serviceHost: "resolved.example.ts.net", serviceHost: "resolved.example.ts.net",
servicePort: nil, servicePort: nil,
sshPort: 2201) sshPort: 2201)
assertSSHTarget(for: gateway, host: "resolved.example.ts.net", port: 2201) self.assertSSHTarget(for: gateway, host: "resolved.example.ts.net", port: 2201)
} }
@Test func sshTargetRejectsTxtOnlyGateways() { @Test func `ssh target rejects txt only gateways`() {
let gateway = self.makeGateway( let gateway = self.makeGateway(
serviceHost: nil, serviceHost: nil,
servicePort: nil, servicePort: nil,
@@ -68,7 +67,7 @@ struct GatewayDiscoveryHelpersTests {
#expect(GatewayDiscoveryHelpers.sshTarget(for: gateway) == nil) #expect(GatewayDiscoveryHelpers.sshTarget(for: gateway) == nil)
} }
@Test func directUrlUsesResolvedServiceEndpointOnly() { @Test func `direct url uses resolved service endpoint only`() {
let tlsGateway = self.makeGateway( let tlsGateway = self.makeGateway(
serviceHost: "resolved.example.ts.net", serviceHost: "resolved.example.ts.net",
servicePort: 443) servicePort: 443)
@@ -85,7 +84,7 @@ struct GatewayDiscoveryHelpersTests {
#expect(GatewayDiscoveryHelpers.directUrl(for: localGateway) == "ws://127.0.0.1:18789") #expect(GatewayDiscoveryHelpers.directUrl(for: localGateway) == "ws://127.0.0.1:18789")
} }
@Test func directUrlRejectsTxtOnlyFallback() { @Test func `direct url rejects txt only fallback`() {
let gateway = self.makeGateway( let gateway = self.makeGateway(
serviceHost: nil, serviceHost: nil,
servicePort: nil, servicePort: nil,

View File

@@ -1,10 +1,9 @@
@testable import OpenClawDiscovery
import Testing import Testing
@testable import OpenClawDiscovery
@Suite
@MainActor @MainActor
struct GatewayDiscoveryModelTests { struct GatewayDiscoveryModelTests {
@Test func localGatewayMatchesLanHost() { @Test func `local gateway matches lan host`() {
let local = GatewayDiscoveryModel.LocalIdentity( let local = GatewayDiscoveryModel.LocalIdentity(
hostTokens: ["studio"], hostTokens: ["studio"],
displayTokens: []) displayTokens: [])
@@ -16,7 +15,7 @@ struct GatewayDiscoveryModelTests {
local: local)) local: local))
} }
@Test func localGatewayMatchesTailnetDns() { @Test func `local gateway matches tailnet dns`() {
let local = GatewayDiscoveryModel.LocalIdentity( let local = GatewayDiscoveryModel.LocalIdentity(
hostTokens: ["studio"], hostTokens: ["studio"],
displayTokens: []) displayTokens: [])
@@ -28,7 +27,7 @@ struct GatewayDiscoveryModelTests {
local: local)) local: local))
} }
@Test func localGatewayMatchesDisplayName() { @Test func `local gateway matches display name`() {
let local = GatewayDiscoveryModel.LocalIdentity( let local = GatewayDiscoveryModel.LocalIdentity(
hostTokens: [], hostTokens: [],
displayTokens: ["peter's mac studio"]) displayTokens: ["peter's mac studio"])
@@ -40,7 +39,7 @@ struct GatewayDiscoveryModelTests {
local: local)) local: local))
} }
@Test func remoteGatewayDoesNotMatch() { @Test func `remote gateway does not match`() {
let local = GatewayDiscoveryModel.LocalIdentity( let local = GatewayDiscoveryModel.LocalIdentity(
hostTokens: ["studio"], hostTokens: ["studio"],
displayTokens: ["peter's mac studio"]) displayTokens: ["peter's mac studio"])
@@ -52,7 +51,7 @@ struct GatewayDiscoveryModelTests {
local: local)) local: local))
} }
@Test func localGatewayMatchesServiceName() { @Test func `local gateway matches service name`() {
let local = GatewayDiscoveryModel.LocalIdentity( let local = GatewayDiscoveryModel.LocalIdentity(
hostTokens: ["studio"], hostTokens: ["studio"],
displayTokens: []) displayTokens: [])
@@ -64,7 +63,7 @@ struct GatewayDiscoveryModelTests {
local: local)) local: local))
} }
@Test func serviceNameDoesNotFalsePositiveOnSubstringHostToken() { @Test func `service name does not false positive on substring host token`() {
let local = GatewayDiscoveryModel.LocalIdentity( let local = GatewayDiscoveryModel.LocalIdentity(
hostTokens: ["steipete"], hostTokens: ["steipete"],
displayTokens: []) displayTokens: [])
@@ -82,7 +81,7 @@ struct GatewayDiscoveryModelTests {
local: local)) local: local))
} }
@Test func parsesGatewayTXTFields() { @Test func `parses gateway TXT fields`() {
let parsed = GatewayDiscoveryModel.parseGatewayTXT([ let parsed = GatewayDiscoveryModel.parseGatewayTXT([
"lanHost": " studio.local ", "lanHost": " studio.local ",
"tailnetDns": " peters-mac-studio-1.ts.net ", "tailnetDns": " peters-mac-studio-1.ts.net ",
@@ -97,7 +96,7 @@ struct GatewayDiscoveryModelTests {
#expect(parsed.cliPath == "/opt/openclaw") #expect(parsed.cliPath == "/opt/openclaw")
} }
@Test func parsesGatewayTXTDefaults() { @Test func `parses gateway TXT defaults`() {
let parsed = GatewayDiscoveryModel.parseGatewayTXT([ let parsed = GatewayDiscoveryModel.parseGatewayTXT([
"lanHost": " ", "lanHost": " ",
"tailnetDns": "\n", "tailnetDns": "\n",
@@ -111,7 +110,7 @@ struct GatewayDiscoveryModelTests {
#expect(parsed.cliPath == nil) #expect(parsed.cliPath == nil)
} }
@Test func buildsSSHTarget() { @Test func `builds SSH target`() {
#expect(GatewayDiscoveryModel.buildSSHTarget( #expect(GatewayDiscoveryModel.buildSSHTarget(
user: "peter", user: "peter",
host: "studio.local", host: "studio.local",
@@ -122,7 +121,7 @@ struct GatewayDiscoveryModelTests {
port: 2201) == "peter@studio.local:2201") port: 2201) == "peter@studio.local:2201")
} }
@Test func dedupeKeyPrefersResolvedEndpointAcrossSources() { @Test func `dedupe key prefers resolved endpoint across sources`() {
let wideArea = GatewayDiscoveryModel.DiscoveredGateway( let wideArea = GatewayDiscoveryModel.DiscoveredGateway(
displayName: "Gateway", displayName: "Gateway",
serviceHost: "gateway-host.tailnet-example.ts.net", serviceHost: "gateway-host.tailnet-example.ts.net",
@@ -151,7 +150,7 @@ struct GatewayDiscoveryModelTests {
#expect(GatewayDiscoveryModel.dedupeKey(for: wideArea) == GatewayDiscoveryModel.dedupeKey(for: serve)) #expect(GatewayDiscoveryModel.dedupeKey(for: wideArea) == GatewayDiscoveryModel.dedupeKey(for: serve))
} }
@Test func dedupeKeyFallsBackToStableIDWithoutEndpoint() { @Test func `dedupe key falls back to stable ID without endpoint`() {
let unresolved = GatewayDiscoveryModel.DiscoveredGateway( let unresolved = GatewayDiscoveryModel.DiscoveredGateway(
displayName: "Gateway", displayName: "Gateway",
serviceHost: nil, serviceHost: nil,
@@ -165,6 +164,7 @@ struct GatewayDiscoveryModelTests {
debugID: "serve", debugID: "serve",
isLocal: false) isLocal: false)
#expect(GatewayDiscoveryModel.dedupeKey(for: unresolved) == "stable|tailscale-serve|gateway-host.tailnet-example.ts.net") #expect(GatewayDiscoveryModel
.dedupeKey(for: unresolved) == "stable|tailscale-serve|gateway-host.tailnet-example.ts.net")
} }
} }

View File

@@ -2,7 +2,7 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct GatewayEndpointStoreTests { struct GatewayEndpointStoreTests {
private func makeLaunchAgentSnapshot( private func makeLaunchAgentSnapshot(
env: [String: String], env: [String: String],
token: String?, token: String?,
@@ -26,7 +26,7 @@ import Testing
return defaults return defaults
} }
@Test func resolveGatewayTokenPrefersEnvAndFallsBackToLaunchd() { @Test func `resolve gateway token prefers env and falls back to launchd`() {
let snapshot = self.makeLaunchAgentSnapshot( let snapshot = self.makeLaunchAgentSnapshot(
env: ["OPENCLAW_GATEWAY_TOKEN": "launchd-token"], env: ["OPENCLAW_GATEWAY_TOKEN": "launchd-token"],
token: "launchd-token", token: "launchd-token",
@@ -47,7 +47,7 @@ import Testing
#expect(fallbackToken == "launchd-token") #expect(fallbackToken == "launchd-token")
} }
@Test func resolveGatewayTokenIgnoresLaunchdInRemoteMode() { @Test func `resolve gateway token ignores launchd in remote mode`() {
let snapshot = self.makeLaunchAgentSnapshot( let snapshot = self.makeLaunchAgentSnapshot(
env: ["OPENCLAW_GATEWAY_TOKEN": "launchd-token"], env: ["OPENCLAW_GATEWAY_TOKEN": "launchd-token"],
token: "launchd-token", token: "launchd-token",
@@ -61,7 +61,7 @@ import Testing
#expect(token == nil) #expect(token == nil)
} }
@Test func resolveGatewayPasswordFallsBackToLaunchd() { @Test func `resolve gateway password falls back to launchd`() {
let snapshot = self.makeLaunchAgentSnapshot( let snapshot = self.makeLaunchAgentSnapshot(
env: ["OPENCLAW_GATEWAY_PASSWORD": "launchd-pass"], env: ["OPENCLAW_GATEWAY_PASSWORD": "launchd-pass"],
token: nil, token: nil,
@@ -75,7 +75,7 @@ import Testing
#expect(password == "launchd-pass") #expect(password == "launchd-pass")
} }
@Test func connectionModeResolverPrefersConfigModeOverDefaults() { @Test func `connection mode resolver prefers config mode over defaults`() {
let defaults = self.makeDefaults() let defaults = self.makeDefaults()
defaults.set("remote", forKey: connectionModeKey) defaults.set("remote", forKey: connectionModeKey)
@@ -89,7 +89,7 @@ import Testing
#expect(resolved.mode == .local) #expect(resolved.mode == .local)
} }
@Test func connectionModeResolverTrimsConfigMode() { @Test func `connection mode resolver trims config mode`() {
let defaults = self.makeDefaults() let defaults = self.makeDefaults()
defaults.set("local", forKey: connectionModeKey) defaults.set("local", forKey: connectionModeKey)
@@ -103,7 +103,7 @@ import Testing
#expect(resolved.mode == .remote) #expect(resolved.mode == .remote)
} }
@Test func connectionModeResolverFallsBackToDefaultsWhenMissingConfig() { @Test func `connection mode resolver falls back to defaults when missing config`() {
let defaults = self.makeDefaults() let defaults = self.makeDefaults()
defaults.set("remote", forKey: connectionModeKey) defaults.set("remote", forKey: connectionModeKey)
@@ -111,7 +111,7 @@ import Testing
#expect(resolved.mode == .remote) #expect(resolved.mode == .remote)
} }
@Test func connectionModeResolverFallsBackToDefaultsOnUnknownConfig() { @Test func `connection mode resolver falls back to defaults on unknown config`() {
let defaults = self.makeDefaults() let defaults = self.makeDefaults()
defaults.set("local", forKey: connectionModeKey) defaults.set("local", forKey: connectionModeKey)
@@ -125,7 +125,7 @@ import Testing
#expect(resolved.mode == .local) #expect(resolved.mode == .local)
} }
@Test func connectionModeResolverPrefersRemoteURLWhenModeMissing() { @Test func `connection mode resolver prefers remote URL when mode missing`() {
let defaults = self.makeDefaults() let defaults = self.makeDefaults()
defaults.set("local", forKey: connectionModeKey) defaults.set("local", forKey: connectionModeKey)
@@ -141,35 +141,35 @@ import Testing
#expect(resolved.mode == .remote) #expect(resolved.mode == .remote)
} }
@Test func resolveLocalGatewayHostUsesLoopbackForAutoEvenWithTailnet() { @Test func `resolve local gateway host uses loopback for auto even with tailnet`() {
let host = GatewayEndpointStore._testResolveLocalGatewayHost( let host = GatewayEndpointStore._testResolveLocalGatewayHost(
bindMode: "auto", bindMode: "auto",
tailscaleIP: "100.64.1.2") tailscaleIP: "100.64.1.2")
#expect(host == "127.0.0.1") #expect(host == "127.0.0.1")
} }
@Test func resolveLocalGatewayHostUsesLoopbackForAutoWithoutTailnet() { @Test func `resolve local gateway host uses loopback for auto without tailnet`() {
let host = GatewayEndpointStore._testResolveLocalGatewayHost( let host = GatewayEndpointStore._testResolveLocalGatewayHost(
bindMode: "auto", bindMode: "auto",
tailscaleIP: nil) tailscaleIP: nil)
#expect(host == "127.0.0.1") #expect(host == "127.0.0.1")
} }
@Test func resolveLocalGatewayHostPrefersTailnetForTailnetMode() { @Test func `resolve local gateway host prefers tailnet for tailnet mode`() {
let host = GatewayEndpointStore._testResolveLocalGatewayHost( let host = GatewayEndpointStore._testResolveLocalGatewayHost(
bindMode: "tailnet", bindMode: "tailnet",
tailscaleIP: "100.64.1.5") tailscaleIP: "100.64.1.5")
#expect(host == "100.64.1.5") #expect(host == "100.64.1.5")
} }
@Test func resolveLocalGatewayHostFallsBackToLoopbackForTailnetMode() { @Test func `resolve local gateway host falls back to loopback for tailnet mode`() {
let host = GatewayEndpointStore._testResolveLocalGatewayHost( let host = GatewayEndpointStore._testResolveLocalGatewayHost(
bindMode: "tailnet", bindMode: "tailnet",
tailscaleIP: nil) tailscaleIP: nil)
#expect(host == "127.0.0.1") #expect(host == "127.0.0.1")
} }
@Test func resolveLocalGatewayHostUsesCustomBindHost() { @Test func `resolve local gateway host uses custom bind host`() {
let host = GatewayEndpointStore._testResolveLocalGatewayHost( let host = GatewayEndpointStore._testResolveLocalGatewayHost(
bindMode: "custom", bindMode: "custom",
tailscaleIP: "100.64.1.9", tailscaleIP: "100.64.1.9",
@@ -177,7 +177,7 @@ import Testing
#expect(host == "192.168.1.10") #expect(host == "192.168.1.10")
} }
@Test func localConfigUsesLocalGatewayAuthAndHostResolution() throws { @Test func `local config uses local gateway auth and host resolution`() {
let snapshot = self.makeLaunchAgentSnapshot( let snapshot = self.makeLaunchAgentSnapshot(
env: [:], env: [:],
token: "launchd-token", token: "launchd-token",
@@ -204,7 +204,7 @@ import Testing
#expect(config.password == "launchd-pass") #expect(config.password == "launchd-pass")
} }
@Test func dashboardURLUsesLocalBasePathInLocalMode() throws { @Test func `dashboard URL uses local base path in local mode`() throws {
let config: GatewayConnection.Config = try ( let config: GatewayConnection.Config = try (
url: #require(URL(string: "ws://127.0.0.1:18789")), url: #require(URL(string: "ws://127.0.0.1:18789")),
token: nil, token: nil,
@@ -217,7 +217,7 @@ import Testing
#expect(url.absoluteString == "http://127.0.0.1:18789/control/") #expect(url.absoluteString == "http://127.0.0.1:18789/control/")
} }
@Test func dashboardURLSkipsLocalBasePathInRemoteMode() throws { @Test func `dashboard URL skips local base path in remote mode`() throws {
let config: GatewayConnection.Config = try ( let config: GatewayConnection.Config = try (
url: #require(URL(string: "ws://gateway.example:18789")), url: #require(URL(string: "ws://gateway.example:18789")),
token: nil, token: nil,
@@ -230,7 +230,7 @@ import Testing
#expect(url.absoluteString == "http://gateway.example:18789/") #expect(url.absoluteString == "http://gateway.example:18789/")
} }
@Test func dashboardURLPrefersPathFromConfigURL() throws { @Test func `dashboard URL prefers path from config URL`() throws {
let config: GatewayConnection.Config = try ( let config: GatewayConnection.Config = try (
url: #require(URL(string: "wss://gateway.example:443/remote-ui")), url: #require(URL(string: "wss://gateway.example:443/remote-ui")),
token: nil, token: nil,
@@ -243,7 +243,7 @@ import Testing
#expect(url.absoluteString == "https://gateway.example:443/remote-ui/") #expect(url.absoluteString == "https://gateway.example:443/remote-ui/")
} }
@Test func dashboardURLUsesFragmentTokenAndOmitsPassword() throws { @Test func `dashboard URL uses fragment token and omits password`() throws {
let config: GatewayConnection.Config = try ( let config: GatewayConnection.Config = try (
url: #require(URL(string: "ws://127.0.0.1:18789")), url: #require(URL(string: "ws://127.0.0.1:18789")),
token: "abc123", token: "abc123",
@@ -257,18 +257,18 @@ import Testing
#expect(url.query == nil) #expect(url.query == nil)
} }
@Test func normalizeGatewayUrlAddsDefaultPortForLoopbackWs() { @Test func `normalize gateway url adds default port for loopback ws`() {
let url = GatewayRemoteConfig.normalizeGatewayUrl("ws://127.0.0.1") let url = GatewayRemoteConfig.normalizeGatewayUrl("ws://127.0.0.1")
#expect(url?.port == 18789) #expect(url?.port == 18789)
#expect(url?.absoluteString == "ws://127.0.0.1:18789") #expect(url?.absoluteString == "ws://127.0.0.1:18789")
} }
@Test func normalizeGatewayUrlRejectsNonLoopbackWs() { @Test func `normalize gateway url rejects non loopback ws`() {
let url = GatewayRemoteConfig.normalizeGatewayUrl("ws://gateway.example:18789") let url = GatewayRemoteConfig.normalizeGatewayUrl("ws://gateway.example:18789")
#expect(url == nil) #expect(url == nil)
} }
@Test func normalizeGatewayUrlRejectsPrefixBypassLoopbackHost() { @Test func `normalize gateway url rejects prefix bypass loopback host`() {
let url = GatewayRemoteConfig.normalizeGatewayUrl("ws://127.attacker.example") let url = GatewayRemoteConfig.normalizeGatewayUrl("ws://127.attacker.example")
#expect(url == nil) #expect(url == nil)
} }

View File

@@ -2,8 +2,8 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct GatewayEnvironmentTests { struct GatewayEnvironmentTests {
@Test func semverParsesCommonForms() { @Test func `semver parses common forms`() {
#expect(Semver.parse("1.2.3") == Semver(major: 1, minor: 2, patch: 3)) #expect(Semver.parse("1.2.3") == Semver(major: 1, minor: 2, patch: 3))
#expect(Semver.parse(" v1.2.3 \n") == Semver(major: 1, minor: 2, patch: 3)) #expect(Semver.parse(" v1.2.3 \n") == Semver(major: 1, minor: 2, patch: 3))
#expect(Semver.parse("v2.0.0") == Semver(major: 2, minor: 0, patch: 0)) #expect(Semver.parse("v2.0.0") == Semver(major: 2, minor: 0, patch: 0))
@@ -21,7 +21,7 @@ import Testing
#expect(Semver.parse("1.2.x") == nil) #expect(Semver.parse("1.2.x") == nil)
} }
@Test func semverCompatibilityRequiresSameMajorAndNotOlder() { @Test func `semver compatibility requires same major and not older`() {
let required = Semver(major: 2, minor: 1, patch: 0) let required = Semver(major: 2, minor: 1, patch: 0)
#expect(Semver(major: 2, minor: 1, patch: 0).compatible(with: required)) #expect(Semver(major: 2, minor: 1, patch: 0).compatible(with: required))
#expect(Semver(major: 2, minor: 2, patch: 0).compatible(with: required)) #expect(Semver(major: 2, minor: 2, patch: 0).compatible(with: required))
@@ -31,7 +31,7 @@ import Testing
#expect(Semver(major: 1, minor: 9, patch: 9).compatible(with: required) == false) #expect(Semver(major: 1, minor: 9, patch: 9).compatible(with: required) == false)
} }
@Test func gatewayPortDefaultsAndRespectsOverride() async { @Test func `gateway port defaults and respects override`() async {
let configPath = TestIsolation.tempConfigPath() let configPath = TestIsolation.tempConfigPath()
await TestIsolation.withIsolatedState( await TestIsolation.withIsolatedState(
env: ["OPENCLAW_CONFIG_PATH": configPath], env: ["OPENCLAW_CONFIG_PATH": configPath],
@@ -46,7 +46,7 @@ import Testing
} }
} }
@Test func expectedGatewayVersionFromStringUsesParser() { @Test func `expected gateway version from string uses parser`() {
#expect(GatewayEnvironment.expectedGatewayVersion(from: "v9.1.2") == Semver(major: 9, minor: 1, patch: 2)) #expect(GatewayEnvironment.expectedGatewayVersion(from: "v9.1.2") == Semver(major: 9, minor: 1, patch: 2))
#expect(GatewayEnvironment.expectedGatewayVersion(from: "2026.1.11-4") == Semver( #expect(GatewayEnvironment.expectedGatewayVersion(from: "2026.1.11-4") == Semver(
major: 2026, major: 2026,

View File

@@ -2,8 +2,8 @@ import Foundation
import OpenClawProtocol import OpenClawProtocol
import Testing import Testing
@Suite struct GatewayFrameDecodeTests { struct GatewayFrameDecodeTests {
@Test func decodesEventFrameWithAnyCodablePayload() throws { @Test func `decodes event frame with any codable payload`() throws {
let json = """ let json = """
{ {
"type": "event", "type": "event",
@@ -29,7 +29,7 @@ import Testing
#expect(evt.seq == 7) #expect(evt.seq == 7)
} }
@Test func decodesRequestFrameWithNestedParams() throws { @Test func `decodes request frame with nested params`() throws {
let json = """ let json = """
{ {
"type": "req", "type": "req",
@@ -68,7 +68,7 @@ import Testing
#expect(meta?["count"]?.value as? Int == 2) #expect(meta?["count"]?.value as? Int == 2)
} }
@Test func decodesUnknownFrameAndPreservesRaw() throws { @Test func `decodes unknown frame and preserves raw`() throws {
let json = """ let json = """
{ {
"type": "made-up", "type": "made-up",

View File

@@ -2,8 +2,8 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct GatewayLaunchAgentManagerTests { struct GatewayLaunchAgentManagerTests {
@Test func launchAgentPlistSnapshotParsesArgsAndEnv() throws { @Test func `launch agent plist snapshot parses args and env`() throws {
let url = FileManager().temporaryDirectory let url = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-launchd-\(UUID().uuidString).plist") .appendingPathComponent("openclaw-launchd-\(UUID().uuidString).plist")
let plist: [String: Any] = [ let plist: [String: Any] = [
@@ -24,7 +24,7 @@ import Testing
#expect(snapshot.password == "pw") #expect(snapshot.password == "pw")
} }
@Test func launchAgentPlistSnapshotAllowsMissingBind() throws { @Test func `launch agent plist snapshot allows missing bind`() throws {
let url = FileManager().temporaryDirectory let url = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-launchd-\(UUID().uuidString).plist") .appendingPathComponent("openclaw-launchd-\(UUID().uuidString).plist")
let plist: [String: Any] = [ let plist: [String: Any] = [

View File

@@ -6,7 +6,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct GatewayProcessManagerTests { struct GatewayProcessManagerTests {
@Test func clearsLastFailureWhenHealthSucceeds() async throws { @Test func `clears last failure when health succeeds`() async throws {
let session = GatewayTestWebSocketSession( let session = GatewayTestWebSocketSession(
taskFactory: { taskFactory: {
GatewayTestWebSocketTask( GatewayTestWebSocketTask(

View File

@@ -83,9 +83,9 @@ enum GatewayWebSocketTestSupport {
} }
} }
private extension NSLock { extension NSLock {
@inline(__always) @inline(__always)
func withLock<T>(_ body: () throws -> T) rethrows -> T { fileprivate func withLock<T>(_ body: () throws -> T) rethrows -> T {
self.lock(); defer { self.unlock() } self.lock(); defer { self.unlock() }
return try body() return try body()
} }
@@ -129,7 +129,10 @@ final class GatewayTestWebSocketTask: WebSocketTasking, @unchecked Sendable {
func cancel(with closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { func cancel(with closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
_ = (closeCode, reason) _ = (closeCode, reason)
let handler = self.lock.withLock { () -> (@Sendable (Result<URLSessionWebSocketTask.Message, Error>) -> Void)? in let handler = self.lock.withLock { () -> (@Sendable (Result<
URLSessionWebSocketTask.Message,
Error,
>) -> Void)? in
self._state = .canceling self._state = .canceling
self.cancelCount += 1 self.cancelCount += 1
defer { self.pendingReceiveHandler = nil } defer { self.pendingReceiveHandler = nil }

View File

@@ -2,13 +2,13 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct HealthDecodeTests { struct HealthDecodeTests {
private let sampleJSON: String = // minimal but complete payload private let sampleJSON: String = // minimal but complete payload
""" """
{"ts":1733622000,"durationMs":420,"channels":{"whatsapp":{"linked":true,"authAgeMs":120000},"telegram":{"configured":true,"probe":{"ok":true,"elapsedMs":800}}},"channelOrder":["whatsapp","telegram"],"heartbeatSeconds":60,"sessions":{"path":"/tmp/sessions.json","count":1,"recent":[{"key":"abc","updatedAt":1733621900,"age":120000}]}} {"ts":1733622000,"durationMs":420,"channels":{"whatsapp":{"linked":true,"authAgeMs":120000},"telegram":{"configured":true,"probe":{"ok":true,"elapsedMs":800}}},"channelOrder":["whatsapp","telegram"],"heartbeatSeconds":60,"sessions":{"path":"/tmp/sessions.json","count":1,"recent":[{"key":"abc","updatedAt":1733621900,"age":120000}]}}
""" """
@Test func decodesCleanJSON() { @Test func `decodes clean JSON`() {
let data = Data(sampleJSON.utf8) let data = Data(sampleJSON.utf8)
let snap = decodeHealthSnapshot(from: data) let snap = decodeHealthSnapshot(from: data)
@@ -16,14 +16,14 @@ import Testing
#expect(snap?.sessions.count == 1) #expect(snap?.sessions.count == 1)
} }
@Test func decodesWithLeadingNoise() { @Test func `decodes with leading noise`() {
let noisy = "debug: something logged\n" + self.sampleJSON + "\ntrailer" let noisy = "debug: something logged\n" + self.sampleJSON + "\ntrailer"
let snap = decodeHealthSnapshot(from: Data(noisy.utf8)) let snap = decodeHealthSnapshot(from: Data(noisy.utf8))
#expect(snap?.channels["telegram"]?.probe?.elapsedMs == 800) #expect(snap?.channels["telegram"]?.probe?.elapsedMs == 800)
} }
@Test func failsWithoutBraces() { @Test func `fails without braces`() {
let data = Data("no json here".utf8) let data = Data("no json here".utf8)
let snap = decodeHealthSnapshot(from: data) let snap = decodeHealthSnapshot(from: data)

View File

@@ -2,8 +2,8 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct HealthStoreStateTests { struct HealthStoreStateTests {
@Test @MainActor func linkedChannelProbeFailureDegradesState() { @Test @MainActor func `linked channel probe failure degrades state`() {
let snap = HealthSnapshot( let snap = HealthSnapshot(
ok: true, ok: true,
ts: 0, ts: 0,

View File

@@ -2,7 +2,7 @@ import Testing
@testable import OpenClaw @testable import OpenClaw
struct HostEnvSanitizerTests { struct HostEnvSanitizerTests {
@Test func sanitizeBlocksShellTraceVariables() { @Test func `sanitize blocks shell trace variables`() {
let env = HostEnvSanitizer.sanitize(overrides: [ let env = HostEnvSanitizer.sanitize(overrides: [
"SHELLOPTS": "xtrace", "SHELLOPTS": "xtrace",
"PS4": "$(touch /tmp/pwned)", "PS4": "$(touch /tmp/pwned)",
@@ -13,7 +13,7 @@ struct HostEnvSanitizerTests {
#expect(env["OPENCLAW_TEST"] == "1") #expect(env["OPENCLAW_TEST"] == "1")
} }
@Test func sanitizeShellWrapperAllowsOnlyExplicitOverrideKeys() { @Test func `sanitize shell wrapper allows only explicit override keys`() {
let env = HostEnvSanitizer.sanitize( let env = HostEnvSanitizer.sanitize(
overrides: [ overrides: [
"LANG": "C", "LANG": "C",
@@ -29,7 +29,7 @@ struct HostEnvSanitizerTests {
#expect(env["PS4"] == nil) #expect(env["PS4"] == nil)
} }
@Test func sanitizeNonShellWrapperKeepsRegularOverrides() { @Test func `sanitize non shell wrapper keeps regular overrides`() {
let env = HostEnvSanitizer.sanitize(overrides: ["OPENCLAW_TOKEN": "secret"]) let env = HostEnvSanitizer.sanitize(overrides: ["OPENCLAW_TOKEN": "secret"])
#expect(env["OPENCLAW_TOKEN"] == "secret") #expect(env["OPENCLAW_TOKEN"] == "secret")
} }

View File

@@ -5,7 +5,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct HoverHUDControllerTests { struct HoverHUDControllerTests {
@Test func hoverHUDControllerPresentsAndDismisses() async { @Test func `hover HUD controller presents and dismisses`() async {
let controller = HoverHUDController() let controller = HoverHUDController()
controller.setSuppressed(false) controller.setSuppressed(false)

View File

@@ -4,7 +4,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct InstancesSettingsSmokeTests { struct InstancesSettingsSmokeTests {
@Test func instancesSettingsBuildsBodyWithMultipleInstances() { @Test func `instances settings builds body with multiple instances`() {
let store = InstancesStore(isPreview: true) let store = InstancesStore(isPreview: true)
store.statusMessage = "Loaded" store.statusMessage = "Loaded"
store.instances = [ store.instances = [
@@ -53,7 +53,7 @@ struct InstancesSettingsSmokeTests {
_ = view.body _ = view.body
} }
@Test func instancesSettingsExercisesHelpers() { @Test func `instances settings exercises helpers`() {
InstancesSettings.exerciseForTesting() InstancesSettings.exerciseForTesting()
} }
} }

View File

@@ -2,10 +2,10 @@ import OpenClawProtocol
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct InstancesStoreTests { struct InstancesStoreTests {
@Test @Test
@MainActor @MainActor
func presenceEventPayloadDecodesViaJSONEncoder() { func `presence event payload decodes via JSON encoder`() {
// Build a payload that mirrors the gateway's presence event shape: // Build a payload that mirrors the gateway's presence event shape:
// { "presence": [ PresenceEntry ] } // { "presence": [ PresenceEntry ] }
let entry: [String: OpenClawProtocol.AnyCodable] = [ let entry: [String: OpenClawProtocol.AnyCodable] = [

View File

@@ -3,8 +3,8 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct LogLocatorTests { struct LogLocatorTests {
@Test func launchdGatewayLogPathEnsuresTmpDirExists() { @Test func `launchd gateway log path ensures tmp dir exists`() {
let fm = FileManager() let fm = FileManager()
let baseDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let baseDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let logDir = baseDir.appendingPathComponent("openclaw-tests-\(UUID().uuidString)") let logDir = baseDir.appendingPathComponent("openclaw-tests-\(UUID().uuidString)")

View File

@@ -8,7 +8,7 @@ import Testing
struct LowCoverageHelperTests { struct LowCoverageHelperTests {
private typealias ProtoAnyCodable = OpenClawProtocol.AnyCodable private typealias ProtoAnyCodable = OpenClawProtocol.AnyCodable
@Test func anyCodableHelperAccessors() throws { @Test func `any codable helper accessors`() throws {
let payload: [String: ProtoAnyCodable] = [ let payload: [String: ProtoAnyCodable] = [
"title": ProtoAnyCodable("Hello"), "title": ProtoAnyCodable("Hello"),
"flag": ProtoAnyCodable(true), "flag": ProtoAnyCodable(true),
@@ -28,7 +28,7 @@ struct LowCoverageHelperTests {
#expect((foundation?["title"] as? String) == "Hello") #expect((foundation?["title"] as? String) == "Hello")
} }
@Test func attributedStringStripsForegroundColor() { @Test func `attributed string strips foreground color`() {
let text = NSMutableAttributedString(string: "Test") let text = NSMutableAttributedString(string: "Test")
text.addAttribute(.foregroundColor, value: NSColor.red, range: NSRange(location: 0, length: 4)) text.addAttribute(.foregroundColor, value: NSColor.red, range: NSRange(location: 0, length: 4))
let stripped = text.strippingForegroundColor() let stripped = text.strippingForegroundColor()
@@ -36,29 +36,29 @@ struct LowCoverageHelperTests {
#expect(color == nil) #expect(color == nil)
} }
@Test func viewMetricsReduceWidth() { @Test func `view metrics reduce width`() {
let value = ViewMetricsTesting.reduceWidth(current: 120, next: 180) let value = ViewMetricsTesting.reduceWidth(current: 120, next: 180)
#expect(value == 180) #expect(value == 180)
} }
@Test func shellExecutorHandlesEmptyCommand() async { @Test func `shell executor handles empty command`() async {
let result = await ShellExecutor.runDetailed(command: [], cwd: nil, env: nil, timeout: nil) let result = await ShellExecutor.runDetailed(command: [], cwd: nil, env: nil, timeout: nil)
#expect(result.success == false) #expect(result.success == false)
#expect(result.errorMessage != nil) #expect(result.errorMessage != nil)
} }
@Test func shellExecutorRunsCommand() async { @Test func `shell executor runs command`() async {
let result = await ShellExecutor.runDetailed(command: ["/bin/echo", "ok"], cwd: nil, env: nil, timeout: 2) let result = await ShellExecutor.runDetailed(command: ["/bin/echo", "ok"], cwd: nil, env: nil, timeout: 2)
#expect(result.success == true) #expect(result.success == true)
#expect(result.stdout.contains("ok") || result.stderr.contains("ok")) #expect(result.stdout.contains("ok") || result.stderr.contains("ok"))
} }
@Test func shellExecutorTimesOut() async { @Test func `shell executor times out`() async {
let result = await ShellExecutor.runDetailed(command: ["/bin/sleep", "1"], cwd: nil, env: nil, timeout: 0.05) let result = await ShellExecutor.runDetailed(command: ["/bin/sleep", "1"], cwd: nil, env: nil, timeout: 0.05)
#expect(result.timedOut == true) #expect(result.timedOut == true)
} }
@Test func shellExecutorDrainsStdoutAndStderr() async { @Test func `shell executor drains stdout and stderr`() async {
let script = """ let script = """
i=0 i=0
while [ $i -lt 2000 ]; do while [ $i -lt 2000 ]; do
@@ -77,7 +77,7 @@ struct LowCoverageHelperTests {
#expect(result.stderr.contains("stderr-1999")) #expect(result.stderr.contains("stderr-1999"))
} }
@Test func nodeInfoCodableRoundTrip() throws { @Test func `node info codable round trip`() throws {
let info = NodeInfo( let info = NodeInfo(
nodeId: "node-1", nodeId: "node-1",
displayName: "Node One", displayName: "Node One",
@@ -100,7 +100,7 @@ struct LowCoverageHelperTests {
#expect(decoded.isConnected == false) #expect(decoded.isConnected == false)
} }
@Test @MainActor func presenceReporterHelpers() { @Test @MainActor func `presence reporter helpers`() {
let summary = PresenceReporter._testComposePresenceSummary(mode: "local", reason: "test") let summary = PresenceReporter._testComposePresenceSummary(mode: "local", reason: "test")
#expect(summary.contains("mode local")) #expect(summary.contains("mode local"))
#expect(!PresenceReporter._testAppVersionString().isEmpty) #expect(!PresenceReporter._testAppVersionString().isEmpty)
@@ -109,7 +109,7 @@ struct LowCoverageHelperTests {
_ = PresenceReporter._testPrimaryIPv4Address() _ = PresenceReporter._testPrimaryIPv4Address()
} }
@Test func portGuardianParsesListenersAndBuildsReports() { @Test func `port guardian parses listeners and builds reports`() {
let output = """ let output = """
p123 p123
cnode cnode
@@ -139,7 +139,7 @@ struct LowCoverageHelperTests {
#expect(emptyReport.summary.contains("Nothing is listening")) #expect(emptyReport.summary.contains("Nothing is listening"))
} }
@Test @MainActor func canvasSchemeHandlerResolvesFilesAndErrors() throws { @Test @MainActor func `canvas scheme handler resolves files and errors`() throws {
let root = FileManager().temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("canvas-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("canvas-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager().removeItem(at: root) } defer { try? FileManager().removeItem(at: root) }
@@ -168,7 +168,7 @@ struct LowCoverageHelperTests {
#expect(handler._testTextEncodingName(for: "application/octet-stream") == nil) #expect(handler._testTextEncodingName(for: "application/octet-stream") == nil)
} }
@Test @MainActor func menuContextCardInjectorInsertsAndFindsIndex() { @Test @MainActor func `menu context card injector inserts and finds index`() {
let injector = MenuContextCardInjector() let injector = MenuContextCardInjector()
let menu = NSMenu() let menu = NSMenu()
menu.minimumWidth = 280 menu.minimumWidth = 280
@@ -190,7 +190,7 @@ struct LowCoverageHelperTests {
#expect(injector._testFindInsertIndex(in: fallbackMenu) == 1) #expect(injector._testFindInsertIndex(in: fallbackMenu) == 1)
} }
@Test @MainActor func canvasWindowHelperFunctions() throws { @Test @MainActor func `canvas window helper functions`() throws {
#expect(CanvasWindowController._testSanitizeSessionKey(" main ") == "main") #expect(CanvasWindowController._testSanitizeSessionKey(" main ") == "main")
#expect(CanvasWindowController._testSanitizeSessionKey("bad/..") == "bad___") #expect(CanvasWindowController._testSanitizeSessionKey("bad/..") == "bad___")
#expect(CanvasWindowController._testJSOptionalStringLiteral(nil) == "null") #expect(CanvasWindowController._testJSOptionalStringLiteral(nil) == "null")

View File

@@ -7,7 +7,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct LowCoverageViewSmokeTests { struct LowCoverageViewSmokeTests {
@Test func contextMenuCardBuildsBody() { @Test func `context menu card builds body`() {
let loading = ContextMenuCardView(rows: [], statusText: "Loading…", isLoading: true) let loading = ContextMenuCardView(rows: [], statusText: "Loading…", isLoading: true)
_ = loading.body _ = loading.body
@@ -18,14 +18,14 @@ struct LowCoverageViewSmokeTests {
_ = withRows.body _ = withRows.body
} }
@Test func settingsToggleRowBuildsBody() { @Test func `settings toggle row builds body`() {
var flag = false var flag = false
let binding = Binding(get: { flag }, set: { flag = $0 }) let binding = Binding(get: { flag }, set: { flag = $0 })
let view = SettingsToggleRow(title: "Enable", subtitle: "Detail", binding: binding) let view = SettingsToggleRow(title: "Enable", subtitle: "Detail", binding: binding)
_ = view.body _ = view.body
} }
@Test func voiceWakeTestCardBuildsBodyAcrossStates() { @Test func `voice wake test card builds body across states`() {
var state = VoiceWakeTestState.idle var state = VoiceWakeTestState.idle
var isTesting = false var isTesting = false
let stateBinding = Binding(get: { state }, set: { state = $0 }) let stateBinding = Binding(get: { state }, set: { state = $0 })
@@ -44,7 +44,7 @@ struct LowCoverageViewSmokeTests {
_ = VoiceWakeTestCard(testState: stateBinding, isTesting: testingBinding, onToggle: {}).body _ = VoiceWakeTestCard(testState: stateBinding, isTesting: testingBinding, onToggle: {}).body
} }
@Test func agentEventsWindowBuildsBodyWithEvent() { @Test func `agent events window builds body with event`() {
AgentEventStore.shared.clear() AgentEventStore.shared.clear()
let sample = ControlAgentEvent( let sample = ControlAgentEvent(
runId: "run-1", runId: "run-1",
@@ -58,7 +58,7 @@ struct LowCoverageViewSmokeTests {
AgentEventStore.shared.clear() AgentEventStore.shared.clear()
} }
@Test func notifyOverlayPresentsAndDismisses() async { @Test func `notify overlay presents and dismisses`() async {
let controller = NotifyOverlayController() let controller = NotifyOverlayController()
controller.present(title: "Hello", body: "World", autoDismissAfter: 0) controller.present(title: "Hello", body: "World", autoDismissAfter: 0)
controller.present(title: "Updated", body: "Again", autoDismissAfter: 0) controller.present(title: "Updated", body: "Again", autoDismissAfter: 0)
@@ -66,14 +66,14 @@ struct LowCoverageViewSmokeTests {
try? await Task.sleep(nanoseconds: 250_000_000) try? await Task.sleep(nanoseconds: 250_000_000)
} }
@Test func visualEffectViewHostsInNSHostingView() { @Test func `visual effect view hosts in NS hosting view`() {
let hosting = NSHostingView(rootView: VisualEffectView(material: .sidebar)) let hosting = NSHostingView(rootView: VisualEffectView(material: .sidebar))
_ = hosting.fittingSize _ = hosting.fittingSize
hosting.rootView = VisualEffectView(material: .popover, emphasized: true) hosting.rootView = VisualEffectView(material: .popover, emphasized: true)
_ = hosting.fittingSize _ = hosting.fittingSize
} }
@Test func menuHostedItemHostsContent() { @Test func `menu hosted item hosts content`() {
let view = MenuHostedItem(width: 240, rootView: AnyView(Text("Menu"))) let view = MenuHostedItem(width: 240, rootView: AnyView(Text("Menu")))
let hosting = NSHostingView(rootView: view) let hosting = NSHostingView(rootView: view)
_ = hosting.fittingSize _ = hosting.fittingSize
@@ -81,18 +81,18 @@ struct LowCoverageViewSmokeTests {
_ = hosting.fittingSize _ = hosting.fittingSize
} }
@Test func dockIconManagerUpdatesVisibility() { @Test func `dock icon manager updates visibility`() {
_ = NSApplication.shared _ = NSApplication.shared
UserDefaults.standard.set(false, forKey: showDockIconKey) UserDefaults.standard.set(false, forKey: showDockIconKey)
DockIconManager.shared.updateDockVisibility() DockIconManager.shared.updateDockVisibility()
DockIconManager.shared.temporarilyShowDock() DockIconManager.shared.temporarilyShowDock()
} }
@Test func voiceWakeSettingsExercisesHelpers() { @Test func `voice wake settings exercises helpers`() {
VoiceWakeSettings.exerciseForTesting() VoiceWakeSettings.exerciseForTesting()
} }
@Test func debugSettingsExercisesHelpers() async { @Test func `debug settings exercises helpers`() async {
await DebugSettings.exerciseForTesting() await DebugSettings.exerciseForTesting()
} }
} }

View File

@@ -3,8 +3,8 @@ import OpenClawProtocol
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct MacGatewayChatTransportMappingTests { struct MacGatewayChatTransportMappingTests {
@Test func snapshotMapsToHealth() { @Test func `snapshot maps to health`() {
let snapshot = Snapshot( let snapshot = Snapshot(
presence: [], presence: [],
health: OpenClawProtocol.AnyCodable(["ok": OpenClawProtocol.AnyCodable(false)]), health: OpenClawProtocol.AnyCodable(["ok": OpenClawProtocol.AnyCodable(false)]),
@@ -35,7 +35,7 @@ import Testing
} }
} }
@Test func healthEventMapsToHealth() { @Test func `health event maps to health`() {
let frame = EventFrame( let frame = EventFrame(
type: "event", type: "event",
event: "health", event: "health",
@@ -52,7 +52,7 @@ import Testing
} }
} }
@Test func tickEventMapsToTick() { @Test func `tick event maps to tick`() {
let frame = EventFrame(type: "event", event: "tick", payload: nil, seq: 1, stateversion: nil) let frame = EventFrame(type: "event", event: "tick", payload: nil, seq: 1, stateversion: nil)
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame)) let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame))
#expect({ #expect({
@@ -61,7 +61,7 @@ import Testing
}()) }())
} }
@Test func chatEventMapsToChat() { @Test func `chat event maps to chat`() {
let payload = OpenClawProtocol.AnyCodable([ let payload = OpenClawProtocol.AnyCodable([
"runId": OpenClawProtocol.AnyCodable("run-1"), "runId": OpenClawProtocol.AnyCodable("run-1"),
"sessionKey": OpenClawProtocol.AnyCodable("main"), "sessionKey": OpenClawProtocol.AnyCodable("main"),
@@ -80,7 +80,7 @@ import Testing
} }
} }
@Test func unknownEventMapsToNil() { @Test func `unknown event maps to nil`() {
let frame = EventFrame( let frame = EventFrame(
type: "event", type: "event",
event: "unknown", event: "unknown",
@@ -91,7 +91,7 @@ import Testing
#expect(mapped == nil) #expect(mapped == nil)
} }
@Test func seqGapMapsToSeqGap() { @Test func `seq gap maps to seq gap`() {
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.seqGap(expected: 1, received: 9)) let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.seqGap(expected: 1, received: 9))
#expect({ #expect({
if case .seqGap = mapped { return true } if case .seqGap = mapped { return true }

View File

@@ -3,7 +3,7 @@ import Testing
@testable import OpenClaw @testable import OpenClaw
struct MacNodeBrowserProxyTests { struct MacNodeBrowserProxyTests {
@Test func requestUsesBrowserControlEndpointAndWrapsResult() async throws { @Test func `request uses browser control endpoint and wraps result`() async throws {
let proxy = MacNodeBrowserProxy( let proxy = MacNodeBrowserProxy(
endpointProvider: { endpointProvider: {
MacNodeBrowserProxy.Endpoint( MacNodeBrowserProxy.Endpoint(

View File

@@ -5,14 +5,14 @@ import Testing
@testable import OpenClaw @testable import OpenClaw
struct MacNodeRuntimeTests { struct MacNodeRuntimeTests {
@Test func handleInvokeRejectsUnknownCommand() async { @Test func `handle invoke rejects unknown command`() async {
let runtime = MacNodeRuntime() let runtime = MacNodeRuntime()
let response = await runtime.handleInvoke( let response = await runtime.handleInvoke(
BridgeInvokeRequest(id: "req-1", command: "unknown.command")) BridgeInvokeRequest(id: "req-1", command: "unknown.command"))
#expect(response.ok == false) #expect(response.ok == false)
} }
@Test func handleInvokeRejectsEmptySystemRun() async throws { @Test func `handle invoke rejects empty system run`() async throws {
let runtime = MacNodeRuntime() let runtime = MacNodeRuntime()
let params = OpenClawSystemRunParams(command: []) let params = OpenClawSystemRunParams(command: [])
let json = try String(data: JSONEncoder().encode(params), encoding: .utf8) let json = try String(data: JSONEncoder().encode(params), encoding: .utf8)
@@ -21,7 +21,7 @@ struct MacNodeRuntimeTests {
#expect(response.ok == false) #expect(response.ok == false)
} }
@Test func handleInvokeRejectsEmptySystemWhich() async throws { @Test func `handle invoke rejects empty system which`() async throws {
let runtime = MacNodeRuntime() let runtime = MacNodeRuntime()
let params = OpenClawSystemWhichParams(bins: []) let params = OpenClawSystemWhichParams(bins: [])
let json = try String(data: JSONEncoder().encode(params), encoding: .utf8) let json = try String(data: JSONEncoder().encode(params), encoding: .utf8)
@@ -30,7 +30,7 @@ struct MacNodeRuntimeTests {
#expect(response.ok == false) #expect(response.ok == false)
} }
@Test func handleInvokeRejectsEmptyNotification() async throws { @Test func `handle invoke rejects empty notification`() async throws {
let runtime = MacNodeRuntime() let runtime = MacNodeRuntime()
let params = OpenClawSystemNotifyParams(title: "", body: "") let params = OpenClawSystemNotifyParams(title: "", body: "")
let json = try String(data: JSONEncoder().encode(params), encoding: .utf8) let json = try String(data: JSONEncoder().encode(params), encoding: .utf8)
@@ -39,7 +39,7 @@ struct MacNodeRuntimeTests {
#expect(response.ok == false) #expect(response.ok == false)
} }
@Test func handleInvokeCameraListRequiresEnabledCamera() async { @Test func `handle invoke camera list requires enabled camera`() async {
await TestIsolation.withUserDefaultsValues([cameraEnabledKey: false]) { await TestIsolation.withUserDefaultsValues([cameraEnabledKey: false]) {
let runtime = MacNodeRuntime() let runtime = MacNodeRuntime()
let response = await runtime.handleInvoke( let response = await runtime.handleInvoke(
@@ -49,7 +49,7 @@ struct MacNodeRuntimeTests {
} }
} }
@Test func handleInvokeScreenRecordUsesInjectedServices() async throws { @Test func `handle invoke screen record uses injected services`() async throws {
@MainActor @MainActor
final class FakeMainActorServices: MacNodeRuntimeMainActorServices, @unchecked Sendable { final class FakeMainActorServices: MacNodeRuntimeMainActorServices, @unchecked Sendable {
func recordScreen( func recordScreen(
@@ -101,20 +101,23 @@ struct MacNodeRuntimeTests {
#expect(!payload.base64.isEmpty) #expect(!payload.base64.isEmpty)
} }
@Test func handleInvokeBrowserProxyUsesInjectedRequest() async throws { @Test func `handle invoke browser proxy uses injected request`() async {
let runtime = MacNodeRuntime(browserProxyRequest: { paramsJSON in let runtime = MacNodeRuntime(browserProxyRequest: { paramsJSON in
#expect(paramsJSON?.contains("/tabs") == true) #expect(paramsJSON?.contains("/tabs") == true)
return #"{"result":{"ok":true,"tabs":[{"id":"tab-1"}]}}"# return #"{"result":{"ok":true,"tabs":[{"id":"tab-1"}]}}"#
}) })
let paramsJSON = #"{"method":"GET","path":"/tabs","timeoutMs":2500}"# let paramsJSON = #"{"method":"GET","path":"/tabs","timeoutMs":2500}"#
let response = await runtime.handleInvoke( let response = await runtime.handleInvoke(
BridgeInvokeRequest(id: "req-browser", command: OpenClawBrowserCommand.proxy.rawValue, paramsJSON: paramsJSON)) BridgeInvokeRequest(
id: "req-browser",
command: OpenClawBrowserCommand.proxy.rawValue,
paramsJSON: paramsJSON))
#expect(response.ok == true) #expect(response.ok == true)
#expect(response.payloadJSON == #"{"result":{"ok":true,"tabs":[{"id":"tab-1"}]}}"#) #expect(response.payloadJSON == #"{"result":{"ok":true,"tabs":[{"id":"tab-1"}]}}"#)
} }
@Test func handleInvokeBrowserProxyRejectsDisabledBrowserControl() async throws { @Test func `handle invoke browser proxy rejects disabled browser control`() async throws {
let override = TestIsolation.tempConfigPath() let override = TestIsolation.tempConfigPath()
try await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) { try await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
try JSONSerialization.data(withJSONObject: ["browser": ["enabled": false]]) try JSONSerialization.data(withJSONObject: ["browser": ["enabled": false]])

View File

@@ -6,7 +6,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct MasterDiscoveryMenuSmokeTests { struct MasterDiscoveryMenuSmokeTests {
@Test func inlineListBuildsBodyWhenEmpty() { @Test func `inline list builds body when empty`() {
let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName) let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName)
discovery.statusText = "Searching…" discovery.statusText = "Searching…"
discovery.gateways = [] discovery.gateways = []
@@ -20,7 +20,7 @@ struct MasterDiscoveryMenuSmokeTests {
_ = view.body _ = view.body
} }
@Test func inlineListBuildsBodyWithMasterAndSelection() { @Test func `inline list builds body with master and selection`() {
let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName) let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName)
discovery.statusText = "Found 1" discovery.statusText = "Found 1"
discovery.gateways = [ discovery.gateways = [
@@ -46,7 +46,7 @@ struct MasterDiscoveryMenuSmokeTests {
_ = view.body _ = view.body
} }
@Test func menuBuildsBodyWithMasters() { @Test func `menu builds body with masters`() {
let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName) let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName)
discovery.statusText = "Found 2" discovery.statusText = "Found 2"
discovery.gateways = [ discovery.gateways = [

View File

@@ -5,28 +5,28 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct MenuContentSmokeTests { struct MenuContentSmokeTests {
@Test func menuContentBuildsBodyLocalMode() { @Test func `menu content builds body local mode`() {
let state = AppState(preview: true) let state = AppState(preview: true)
state.connectionMode = .local state.connectionMode = .local
let view = MenuContent(state: state, updater: nil) let view = MenuContent(state: state, updater: nil)
_ = view.body _ = view.body
} }
@Test func menuContentBuildsBodyRemoteMode() { @Test func `menu content builds body remote mode`() {
let state = AppState(preview: true) let state = AppState(preview: true)
state.connectionMode = .remote state.connectionMode = .remote
let view = MenuContent(state: state, updater: nil) let view = MenuContent(state: state, updater: nil)
_ = view.body _ = view.body
} }
@Test func menuContentBuildsBodyUnconfiguredMode() { @Test func `menu content builds body unconfigured mode`() {
let state = AppState(preview: true) let state = AppState(preview: true)
state.connectionMode = .unconfigured state.connectionMode = .unconfigured
let view = MenuContent(state: state, updater: nil) let view = MenuContent(state: state, updater: nil)
_ = view.body _ = view.body
} }
@Test func menuContentBuildsBodyWithDebugAndCanvas() { @Test func `menu content builds body with debug and canvas`() {
let state = AppState(preview: true) let state = AppState(preview: true)
state.connectionMode = .local state.connectionMode = .local
state.debugPaneEnabled = true state.debugPaneEnabled = true

View File

@@ -5,7 +5,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct MenuSessionsInjectorTests { struct MenuSessionsInjectorTests {
@Test func injectsDisconnectedMessage() { @Test func `injects disconnected message`() {
let injector = MenuSessionsInjector() let injector = MenuSessionsInjector()
injector.setTestingControlChannelConnected(false) injector.setTestingControlChannelConnected(false)
injector.setTestingSnapshot(nil, errorText: nil) injector.setTestingSnapshot(nil, errorText: nil)
@@ -19,7 +19,7 @@ struct MenuSessionsInjectorTests {
#expect(menu.items.contains { $0.tag == 9_415_557 }) #expect(menu.items.contains { $0.tag == 9_415_557 })
} }
@Test func injectsSessionRows() { @Test func `injects session rows`() {
let injector = MenuSessionsInjector() let injector = MenuSessionsInjector()
injector.setTestingControlChannelConnected(true) injector.setTestingControlChannelConnected(true)
@@ -94,7 +94,7 @@ struct MenuSessionsInjectorTests {
#expect(menu.items.contains { $0.tag == 9_415_557 && $0.isSeparatorItem }) #expect(menu.items.contains { $0.tag == 9_415_557 && $0.isSeparatorItem })
} }
@Test func costUsageSubmenuDoesNotUseInjectorDelegate() { @Test func `cost usage submenu does not use injector delegate`() {
let injector = MenuSessionsInjector() let injector = MenuSessionsInjector()
injector.setTestingControlChannelConnected(true) injector.setTestingControlChannelConnected(true)

View File

@@ -2,10 +2,9 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite
struct ModelCatalogLoaderTests { struct ModelCatalogLoaderTests {
@Test @Test
func loadParsesModelsFromTypeScriptAndSorts() async throws { func `load parses models from type script and sorts`() async throws {
let src = """ let src = """
export const MODELS = { export const MODELS = {
openai: { openai: {
@@ -40,7 +39,7 @@ struct ModelCatalogLoaderTests {
} }
@Test @Test
func loadWithNoExportReturnsEmptyChoices() async throws { func `load with no export returns empty choices`() async throws {
let src = "const NOPE = 1;" let src = "const NOPE = 1;"
let tmp = FileManager().temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("models-\(UUID().uuidString).ts") .appendingPathComponent("models-\(UUID().uuidString).ts")

View File

@@ -4,7 +4,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
struct NixModeStableSuiteTests { struct NixModeStableSuiteTests {
@Test func resolvesFromStableSuiteForAppBundles() throws { @Test func `resolves from stable suite for app bundles`() throws {
let suite = try #require(UserDefaults(suiteName: launchdLabel)) let suite = try #require(UserDefaults(suiteName: launchdLabel))
let key = "openclaw.nixMode" let key = "openclaw.nixMode"
let prev = suite.object(forKey: key) let prev = suite.object(forKey: key)
@@ -25,7 +25,7 @@ struct NixModeStableSuiteTests {
#expect(resolved) #expect(resolved)
} }
@Test func ignoresStableSuiteOutsideAppBundles() throws { @Test func `ignores stable suite outside app bundles`() throws {
let suite = try #require(UserDefaults(suiteName: launchdLabel)) let suite = try #require(UserDefaults(suiteName: launchdLabel))
let key = "openclaw.nixMode" let key = "openclaw.nixMode"
let prev = suite.object(forKey: key) let prev = suite.object(forKey: key)

View File

@@ -2,8 +2,8 @@ import Foundation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct NodeManagerPathsTests { struct NodeManagerPathsTests {
@Test func fnmNodeBinsPreferNewestInstalledVersion() throws { @Test func `fnm node bins prefer newest installed version`() throws {
let home = try makeTempDirForTests() let home = try makeTempDirForTests()
let v20Bin = home let v20Bin = home
@@ -18,7 +18,7 @@ import Testing
#expect(bins.contains(v20Bin.deletingLastPathComponent().path)) #expect(bins.contains(v20Bin.deletingLastPathComponent().path))
} }
@Test func ignoresEntriesWithoutNodeExecutable() throws { @Test func `ignores entries without node executable`() throws {
let home = try makeTempDirForTests() let home = try makeTempDirForTests()
let missingNodeBin = home let missingNodeBin = home
.appendingPathComponent(".local/share/fnm/node-versions/v99.0.0/installation/bin") .appendingPathComponent(".local/share/fnm/node-versions/v99.0.0/installation/bin")

View File

@@ -4,7 +4,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct NodePairingApprovalPrompterTests { struct NodePairingApprovalPrompterTests {
@Test func nodePairingApprovalPrompterExercises() async { @Test func `node pairing approval prompter exercises`() async {
await NodePairingApprovalPrompter.exerciseForTesting() await NodePairingApprovalPrompter.exerciseForTesting()
} }
} }

View File

@@ -1,14 +1,14 @@
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite struct NodePairingReconcilePolicyTests { struct NodePairingReconcilePolicyTests {
@Test func policyPollsOnlyWhenActive() { @Test func `policy polls only when active`() {
#expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 0, isPresenting: false) == false) #expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 0, isPresenting: false) == false)
#expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 1, isPresenting: false)) #expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 1, isPresenting: false))
#expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 0, isPresenting: true)) #expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 0, isPresenting: true))
} }
@Test func policyUsesSlowSafetyInterval() { @Test func `policy uses slow safety interval`() {
#expect(NodePairingReconcilePolicy.activeIntervalMs >= 10000) #expect(NodePairingReconcilePolicy.activeIntervalMs >= 10000)
} }
} }

View File

@@ -4,7 +4,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct OnboardingCoverageTests { struct OnboardingCoverageTests {
@Test func exerciseOnboardingPages() { @Test func `exercise onboarding pages`() {
OnboardingView.exerciseForTesting() OnboardingView.exerciseForTesting()
} }
} }

View File

@@ -7,7 +7,7 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct OnboardingViewSmokeTests { struct OnboardingViewSmokeTests {
@Test func onboardingViewBuildsBody() { @Test func `onboarding view builds body`() {
let state = AppState(preview: true) let state = AppState(preview: true)
let view = OnboardingView( let view = OnboardingView(
state: state, state: state,
@@ -16,18 +16,18 @@ struct OnboardingViewSmokeTests {
_ = view.body _ = view.body
} }
@Test func pageOrderOmitsWorkspaceAndIdentitySteps() { @Test func `page order omits workspace and identity steps`() {
let order = OnboardingView.pageOrder(for: .local, showOnboardingChat: false) let order = OnboardingView.pageOrder(for: .local, showOnboardingChat: false)
#expect(!order.contains(7)) #expect(!order.contains(7))
#expect(order.contains(3)) #expect(order.contains(3))
} }
@Test func pageOrderOmitsOnboardingChatWhenIdentityKnown() { @Test func `page order omits onboarding chat when identity known`() {
let order = OnboardingView.pageOrder(for: .local, showOnboardingChat: false) let order = OnboardingView.pageOrder(for: .local, showOnboardingChat: false)
#expect(!order.contains(8)) #expect(!order.contains(8))
} }
@Test func selectRemoteGatewayClearsStaleSshTargetWhenEndpointUnresolved() async { @Test func `select remote gateway clears stale ssh target when endpoint unresolved`() async {
let override = FileManager().temporaryDirectory let override = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-config-\(UUID().uuidString)") .appendingPathComponent("openclaw-config-\(UUID().uuidString)")
.appendingPathComponent("openclaw.json") .appendingPathComponent("openclaw.json")

View File

@@ -8,7 +8,7 @@ private typealias ProtoAnyCodable = OpenClawProtocol.AnyCodable
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct OnboardingWizardStepViewTests { struct OnboardingWizardStepViewTests {
@Test func noteStepBuilds() { @Test func `note step builds`() {
let step = WizardStep( let step = WizardStep(
id: "step-1", id: "step-1",
type: ProtoAnyCodable("note"), type: ProtoAnyCodable("note"),
@@ -23,7 +23,7 @@ struct OnboardingWizardStepViewTests {
_ = view.body _ = view.body
} }
@Test func selectStepBuilds() { @Test func `select step builds`() {
let options: [[String: ProtoAnyCodable]] = [ let options: [[String: ProtoAnyCodable]] = [
["value": ProtoAnyCodable("local"), "label": ProtoAnyCodable("Local"), "hint": ProtoAnyCodable("This Mac")], ["value": ProtoAnyCodable("local"), "label": ProtoAnyCodable("Local"), "hint": ProtoAnyCodable("This Mac")],
["value": ProtoAnyCodable("remote"), "label": ProtoAnyCodable("Remote")], ["value": ProtoAnyCodable("remote"), "label": ProtoAnyCodable("Remote")],

View File

@@ -12,8 +12,8 @@ struct OpenClawConfigFileTests {
} }
@Test @Test
func configPathRespectsEnvOverride() async { func `config path respects env override`() async {
let override = makeConfigOverridePath() let override = self.makeConfigOverridePath()
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) { await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
#expect(OpenClawConfigFile.url().path == override) #expect(OpenClawConfigFile.url().path == override)
@@ -22,8 +22,8 @@ struct OpenClawConfigFileTests {
@MainActor @MainActor
@Test @Test
func remoteGatewayPortParsesAndMatchesHost() async { func `remote gateway port parses and matches host`() async {
let override = makeConfigOverridePath() let override = self.makeConfigOverridePath()
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) { await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
OpenClawConfigFile.saveDict([ OpenClawConfigFile.saveDict([
@@ -42,8 +42,8 @@ struct OpenClawConfigFileTests {
@MainActor @MainActor
@Test @Test
func setRemoteGatewayUrlPreservesScheme() async { func `set remote gateway url preserves scheme`() async {
let override = makeConfigOverridePath() let override = self.makeConfigOverridePath()
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) { await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
OpenClawConfigFile.saveDict([ OpenClawConfigFile.saveDict([
@@ -62,8 +62,8 @@ struct OpenClawConfigFileTests {
@MainActor @MainActor
@Test @Test
func clearRemoteGatewayUrlRemovesOnlyUrlField() async { func `clear remote gateway url removes only url field`() async {
let override = makeConfigOverridePath() let override = self.makeConfigOverridePath()
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) { await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
OpenClawConfigFile.saveDict([ OpenClawConfigFile.saveDict([
@@ -83,7 +83,7 @@ struct OpenClawConfigFileTests {
} }
@Test @Test
func stateDirOverrideSetsConfigPath() async { func `state dir override sets config path`() async {
let dir = FileManager().temporaryDirectory let dir = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-state-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-state-\(UUID().uuidString)", isDirectory: true)
.path .path
@@ -99,7 +99,7 @@ struct OpenClawConfigFileTests {
@MainActor @MainActor
@Test @Test
func saveDictAppendsConfigAuditLog() async throws { func `save dict appends config audit log`() async throws {
let stateDir = FileManager().temporaryDirectory let stateDir = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-state-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("openclaw-state-\(UUID().uuidString)", isDirectory: true)
let configPath = stateDir.appendingPathComponent("openclaw.json") let configPath = stateDir.appendingPathComponent("openclaw.json")

View File

@@ -2,16 +2,15 @@ import CoreLocation
import Testing import Testing
@testable import OpenClaw @testable import OpenClaw
@Suite("PermissionManager Location")
struct PermissionManagerLocationTests { struct PermissionManagerLocationTests {
@Test("authorizedAlways counts for both modes") @Test
func authorizedAlwaysCountsForBothModes() { func `authorizedAlways counts for both modes`() {
#expect(PermissionManager.isLocationAuthorized(status: .authorizedAlways, requireAlways: false)) #expect(PermissionManager.isLocationAuthorized(status: .authorizedAlways, requireAlways: false))
#expect(PermissionManager.isLocationAuthorized(status: .authorizedAlways, requireAlways: true)) #expect(PermissionManager.isLocationAuthorized(status: .authorizedAlways, requireAlways: true))
} }
@Test("other statuses not authorized") @Test
func otherStatusesNotAuthorized() { func `other statuses not authorized`() {
#expect(!PermissionManager.isLocationAuthorized(status: .notDetermined, requireAlways: false)) #expect(!PermissionManager.isLocationAuthorized(status: .notDetermined, requireAlways: false))
#expect(!PermissionManager.isLocationAuthorized(status: .denied, requireAlways: false)) #expect(!PermissionManager.isLocationAuthorized(status: .denied, requireAlways: false))
#expect(!PermissionManager.isLocationAuthorized(status: .restricted, requireAlways: false)) #expect(!PermissionManager.isLocationAuthorized(status: .restricted, requireAlways: false))

View File

@@ -6,31 +6,31 @@ import Testing
@Suite(.serialized) @Suite(.serialized)
@MainActor @MainActor
struct PermissionManagerTests { struct PermissionManagerTests {
@Test func voiceWakePermissionHelpersMatchStatus() async { @Test func `voice wake permission helpers match status`() async {
let direct = PermissionManager.voiceWakePermissionsGranted() let direct = PermissionManager.voiceWakePermissionsGranted()
let ensured = await PermissionManager.ensureVoiceWakePermissions(interactive: false) let ensured = await PermissionManager.ensureVoiceWakePermissions(interactive: false)
#expect(ensured == direct) #expect(ensured == direct)
} }
@Test func statusCanQueryNonInteractiveCaps() async { @Test func `status can query non interactive caps`() async {
let caps: [Capability] = [.microphone, .speechRecognition, .screenRecording] let caps: [Capability] = [.microphone, .speechRecognition, .screenRecording]
let status = await PermissionManager.status(caps) let status = await PermissionManager.status(caps)
#expect(status.keys.count == caps.count) #expect(status.keys.count == caps.count)
} }
@Test func ensureNonInteractiveDoesNotThrow() async { @Test func `ensure non interactive does not throw`() async {
let caps: [Capability] = [.microphone, .speechRecognition, .screenRecording] let caps: [Capability] = [.microphone, .speechRecognition, .screenRecording]
let ensured = await PermissionManager.ensure(caps, interactive: false) let ensured = await PermissionManager.ensure(caps, interactive: false)
#expect(ensured.keys.count == caps.count) #expect(ensured.keys.count == caps.count)
} }
@Test func locationStatusMatchesAuthorizationAlways() async { @Test func `location status matches authorization always`() async {
let status = CLLocationManager().authorizationStatus let status = CLLocationManager().authorizationStatus
let results = await PermissionManager.status([.location]) let results = await PermissionManager.status([.location])
#expect(results[.location] == (status == .authorizedAlways)) #expect(results[.location] == (status == .authorizedAlways))
} }
@Test func ensureLocationNonInteractiveMatchesAuthorizationAlways() async { @Test func `ensure location non interactive matches authorization always`() async {
let status = CLLocationManager().authorizationStatus let status = CLLocationManager().authorizationStatus
let ensured = await PermissionManager.ensure([.location], interactive: false) let ensured = await PermissionManager.ensure([.location], interactive: false)
#expect(ensured[.location] == (status == .authorizedAlways)) #expect(ensured[.location] == (status == .authorizedAlways))

View File

@@ -1,6 +1,6 @@
import Testing import Testing
@Suite struct PlaceholderTests { struct PlaceholderTests {
@Test func placeholder() { @Test func placeholder() {
#expect(true) #expect(true)
} }

Some files were not shown because too many files have changed in this diff Show More