1fabc2abdbf1
report wallhaven status page as an outage instead of a decoding failure (v0.6.3)
- time
- 2026-09-19 14:07 UTC · 3 weeks ago
- machine
- koh2
- agent
- deepseek/deepseek-flash
- session
- fix pill clicks and error banner
- parent
- 987de19ffe51
↓ haven-1fabc2ab.tar.gzatlas at this save
files changed3 files
Info.plist+2 −2Sources/Haven/API/WallhavenAPI.swift+14 −0Tests/HavenTests/WallhavenAPITests.swift+25 −2Info.plist+2 −2
@@ -9,9 +9,9 @@99 <key>CFBundleName</key>1010 <string>Haven</string>1111 <key>CFBundleShortVersionString</key>12- <string>0.6.2</string>12+ <string>0.6.3</string>1313 <key>CFBundleVersion</key>14- <string>8</string>14+ <string>9</string>1515 <key>LSMinimumSystemVersion</key>1616 <string>13.0</string>1717 <key>LSUIElement</key>Sources/Haven/API/WallhavenAPI.swift+14 −0
@@ -94,6 +94,12 @@9494 }9595 }9696 97+ /// Wallhaven's status page is HTML; the API itself is always JSON, so any98+ /// other content type means the response did not come from the API.99+ static func isStatusPage(response: HTTPURLResponse) -> Bool {100+ !(response.mimeType ?? "").contains("json")101+ }102+97103 private func performSearch(queryItems: [URLQueryItem]) async throws -> ([Wallpaper], Meta?) {98104 var urlComponents = URLComponents(99105 url: Configuration.baseURL.appendingPathComponent("search"), resolvingAgainstBaseURL: true)!@@ -109,6 +115,14 @@109115 110116 guard (200...299).contains(httpResponse.statusCode) else {111117 throw WallhavenError.apiError(Self.message(forStatus: httpResponse.statusCode))118+ }119+120+ // Wallhaven answers API calls with its HTML status page ("taking a little121+ // nap") while its server is down, under a 200 status. Saying so beats122+ // reporting a decoding failure for a body that was never going to decode.123+ guard !Self.isStatusPage(response: httpResponse) else {124+ throw WallhavenError.apiError(125+ "Wallhaven is offline right now — it's serving its status page.")112126 }113127 114128 do {Tests/HavenTests/WallhavenAPITests.swift+25 −2
@@ -38,12 +38,14 @@3838 return WallhavenAPI(session: URLSession(configuration: configuration))3939 }4040 41- private func response(for request: URLRequest, statusCode: Int = 200) -> HTTPURLResponse {41+ private func response(42+ for request: URLRequest, statusCode: Int = 200, contentType: String = "application/json"43+ ) -> HTTPURLResponse {4244 HTTPURLResponse(4345 url: request.url!,4446 statusCode: statusCode,4547 httpVersion: "HTTP/1.1",46- headerFields: ["Content-Type": "application/json"]48+ headerFields: ["Content-Type": contentType]4749 )!4850 }4951 @@ -153,6 +155,27 @@153155 } catch {154156 Issue.record("Unexpected error: \(error)")155157 }158+ }159+ }160+161+ @Test("Wallhaven's HTML status page is reported as an outage, not a bad response")162+ func statusPageIsReportedAsOffline() async {163+ MockURLProtocol.requestHandler = { request in164+ (165+ response(166+ for: request,167+ contentType: "text/html; charset=utf-8"),168+ Data("<!DOCTYPE html><title>wallhaven.cc Status</title>".utf8)169+ )170+ }171+172+ do {173+ _ = try await makeAPI().searchWallpapers(query: "")174+ Issue.record("Expected a status page to throw")175+ } catch WallhavenError.apiError(let message) {176+ #expect(message == "Wallhaven is offline right now — it's serving its status page.")177+ } catch {178+ Issue.record("Unexpected error: \(error)")156179 }157180 }158181