gitbayTests/WebhookTests.swift

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

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