a native ios client for gitbay

client ios swift

https://gitbay.org

gitbayTests/ProfileCommitTests.swift

ui-smoke
gitbay-ios/gitbayTests/ProfileCommitTests.swift history · blame · raw

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}