gitbayTests/ProfileCommitTests.swift
130 lines · 5020 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 let userProfileJSON = """
16 {"protocol_version":1,"data":{"name":"cmc","kind":"user",\
17 "description":"Org-Mode · Self-Hosting · Privacy","website":"https://cleberg.net",\
18 "orgs":[{"name":"krz","role":"admin"},{"name":"audit-labs","role":"admin"}],\
19 "repos":[{"path":"cmc/notes","visibility":"private","description":"notes"},\
20 {"path":"cmc/cv","visibility":"public"}],\
21 "activity":[{"date":"2026-08-01","count":3},{"date":"2026-08-02","count":12}],\
22 "activity_total":15},"exit_code":0}
23 """
24
25private let orgProfileJSON = """
26 {"protocol_version":1,"data":{"name":"krz","kind":"org","description":"warez",\
27 "members":[{"name":"cmc","role":"admin"}],"repos":[],"activity_total":0},"exit_code":0}
28 """
29
30@MainActor
31struct ProfileAggregateTests {
32
33 @Test func aUserProfileCarriesOrgsReposAndActivity() async throws {
34 let (client, stub) = try makeClient()
35 stub.enqueue(.init(status: 200, json: userProfileJSON))
36 let model = ProfileViewModel(client: client, name: "cmc")
37
38 await model.load()
39
40 let profile = try #require(model.state.value)
41 #expect(!profile.isOrg)
42 #expect(profile.orgs?.map(\.name) == ["krz", "audit-labs"])
43 #expect(profile.repos.map(\.path) == ["cmc/notes", "cmc/cv"])
44 #expect(profile.repos[0].isPrivate)
45 #expect(profile.activityTotal == 15)
46 // One read builds the whole page.
47 #expect(stub.seen.count == 1)
48 #expect(stub.seen[0].url.query() == "argv=profile&argv=show&argv=cmc")
49 }
50
51 @Test func anOrgProfileCarriesMembersInsteadOfOrgs() async throws {
52 let (client, stub) = try makeClient()
53 stub.enqueue(.init(status: 200, json: orgProfileJSON))
54 let model = ProfileViewModel(client: client, name: "krz")
55
56 await model.load()
57
58 let profile = try #require(model.state.value)
59 #expect(profile.isOrg)
60 #expect(profile.members?.map(\.name) == ["cmc"])
61 #expect(profile.orgs == nil)
62 #expect(profile.repos.isEmpty)
63 }
64
65 @Test func anUnknownNameIsAnEmptyState() async throws {
66 let (client, stub) = try makeClient()
67 stub.enqueue(.init(status: 404, json:
68 #"{"protocol_version":1,"error":"no user or organization \"nobody\"","exit_code":3}"#))
69 let model = ProfileViewModel(client: client, name: "nobody")
70
71 await model.load()
72
73 guard case .empty = model.state else {
74 Issue.record("expected .empty, got \(model.state)")
75 return
76 }
77 }
78}
79
80@MainActor
81struct CommitDetailTests {
82
83 private let commitJSON = """
84 {"protocol_version":1,"data":{"path":"krz/gitbay",\
85 "sha":"59ae14400000000000000000000000000000abcd",\
86 "subject":"control: profile aggregate","message":"Body text.",\
87 "author_name":"Christian Cleberg","author_email":"hello@cleberg.net",\
88 "date":"2026-08-28T02:00:00Z",\
89 "signature":{"state":"verified","signer":"cmc"},\
90 "checks":[{"context":"ci","state":"success"}],\
91 "diff":"diff --git a/main.go b/main.go\\n--- a/main.go\\n+++ b/main.go\\n@@ -1,2 +1,3 @@\\n package main\\n+// added\\n"},\
92 "exit_code":0}
93 """
94
95 @Test func loadsTheCommitAndParsesItsPatch() async throws {
96 let (client, stub) = try makeClient()
97 stub.enqueue(.init(status: 200, json: commitJSON))
98 let model = CommitDetailViewModel(
99 client: client, repoPath: "krz/gitbay", sha: "59ae144")
100
101 await model.load()
102
103 let commit = try #require(model.state.value)
104 #expect(commit.subject == "control: profile aggregate")
105 #expect(commit.signature.state == .verified)
106 #expect(commit.checks?.first?.state == "success")
107 #expect(commit.shortSHA == "59ae144000")
108 // The patch is parsed by the same parser mr diff uses.
109 let diff = try #require(model.diff)
110 #expect(diff.files.map(\.displayPath) == ["main.go"])
111 #expect(diff.additions == 1)
112 #expect(stub.seen.first?.url.query() ==
113 "argv=repo&argv=commit&argv=krz/gitbay&argv=59ae144")
114 }
115
116 @Test func anUnknownShaIsAnEmptyState() async throws {
117 let (client, stub) = try makeClient()
118 stub.enqueue(.init(status: 404, json:
119 #"{"protocol_version":1,"error":"no commit \"deadbeef\" in krz/gitbay","exit_code":3}"#))
120 let model = CommitDetailViewModel(
121 client: client, repoPath: "krz/gitbay", sha: "deadbeef")
122
123 await model.load()
124
125 guard case .empty = model.state else {
126 Issue.record("expected .empty, got \(model.state)")
127 return
128 }
129 }
130}