luminate/Tests/LuminateServicesTests/AuthenticationMiddlewareTests.swift

97 lines
3.7 KiB
Swift

//
// AuthenticationMiddlewareTests.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 OpenAPIRuntime
import Testing
@testable import LuminateServices
/// Records the request a middleware forwarded, so a test can inspect it after the fact.
private actor RequestRecorder {
/// The last forwarded request.
private(set) var request: HTTPRequest?
/// Stores a forwarded request.
///
/// - Parameter request: The request the middleware passed down the chain.
func record(_ request: HTTPRequest) {
self.request = request
}
}
/// Pins the exact `Authorization` header Jellyfin expects.
///
/// Jellyfin rejects the request outright if this string is malformed, and the failure surfaces as a
/// bare 401 with no explanation, so the format is asserted literally rather than by parsing.
@Suite struct AuthenticationMiddlewareTests {
/// Runs `middleware` over a throwaway request and returns the `Authorization` header it set.
///
/// - Parameter middleware: The middleware under test.
/// - Returns: The forwarded request's `Authorization` header, if it set one.
private func authorizationHeader(from middleware: AuthenticationMiddleware) async throws -> String? {
let recorder = RequestRecorder()
let request = HTTPRequest(method: .get, scheme: "http", authority: "localhost", path: "/System/Info/Public")
_ = try await middleware.intercept(
request,
body: nil,
baseURL: URL(string: "http://localhost")!,
operationID: "GetPublicSystemInfo"
) { forwarded, _, _ in
await recorder.record(forwarded)
return (HTTPResponse(status: .ok), nil)
}
return await recorder.request?.headerFields[.authorization]
}
@Test("Builds the MediaBrowser credential string with the token")
func authenticatedHeader() async throws {
let middleware = AuthenticationMiddleware(
clientName: "Luminate",
deviceName: "test-device",
deviceID: "test-device-id",
version: "0.1.0",
token: "test-token"
)
let header = try await authorizationHeader(from: middleware)
#expect(
header
== #"MediaBrowser Client="Luminate", Device="test-device", DeviceId="test-device-id", Version="0.1.0", Token="test-token""#
)
}
@Test("Sends an empty token before sign-in rather than omitting the header")
func unauthenticatedHeader() async throws {
let middleware = AuthenticationMiddleware(
clientName: "Luminate",
deviceName: "test-device",
deviceID: "test-device-id",
version: "0.1.0",
token: nil
)
let header = try await authorizationHeader(from: middleware)
#expect(
header
== #"MediaBrowser Client="Luminate", Device="test-device", DeviceId="test-device-id", Version="0.1.0", Token="""#
)
}
}