ecbbff5544
apple / swift (push) Successful in 1m5s
audit / cargo-audit (push) Successful in 1m19s
android / android (push) Successful in 4m21s
ci / web (push) Successful in 58s
ci / docs-site (push) Successful in 58s
ci / rust (push) Successful in 8m40s
release / apple (push) Successful in 9m10s
ci / bench (push) Successful in 4m39s
decky / build-publish (push) Successful in 14s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 29s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
deb / build-publish (push) Successful in 3m50s
apple / screenshots (push) Successful in 5m38s
flatpak / build-publish (push) Successful in 4m12s
windows-host / package (push) Successful in 19m17s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m15s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m5s
docker / deploy-docs (push) Successful in 18s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m1s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m53s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 3m24s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 3m30s
82 lines
3.5 KiB
Swift
82 lines
3.5 KiB
Swift
// Unit tests for the game-library models — decoding the management API's GET /api/v1/library
|
|
// payload and the poster-art fallback order. (The network fetch itself isn't unit-tested; it's
|
|
// exercised live against a host.)
|
|
|
|
import XCTest
|
|
@testable import PunktfunkKit
|
|
|
|
final class LibraryClientTests: XCTestCase {
|
|
func testDecodesLibraryPayload() throws {
|
|
// A Steam entry (full art + launch) and a custom entry (sparse art, no launch) — the two
|
|
// shapes the host's `GameEntry` serializes (note the host omits null fields).
|
|
let json = """
|
|
[
|
|
{
|
|
"id": "steam:570",
|
|
"store": "steam",
|
|
"title": "Dota 2",
|
|
"art": {
|
|
"portrait": "https://cdn.cloudflare.steamstatic.com/steam/apps/570/library_600x900.jpg",
|
|
"hero": "https://cdn.cloudflare.steamstatic.com/steam/apps/570/library_hero.jpg",
|
|
"logo": "https://cdn.cloudflare.steamstatic.com/steam/apps/570/logo.png",
|
|
"header": "https://cdn.cloudflare.steamstatic.com/steam/apps/570/header.jpg"
|
|
},
|
|
"launch": { "kind": "steam_appid", "value": "570" }
|
|
},
|
|
{
|
|
"id": "custom:abc123",
|
|
"store": "custom",
|
|
"title": "Dolphin",
|
|
"art": { "header": "https://example.com/dolphin.jpg" }
|
|
}
|
|
]
|
|
""".data(using: .utf8)!
|
|
|
|
let games = try JSONDecoder().decode([GameEntry].self, from: json)
|
|
XCTAssertEqual(games.count, 2)
|
|
|
|
let steam = games[0]
|
|
XCTAssertEqual(steam.id, "steam:570")
|
|
XCTAssertFalse(steam.isCustom)
|
|
XCTAssertEqual(steam.launch?.kind, "steam_appid")
|
|
XCTAssertEqual(steam.launch?.value, "570")
|
|
|
|
let custom = games[1]
|
|
XCTAssertTrue(custom.isCustom)
|
|
XCTAssertNil(custom.launch)
|
|
XCTAssertNil(custom.art.portrait)
|
|
}
|
|
|
|
func testPosterCandidatesPreferPortraitThenHeader() {
|
|
let full = Artwork(
|
|
portrait: "https://x/p.jpg", hero: "https://x/hero.jpg",
|
|
logo: "https://x/logo.png", header: "https://x/h.jpg")
|
|
XCTAssertEqual(full.posterCandidates.map(\.absoluteString),
|
|
["https://x/p.jpg", "https://x/h.jpg", "https://x/hero.jpg"])
|
|
|
|
// No portrait → header leads; absent fields are skipped, not nil-padded.
|
|
let sparse = Artwork(portrait: nil, hero: nil, logo: nil, header: "https://x/h.jpg")
|
|
XCTAssertEqual(sparse.posterCandidates.map(\.absoluteString), ["https://x/h.jpg"])
|
|
|
|
XCTAssertTrue(Artwork().posterCandidates.isEmpty)
|
|
}
|
|
|
|
func testArtworkResolvedRewritesOnlyHostRelativePaths() {
|
|
let base = URL(string: "https://192.168.1.70:47990/api/v1/library")!
|
|
// Steam art now comes back as host-relative proxy paths; external CDN URLs (GOG/Heroic/Xbox)
|
|
// and `data:` URLs (Lutris) are untouched.
|
|
let art = Artwork(
|
|
portrait: "/api/v1/library/art/steam:3527290/portrait",
|
|
hero: "https://cdn.example.com/hero.jpg",
|
|
logo: nil,
|
|
header: "/api/v1/library/art/steam:3527290/header")
|
|
let resolved = art.resolved(against: base)
|
|
XCTAssertEqual(
|
|
resolved.portrait, "https://192.168.1.70:47990/api/v1/library/art/steam:3527290/portrait")
|
|
XCTAssertEqual(
|
|
resolved.header, "https://192.168.1.70:47990/api/v1/library/art/steam:3527290/header")
|
|
XCTAssertEqual(resolved.hero, "https://cdn.example.com/hero.jpg") // unchanged
|
|
XCTAssertNil(resolved.logo)
|
|
}
|
|
}
|