openclaw/apps/macos/Tests/OpenClawIPCTests/ChatTranscriptCacheIdentityTests.swift

173 lines
7 KiB
Swift

import Foundation
import Testing
@testable import OpenClaw
struct ChatTranscriptCacheIdentityTests {
private static let defaultStateDir = URL(fileURLWithPath: "/Users/tester/.openclaw", isDirectory: true)
@Test func `unconfigured mode has no cache identity`() {
let id = MacChatTranscriptCache.gatewayID(
mode: .unconfigured,
localStateDir: Self.defaultStateDir,
remoteTransport: .ssh,
directURL: nil,
sshTarget: "user@host",
sshRemotePort: 18789)
#expect(id == nil)
}
@Test @MainActor func `windows share one outbox owner per gateway`() {
let databaseURL = FileManager.default.temporaryDirectory
.appendingPathComponent("openclaw-cache-owner-\(UUID().uuidString).sqlite")
let gatewayID = "gw-shared-\(UUID().uuidString)"
let first = MacChatTranscriptCache.store(databaseURL: databaseURL, gatewayID: gatewayID)
let second = MacChatTranscriptCache.store(databaseURL: databaseURL, gatewayID: gatewayID)
#expect(first === second)
}
@Test func `local mode keys on state dir so profiles never collide`() {
let defaultProfile = MacChatTranscriptCache.gatewayID(
mode: .local,
localStateDir: Self.defaultStateDir,
remoteTransport: .ssh,
directURL: nil,
sshTarget: "",
sshRemotePort: 18789)
let devProfile = MacChatTranscriptCache.gatewayID(
mode: .local,
localStateDir: URL(fileURLWithPath: "/Users/tester/.openclaw-dev", isDirectory: true),
remoteTransport: .ssh,
directURL: nil,
sshTarget: "",
sshRemotePort: 18789)
#expect(defaultProfile == "local:/Users/tester/.openclaw")
#expect(devProfile == "local:/Users/tester/.openclaw-dev")
#expect(defaultProfile != devProfile)
}
@Test func `local state dir aliases resolve to one identity`() throws {
// macOS tmp lives behind a /var -> /private/var symlink; both spellings
// of the same state dir must map to a single cache scope.
let canonical = try FileManager.default.temporaryDirectory
.appendingPathComponent("openclaw-cache-identity-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: canonical, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: canonical) }
let resolved = canonical.resolvingSymlinksInPath()
let viaSymlink = MacChatTranscriptCache.gatewayID(
mode: .local,
localStateDir: canonical,
remoteTransport: .ssh,
directURL: nil,
sshTarget: "",
sshRemotePort: 18789)
let viaResolved = MacChatTranscriptCache.gatewayID(
mode: .local,
localStateDir: resolved,
remoteTransport: .ssh,
directURL: nil,
sshTarget: "",
sshRemotePort: 18789)
#expect(viaSymlink == viaResolved)
}
@Test func `remote direct keys on the full canonical url`() {
let explicitPort = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .direct,
directURL: URL(string: "ws://Gateway.Example.com:9001"),
sshTarget: "",
sshRemotePort: 18789)
#expect(explicitPort == "remote:ws://gateway.example.com:9001")
let defaultWSSPort = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .direct,
directURL: URL(string: "wss://gw.example.com"),
sshTarget: "",
sshRemotePort: 18789)
#expect(defaultWSSPort == "remote:wss://gw.example.com:443")
// One origin can route to several gateways by path; each path is its
// own cache scope.
let teamA = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .direct,
directURL: URL(string: "wss://gw.example.com/team-a"),
sshTarget: "",
sshRemotePort: 18789)
let teamB = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .direct,
directURL: URL(string: "wss://gw.example.com/team-b"),
sshTarget: "",
sshRemotePort: 18789)
#expect(teamA == "remote:wss://gw.example.com:443/team-a")
#expect(teamB == "remote:wss://gw.example.com:443/team-b")
#expect(teamA != teamB)
// Percent-encoded path spelling is part of the request URL and must
// not collapse into the decoded form's scope.
let encodedPath = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .direct,
directURL: URL(string: "wss://gw.example.com/team%2Fa"),
sshTarget: "",
sshRemotePort: 18789)
let decodedPath = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .direct,
directURL: URL(string: "wss://gw.example.com/team/a"),
sshTarget: "",
sshRemotePort: 18789)
#expect(encodedPath == "remote:wss://gw.example.com:443/team%2Fa")
#expect(encodedPath != decodedPath)
let missingURL = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .direct,
directURL: nil,
sshTarget: "",
sshRemotePort: 18789)
#expect(missingURL == nil)
}
@Test func `remote ssh keys on the ssh target and remote gateway port`() {
let id = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .ssh,
directURL: nil,
sshTarget: " user@studio.local ",
sshRemotePort: 18789)
#expect(id == "ssh:user@studio.local:18789")
// One SSH target can front several gateways on different remote ports;
// each must get its own cache scope.
let otherGateway = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .ssh,
directURL: nil,
sshTarget: "user@studio.local",
sshRemotePort: 19001)
#expect(otherGateway == "ssh:user@studio.local:19001")
#expect(otherGateway != id)
let missingTarget = MacChatTranscriptCache.gatewayID(
mode: .remote,
localStateDir: Self.defaultStateDir,
remoteTransport: .ssh,
directURL: nil,
sshTarget: " ",
sshRemotePort: 18789)
#expect(missingTarget == nil)
}
}