import Foundation import Testing import RemoteCore import WatchBridgeCommon import WatchBridgePhone @MainActor private final class FakePhoneSession: RelaySessionSending { var activationState: RelayActivationState = .notActivated var isReachable = true var sent: [[String: Any]] = [] var reply: (([String: Any]) -> Void)? var error: ((Error) -> Void)? func activate() { activationState = .activated } func sendMessage(_ message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void, errorHandler: @escaping (Error) -> Void) { sent.append(message) reply = replyHandler error = errorHandler } func updateApplicationContext(_ context: [String: Any]) throws {} func answer(_ value: [String: Any]) { reply?(value) } } private actor RecordingPhoneCoordinator: PhoneCommandCoordinating { var activated: [WatchPeerGeneration] = [] var deactivated: [WatchPeerGeneration] = [] var commands: [CommandEnvelope] = [] let sessionID = UUID() let televisionID = UUID() func watchBridgeDidActivate(peer: WatchPeerGeneration) { activated.append(peer) } func watchBridgeDidDeactivate(peer: WatchPeerGeneration) { deactivated.append(peer) } func beginWatchSession(_ request: BeginRemoteSessionRequest, peer: WatchPeerGeneration) -> Result { .success(RemoteSessionGrant(sessionID: sessionID, televisionID: televisionID, issuedAt: Date(timeIntervalSince1970: 1_700_000_000))) } func endWatchSession(_ request: EndRemoteSessionRequest, peer: WatchPeerGeneration) -> Result { .success(RemoteSessionEnded(sessionID: request.sessionID)) } func submitWatchCommand(_ envelope: CommandEnvelope, peer: WatchPeerGeneration) -> CommandReceipt { commands.append(envelope) return CommandReceipt(id: envelope.id, status: .sentToTransport) } } @MainActor @Test func phoneWaitsForActorActivationAndForwardsCommandWithoutAdmission() async throws { let session = FakePhoneSession() let actor = RecordingPhoneCoordinator() let bridge = PhoneRelayBridge(session: session, dependency: PhoneRelayDependency(coordinator: actor)) bridge.activate() bridge.activationDidComplete(error: nil) await Task.yield() #expect({ if case .ready = bridge.state { return true } return false }()) let begin = try RelayContainer(kind: .beginSession, value: BeginRemoteSessionRequest()).dictionary() bridge.receive(message: begin) { _ in } await Task.yield() #expect(session.sent.isEmpty) let peerActivated = await actor.activated #expect(peerActivated.count == 1) bridge.receive(message: begin) { _ in } await Task.yield() #expect(session.sent.isEmpty) } @MainActor @Test func phoneRejectsMalformedAndWrongDirectionRequests() async throws { let session = FakePhoneSession() let actor = RecordingPhoneCoordinator() let bridge = PhoneRelayBridge(session: session, dependency: PhoneRelayDependency(coordinator: actor)) bridge.activate(); bridge.activationDidComplete(error: nil) await Task.yield() var replies = 0 bridge.receive(message: ["schemaVersion": true, "kind": "command", "payload": Data()]) { _ in replies += 1 } await Task.yield() #expect(replies == 1) }