| @@ -39,11 +39,11 @@ private let adminDashboardJSON = """ |
| 39 | 39 | }},"exit_code":0} |
| 40 | 40 | """ |
| 41 | 41 | |
| 42 | | nonisolated private struct Envelope: Decodable { let data: AdminDashboard } |
| 42 | nonisolated private struct DashboardEnvelope: Decodable { let data: AdminDashboard } |
| 43 | 43 | |
| 44 | 44 | private func sections() throws -> [QueueSection] { |
| 45 | 45 | let dashboard = try GitbayClient.decoder() |
| 46 | | .decode(Envelope.self, from: Data(adminDashboardJSON.utf8)).data |
| 46 | .decode(DashboardEnvelope.self, from: Data(adminDashboardJSON.utf8)).data |
| 47 | 47 | let queues = try #require(dashboard.queues) |
| 48 | 48 | // A fixed stand-in for relative time keeps the text deterministic. |
| 49 | 49 | return queues.sections(when: { "at \($0)" }) |
| @@ -94,7 +94,7 @@ struct AdminQueueSectionTests { |
| 94 | 94 | /// Everyone else's dashboard omits both blocks. |
| 95 | 95 | @Test func aNonAdminDashboardHasNoQueues() throws { |
| 96 | 96 | let json = #"{"protocol_version":1,"data":{"unread":3},"exit_code":0}"# |
| 97 | | let dashboard = try GitbayClient.decoder().decode(Envelope.self, from: Data(json.utf8)).data |
| 97 | let dashboard = try GitbayClient.decoder().decode(DashboardEnvelope.self, from: Data(json.utf8)).data |
| 98 | 98 | #expect(dashboard.queues == nil) |
| 99 | 99 | #expect(dashboard.server == nil) |
| 100 | 100 | } |
| @@ -243,6 +243,18 @@ struct AdminUsersViewModelTests { |
| 243 | 243 | #expect(stub.seen.first?.url.query() == "argv=admin&argv=user&argv=list&argv=--limit&argv=50") |
| 244 | 244 | } |
| 245 | 245 | |
| 246 | @Test func loadIfNeededOnlyReadsOnce() async throws { |
| 247 | let (client, stub) = try makeClient() |
| 248 | stub.enqueue(.init(status: 200, json: userPageJSON)) |
| 249 | let model = AdminUsersViewModel(client: client) |
| 250 | |
| 251 | await model.loadIfNeeded() |
| 252 | await model.loadIfNeeded() |
| 253 | |
| 254 | #expect(stub.seen.count == 1) |
| 255 | #expect(stub.seen.first?.url.query() == "argv=admin&argv=user&argv=list&argv=--limit&argv=50") |
| 256 | } |
| 257 | |
| 246 | 258 | @Test func aFilterReloadsWithItsState() async throws { |
| 247 | 259 | let (client, stub) = try makeClient() |
| 248 | 260 | stub.enqueue(.init(status: 200, json: userPageJSON)) |
| @@ -277,14 +289,36 @@ struct AdminUsersViewModelTests { |
| 277 | 289 | #expect(model.list.state.value?.count == 2) |
| 278 | 290 | } |
| 279 | 291 | |
| 292 | /// The last-admin refusal is a real server failure (exit 1, HTTP 500), |
| 293 | /// which the client retries once before surfacing it. |
| 280 | 294 | @Test func aRefusalIsShownVerbatim() async throws { |
| 281 | 295 | let (client, stub) = try makeClient() |
| 282 | | stub.enqueue(.init(status: 200, json: |
| 283 | | #"{"protocol_version":1,"error":"cmc is the last admin","exit_code":4}"#, match: "cmd")) |
| 296 | let refusal = StubProtocol.Stub(status: 500, json: |
| 297 | #"{"protocol_version":1,"error":"that is the only instance admin; promote someone else first","exit_code":1}"#, |
| 298 | match: "cmd") |
| 299 | stub.enqueue(refusal) |
| 300 | stub.enqueue(refusal) |
| 301 | stub.enqueue(.init(status: 200, json: userPageJSON, match: "argv=list")) |
| 284 | 302 | let model = AdminUsersViewModel(client: client) |
| 285 | 303 | |
| 286 | 304 | await model.perform(.demote, on: "cmc") |
| 287 | 305 | |
| 288 | | #expect(model.actionError == "cmc is the last admin") |
| 306 | #expect(model.actionError == "that is the only instance admin; promote someone else first") |
| 307 | #expect(model.list.state.value?.count == 2) |
| 308 | } |
| 309 | |
| 310 | /// A refusal must still reload the list, so a stale row does not |
| 311 | /// linger after the account changed state meanwhile. |
| 312 | @Test func aRefusedActionStillReloadsTheList() async throws { |
| 313 | let (client, stub) = try makeClient() |
| 314 | stub.enqueue(.init(status: 403, json: |
| 315 | #"{"protocol_version":1,"error":"account changed state","exit_code":4}"#, match: "cmd")) |
| 316 | stub.enqueue(.init(status: 200, json: userPageJSON, match: "argv=list")) |
| 317 | let model = AdminUsersViewModel(client: client) |
| 318 | |
| 319 | await model.perform(.disable, on: "bhargavkk") |
| 320 | |
| 321 | #expect(model.actionError == "account changed state") |
| 322 | #expect(stub.seen.contains { $0.url.query()?.contains("argv=list") == true }) |
| 289 | 323 | } |
| 290 | 324 | } |