Files

158 lines
5.0 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Testing
import Foundation
@testable import Mobile_Music_Assistant
// MARK: - ConnectionState
@Suite("MAWebSocketClient ConnectionState")
struct ConnectionStateTests {
@Test("Fresh client starts disconnected")
func startsDisconnected() {
let client = MAWebSocketClient()
if case .disconnected = client.connectionState {
// pass
} else {
Issue.record("Expected .disconnected, got \(client.connectionState)")
}
}
@Test("isConnected is false when disconnected")
func isConnectedFalseWhenDisconnected() {
let client = MAWebSocketClient()
#expect(client.isConnected == false)
}
@Test("ConnectionState descriptions are distinct")
func statesAreDistinct() {
// Regression: ensure no two states resolve to the same string representation
let states: [MAWebSocketClient.ConnectionState] = [
.disconnected,
.connecting,
.connected,
.reconnecting(attempt: 1),
.reconnecting(attempt: 2)
]
let descriptions = states.map { "\($0)" }
let unique = Set(descriptions)
#expect(unique.count == descriptions.count)
}
}
// MARK: - Reconnection Backoff
@Suite("MAWebSocketClient Reconnection Backoff")
struct ReconnectionBackoffTests {
/// Replicates the exact formula used in MAWebSocketClient.
private func backoffDelay(attempt: Int) -> Double {
let base = 3.0
let maxDelay = 30.0
return min(base * pow(2.0, Double(attempt - 1)), maxDelay)
}
@Test("Attempt 1 gives 3 seconds")
func attempt1() {
#expect(backoffDelay(attempt: 1) == 3.0)
}
@Test("Attempt 2 gives 6 seconds")
func attempt2() {
#expect(backoffDelay(attempt: 2) == 6.0)
}
@Test("Attempt 3 gives 12 seconds")
func attempt3() {
#expect(backoffDelay(attempt: 3) == 12.0)
}
@Test("Attempt 4 gives 24 seconds")
func attempt4() {
#expect(backoffDelay(attempt: 4) == 24.0)
}
@Test("Attempt 5 is capped at 30 seconds")
func attempt5Capped() {
#expect(backoffDelay(attempt: 5) == 30.0)
}
@Test("High attempt numbers stay capped at 30 seconds")
func highAttemptStaysCapped() {
for attempt in 6...20 {
#expect(backoffDelay(attempt: attempt) == 30.0)
}
}
@Test("Backoff is strictly increasing up to the cap")
func strictlyIncreasing() {
var previous = 0.0
for attempt in 1...5 {
let delay = backoffDelay(attempt: attempt)
#expect(delay > previous)
previous = delay
}
}
}
// MARK: - AnyCodable
@Suite("AnyCodable")
struct AnyCodableTests {
@Test("Bool encodes and decodes correctly")
func boolRoundTrip() throws {
let encoded = try JSONEncoder().encode(AnyCodable(true))
let decoded = try JSONDecoder().decode(AnyCodable.self, from: encoded)
#expect(try decoded.decode(as: Bool.self) == true)
}
@Test("Int encodes and decodes correctly")
func intRoundTrip() throws {
let encoded = try JSONEncoder().encode(AnyCodable(42))
let decoded = try JSONDecoder().decode(AnyCodable.self, from: encoded)
#expect(try decoded.decode(as: Int.self) == 42)
}
@Test("String encodes and decodes correctly")
func stringRoundTrip() throws {
let encoded = try JSONEncoder().encode(AnyCodable("hello"))
let decoded = try JSONDecoder().decode(AnyCodable.self, from: encoded)
#expect(try decoded.decode(as: String.self) == "hello")
}
@Test("Double encodes and decodes correctly")
func doubleRoundTrip() throws {
let encoded = try JSONEncoder().encode(AnyCodable(3.14))
let decoded = try JSONDecoder().decode(AnyCodable.self, from: encoded)
let value = try decoded.decode(as: Double.self)
#expect(abs(value - 3.14) < 0.001)
}
@Test("Array encodes and decodes correctly")
func arrayRoundTrip() throws {
let arr = AnyCodable(["a", "b", "c"])
let encoded = try JSONEncoder().encode(arr)
let decoded = try JSONDecoder().decode(AnyCodable.self, from: encoded)
let values = try decoded.decode(as: [AnyCodable].self)
#expect(values.count == 3)
}
@Test("decode(as:) throws for wrong type")
func decodeThrowsForWrongType() throws {
let encoded = try JSONEncoder().encode(AnyCodable("text"))
let decoded = try JSONDecoder().decode(AnyCodable.self, from: encoded)
#expect(throws: (any Error).self) {
try decoded.decode(as: Int.self)
}
}
@Test("Dictionary decodes keys correctly")
func dictRoundTrip() throws {
// Build the AnyCodable from raw JSON to avoid double-wrapping issues
let json = #"{"key":"value"}"#
let decoded = try JSONDecoder().decode(AnyCodable.self, from: Data(json.utf8))
let map = try decoded.decode(as: [String: AnyCodable].self)
#expect(try map["key"]?.decode(as: String.self) == "value")
}
}