| @@ -88,3 +88,145 @@ struct MRLifecycleDecodingTests { |
| 88 | 88 | #expect(row.stackedOn == nil) |
| 89 | 89 | } |
| 90 | 90 | } |
| 91 | |
| 92 | @MainActor |
| 93 | struct MRLifecycleActionTests { |
| 94 | |
| 95 | private func loaded() async throws -> (MRDetailViewModel, StubProtocol.Box) { |
| 96 | let box = StubProtocol.box() |
| 97 | let client = GitbayClient( |
| 98 | instance: try GitbayInstance(url: "https://gitbay.org"), |
| 99 | token: "test-token", |
| 100 | session: box.session() |
| 101 | ) |
| 102 | box.enqueue(.init(status: 200, json: """ |
| 103 | {"protocol_version":1,"data":\(plainDetail),"exit_code":0} |
| 104 | """)) |
| 105 | let model = MRDetailViewModel(client: client, repoPath: "krz/gitbay", number: 7) |
| 106 | await model.load() |
| 107 | return (model, box) |
| 108 | } |
| 109 | |
| 110 | private func argvOf(_ seen: StubProtocol.Seen) throws -> [String] { |
| 111 | let body = try #require(try JSONSerialization.jsonObject(with: seen.body) as? [String: Any]) |
| 112 | return try #require(body["argv"] as? [String]) |
| 113 | } |
| 114 | |
| 115 | private func ok(_ box: StubProtocol.Box) { |
| 116 | box.enqueue(.init(status: 200, json: """ |
| 117 | {"protocol_version":1,"exit_code":0} |
| 118 | """)) |
| 119 | box.enqueue(.init(status: 200, json: """ |
| 120 | {"protocol_version":1,"data":\(plainDetail),"exit_code":0} |
| 121 | """)) |
| 122 | } |
| 123 | |
| 124 | @Test func markingDraftAndReadyAreDifferentCommands() async throws { |
| 125 | let (model, box) = try await loaded() |
| 126 | ok(box) |
| 127 | await model.setDraft(true) |
| 128 | var write = try #require(box.seen.first { $0.method == "POST" }) |
| 129 | #expect(try argvOf(write) == ["mr", "draft", "krz/gitbay", "7"]) |
| 130 | |
| 131 | let (model2, box2) = try await loaded() |
| 132 | ok(box2) |
| 133 | await model2.setDraft(false) |
| 134 | write = try #require(box2.seen.first { $0.method == "POST" }) |
| 135 | #expect(try argvOf(write) == ["mr", "ready", "krz/gitbay", "7"]) |
| 136 | } |
| 137 | |
| 138 | @Test func retargetPassesTheBranchPositionally() async throws { |
| 139 | let (model, box) = try await loaded() |
| 140 | ok(box) |
| 141 | await model.retarget(to: "release") |
| 142 | let write = try #require(box.seen.first { $0.method == "POST" }) |
| 143 | #expect(try argvOf(write) == ["mr", "retarget", "krz/gitbay", "7", "release"]) |
| 144 | } |
| 145 | |
| 146 | /// `mr review request` is a THREE-word path. Two words posts a |
| 147 | /// review instead — silently, with a 200. |
| 148 | @Test func requestingAReviewUsesTheThreeWordPath() async throws { |
| 149 | let (model, box) = try await loaded() |
| 150 | ok(box) |
| 151 | await model.requestReview(from: "rae") |
| 152 | let write = try #require(box.seen.first { $0.method == "POST" }) |
| 153 | #expect(try argvOf(write) |
| 154 | == ["mr", "review", "request", "krz/gitbay", "7", "--add", "rae"]) |
| 155 | } |
| 156 | |
| 157 | @Test func removingAReviewRequestUsesRemoveNotAdd() async throws { |
| 158 | let (model, box) = try await loaded() |
| 159 | ok(box) |
| 160 | await model.removeReviewRequest("rae") |
| 161 | let write = try #require(box.seen.first { $0.method == "POST" }) |
| 162 | #expect(try argvOf(write) |
| 163 | == ["mr", "review", "request", "krz/gitbay", "7", "--remove", "rae"]) |
| 164 | } |
| 165 | |
| 166 | @Test func branchesComeFromRepoRefs() async throws { |
| 167 | let (model, box) = try await loaded() |
| 168 | box.enqueue(.init(status: 200, json: """ |
| 169 | {"protocol_version":1,"data":{"branches":[{"name":"main","sha":"a"},\ |
| 170 | {"name":"release","sha":"b"}],"tags":[]},"exit_code":0} |
| 171 | """)) |
| 172 | await model.loadBranches() |
| 173 | #expect(model.branches == ["main", "release"]) |
| 174 | } |
| 175 | |
| 176 | @Test func aRefusalSurfacesAndDoesNotReload() async throws { |
| 177 | let (model, box) = try await loaded() |
| 178 | box.enqueue(.init(status: 200, json: """ |
| 179 | {"protocol_version":1,"error":"cannot retarget a merged merge request",\ |
| 180 | "exit_code":1} |
| 181 | """)) |
| 182 | await model.retarget(to: "release") |
| 183 | #expect(model.actionError?.isEmpty == false) |
| 184 | #expect(model.working == false) |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | @MainActor |
| 189 | struct MRCreateDraftTests { |
| 190 | |
| 191 | @Test func creatingAsADraftAppendsTheFlag() async throws { |
| 192 | let box = StubProtocol.box() |
| 193 | let client = GitbayClient( |
| 194 | instance: try GitbayInstance(url: "https://gitbay.org"), |
| 195 | token: "test-token", |
| 196 | session: box.session() |
| 197 | ) |
| 198 | box.enqueue(.init(status: 200, json: """ |
| 199 | {"protocol_version":1,"data":{"number":7},"exit_code":0} |
| 200 | """)) |
| 201 | let model = MRCreateViewModel(client: client, repoPath: "krz/gitbay") |
| 202 | model.draft = true |
| 203 | _ = await model.create(source: "feat", target: "main", title: "a change", body: "") |
| 204 | |
| 205 | let write = try #require(box.seen.first { $0.method == "POST" }) |
| 206 | let body = try #require( |
| 207 | try JSONSerialization.jsonObject(with: write.body) as? [String: Any] |
| 208 | ) |
| 209 | let argv = try #require(body["argv"] as? [String]) |
| 210 | #expect(argv.contains("--draft")) |
| 211 | } |
| 212 | |
| 213 | @Test func creatingWithoutDraftOmitsTheFlag() async throws { |
| 214 | let box = StubProtocol.box() |
| 215 | let client = GitbayClient( |
| 216 | instance: try GitbayInstance(url: "https://gitbay.org"), |
| 217 | token: "test-token", |
| 218 | session: box.session() |
| 219 | ) |
| 220 | box.enqueue(.init(status: 200, json: """ |
| 221 | {"protocol_version":1,"data":{"number":7},"exit_code":0} |
| 222 | """)) |
| 223 | let model = MRCreateViewModel(client: client, repoPath: "krz/gitbay") |
| 224 | _ = await model.create(source: "feat", target: "main", title: "a change", body: "") |
| 225 | |
| 226 | let write = try #require(box.seen.first { $0.method == "POST" }) |
| 227 | let body = try #require( |
| 228 | try JSONSerialization.jsonObject(with: write.body) as? [String: Any] |
| 229 | ) |
| 230 | #expect((body["argv"] as? [String])?.contains("--draft") == false) |
| 231 | } |
| 232 | } |