gitbayTests/WebhookTests.swift

v1.1.0
gitbay-ios/gitbayTests/WebhookTests.swift history · blame · raw

146 lines · 6009 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 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 okJSON = #"{"protocol_version":1,"data":{},"exit_code":0}"#
 21
 22private let hooksJSON = """
 23    {"protocol_version":1,"data":[\
 24    {"id":4,"url":"https://ci.example/hook","events":"*","active":true,"has_secret":true},\
 25    {"id":9,"url":"https://chat.example/in","events":"push,issue.created","active":false,"has_secret":false}\
 26    ],"exit_code":0}
 27    """
 28
 29private let deliveriesJSON = """
 30    {"protocol_version":1,"data":[\
 31    {"id":71,"url":"https://ci.example/hook","event":"push","status":"delivered","attempts":1,"last_status":200},\
 32    {"id":70,"url":"https://chat.example/in","event":"issue.created","status":"failed","attempts":3,\
 33    "last_status":502,"last_error":"bad gateway"}\
 34    ],"exit_code":0}
 35    """
 36
 37@MainActor
 38struct WebhookListViewModelTests {
 39
 40    private func loaded() async throws -> (WebhookListViewModel, StubProtocol.Box) {
 41        let (client, stub) = try makeClient()
 42        stub.enqueue(.init(status: 200, json: hooksJSON, match: "argv=list"))
 43        let model = WebhookListViewModel(client: client, repoPath: "krz/gitbay")
 44        await model.load()
 45        return (model, stub)
 46    }
 47
 48    @Test func listsHooksWithTheirEventsAndSecretFlag() async throws {
 49        let (model, stub) = try await loaded()
 50
 51        let hooks = try #require(model.state.value)
 52        #expect(hooks.map(\.id) == [4, 9])
 53        #expect(hooks[0].hasSecret && hooks[0].active)
 54        #expect(hooks[1].events == "push,issue.created")
 55        #expect(stub.seen.first?.url.query() == "argv=webhook&argv=list&argv=krz/gitbay")
 56    }
 57
 58    @Test func noHooksIsAnEmptyState() async throws {
 59        let (client, stub) = try makeClient()
 60        stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":[],"exit_code":0}"#))
 61        let model = WebhookListViewModel(client: client, repoPath: "krz/gitbay")
 62
 63        await model.load()
 64
 65        guard case .empty = model.state else {
 66            Issue.record("expected .empty, got \(model.state)")
 67            return
 68        }
 69    }
 70
 71    @Test func addSendsSecretAndEventsOnlyWhenGiven() async throws {
 72        let (model, stub) = try await loaded()
 73        for _ in 0..<2 {
 74            stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":{"id":10,"url":"u","events":"*"},"exit_code":0}"#, match: "cmd"))
 75            stub.enqueue(.init(status: 200, json: hooksJSON, match: "argv=list"))
 76        }
 77
 78        let first = await model.add(url: " https://a.example/h ", secret: "s3", events: "push")
 79        let second = await model.add(url: "https://b.example/h", secret: "", events: "")
 80
 81        #expect(first && second)
 82        let writes = try stub.seen.filter { $0.method == "POST" }.map(argvOf)
 83        #expect(writes == [
 84            ["webhook", "add", "krz/gitbay", "https://a.example/h", "--secret", "s3", "--events", "push"],
 85            ["webhook", "add", "krz/gitbay", "https://b.example/h"],
 86        ])
 87    }
 88
 89    @Test func removeSendsTheIdThenReloads() async throws {
 90        let (model, stub) = try await loaded()
 91        stub.enqueue(.init(status: 200, json: okJSON, match: "cmd"))
 92        stub.enqueue(.init(status: 200, json: hooksJSON, match: "argv=list"))
 93
 94        await model.remove(id: 9)
 95
 96        let write = try #require(stub.seen.first { $0.method == "POST" })
 97        #expect(try argvOf(write) == ["webhook", "remove", "krz/gitbay", "9"])
 98        #expect(stub.seen.filter { $0.method == "GET" }.count == 2)
 99    }
100
101    @Test func aRefusedAddSurfacesAndReturnsFalse() async throws {
102        let (model, stub) = try await loaded()
103        stub.enqueue(.init(status: 403, json: #"{"protocol_version":1,"error":"admin access required","exit_code":4}"#, match: "cmd"))
104
105        let ok = await model.add(url: "https://a.example/h", secret: "", events: "")
106
107        #expect(!ok)
108        #expect(model.actionError == "admin access required")
109    }
110}
111
112@MainActor
113struct WebhookDeliveriesViewModelTests {
114
115    @Test func listsDeliveriesWithStatusAndLastError() async throws {
116        let (client, stub) = try makeClient()
117        stub.enqueue(.init(status: 200, json: deliveriesJSON))
118        let model = WebhookDeliveriesViewModel(client: client, repoPath: "krz/gitbay")
119
120        await model.load()
121
122        let rows = try #require(model.state.value)
123        #expect(rows.map(\.id) == [71, 70])
124        #expect(rows[1].status == "failed")
125        #expect(rows[1].lastStatus == 502)
126        #expect(rows[1].lastError == "bad gateway")
127        #expect(rows[0].lastError == nil)
128        #expect(stub.seen.first?.url.query() == "argv=webhook&argv=deliveries&argv=krz/gitbay")
129    }
130
131    @Test func redeliverSendsTheDeliveryIdThenReloads() async throws {
132        let (client, stub) = try makeClient()
133        stub.enqueue(.init(status: 200, json: deliveriesJSON, match: "argv=deliveries"))
134        let model = WebhookDeliveriesViewModel(client: client, repoPath: "krz/gitbay")
135        await model.load()
136        stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":{"requeued":70},"exit_code":0}"#, match: "cmd"))
137        stub.enqueue(.init(status: 200, json: deliveriesJSON, match: "argv=deliveries"))
138
139        await model.redeliver(id: 70)
140
141        let write = try #require(stub.seen.first { $0.method == "POST" })
142        #expect(try argvOf(write) == ["webhook", "redeliver", "krz/gitbay", "70"])
143        #expect(stub.seen.filter { $0.method == "GET" }.count == 2)
144        #expect(model.actionError == nil)
145    }
146}