mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(macos): clean swiftformat pass and sendable warning
This commit is contained in:
@@ -6,14 +6,14 @@ import OpenClawKit
|
||||
import OSLog
|
||||
|
||||
actor CameraCaptureService {
|
||||
struct CameraDeviceInfo: Encodable, Sendable {
|
||||
struct CameraDeviceInfo: Encodable {
|
||||
let id: String
|
||||
let name: String
|
||||
let position: String
|
||||
let deviceType: String
|
||||
}
|
||||
|
||||
enum CameraError: LocalizedError, Sendable {
|
||||
enum CameraError: LocalizedError {
|
||||
case cameraUnavailable
|
||||
case microphoneUnavailable
|
||||
case permissionDenied(kind: String)
|
||||
|
||||
@@ -2,7 +2,7 @@ import Foundation
|
||||
import OpenClawProtocol
|
||||
|
||||
enum ConfigStore {
|
||||
struct Overrides: Sendable {
|
||||
struct Overrides {
|
||||
var isRemoteMode: (@Sendable () async -> Bool)?
|
||||
var loadLocal: (@MainActor @Sendable () -> [String: Any])?
|
||||
var saveLocal: (@MainActor @Sendable ([String: Any]) -> Void)?
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Foundation
|
||||
|
||||
enum EffectiveConnectionModeSource: Sendable, Equatable {
|
||||
enum EffectiveConnectionModeSource: Equatable {
|
||||
case configMode
|
||||
case configRemoteURL
|
||||
case userDefaults
|
||||
case onboarding
|
||||
}
|
||||
|
||||
struct EffectiveConnectionMode: Sendable, Equatable {
|
||||
struct EffectiveConnectionMode: Equatable {
|
||||
let mode: AppState.ConnectionMode
|
||||
let source: EffectiveConnectionModeSource
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ struct ControlHeartbeatEvent: Codable {
|
||||
let reason: String?
|
||||
}
|
||||
|
||||
struct ControlAgentEvent: Codable, Sendable, Identifiable {
|
||||
struct ControlAgentEvent: Codable, Identifiable {
|
||||
var id: String {
|
||||
"\(self.runId)-\(self.seq)"
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ struct CronJob: Identifiable, Codable, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
struct CronEvent: Codable, Sendable {
|
||||
struct CronEvent: Codable {
|
||||
let jobId: String
|
||||
let action: String
|
||||
let runAtMs: Int?
|
||||
@@ -237,7 +237,7 @@ struct CronEvent: Codable, Sendable {
|
||||
let nextRunAtMs: Int?
|
||||
}
|
||||
|
||||
struct CronRunLogEntry: Codable, Identifiable, Sendable {
|
||||
struct CronRunLogEntry: Codable, Identifiable {
|
||||
var id: String {
|
||||
"\(self.jobId)-\(self.ts)"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
struct DevicePresentation: Sendable {
|
||||
struct DevicePresentation {
|
||||
let title: String
|
||||
let symbol: String?
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ actor DiagnosticsFileLog {
|
||||
private let maxBytes: Int64 = 5 * 1024 * 1024
|
||||
private let maxBackups = 5
|
||||
|
||||
struct Record: Codable, Sendable {
|
||||
struct Record: Codable {
|
||||
let ts: String
|
||||
let pid: Int32
|
||||
let category: String
|
||||
|
||||
@@ -84,13 +84,13 @@ enum ExecAsk: String, CaseIterable, Codable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
enum ExecApprovalDecision: String, Codable, Sendable {
|
||||
enum ExecApprovalDecision: String, Codable {
|
||||
case allowOnce = "allow-once"
|
||||
case allowAlways = "allow-always"
|
||||
case deny
|
||||
}
|
||||
|
||||
enum ExecAllowlistPatternValidationReason: String, Codable, Sendable, Equatable {
|
||||
enum ExecAllowlistPatternValidationReason: String, Codable, Equatable {
|
||||
case empty
|
||||
case missingPathComponent
|
||||
|
||||
@@ -104,12 +104,12 @@ enum ExecAllowlistPatternValidationReason: String, Codable, Sendable, Equatable
|
||||
}
|
||||
}
|
||||
|
||||
enum ExecAllowlistPatternValidation: Sendable, Equatable {
|
||||
enum ExecAllowlistPatternValidation: Equatable {
|
||||
case valid(String)
|
||||
case invalid(ExecAllowlistPatternValidationReason)
|
||||
}
|
||||
|
||||
struct ExecAllowlistRejectedEntry: Sendable, Equatable {
|
||||
struct ExecAllowlistRejectedEntry: Equatable {
|
||||
let id: UUID
|
||||
let pattern: String
|
||||
let reason: ExecAllowlistPatternValidationReason
|
||||
@@ -753,7 +753,7 @@ enum ExecApprovalHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
struct ExecEventPayload: Codable, Sendable {
|
||||
struct ExecEventPayload: Codable {
|
||||
var sessionKey: String
|
||||
var runId: String
|
||||
var host: String
|
||||
|
||||
@@ -11,7 +11,7 @@ final class ExecApprovalsGatewayPrompter {
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "exec-approvals.gateway")
|
||||
private var task: Task<Void, Never>?
|
||||
|
||||
struct GatewayApprovalRequest: Codable, Sendable {
|
||||
struct GatewayApprovalRequest: Codable {
|
||||
var id: String
|
||||
var request: ExecApprovalPromptRequest
|
||||
var createdAtMs: Int
|
||||
|
||||
@@ -5,7 +5,7 @@ import Foundation
|
||||
import OpenClawKit
|
||||
import OSLog
|
||||
|
||||
struct ExecApprovalPromptRequest: Codable, Sendable {
|
||||
struct ExecApprovalPromptRequest: Codable {
|
||||
var command: String
|
||||
var cwd: String?
|
||||
var host: String?
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
struct ExecCommandResolution: Sendable {
|
||||
struct ExecCommandResolution {
|
||||
let rawExecutable: String
|
||||
let resolvedPath: String?
|
||||
let executableName: String
|
||||
|
||||
@@ -6,7 +6,7 @@ import OSLog
|
||||
|
||||
private let gatewayConnectionLogger = Logger(subsystem: "ai.openclaw", category: "gateway.connection")
|
||||
|
||||
enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
|
||||
enum GatewayAgentChannel: String, Codable, CaseIterable {
|
||||
case last
|
||||
case whatsapp
|
||||
case telegram
|
||||
@@ -33,7 +33,7 @@ enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
struct GatewayAgentInvocation: Sendable {
|
||||
struct GatewayAgentInvocation {
|
||||
var message: String
|
||||
var sessionKey: String = "main"
|
||||
var thinking: String?
|
||||
@@ -53,7 +53,7 @@ actor GatewayConnection {
|
||||
|
||||
typealias Config = (url: URL, token: String?, password: String?)
|
||||
|
||||
enum Method: String, Sendable {
|
||||
enum Method: String {
|
||||
case agent
|
||||
case status
|
||||
case setHeartbeats = "set-heartbeats"
|
||||
@@ -428,9 +428,9 @@ actor GatewayConnection {
|
||||
// MARK: - Typed gateway API
|
||||
|
||||
extension GatewayConnection {
|
||||
struct ConfigGetSnapshot: Decodable, Sendable {
|
||||
struct SnapshotConfig: Decodable, Sendable {
|
||||
struct Session: Decodable, Sendable {
|
||||
struct ConfigGetSnapshot: Decodable {
|
||||
struct SnapshotConfig: Decodable {
|
||||
struct Session: Decodable {
|
||||
let mainKey: String?
|
||||
let scope: String?
|
||||
}
|
||||
@@ -729,7 +729,7 @@ extension GatewayConnection {
|
||||
|
||||
// MARK: - Cron
|
||||
|
||||
struct CronSchedulerStatus: Decodable, Sendable {
|
||||
struct CronSchedulerStatus: Decodable {
|
||||
let enabled: Bool
|
||||
let storePath: String
|
||||
let jobs: Int
|
||||
|
||||
@@ -2,7 +2,7 @@ import ConcurrencyExtras
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
enum GatewayEndpointState: Sendable, Equatable {
|
||||
enum GatewayEndpointState: Equatable {
|
||||
case ready(mode: AppState.ConnectionMode, url: URL, token: String?, password: String?)
|
||||
case connecting(mode: AppState.ConnectionMode, detail: 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 staticLogger = Logger(subsystem: "ai.openclaw", category: "gateway-endpoint")
|
||||
private enum EnvOverrideWarningKind: Sendable {
|
||||
private enum EnvOverrideWarningKind {
|
||||
case token
|
||||
case password
|
||||
}
|
||||
|
||||
private static let envOverrideWarnings = LockIsolated((token: false, password: false))
|
||||
|
||||
struct Deps: Sendable {
|
||||
struct Deps {
|
||||
let mode: @Sendable () async -> AppState.ConnectionMode
|
||||
let token: @Sendable () -> String?
|
||||
let password: @Sendable () -> String?
|
||||
|
||||
@@ -3,7 +3,7 @@ import OpenClawIPC
|
||||
import OSLog
|
||||
|
||||
/// Lightweight SemVer helper (major.minor.patch only) for gateway compatibility checks.
|
||||
struct Semver: Comparable, CustomStringConvertible, Sendable {
|
||||
struct Semver: Comparable, CustomStringConvertible {
|
||||
let major: Int
|
||||
let minor: Int
|
||||
let patch: Int
|
||||
|
||||
@@ -3,14 +3,14 @@ import Network
|
||||
import Observation
|
||||
import SwiftUI
|
||||
|
||||
struct HealthSnapshot: Codable, Sendable {
|
||||
struct ChannelSummary: Codable, Sendable {
|
||||
struct Probe: Codable, Sendable {
|
||||
struct Bot: Codable, Sendable {
|
||||
struct HealthSnapshot: Codable {
|
||||
struct ChannelSummary: Codable {
|
||||
struct Probe: Codable {
|
||||
struct Bot: Codable {
|
||||
let username: String?
|
||||
}
|
||||
|
||||
struct Webhook: Codable, Sendable {
|
||||
struct Webhook: Codable {
|
||||
let url: String?
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ struct HealthSnapshot: Codable, Sendable {
|
||||
let lastProbeAt: Double?
|
||||
}
|
||||
|
||||
struct SessionInfo: Codable, Sendable {
|
||||
struct SessionInfo: Codable {
|
||||
let key: String
|
||||
let updatedAt: Double?
|
||||
let age: Double?
|
||||
}
|
||||
|
||||
struct Sessions: Codable, Sendable {
|
||||
struct Sessions: Codable {
|
||||
let path: String
|
||||
let count: Int
|
||||
let recent: [SessionInfo]
|
||||
|
||||
@@ -22,7 +22,7 @@ enum HostEnvSecurityPolicy {
|
||||
"PS4",
|
||||
"GCONV_PATH",
|
||||
"IFS",
|
||||
"SSLKEYLOGFILE"
|
||||
"SSLKEYLOGFILE",
|
||||
]
|
||||
|
||||
static let blockedOverrideKeys: Set<String> = [
|
||||
@@ -50,17 +50,17 @@ enum HostEnvSecurityPolicy {
|
||||
"OPENSSL_ENGINES",
|
||||
"PYTHONSTARTUP",
|
||||
"WGETRC",
|
||||
"CURL_HOME"
|
||||
"CURL_HOME",
|
||||
]
|
||||
|
||||
static let blockedOverridePrefixes: [String] = [
|
||||
"GIT_CONFIG_",
|
||||
"NPM_CONFIG_"
|
||||
"NPM_CONFIG_",
|
||||
]
|
||||
|
||||
static let blockedPrefixes: [String] = [
|
||||
"DYLD_",
|
||||
"LD_",
|
||||
"BASH_FUNC_"
|
||||
"BASH_FUNC_",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
enum Launchctl {
|
||||
struct Result: Sendable {
|
||||
struct Result {
|
||||
let status: Int32
|
||||
let output: String
|
||||
}
|
||||
@@ -26,7 +26,7 @@ enum Launchctl {
|
||||
}
|
||||
}
|
||||
|
||||
struct LaunchAgentPlistSnapshot: Equatable, Sendable {
|
||||
struct LaunchAgentPlistSnapshot: Equatable {
|
||||
let programArguments: [String]
|
||||
let environment: [String: String]
|
||||
let stdoutPath: String?
|
||||
|
||||
@@ -122,7 +122,7 @@ actor MacNodeBrowserProxy {
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
if !queryItems.isEmpty {
|
||||
@@ -172,7 +172,7 @@ actor MacNodeBrowserProxy {
|
||||
}
|
||||
if let text = String(data: data, encoding: .utf8)?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!text.isEmpty
|
||||
!text.isEmpty
|
||||
{
|
||||
return text
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Foundation
|
||||
|
||||
enum MacNodeScreenCommand: String, Codable, Sendable {
|
||||
enum MacNodeScreenCommand: String, Codable {
|
||||
case record = "screen.record"
|
||||
}
|
||||
|
||||
struct MacNodeScreenRecordParams: Codable, Sendable, Equatable {
|
||||
struct MacNodeScreenRecordParams: Codable, Equatable {
|
||||
var screenIndex: Int?
|
||||
var durationMs: Int?
|
||||
var fps: Double?
|
||||
|
||||
@@ -87,7 +87,7 @@ enum OverlayPanelFactory {
|
||||
offsetX: CGFloat = 6,
|
||||
offsetY: CGFloat = 6,
|
||||
duration: TimeInterval = 0.16,
|
||||
completion: @escaping () -> Void)
|
||||
completion: @escaping @MainActor @Sendable () -> Void)
|
||||
{
|
||||
let target = window.frame.offsetBy(dx: offsetX, dy: offsetY)
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
@@ -96,7 +96,7 @@ enum OverlayPanelFactory {
|
||||
window.animator().setFrame(target, display: true)
|
||||
window.animator().alphaValue = 0
|
||||
} completionHandler: {
|
||||
completion()
|
||||
Task { @MainActor in completion() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,10 +109,8 @@ enum OverlayPanelFactory {
|
||||
onHidden: @escaping @MainActor () -> Void)
|
||||
{
|
||||
self.animateDismiss(window: window, offsetX: offsetX, offsetY: offsetY, duration: duration) {
|
||||
Task { @MainActor in
|
||||
window.orderOut(nil)
|
||||
onHidden()
|
||||
}
|
||||
window.orderOut(nil)
|
||||
onHidden()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ final class PeekabooBridgeHostCoordinator {
|
||||
private func startIfNeeded() async {
|
||||
guard self.host == nil else { return }
|
||||
|
||||
var allowlistedTeamIDs: Set<String> = ["Y5PE65HELJ"]
|
||||
var allowlistedTeamIDs: Set = ["Y5PE65HELJ"]
|
||||
if let teamID = Self.currentTeamID() {
|
||||
allowlistedTeamIDs.insert(teamID)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ actor PortGuardian {
|
||||
let timestamp: TimeInterval
|
||||
}
|
||||
|
||||
struct Descriptor: Sendable {
|
||||
struct Descriptor {
|
||||
let pid: Int32
|
||||
let command: String
|
||||
let executablePath: String?
|
||||
|
||||
@@ -4,13 +4,13 @@ import OpenClawProtocol
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
|
||||
struct SessionPreviewItem: Identifiable, Sendable {
|
||||
struct SessionPreviewItem: Identifiable {
|
||||
let id: String
|
||||
let role: PreviewRole
|
||||
let text: String
|
||||
}
|
||||
|
||||
enum PreviewRole: String, Sendable {
|
||||
enum PreviewRole: String {
|
||||
case user
|
||||
case assistant
|
||||
case tool
|
||||
@@ -114,7 +114,7 @@ extension SessionPreviewCache {
|
||||
}
|
||||
#endif
|
||||
|
||||
struct SessionMenuPreviewSnapshot: Sendable {
|
||||
struct SessionMenuPreviewSnapshot {
|
||||
let items: [SessionPreviewItem]
|
||||
let status: SessionMenuPreviewView.LoadStatus
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ final class TalkAudioPlayer: NSObject, @preconcurrency AVAudioPlayerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
struct TalkPlaybackResult: Sendable {
|
||||
struct TalkPlaybackResult {
|
||||
let finished: Bool
|
||||
let interruptedAt: Double?
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import AppKit
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
enum VoiceWakeChime: Codable, Equatable, Sendable {
|
||||
enum VoiceWakeChime: Codable, Equatable {
|
||||
case none
|
||||
case system(name: String)
|
||||
case custom(displayName: String, bookmark: Data)
|
||||
|
||||
@@ -32,7 +32,7 @@ enum VoiceWakeForwarder {
|
||||
}
|
||||
}
|
||||
|
||||
struct ForwardOptions: Sendable {
|
||||
struct ForwardOptions {
|
||||
var sessionKey: String = "main"
|
||||
var thinking: String = "low"
|
||||
var deliver: Bool = true
|
||||
|
||||
@@ -16,7 +16,7 @@ private enum WebChatSwiftUILayout {
|
||||
static let anchorPadding: CGFloat = 8
|
||||
}
|
||||
|
||||
struct MacGatewayChatTransport: OpenClawChatTransport, Sendable {
|
||||
struct MacGatewayChatTransport: OpenClawChatTransport {
|
||||
func requestHistory(sessionKey: String) async throws -> OpenClawChatHistoryPayload {
|
||||
try await GatewayConnection.shared.chatHistory(sessionKey: sessionKey)
|
||||
}
|
||||
|
||||
@@ -374,9 +374,9 @@ public final class GatewayDiscoveryModel {
|
||||
if let host = gateway.serviceHost?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.lowercased(),
|
||||
!host.isEmpty,
|
||||
let port = gateway.servicePort,
|
||||
port > 0
|
||||
!host.isEmpty,
|
||||
let port = gateway.servicePort,
|
||||
port > 0
|
||||
{
|
||||
return "endpoint|\(host):\(port)"
|
||||
}
|
||||
@@ -674,7 +674,7 @@ public final class GatewayDiscoveryModel {
|
||||
}
|
||||
}
|
||||
|
||||
struct ResolvedGatewayService: Equatable, Sendable {
|
||||
struct ResolvedGatewayService: Equatable {
|
||||
var txt: [String: String]
|
||||
var host: String?
|
||||
var port: Int?
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
import OpenClawKit
|
||||
|
||||
struct TailscaleServeGatewayBeacon: Sendable, Equatable {
|
||||
struct TailscaleServeGatewayBeacon: Equatable {
|
||||
var displayName: String
|
||||
var tailnetDns: String
|
||||
var host: String
|
||||
@@ -13,7 +13,7 @@ enum TailscaleServeGatewayDiscovery {
|
||||
private static let probeConcurrency = 6
|
||||
private static let defaultProbeTimeoutSeconds: TimeInterval = 1.6
|
||||
|
||||
struct DiscoveryContext: Sendable {
|
||||
struct DiscoveryContext {
|
||||
var tailscaleStatus: @Sendable () async -> String?
|
||||
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 displayName: String
|
||||
}
|
||||
|
||||
private static func collectCandidates(status: TailscaleStatus) -> [Candidate] {
|
||||
let selfDns = normalizeDnsName(status.selfNode?.dnsName)
|
||||
let selfDns = self.normalizeDnsName(status.selfNode?.dnsName)
|
||||
var out: [Candidate] = []
|
||||
var seen = Set<String>()
|
||||
|
||||
@@ -112,7 +112,7 @@ enum TailscaleServeGatewayDiscovery {
|
||||
|
||||
out.append(Candidate(
|
||||
dnsName: dnsName,
|
||||
displayName: displayName(hostName: node.hostName, dnsName: dnsName)))
|
||||
displayName: self.displayName(hostName: node.hostName, dnsName: dnsName)))
|
||||
|
||||
if out.count >= self.maxCandidates {
|
||||
break
|
||||
@@ -257,7 +257,7 @@ enum TailscaleServeGatewayDiscovery {
|
||||
operation: {
|
||||
while true {
|
||||
let message = try await task.receive()
|
||||
if isConnectChallenge(message: message) {
|
||||
if self.isConnectChallenge(message: message) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
import OpenClawKit
|
||||
|
||||
struct WideAreaGatewayBeacon: Sendable, Equatable {
|
||||
struct WideAreaGatewayBeacon: Equatable {
|
||||
var instanceName: String
|
||||
var displayName: String
|
||||
var host: String
|
||||
@@ -19,7 +19,7 @@ enum WideAreaGatewayDiscovery {
|
||||
private static let defaultTimeoutSeconds: TimeInterval = 0.2
|
||||
private static let nameserverProbeConcurrency = 6
|
||||
|
||||
struct DiscoveryContext: Sendable {
|
||||
struct DiscoveryContext {
|
||||
var tailscaleStatus: @Sendable () -> String?
|
||||
var dig: @Sendable (_ args: [String], _ timeout: TimeInterval) -> String?
|
||||
|
||||
|
||||
@@ -3,11 +3,10 @@ import OpenClawProtocol
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite
|
||||
@MainActor
|
||||
struct AgentEventStoreTests {
|
||||
@Test
|
||||
func appendAndClear() {
|
||||
func `append and clear`() {
|
||||
let store = AgentEventStore()
|
||||
#expect(store.events.isEmpty)
|
||||
|
||||
@@ -25,7 +24,7 @@ struct AgentEventStoreTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func trimsToMaxEvents() {
|
||||
func `trims to max events`() {
|
||||
let store = AgentEventStore()
|
||||
for i in 1...401 {
|
||||
store.append(ControlAgentEvent(
|
||||
|
||||
@@ -2,10 +2,9 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite
|
||||
struct AgentWorkspaceTests {
|
||||
@Test
|
||||
func displayPathUsesTildeForHome() {
|
||||
func `display path uses tilde for home`() {
|
||||
let home = FileManager().homeDirectoryForCurrentUser
|
||||
#expect(AgentWorkspace.displayPath(for: home) == "~")
|
||||
|
||||
@@ -14,20 +13,20 @@ struct AgentWorkspaceTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func resolveWorkspaceURLExpandsTilde() {
|
||||
func `resolve workspace URL expands tilde`() {
|
||||
let url = AgentWorkspace.resolveWorkspaceURL(from: "~/tmp")
|
||||
#expect(url.path.hasSuffix("/tmp"))
|
||||
}
|
||||
|
||||
@Test
|
||||
func agentsURLAppendsFilename() {
|
||||
func `agents URL appends filename`() {
|
||||
let root = URL(fileURLWithPath: "/tmp/ws", isDirectory: true)
|
||||
let url = AgentWorkspace.agentsURL(workspaceURL: root)
|
||||
#expect(url.lastPathComponent == AgentWorkspace.agentsFilename)
|
||||
}
|
||||
|
||||
@Test
|
||||
func bootstrapCreatesAgentsFileWhenMissing() throws {
|
||||
func `bootstrap creates agents file when missing`() throws {
|
||||
let tmp = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: tmp) }
|
||||
@@ -50,7 +49,7 @@ struct AgentWorkspaceTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func bootstrapSafetyRejectsNonEmptyFolderWithoutAgents() throws {
|
||||
func `bootstrap safety rejects non empty folder without agents`() throws {
|
||||
let tmp = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: tmp) }
|
||||
@@ -63,7 +62,7 @@ struct AgentWorkspaceTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func bootstrapSafetyAllowsExistingAgentsFile() throws {
|
||||
func `bootstrap safety allows existing agents file`() throws {
|
||||
let tmp = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: tmp) }
|
||||
@@ -76,7 +75,7 @@ struct AgentWorkspaceTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func bootstrapSkipsBootstrapFileWhenWorkspaceHasContent() throws {
|
||||
func `bootstrap skips bootstrap file when workspace has content`() throws {
|
||||
let tmp = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: tmp) }
|
||||
@@ -91,7 +90,7 @@ struct AgentWorkspaceTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func needsBootstrapFalseWhenIdentityAlreadySet() throws {
|
||||
func `needs bootstrap false when identity already set`() throws {
|
||||
let tmp = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-ws-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: tmp) }
|
||||
|
||||
@@ -3,8 +3,8 @@ import OpenClawProtocol
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct AnyCodableEncodingTests {
|
||||
@Test func encodesSwiftArrayAndDictionaryValues() throws {
|
||||
struct AnyCodableEncodingTests {
|
||||
@Test func `encodes swift array and dictionary values`() throws {
|
||||
let payload: [String: Any] = [
|
||||
"tags": ["node", "ios"],
|
||||
"meta": ["count": 2],
|
||||
@@ -19,7 +19,7 @@ import Testing
|
||||
#expect(obj["null"] is NSNull)
|
||||
}
|
||||
|
||||
@Test func protocolAnyCodableEncodesPrimitiveArrays() throws {
|
||||
@Test func `protocol any codable encodes primitive arrays`() throws {
|
||||
let payload: [String: Any] = [
|
||||
"items": [1, "two", NSNull(), ["ok": true]],
|
||||
]
|
||||
|
||||
@@ -2,15 +2,15 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct AudioInputDeviceObserverTests {
|
||||
@Test func hasUsableDefaultInputDeviceReturnsBool() {
|
||||
struct AudioInputDeviceObserverTests {
|
||||
@Test func `has usable default input device returns bool`() {
|
||||
// Smoke test: verifies the composition logic runs without crashing.
|
||||
// Actual result depends on whether the host has an audio input device.
|
||||
let result = AudioInputDeviceObserver.hasUsableDefaultInputDevice()
|
||||
_ = 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 a default UID exists, the result must match alive-set membership.
|
||||
let uid = AudioInputDeviceObserver.defaultInputDeviceUID()
|
||||
|
||||
@@ -5,7 +5,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct CLIInstallerTests {
|
||||
@Test func installedLocationFindsExecutable() throws {
|
||||
@Test func `installed location finds executable`() throws {
|
||||
let fm = FileManager()
|
||||
let root = fm.temporaryDirectory.appendingPathComponent(
|
||||
"openclaw-cli-installer-\(UUID().uuidString)")
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct CameraCaptureServiceTests {
|
||||
@Test func normalizeSnapDefaults() {
|
||||
struct CameraCaptureServiceTests {
|
||||
@Test func `normalize snap defaults`() {
|
||||
let res = CameraCaptureService.normalizeSnap(maxWidth: nil, quality: nil)
|
||||
#expect(res.maxWidth == 1600)
|
||||
#expect(res.quality == 0.9)
|
||||
}
|
||||
|
||||
@Test func normalizeSnapClampsValues() {
|
||||
@Test func `normalize snap clamps values`() {
|
||||
let low = CameraCaptureService.normalizeSnap(maxWidth: -1, quality: -10)
|
||||
#expect(low.maxWidth == 1600)
|
||||
#expect(low.quality == 0.05)
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import OpenClawIPC
|
||||
import Testing
|
||||
|
||||
@Suite struct CameraIPCTests {
|
||||
@Test func cameraSnapCodableRoundtrip() throws {
|
||||
struct CameraIPCTests {
|
||||
@Test func `camera snap codable roundtrip`() throws {
|
||||
let req: Request = .cameraSnap(
|
||||
facing: .front,
|
||||
maxWidth: 640,
|
||||
@@ -24,7 +24,7 @@ import Testing
|
||||
}
|
||||
}
|
||||
|
||||
@Test func cameraClipCodableRoundtrip() throws {
|
||||
@Test func `camera clip codable roundtrip`() throws {
|
||||
let req: Request = .cameraClip(
|
||||
facing: .back,
|
||||
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 = """
|
||||
{"type":"cameraClip","durationMs":1234}
|
||||
"""
|
||||
|
||||
@@ -11,7 +11,7 @@ import Testing
|
||||
return dir
|
||||
}
|
||||
|
||||
@Test func detectsInPlaceFileWrites() async throws {
|
||||
@Test func `detects in place file writes`() async throws {
|
||||
let dir = try self.makeTempDir()
|
||||
defer { try? FileManager().removeItem(at: dir) }
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import OpenClawIPC
|
||||
import Testing
|
||||
|
||||
@Suite struct CanvasIPCTests {
|
||||
@Test func canvasPresentCodableRoundtrip() throws {
|
||||
struct CanvasIPCTests {
|
||||
@Test func `canvas present codable roundtrip`() throws {
|
||||
let placement = CanvasPlacement(x: 10, y: 20, width: 640, height: 480)
|
||||
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 = """
|
||||
{"type":"canvasPresent","session":"s","path":"/"}
|
||||
"""
|
||||
|
||||
@@ -7,7 +7,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct CanvasWindowSmokeTests {
|
||||
@Test func panelControllerShowsAndHides() async throws {
|
||||
@Test func `panel controller shows and hides`() async throws {
|
||||
let root = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-canvas-test-\(UUID().uuidString)")
|
||||
try FileManager().createDirectory(at: root, withIntermediateDirectories: true)
|
||||
@@ -30,7 +30,7 @@ struct CanvasWindowSmokeTests {
|
||||
controller.close()
|
||||
}
|
||||
|
||||
@Test func windowControllerShowsAndCloses() throws {
|
||||
@Test func `window controller shows and closes`() throws {
|
||||
let root = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-canvas-test-\(UUID().uuidString)")
|
||||
try FileManager().createDirectory(at: root, withIntermediateDirectories: true)
|
||||
|
||||
@@ -41,7 +41,7 @@ private func makeChannelsStore(
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct ChannelsSettingsSmokeTests {
|
||||
@Test func channelsSettingsBuildsBodyWithSnapshot() {
|
||||
@Test func `channels settings builds body with snapshot`() {
|
||||
let store = makeChannelsStore(
|
||||
channels: [
|
||||
"whatsapp": SnapshotAnyCodable([
|
||||
@@ -108,7 +108,7 @@ struct ChannelsSettingsSmokeTests {
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func channelsSettingsBuildsBodyWithoutSnapshot() {
|
||||
@Test func `channels settings builds body without snapshot`() {
|
||||
let store = makeChannelsStore(
|
||||
channels: [
|
||||
"whatsapp": SnapshotAnyCodable([
|
||||
|
||||
@@ -23,7 +23,7 @@ import Testing
|
||||
return (tmp, pnpmPath)
|
||||
}
|
||||
|
||||
@Test func prefersOpenClawBinary() throws {
|
||||
@Test func `prefers open claw binary`() throws {
|
||||
let defaults = self.makeLocalDefaults()
|
||||
|
||||
let tmp = try makeTempDirForTests()
|
||||
@@ -36,7 +36,7 @@ import Testing
|
||||
#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 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 tmp = try makeTempDirForTests()
|
||||
@@ -84,7 +84,7 @@ import Testing
|
||||
#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 tmp = try makeTempDirForTests()
|
||||
@@ -103,7 +103,7 @@ import Testing
|
||||
#expect(cmd.prefix(2).elementsEqual([openclawPath.path, "gateway"]))
|
||||
}
|
||||
|
||||
@Test func fallsBackToPnpm() throws {
|
||||
@Test func `falls back to pnpm`() throws {
|
||||
let defaults = self.makeLocalDefaults()
|
||||
let (tmp, pnpmPath) = try self.makeProjectRootWithPnpm()
|
||||
|
||||
@@ -116,7 +116,7 @@ import Testing
|
||||
#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 (tmp, pnpmPath) = try self.makeProjectRootWithPnpm()
|
||||
|
||||
@@ -131,7 +131,7 @@ import Testing
|
||||
#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()
|
||||
CommandResolver.setProjectRoot(tmp.path)
|
||||
|
||||
@@ -139,7 +139,7 @@ import Testing
|
||||
#expect(first == tmp.appendingPathComponent("node_modules/.bin").path)
|
||||
}
|
||||
|
||||
@Test func buildsSSHCommandForRemoteMode() {
|
||||
@Test func `builds SSH command for remote mode`() {
|
||||
let defaults = self.makeDefaults()
|
||||
defaults.set(AppState.ConnectionMode.remote.rawValue, forKey: connectionModeKey)
|
||||
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("host:-oProxyCommand=calc") == nil)
|
||||
#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()
|
||||
defaults.set(AppState.ConnectionMode.remote.rawValue, forKey: connectionModeKey)
|
||||
defaults.set("openclaw@example.com:2222", forKey: remoteTargetKey)
|
||||
|
||||
@@ -4,7 +4,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct ConfigStoreTests {
|
||||
@Test func loadUsesRemoteInRemoteMode() async {
|
||||
@Test func `load uses remote in remote mode`() async {
|
||||
var localHit = false
|
||||
var remoteHit = false
|
||||
await ConfigStore._testSetOverrides(.init(
|
||||
@@ -20,7 +20,7 @@ struct ConfigStoreTests {
|
||||
#expect(result["remote"] as? Bool == true)
|
||||
}
|
||||
|
||||
@Test func loadUsesLocalInLocalMode() async {
|
||||
@Test func `load uses local in local mode`() async {
|
||||
var localHit = false
|
||||
var remoteHit = false
|
||||
await ConfigStore._testSetOverrides(.init(
|
||||
@@ -36,7 +36,7 @@ struct ConfigStoreTests {
|
||||
#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 remoteHit = false
|
||||
await ConfigStore._testSetOverrides(.init(
|
||||
@@ -51,7 +51,7 @@ struct ConfigStoreTests {
|
||||
#expect(!localHit)
|
||||
}
|
||||
|
||||
@Test func saveRoutesToLocalInLocalMode() async throws {
|
||||
@Test func `save routes to local in local mode`() async throws {
|
||||
var localHit = false
|
||||
var remoteHit = false
|
||||
await ConfigStore._testSetOverrides(.init(
|
||||
|
||||
@@ -4,7 +4,7 @@ import Testing
|
||||
|
||||
@Suite(.serialized)
|
||||
struct CoverageDumpTests {
|
||||
@Test func periodicallyFlushCoverage() async {
|
||||
@Test func `periodically flush coverage`() async {
|
||||
guard ProcessInfo.processInfo.environment["LLVM_PROFILE_FILE"] != nil else { return }
|
||||
guard let writeProfile = resolveProfileWriteFile() else { return }
|
||||
let deadline = Date().addingTimeInterval(4)
|
||||
|
||||
@@ -2,10 +2,9 @@ import AppKit
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite
|
||||
@MainActor
|
||||
struct CritterIconRendererTests {
|
||||
@Test func makeIconRendersExpectedSize() {
|
||||
@Test func `make icon renders expected size`() {
|
||||
let image = CritterIconRenderer.makeIcon(
|
||||
blink: 0.25,
|
||||
legWiggle: 0.5,
|
||||
@@ -19,7 +18,7 @@ struct CritterIconRendererTests {
|
||||
#expect(image.tiffRepresentation != nil)
|
||||
}
|
||||
|
||||
@Test func makeIconRendersWithBadge() {
|
||||
@Test func `make icon renders with badge`() {
|
||||
let image = CritterIconRenderer.makeIcon(
|
||||
blink: 0,
|
||||
legWiggle: 0,
|
||||
@@ -31,7 +30,7 @@ struct CritterIconRendererTests {
|
||||
#expect(image.tiffRepresentation != nil)
|
||||
}
|
||||
|
||||
@Test func critterStatusLabelExercisesHelpers() async {
|
||||
@Test func `critter status label exercises helpers`() async {
|
||||
await CritterStatusLabel.exerciseForTesting()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,17 @@ struct CronJobEditorSmokeTests {
|
||||
onSave: { _ in })
|
||||
}
|
||||
|
||||
@Test func statusPillBuildsBody() {
|
||||
@Test func `status pill builds body`() {
|
||||
_ = StatusPill(text: "ok", tint: .green).body
|
||||
_ = StatusPill(text: "disabled", tint: .secondary).body
|
||||
}
|
||||
|
||||
@Test func cronJobEditorBuildsBodyForNewJob() {
|
||||
@Test func `cron job editor builds body for new job`() {
|
||||
let view = self.makeEditor()
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func cronJobEditorBuildsBodyForExistingJob() {
|
||||
@Test func `cron job editor builds body for existing job`() {
|
||||
let channelsStore = ChannelsStore(isPreview: true)
|
||||
let job = CronJob(
|
||||
id: "job-1",
|
||||
@@ -60,12 +60,12 @@ struct CronJobEditorSmokeTests {
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func cronJobEditorExercisesBuilders() {
|
||||
@Test func `cron job editor exercises builders`() {
|
||||
var view = self.makeEditor()
|
||||
view.exerciseForTesting()
|
||||
}
|
||||
|
||||
@Test func cronJobEditorIncludesDeleteAfterRunForAtSchedule() {
|
||||
@Test func `cron job editor includes delete after run for at schedule`() {
|
||||
let view = self.makeEditor()
|
||||
|
||||
var root: [String: Any] = [:]
|
||||
|
||||
@@ -2,7 +2,6 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite
|
||||
struct CronModelsTests {
|
||||
private func makeCronJob(
|
||||
name: String,
|
||||
@@ -26,14 +25,14 @@ struct CronModelsTests {
|
||||
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 data = try JSONEncoder().encode(schedule)
|
||||
let decoded = try JSONDecoder().decode(CronSchedule.self, from: data)
|
||||
#expect(decoded == schedule)
|
||||
}
|
||||
|
||||
@Test func scheduleAtDecodesLegacyAtMs() throws {
|
||||
@Test func `schedule at decodes legacy at ms`() throws {
|
||||
let json = """
|
||||
{"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 data = try JSONEncoder().encode(schedule)
|
||||
let decoded = try JSONDecoder().decode(CronSchedule.self, from: data)
|
||||
#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 data = try JSONEncoder().encode(schedule)
|
||||
let decoded = try JSONDecoder().decode(CronSchedule.self, from: data)
|
||||
#expect(decoded == schedule)
|
||||
}
|
||||
|
||||
@Test func payloadAgentTurnEncodesAndDecodes() throws {
|
||||
@Test func `payload agent turn encodes and decodes`() throws {
|
||||
let payload = CronPayload.agentTurn(
|
||||
message: "hello",
|
||||
thinking: "low",
|
||||
@@ -73,7 +72,7 @@ struct CronModelsTests {
|
||||
#expect(decoded == payload)
|
||||
}
|
||||
|
||||
@Test func jobEncodesAndDecodesDeleteAfterRun() throws {
|
||||
@Test func `job encodes and decodes delete after run`() throws {
|
||||
let job = CronJob(
|
||||
id: "job-1",
|
||||
agentId: nil,
|
||||
@@ -94,7 +93,7 @@ struct CronModelsTests {
|
||||
#expect(decoded.deleteAfterRun == true)
|
||||
}
|
||||
|
||||
@Test func scheduleDecodeRejectsUnknownKind() {
|
||||
@Test func `schedule decode rejects unknown kind`() {
|
||||
let json = """
|
||||
{"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 = """
|
||||
{"kind":"wat","text":"hello"}
|
||||
"""
|
||||
@@ -112,8 +111,8 @@ struct CronModelsTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test func displayNameTrimsWhitespaceAndFallsBack() {
|
||||
let base = makeCronJob(name: " hello ", payloadText: "hi")
|
||||
@Test func `display name trims whitespace and falls back`() {
|
||||
let base = self.makeCronJob(name: " hello ", payloadText: "hi")
|
||||
#expect(base.displayName == "hello")
|
||||
|
||||
var unnamed = base
|
||||
@@ -121,8 +120,8 @@ struct CronModelsTests {
|
||||
#expect(unnamed.displayName == "Untitled job")
|
||||
}
|
||||
|
||||
@Test func nextRunDateAndLastRunDateDeriveFromState() {
|
||||
let job = makeCronJob(
|
||||
@Test func `next run date and last run date derive from state`() {
|
||||
let job = self.makeCronJob(
|
||||
name: "t",
|
||||
payloadText: "hi",
|
||||
state: CronJobState(
|
||||
@@ -136,7 +135,7 @@ struct CronModelsTests {
|
||||
#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 = """
|
||||
{
|
||||
"jobs": [
|
||||
@@ -177,7 +176,7 @@ struct CronModelsTests {
|
||||
#expect(jobs.first?.id == "good")
|
||||
}
|
||||
|
||||
@Test func decodeCronRunsResponseSkipsMalformedEntries() throws {
|
||||
@Test func `decode cron runs response skips malformed entries`() throws {
|
||||
let json = """
|
||||
{
|
||||
"entries": [
|
||||
|
||||
@@ -2,8 +2,8 @@ import OpenClawKit
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct DeepLinkAgentPolicyTests {
|
||||
@Test func validateMessageForHandleRejectsTooLongWhenUnkeyed() {
|
||||
struct DeepLinkAgentPolicyTests {
|
||||
@Test func `validate message for handle rejects too long when unkeyed`() {
|
||||
let msg = String(repeating: "a", count: DeepLinkAgentPolicy.maxUnkeyedConfirmChars + 1)
|
||||
let res = DeepLinkAgentPolicy.validateMessageForHandle(message: msg, allowUnattended: false)
|
||||
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 res = DeepLinkAgentPolicy.validateMessageForHandle(message: msg, allowUnattended: true)
|
||||
switch res {
|
||||
@@ -28,7 +28,7 @@ import Testing
|
||||
}
|
||||
}
|
||||
|
||||
@Test func effectiveDeliveryIgnoresDeliveryFieldsWhenUnkeyed() {
|
||||
@Test func `effective delivery ignores delivery fields when unkeyed`() {
|
||||
let link = AgentDeepLink(
|
||||
message: "Hello",
|
||||
sessionKey: "s",
|
||||
@@ -44,7 +44,7 @@ import Testing
|
||||
#expect(res.channel == .last)
|
||||
}
|
||||
|
||||
@Test func effectiveDeliveryHonorsDeliverForDeliverableChannelsWhenKeyed() {
|
||||
@Test func `effective delivery honors deliver for deliverable channels when keyed`() {
|
||||
let link = AgentDeepLink(
|
||||
message: "Hello",
|
||||
sessionKey: "s",
|
||||
@@ -60,7 +60,7 @@ import Testing
|
||||
#expect(res.channel == .whatsapp)
|
||||
}
|
||||
|
||||
@Test func effectiveDeliveryStillBlocksWebChatDeliveryWhenKeyed() {
|
||||
@Test func `effective delivery still blocks web chat delivery when keyed`() {
|
||||
let link = AgentDeepLink(
|
||||
message: "Hello",
|
||||
sessionKey: "s",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite
|
||||
struct DeviceModelCatalogTests {
|
||||
@Test
|
||||
func symbolPrefersModelIdentifierPrefixes() {
|
||||
func `symbol prefers model identifier prefixes`() {
|
||||
#expect(DeviceModelCatalog
|
||||
.symbol(deviceFamily: "iPad", modelIdentifier: "iPad16,6", friendlyName: nil) == "ipad")
|
||||
#expect(DeviceModelCatalog
|
||||
@@ -12,7 +11,7 @@ struct DeviceModelCatalogTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func symbolUsesFriendlyNameForMacVariants() {
|
||||
func `symbol uses friendly name for mac variants`() {
|
||||
#expect(DeviceModelCatalog.symbol(
|
||||
deviceFamily: "Mac",
|
||||
modelIdentifier: "Mac99,1",
|
||||
@@ -28,13 +27,13 @@ struct DeviceModelCatalogTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func symbolFallsBackToDeviceFamily() {
|
||||
func `symbol falls back to device family`() {
|
||||
#expect(DeviceModelCatalog.symbol(deviceFamily: "Android", modelIdentifier: "", friendlyName: nil) == "android")
|
||||
#expect(DeviceModelCatalog.symbol(deviceFamily: "Linux", modelIdentifier: "", friendlyName: nil) == "cpu")
|
||||
}
|
||||
|
||||
@Test
|
||||
func presentationUsesBundledModelMappings() {
|
||||
func `presentation uses bundled model mappings`() {
|
||||
let presentation = DeviceModelCatalog.presentation(deviceFamily: "iPhone", modelIdentifier: "iPhone1,1")
|
||||
#expect(presentation?.title == "iPhone")
|
||||
}
|
||||
|
||||
@@ -59,21 +59,21 @@ struct ExecAllowlistTests {
|
||||
cwd: nil)
|
||||
}
|
||||
|
||||
@Test func matchUsesResolvedPath() {
|
||||
@Test func `match uses resolved path`() {
|
||||
let entry = ExecAllowlistEntry(pattern: "/opt/homebrew/bin/rg")
|
||||
let resolution = Self.homebrewRGResolution()
|
||||
let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution)
|
||||
#expect(match?.pattern == entry.pattern)
|
||||
}
|
||||
|
||||
@Test func matchIgnoresBasenamePattern() {
|
||||
@Test func `match ignores basename pattern`() {
|
||||
let entry = ExecAllowlistEntry(pattern: "rg")
|
||||
let resolution = Self.homebrewRGResolution()
|
||||
let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution)
|
||||
#expect(match == nil)
|
||||
}
|
||||
|
||||
@Test func matchIgnoresBasenameForRelativeExecutable() {
|
||||
@Test func `match ignores basename for relative executable`() {
|
||||
let entry = ExecAllowlistEntry(pattern: "echo")
|
||||
let resolution = ExecCommandResolution(
|
||||
rawExecutable: "./echo",
|
||||
@@ -84,21 +84,21 @@ struct ExecAllowlistTests {
|
||||
#expect(match == nil)
|
||||
}
|
||||
|
||||
@Test func matchIsCaseInsensitive() {
|
||||
@Test func `match is case insensitive`() {
|
||||
let entry = ExecAllowlistEntry(pattern: "/OPT/HOMEBREW/BIN/RG")
|
||||
let resolution = Self.homebrewRGResolution()
|
||||
let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution)
|
||||
#expect(match?.pattern == entry.pattern)
|
||||
}
|
||||
|
||||
@Test func matchSupportsGlobStar() {
|
||||
@Test func `match supports glob star`() {
|
||||
let entry = ExecAllowlistEntry(pattern: "/opt/**/rg")
|
||||
let resolution = Self.homebrewRGResolution()
|
||||
let match = ExecAllowlistMatcher.match(entries: [entry], resolution: resolution)
|
||||
#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 resolutions = ExecCommandResolution.resolveForAllowlist(
|
||||
command: command,
|
||||
@@ -110,7 +110,7 @@ struct ExecAllowlistTests {
|
||||
#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 resolutions = ExecCommandResolution.resolveForAllowlist(
|
||||
command: command,
|
||||
@@ -121,7 +121,7 @@ struct ExecAllowlistTests {
|
||||
#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 resolutions = ExecCommandResolution.resolveForAllowlist(
|
||||
command: command,
|
||||
@@ -131,7 +131,7 @@ struct ExecAllowlistTests {
|
||||
#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 resolutions = ExecCommandResolution.resolveForAllowlist(
|
||||
command: command,
|
||||
@@ -141,7 +141,7 @@ struct ExecAllowlistTests {
|
||||
#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 resolutions = ExecCommandResolution.resolveForAllowlist(
|
||||
command: command,
|
||||
@@ -151,7 +151,7 @@ struct ExecAllowlistTests {
|
||||
#expect(resolutions.isEmpty)
|
||||
}
|
||||
|
||||
@Test func resolveForAllowlistMatchesSharedShellParserFixture() throws {
|
||||
@Test func `resolve for allowlist matches shared shell parser fixture`() throws {
|
||||
let fixtures = try Self.loadShellParserParityCases()
|
||||
for fixture in fixtures {
|
||||
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()
|
||||
for fixture in fixtures {
|
||||
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 resolutions = ExecCommandResolution.resolveForAllowlist(
|
||||
command: command,
|
||||
@@ -191,7 +191,7 @@ struct ExecAllowlistTests {
|
||||
#expect(resolutions[0].executableName == "sh")
|
||||
}
|
||||
|
||||
@Test func resolveForAllowlistUnwrapsEnvShellWrapperChains() {
|
||||
@Test func `resolve for allowlist unwraps env shell wrapper chains`() {
|
||||
let command = [
|
||||
"/usr/bin/env",
|
||||
"/bin/sh",
|
||||
@@ -208,7 +208,7 @@ struct ExecAllowlistTests {
|
||||
#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 resolutions = ExecCommandResolution.resolveForAllowlist(
|
||||
command: command,
|
||||
@@ -220,7 +220,7 @@ struct ExecAllowlistTests {
|
||||
#expect(resolutions[0].executableName == "printf")
|
||||
}
|
||||
|
||||
@Test func matchAllRequiresEverySegmentToMatch() {
|
||||
@Test func `match all requires every segment to match`() {
|
||||
let first = ExecCommandResolution(
|
||||
rawExecutable: "echo",
|
||||
resolvedPath: "/usr/bin/echo",
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct ExecApprovalHelpersTests {
|
||||
@Test func parseDecisionTrimsAndRejectsInvalid() {
|
||||
struct ExecApprovalHelpersTests {
|
||||
@Test func `parse decision trims and rejects invalid`() {
|
||||
#expect(ExecApprovalHelpers.parseDecision("allow-once") == .allowOnce)
|
||||
#expect(ExecApprovalHelpers.parseDecision(" allow-always ") == .allowAlways)
|
||||
#expect(ExecApprovalHelpers.parseDecision("deny") == .deny)
|
||||
@@ -11,7 +11,7 @@ import Testing
|
||||
#expect(ExecApprovalHelpers.parseDecision("nope") == nil)
|
||||
}
|
||||
|
||||
@Test func allowlistPatternPrefersResolution() {
|
||||
@Test func `allowlist pattern prefers resolution`() {
|
||||
let resolved = ExecCommandResolution(
|
||||
rawExecutable: "rg",
|
||||
resolvedPath: "/opt/homebrew/bin/rg",
|
||||
@@ -29,7 +29,7 @@ import Testing
|
||||
#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(" ~/bin/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)
|
||||
#expect(ExecApprovalHelpers.requiresAsk(
|
||||
ask: .always,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite
|
||||
@MainActor
|
||||
struct ExecApprovalsGatewayPrompterTests {
|
||||
@Test func sessionMatchPrefersActiveSession() {
|
||||
@Test func `session match prefers active session`() {
|
||||
let matches = ExecApprovalsGatewayPrompter._testShouldPresent(
|
||||
mode: .remote,
|
||||
activeSession: " main ",
|
||||
@@ -20,7 +19,7 @@ struct ExecApprovalsGatewayPrompterTests {
|
||||
#expect(!mismatched)
|
||||
}
|
||||
|
||||
@Test func sessionFallbackUsesRecentActivity() {
|
||||
@Test func `session fallback uses recent activity`() {
|
||||
let recent = ExecApprovalsGatewayPrompter._testShouldPresent(
|
||||
mode: .remote,
|
||||
activeSession: nil,
|
||||
@@ -38,7 +37,7 @@ struct ExecApprovalsGatewayPrompterTests {
|
||||
#expect(!stale)
|
||||
}
|
||||
|
||||
@Test func defaultBehaviorMatchesMode() {
|
||||
@Test func `default behavior matches mode`() {
|
||||
let local = ExecApprovalsGatewayPrompter._testShouldPresent(
|
||||
mode: .local,
|
||||
activeSession: nil,
|
||||
|
||||
@@ -5,7 +5,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
struct ExecApprovalsSocketPathGuardTests {
|
||||
@Test
|
||||
func hardenParentDirectoryCreatesDirectoryWith0700Permissions() throws {
|
||||
func `harden parent directory creates directory with0700 permissions`() throws {
|
||||
let root = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: root) }
|
||||
@@ -24,7 +24,7 @@ struct ExecApprovalsSocketPathGuardTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func removeExistingSocketRejectsSymlinkPath() throws {
|
||||
func `remove existing socket rejects symlink path`() throws {
|
||||
let root = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: root) }
|
||||
@@ -50,7 +50,7 @@ struct ExecApprovalsSocketPathGuardTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func removeExistingSocketRejectsRegularFilePath() throws {
|
||||
func `remove existing socket rejects regular file path`() throws {
|
||||
let root = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-socket-guard-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: root) }
|
||||
|
||||
@@ -17,8 +17,8 @@ struct ExecApprovalsStoreRefactorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func ensureFileSkipsRewriteWhenUnchanged() async throws {
|
||||
try await self.withTempStateDir { stateDir in
|
||||
func `ensure file skips rewrite when unchanged`() async throws {
|
||||
try await self.withTempStateDir { _ in
|
||||
_ = ExecApprovalsStore.ensureFile()
|
||||
let url = ExecApprovalsStore.fileURL()
|
||||
let firstWriteDate = try Self.modificationDate(at: url)
|
||||
@@ -32,7 +32,7 @@ struct ExecApprovalsStoreRefactorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func updateAllowlistReportsRejectedBasenamePattern() async throws {
|
||||
func `update allowlist reports rejected basename pattern`() async throws {
|
||||
try await self.withTempStateDir { _ in
|
||||
let rejected = ExecApprovalsStore.updateAllowlist(
|
||||
agentId: "main",
|
||||
@@ -50,7 +50,7 @@ struct ExecApprovalsStoreRefactorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func updateAllowlistMigratesLegacyPatternFromResolvedPath() async throws {
|
||||
func `update allowlist migrates legacy pattern from resolved path`() async throws {
|
||||
try await self.withTempStateDir { _ in
|
||||
let rejected = ExecApprovalsStore.updateAllowlist(
|
||||
agentId: "main",
|
||||
@@ -69,7 +69,7 @@ struct ExecApprovalsStoreRefactorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func ensureFileHardensStateDirectoryPermissions() async throws {
|
||||
func `ensure file hardens state directory permissions`() async throws {
|
||||
try await self.withTempStateDir { stateDir in
|
||||
try FileManager().createDirectory(at: stateDir, withIntermediateDirectories: true)
|
||||
try FileManager().setAttributes([.posixPermissions: 0o755], ofItemAtPath: stateDir.path)
|
||||
|
||||
@@ -3,7 +3,7 @@ import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
struct ExecHostRequestEvaluatorTests {
|
||||
@Test func validateRequestRejectsEmptyCommand() {
|
||||
@Test func `validate request rejects empty command`() {
|
||||
let request = ExecHostRequest(
|
||||
command: [],
|
||||
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 decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: nil)
|
||||
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 decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: .allowOnce)
|
||||
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 decision = ExecHostRequestEvaluator.evaluate(context: context, approvalDecision: .deny)
|
||||
switch decision {
|
||||
|
||||
@@ -20,7 +20,7 @@ private struct SystemRunCommandContractExpected: Decodable {
|
||||
}
|
||||
|
||||
struct ExecSystemRunCommandValidatorTests {
|
||||
@Test func matchesSharedSystemRunCommandContractFixture() throws {
|
||||
@Test func `matches shared system run command contract fixture`() throws {
|
||||
for entry in try Self.loadContractCases() {
|
||||
let result = ExecSystemRunCommandValidator.resolve(command: entry.command, rawCommand: entry.rawCommand)
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@Suite struct FileHandleLegacyAPIGuardTests {
|
||||
@Test func sourcesAvoidLegacyNonThrowingFileHandleReadAPIs() throws {
|
||||
struct FileHandleLegacyAPIGuardTests {
|
||||
@Test func `sources avoid legacy non throwing file handle read AP is`() throws {
|
||||
let testFile = URL(fileURLWithPath: #filePath)
|
||||
let packageRoot = testFile
|
||||
.deletingLastPathComponent() // OpenClawIPCTests
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct FileHandleSafeReadTests {
|
||||
@Test func readToEndSafelyReturnsEmptyForClosedHandle() {
|
||||
struct FileHandleSafeReadTests {
|
||||
@Test func `read to end safely returns empty for closed handle`() {
|
||||
let pipe = Pipe()
|
||||
let handle = pipe.fileHandleForReading
|
||||
try? handle.close()
|
||||
@@ -12,7 +12,7 @@ import Testing
|
||||
#expect(data.isEmpty)
|
||||
}
|
||||
|
||||
@Test func readSafelyUpToCountReturnsEmptyForClosedHandle() {
|
||||
@Test func `read safely up to count returns empty for closed handle`() {
|
||||
let pipe = Pipe()
|
||||
let handle = pipe.fileHandleForReading
|
||||
try? handle.close()
|
||||
@@ -21,7 +21,7 @@ import Testing
|
||||
#expect(data.isEmpty)
|
||||
}
|
||||
|
||||
@Test func readToEndSafelyReadsPipeContents() {
|
||||
@Test func `read to end safely reads pipe contents`() {
|
||||
let pipe = Pipe()
|
||||
let writeHandle = pipe.fileHandleForWriting
|
||||
writeHandle.write(Data("hello".utf8))
|
||||
@@ -31,7 +31,7 @@ import Testing
|
||||
#expect(String(data: data, encoding: .utf8) == "hello")
|
||||
}
|
||||
|
||||
@Test func readSafelyUpToCountReadsIncrementally() {
|
||||
@Test func `read safely up to count reads incrementally`() {
|
||||
let pipe = Pipe()
|
||||
let writeHandle = pipe.fileHandleForWriting
|
||||
writeHandle.write(Data("hello world".utf8))
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct GatewayAgentChannelTests {
|
||||
@Test func shouldDeliverBlocksWebChat() {
|
||||
struct GatewayAgentChannelTests {
|
||||
@Test func `should deliver blocks web chat`() {
|
||||
#expect(GatewayAgentChannel.webchat.shouldDeliver(true) == 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.whatsapp.shouldDeliver(true) == true)
|
||||
#expect(GatewayAgentChannel.telegram.shouldDeliver(true) == true)
|
||||
@@ -16,7 +16,7 @@ import Testing
|
||||
#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: " ") == .last)
|
||||
#expect(GatewayAgentChannel(raw: "WEBCHAT") == .webchat)
|
||||
|
||||
@@ -3,14 +3,14 @@ import Testing
|
||||
|
||||
@Suite(.serialized)
|
||||
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: true))
|
||||
#expect(!GatewayAutostartPolicy.shouldStartGateway(mode: .remote, 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(
|
||||
mode: .local,
|
||||
paused: false))
|
||||
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct GatewayConnectionTests {
|
||||
struct GatewayConnectionTests {
|
||||
private func makeConnection(
|
||||
session: GatewayTestWebSocketSession,
|
||||
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 (conn, _) = try self.makeConnection(session: session)
|
||||
|
||||
@@ -68,7 +68,7 @@ import Testing
|
||||
#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 (conn, cfg) = try self.makeConnection(session: session, token: "a")
|
||||
|
||||
@@ -81,7 +81,7 @@ import Testing
|
||||
#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 (conn, _) = try self.makeConnection(session: session)
|
||||
|
||||
@@ -92,7 +92,7 @@ import Testing
|
||||
#expect(session.snapshotMakeCount() == 1)
|
||||
}
|
||||
|
||||
@Test func subscribeReplaysLatestSnapshot() async throws {
|
||||
@Test func `subscribe replays latest snapshot`() async throws {
|
||||
let session = self.makeSession()
|
||||
let (conn, _) = try self.makeConnection(session: session)
|
||||
|
||||
@@ -109,7 +109,7 @@ import Testing
|
||||
#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 (conn, _) = try self.makeConnection(session: session)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import OpenClawKit
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct GatewayChannelConnectTests {
|
||||
struct GatewayChannelConnectTests {
|
||||
private enum FakeResponse {
|
||||
case helloOk(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 channel = try GatewayChannelActor(
|
||||
url: #require(URL(string: "ws://example.invalid")),
|
||||
@@ -50,7 +50,7 @@ import Testing
|
||||
#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 channel = try GatewayChannelActor(
|
||||
url: #require(URL(string: "ws://example.invalid")),
|
||||
|
||||
@@ -3,7 +3,7 @@ import OpenClawKit
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct GatewayChannelRequestTests {
|
||||
struct GatewayChannelRequestTests {
|
||||
private func makeSession(requestSendDelayMs: Int) -> GatewayTestWebSocketSession {
|
||||
GatewayTestWebSocketSession(
|
||||
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 channel = try GatewayChannelActor(
|
||||
url: #require(URL(string: "ws://example.invalid")),
|
||||
|
||||
@@ -3,8 +3,8 @@ import OpenClawKit
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct GatewayChannelShutdownTests {
|
||||
@Test func shutdownPreventsReconnectLoopFromReceiveFailure() async throws {
|
||||
struct GatewayChannelShutdownTests {
|
||||
@Test func `shutdown prevents reconnect loop from receive failure`() async throws {
|
||||
let session = GatewayTestWebSocketSession()
|
||||
let channel = try GatewayChannelActor(
|
||||
url: #require(URL(string: "ws://example.invalid")),
|
||||
|
||||
@@ -39,14 +39,14 @@ private func makeTestGatewayConnection() -> GatewayConnection {
|
||||
}
|
||||
|
||||
@Suite(.serialized) struct GatewayConnectionControlTests {
|
||||
@Test func statusFailsWhenProcessMissing() async {
|
||||
@Test func `status fails when process missing`() async {
|
||||
let connection = makeTestGatewayConnection()
|
||||
let result = await connection.status()
|
||||
#expect(result.ok == false)
|
||||
#expect(result.error != nil)
|
||||
}
|
||||
|
||||
@Test func rejectEmptyMessage() async {
|
||||
@Test func `reject empty message`() async {
|
||||
let connection = makeTestGatewayConnection()
|
||||
let result = await connection.sendAgent(
|
||||
message: "",
|
||||
|
||||
@@ -3,7 +3,6 @@ import OpenClawDiscovery
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite
|
||||
struct GatewayDiscoveryHelpersTests {
|
||||
private func makeGateway(
|
||||
serviceHost: String?,
|
||||
@@ -41,23 +40,23 @@ struct GatewayDiscoveryHelpersTests {
|
||||
#expect(parsed?.port == port)
|
||||
}
|
||||
|
||||
@Test func sshTargetUsesResolvedServiceHostOnly() {
|
||||
@Test func `ssh target uses resolved service host only`() {
|
||||
let gateway = self.makeGateway(
|
||||
serviceHost: "resolved.example.ts.net",
|
||||
servicePort: 18789,
|
||||
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(
|
||||
serviceHost: "resolved.example.ts.net",
|
||||
servicePort: nil,
|
||||
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(
|
||||
serviceHost: nil,
|
||||
servicePort: nil,
|
||||
@@ -68,7 +67,7 @@ struct GatewayDiscoveryHelpersTests {
|
||||
#expect(GatewayDiscoveryHelpers.sshTarget(for: gateway) == nil)
|
||||
}
|
||||
|
||||
@Test func directUrlUsesResolvedServiceEndpointOnly() {
|
||||
@Test func `direct url uses resolved service endpoint only`() {
|
||||
let tlsGateway = self.makeGateway(
|
||||
serviceHost: "resolved.example.ts.net",
|
||||
servicePort: 443)
|
||||
@@ -85,7 +84,7 @@ struct GatewayDiscoveryHelpersTests {
|
||||
#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(
|
||||
serviceHost: nil,
|
||||
servicePort: nil,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
@testable import OpenClawDiscovery
|
||||
import Testing
|
||||
@testable import OpenClawDiscovery
|
||||
|
||||
@Suite
|
||||
@MainActor
|
||||
struct GatewayDiscoveryModelTests {
|
||||
@Test func localGatewayMatchesLanHost() {
|
||||
@Test func `local gateway matches lan host`() {
|
||||
let local = GatewayDiscoveryModel.LocalIdentity(
|
||||
hostTokens: ["studio"],
|
||||
displayTokens: [])
|
||||
@@ -16,7 +15,7 @@ struct GatewayDiscoveryModelTests {
|
||||
local: local))
|
||||
}
|
||||
|
||||
@Test func localGatewayMatchesTailnetDns() {
|
||||
@Test func `local gateway matches tailnet dns`() {
|
||||
let local = GatewayDiscoveryModel.LocalIdentity(
|
||||
hostTokens: ["studio"],
|
||||
displayTokens: [])
|
||||
@@ -28,7 +27,7 @@ struct GatewayDiscoveryModelTests {
|
||||
local: local))
|
||||
}
|
||||
|
||||
@Test func localGatewayMatchesDisplayName() {
|
||||
@Test func `local gateway matches display name`() {
|
||||
let local = GatewayDiscoveryModel.LocalIdentity(
|
||||
hostTokens: [],
|
||||
displayTokens: ["peter's mac studio"])
|
||||
@@ -40,7 +39,7 @@ struct GatewayDiscoveryModelTests {
|
||||
local: local))
|
||||
}
|
||||
|
||||
@Test func remoteGatewayDoesNotMatch() {
|
||||
@Test func `remote gateway does not match`() {
|
||||
let local = GatewayDiscoveryModel.LocalIdentity(
|
||||
hostTokens: ["studio"],
|
||||
displayTokens: ["peter's mac studio"])
|
||||
@@ -52,7 +51,7 @@ struct GatewayDiscoveryModelTests {
|
||||
local: local))
|
||||
}
|
||||
|
||||
@Test func localGatewayMatchesServiceName() {
|
||||
@Test func `local gateway matches service name`() {
|
||||
let local = GatewayDiscoveryModel.LocalIdentity(
|
||||
hostTokens: ["studio"],
|
||||
displayTokens: [])
|
||||
@@ -64,7 +63,7 @@ struct GatewayDiscoveryModelTests {
|
||||
local: local))
|
||||
}
|
||||
|
||||
@Test func serviceNameDoesNotFalsePositiveOnSubstringHostToken() {
|
||||
@Test func `service name does not false positive on substring host token`() {
|
||||
let local = GatewayDiscoveryModel.LocalIdentity(
|
||||
hostTokens: ["steipete"],
|
||||
displayTokens: [])
|
||||
@@ -82,7 +81,7 @@ struct GatewayDiscoveryModelTests {
|
||||
local: local))
|
||||
}
|
||||
|
||||
@Test func parsesGatewayTXTFields() {
|
||||
@Test func `parses gateway TXT fields`() {
|
||||
let parsed = GatewayDiscoveryModel.parseGatewayTXT([
|
||||
"lanHost": " studio.local ",
|
||||
"tailnetDns": " peters-mac-studio-1.ts.net ",
|
||||
@@ -97,7 +96,7 @@ struct GatewayDiscoveryModelTests {
|
||||
#expect(parsed.cliPath == "/opt/openclaw")
|
||||
}
|
||||
|
||||
@Test func parsesGatewayTXTDefaults() {
|
||||
@Test func `parses gateway TXT defaults`() {
|
||||
let parsed = GatewayDiscoveryModel.parseGatewayTXT([
|
||||
"lanHost": " ",
|
||||
"tailnetDns": "\n",
|
||||
@@ -111,7 +110,7 @@ struct GatewayDiscoveryModelTests {
|
||||
#expect(parsed.cliPath == nil)
|
||||
}
|
||||
|
||||
@Test func buildsSSHTarget() {
|
||||
@Test func `builds SSH target`() {
|
||||
#expect(GatewayDiscoveryModel.buildSSHTarget(
|
||||
user: "peter",
|
||||
host: "studio.local",
|
||||
@@ -122,7 +121,7 @@ struct GatewayDiscoveryModelTests {
|
||||
port: 2201) == "peter@studio.local:2201")
|
||||
}
|
||||
|
||||
@Test func dedupeKeyPrefersResolvedEndpointAcrossSources() {
|
||||
@Test func `dedupe key prefers resolved endpoint across sources`() {
|
||||
let wideArea = GatewayDiscoveryModel.DiscoveredGateway(
|
||||
displayName: "Gateway",
|
||||
serviceHost: "gateway-host.tailnet-example.ts.net",
|
||||
@@ -151,7 +150,7 @@ struct GatewayDiscoveryModelTests {
|
||||
#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(
|
||||
displayName: "Gateway",
|
||||
serviceHost: nil,
|
||||
@@ -165,6 +164,7 @@ struct GatewayDiscoveryModelTests {
|
||||
debugID: "serve",
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct GatewayEndpointStoreTests {
|
||||
struct GatewayEndpointStoreTests {
|
||||
private func makeLaunchAgentSnapshot(
|
||||
env: [String: String],
|
||||
token: String?,
|
||||
@@ -26,7 +26,7 @@ import Testing
|
||||
return defaults
|
||||
}
|
||||
|
||||
@Test func resolveGatewayTokenPrefersEnvAndFallsBackToLaunchd() {
|
||||
@Test func `resolve gateway token prefers env and falls back to launchd`() {
|
||||
let snapshot = self.makeLaunchAgentSnapshot(
|
||||
env: ["OPENCLAW_GATEWAY_TOKEN": "launchd-token"],
|
||||
token: "launchd-token",
|
||||
@@ -47,7 +47,7 @@ import Testing
|
||||
#expect(fallbackToken == "launchd-token")
|
||||
}
|
||||
|
||||
@Test func resolveGatewayTokenIgnoresLaunchdInRemoteMode() {
|
||||
@Test func `resolve gateway token ignores launchd in remote mode`() {
|
||||
let snapshot = self.makeLaunchAgentSnapshot(
|
||||
env: ["OPENCLAW_GATEWAY_TOKEN": "launchd-token"],
|
||||
token: "launchd-token",
|
||||
@@ -61,7 +61,7 @@ import Testing
|
||||
#expect(token == nil)
|
||||
}
|
||||
|
||||
@Test func resolveGatewayPasswordFallsBackToLaunchd() {
|
||||
@Test func `resolve gateway password falls back to launchd`() {
|
||||
let snapshot = self.makeLaunchAgentSnapshot(
|
||||
env: ["OPENCLAW_GATEWAY_PASSWORD": "launchd-pass"],
|
||||
token: nil,
|
||||
@@ -75,7 +75,7 @@ import Testing
|
||||
#expect(password == "launchd-pass")
|
||||
}
|
||||
|
||||
@Test func connectionModeResolverPrefersConfigModeOverDefaults() {
|
||||
@Test func `connection mode resolver prefers config mode over defaults`() {
|
||||
let defaults = self.makeDefaults()
|
||||
defaults.set("remote", forKey: connectionModeKey)
|
||||
|
||||
@@ -89,7 +89,7 @@ import Testing
|
||||
#expect(resolved.mode == .local)
|
||||
}
|
||||
|
||||
@Test func connectionModeResolverTrimsConfigMode() {
|
||||
@Test func `connection mode resolver trims config mode`() {
|
||||
let defaults = self.makeDefaults()
|
||||
defaults.set("local", forKey: connectionModeKey)
|
||||
|
||||
@@ -103,7 +103,7 @@ import Testing
|
||||
#expect(resolved.mode == .remote)
|
||||
}
|
||||
|
||||
@Test func connectionModeResolverFallsBackToDefaultsWhenMissingConfig() {
|
||||
@Test func `connection mode resolver falls back to defaults when missing config`() {
|
||||
let defaults = self.makeDefaults()
|
||||
defaults.set("remote", forKey: connectionModeKey)
|
||||
|
||||
@@ -111,7 +111,7 @@ import Testing
|
||||
#expect(resolved.mode == .remote)
|
||||
}
|
||||
|
||||
@Test func connectionModeResolverFallsBackToDefaultsOnUnknownConfig() {
|
||||
@Test func `connection mode resolver falls back to defaults on unknown config`() {
|
||||
let defaults = self.makeDefaults()
|
||||
defaults.set("local", forKey: connectionModeKey)
|
||||
|
||||
@@ -125,7 +125,7 @@ import Testing
|
||||
#expect(resolved.mode == .local)
|
||||
}
|
||||
|
||||
@Test func connectionModeResolverPrefersRemoteURLWhenModeMissing() {
|
||||
@Test func `connection mode resolver prefers remote URL when mode missing`() {
|
||||
let defaults = self.makeDefaults()
|
||||
defaults.set("local", forKey: connectionModeKey)
|
||||
|
||||
@@ -141,35 +141,35 @@ import Testing
|
||||
#expect(resolved.mode == .remote)
|
||||
}
|
||||
|
||||
@Test func resolveLocalGatewayHostUsesLoopbackForAutoEvenWithTailnet() {
|
||||
@Test func `resolve local gateway host uses loopback for auto even with tailnet`() {
|
||||
let host = GatewayEndpointStore._testResolveLocalGatewayHost(
|
||||
bindMode: "auto",
|
||||
tailscaleIP: "100.64.1.2")
|
||||
#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(
|
||||
bindMode: "auto",
|
||||
tailscaleIP: nil)
|
||||
#expect(host == "127.0.0.1")
|
||||
}
|
||||
|
||||
@Test func resolveLocalGatewayHostPrefersTailnetForTailnetMode() {
|
||||
@Test func `resolve local gateway host prefers tailnet for tailnet mode`() {
|
||||
let host = GatewayEndpointStore._testResolveLocalGatewayHost(
|
||||
bindMode: "tailnet",
|
||||
tailscaleIP: "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(
|
||||
bindMode: "tailnet",
|
||||
tailscaleIP: nil)
|
||||
#expect(host == "127.0.0.1")
|
||||
}
|
||||
|
||||
@Test func resolveLocalGatewayHostUsesCustomBindHost() {
|
||||
@Test func `resolve local gateway host uses custom bind host`() {
|
||||
let host = GatewayEndpointStore._testResolveLocalGatewayHost(
|
||||
bindMode: "custom",
|
||||
tailscaleIP: "100.64.1.9",
|
||||
@@ -177,7 +177,7 @@ import Testing
|
||||
#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(
|
||||
env: [:],
|
||||
token: "launchd-token",
|
||||
@@ -204,7 +204,7 @@ import Testing
|
||||
#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 (
|
||||
url: #require(URL(string: "ws://127.0.0.1:18789")),
|
||||
token: nil,
|
||||
@@ -217,7 +217,7 @@ import Testing
|
||||
#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 (
|
||||
url: #require(URL(string: "ws://gateway.example:18789")),
|
||||
token: nil,
|
||||
@@ -230,7 +230,7 @@ import Testing
|
||||
#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 (
|
||||
url: #require(URL(string: "wss://gateway.example:443/remote-ui")),
|
||||
token: nil,
|
||||
@@ -243,7 +243,7 @@ import Testing
|
||||
#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 (
|
||||
url: #require(URL(string: "ws://127.0.0.1:18789")),
|
||||
token: "abc123",
|
||||
@@ -257,18 +257,18 @@ import Testing
|
||||
#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")
|
||||
#expect(url?.port == 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")
|
||||
#expect(url == nil)
|
||||
}
|
||||
|
||||
@Test func normalizeGatewayUrlRejectsPrefixBypassLoopbackHost() {
|
||||
@Test func `normalize gateway url rejects prefix bypass loopback host`() {
|
||||
let url = GatewayRemoteConfig.normalizeGatewayUrl("ws://127.attacker.example")
|
||||
#expect(url == nil)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct GatewayEnvironmentTests {
|
||||
@Test func semverParsesCommonForms() {
|
||||
struct GatewayEnvironmentTests {
|
||||
@Test func `semver parses common forms`() {
|
||||
#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("v2.0.0") == Semver(major: 2, minor: 0, patch: 0))
|
||||
@@ -21,7 +21,7 @@ import Testing
|
||||
#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)
|
||||
#expect(Semver(major: 2, minor: 1, 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)
|
||||
}
|
||||
|
||||
@Test func gatewayPortDefaultsAndRespectsOverride() async {
|
||||
@Test func `gateway port defaults and respects override`() async {
|
||||
let configPath = TestIsolation.tempConfigPath()
|
||||
await TestIsolation.withIsolatedState(
|
||||
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: "2026.1.11-4") == Semver(
|
||||
major: 2026,
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import OpenClawProtocol
|
||||
import Testing
|
||||
|
||||
@Suite struct GatewayFrameDecodeTests {
|
||||
@Test func decodesEventFrameWithAnyCodablePayload() throws {
|
||||
struct GatewayFrameDecodeTests {
|
||||
@Test func `decodes event frame with any codable payload`() throws {
|
||||
let json = """
|
||||
{
|
||||
"type": "event",
|
||||
@@ -29,7 +29,7 @@ import Testing
|
||||
#expect(evt.seq == 7)
|
||||
}
|
||||
|
||||
@Test func decodesRequestFrameWithNestedParams() throws {
|
||||
@Test func `decodes request frame with nested params`() throws {
|
||||
let json = """
|
||||
{
|
||||
"type": "req",
|
||||
@@ -68,7 +68,7 @@ import Testing
|
||||
#expect(meta?["count"]?.value as? Int == 2)
|
||||
}
|
||||
|
||||
@Test func decodesUnknownFrameAndPreservesRaw() throws {
|
||||
@Test func `decodes unknown frame and preserves raw`() throws {
|
||||
let json = """
|
||||
{
|
||||
"type": "made-up",
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct GatewayLaunchAgentManagerTests {
|
||||
@Test func launchAgentPlistSnapshotParsesArgsAndEnv() throws {
|
||||
struct GatewayLaunchAgentManagerTests {
|
||||
@Test func `launch agent plist snapshot parses args and env`() throws {
|
||||
let url = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-launchd-\(UUID().uuidString).plist")
|
||||
let plist: [String: Any] = [
|
||||
@@ -24,7 +24,7 @@ import Testing
|
||||
#expect(snapshot.password == "pw")
|
||||
}
|
||||
|
||||
@Test func launchAgentPlistSnapshotAllowsMissingBind() throws {
|
||||
@Test func `launch agent plist snapshot allows missing bind`() throws {
|
||||
let url = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-launchd-\(UUID().uuidString).plist")
|
||||
let plist: [String: Any] = [
|
||||
|
||||
@@ -6,7 +6,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct GatewayProcessManagerTests {
|
||||
@Test func clearsLastFailureWhenHealthSucceeds() async throws {
|
||||
@Test func `clears last failure when health succeeds`() async throws {
|
||||
let session = GatewayTestWebSocketSession(
|
||||
taskFactory: {
|
||||
GatewayTestWebSocketTask(
|
||||
|
||||
@@ -83,9 +83,9 @@ enum GatewayWebSocketTestSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private extension NSLock {
|
||||
extension NSLock {
|
||||
@inline(__always)
|
||||
func withLock<T>(_ body: () throws -> T) rethrows -> T {
|
||||
fileprivate func withLock<T>(_ body: () throws -> T) rethrows -> T {
|
||||
self.lock(); defer { self.unlock() }
|
||||
return try body()
|
||||
}
|
||||
@@ -129,7 +129,10 @@ final class GatewayTestWebSocketTask: WebSocketTasking, @unchecked Sendable {
|
||||
|
||||
func cancel(with closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
|
||||
_ = (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.cancelCount += 1
|
||||
defer { self.pendingReceiveHandler = nil }
|
||||
|
||||
@@ -2,13 +2,13 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct HealthDecodeTests {
|
||||
struct HealthDecodeTests {
|
||||
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}]}}
|
||||
"""
|
||||
|
||||
@Test func decodesCleanJSON() {
|
||||
@Test func `decodes clean JSON`() {
|
||||
let data = Data(sampleJSON.utf8)
|
||||
let snap = decodeHealthSnapshot(from: data)
|
||||
|
||||
@@ -16,14 +16,14 @@ import Testing
|
||||
#expect(snap?.sessions.count == 1)
|
||||
}
|
||||
|
||||
@Test func decodesWithLeadingNoise() {
|
||||
@Test func `decodes with leading noise`() {
|
||||
let noisy = "debug: something logged\n" + self.sampleJSON + "\ntrailer"
|
||||
let snap = decodeHealthSnapshot(from: Data(noisy.utf8))
|
||||
|
||||
#expect(snap?.channels["telegram"]?.probe?.elapsedMs == 800)
|
||||
}
|
||||
|
||||
@Test func failsWithoutBraces() {
|
||||
@Test func `fails without braces`() {
|
||||
let data = Data("no json here".utf8)
|
||||
let snap = decodeHealthSnapshot(from: data)
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct HealthStoreStateTests {
|
||||
@Test @MainActor func linkedChannelProbeFailureDegradesState() {
|
||||
struct HealthStoreStateTests {
|
||||
@Test @MainActor func `linked channel probe failure degrades state`() {
|
||||
let snap = HealthSnapshot(
|
||||
ok: true,
|
||||
ts: 0,
|
||||
|
||||
@@ -2,7 +2,7 @@ import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
struct HostEnvSanitizerTests {
|
||||
@Test func sanitizeBlocksShellTraceVariables() {
|
||||
@Test func `sanitize blocks shell trace variables`() {
|
||||
let env = HostEnvSanitizer.sanitize(overrides: [
|
||||
"SHELLOPTS": "xtrace",
|
||||
"PS4": "$(touch /tmp/pwned)",
|
||||
@@ -13,7 +13,7 @@ struct HostEnvSanitizerTests {
|
||||
#expect(env["OPENCLAW_TEST"] == "1")
|
||||
}
|
||||
|
||||
@Test func sanitizeShellWrapperAllowsOnlyExplicitOverrideKeys() {
|
||||
@Test func `sanitize shell wrapper allows only explicit override keys`() {
|
||||
let env = HostEnvSanitizer.sanitize(
|
||||
overrides: [
|
||||
"LANG": "C",
|
||||
@@ -29,7 +29,7 @@ struct HostEnvSanitizerTests {
|
||||
#expect(env["PS4"] == nil)
|
||||
}
|
||||
|
||||
@Test func sanitizeNonShellWrapperKeepsRegularOverrides() {
|
||||
@Test func `sanitize non shell wrapper keeps regular overrides`() {
|
||||
let env = HostEnvSanitizer.sanitize(overrides: ["OPENCLAW_TOKEN": "secret"])
|
||||
#expect(env["OPENCLAW_TOKEN"] == "secret")
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct HoverHUDControllerTests {
|
||||
@Test func hoverHUDControllerPresentsAndDismisses() async {
|
||||
@Test func `hover HUD controller presents and dismisses`() async {
|
||||
let controller = HoverHUDController()
|
||||
controller.setSuppressed(false)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct InstancesSettingsSmokeTests {
|
||||
@Test func instancesSettingsBuildsBodyWithMultipleInstances() {
|
||||
@Test func `instances settings builds body with multiple instances`() {
|
||||
let store = InstancesStore(isPreview: true)
|
||||
store.statusMessage = "Loaded"
|
||||
store.instances = [
|
||||
@@ -53,7 +53,7 @@ struct InstancesSettingsSmokeTests {
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func instancesSettingsExercisesHelpers() {
|
||||
@Test func `instances settings exercises helpers`() {
|
||||
InstancesSettings.exerciseForTesting()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ import OpenClawProtocol
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct InstancesStoreTests {
|
||||
struct InstancesStoreTests {
|
||||
@Test
|
||||
@MainActor
|
||||
func presenceEventPayloadDecodesViaJSONEncoder() {
|
||||
func `presence event payload decodes via JSON encoder`() {
|
||||
// Build a payload that mirrors the gateway's presence event shape:
|
||||
// { "presence": [ PresenceEntry ] }
|
||||
let entry: [String: OpenClawProtocol.AnyCodable] = [
|
||||
|
||||
@@ -3,8 +3,8 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct LogLocatorTests {
|
||||
@Test func launchdGatewayLogPathEnsuresTmpDirExists() {
|
||||
struct LogLocatorTests {
|
||||
@Test func `launchd gateway log path ensures tmp dir exists`() {
|
||||
let fm = FileManager()
|
||||
let baseDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
||||
let logDir = baseDir.appendingPathComponent("openclaw-tests-\(UUID().uuidString)")
|
||||
|
||||
@@ -8,7 +8,7 @@ import Testing
|
||||
struct LowCoverageHelperTests {
|
||||
private typealias ProtoAnyCodable = OpenClawProtocol.AnyCodable
|
||||
|
||||
@Test func anyCodableHelperAccessors() throws {
|
||||
@Test func `any codable helper accessors`() throws {
|
||||
let payload: [String: ProtoAnyCodable] = [
|
||||
"title": ProtoAnyCodable("Hello"),
|
||||
"flag": ProtoAnyCodable(true),
|
||||
@@ -28,7 +28,7 @@ struct LowCoverageHelperTests {
|
||||
#expect((foundation?["title"] as? String) == "Hello")
|
||||
}
|
||||
|
||||
@Test func attributedStringStripsForegroundColor() {
|
||||
@Test func `attributed string strips foreground color`() {
|
||||
let text = NSMutableAttributedString(string: "Test")
|
||||
text.addAttribute(.foregroundColor, value: NSColor.red, range: NSRange(location: 0, length: 4))
|
||||
let stripped = text.strippingForegroundColor()
|
||||
@@ -36,29 +36,29 @@ struct LowCoverageHelperTests {
|
||||
#expect(color == nil)
|
||||
}
|
||||
|
||||
@Test func viewMetricsReduceWidth() {
|
||||
@Test func `view metrics reduce width`() {
|
||||
let value = ViewMetricsTesting.reduceWidth(current: 120, next: 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)
|
||||
#expect(result.success == false)
|
||||
#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)
|
||||
#expect(result.success == true)
|
||||
#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)
|
||||
#expect(result.timedOut == true)
|
||||
}
|
||||
|
||||
@Test func shellExecutorDrainsStdoutAndStderr() async {
|
||||
@Test func `shell executor drains stdout and stderr`() async {
|
||||
let script = """
|
||||
i=0
|
||||
while [ $i -lt 2000 ]; do
|
||||
@@ -77,7 +77,7 @@ struct LowCoverageHelperTests {
|
||||
#expect(result.stderr.contains("stderr-1999"))
|
||||
}
|
||||
|
||||
@Test func nodeInfoCodableRoundTrip() throws {
|
||||
@Test func `node info codable round trip`() throws {
|
||||
let info = NodeInfo(
|
||||
nodeId: "node-1",
|
||||
displayName: "Node One",
|
||||
@@ -100,7 +100,7 @@ struct LowCoverageHelperTests {
|
||||
#expect(decoded.isConnected == false)
|
||||
}
|
||||
|
||||
@Test @MainActor func presenceReporterHelpers() {
|
||||
@Test @MainActor func `presence reporter helpers`() {
|
||||
let summary = PresenceReporter._testComposePresenceSummary(mode: "local", reason: "test")
|
||||
#expect(summary.contains("mode local"))
|
||||
#expect(!PresenceReporter._testAppVersionString().isEmpty)
|
||||
@@ -109,7 +109,7 @@ struct LowCoverageHelperTests {
|
||||
_ = PresenceReporter._testPrimaryIPv4Address()
|
||||
}
|
||||
|
||||
@Test func portGuardianParsesListenersAndBuildsReports() {
|
||||
@Test func `port guardian parses listeners and builds reports`() {
|
||||
let output = """
|
||||
p123
|
||||
cnode
|
||||
@@ -139,7 +139,7 @@ struct LowCoverageHelperTests {
|
||||
#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
|
||||
.appendingPathComponent("canvas-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: root) }
|
||||
@@ -168,7 +168,7 @@ struct LowCoverageHelperTests {
|
||||
#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 menu = NSMenu()
|
||||
menu.minimumWidth = 280
|
||||
@@ -190,7 +190,7 @@ struct LowCoverageHelperTests {
|
||||
#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("bad/..") == "bad___")
|
||||
#expect(CanvasWindowController._testJSOptionalStringLiteral(nil) == "null")
|
||||
|
||||
@@ -7,7 +7,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct LowCoverageViewSmokeTests {
|
||||
@Test func contextMenuCardBuildsBody() {
|
||||
@Test func `context menu card builds body`() {
|
||||
let loading = ContextMenuCardView(rows: [], statusText: "Loading…", isLoading: true)
|
||||
_ = loading.body
|
||||
|
||||
@@ -18,14 +18,14 @@ struct LowCoverageViewSmokeTests {
|
||||
_ = withRows.body
|
||||
}
|
||||
|
||||
@Test func settingsToggleRowBuildsBody() {
|
||||
@Test func `settings toggle row builds body`() {
|
||||
var flag = false
|
||||
let binding = Binding(get: { flag }, set: { flag = $0 })
|
||||
let view = SettingsToggleRow(title: "Enable", subtitle: "Detail", binding: binding)
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func voiceWakeTestCardBuildsBodyAcrossStates() {
|
||||
@Test func `voice wake test card builds body across states`() {
|
||||
var state = VoiceWakeTestState.idle
|
||||
var isTesting = false
|
||||
let stateBinding = Binding(get: { state }, set: { state = $0 })
|
||||
@@ -44,7 +44,7 @@ struct LowCoverageViewSmokeTests {
|
||||
_ = VoiceWakeTestCard(testState: stateBinding, isTesting: testingBinding, onToggle: {}).body
|
||||
}
|
||||
|
||||
@Test func agentEventsWindowBuildsBodyWithEvent() {
|
||||
@Test func `agent events window builds body with event`() {
|
||||
AgentEventStore.shared.clear()
|
||||
let sample = ControlAgentEvent(
|
||||
runId: "run-1",
|
||||
@@ -58,7 +58,7 @@ struct LowCoverageViewSmokeTests {
|
||||
AgentEventStore.shared.clear()
|
||||
}
|
||||
|
||||
@Test func notifyOverlayPresentsAndDismisses() async {
|
||||
@Test func `notify overlay presents and dismisses`() async {
|
||||
let controller = NotifyOverlayController()
|
||||
controller.present(title: "Hello", body: "World", autoDismissAfter: 0)
|
||||
controller.present(title: "Updated", body: "Again", autoDismissAfter: 0)
|
||||
@@ -66,14 +66,14 @@ struct LowCoverageViewSmokeTests {
|
||||
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))
|
||||
_ = hosting.fittingSize
|
||||
hosting.rootView = VisualEffectView(material: .popover, emphasized: true)
|
||||
_ = hosting.fittingSize
|
||||
}
|
||||
|
||||
@Test func menuHostedItemHostsContent() {
|
||||
@Test func `menu hosted item hosts content`() {
|
||||
let view = MenuHostedItem(width: 240, rootView: AnyView(Text("Menu")))
|
||||
let hosting = NSHostingView(rootView: view)
|
||||
_ = hosting.fittingSize
|
||||
@@ -81,18 +81,18 @@ struct LowCoverageViewSmokeTests {
|
||||
_ = hosting.fittingSize
|
||||
}
|
||||
|
||||
@Test func dockIconManagerUpdatesVisibility() {
|
||||
@Test func `dock icon manager updates visibility`() {
|
||||
_ = NSApplication.shared
|
||||
UserDefaults.standard.set(false, forKey: showDockIconKey)
|
||||
DockIconManager.shared.updateDockVisibility()
|
||||
DockIconManager.shared.temporarilyShowDock()
|
||||
}
|
||||
|
||||
@Test func voiceWakeSettingsExercisesHelpers() {
|
||||
@Test func `voice wake settings exercises helpers`() {
|
||||
VoiceWakeSettings.exerciseForTesting()
|
||||
}
|
||||
|
||||
@Test func debugSettingsExercisesHelpers() async {
|
||||
@Test func `debug settings exercises helpers`() async {
|
||||
await DebugSettings.exerciseForTesting()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import OpenClawProtocol
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct MacGatewayChatTransportMappingTests {
|
||||
@Test func snapshotMapsToHealth() {
|
||||
struct MacGatewayChatTransportMappingTests {
|
||||
@Test func `snapshot maps to health`() {
|
||||
let snapshot = Snapshot(
|
||||
presence: [],
|
||||
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(
|
||||
type: "event",
|
||||
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 mapped = MacGatewayChatTransport.mapPushToTransportEvent(.event(frame))
|
||||
#expect({
|
||||
@@ -61,7 +61,7 @@ import Testing
|
||||
}())
|
||||
}
|
||||
|
||||
@Test func chatEventMapsToChat() {
|
||||
@Test func `chat event maps to chat`() {
|
||||
let payload = OpenClawProtocol.AnyCodable([
|
||||
"runId": OpenClawProtocol.AnyCodable("run-1"),
|
||||
"sessionKey": OpenClawProtocol.AnyCodable("main"),
|
||||
@@ -80,7 +80,7 @@ import Testing
|
||||
}
|
||||
}
|
||||
|
||||
@Test func unknownEventMapsToNil() {
|
||||
@Test func `unknown event maps to nil`() {
|
||||
let frame = EventFrame(
|
||||
type: "event",
|
||||
event: "unknown",
|
||||
@@ -91,7 +91,7 @@ import Testing
|
||||
#expect(mapped == nil)
|
||||
}
|
||||
|
||||
@Test func seqGapMapsToSeqGap() {
|
||||
@Test func `seq gap maps to seq gap`() {
|
||||
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.seqGap(expected: 1, received: 9))
|
||||
#expect({
|
||||
if case .seqGap = mapped { return true }
|
||||
|
||||
@@ -3,7 +3,7 @@ import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
struct MacNodeBrowserProxyTests {
|
||||
@Test func requestUsesBrowserControlEndpointAndWrapsResult() async throws {
|
||||
@Test func `request uses browser control endpoint and wraps result`() async throws {
|
||||
let proxy = MacNodeBrowserProxy(
|
||||
endpointProvider: {
|
||||
MacNodeBrowserProxy.Endpoint(
|
||||
|
||||
@@ -5,14 +5,14 @@ import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
struct MacNodeRuntimeTests {
|
||||
@Test func handleInvokeRejectsUnknownCommand() async {
|
||||
@Test func `handle invoke rejects unknown command`() async {
|
||||
let runtime = MacNodeRuntime()
|
||||
let response = await runtime.handleInvoke(
|
||||
BridgeInvokeRequest(id: "req-1", command: "unknown.command"))
|
||||
#expect(response.ok == false)
|
||||
}
|
||||
|
||||
@Test func handleInvokeRejectsEmptySystemRun() async throws {
|
||||
@Test func `handle invoke rejects empty system run`() async throws {
|
||||
let runtime = MacNodeRuntime()
|
||||
let params = OpenClawSystemRunParams(command: [])
|
||||
let json = try String(data: JSONEncoder().encode(params), encoding: .utf8)
|
||||
@@ -21,7 +21,7 @@ struct MacNodeRuntimeTests {
|
||||
#expect(response.ok == false)
|
||||
}
|
||||
|
||||
@Test func handleInvokeRejectsEmptySystemWhich() async throws {
|
||||
@Test func `handle invoke rejects empty system which`() async throws {
|
||||
let runtime = MacNodeRuntime()
|
||||
let params = OpenClawSystemWhichParams(bins: [])
|
||||
let json = try String(data: JSONEncoder().encode(params), encoding: .utf8)
|
||||
@@ -30,7 +30,7 @@ struct MacNodeRuntimeTests {
|
||||
#expect(response.ok == false)
|
||||
}
|
||||
|
||||
@Test func handleInvokeRejectsEmptyNotification() async throws {
|
||||
@Test func `handle invoke rejects empty notification`() async throws {
|
||||
let runtime = MacNodeRuntime()
|
||||
let params = OpenClawSystemNotifyParams(title: "", body: "")
|
||||
let json = try String(data: JSONEncoder().encode(params), encoding: .utf8)
|
||||
@@ -39,7 +39,7 @@ struct MacNodeRuntimeTests {
|
||||
#expect(response.ok == false)
|
||||
}
|
||||
|
||||
@Test func handleInvokeCameraListRequiresEnabledCamera() async {
|
||||
@Test func `handle invoke camera list requires enabled camera`() async {
|
||||
await TestIsolation.withUserDefaultsValues([cameraEnabledKey: false]) {
|
||||
let runtime = MacNodeRuntime()
|
||||
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
|
||||
final class FakeMainActorServices: MacNodeRuntimeMainActorServices, @unchecked Sendable {
|
||||
func recordScreen(
|
||||
@@ -101,20 +101,23 @@ struct MacNodeRuntimeTests {
|
||||
#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
|
||||
#expect(paramsJSON?.contains("/tabs") == true)
|
||||
return #"{"result":{"ok":true,"tabs":[{"id":"tab-1"}]}}"#
|
||||
})
|
||||
let paramsJSON = #"{"method":"GET","path":"/tabs","timeoutMs":2500}"#
|
||||
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.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()
|
||||
try await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
|
||||
try JSONSerialization.data(withJSONObject: ["browser": ["enabled": false]])
|
||||
|
||||
@@ -6,7 +6,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct MasterDiscoveryMenuSmokeTests {
|
||||
@Test func inlineListBuildsBodyWhenEmpty() {
|
||||
@Test func `inline list builds body when empty`() {
|
||||
let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName)
|
||||
discovery.statusText = "Searching…"
|
||||
discovery.gateways = []
|
||||
@@ -20,7 +20,7 @@ struct MasterDiscoveryMenuSmokeTests {
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func inlineListBuildsBodyWithMasterAndSelection() {
|
||||
@Test func `inline list builds body with master and selection`() {
|
||||
let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName)
|
||||
discovery.statusText = "Found 1"
|
||||
discovery.gateways = [
|
||||
@@ -46,7 +46,7 @@ struct MasterDiscoveryMenuSmokeTests {
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func menuBuildsBodyWithMasters() {
|
||||
@Test func `menu builds body with masters`() {
|
||||
let discovery = GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName)
|
||||
discovery.statusText = "Found 2"
|
||||
discovery.gateways = [
|
||||
|
||||
@@ -5,28 +5,28 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct MenuContentSmokeTests {
|
||||
@Test func menuContentBuildsBodyLocalMode() {
|
||||
@Test func `menu content builds body local mode`() {
|
||||
let state = AppState(preview: true)
|
||||
state.connectionMode = .local
|
||||
let view = MenuContent(state: state, updater: nil)
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func menuContentBuildsBodyRemoteMode() {
|
||||
@Test func `menu content builds body remote mode`() {
|
||||
let state = AppState(preview: true)
|
||||
state.connectionMode = .remote
|
||||
let view = MenuContent(state: state, updater: nil)
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func menuContentBuildsBodyUnconfiguredMode() {
|
||||
@Test func `menu content builds body unconfigured mode`() {
|
||||
let state = AppState(preview: true)
|
||||
state.connectionMode = .unconfigured
|
||||
let view = MenuContent(state: state, updater: nil)
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func menuContentBuildsBodyWithDebugAndCanvas() {
|
||||
@Test func `menu content builds body with debug and canvas`() {
|
||||
let state = AppState(preview: true)
|
||||
state.connectionMode = .local
|
||||
state.debugPaneEnabled = true
|
||||
|
||||
@@ -5,7 +5,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct MenuSessionsInjectorTests {
|
||||
@Test func injectsDisconnectedMessage() {
|
||||
@Test func `injects disconnected message`() {
|
||||
let injector = MenuSessionsInjector()
|
||||
injector.setTestingControlChannelConnected(false)
|
||||
injector.setTestingSnapshot(nil, errorText: nil)
|
||||
@@ -19,7 +19,7 @@ struct MenuSessionsInjectorTests {
|
||||
#expect(menu.items.contains { $0.tag == 9_415_557 })
|
||||
}
|
||||
|
||||
@Test func injectsSessionRows() {
|
||||
@Test func `injects session rows`() {
|
||||
let injector = MenuSessionsInjector()
|
||||
injector.setTestingControlChannelConnected(true)
|
||||
|
||||
@@ -94,7 +94,7 @@ struct MenuSessionsInjectorTests {
|
||||
#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()
|
||||
injector.setTestingControlChannelConnected(true)
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite
|
||||
struct ModelCatalogLoaderTests {
|
||||
@Test
|
||||
func loadParsesModelsFromTypeScriptAndSorts() async throws {
|
||||
func `load parses models from type script and sorts`() async throws {
|
||||
let src = """
|
||||
export const MODELS = {
|
||||
openai: {
|
||||
@@ -40,7 +39,7 @@ struct ModelCatalogLoaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func loadWithNoExportReturnsEmptyChoices() async throws {
|
||||
func `load with no export returns empty choices`() async throws {
|
||||
let src = "const NOPE = 1;"
|
||||
let tmp = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("models-\(UUID().uuidString).ts")
|
||||
|
||||
@@ -4,7 +4,7 @@ import Testing
|
||||
|
||||
@Suite(.serialized)
|
||||
struct NixModeStableSuiteTests {
|
||||
@Test func resolvesFromStableSuiteForAppBundles() throws {
|
||||
@Test func `resolves from stable suite for app bundles`() throws {
|
||||
let suite = try #require(UserDefaults(suiteName: launchdLabel))
|
||||
let key = "openclaw.nixMode"
|
||||
let prev = suite.object(forKey: key)
|
||||
@@ -25,7 +25,7 @@ struct NixModeStableSuiteTests {
|
||||
#expect(resolved)
|
||||
}
|
||||
|
||||
@Test func ignoresStableSuiteOutsideAppBundles() throws {
|
||||
@Test func `ignores stable suite outside app bundles`() throws {
|
||||
let suite = try #require(UserDefaults(suiteName: launchdLabel))
|
||||
let key = "openclaw.nixMode"
|
||||
let prev = suite.object(forKey: key)
|
||||
|
||||
@@ -2,8 +2,8 @@ import Foundation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct NodeManagerPathsTests {
|
||||
@Test func fnmNodeBinsPreferNewestInstalledVersion() throws {
|
||||
struct NodeManagerPathsTests {
|
||||
@Test func `fnm node bins prefer newest installed version`() throws {
|
||||
let home = try makeTempDirForTests()
|
||||
|
||||
let v20Bin = home
|
||||
@@ -18,7 +18,7 @@ import Testing
|
||||
#expect(bins.contains(v20Bin.deletingLastPathComponent().path))
|
||||
}
|
||||
|
||||
@Test func ignoresEntriesWithoutNodeExecutable() throws {
|
||||
@Test func `ignores entries without node executable`() throws {
|
||||
let home = try makeTempDirForTests()
|
||||
let missingNodeBin = home
|
||||
.appendingPathComponent(".local/share/fnm/node-versions/v99.0.0/installation/bin")
|
||||
|
||||
@@ -4,7 +4,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct NodePairingApprovalPrompterTests {
|
||||
@Test func nodePairingApprovalPrompterExercises() async {
|
||||
@Test func `node pairing approval prompter exercises`() async {
|
||||
await NodePairingApprovalPrompter.exerciseForTesting()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite struct NodePairingReconcilePolicyTests {
|
||||
@Test func policyPollsOnlyWhenActive() {
|
||||
struct NodePairingReconcilePolicyTests {
|
||||
@Test func `policy polls only when active`() {
|
||||
#expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 0, isPresenting: false) == false)
|
||||
#expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 1, isPresenting: false))
|
||||
#expect(NodePairingReconcilePolicy.shouldPoll(pendingCount: 0, isPresenting: true))
|
||||
}
|
||||
|
||||
@Test func policyUsesSlowSafetyInterval() {
|
||||
@Test func `policy uses slow safety interval`() {
|
||||
#expect(NodePairingReconcilePolicy.activeIntervalMs >= 10000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct OnboardingCoverageTests {
|
||||
@Test func exerciseOnboardingPages() {
|
||||
@Test func `exercise onboarding pages`() {
|
||||
OnboardingView.exerciseForTesting()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct OnboardingViewSmokeTests {
|
||||
@Test func onboardingViewBuildsBody() {
|
||||
@Test func `onboarding view builds body`() {
|
||||
let state = AppState(preview: true)
|
||||
let view = OnboardingView(
|
||||
state: state,
|
||||
@@ -16,18 +16,18 @@ struct OnboardingViewSmokeTests {
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func pageOrderOmitsWorkspaceAndIdentitySteps() {
|
||||
@Test func `page order omits workspace and identity steps`() {
|
||||
let order = OnboardingView.pageOrder(for: .local, showOnboardingChat: false)
|
||||
#expect(!order.contains(7))
|
||||
#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)
|
||||
#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
|
||||
.appendingPathComponent("openclaw-config-\(UUID().uuidString)")
|
||||
.appendingPathComponent("openclaw.json")
|
||||
|
||||
@@ -8,7 +8,7 @@ private typealias ProtoAnyCodable = OpenClawProtocol.AnyCodable
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct OnboardingWizardStepViewTests {
|
||||
@Test func noteStepBuilds() {
|
||||
@Test func `note step builds`() {
|
||||
let step = WizardStep(
|
||||
id: "step-1",
|
||||
type: ProtoAnyCodable("note"),
|
||||
@@ -23,7 +23,7 @@ struct OnboardingWizardStepViewTests {
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func selectStepBuilds() {
|
||||
@Test func `select step builds`() {
|
||||
let options: [[String: ProtoAnyCodable]] = [
|
||||
["value": ProtoAnyCodable("local"), "label": ProtoAnyCodable("Local"), "hint": ProtoAnyCodable("This Mac")],
|
||||
["value": ProtoAnyCodable("remote"), "label": ProtoAnyCodable("Remote")],
|
||||
|
||||
@@ -12,8 +12,8 @@ struct OpenClawConfigFileTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func configPathRespectsEnvOverride() async {
|
||||
let override = makeConfigOverridePath()
|
||||
func `config path respects env override`() async {
|
||||
let override = self.makeConfigOverridePath()
|
||||
|
||||
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
|
||||
#expect(OpenClawConfigFile.url().path == override)
|
||||
@@ -22,8 +22,8 @@ struct OpenClawConfigFileTests {
|
||||
|
||||
@MainActor
|
||||
@Test
|
||||
func remoteGatewayPortParsesAndMatchesHost() async {
|
||||
let override = makeConfigOverridePath()
|
||||
func `remote gateway port parses and matches host`() async {
|
||||
let override = self.makeConfigOverridePath()
|
||||
|
||||
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
|
||||
OpenClawConfigFile.saveDict([
|
||||
@@ -42,8 +42,8 @@ struct OpenClawConfigFileTests {
|
||||
|
||||
@MainActor
|
||||
@Test
|
||||
func setRemoteGatewayUrlPreservesScheme() async {
|
||||
let override = makeConfigOverridePath()
|
||||
func `set remote gateway url preserves scheme`() async {
|
||||
let override = self.makeConfigOverridePath()
|
||||
|
||||
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
|
||||
OpenClawConfigFile.saveDict([
|
||||
@@ -62,8 +62,8 @@ struct OpenClawConfigFileTests {
|
||||
|
||||
@MainActor
|
||||
@Test
|
||||
func clearRemoteGatewayUrlRemovesOnlyUrlField() async {
|
||||
let override = makeConfigOverridePath()
|
||||
func `clear remote gateway url removes only url field`() async {
|
||||
let override = self.makeConfigOverridePath()
|
||||
|
||||
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
|
||||
OpenClawConfigFile.saveDict([
|
||||
@@ -83,7 +83,7 @@ struct OpenClawConfigFileTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func stateDirOverrideSetsConfigPath() async {
|
||||
func `state dir override sets config path`() async {
|
||||
let dir = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-state-\(UUID().uuidString)", isDirectory: true)
|
||||
.path
|
||||
@@ -99,7 +99,7 @@ struct OpenClawConfigFileTests {
|
||||
|
||||
@MainActor
|
||||
@Test
|
||||
func saveDictAppendsConfigAuditLog() async throws {
|
||||
func `save dict appends config audit log`() async throws {
|
||||
let stateDir = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-state-\(UUID().uuidString)", isDirectory: true)
|
||||
let configPath = stateDir.appendingPathComponent("openclaw.json")
|
||||
|
||||
@@ -2,16 +2,15 @@ import CoreLocation
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite("PermissionManager Location")
|
||||
struct PermissionManagerLocationTests {
|
||||
@Test("authorizedAlways counts for both modes")
|
||||
func authorizedAlwaysCountsForBothModes() {
|
||||
@Test
|
||||
func `authorizedAlways counts for both modes`() {
|
||||
#expect(PermissionManager.isLocationAuthorized(status: .authorizedAlways, requireAlways: false))
|
||||
#expect(PermissionManager.isLocationAuthorized(status: .authorizedAlways, requireAlways: true))
|
||||
}
|
||||
|
||||
@Test("other statuses not authorized")
|
||||
func otherStatusesNotAuthorized() {
|
||||
@Test
|
||||
func `other statuses not authorized`() {
|
||||
#expect(!PermissionManager.isLocationAuthorized(status: .notDetermined, requireAlways: false))
|
||||
#expect(!PermissionManager.isLocationAuthorized(status: .denied, requireAlways: false))
|
||||
#expect(!PermissionManager.isLocationAuthorized(status: .restricted, requireAlways: false))
|
||||
|
||||
@@ -6,31 +6,31 @@ import Testing
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct PermissionManagerTests {
|
||||
@Test func voiceWakePermissionHelpersMatchStatus() async {
|
||||
@Test func `voice wake permission helpers match status`() async {
|
||||
let direct = PermissionManager.voiceWakePermissionsGranted()
|
||||
let ensured = await PermissionManager.ensureVoiceWakePermissions(interactive: false)
|
||||
#expect(ensured == direct)
|
||||
}
|
||||
|
||||
@Test func statusCanQueryNonInteractiveCaps() async {
|
||||
@Test func `status can query non interactive caps`() async {
|
||||
let caps: [Capability] = [.microphone, .speechRecognition, .screenRecording]
|
||||
let status = await PermissionManager.status(caps)
|
||||
#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 ensured = await PermissionManager.ensure(caps, interactive: false)
|
||||
#expect(ensured.keys.count == caps.count)
|
||||
}
|
||||
|
||||
@Test func locationStatusMatchesAuthorizationAlways() async {
|
||||
@Test func `location status matches authorization always`() async {
|
||||
let status = CLLocationManager().authorizationStatus
|
||||
let results = await PermissionManager.status([.location])
|
||||
#expect(results[.location] == (status == .authorizedAlways))
|
||||
}
|
||||
|
||||
@Test func ensureLocationNonInteractiveMatchesAuthorizationAlways() async {
|
||||
@Test func `ensure location non interactive matches authorization always`() async {
|
||||
let status = CLLocationManager().authorizationStatus
|
||||
let ensured = await PermissionManager.ensure([.location], interactive: false)
|
||||
#expect(ensured[.location] == (status == .authorizedAlways))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Testing
|
||||
|
||||
@Suite struct PlaceholderTests {
|
||||
struct PlaceholderTests {
|
||||
@Test func placeholder() {
|
||||
#expect(true)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user