gitbayTests/WebSessionTests.swift
72 lines · 3045 bytes
6 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 writes(_ stub: StubProtocol.Box) throws -> [[String]] {
16 try stub.seen.filter { $0.url.path().hasSuffix("/cmd") }.map {
17 let body = try #require(try JSONSerialization.jsonObject(with: $0.body) as? [String: Any])
18 return try #require(body["argv"] as? [String])
19 }
20}
21
22private let sessionsJSON = """
23 {"protocol_version":1,"data":[\
24 {"id":"a1b2c3d4e5f6","created_at":"2026-10-14T08:00:00.000Z","expires_at":"2026-11-13T08:00:00.000Z","last_used_at":"2026-10-15T09:00:00.000Z"},\
25 {"id":"0f9e8d7c6b5a","created_at":"2026-10-01T08:00:00.000Z","expires_at":"2026-10-31T08:00:00.000Z","last_used_at":"2026-10-01T08:00:00.000Z"}\
26 ],"exit_code":0}
27 """
28
29@MainActor
30struct WebSessionsViewModelTests {
31
32 @Test func listsSessionsNewestFirst() async throws {
33 let (client, stub) = try makeClient()
34 stub.enqueue(.init(status: 200, json: sessionsJSON))
35 let model = WebSessionsViewModel(client: client)
36 await model.load()
37
38 let sessions = try #require(model.state.value)
39 #expect(sessions.map(\.id) == ["a1b2c3d4e5f6", "0f9e8d7c6b5a"])
40 #expect(sessions[0].lastUsedAt > sessions[1].lastUsedAt)
41 #expect(stub.seen.first?.url.query() == "argv=web&argv=sessions&argv=list")
42 }
43
44 @Test func noSessionsIsAnEmptyState() async throws {
45 let (client, stub) = try makeClient()
46 stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":[],"exit_code":0}"#))
47 let model = WebSessionsViewModel(client: client)
48 await model.load()
49 guard case .empty = model.state else {
50 Issue.record("expected .empty, got \(model.state)")
51 return
52 }
53 }
54
55 @Test func revokeOneThenAll() async throws {
56 let (client, stub) = try makeClient()
57 stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":{"revoked":"a1b2c3d4e5f6"},"exit_code":0}"#, match: "cmd"))
58 stub.enqueue(.init(status: 200, json: sessionsJSON, match: "argv=list"))
59 stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":{"revoked":2},"exit_code":0}"#, match: "cmd"))
60 stub.enqueue(.init(status: 200, json: #"{"protocol_version":1,"data":[],"exit_code":0}"#, match: "argv=list"))
61 let model = WebSessionsViewModel(client: client)
62
63 await model.revoke(id: "a1b2c3d4e5f6")
64 await model.revokeAll()
65
66 #expect(try writes(stub) == [["web", "sessions", "revoke", "a1b2c3d4e5f6"], ["web", "sessions", "revoke", "--all"]])
67 guard case .empty = model.state else {
68 Issue.record("expected .empty after revoking all, got \(model.state)")
69 return
70 }
71 }
72}