Skip to content

[trello.com/c/dmJg0inY]: DogeWalletService tests #658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 30, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Adamant.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -466,6 +466,11 @@
AA33BEB72D3041A30083E59C /* AddressConverterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB42D303CBD0083E59C /* AddressConverterMock.swift */; };
AA33BEB92D3044760083E59C /* BtcApiServiceProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */; };
AAB01CAD2D3AE44B007D6BF4 /* BitcoinKitTransactionFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CAC2D3AE449007D6BF4 /* BitcoinKitTransactionFactory.swift */; };
AAB01CAF2D3AECED007D6BF4 /* DogeWalletServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CAE2D3AECE6007D6BF4 /* DogeWalletServiceTests.swift */; };
AAB01CB12D3AF01B007D6BF4 /* DogeApiServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CB02D3AF015007D6BF4 /* DogeApiServiceProtocol.swift */; };
AAB01CB32D3AF0B4007D6BF4 /* DogeApiServiceProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CB22D3AF0AE007D6BF4 /* DogeApiServiceProtocolMock.swift */; };
AAB01CB52D3AF27E007D6BF4 /* DogeInternalApiProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CB42D3AF278007D6BF4 /* DogeInternalApiProtocol.swift */; };
AAC641332D3ED1BB00619DFE /* DogeWalletServiceIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC641322D3ED1B200619DFE /* DogeWalletServiceIntegrationTests.swift */; };
AAFB3C8B2D31C0DD000CCCE9 /* Actor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C8A2D31C0D0000CCCE9 /* Actor+Extensions.swift */; };
AAFB3C8D2D31C0EE000CCCE9 /* Result+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C8C2D31C0EA000CCCE9 /* Result+Extensions.swift */; };
AAFB3C8F2D31C119000CCCE9 /* WalletServiceError+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C8E2D31C110000CCCE9 /* WalletServiceError+Equatable.swift */; };
@@ -1111,6 +1116,11 @@
AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICoreProtocolMock.swift; sourceTree = "<group>"; };
AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcApiServiceProtocolMock.swift; sourceTree = "<group>"; };
AAB01CAC2D3AE449007D6BF4 /* BitcoinKitTransactionFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinKitTransactionFactory.swift; sourceTree = "<group>"; };
AAB01CAE2D3AECE6007D6BF4 /* DogeWalletServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DogeWalletServiceTests.swift; sourceTree = "<group>"; };
AAB01CB02D3AF015007D6BF4 /* DogeApiServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DogeApiServiceProtocol.swift; sourceTree = "<group>"; };
AAB01CB22D3AF0AE007D6BF4 /* DogeApiServiceProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DogeApiServiceProtocolMock.swift; sourceTree = "<group>"; };
AAB01CB42D3AF278007D6BF4 /* DogeInternalApiProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DogeInternalApiProtocol.swift; sourceTree = "<group>"; };
AAC641322D3ED1B200619DFE /* DogeWalletServiceIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DogeWalletServiceIntegrationTests.swift; sourceTree = "<group>"; };
AAFB3C8A2D31C0D0000CCCE9 /* Actor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Actor+Extensions.swift"; sourceTree = "<group>"; };
AAFB3C8C2D31C0EA000CCCE9 /* Result+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Extensions.swift"; sourceTree = "<group>"; };
AAFB3C8E2D31C110000CCCE9 /* WalletServiceError+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WalletServiceError+Equatable.swift"; sourceTree = "<group>"; };
@@ -1759,6 +1769,8 @@
64E1C82E222E95F6006C4DA7 /* DogeWallet.swift */,
64E1C830222E9617006C4DA7 /* DogeWalletService.swift */,
93CCAE782B06D81D00EA5B94 /* DogeApiService.swift */,
AAB01CB02D3AF015007D6BF4 /* DogeApiServiceProtocol.swift */,
AAB01CB42D3AF278007D6BF4 /* DogeInternalApiProtocol.swift */,
4186B337294200E8006594A3 /* DogeWalletService+DynamicConstants.swift */,
648DD7A32237DB9E00B811FD /* DogeWalletService+Send.swift */,
648DD7A72239147800B811FD /* DogeWalletService+RichMessageProvider.swift */,
@@ -2280,6 +2292,8 @@
AA33BEB02D303C470083E59C /* Wallets */ = {
isa = PBXGroup;
children = (
AAC641322D3ED1B200619DFE /* DogeWalletServiceIntegrationTests.swift */,
AAB01CAE2D3AECE6007D6BF4 /* DogeWalletServiceTests.swift */,
AAFB3C9A2D383BA9000CCCE9 /* EthWalletServiceTests.swift */,
AAFB3C982D357E16000CCCE9 /* BtcWalletServiceIntegrationTests.swift */,
AA33BEB12D303C5F0083E59C /* BtcWalletServiceTests.swift */,
@@ -2298,6 +2312,7 @@
AAFB3C9C2D383F73000CCCE9 /* EthApiServiceProtocolMock.swift */,
AAFB3C942D31C587000CCCE9 /* BitcoinKitTransactionFactoryProtocolMock.swift */,
AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */,
AAB01CB22D3AF0AE007D6BF4 /* DogeApiServiceProtocolMock.swift */,
AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */,
AA33BEB42D303CBD0083E59C /* AddressConverterMock.swift */,
);
@@ -3435,6 +3450,7 @@
6403F5E222723F7500D58779 /* DashWallet.swift in Sources */,
26A975FF2B7E843E0095C367 /* SelectTextView.swift in Sources */,
93294B822AAD0BB400911109 /* BtcWalletFactory.swift in Sources */,
AAB01CB12D3AF01B007D6BF4 /* DogeApiServiceProtocol.swift in Sources */,
648DD7A42237DB9E00B811FD /* DogeWalletService+Send.swift in Sources */,
93294B7D2AAD067000911109 /* AppContainer.swift in Sources */,
93ED214C2CC3561800AA1FC8 /* TransactionsStatusServiceComposeProtocol.swift in Sources */,
@@ -3820,6 +3836,7 @@
64BD2B7720E2820300E2CD36 /* TransactionDetails.swift in Sources */,
3A9015A52A614A18002A2464 /* EmojiService.swift in Sources */,
9322E875297042F000B8357C /* ChatSender.swift in Sources */,
AAB01CB52D3AF27E007D6BF4 /* DogeInternalApiProtocol.swift in Sources */,
93ED214F2CC3567600AA1FC8 /* TransactionsStatusServiceCompose.swift in Sources */,
E96E86B821679C120061F80A /* EthTransactionDetailsViewController.swift in Sources */,
3A26D9432C3C2E19003AD832 /* KlyWalletService+StatusCheck.swift in Sources */,
@@ -3912,9 +3929,12 @@
AA33BEB92D3044760083E59C /* BtcApiServiceProtocolMock.swift in Sources */,
AAFB3CA52D3854BB000CCCE9 /* MockURLProtocol.swift in Sources */,
AAFB3C9B2D383BB1000CCCE9 /* EthWalletServiceTests.swift in Sources */,
AAC641332D3ED1BB00619DFE /* DogeWalletServiceIntegrationTests.swift in Sources */,
AA33BEB22D303C730083E59C /* BtcWalletServiceTests.swift in Sources */,
AAB01CAF2D3AECED007D6BF4 /* DogeWalletServiceTests.swift in Sources */,
AA33BEB72D3041A30083E59C /* AddressConverterMock.swift in Sources */,
AAFB3C8D2D31C0EE000CCCE9 /* Result+Extensions.swift in Sources */,
AAB01CB32D3AF0B4007D6BF4 /* DogeApiServiceProtocolMock.swift in Sources */,
AA33BEB62D303E240083E59C /* APICoreProtocolMock.swift in Sources */,
AAFB3C912D31C14A000CCCE9 /* BitcoinKitTransaction+Equatable.swift in Sources */,
AAFB3C8B2D31C0DD000CCCE9 /* Actor+Extensions.swift in Sources */,
18 changes: 12 additions & 6 deletions Adamant/Modules/Wallets/Doge/DogeApiService.swift
Original file line number Diff line number Diff line change
@@ -45,19 +45,23 @@ final class DogeApiCore: BlockchainHealthCheckableService, Sendable {
}
}

final class DogeApiService: ApiServiceProtocol {
let api: BlockchainHealthCheckWrapper<DogeApiCore>
final class DogeApiService: DogeApiServiceProtocol {
var api: DogeInternalApiProtocol {
_api
}

let _api: BlockchainHealthCheckWrapper<DogeApiCore>

@MainActor
var nodesInfoPublisher: AnyObservable<NodesListInfo> { api.nodesInfoPublisher }
var nodesInfoPublisher: AnyObservable<NodesListInfo> { _api.nodesInfoPublisher }

@MainActor
var nodesInfo: NodesListInfo { api.nodesInfo }
var nodesInfo: NodesListInfo { _api.nodesInfo }

func healthCheck() { api.healthCheck() }
func healthCheck() { _api.healthCheck() }

init(api: BlockchainHealthCheckWrapper<DogeApiCore>) {
self.api = api
self._api = api
}

func request<Output>(
@@ -75,3 +79,5 @@ final class DogeApiService: ApiServiceProtocol {
}
}
}

extension BlockchainHealthCheckWrapper: DogeInternalApiProtocol where Service == DogeApiCore {}
22 changes: 22 additions & 0 deletions Adamant/Modules/Wallets/Doge/DogeApiServiceProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// DogeApiServiceProtocol.swift
// Adamant
//
// Created by Christian Benua on 17.01.2025.
// Copyright © 2025 Adamant. All rights reserved.
//

import CommonKit
import Foundation

protocol DogeApiServiceProtocol: ApiServiceProtocol {

var api: DogeInternalApiProtocol { get }

func request<Output>(
waitsForConnectivity: Bool,
_ request: @Sendable @escaping (APICoreProtocol, NodeOrigin) async -> ApiServiceResult<Output>
) async -> WalletServiceResult<Output>

func getStatusInfo() async -> WalletServiceResult<NodeStatusInfo>
}
16 changes: 16 additions & 0 deletions Adamant/Modules/Wallets/Doge/DogeInternalApiProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// DogeInternalApiProtocol.swift
// Adamant
//
// Created by Christian Benua on 17.01.2025.
// Copyright © 2025 Adamant. All rights reserved.
//

import CommonKit

protocol DogeInternalApiProtocol {
func request<Output>(
waitsForConnectivity: Bool,
_ requestAction: @Sendable (DogeApiCore, NodeOrigin) async -> WalletServiceResult<Output>
) async -> WalletServiceResult<Output>
}
14 changes: 14 additions & 0 deletions Adamant/Modules/Wallets/Doge/DogeWallet.swift
Original file line number Diff line number Diff line change
@@ -49,4 +49,18 @@ final class DogeWallet: WalletAccount, @unchecked Sendable {
self.publicKey = privateKey.publicKey()
self.addressEntity = try addressConverter.convert(publicKey: publicKey, type: .p2pkh)
}

#if DEBUG
@available(*, deprecated, message: "For testing purposes only")
init(
unicId: String,
privateKey: PrivateKey,
addressEntity: Address
) {
self.unicId = unicId
self.privateKey = privateKey
self.publicKey = privateKey.publicKey()
self.addressEntity = addressEntity
}
#endif
}
3 changes: 2 additions & 1 deletion Adamant/Modules/Wallets/Doge/DogeWalletService+Send.swift
Original file line number Diff line number Diff line change
@@ -52,12 +52,13 @@ extension DogeWalletService: WalletServiceTwoStepSend {
}

// Create local transaction
let transaction = BitcoinKit.Transaction.createNewTransaction(
let transaction = btcTransactionFactory.createTransaction(
toAddress: toAddress,
amount: rawAmount,
fee: fee,
changeAddress: wallet.addressEntity,
utxos: utxos,
lockTime: 0,
keys: [key]
)
return transaction
13 changes: 12 additions & 1 deletion Adamant/Modules/Wallets/Doge/DogeWalletService.swift
Original file line number Diff line number Diff line change
@@ -55,7 +55,8 @@ final class DogeWalletService: WalletCoreProtocol, @unchecked Sendable {

// MARK: - Dependencies
var apiService: AdamantApiServiceProtocol!
var dogeApiService: DogeApiService!
var dogeApiService: DogeApiServiceProtocol!
var btcTransactionFactory: BitcoinKitTransactionFactoryProtocol!
var accountService: AccountService!
var dialogService: DialogService!
var addressConverter: AddressConverter!
@@ -390,6 +391,7 @@ extension DogeWalletService: SwinjectDependentService {
accountService = container.resolve(AccountService.self)
apiService = container.resolve(AdamantApiServiceProtocol.self)
dialogService = container.resolve(DialogService.self)
btcTransactionFactory = container.resolve(BitcoinKitTransactionFactoryProtocol.self)
addressConverter = container.resolve(AddressConverterFactory.self)?
.make(network: network)
dogeApiService = container.resolve(DogeApiService.self)
@@ -689,6 +691,15 @@ extension DogeWalletService {
}
}

#if DEBUG
extension DogeWalletService {
@available(*, deprecated, message: "For testing purposes only")
func setWalletForTests(_ wallet: DogeWallet?) {
self.dogeWallet = wallet
}
}
#endif

// MARK: - PrivateKey generator
extension DogeWalletService: PrivateKeyGenerator {
var rowTitle: String {
135 changes: 135 additions & 0 deletions AdamantTests/Modules/Wallets/DogeWalletServiceIntegrationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//
// DogeWalletServiceIntegrationTests.swift
// Adamant
//
// Created by Christian Benua on 20.01.2025.
// Copyright © 2025 Adamant. All rights reserved.
//

import XCTest
@testable import Adamant
import Swinject
import BitcoinKit
import CommonKit

final class DogeWalletServiceIntegrationTests: XCTestCase {

private var apiCoreMock: APICoreProtocolMock!
private var dogeApiServiceProtocolMock: DogeApiServiceProtocolMock!
private var sut: DogeWalletService!

override func setUp() {
super.setUp()
apiCoreMock = APICoreProtocolMock()
dogeApiServiceProtocolMock = DogeApiServiceProtocolMock()
dogeApiServiceProtocolMock._api = DogeApiCore(apiCore: apiCoreMock)

sut = DogeWalletService()
sut.addressConverter = AddressConverterFactory().make(network: DogeMainnet())
sut.dogeApiService = dogeApiServiceProtocolMock
sut.btcTransactionFactory = BitcoinKitTransactionFactory()
}

override func tearDown() {
apiCoreMock = nil
dogeApiServiceProtocolMock = nil
sut = nil
super.tearDown()
}

func test_createAndSendTransaction_createsValidTxIdAndHash() async throws {
// given
sut.setWalletForTests(try makeWallet())
let data = Constants.unspentTranscationsData
await apiCoreMock.isolated { mock in
mock.stubbedSendRequestBasicGenericResult = APIResponseModel(result: .success(data), data: data, code: 200)
}

// when 1
let result = await Result(catchingAsync: {
try await self.sut.createTransaction(
recipient: Constants.recipient,
amount: 9,
fee: 1,
comment: nil
)
})

// then 1
let transaction = try XCTUnwrap(result.value)
XCTAssertEqual(transaction.serialized().hex, Constants.expectedTransactionHex)
XCTAssertEqual(transaction.txID, Constants.expectedTransactionID)

// given 2
let txData = try XCTUnwrap(transaction.txID.data(using: .utf8))
await apiCoreMock.isolated { mock in
mock.stubbedSendRequestBasicGenericResult = APIResponseModel(result: .success(txData), data: txData, code: 200)
}

// when 2
let result2 = await Result {
try await self.sut.sendTransaction(transaction)
}
// then 3
XCTAssertNil(result2.error)
await apiCoreMock.isolated { mock in
XCTAssertEqual(mock.invokedSendRequestBasicGenericCount, 2)
}
}
}

private extension DogeWalletServiceIntegrationTests {
func makeWallet() throws -> DogeWallet {
let privateKeyData = Constants.passphrase
.data(using: .utf8)!
.sha256()
let privateKey = PrivateKey(
data: privateKeyData,
network: DogeMainnet(),
isPublicKeyCompressed: true
)
return try DogeWallet(
unicId: Constants.tokenId,
privateKey: privateKey,
addressConverter: AddressConverterFactory().make(network: DogeMainnet())
)
}
}

private enum Constants {

static let passphrase = "village lunch say patrol glow first hurt shiver name method dolphin dead"

static let recipient = "DPCnnvzngz9AcpToiM7Y8qLewEDtP7jN8T"

static let tokenId = "DOGEDOGE"

static let expectedTransactionID = "4f9700bca38cce8f442ba0ebf6b2c1b95d235854cabb797fb1178499a5403c7a"

static let expectedTransactionHex = "0100000001010000006b483045022100c54ae687dfaa6e910eaf2d40ec755cc11eb1263de38cbe4a5b48b1a13c6d113c022043cce15981221cef35fbcfe0a35ad9a9a218257acb854d1dc0f6e0ebbe892c2d012102cd3dcbdfc1b77e54b3a8f273310806ab56b0c2463c2f1677c7694a89a713e0d0ffffffff0200e9a435000000001976a914c6251d0e16c0e1946b745b69caa3a7c36014381088ac00362634f28623001976a91457f6f900ac7a7e3ccab712326cd7b85638fc15a888ac00000000"

static let unspentTranscationsData = unspentTranscationsRawJSON.data(using: .utf8)!

static let unspentTranscationsRawJSON: String = """
[
{
"txid": "1",
"vout": 1,
"amount": 100000000,
"confirmations": 1
},
{
"txid": "1",
"vout": 2,
"amount": 100000000,
"confirmations": 1
},
{
"txid": "1",
"vout": 3,
"amount": 200000000,
"confirmations": 0
}
]
"""
}
Loading