Files
wristivo/Packages/WristivoKit/Tests/RemoteCoreTests/WatchSessionTests.swift
T

89 lines
4.9 KiB
Swift

import Foundation
import Testing
@testable import RemoteCore
@Test func wireDatesUseUnixMillisecondsForGrantAndCommand() throws {
let session = UUID(), tv = UUID()
let issued = Date(timeIntervalSince1970: 1_700_000_000.125)
let grant = RemoteSessionGrant(sessionID: session, televisionID: tv, issuedAt: issued)
let data = try RelayJSON.encode(grant)
let object = try #require(JSONSerialization.jsonObject(with: data) as? [String: Any])
#expect(object["issuedAt"] as? Double == 1_700_000_000_125)
#expect(try RelayJSON.decode(RemoteSessionGrant.self, from: data) == grant)
let command = CommandEnvelope(sessionID: session, televisionID: tv, sequence: 1,
issuedAt: issued, expiresAt: issued.addingTimeInterval(0.5), command: .right)
let commandData = try RelayJSON.encode(command)
let commandObject = try #require(JSONSerialization.jsonObject(with: commandData) as? [String: Any])
#expect(commandObject["expiresAt"] as? Double == 1_700_000_000_625)
#expect(try RelayJSON.decode(CommandEnvelope.self, from: commandData) == command)
}
@Test func controlPayloadsRoundTripAndFailureIsAnError() throws {
let session = UUID()
let begin = BeginRemoteSessionRequest()
let end = EndRemoteSessionRequest(sessionID: session)
let ended = RemoteSessionEnded(sessionID: session)
let failure = RelayFailure(code: .sessionInvalidated)
#expect(try RelayJSON.decode(BeginRemoteSessionRequest.self, from: RelayJSON.encode(begin)) == begin)
#expect(try RelayJSON.decode(EndRemoteSessionRequest.self, from: RelayJSON.encode(end)) == end)
#expect(try RelayJSON.decode(RemoteSessionEnded.self, from: RelayJSON.encode(ended)) == ended)
#expect(try RelayJSON.decode(RelayFailure.self, from: RelayJSON.encode(failure)) == failure)
let result: Result<RemoteSessionGrant, RelayFailure> = .failure(failure)
#expect(throws: failure) { try result.get() }
}
@Test func malformedOrUnsupportedControlPayloadsFailClosed() throws {
#expect(throws: RelayContractError.unsupportedSchema(2)) {
try RelayJSON.decode(BeginRemoteSessionRequest.self, from: Data(#"{"schemaVersion":2}"#.utf8))
}
#expect(throws: (any Error).self) {
try RelayJSON.decode(EndRemoteSessionRequest.self, from: Data(#"{"schemaVersion":1,"sessionID":"bad"}"#.utf8))
}
#expect(throws: (any Error).self) {
try RelayJSON.decode(BeginRemoteSessionRequest.self, from: Data(#"{"schemaVersion":true}"#.utf8))
}
#expect(throws: RelayContractError.unsupportedSchema(9)) {
try RelayJSON.encode(RemoteSessionEnded(sessionID: UUID(), schemaVersion: 9))
}
}
@Test func invalidGrantLifetimeRejectedOnEncodeAndDecode() throws {
for lifetime: UInt64 in [0, 1_001, .max] {
let invalid = RemoteSessionGrant(sessionID: UUID(), televisionID: UUID(),
issuedAt: Date(timeIntervalSince1970: 1_000), maximumCommandLifetimeMilliseconds: lifetime)
#expect(throws: RelayContractError.invalidLifetime) { try RelayJSON.encode(invalid) }
// Bypass the validating encoder to simulate an untrusted peer.
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .millisecondsSince1970
let raw = try encoder.encode(invalid)
#expect(throws: RelayContractError.invalidLifetime) {
try RelayJSON.decode(RemoteSessionGrant.self, from: raw)
}
}
let invalidDate = RemoteSessionGrant(sessionID: UUID(), televisionID: UUID(),
issuedAt: Date(timeIntervalSince1970: .infinity))
#expect(throws: RelayContractError.invalidTimestamp) { try RelayJSON.encode(invalidDate) }
}
@Test func byteLimitIsCheckedBeforeDecodingAndAtEncoding() throws {
// JSON string quotes use 2 bytes. Exactly 4096 bytes is permitted.
let boundary = String(repeating: "a", count: RelayWire.maximumPayloadBytes - 2)
let encoded = try RelayJSON.encode(boundary)
#expect(encoded.count == RelayWire.maximumPayloadBytes)
#expect(try RelayJSON.decode(String.self, from: encoded) == boundary)
#expect(throws: RelayContractError.payloadTooLarge) { try RelayJSON.encode(boundary + "a") }
#expect(throws: RelayContractError.payloadTooLarge) {
try RelayJSON.decode(BeginRemoteSessionRequest.self, from: Data(repeating: 0, count: 4_097))
}
}
@Test func commandDecodeDoesNotConsumeOrReplaceAdmission() throws {
let session = UUID(), tv = UUID(), now = Date(timeIntervalSince1970: 1_000)
let command = CommandEnvelope(sessionID: session, televisionID: tv, sequence: 1,
issuedAt: now, expiresAt: now.addingTimeInterval(0.5), command: .left)
let data = try RelayJSON.encode(command)
var admission = CommandAdmission(sessionID: session, televisionID: tv)
#expect(admission.admit(try RelayJSON.decode(CommandEnvelope.self, from: data), now: now) == .accepted)
#expect(admission.admit(try RelayJSON.decode(CommandEnvelope.self, from: data), now: now) == .rejected(.duplicate))
}