gitbayTests/OrgDeleteMRPruneTests.swift

v1.14.1
gitbay-ios/gitbayTests/OrgDeleteMRPruneTests.swift history · blame · raw

101 lines · 4670 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 writes(_ stub: StubProtocol.Box) throws -> [[String]] {
 16    try stub.seen.filter { $0.url.path().hasSuffix("/cmd") }.map {
 17        let body = try #require(try JSONSerialization.jsonObject(with: $0.body) as? [String: Any])
 18        return try #require(body["argv"] as? [String])
 19    }
 20}
 21
 22private let mergedJSON = """
 23    {"protocol_version":1,"data":{"number":7,"title":"client: envelope decoding",\
 24    "state":"merged","author":"cmc","source":"client-envelope","target_ref":"main",\
 25    "head_sha":"aabbcc","created_at":"2026-08-20T10:00:00.000Z",\
 26    "merged_at":"2026-08-21T09:15:00.000Z","merged_by":"cmc"},"exit_code":0}
 27    """
 28
 29@MainActor
 30struct OrgDeleteTests {
 31
 32    @Test func deleteConfirmsAndTellsOtherScreens() async throws {
 33        let (client, stub) = try makeClient()
 34        stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":{"deleted":"acme"},"exit_code":0}"#, match: "cmd"))
 35        let model = OrgViewModel(client: client, orgName: "acme")
 36        var noticed: String?
 37        let token = NotificationCenter.default.addObserver(forName: .gitbayOrganizationsChanged, object: nil, queue: nil) {
 38            noticed = $0.object as? String
 39        }
 40        defer { NotificationCenter.default.removeObserver(token) }
 41
 42        #expect(await model.delete())
 43
 44        #expect(try writes(stub) == [["org", "delete", "acme", "--yes"]])
 45        #expect(noticed == "acme")
 46    }
 47
 48    @Test func aNonEmptyOrganizationIsRefusedWithTheReason() async throws {
 49        let (client, stub) = try makeClient()
 50        let refusal = #"{"protocol_version":1,"error":"the organization still owns 2 repositories; delete or transfer them first","exit_code":1}"#
 51        stub.enqueue(.init(status: 500, json: refusal, match: "cmd"))
 52        stub.enqueue(.init(status: 500, json: refusal, match: "cmd"))
 53        let model = OrgViewModel(client: client, orgName: "acme")
 54
 55        #expect(await model.delete() == false)
 56        #expect(model.actionError == "the organization still owns 2 repositories; delete or transfer them first")
 57    }
 58
 59    @Test func aRetryThatFindsItGoneIsDone() async throws {
 60        let (client, stub) = try makeClient()
 61        stub.enqueue(.init(status: 500, json: #"{"protocol_version":1,"error":"database is locked","exit_code":1}"#, match: "cmd"))
 62        stub.enqueue(.init(status: 404, json: #"{"protocol_version":1,"error":"no organization acme","exit_code":3}"#, match: "cmd"))
 63        let model = OrgViewModel(client: client, orgName: "acme")
 64
 65        #expect(await model.delete())
 66    }
 67}
 68
 69@MainActor
 70struct MRPruneTests {
 71
 72    @Test func pruneNamesTheMergeRequestAndConfirms() async throws {
 73        let (client, stub) = try makeClient()
 74        stub.enqueue(.init(status: 200, json: mergedJSON, match: "argv=show"))
 75        stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":{"user":"cmc","admin":true},"exit_code":0}"#, match: "argv=whoami"))
 76        stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":[{"number":7,"head":"abc123"}],"exit_code":0}"#, match: "cmd"))
 77        stub.enqueue(.init(status: 200, json: mergedJSON, match: "argv=show"))
 78        let model = MRDetailViewModel(client: client, repoPath: "krz/gitbay", number: 7)
 79        await model.load()
 80        await model.checkCanPruneHead()
 81        #expect(model.canPruneHead, "an admin on a merged merge request is offered the prune")
 82
 83        await model.pruneHead()
 84
 85        #expect(try writes(stub) == [["admin", "mr", "prune", "krz/gitbay", "7", "--yes"]])
 86        #expect(model.actionError == nil)
 87        #expect(model.canPruneHead == false, "a pruned head is not offered again")
 88    }
 89
 90    @Test func aStillMergeableHeadIsRefusedWithTheReason() async throws {
 91        let (client, stub) = try makeClient()
 92        let refusal = #"{"protocol_version":1,"error":"!7 is still mergeable and its head is what makes it so; merge or close it first","exit_code":1}"#
 93        stub.enqueue(.init(status: 500, json: refusal, match: "cmd"))
 94        stub.enqueue(.init(status: 500, json: refusal, match: "cmd"))
 95        let model = MRDetailViewModel(client: client, repoPath: "krz/gitbay", number: 7)
 96
 97        await model.pruneHead()
 98
 99        #expect(model.actionError == "!7 is still mergeable and its head is what makes it so; merge or close it first")
100    }
101}