gitbayTests/RequiredContextsTests.swift
95 lines · 3719 bytes
9 symbols in this file
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 postedArgv(_ stub: StubProtocol.Box) throws -> [[String]] {
16 try stub.seen.filter { $0.method == "POST" }.map { seen in
17 let body = try #require(try JSONSerialization.jsonObject(with: seen.body) as? [String: Any])
18 return try #require(body["argv"] as? [String])
19 }
20}
21
22private let settingsJSON = """
23 {"protocol_version":1,"data":{"protected_branches":["main"],"require_checks":true,\
24 "required_contexts":["ci/test","ext/lint"]},"exit_code":0}
25 """
26
27private let repoShowJSON = """
28 {"protocol_version":1,"data":{"path":"krz/gitbay","description":"a forge",\
29 "visibility":"public","default_branch":"main"},"exit_code":0}
30 """
31
32private let ok = #"{"protocol_version":1,"data":{},"exit_code":0}"#
33
34@MainActor
35struct RequiredContextsTests {
36
37 private func loaded() async throws -> (RepoSettingsViewModel, StubProtocol.Box) {
38 let (client, stub) = try makeClient()
39 stub.enqueue(.init(status: 200, json: settingsJSON, match: "argv=settings"))
40 stub.enqueue(.init(status: 200, json: repoShowJSON, match: "argv=show&argv=krz"))
41 let model = RepoSettingsViewModel(client: client, repoPath: "krz/gitbay")
42 await model.load()
43 return (model, stub)
44 }
45
46 @Test func requiredContextsDecode() async throws {
47 let (model, _) = try await loaded()
48 #expect(model.state.value?.settings.contexts == ["ci/test", "ext/lint"])
49 }
50
51 @Test func addingAContextSendsTheWholeListWithItAppended() async throws {
52 let (model, stub) = try await loaded()
53 stub.enqueue(.init(status: 200, json: ok, match: "cmd"))
54
55 let cleared = await model.addRequiredContext(" ci/build ")
56
57 #expect(cleared)
58 #expect(try postedArgv(stub) == [["repo", "settings", "require-contexts", "krz/gitbay",
59 "ci/test", "ext/lint", "ci/build"]])
60 }
61
62 @Test func addingOneAlreadyListedSendsNothing() async throws {
63 let (model, stub) = try await loaded()
64
65 await model.addRequiredContext("ci/test")
66
67 #expect(try postedArgv(stub).isEmpty)
68 }
69
70 @Test func aRefusedContextIsNotClearedFromTheField() async throws {
71 let (model, stub) = try await loaded()
72 stub.enqueue(.init(status: 400, json: #"{"protocol_version":1,"error":"at most 20 required contexts","exit_code":2}"#, match: "cmd"))
73
74 let cleared = await model.addRequiredContext("ci/more")
75
76 #expect(!cleared)
77 // The name was typed, so the refusal is shown as the server said it.
78 #expect(model.actionError == "at most 20 required contexts")
79 }
80
81 @Test func removingTheLastContextSendsAnEmptyList() async throws {
82 let (client, stub) = try makeClient()
83 stub.enqueue(.init(status: 200, json: """
84 {"protocol_version":1,"data":{"require_checks":true,"required_contexts":["ci/test"]},"exit_code":0}
85 """, match: "argv=settings"))
86 stub.enqueue(.init(status: 200, json: repoShowJSON, match: "argv=show&argv=krz"))
87 let model = RepoSettingsViewModel(client: client, repoPath: "krz/gitbay")
88 await model.load()
89 stub.enqueue(.init(status: 200, json: ok, match: "cmd"))
90
91 await model.removeRequiredContext("ci/test")
92
93 #expect(try postedArgv(stub) == [["repo", "settings", "require-contexts", "krz/gitbay"]])
94 }
95}