gitbayTests/RepoActionTests.swift
164 lines · 6573 bytes
12 symbols in this file
makeClientargvOfRepoActionsTestsbookmarkingAndUnbookmarkingAreDifferentCommandswatchMuteAndUnwatchSendTheirOwnCommandsforkingReturnsTheNewPathforkingWithANameAppendsTheFlagforkingWithSurroundingWhitespaceTrimsTheNameforkingWithABlankNameOmitsTheFlagaRefusedForkReturnsNilAndSurfacesaFailedActionClearsAStaleNoticeaForkThatReportsNothingBackSetsAnError
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}