Unit tests und nudging screen
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
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 {
|
||||
let dict = AnyCodable(["key": AnyCodable("value")])
|
||||
let encoded = try JSONEncoder().encode(dict)
|
||||
let decoded = try JSONDecoder().decode(AnyCodable.self, from: encoded)
|
||||
let map = try decoded.decode(as: [String: AnyCodable].self)
|
||||
#expect(try map["key"]?.decode(as: String.self) == "value")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user