gitbayTests/AdminInstanceTests.swift
97 lines · 4326 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 argv(_ url: URL) -> [String] {
16 URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.compactMap(\.value) ?? []
17}
18
19@MainActor
20struct AdminInstanceTests {
21
22 @Test func statsDecode() async throws {
23 let (client, stub) = try makeClient()
24 stub.enqueue(.init(status: 200, json: """
25 {"protocol_version":1,"data":{"counts":{"users":3,"orgs":1,"repos":1,"issues":1,"open_issues":1,\
26 "mrs":4,"open_mrs":2},"db_bytes":876544,"repo_bytes":38159,"lfs_bytes":0,\
27 "repos":[{"path":"sim/app","bytes":38159}]}}
28 """))
29 let model = AdminInstanceViewModel(client: client)
30 await model.loadStats()
31 let stats = try #require(model.stats.value)
32 #expect(stats.counts.openMRs == 2)
33 #expect(stats.repos.first?.bytes == 38159)
34 #expect(argv(try #require(stub.seen.first).url) == ["admin", "stats"])
35 }
36
37 @Test func auditTakesTheTypedFilters() async throws {
38 let (client, stub) = try makeClient()
39 stub.enqueue(.init(status: 200, json: """
40 {"protocol_version":1,"data":[{"id":172,"actor":"sim","action":"cmd admin org limits",\
41 "data":"{}","created_at":"2026-10-14T08:09:23.929Z"}]}
42 """))
43 let model = AdminInstanceViewModel(client: client)
44 model.auditActor = " sim "
45 model.auditAction = "cmd admin"
46 model.auditSince = ""
47
48 await model.loadAudit()
49
50 #expect(argv(try #require(stub.seen.first).url) == ["audit", "--actor", "sim", "--action", "cmd admin", "--limit", "200"])
51 #expect(model.audit.value?.first?.action == "cmd admin org limits")
52 }
53
54 @Test func aBadSinceSaysWhy() async throws {
55 let (client, stub) = try makeClient()
56 stub.enqueue(.init(status: 400, json: #"{"protocol_version":1,"error":"--since takes a duration like 24h or a date","exit_code":2}"#))
57 let model = AdminInstanceViewModel(client: client)
58 model.auditSince = "yesterday"
59
60 await model.loadAudit()
61
62 guard case .failed(let message) = model.audit else { Issue.record("not failed"); return }
63 #expect(message == "--since takes a duration like 24h or a date")
64 }
65
66 @Test func runnersDecodeAndRemoveByFingerprint() async throws {
67 let (client, stub) = try makeClient()
68 let json = """
69 {"protocol_version":1,"data":{"queue":{"pending":3,"claimed_24h":1,"claim_wait_avg_s":1,\
70 "claim_wait_max_s":1,"reaped_24h":0},"runners":[{"username":"ci","fingerprint":"SHA256:r",\
71 "last_seen":"2026-10-14T05:36:28.062Z","scope":"krz/gitbay","build_repo":"krz/gitbay",\
72 "build_number":9,"build_job":"test"}]}}
73 """
74 stub.enqueue(.init(status: 200, json: json, match: "argv=runners"))
75 stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":{},"exit_code":0}"#, match: "cmd"))
76 stub.enqueue(.init(status: 200, json: json, match: "argv=runners"))
77 let model = AdminInstanceViewModel(client: client)
78 await model.loadRunners()
79 let runner = try #require(model.runners.value?.runners.first)
80 #expect(runner.buildNumber == 9)
81 #expect(model.runners.value?.queue.pending == 3)
82
83 await model.removeRunner(runner)
84
85 let post = try #require(stub.seen.first { $0.method == "POST" })
86 let body = try #require(try JSONSerialization.jsonObject(with: post.body) as? [String: Any])
87 #expect(body["argv"] as? [String] == ["admin", "runners", "remove", "SHA256:r"])
88 }
89
90 @Test func inboundCheckDecodesOff() async throws {
91 let (client, stub) = try makeClient()
92 stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":{"enabled":false,"messages":0,"unseen":0,"require_dkim":false}}"#))
93 let model = AdminInstanceViewModel(client: client)
94 await model.checkInbound()
95 #expect(model.inbound.value?.enabled == false)
96 }
97}