// // OutboundTransactionManagerTests.swift // ZcashLightClientKit-Unit-Tests // // Created by Francisco Gindre on 11/26/19. // import XCTest @testable import ZcashLightClientKit class OutboundTransactionManagerTests: XCTestCase { var transactionManager: OutboundTransactionManager! var encoder: TransactionEncoder! var pendingRespository: PendingTransactionSQLDAO! var dataDbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedDataDbURL()!) var cacheDbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedCacheDbURL()!) var pendingDbhandle = TestDbHandle(originalDb: try! TestDbBuilder.pendingTransactionsDbURL()) var service: LightWalletService! var initializer: Initializer! let spendingKey = "secret-extended-key-test1qvpevftsqqqqpqy52ut2vv24a2qh7nsukew7qg9pq6djfwyc3xt5vaxuenshp2hhspp9qmqvdh0gs2ljpwxders5jkwgyhgln0drjqaguaenfhehz4esdl4kwlm5t9q0l6wmzcrvcf5ed6dqzvct3e2ge7f6qdvzhp02m7sp5a0qjssrwpdh7u6tq89hl3wchuq8ljq8r8rwd6xdwh3nry9at80z7amnj3s6ah4jevnvfr08gxpws523z95g6dmn4wm6l3658kd4xcq9rc0qn" let recipientAddress = "ztestsapling1ctuamfer5xjnnrdr3xdazenljx0mu0gutcf9u9e74tr2d3jwjnt0qllzxaplu54hgc2tyjdc2p6" let zpend: Int = 500_000 override func setUp() { try! dataDbHandle.setUp() try! cacheDbHandle.setUp() pendingRespository = PendingTransactionSQLDAO(dbProvider: pendingDbhandle.connectionProvider(readwrite: true)) try! pendingRespository.createrTableIfNeeded() initializer = Initializer(cacheDbURL: cacheDbHandle.readWriteDb, dataDbURL: dataDbHandle.readWriteDb, pendingDbURL: try! TestDbBuilder.pendingTransactionsDbURL(), endpoint: LightWalletEndpointBuilder.default, spendParamsURL: try! __spendParamsURL(), outputParamsURL: try! __outputParamsURL()) encoder = WalletTransactionEncoder(initializer: initializer) transactionManager = PersistentTransactionManager(encoder: encoder, service: MockLightWalletService(latestBlockHeight: 620999), repository: pendingRespository) } override func tearDown() { transactionManager = nil encoder = nil service = nil initializer = nil pendingRespository = nil dataDbHandle.dispose() cacheDbHandle.dispose() pendingDbhandle.dispose() } func testInitSpend() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. var tx: PendingTransactionEntity? XCTAssertNoThrow(try { tx = try transactionManager.initSpend(zatoshi: zpend, toAddress: recipientAddress, memo: nil, from: 0) }()) guard let pendingTx = tx else { XCTFail("failed to create pending transaction") return } XCTAssertEqual(pendingTx.toAddress, recipientAddress) XCTAssertEqual(pendingTx.memo, nil) XCTAssertEqual(pendingTx.value, zpend) } func testEncodeSpendSucess() { let expect = XCTestExpectation(description: self.description) var tx: PendingTransactionEntity? XCTAssertNoThrow(try { tx = try transactionManager.initSpend(zatoshi: zpend, toAddress: recipientAddress, memo: nil, from: 0) }()) guard let pendingTx = tx else { XCTFail("failed to create pending transaction") return } transactionManager.encode(spendingKey: spendingKey, pendingTransaction: pendingTx) { (result) in expect.fulfill() switch result { case .failure(let error): XCTFail("failed with error: \(error)") case .success(let tx): XCTAssertEqual(tx.id, pendingTx.id) XCTAssertTrue(tx.encodeAttempts > 0) XCTAssertFalse(tx.isFailedEncoding) } } wait(for: [expect], timeout: 20) } func testSubmitFailed() { transactionManager = PersistentTransactionManager(encoder: encoder, service: SlightlyBadLightWalletService(latestBlockHeight: 620999), repository: pendingRespository) let submitExpect = XCTestExpectation(description: "submit") guard let tx = submittableTx() else { XCTFail("failed to encode and all that") return } transactionManager.submit(pendingTransaction: tx) { (result) in submitExpect.fulfill() switch result { case .failure(_): let failedTx = try? self.pendingRespository.find(by: tx.id!) XCTAssertTrue(failedTx?.isFailedSubmit ?? false) case .success(_): XCTFail("test should have failed but succeeeded!") } } wait(for: [submitExpect], timeout: 5) } private func submittableTx() -> PendingTransactionEntity? { let encodeExpect = XCTestExpectation(description: "encode") var tx: PendingTransactionEntity? XCTAssertNoThrow(try { tx = try transactionManager.initSpend(zatoshi: zpend, toAddress: recipientAddress, memo: nil, from: 0) }()) guard let pendingTx = tx else { XCTFail("failed to create pending transaction") return nil } var encodedTx: PendingTransactionEntity? transactionManager.encode(spendingKey: spendingKey, pendingTransaction: pendingTx) { (result) in encodeExpect.fulfill() switch result { case .failure(let error): XCTFail("failed with error: \(error)") case .success(let tx): XCTAssertEqual(tx.id, pendingTx.id) encodedTx = tx } } wait(for: [encodeExpect], timeout: 20) guard let submittableTx = encodedTx else { XCTFail("failed to encode tx") return nil } return submittableTx } func testSubmit() { let submitExpect = XCTestExpectation(description: "submit") guard let tx = submittableTx() else { XCTFail("failed to encode and all that") return } transactionManager.submit(pendingTransaction: tx) { (result) in submitExpect.fulfill() switch result { case .failure(let error): XCTFail("submission failed with error: \(error)") let failedTx = try? self.pendingRespository.find(by: tx.id!) XCTAssertTrue(failedTx?.isFailedSubmit ?? false) case .success(let successfulTx): XCTAssertEqual(tx.id, successfulTx.id) XCTAssertTrue(successfulTx.isSubmitted) XCTAssertTrue(successfulTx.isSubmitSuccess) } } wait(for: [submitExpect], timeout: 5) } func testApplyMinedHeight() { var tx: PendingTransactionEntity? let minedHeight = 789_000 XCTAssertNoThrow(try { tx = try transactionManager.initSpend(zatoshi: zpend, toAddress: recipientAddress, memo: nil, from: 0) }()) guard let pendingTx = tx else { XCTFail("failed to create pending transaction") return } var minedTransaction: PendingTransactionEntity? XCTAssertNoThrow(try { minedTransaction = try transactionManager.applyMinedHeight(pendingTransaction: pendingTx, minedHeight: minedHeight)}()) guard let minedTx = minedTransaction else { XCTFail("failed to apply mined height") return } XCTAssertTrue(minedTx.isMined) XCTAssertEqual(minedTx.minedHeight, minedHeight) } func testCancel() { var tx: PendingTransactionEntity? XCTAssertNoThrow(try { tx = try transactionManager.initSpend(zatoshi: zpend, toAddress: recipientAddress, memo: nil, from: 0) }()) guard let pendingTx = tx else { XCTFail("failed to create pending transaction") return } let cancellationResult = transactionManager.cancel(pendingTransaction: pendingTx) guard let id = pendingTx.id else { XCTFail("transaction with no id") return } guard let retrievedTransaction = try! pendingRespository.find(by: id) else { XCTFail("failed to retrieve previously created transation") return } XCTAssertEqual(cancellationResult, retrievedTransaction.isCancelled) } func testAllPendingTransactions() { let txCount = 100 for i in 0 ..< txCount { var tx: PendingTransactionEntity? XCTAssertNoThrow(try { tx = try transactionManager.initSpend(zatoshi: zpend, toAddress: recipientAddress, memo: nil, from: 0) }()) guard tx != nil else { XCTFail("failed to create pending transaction \(i)") return } } guard let allPending = try! transactionManager.allPendingTransactions() else { XCTFail("failed to retrieve all pending transactions") return } XCTAssertEqual(allPending.count, txCount) } }