luminate/Tests/LuminateServicesTests/JellyfinClientReconnectTests.swift

89 lines
3.1 KiB
Swift

//
// JellyfinClientReconnectTests.swift
//
// Copyright 2026 Brendan Szymanski <hello@bscubed.dev>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
import Foundation
import HTTPTypes
import LuminateCore
import OpenAPIRuntime
import Synchronization
import Testing
@testable import LuminateServices
/// A transport double that always answers with the same canned public-users payload.
private struct FixedPublicUsersTransport: ClientTransport {
let responseBody: String
func send(
_ request: HTTPRequest,
body: HTTPBody?,
baseURL: URL,
operationID: String
) async throws -> (HTTPResponse, HTTPBody?) {
let response = HTTPResponse(status: .ok, headerFields: [.contentType: "application/json"])
return (response, HTTPBody(responseBody))
}
}
/// Covers ``JellyfinClient/reconnect()``: the fix for a transport that failed once (most notably,
/// `URLSession` caching a DNS resolution failure for its whole lifetime on Linux) and must be
/// rebuilt from scratch, rather than reused, before a retry can possibly succeed.
@Suite struct JellyfinClientReconnectTests {
private static func publicUsersBody(name: String, id: String) -> String {
"""
[
{
"Name": "\(name)",
"Id": "\(id)"
}
]
"""
}
@Test("reconnect() rebuilds the API client against a freshly constructed transport")
func reconnectRebuildsTransport() async throws {
let invocationCount = Mutex(0)
let responses = [
Self.publicUsersBody(name: "alpha", id: "user-1"),
Self.publicUsersBody(name: "beta", id: "user-2"),
]
let client = JellyfinClient(
configuration: JellyfinClientConfiguration(serverURL: URL(string: "http://localhost")!),
makeTransport: {
let index = invocationCount.withLock { count in
defer { count += 1 }
return count
}
return FixedPublicUsersTransport(responseBody: responses[index])
}
)
let beforeReconnect = try await client.publicUsers()
#expect(beforeReconnect.map(\.name) == ["alpha"])
#expect(invocationCount.withLock { $0 } == 1)
await client.reconnect()
let afterReconnect = try await client.publicUsers()
#expect(afterReconnect.map(\.name) == ["beta"])
#expect(invocationCount.withLock { $0 } == 2)
}
}