| @@ -182,6 +182,56 @@ struct IssueDetailColorsTests { |
| 182 | 182 | #expect(model.state.value?.number == 7) |
| 183 | 183 | #expect(model.colors.hex("bug") == "#cf222e") |
| 184 | 184 | } |
| 185 | |
| 186 | /// Decoration must not take the screen down with it. |
| 187 | @Test func issueStillLoadsWhenTheLabelReadFails() async throws { |
| 188 | let (client, stub) = try makeClient() |
| 189 | stub.enqueue(.init(status: 200, json: """ |
| 190 | {"protocol_version":1,"data":{"number":7,"title":"a bug","state":"open",\ |
| 191 | "author":"cmc","labels":["bug"],"created_at":"2026-09-01T00:00:00Z"},\ |
| 192 | "exit_code":0} |
| 193 | """, match: "argv=show")) |
| 194 | stub.enqueue(.init(status: 403, json: """ |
| 195 | {"protocol_version":1,"error":"denied","exit_code":4} |
| 196 | """, match: "argv=label")) |
| 197 | |
| 198 | let model = IssueDetailViewModel(client: client, repoPath: "krz/gitbay", number: 7) |
| 199 | await model.load() |
| 200 | |
| 201 | #expect(model.state.value?.number == 7) |
| 202 | // Derived from the name, since nothing was stored. |
| 203 | #expect(model.colors.hex("bug") == "#b93a86") |
| 204 | } |
| 205 | |
| 206 | /// Regression test: `perform(argv:)` used to reload labels on every |
| 207 | /// write. Labels rarely change mid-session, so the read must happen |
| 208 | /// at most once per view model instance. |
| 209 | @Test func aWriteDoesNotReissueLabelList() async throws { |
| 210 | let (client, stub) = try makeClient() |
| 211 | stub.enqueue(.init(status: 200, json: """ |
| 212 | {"protocol_version":1,"data":{"number":7,"title":"a bug","state":"open",\ |
| 213 | "author":"cmc","labels":["bug"],"created_at":"2026-09-01T00:00:00Z"},\ |
| 214 | "exit_code":0} |
| 215 | """, match: "argv=show")) |
| 216 | stub.enqueue(.init(status: 200, json: labelListJSON, match: "argv=label")) |
| 217 | |
| 218 | let model = IssueDetailViewModel(client: client, repoPath: "krz/gitbay", number: 7) |
| 219 | await model.load() |
| 220 | |
| 221 | stub.enqueue(.init(status: 200, json: """ |
| 222 | {"protocol_version":1,"exit_code":0} |
| 223 | """, match: "argv=close")) |
| 224 | stub.enqueue(.init(status: 200, json: """ |
| 225 | {"protocol_version":1,"data":{"number":7,"title":"a bug","state":"closed",\ |
| 226 | "author":"cmc","labels":["bug"],"created_at":"2026-09-01T00:00:00Z"},\ |
| 227 | "exit_code":0} |
| 228 | """, match: "argv=show")) |
| 229 | |
| 230 | await model.close() |
| 231 | |
| 232 | let labelReads = stub.seen.filter { ($0.url.query() ?? "").contains("argv=label") } |
| 233 | #expect(labelReads.count == 1) |
| 234 | } |
| 185 | 235 | } |
| 186 | 236 | |
| 187 | 237 | @MainActor |
| @@ -284,4 +334,19 @@ struct LabelListViewModelTests { |
| 284 | 334 | #expect(model.actionError?.isEmpty == false) |
| 285 | 335 | #expect(model.working == false) |
| 286 | 336 | } |
| 337 | |
| 338 | @Test func aServerFailureSurfacesAsFailed() async throws { |
| 339 | let (client, stub) = try makeClient() |
| 340 | stub.enqueue(.init(status: 200, json: """ |
| 341 | {"protocol_version":1,"error":"boom","exit_code":1} |
| 342 | """)) |
| 343 | let model = LabelListViewModel(client: client, repoPath: "krz/gitbay") |
| 344 | |
| 345 | await model.load() |
| 346 | |
| 347 | guard case .failed = model.state else { |
| 348 | Testing.Issue.record("expected a failed state, got \(model.state)") |
| 349 | return |
| 350 | } |
| 351 | } |
| 287 | 352 | } |