Files
Amphlify/Packages/AmphlifyKit/Tests/SourceSubsonicTests/FixtureTransport.swift
T

88 lines
3.5 KiB
Swift

import Domain
import Foundation
@testable import SourceSubsonic
struct FixtureTransport: Sendable {
var droppedAlbum: String?
static func fixture(_ name: String) throws -> Data {
let url = try unwrap(Bundle.module.url(forResource: name, withExtension: "json", subdirectory: "Fixtures"))
return try Data(contentsOf: url)
}
static func empty(_ key: String) -> Data {
Data(#"{"subsonic-response":{"status":"ok","version":"1.16.1","\#(key)":{}}}"#.utf8)
}
static func method(of request: URLRequest) -> (String, [String: String]) {
let url = request.url ?? URL(filePath: "/")
let method = url.lastPathComponent.replacingOccurrences(of: ".view", with: "")
let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems ?? []
return (method, Dictionary(items.map { ($0.name, $0.value ?? "") }) { $1 })
}
func callAsFunction(_ request: URLRequest) async throws -> (Data, URLResponse) {
let (method, params) = Self.method(of: request)
let response = HTTPURLResponse(url: request.url ?? URL(filePath: "/"), statusCode: 200, httpVersion: nil, headerFields: nil)
return try (data(for: method, params: params), response ?? URLResponse())
}
private func data(for method: String, params: [String: String]) throws -> Data {
switch method {
case "getArtists": try Self.fixture("artists")
case "search3" where params["songOffset"] == "0": try Self.fixture("search3All")
case "search3": Self.empty("searchResult3")
case "getAlbumList2" where params["offset"] == "0": try albums()
case "getAlbumList2": Self.empty("albumList2")
case "getPlaylists": try Self.fixture("playlists")
case "getPlaylist": try Self.fixture("playlist")
case "getInternetRadioStations": try Self.fixture("radioStations")
case "getStarred2": try Self.fixture("starred2")
case "getLyricsBySongId": try Self.fixture("lyricsEmpty")
default: try Self.fixture("ping")
}
}
private func albums() throws -> Data {
let data = try Self.fixture("albumList2All")
guard let droppedAlbum else { return data }
var root = try unwrap(JSONSerialization.jsonObject(with: data) as? [String: Any])
var response = try unwrap(root["subsonic-response"] as? [String: Any])
var list = try unwrap(response["albumList2"] as? [String: Any])
let albums = try unwrap(list["album"] as? [[String: Any]])
list["album"] = albums.filter { $0["id"] as? String != droppedAlbum }
response["albumList2"] = list
root["subsonic-response"] = response
return try JSONSerialization.data(withJSONObject: root)
}
}
extension SubsonicSource {
static func fixture(transport: FixtureTransport = FixtureTransport(), id: SourceID = SourceID()) -> SubsonicSource {
let client = SubsonicClient(
baseURL: URL(string: "https://music.example.test")!,
username: "h",
password: "secret",
transport: { try await transport($0) }
)
return SubsonicSource(id: id, name: "Test", client: client)
}
func collect() async throws -> [Change] {
var collected: [Change] = []
for try await change in changes(since: nil) {
collected.append(change)
}
return collected
}
}
private func unwrap<T>(_ value: T?) throws -> T {
guard let value else { throw FixtureError.missing }
return value
}
private enum FixtureError: Error {
case missing
}