From 974ab5a8ddf25eb5e3e1857731b79d4424a10d77 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 14 Dec 2025 02:32:50 +0000 Subject: [PATCH] test(ios): add bridge session + keychain suites --- apps/ios/Tests/BridgeSessionTests.swift | 48 ++++++++++++++++++++++++ apps/ios/Tests/DeepLinkParserTests.swift | 33 ++++++++++++++++ apps/ios/Tests/KeychainStoreTests.swift | 22 +++++++++++ 3 files changed, 103 insertions(+) create mode 100644 apps/ios/Tests/BridgeSessionTests.swift create mode 100644 apps/ios/Tests/KeychainStoreTests.swift diff --git a/apps/ios/Tests/BridgeSessionTests.swift b/apps/ios/Tests/BridgeSessionTests.swift new file mode 100644 index 000000000..4c13ea705 --- /dev/null +++ b/apps/ios/Tests/BridgeSessionTests.swift @@ -0,0 +1,48 @@ +import Foundation +import Testing +@testable import Clawdis + +@Suite struct BridgeSessionTests { + @Test func initialStateIsIdle() async { + let session = BridgeSession() + #expect(await session.state == .idle) + } + + @Test func requestFailsWhenNotConnected() async { + let session = BridgeSession() + + do { + _ = try await session.request(method: "health", paramsJSON: nil, timeoutSeconds: 1) + Issue.record("Expected request to throw when not connected") + } catch let error as NSError { + #expect(error.domain == "Bridge") + #expect(error.code == 11) + } + } + + @Test func sendEventFailsWhenNotConnected() async { + let session = BridgeSession() + + do { + try await session.sendEvent(event: "tick", payloadJSON: nil) + Issue.record("Expected sendEvent to throw when not connected") + } catch let error as NSError { + #expect(error.domain == "Bridge") + #expect(error.code == 10) + } + } + + @Test func disconnectFinishesServerEventStreams() async throws { + let session = BridgeSession() + let stream = await session.subscribeServerEvents(bufferingNewest: 1) + + let consumer = Task { @Sendable in + for await _ in stream {} + } + + await session.disconnect() + + _ = await consumer.result + #expect(await session.state == .idle) + } +} diff --git a/apps/ios/Tests/DeepLinkParserTests.swift b/apps/ios/Tests/DeepLinkParserTests.swift index 35615d56a..d16ba72fe 100644 --- a/apps/ios/Tests/DeepLinkParserTests.swift +++ b/apps/ios/Tests/DeepLinkParserTests.swift @@ -3,6 +3,24 @@ import Foundation import Testing @Suite struct DeepLinkParserTests { + @Test func parseRejectsUnknownHost() { + let url = URL(string: "clawdis://nope?message=hi")! + #expect(DeepLinkParser.parse(url) == nil) + } + + @Test func parseHostIsCaseInsensitive() { + let url = URL(string: "clawdis://AGENT?message=Hello")! + #expect(DeepLinkParser.parse(url) == .agent(.init( + message: "Hello", + sessionKey: nil, + thinking: nil, + deliver: false, + to: nil, + channel: nil, + timeoutSeconds: nil, + key: nil))) + } + @Test func parseRejectsNonClawdisScheme() { let url = URL(string: "https://example.com/agent?message=hi")! #expect(DeepLinkParser.parse(url) == nil) @@ -28,6 +46,21 @@ import Testing key: nil))) } + @Test func parseAgentLinkParsesTargetRoutingFields() { + let url = URL(string: "clawdis://agent?message=Hello%20World&deliver=1&to=%2B15551234567&channel=whatsapp&key=secret")! + #expect( + DeepLinkParser.parse(url) == .agent( + .init( + message: "Hello World", + sessionKey: nil, + thinking: nil, + deliver: true, + to: "+15551234567", + channel: "whatsapp", + timeoutSeconds: nil, + key: "secret"))) + } + @Test func parseRejectsNegativeTimeoutSeconds() { let url = URL(string: "clawdis://agent?message=Hello&timeoutSeconds=-1")! #expect(DeepLinkParser.parse(url) == .agent(.init( diff --git a/apps/ios/Tests/KeychainStoreTests.swift b/apps/ios/Tests/KeychainStoreTests.swift new file mode 100644 index 000000000..06040f7e5 --- /dev/null +++ b/apps/ios/Tests/KeychainStoreTests.swift @@ -0,0 +1,22 @@ +import Foundation +import Testing +@testable import Clawdis + +@Suite struct KeychainStoreTests { + @Test func saveLoadUpdateDeleteRoundTrip() { + let service = "com.steipete.clawdis.tests.\(UUID().uuidString)" + let account = "value" + + #expect(KeychainStore.delete(service: service, account: account)) + #expect(KeychainStore.loadString(service: service, account: account) == nil) + + #expect(KeychainStore.saveString("first", service: service, account: account)) + #expect(KeychainStore.loadString(service: service, account: account) == "first") + + #expect(KeychainStore.saveString("second", service: service, account: account)) + #expect(KeychainStore.loadString(service: service, account: account) == "second") + + #expect(KeychainStore.delete(service: service, account: account)) + #expect(KeychainStore.loadString(service: service, account: account) == nil) + } +}