gitbayTests/ListReturnTests.swift

v1.11.0
gitbay-ios/gitbayTests/ListReturnTests.swift history · blame · raw

91 lines · 3420 bytes

 1import Foundation
 2import Testing
 3@testable import gitbay
 4
 5private func makeClient() throws -> (GitbayClient, StubProtocol.Box) {
 6    let box = StubProtocol.box()
 7    let client = GitbayClient(
 8        instance: try GitbayInstance(url: "https://gitbay.org"),
 9        token: "test-token",
10        session: box.session()
11    )
12    return (client, box)
13}
14
15private let emptyPage = #"{"protocol_version":1,"data":{"items":[]},"exit_code":0}"#
16/// A refusal, not a 5xx: the client retries a 5xx once, which would
17/// take the next stub and hide the failure.
18private let refusal = #"{"protocol_version":1,"error":"denied","exit_code":4}"#
19
20private func reads(_ stub: StubProtocol.Box, _ command: String) -> Int {
21    stub.seen.count { $0.url.query()?.contains(command) == true }
22}
23
24/// Returning to a list from a pushed detail screen runs its `.task`
25/// again. `loadIfNeeded()` reads the list only when nothing is shown yet,
26/// so the pages and scroll position the user left are still there.
27@MainActor
28struct ListReturnTests {
29
30    @Test func theIssueListLoadsOnce() async throws {
31        let (client, stub) = try makeClient()
32        stub.enqueue(.init(status: 200, json: emptyPage, match: "argv=issue&argv=list"))
33        let model = IssueListViewModel(client: client, repoPath: "krz/gitbay")
34
35        await model.loadIfNeeded()
36        await model.loadIfNeeded()
37
38        #expect(reads(stub, "argv=issue&argv=list") == 1)
39    }
40
41    @Test func theMRListLoadsOnce() async throws {
42        let (client, stub) = try makeClient()
43        stub.enqueue(.init(status: 200, json: emptyPage, match: "argv=mr&argv=list"))
44        let model = MRListViewModel(client: client, repoPath: "krz/gitbay")
45
46        await model.loadIfNeeded()
47        await model.loadIfNeeded()
48
49        #expect(reads(stub, "argv=mr&argv=list") == 1)
50    }
51
52    @Test func theBuildListLoadsOnce() async throws {
53        let (client, stub) = try makeClient()
54        stub.enqueue(.init(status: 200, json: emptyPage, match: "argv=build&argv=list"))
55        let model = BuildListViewModel(client: client, repoPath: "krz/gitbay")
56
57        await model.loadIfNeeded()
58        await model.loadIfNeeded()
59
60        #expect(reads(stub, "argv=build&argv=list") == 1)
61    }
62
63    @Test func aFailedLoadIsTriedAgainOnReturn() async throws {
64        let (client, stub) = try makeClient()
65        stub.enqueue(.init(status: 403, json: refusal, match: "argv=mr&argv=list"))
66        stub.enqueue(.init(status: 200, json: emptyPage, match: "argv=mr&argv=list"))
67        let model = MRListViewModel(client: client, repoPath: "krz/gitbay")
68
69        await model.loadIfNeeded()
70        await model.loadIfNeeded()
71
72        #expect(reads(stub, "argv=mr&argv=list") == 2)
73        guard case .empty = model.state else {
74            Issue.record("expected .empty after the retry, got \(model.state)")
75            return
76        }
77    }
78
79    /// Pull-to-refresh still calls `load()`, which always reads.
80    @Test func loadStillReloads() async throws {
81        let (client, stub) = try makeClient()
82        stub.enqueue(.init(status: 200, json: emptyPage, match: "argv=issue&argv=list"))
83        stub.enqueue(.init(status: 200, json: emptyPage, match: "argv=issue&argv=list"))
84        let model = IssueListViewModel(client: client, repoPath: "krz/gitbay")
85
86        await model.loadIfNeeded()
87        await model.load()
88
89        #expect(reads(stub, "argv=issue&argv=list") == 2)
90    }
91}