Skip to content

Commit b0e40c3

Browse files
committed
add tests
1 parent dcb9dc6 commit b0e40c3

File tree

5 files changed

+510
-2
lines changed

5 files changed

+510
-2
lines changed

.github/workflows/integration-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ jobs:
6767
-only-testing:BitkitTests/UtxoSelectionTests \
6868
-only-testing:BitkitTests/BlocktankTests \
6969
-only-testing:BitkitTests/PaymentFlowTests \
70+
-only-testing:BitkitTests/AddressTypeIntegrationTests \
7071
| xcbeautify --report junit
7172
}
7273

Bitkit/ViewModels/SettingsViewModel.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,15 @@ class SettingsViewModel: NSObject, ObservableObject {
300300
}
301301
}
302302

303+
/// Parses a comma-separated string of address types, filtering invalid values.
304+
static func parseAddressTypesString(_ string: String) -> [AddressScriptType] {
305+
let strings = string.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespaces) }
306+
return strings.compactMap { stringToAddressType($0) }
307+
}
308+
303309
var addressTypesToMonitor: [AddressScriptType] {
304310
get {
305-
let strings = _addressTypesToMonitor.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespaces) }
306-
return strings.compactMap { Self.stringToAddressType($0) }
311+
Self.parseAddressTypesString(_addressTypesToMonitor)
307312
}
308313
set {
309314
_addressTypesToMonitor = newValue.map { Self.addressTypeToString($0) }.joined(separator: ",")
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import BitkitCore
2+
import LDKNode
3+
import XCTest
4+
5+
@testable import Bitkit
6+
7+
final class AddressTypeIntegrationTests: XCTestCase {
8+
let walletIndex = 0
9+
let lightning = LightningService.shared
10+
let settings = SettingsViewModel.shared
11+
12+
override func setUp() async throws {
13+
try await super.setUp()
14+
Logger.test("Starting address type integration test setup", context: "AddressTypeIntegrationTests")
15+
try Keychain.wipeEntireKeychain()
16+
}
17+
18+
override func tearDown() async throws {
19+
lightning.dumpLdkLogs()
20+
try Keychain.wipeEntireKeychain()
21+
let isRunning = await MainActor.run { lightning.status?.isRunning == true }
22+
if isRunning {
23+
try? await lightning.stop()
24+
}
25+
try? await lightning.wipeStorage(walletIndex: walletIndex)
26+
await MainActor.run { settings.resetToDefaults() }
27+
try await super.tearDown()
28+
}
29+
30+
/// Skip if not regtest - integration tests require regtest
31+
private func skipIfNotRegtest() throws {
32+
guard Env.network == .regtest else {
33+
throw XCTSkip("Address type integration tests require regtest")
34+
}
35+
}
36+
37+
/// Shared setup: create wallet, start lightning node, sync
38+
private func setupWalletAndNode() async throws {
39+
try skipIfNotRegtest()
40+
let mnemonic = try StartupHandler.createNewWallet(bip39Passphrase: nil, walletIndex: walletIndex)
41+
XCTAssertFalse(mnemonic.isEmpty)
42+
try await lightning.setup(walletIndex: walletIndex)
43+
try await lightning.start()
44+
try await lightning.sync()
45+
}
46+
47+
@MainActor
48+
func testGetBalanceForAddressType() async throws {
49+
try await setupWalletAndNode()
50+
51+
Logger.test("Getting balance for nativeSegwit", context: "AddressTypeIntegrationTests")
52+
let balance = try await lightning.getBalanceForAddressType(.nativeSegwit)
53+
XCTAssertGreaterThanOrEqual(balance.totalSats, 0)
54+
Logger.test("Balance: \(balance.totalSats) sats", context: "AddressTypeIntegrationTests")
55+
}
56+
57+
func testGetChannelFundableBalance() async throws {
58+
try await setupWalletAndNode()
59+
60+
Logger.test("Getting channel fundable balance", context: "AddressTypeIntegrationTests")
61+
let fundable = try await lightning.getChannelFundableBalance()
62+
XCTAssertGreaterThanOrEqual(fundable, 0)
63+
Logger.test("Channel fundable: \(fundable) sats", context: "AddressTypeIntegrationTests")
64+
}
65+
66+
@MainActor
67+
func testUpdateAddressType() async throws {
68+
try await setupWalletAndNode()
69+
70+
Logger.test("Updating address type to taproot", context: "AddressTypeIntegrationTests")
71+
let success = await settings.updateAddressType(.taproot, wallet: nil)
72+
XCTAssertTrue(success, "updateAddressType should succeed")
73+
74+
XCTAssertEqual(UserDefaults.standard.string(forKey: "selectedAddressType"), "taproot")
75+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.taproot))
76+
Logger.test("Address type updated successfully", context: "AddressTypeIntegrationTests")
77+
}
78+
79+
@MainActor
80+
func testUpdateAddressTypeToLegacy() async throws {
81+
try await setupWalletAndNode()
82+
83+
Logger.test("Updating address type to legacy", context: "AddressTypeIntegrationTests")
84+
let success = await settings.updateAddressType(.legacy, wallet: nil)
85+
XCTAssertTrue(success, "updateAddressType to legacy should succeed")
86+
87+
XCTAssertEqual(UserDefaults.standard.string(forKey: "selectedAddressType"), "legacy")
88+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.legacy))
89+
Logger.test("Address type updated to legacy successfully", context: "AddressTypeIntegrationTests")
90+
}
91+
92+
@MainActor
93+
func testSetMonitoringEnable() async throws {
94+
try await setupWalletAndNode()
95+
96+
settings.addressTypesToMonitor = [.nativeSegwit]
97+
UserDefaults.standard.synchronize()
98+
99+
Logger.test("Enabling monitoring for taproot", context: "AddressTypeIntegrationTests")
100+
let success = await settings.setMonitoring(.taproot, enabled: true, wallet: nil)
101+
XCTAssertTrue(success)
102+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.taproot))
103+
}
104+
105+
@MainActor
106+
func testSetMonitoringDisableForEmptyTypeSucceeds() async throws {
107+
try await setupWalletAndNode()
108+
109+
// Add taproot via setMonitoring (handles restart internally so LDK creates taproot wallet)
110+
settings.addressTypesToMonitor = [.nativeSegwit]
111+
UserDefaults.standard.synchronize()
112+
let addSuccess = await settings.setMonitoring(.taproot, enabled: true, wallet: nil)
113+
XCTAssertTrue(addSuccess, "Adding taproot to monitoring should succeed")
114+
115+
Logger.test("Disabling monitoring for empty taproot type", context: "AddressTypeIntegrationTests")
116+
let success = await settings.setMonitoring(.taproot, enabled: false, wallet: nil)
117+
XCTAssertTrue(success, "Disabling empty type should succeed when nativeSegwit remains")
118+
XCTAssertFalse(settings.addressTypesToMonitor.contains(.taproot))
119+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.nativeSegwit))
120+
}
121+
122+
@MainActor
123+
func testSetMonitoringDisableLastNativeWitnessFails() async throws {
124+
try await setupWalletAndNode()
125+
126+
settings.addressTypesToMonitor = [.nativeSegwit]
127+
UserDefaults.standard.synchronize()
128+
129+
Logger.test("Attempting to disable last native witness type", context: "AddressTypeIntegrationTests")
130+
let success = await settings.setMonitoring(.nativeSegwit, enabled: false, wallet: nil)
131+
XCTAssertFalse(success, "Disabling last native witness type should fail")
132+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.nativeSegwit))
133+
}
134+
135+
@MainActor
136+
func testSetMonitoringDisableSelectedTypeFails() async throws {
137+
try await setupWalletAndNode()
138+
139+
// Add taproot, then set taproot as selected; cannot disable selected type
140+
settings.addressTypesToMonitor = [.nativeSegwit]
141+
UserDefaults.standard.synchronize()
142+
let addSuccess = await settings.setMonitoring(.taproot, enabled: true, wallet: nil)
143+
XCTAssertTrue(addSuccess)
144+
let updateSuccess = await settings.updateAddressType(.taproot, wallet: nil)
145+
XCTAssertTrue(updateSuccess, "Taproot should be selected")
146+
147+
Logger.test("Attempting to disable selected type (taproot)", context: "AddressTypeIntegrationTests")
148+
let success = await settings.setMonitoring(.taproot, enabled: false, wallet: nil)
149+
XCTAssertFalse(success, "Disabling selected address type should fail")
150+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.taproot))
151+
}
152+
153+
@MainActor
154+
func testPruneEmptyAddressTypesAfterRestore() async throws {
155+
try await setupWalletAndNode()
156+
157+
settings.addressTypesToMonitor = [.nativeSegwit, .taproot]
158+
UserDefaults.standard.synchronize()
159+
try await lightning.restart()
160+
try await lightning.sync()
161+
162+
Logger.test("Pruning empty address types after restore", context: "AddressTypeIntegrationTests")
163+
await settings.pruneEmptyAddressTypesAfterRestore()
164+
165+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.nativeSegwit))
166+
let monitored = settings.addressTypesToMonitor
167+
XCTAssertLessThanOrEqual(monitored.count, 4)
168+
Logger.test(
169+
"Pruned monitored types: \(monitored.map { SettingsViewModel.addressTypeToString($0) }.joined(separator: ","))",
170+
context: "AddressTypeIntegrationTests"
171+
)
172+
}
173+
}

0 commit comments

Comments
 (0)