Create Wristivo iPhone and Apple Watch foundation
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import RemoteCore
|
||||
|
||||
private let now = Date(timeIntervalSince1970: 1_000)
|
||||
|
||||
private func envelope(session: UUID, tv: UUID, sequence: UInt64 = 1,
|
||||
id: UUID = UUID(), issued: Date = now,
|
||||
expires: Date = now.addingTimeInterval(0.5),
|
||||
version: Int = 1) -> CommandEnvelope {
|
||||
CommandEnvelope(sessionID: session, televisionID: tv, sequence: sequence,
|
||||
issuedAt: issued, expiresAt: expires, command: .right,
|
||||
id: id, schemaVersion: version)
|
||||
}
|
||||
|
||||
@Test func admitsExactlyOnceAndRequiresIncreasingSequence() {
|
||||
let session = UUID(), tv = UUID()
|
||||
var gate = CommandAdmission(sessionID: session, televisionID: tv)
|
||||
let first = envelope(session: session, tv: tv)
|
||||
#expect(gate.admit(first, now: now) == .accepted)
|
||||
#expect(gate.admit(first, now: now) == .rejected(.duplicate))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv), now: now) == .rejected(.outOfOrder))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, sequence: 2), now: now) == .accepted)
|
||||
}
|
||||
|
||||
@Test func rejectsWrongRoutingAndUnsupportedSchemaWithoutConsumingSequence() {
|
||||
let session = UUID(), tv = UUID()
|
||||
var gate = CommandAdmission(sessionID: session, televisionID: tv)
|
||||
#expect(gate.admit(envelope(session: UUID(), tv: tv), now: now) == .rejected(.wrongSession))
|
||||
#expect(gate.admit(envelope(session: session, tv: UUID()), now: now) == .rejected(.wrongTelevision))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, version: 2), now: now) == .rejected(.unsupportedSchema))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv), now: now) == .accepted)
|
||||
}
|
||||
|
||||
@Test func rejectsStaleExpiredFutureAndInvalidLifetime() {
|
||||
let session = UUID(), tv = UUID()
|
||||
var gate = CommandAdmission(sessionID: session, televisionID: tv)
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, issued: now.addingTimeInterval(-2), expires: now.addingTimeInterval(-1.5)), now: now) == .rejected(.stale))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, issued: now.addingTimeInterval(-0.5), expires: now), now: now) == .rejected(.expired))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, issued: now.addingTimeInterval(0.3), expires: now.addingTimeInterval(0.8)), now: now) == .rejected(.futureTimestamp))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, expires: now.addingTimeInterval(2)), now: now) == .rejected(.invalidLifetime))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, expires: now), now: now) == .rejected(.invalidLifetime))
|
||||
#expect(gate.admit(envelope(session: session, tv: tv), now: Date(timeIntervalSince1970: .infinity)) == .rejected(.invalidTimestamp))
|
||||
}
|
||||
|
||||
@Test func acceptsSkewBoundaryAndMaximumLifetime() {
|
||||
let session = UUID(), tv = UUID()
|
||||
var gate = CommandAdmission(sessionID: session, televisionID: tv)
|
||||
let issued = now.addingTimeInterval(0.25)
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, issued: issued, expires: issued.addingTimeInterval(1)), now: now) == .accepted)
|
||||
}
|
||||
|
||||
@Test func resetUsesFreshSessionAndOldReplayCannotPassAfterIDEviction() {
|
||||
let session = UUID(), tv = UUID()
|
||||
var gate = CommandAdmission(sessionID: session, televisionID: tv)
|
||||
let first = envelope(session: session, tv: tv, sequence: 0)
|
||||
#expect(gate.admit(first, now: now) == .accepted)
|
||||
for value in 1...CommandAdmission.rememberedIDLimit {
|
||||
#expect(gate.admit(envelope(session: session, tv: tv, sequence: UInt64(value)), now: now) == .accepted)
|
||||
}
|
||||
#expect(gate.admit(first, now: now) == .rejected(.outOfOrder))
|
||||
let newSession = gate.startNewSession(televisionID: tv)
|
||||
#expect(newSession != session)
|
||||
#expect(gate.admit(first, now: now) == .rejected(.wrongSession))
|
||||
#expect(gate.admit(envelope(session: newSession, tv: tv, sequence: 0), now: now) == .accepted)
|
||||
}
|
||||
|
||||
@Test func decodingDoesNotBypassAdmission() throws {
|
||||
let session = UUID(), tv = UUID()
|
||||
let invalid = envelope(session: session, tv: tv, version: 99)
|
||||
let decoded = try JSONDecoder().decode(CommandEnvelope.self, from: JSONEncoder().encode(invalid))
|
||||
#expect(decoded == invalid)
|
||||
var gate = CommandAdmission(sessionID: session, televisionID: tv)
|
||||
#expect(gate.admit(decoded, now: now) == .rejected(.unsupportedSchema))
|
||||
}
|
||||
Reference in New Issue
Block a user