git.iko.soy/parseable-macos
on 2026.06.20.1438 help sign in
parseable-macos/ParseableViewerTests/ConnectionStoreTests.swift
123 lines 4.6 kB history raw
1import XCTest
2@testable import ParseableViewer
4final class ConnectionStoreTests: XCTestCase {
5 /// Whether the data-protection keychain is available in this environment.
6 /// CI runners may lack the entitlements needed for `kSecUseDataProtectionKeychain`.
7 private static let keychainAvailable: Bool = {
8 let probe = "keychain-probe"
9 let saved = KeychainService.saveData(Data(probe.utf8), for: "_test_probe")
10 if saved {
11 KeychainService.deleteData(for: "_test_probe")
12 }
13 return saved
14 }()
16 override func tearDown() {
17 super.tearDown()
18 KeychainService.deleteData(for: "parseable_connections_list")
19 KeychainService.deleteData(for: "parseable_active_connection_id")
20 }
22 func testSaveNilActiveConnectionID() throws {
23 try XCTSkipUnless(Self.keychainAvailable, "Data-protection keychain unavailable")
24 let id = UUID()
25 ConnectionStore.saveActiveConnectionID(id)
26 ConnectionStore.saveActiveConnectionID(nil)
27 let loaded = ConnectionStore.loadActiveConnectionID()
28 XCTAssertNil(loaded)
29 }
31 func testLoadActiveConnectionIDWhenEmpty() {
32 KeychainService.deleteData(for: "parseable_active_connection_id")
33 let loaded = ConnectionStore.loadActiveConnectionID()
34 XCTAssertNil(loaded)
35 }
37 // MARK: - Connection persistence
39 func testLoadConnectionsWhenEmpty() {
40 KeychainService.deleteData(for: "parseable_connections_list")
41 let connections = ConnectionStore.loadConnections()
42 XCTAssertTrue(connections.isEmpty)
43 }
45 func testSaveAndLoadActiveConnectionID() throws {
46 try XCTSkipUnless(Self.keychainAvailable, "Data-protection keychain unavailable")
47 let id = UUID()
48 ConnectionStore.saveActiveConnectionID(id)
49 let loaded = ConnectionStore.loadActiveConnectionID()
50 XCTAssertEqual(loaded, id)
51 }
53 func testRoundTripConnectionPersistence() throws {
54 try XCTSkipUnless(Self.keychainAvailable, "Data-protection keychain unavailable")
55 let conn = ServerConnection(
56 name: "Prod",
57 url: "https://logs.example.com",
58 username: "admin",
59 password: "s3cret!"
60 )
61 ConnectionStore.saveConnections([conn])
63 let loaded = ConnectionStore.loadConnections()
64 XCTAssertEqual(loaded.count, 1)
65 XCTAssertEqual(loaded.first?.id, conn.id)
66 XCTAssertEqual(loaded.first?.name, "Prod")
67 XCTAssertEqual(loaded.first?.url, "https://logs.example.com")
68 XCTAssertEqual(loaded.first?.username, "admin")
69 XCTAssertEqual(loaded.first?.password, "s3cret!")
71 // Clean up
72 KeychainService.deletePassword(for: conn.id)
73 }
75 func testRoundTripMultipleConnections() throws {
76 try XCTSkipUnless(Self.keychainAvailable, "Data-protection keychain unavailable")
77 let conn1 = ServerConnection(
78 name: "Server A",
79 url: "https://a.example.com",
80 username: "user1",
81 password: "pass1"
82 )
83 let conn2 = ServerConnection(
84 name: "Server B",
85 url: "https://b.example.com",
86 username: "user2",
87 password: "pass2"
88 )
89 ConnectionStore.saveConnections([conn1, conn2])
91 let loaded = ConnectionStore.loadConnections()
92 XCTAssertEqual(loaded.count, 2)
94 let loadedA = loaded.first { $0.id == conn1.id }
95 let loadedB = loaded.first { $0.id == conn2.id }
96 XCTAssertEqual(loadedA?.name, "Server A")
97 XCTAssertEqual(loadedA?.password, "pass1")
98 XCTAssertEqual(loadedB?.name, "Server B")
99 XCTAssertEqual(loadedB?.password, "pass2")
101 // Clean up
102 KeychainService.deletePassword(for: conn1.id)
103 KeychainService.deletePassword(for: conn2.id)
104 }
106 func testPasswordNotInKeychainConnectionData() throws {
107 try XCTSkipUnless(Self.keychainAvailable, "Data-protection keychain unavailable")
108 let connection = ServerConnection(
109 name: "Test",
110 url: "https://example.com",
111 username: "admin",
112 password: "supersecret"
113 )
114 ConnectionStore.saveConnections([connection])
116 // Verify password is NOT in the connections list data
117 if let data = KeychainService.loadData(for: "parseable_connections_list"),
118 let jsonString = String(data: data, encoding: .utf8) {
119 XCTAssertFalse(jsonString.contains("supersecret"),
120 "Password should not be stored in connection list JSON")
121 }
122 }