krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.4.0: HutchTests/SettingsViewModelTests.swift · raw

 1import Foundation
 2import Testing
 3@testable import Hutch
 4
 5private final class SettingsViewModelCapturingURLProtocol: URLProtocol, @unchecked Sendable {
 6    nonisolated(unsafe) static var capturedRequests: [URLRequest] = []
 7
 8    override class func canInit(with _: URLRequest) -> Bool { true }
 9    override class func canonicalRequest(for request: URLRequest) -> URLRequest { request }
10
11    override func startLoading() {
12        Self.capturedRequests.append(request)
13
14        let response = HTTPURLResponse(
15            url: request.url!,
16            statusCode: 401,
17            httpVersion: nil,
18            headerFields: nil
19        )!
20        client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
21        client?.urlProtocol(self, didLoad: Data())
22        client?.urlProtocolDidFinishLoading(self)
23    }
24
25    override func stopLoading() {
26        // No cleanup is needed because the stub responds immediately in `startLoading()`.
27    }
28
29    static func makeSession() -> URLSession {
30        let config = URLSessionConfiguration.ephemeral
31        config.protocolClasses = [Self.self]
32        return URLSession(configuration: config)
33    }
34}
35
36private struct DeletePGPKeyEnvelope: Decodable {
37    let deletePGPKey: DeleteResultPayload?
38}
39
40private struct DeleteResultPayload: Decodable {
41    let id: Int?
42}
43
44@Suite(.serialized)
45struct SettingsViewModelTests {
46
47    @Test
48    @MainActor
49    func deletePGPKeyResponseDecodesNullPayloadWithGraphQLErrors() throws {
50        let json = """
51        {
52            "errors": [
53                {
54                    "message": "PGP key ID 13629 is set as the user's preferred PGP key - it must be unset before removing the key"
55                }
56            ],
57            "data": {
58                "deletePGPKey": null
59            }
60        }
61        """
62
63        let decoded = try JSONDecoder().decode(
64            GraphQLResponse<DeletePGPKeyEnvelope>.self,
65            from: Data(json.utf8)
66        )
67
68        #expect(decoded.data?.deletePGPKey == nil)
69        #expect(decoded.errors?.first?.message.contains("preferred PGP key") == true)
70    }
71
72    @Test
73    @MainActor
74    func loadProfileDoesNotRequestSSHKeyFingerprintField() async throws {
75        SettingsViewModelCapturingURLProtocol.capturedRequests = []
76
77        let client = SRHTClient(
78            session: SettingsViewModelCapturingURLProtocol.makeSession(),
79            token: "test-token"
80        )
81        let viewModel = SettingsViewModel(client: client)
82
83        await viewModel.loadProfile()
84
85        let request = try #require(SettingsViewModelCapturingURLProtocol.capturedRequests.first)
86        let body = try #require(request.httpBody)
87        let jsonObject = try #require(JSONSerialization.jsonObject(with: body) as? [String: Any])
88        let query = try #require(jsonObject["query"] as? String)
89
90        #expect(query.contains("sshKeys"))
91        #expect(!query.contains("results { id fingerprint comment created lastUsed }"))
92        #expect(!query.contains("fingerprint comment created lastUsed"))
93    }
94}