velody/packages/apple/VelodyNetworking/Tests/VelodyNetworkingTests/URLSessionVelodyAPIClientAuthorizationTests.swift

177 lines
5.9 KiB
Swift

import Foundation
import XCTest
import VelodyDomain
@testable import VelodyNetworking
final class URLSessionVelodyAPIClientAuthorizationTests: XCTestCase {
override func tearDown() {
RecordingURLProtocol.handler = nil
super.tearDown()
}
func testFetchRemoteLibrarySendsAuthorizationHeaderWhenDeviceTokenIsAvailable() async throws {
RecordingURLProtocol.handler = { request in
XCTAssertEqual(
request.value(forHTTPHeaderField: "Authorization"),
"Bearer test-device-access-token"
)
XCTAssertEqual(request.url?.path, "/api/v1/library/tracks")
XCTAssertEqual(request.url?.query, "deviceId=device-123")
return (
HTTPURLResponse(
url: try XCTUnwrap(request.url),
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!,
Data(#"{"tracks":[]}"#.utf8)
)
}
let client = URLSessionVelodyAPIClient(
environment: ServerEnvironment(
baseURL: URL(string: "http://127.0.0.1:3007")!,
appVersion: "Tests"
),
session: makeSession(),
deviceAccessTokenProvider: {
"test-device-access-token"
}
)
let response = try await client.fetchRemoteLibrary(deviceId: "device-123")
XCTAssertEqual(response.tracks.count, 0)
}
func testFetchSyncChangesSendsAuthorizationHeaderAndCursorQuery() async throws {
RecordingURLProtocol.handler = { request in
XCTAssertEqual(
request.value(forHTTPHeaderField: "Authorization"),
"Bearer test-device-access-token"
)
XCTAssertEqual(request.url?.path, "/api/v1/sync/changes")
XCTAssertEqual(request.url?.query, "cursor=12")
return (
HTTPURLResponse(
url: try XCTUnwrap(request.url),
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!,
Data(
"""
{
"nextCursor": "12",
"hasMore": false,
"requiresBootstrap": false,
"events": [],
"serverTime": "2026-06-15T12:00:00.000Z"
}
""".utf8
)
)
}
let client = URLSessionVelodyAPIClient(
environment: ServerEnvironment(
baseURL: URL(string: "http://127.0.0.1:3007")!,
appVersion: "Tests"
),
session: makeSession(),
deviceAccessTokenProvider: {
"test-device-access-token"
}
)
let response = try await client.fetchSyncChanges(cursor: SyncCursor(value: "12"))
XCTAssertEqual(response.nextCursor.value, "12")
XCTAssertEqual(response.events.count, 0)
}
func testRegisterDeviceDoesNotSendAuthorizationHeader() async throws {
RecordingURLProtocol.handler = { request in
XCTAssertNil(request.value(forHTTPHeaderField: "Authorization"))
XCTAssertEqual(request.url?.path, "/api/v1/devices/register")
return (
HTTPURLResponse(
url: try XCTUnwrap(request.url),
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"]
)!,
Data(
"""
{
"deviceId": "device-123",
"deviceAccessToken": "registered-device-token",
"bootstrapToken": "bootstrap-token",
"serverTime": "2026-06-09T10:00:00.000Z"
}
""".utf8
)
)
}
let client = URLSessionVelodyAPIClient(
environment: ServerEnvironment(
baseURL: URL(string: "http://127.0.0.1:3007")!,
appVersion: "Tests"
),
session: makeSession(),
deviceAccessTokenProvider: {
"should-not-be-sent"
}
)
let response = try await client.registerDevice(
DeviceRegistrationPayload(
platform: .iphone,
deviceName: "Test iPhone",
appVersion: "Tests"
)
)
XCTAssertEqual(response.deviceId, "device-123")
XCTAssertEqual(response.deviceAccessToken, "registered-device-token")
}
private func makeSession() -> URLSession {
let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [RecordingURLProtocol.self]
return URLSession(configuration: configuration)
}
}
private final class RecordingURLProtocol: URLProtocol {
static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))?
override class func canInit(with request: URLRequest) -> Bool {
request.url?.host == "127.0.0.1"
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
request
}
override func startLoading() {
guard let handler = Self.handler else {
XCTFail("RecordingURLProtocol.handler must be set before use.")
return
}
do {
let (response, data) = try handler(request)
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: data)
client?.urlProtocolDidFinishLoading(self)
} catch {
client?.urlProtocol(self, didFailWithError: error)
}
}
override func stopLoading() {}
}