Skip to content

Commit 8956313

Browse files
committed
Add tests
1 parent 8e59b05 commit 8956313

1 file changed

Lines changed: 160 additions & 6 deletions

File tree

BitkitTests/AddressTypeIntegrationTests.swift

Lines changed: 160 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import XCTest
66

77
final class AddressTypeIntegrationTests: XCTestCase {
88
let walletIndex = 0
9-
let lightning = LightningService.shared
109
let settings = SettingsViewModel.shared
1110

1211
override func setUp() async throws {
@@ -16,6 +15,7 @@ final class AddressTypeIntegrationTests: XCTestCase {
1615
}
1716

1817
override func tearDown() async throws {
18+
let lightning = await MainActor.run { settings.lightningService }
1919
lightning.dumpLdkLogs()
2020
try Keychain.wipeEntireKeychain()
2121
let isRunning = await MainActor.run { lightning.status?.isRunning == true }
@@ -39,6 +39,7 @@ final class AddressTypeIntegrationTests: XCTestCase {
3939
try skipIfNotRegtest()
4040
let mnemonic = try StartupHandler.createNewWallet(bip39Passphrase: nil, walletIndex: walletIndex)
4141
XCTAssertFalse(mnemonic.isEmpty)
42+
let lightning = await MainActor.run { settings.lightningService }
4243
try await lightning.setup(walletIndex: walletIndex)
4344
try await lightning.start()
4445
try await lightning.sync()
@@ -49,17 +50,18 @@ final class AddressTypeIntegrationTests: XCTestCase {
4950
try await setupWalletAndNode()
5051

5152
Logger.test("Getting balance for nativeSegwit", context: "AddressTypeIntegrationTests")
52-
let balance = try await lightning.getBalanceForAddressType(.nativeSegwit)
53+
let balance = try await settings.lightningService.getBalanceForAddressType(.nativeSegwit)
5354
XCTAssertGreaterThanOrEqual(balance.totalSats, 0)
5455
Logger.test("Balance: \(balance.totalSats) sats", context: "AddressTypeIntegrationTests")
5556
}
5657

58+
@MainActor
5759
func testGetChannelFundableBalance() async throws {
5860
try await setupWalletAndNode()
5961

6062
Logger.test("Getting channel fundable balance", context: "AddressTypeIntegrationTests")
61-
let (selectedType, monitoredTypes) = LightningService.addressTypeStateFromUserDefaults()
62-
let fundable = try await lightning.getChannelFundableBalance(selectedType: selectedType, monitoredTypes: monitoredTypes)
63+
let (selectedType, monitoredTypes) = Bitkit.LightningService.addressTypeStateFromUserDefaults()
64+
let fundable = try await settings.lightningService.getChannelFundableBalance(selectedType: selectedType, monitoredTypes: monitoredTypes)
6365
XCTAssertGreaterThanOrEqual(fundable, 0)
6466
Logger.test("Channel fundable: \(fundable) sats", context: "AddressTypeIntegrationTests")
6567
}
@@ -157,8 +159,8 @@ final class AddressTypeIntegrationTests: XCTestCase {
157159

158160
settings.addressTypesToMonitor = [.nativeSegwit, .taproot]
159161
UserDefaults.standard.synchronize()
160-
try await lightning.restart()
161-
try await lightning.sync()
162+
try await settings.lightningService.restart()
163+
try await settings.lightningService.sync()
162164

163165
Logger.test("Pruning empty address types after restore", context: "AddressTypeIntegrationTests")
164166
await settings.pruneEmptyAddressTypesAfterRestore()
@@ -171,4 +173,156 @@ final class AddressTypeIntegrationTests: XCTestCase {
171173
context: "AddressTypeIntegrationTests"
172174
)
173175
}
176+
177+
// MARK: - Mutex / Concurrency
178+
179+
@MainActor
180+
func testUpdateAddressTypeMutexReturnsImmediately() async throws {
181+
try await setupWalletAndNode()
182+
183+
Logger.test("Testing updateAddressType mutex guard", context: "AddressTypeIntegrationTests")
184+
// First call should succeed
185+
let success = await settings.updateAddressType(.taproot, wallet: nil)
186+
XCTAssertTrue(success)
187+
188+
// Same type returns true (guard: addressType == selectedAddressType)
189+
let sameTypeResult = await settings.updateAddressType(.taproot, wallet: nil)
190+
XCTAssertTrue(sameTypeResult, "Same type should return true immediately")
191+
}
192+
193+
// MARK: - Channel Fundable Balance Excludes Legacy
194+
195+
@MainActor
196+
func testGetChannelFundableBalanceExcludesLegacy() async throws {
197+
try await setupWalletAndNode()
198+
199+
let blocktank = CoreService.shared.blocktank
200+
201+
// Enable legacy monitoring and switch to legacy
202+
settings.addressTypesToMonitor = [.nativeSegwit, .legacy]
203+
UserDefaults.standard.synchronize()
204+
let updateSuccess = await settings.updateAddressType(.legacy, wallet: nil)
205+
XCTAssertTrue(updateSuccess)
206+
207+
let legacyAddress = try await settings.lightningService.newAddressForType(.legacy)
208+
Logger.test("Funding legacy address: \(legacyAddress)", context: "AddressTypeIntegrationTests")
209+
let txId = try await blocktank.regtestDepositFunds(address: legacyAddress, amountSat: 50000)
210+
XCTAssertFalse(txId.isEmpty)
211+
212+
try await blocktank.regtestMineBlocks(6)
213+
try await Task.sleep(nanoseconds: 15_000_000_000)
214+
try await settings.lightningService.sync()
215+
216+
// Verify legacy has balance
217+
let legacyBalance = try await settings.lightningService.getBalanceForAddressType(.legacy)
218+
XCTAssertGreaterThan(legacyBalance.totalSats, 0, "Legacy should have balance")
219+
220+
// Channel fundable should NOT include legacy
221+
let fundable = try await settings.lightningService.getChannelFundableBalance(
222+
selectedType: .legacy,
223+
monitoredTypes: [.nativeSegwit, .legacy]
224+
)
225+
XCTAssertEqual(fundable, 0, "Channel fundable should exclude legacy even when it has balance")
226+
Logger.test("Channel fundable correctly excludes legacy: \(fundable)", context: "AddressTypeIntegrationTests")
227+
}
228+
229+
// MARK: - Disable Monitoring With Balance Fails
230+
231+
@MainActor
232+
func testSetMonitoringDisableWithBalanceFails() async throws {
233+
try await setupWalletAndNode()
234+
235+
let blocktank = CoreService.shared.blocktank
236+
237+
// Enable taproot monitoring
238+
settings.addressTypesToMonitor = [.nativeSegwit]
239+
UserDefaults.standard.synchronize()
240+
let addSuccess = await settings.setMonitoring(.taproot, enabled: true, wallet: nil)
241+
XCTAssertTrue(addSuccess, "Adding taproot should succeed")
242+
243+
// Fund the taproot address
244+
let taprootAddress = try await settings.lightningService.newAddressForType(.taproot)
245+
Logger.test("Funding taproot address: \(taprootAddress)", context: "AddressTypeIntegrationTests")
246+
let txId = try await blocktank.regtestDepositFunds(address: taprootAddress, amountSat: 50000)
247+
XCTAssertFalse(txId.isEmpty)
248+
249+
try await blocktank.regtestMineBlocks(6)
250+
try await Task.sleep(nanoseconds: 15_000_000_000)
251+
try await settings.lightningService.sync()
252+
253+
// Verify taproot has balance
254+
let taprootBalance = try await settings.lightningService.getBalanceForAddressType(.taproot)
255+
XCTAssertGreaterThan(taprootBalance.totalSats, 0, "Taproot should have balance after funding")
256+
257+
// Attempt to disable — should fail because of balance
258+
Logger.test("Attempting to disable taproot monitoring with balance", context: "AddressTypeIntegrationTests")
259+
let disableSuccess = await settings.setMonitoring(.taproot, enabled: false, wallet: nil)
260+
XCTAssertFalse(disableSuccess, "Disabling type with balance should fail")
261+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.taproot), "Taproot should remain monitored")
262+
}
263+
264+
// MARK: - Prune Preserves Types With Balance
265+
266+
@MainActor
267+
func testPruneEmptyPreservesTypesWithBalance() async throws {
268+
try await setupWalletAndNode()
269+
270+
let blocktank = CoreService.shared.blocktank
271+
272+
// Enable taproot monitoring
273+
settings.addressTypesToMonitor = [.nativeSegwit]
274+
UserDefaults.standard.synchronize()
275+
let addSuccess = await settings.setMonitoring(.taproot, enabled: true, wallet: nil)
276+
XCTAssertTrue(addSuccess)
277+
278+
// Fund the taproot address
279+
let taprootAddress = try await settings.lightningService.newAddressForType(.taproot)
280+
Logger.test("Funding taproot for prune test: \(taprootAddress)", context: "AddressTypeIntegrationTests")
281+
let txId = try await blocktank.regtestDepositFunds(address: taprootAddress, amountSat: 50000)
282+
XCTAssertFalse(txId.isEmpty)
283+
284+
try await blocktank.regtestMineBlocks(6)
285+
try await Task.sleep(nanoseconds: 15_000_000_000)
286+
try await settings.lightningService.sync()
287+
288+
// Add legacy (will be empty)
289+
let addLegacy = await settings.setMonitoring(.legacy, enabled: true, wallet: nil)
290+
XCTAssertTrue(addLegacy)
291+
XCTAssertEqual(settings.addressTypesToMonitor.count, 3)
292+
293+
Logger.test("Pruning — should remove empty legacy but keep funded taproot", context: "AddressTypeIntegrationTests")
294+
await settings.pruneEmptyAddressTypesAfterRestore()
295+
296+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.nativeSegwit), "nativeSegwit should remain")
297+
XCTAssertTrue(settings.addressTypesToMonitor.contains(.taproot), "Funded taproot should remain")
298+
XCTAssertFalse(settings.addressTypesToMonitor.contains(.legacy), "Empty legacy should be pruned")
299+
}
300+
301+
// MARK: - Address Format Verification
302+
303+
@MainActor
304+
func testNewAddressMatchesTypeFormat() async throws {
305+
try await setupWalletAndNode()
306+
307+
// Enable all types so LDK creates wallets for each
308+
settings.addressTypesToMonitor = [.nativeSegwit]
309+
UserDefaults.standard.synchronize()
310+
for type in [LDKNode.AddressType.taproot, .nestedSegwit, .legacy] {
311+
let success = await settings.setMonitoring(type, enabled: true, wallet: nil)
312+
XCTAssertTrue(success, "Enabling \(type.stringValue) monitoring should succeed")
313+
}
314+
315+
let expectations: [(LDKNode.AddressType, String, String)] = [
316+
(.legacy, "m", "Legacy address should start with m or n on regtest"),
317+
(.nestedSegwit, "2", "Nested SegWit address should start with 2 on regtest"),
318+
(.nativeSegwit, "bcrt1q", "Native SegWit address should start with bcrt1q on regtest"),
319+
(.taproot, "bcrt1p", "Taproot address should start with bcrt1p on regtest"),
320+
]
321+
322+
for (type, prefix, message) in expectations {
323+
let address = try await settings.lightningService.newAddressForType(type)
324+
Logger.test("\(type.stringValue) address: \(address)", context: "AddressTypeIntegrationTests")
325+
XCTAssertTrue(address.hasPrefix(prefix), "\(message), got: \(address)")
326+
}
327+
}
174328
}

0 commit comments

Comments
 (0)