gitbayTests/RepoActionTests.swift

v1.6.0
gitbay-ios/gitbayTests/RepoActionTests.swift history · blame · raw

164 lines · 6573 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 func argvOf(_ seen: StubProtocol.Seen) throws -> [String] {
 16    let body = try #require(try JSONSerialization.jsonObject(with: seen.body) as? [String: Any])
 17    return try #require(body["argv"] as? [String])
 18}
 19
 20private let ok = """
 21    {"protocol_version":1,"exit_code":0}
 22    """
 23
 24@MainActor
 25struct RepoActionsTests {
 26
 27    @Test func bookmarkingAndUnbookmarkingAreDifferentCommands() async throws {
 28        let (client, stub) = try makeClient()
 29        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
 30
 31        stub.enqueue(.init(status: 200, json: ok))
 32        await model.setBookmarked(false)
 33        #expect(stub.seen.contains { (try? argvOf($0)) == ["repo", "unbookmark", "krz/gitbay"] })
 34        #expect(stub.seen.count == 1)
 35
 36        let model2 = RepoActionsViewModel(client: client, repoPath: "krz/other")
 37        let before = stub.seen.count
 38        stub.enqueue(.init(status: 200, json: ok))
 39        await model2.setBookmarked(true)
 40        #expect(stub.seen.contains { (try? argvOf($0)) == ["repo", "bookmark", "krz/other"] })
 41        #expect(stub.seen.count == before + 1)
 42    }
 43
 44    @Test func watchMuteAndUnwatchSendTheirOwnCommands() async throws {
 45        let (client, stub) = try makeClient()
 46        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
 47
 48        stub.enqueue(.init(status: 200, json: ok))
 49        await model.watch()
 50        #expect(try argvOf(try #require(stub.seen.last)) == ["repo", "watch", "krz/gitbay"])
 51
 52        stub.enqueue(.init(status: 200, json: ok))
 53        await model.mute()
 54        #expect(try argvOf(try #require(stub.seen.last)) == ["repo", "mute", "krz/gitbay"])
 55
 56        stub.enqueue(.init(status: 200, json: ok))
 57        await model.unwatch()
 58        #expect(try argvOf(try #require(stub.seen.last)) == ["repo", "unwatch", "krz/gitbay"])
 59    }
 60
 61    @Test func forkingReturnsTheNewPath() async throws {
 62        let (client, stub) = try makeClient()
 63        stub.enqueue(.init(status: 200, json: """
 64            {"protocol_version":1,"data":{"path":"cmc/gitbay","fork_of":"krz/gitbay"},\
 65            "exit_code":0}
 66            """))
 67        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
 68        let result = await model.fork(named: nil)
 69
 70        #expect(result?.path == "cmc/gitbay")
 71        #expect(result?.forkOf == "krz/gitbay")
 72        #expect(try argvOf(try #require(stub.seen.last)) == ["repo", "fork", "krz/gitbay"])
 73    }
 74
 75    @Test func forkingWithANameAppendsTheFlag() async throws {
 76        let (client, stub) = try makeClient()
 77        stub.enqueue(.init(status: 200, json: """
 78            {"protocol_version":1,"data":{"path":"cmc/mine","fork_of":"krz/gitbay"},\
 79            "exit_code":0}
 80            """))
 81        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
 82        _ = await model.fork(named: "mine")
 83
 84        #expect(try argvOf(try #require(stub.seen.last))
 85            == ["repo", "fork", "krz/gitbay", "--name", "mine"])
 86    }
 87
 88    /// Surrounding whitespace must not reach the server — the name is
 89    /// trimmed, not just tested for blankness.
 90    @Test func forkingWithSurroundingWhitespaceTrimsTheName() async throws {
 91        let (client, stub) = try makeClient()
 92        stub.enqueue(.init(status: 200, json: """
 93            {"protocol_version":1,"data":{"path":"cmc/mine","fork_of":"krz/gitbay"},\
 94            "exit_code":0}
 95            """))
 96        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
 97        _ = await model.fork(named: "  mine  ")
 98
 99        #expect(try argvOf(try #require(stub.seen.last))
100            == ["repo", "fork", "krz/gitbay", "--name", "mine"])
101    }
102
103    /// A blank name must not send an empty flag value.
104    @Test func forkingWithABlankNameOmitsTheFlag() async throws {
105        let (client, stub) = try makeClient()
106        stub.enqueue(.init(status: 200, json: """
107            {"protocol_version":1,"data":{"path":"cmc/gitbay","fork_of":"krz/gitbay"},\
108            "exit_code":0}
109            """))
110        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
111        _ = await model.fork(named: "   ")
112
113        #expect(try argvOf(try #require(stub.seen.last)) == ["repo", "fork", "krz/gitbay"])
114    }
115
116    @Test func aRefusedForkReturnsNilAndSurfaces() async throws {
117        let (client, stub) = try makeClient()
118        stub.enqueue(.init(status: 200, json: """
119            {"protocol_version":1,"error":"a repository named gitbay already exists",\
120            "exit_code":1}
121            """))
122        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
123        let result = await model.fork(named: nil)
124
125        #expect(result == nil)
126        #expect(model.actionError?.isEmpty == false)
127        #expect(model.working == false)
128    }
129
130    /// A stale `notice` from an earlier success must not survive a later
131    /// failure — the screen must never show a success banner and a
132    /// failure banner at once.
133    @Test func aFailedActionClearsAStaleNotice() async throws {
134        let (client, stub) = try makeClient()
135        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
136
137        stub.enqueue(.init(status: 200, json: ok))
138        await model.watch()
139        #expect(model.notice != nil)
140
141        stub.enqueue(.init(status: 200, json: """
142            {"protocol_version":1,"error":"a repository named gitbay already exists",\
143            "exit_code":1}
144            """))
145        _ = await model.fork(named: nil)
146
147        #expect(model.notice == nil)
148        #expect(model.actionError?.isEmpty == false)
149    }
150
151    /// A 0-exit fork whose envelope carries no `data` must not fail
152    /// silently — the caller needs to know nothing came back.
153    @Test func aForkThatReportsNothingBackSetsAnError() async throws {
154        let (client, stub) = try makeClient()
155        stub.enqueue(.init(status: 200, json: """
156            {"protocol_version":1,"exit_code":0}
157            """))
158        let model = RepoActionsViewModel(client: client, repoPath: "krz/gitbay")
159        let result = await model.fork(named: nil)
160
161        #expect(result == nil)
162        #expect(model.actionError?.isEmpty == false)
163    }
164}