diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 000000000..4fb16938a --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,55 @@ +# This workflow will build a Swift project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift + +name: Ubuntu + +on: + push: + branches: + - master + - develop + - hotfix + - unstable + - linux-support + paths: + - Packag*.swift + - web3swift.podspec + - Cartfile + - Sources/** + - 'Tests/**' + - 'web3swift*/**' + - '.github/workflows/**' + pull_request: + branches: + - master + - develop + - unstable + - MoonfishApp/develop + +jobs: + build: + + runs-on: ubuntu-latest + container: swift:5.7-focal + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Resolve dependencies + run: swift package resolve + - name: Build + run: swift build + - name: Install ganache + run: npm install ganache --global + - name: install nc + run: apt-get update && apt-get install -y netcat + - name: Start ganache in background + run: ganache & + - name: Wait till ganache starts + run: sleep 1 + - name: Ping + run: nc -vz 127.0.0.1 8545 + - name: Run tests + run: swift test diff --git a/Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift b/Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift index 58e13aa0f..86a47dec5 100644 --- a/Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift +++ b/Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift @@ -7,6 +7,9 @@ import Foundation import BigInt +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif extension APIRequest { public static func sendRequest(with provider: Web3Provider, for call: APIRequest) async throws -> APIResponse { diff --git a/Sources/Web3Core/EthereumNetwork/Utility/Async+BackwardCapability.swift b/Sources/Web3Core/EthereumNetwork/Utility/Async+BackwardCapability.swift index e6b67ae83..952a0bc2b 100644 --- a/Sources/Web3Core/EthereumNetwork/Utility/Async+BackwardCapability.swift +++ b/Sources/Web3Core/EthereumNetwork/Utility/Async+BackwardCapability.swift @@ -6,6 +6,9 @@ // import Foundation +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif @available(iOS, obsoleted: 15.0, message: "Use the built-in API instead") @available(macOS, obsoleted: 12.0, message: "Use the built-in API instead") diff --git a/Sources/Web3Core/KeystoreManager/BIP32Keystore.swift b/Sources/Web3Core/KeystoreManager/BIP32Keystore.swift index 4277cca61..fae2b68ed 100755 --- a/Sources/Web3Core/KeystoreManager/BIP32Keystore.swift +++ b/Sources/Web3Core/KeystoreManager/BIP32Keystore.swift @@ -220,17 +220,13 @@ public class BIP32Keystore: AbstractKeystore { guard data.count == 82 else { throw AbstractKeystoreError.encryptionError("Invalid expected data length") } - guard let saltData = Data.randomBytes(length: 32) else { - throw AbstractKeystoreError.noEntropyError - } + let saltData = try Data.randomBytes(count: 32) guard let derivedKey = scrypt(password: password, salt: saltData, length: dkLen, N: N, R: R, P: P) else { throw AbstractKeystoreError.keyDerivationError } let last16bytes = derivedKey[(derivedKey.count - 16)...(derivedKey.count - 1)] let encryptionKey = derivedKey[0...15] - guard let IV = Data.randomBytes(length: 16) else { - throw AbstractKeystoreError.noEntropyError - } + let IV = try Data.randomBytes(count: 16) var aesCipher: AES? switch aesMode { case "aes-128-cbc": diff --git a/Sources/Web3Core/KeystoreManager/BIP39.swift b/Sources/Web3Core/KeystoreManager/BIP39.swift index e9965ef5d..c97b934fb 100755 --- a/Sources/Web3Core/KeystoreManager/BIP39.swift +++ b/Sources/Web3Core/KeystoreManager/BIP39.swift @@ -95,13 +95,10 @@ public class BIP39 { } private static func entropyOf(size: Int) throws -> Data { - guard - size >= 128 && size <= 256 && size.isMultiple(of: 32), - let entropy = Data.randomBytes(length: size/8) - else { + guard size >= 128 && size <= 256 && size.isMultiple(of: 32) else { throw AbstractKeystoreError.noEntropyError } - return entropy + return try Data.randomBytes(count: size / 8) } static func bitarray(from data: Data) -> String { diff --git a/Sources/Web3Core/KeystoreManager/EthereumKeystoreV3.swift b/Sources/Web3Core/KeystoreManager/EthereumKeystoreV3.swift index d2602637c..3f85a7a12 100755 --- a/Sources/Web3Core/KeystoreManager/EthereumKeystoreV3.swift +++ b/Sources/Web3Core/KeystoreManager/EthereumKeystoreV3.swift @@ -95,17 +95,13 @@ public class EthereumKeystoreV3: AbstractKeystore { throw AbstractKeystoreError.encryptionError("Encryption without key data") } let saltLen = 32 - guard let saltData = Data.randomBytes(length: saltLen) else { - throw AbstractKeystoreError.noEntropyError - } + let saltData = try Data.randomBytes(count: saltLen) guard let derivedKey = scrypt(password: password, salt: saltData, length: dkLen, N: N, R: R, P: P) else { throw AbstractKeystoreError.keyDerivationError } let last16bytes = Data(derivedKey[(derivedKey.count - 16)...(derivedKey.count - 1)]) let encryptionKey = Data(derivedKey[0...15]) - guard let IV = Data.randomBytes(length: 16) else { - throw AbstractKeystoreError.noEntropyError - } + let IV = try Data.randomBytes(count: 16) var aesCipher: AES? switch aesMode { case "aes-128-cbc": diff --git a/Sources/Web3Core/KeystoreManager/EtherscanTransactionChecker.swift b/Sources/Web3Core/KeystoreManager/EtherscanTransactionChecker.swift index 1b0353285..51efd40b9 100644 --- a/Sources/Web3Core/KeystoreManager/EtherscanTransactionChecker.swift +++ b/Sources/Web3Core/KeystoreManager/EtherscanTransactionChecker.swift @@ -4,6 +4,9 @@ // import Foundation +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif public struct EtherscanTransactionChecker: TransactionChecker { private let urlSession: URLSessionProxy diff --git a/Sources/Web3Core/Structure/SECP256k1.swift b/Sources/Web3Core/Structure/SECP256k1.swift index 82f7fb375..4e68b0d40 100755 --- a/Sources/Web3Core/Structure/SECP256k1.swift +++ b/Sources/Web3Core/Structure/SECP256k1.swift @@ -244,7 +244,7 @@ extension SECP256K1 { return nil } var recoverableSignature: secp256k1_ecdsa_recoverable_signature = secp256k1_ecdsa_recoverable_signature() - guard let extraEntropy = SECP256K1.randomBytes(length: 32) else { return nil } + guard let extraEntropy = try? Data.randomBytes(count: 32) else { return nil } let result = hash.withUnsafeBytes { hashRBPointer -> Int32? in if let hashRPointer = hashRBPointer.baseAddress, hashRBPointer.count > 0 { let hashPointer = hashRPointer.assumingMemoryBound(to: UInt8.self) @@ -303,7 +303,7 @@ extension SECP256K1 { public static func generatePrivateKey() -> Data? { for _ in 0...1024 { - guard let keyData = SECP256K1.randomBytes(length: 32) else { + guard let keyData = try? Data.randomBytes(count: 32) else { continue } guard SECP256K1.verifyPrivateKey(privateKey: keyData) else { @@ -338,26 +338,6 @@ extension SECP256K1 { return completeSignature } - internal static func randomBytes(length: Int) -> Data? { - for _ in 0...1024 { - var data = Data(repeating: 0, count: length) - let result = data.withUnsafeMutableBytes { mutableRBBytes -> Int32? in - if let mutableRBytes = mutableRBBytes.baseAddress, mutableRBBytes.count > 0 { - let mutableBytes = mutableRBytes.assumingMemoryBound(to: UInt8.self) - return SecRandomCopyBytes(kSecRandomDefault, length, mutableBytes) - } else { - return nil - } - } - if let res = result, res == errSecSuccess { - return data - } else { - continue - } - } - return nil - } - internal static func toByteArray(_ value: T) -> [UInt8] { var value = value return withUnsafeBytes(of: &value) { Array($0) } diff --git a/Sources/Web3Core/Structure/Web3ProviderProtocol.swift b/Sources/Web3Core/Structure/Web3ProviderProtocol.swift index 9dda5ebf5..b675b7432 100644 --- a/Sources/Web3Core/Structure/Web3ProviderProtocol.swift +++ b/Sources/Web3Core/Structure/Web3ProviderProtocol.swift @@ -6,6 +6,9 @@ // import Foundation +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif public protocol Web3Provider { var network: Networks? {get set} diff --git a/Sources/Web3Core/Utility/Data+Extension.swift b/Sources/Web3Core/Utility/Data+Extension.swift index 448728f1a..c89c33947 100755 --- a/Sources/Web3Core/Utility/Data+Extension.swift +++ b/Sources/Web3Core/Utility/Data+Extension.swift @@ -5,6 +5,7 @@ import Foundation + extension Data { init(fromArray values: [T]) { let values = values @@ -40,22 +41,51 @@ extension Data { } } + @available(*, deprecated, message: "Please, use throwing `randomBytes(count)` function instead to get information instead of `nil` value on why the function call failed.") + /// Runs `SecRandomCopyBytes` for Apple platforms and `openssl rand -hex` for other platforms + /// to generate cryptographically secure random bytes. + /// - Parameter count: how many bytes to generate. Value below or equal to 0 will return `nil`. + /// - Returns: random bytes or `nil`. public static func randomBytes(length: Int) -> Data? { - for _ in 0...1024 { - var data = Data(repeating: 0, count: length) - let result = data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) -> Int32? in - if let bodyAddress = body.baseAddress, body.count > 0 { + try? randomBytes(count: length) + } + + /// Runs `SecRandomCopyBytes` for Apple platforms and `openssl rand -hex` for other platforms + /// to generate cryptographically secure random bytes. + /// - Parameter count: how many bytes to generate. Value below or equal to 0 will throw an error. + /// - Parameter useOpenSSL: **has no effect on Linux and Windows**. When set to `true` forces the use of external executable `openssl`. It's your responsibility to make sure openssl is installed on this machine. By default set to `false`. To install follow [the official guide](https://www.openssl.org/source/ ). + /// - Returns: random bytes or throws an error, + public static func randomBytes(count: Int, useOpenSSL: Bool = false) throws -> Data { + guard count > 0 else { throw Web3Error.valueError(desc: "Cannot generate \(count) random bytes.") } + + #if !(os(Linux) || os(Windows)) + if !useOpenSSL { + for _ in 0...1024 { + var data = Data(repeating: 0, count: count) + let result = try data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) -> Int32? in + guard let bodyAddress = body.baseAddress, body.count == count else { + throw Web3Error.processingError(desc: "Address of the buffer is nil (\(body.baseAddress == nil)) or the count of bytes in the buffer is not equal to the count requested by the user (\(body.count < count)).") + } + let pointer = bodyAddress.assumingMemoryBound(to: UInt8.self) - return SecRandomCopyBytes(kSecRandomDefault, length, pointer) - } else { - return nil + return SecRandomCopyBytes(kSecRandomDefault, count, pointer) + } + if let notNilResult = result, notNilResult == errSecSuccess { + return data } } - if let notNilResult = result, notNilResult == errSecSuccess { - return data - } } - return nil + #endif + let randomBytesHex = try ShellCommandExecutor().run(commandName: "openssl rand -hex \(count)") + guard let bytes = Data.fromHex(randomBytesHex) else { + throw Web3Error.processingError(desc: "Random bytes generated by the openssl are in an invalid hex representation: \(randomBytesHex)") + } + + guard bytes.count == count else { + throw Web3Error.processingError(desc: "Count of generated by the openssl random bytes is not equal to the count requested by the user: expected \(count), given \(bytes.count).") + } + + return bytes } public func bitsInRange(_ startingBit: Int, _ length: Int) -> UInt64? { // return max of 8 bytes for simplicity, non-public diff --git a/Sources/Web3Core/Utility/ShellCommandExecutor.swift b/Sources/Web3Core/Utility/ShellCommandExecutor.swift new file mode 100644 index 000000000..309a6e18e --- /dev/null +++ b/Sources/Web3Core/Utility/ShellCommandExecutor.swift @@ -0,0 +1,67 @@ +// +// ShellCommandExecutor.swift +// +// Created by JeneaVranceanu on 05.04.2023. +// + +import Foundation + +internal typealias ShellCommandExecutor = BashCommandExecutor + +internal enum ShellError: Error { + case commandNotFound(name: String) +} + +internal struct BashCommandExecutor { + func run(commandName: String, arguments: [String] = []) throws -> String { + var arguments = arguments + var command = commandName + if commandName.contains(" ") { + var args = commandName.trim().split(separator: " ") + command = String(args.removeFirst()) + arguments.append(contentsOf: args.map { String($0) }) + } + return try run(resolve(command), with: arguments) + } + + func run(commandName: String, arguments: [String] = []) async throws -> String { + try await withCheckedThrowingContinuation { continuation in + do { + continuation.resume(returning: try run(commandName: commandName, arguments: arguments)) + } catch { + continuation.resume(throwing: error) + } + } + } + + + func resolve(_ command: String) throws -> String { +#if os(Windows) + // TODO: add a check to make sure command exists on Windows + return command +#else + let shellCommand = try run("/bin/bash", + with: ["-l", "-c", "which \(command)"]) + .trimmingCharacters(in: .whitespacesAndNewlines) + + guard !shellCommand.isEmpty else { + throw ShellError.commandNotFound(name: command) + } + return shellCommand +#endif + } + + func run(_ command: String, with arguments: [String] = []) throws -> String { + let process = Process() + process.launchPath = command + process.arguments = arguments + let outputPipe = Pipe() + process.standardOutput = outputPipe + try process.run() + let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() + guard let output = String(bytes: outputData, encoding: .utf8)?.trim() else { + throw Web3Error.valueError(desc: "Shell command returned bytes that cannot be decoded as UTF-8: \(outputData.toHexString())") + } + return output + } +} diff --git a/Sources/web3swift/Browser/Bridge.swift b/Sources/web3swift/Browser/Bridge.swift index 3733d7af4..d6ed009f7 100644 --- a/Sources/web3swift/Browser/Bridge.swift +++ b/Sources/web3swift/Browser/Bridge.swift @@ -6,6 +6,7 @@ // Copyright © 2017 Samaritan. All rights reserved. // +#if !os(Linux) import WebKit /// Bridge for WKWebView and JavaScript @@ -245,3 +246,4 @@ fileprivate extension WKWebView { evaluateJavaScript(jsString, completionHandler: completionHandler) } } +#endif diff --git a/Sources/web3swift/Utils/EIP/EIP67Code.swift b/Sources/web3swift/Utils/EIP/EIP67Code.swift index 9bc7aa013..c57d07d6f 100755 --- a/Sources/web3swift/Utils/EIP/EIP67Code.swift +++ b/Sources/web3swift/Utils/EIP/EIP67Code.swift @@ -4,7 +4,9 @@ // import Foundation +#if !os(Linux) import CoreImage +#endif import BigInt import Web3Core @@ -77,11 +79,14 @@ extension Web3 { return mainPart } + #if !os(Linux) public func toImage(scale: Double = 1.0) -> CIImage { return EIP67CodeGenerator.createImage(from: self, scale: scale) } + #endif } + #if !os(Linux) public struct EIP67CodeGenerator { public static func createImage(from: EIP67Code, scale: Double = 1.0) -> CIImage { @@ -94,6 +99,7 @@ extension Web3 { return image } } + #endif public struct EIP67CodeParser { public static func parse(_ data: Data) -> EIP67Code? { diff --git a/Sources/web3swift/Web3/Web3+HttpProvider.swift b/Sources/web3swift/Web3/Web3+HttpProvider.swift index a6411552d..5d3e22856 100755 --- a/Sources/web3swift/Web3/Web3+HttpProvider.swift +++ b/Sources/web3swift/Web3/Web3+HttpProvider.swift @@ -6,6 +6,9 @@ import Foundation import BigInt import Web3Core +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif /// The default http provider. public class Web3HttpProvider: Web3Provider { diff --git a/Tests/web3swiftTests/localTests/BIP39Tests.swift b/Tests/web3swiftTests/localTests/BIP39Tests.swift index e9ccf6410..fbc489321 100644 --- a/Tests/web3swiftTests/localTests/BIP39Tests.swift +++ b/Tests/web3swiftTests/localTests/BIP39Tests.swift @@ -25,7 +25,7 @@ final class BIP39Tests: XCTestCase { } func testBIP39SeedAndMnemConversions() throws { - let seed = Data.randomBytes(length: 32)! + let seed = try Data.randomBytes(count: 32) let mnemonics = BIP39.generateMnemonicsFromEntropy(entropy: seed) let recoveredSeed = BIP39.mnemonicsToEntropy(mnemonics!, language: .english) XCTAssert(seed == recoveredSeed) @@ -95,7 +95,7 @@ final class BIP39Tests: XCTestCase { } func testBIP39SeedAndMnemConversionsArray() throws { - let seed = Data.randomBytes(length: 32)! + let seed = try Data.randomBytes(count: 32) let mnemonics = BIP39.generateMnemonicsFrom(entropy: seed) let recoveredSeed = BIP39.mnemonicsToEntropy(mnemonics, language: .english) XCTAssert(seed == recoveredSeed) diff --git a/Tests/web3swiftTests/localTests/EIP67Tests.swift b/Tests/web3swiftTests/localTests/EIP67Tests.swift index de9f694d1..8abf20f12 100755 --- a/Tests/web3swiftTests/localTests/EIP67Tests.swift +++ b/Tests/web3swiftTests/localTests/EIP67Tests.swift @@ -21,6 +21,7 @@ class EIP67Tests: LocalTestCase { } + #if !os(Linux) func testEIP67codeGeneration() throws { var eip67Data = Web3.EIP67Code.init(address: EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")!) eip67Data.gasLimit = BigUInt(21000) @@ -29,6 +30,7 @@ class EIP67Tests: LocalTestCase { let encoding = eip67Data.toImage(scale: 5.0) XCTAssert(encoding != CIImage()) } + #endif func testEIP67decoding() throws { var eip67Data = Web3.EIP67Code.init(address: EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")!) diff --git a/Tests/web3swiftTests/localTests/EthereumContractTest.swift b/Tests/web3swiftTests/localTests/EthereumContractTest.swift index be6c6cd4f..2d72c5133 100644 --- a/Tests/web3swiftTests/localTests/EthereumContractTest.swift +++ b/Tests/web3swiftTests/localTests/EthereumContractTest.swift @@ -66,8 +66,8 @@ class EthereumContractTest: LocalTestCase { let web3 = try await Web3.new(LocalTestCase.url) let contract = try XCTUnwrap(web3.contract(EthereumContractTest.overloadedFunctionsABI, at: EthereumAddress("0x6394b37Cf80A7358b38068f0CA4760ad49983a1B"))) let parameters: [Any] = [ - [Data.randomBytes(length: 32), Data.randomBytes(length: 32)], - [Data.randomBytes(length: 32), Data.randomBytes(length: 32)] + [try Data.randomBytes(count: 32), try Data.randomBytes(count: 32)], + [try Data.randomBytes(count: 32), try Data.randomBytes(count: 32)] ] let functionNameWithParameters = "setData(bytes32[],bytes[])" let transaction = contract.createWriteOperation(functionNameWithParameters, parameters: parameters) @@ -88,7 +88,7 @@ class EthereumContractTest: LocalTestCase { func test_encodeMethodBasedOnHexSignature() async throws { let web3 = try await Web3.new(LocalTestCase.url) let contract = try XCTUnwrap(web3.contract(EthereumContractTest.overloadedFunctionsABI, at: EthereumAddress("0x6394b37Cf80A7358b38068f0CA4760ad49983a1B"))) - let parameters: [Any] = [Data.randomBytes(length: 32), Data.randomBytes(length: 32)] + let parameters: [Any] = [try Data.randomBytes(count: 32), try Data.randomBytes(count: 32)] let functionSignature = getFuncSignature("setData(bytes32,bytes)") let transaction = contract.createWriteOperation(functionSignature, parameters: parameters) XCTAssertNotNil(transaction) diff --git a/Tests/web3swiftTests/localTests/KeystoresTests.swift b/Tests/web3swiftTests/localTests/KeystoresTests.swift index 46da9aa9b..d7f8cb5c1 100755 --- a/Tests/web3swiftTests/localTests/KeystoresTests.swift +++ b/Tests/web3swiftTests/localTests/KeystoresTests.swift @@ -27,7 +27,7 @@ class KeystoresTests: LocalTestCase { } func testBIP39SeedAndMnemConversions() throws { - let seed = Data.randomBytes(length: 32)! + let seed = try Data.randomBytes(count: 32) let mnemonics = BIP39.generateMnemonicsFromEntropy(entropy: seed) let recoveredSeed = BIP39.mnemonicsToEntropy(mnemonics!, language: .english) XCTAssert(seed == recoveredSeed) @@ -263,7 +263,7 @@ class KeystoresTests: LocalTestCase { } func testKeystoreDerivationTime() throws { - let privateKey = Data.randomBytes(length: 32)! + let privateKey = try Data.randomBytes(count: 32) measure { let ks = try! EthereumKeystoreV3(privateKey: privateKey, password: "TEST")! let account = ks.addresses!.first! @@ -272,7 +272,7 @@ class KeystoresTests: LocalTestCase { } func testSingleScryptDerivation() throws { - let privateKey = Data.randomBytes(length: 32)! + let privateKey = try Data.randomBytes(count: 32) _ = try! EthereumKeystoreV3(privateKey: privateKey, password: "TEST")! } diff --git a/Tests/web3swiftTests/localTests/LocalTestCase.swift b/Tests/web3swiftTests/localTests/LocalTestCase.swift index 7b79589c4..7bf98e28b 100644 --- a/Tests/web3swiftTests/localTests/LocalTestCase.swift +++ b/Tests/web3swiftTests/localTests/LocalTestCase.swift @@ -9,26 +9,34 @@ import web3swift // while this class does show up in the navigator, it has no associated tests class LocalTestCase: XCTestCase { + enum TestError: Error { + case testError + } + static let url = URL(string: "http://127.0.0.1:8545")! static let keyStoreManager: KeystoreManager = KeystoreManager([try! EthereumKeystoreV3(password: "web3swift")!]) override func setUp() async throws { - let web3 = try! await Web3.new(LocalTestCase.url) + let web3 = try await Web3.new(LocalTestCase.url) let block = try await web3.eth.blockNumber() guard block < 25 else { return } - let allAddresses = try! await web3.eth.ownedAccounts() + let allAddresses = try await web3.eth.ownedAccounts() let sendToAddress = allAddresses[0] - let contract = web3.contract(Web3.Utils.coldWalletABI, at: sendToAddress, abiVersion: 2) + + let contract = try XCTUnwrap(web3.contract(Web3.Utils.coldWalletABI, at: sendToAddress, abiVersion: 2)) let value = try XCTUnwrap(Utilities.parseToBigUInt("1.0", units: .ether)) + let from = allAddresses[0] - let writeTX = contract!.createWriteOperation("fallback")! + guard let writeTX = contract.createWriteOperation("fallback") else { + throw TestError.testError + } writeTX.transaction.from = from writeTX.transaction.value = value let policies = Policies(gasLimitPolicy: .manual(78423), gasPricePolicy: .manual(20000000000)) for _ in block..<25 { - _ = try! await writeTX.writeToChain(password: "", policies: policies, sendRaw: false) + _ = try await writeTX.writeToChain(password: "", policies: policies, sendRaw: false) } } } diff --git a/Tests/web3swiftTests/localTests/ShellCommandExecutorTest.swift b/Tests/web3swiftTests/localTests/ShellCommandExecutorTest.swift new file mode 100644 index 000000000..0da82e4e3 --- /dev/null +++ b/Tests/web3swiftTests/localTests/ShellCommandExecutorTest.swift @@ -0,0 +1,32 @@ +// +// ShellCommandExecutorTest.swift +// +// +// Created by JeneaVranceanu on 05.04.2023. +// + +import Foundation +import XCTest +@testable import Web3Core + +final class ShellCommandExecutorTest: XCTestCase { + + func testCommandNotFound() throws { + let commandName = "some_weird_command" + do { + _ = try ShellCommandExecutor().resolve(commandName) + } catch { + guard case let .commandNotFound(name) = error as? ShellError else { + throw error + } + + XCTAssertEqual(commandName, name) + } + } + + func testCommandFound() { + let commandName = "echo" + XCTAssertNoThrow(try ShellCommandExecutor().resolve(commandName)) + } + +} diff --git a/Tests/web3swiftTests/remoteTests/EtherscanTransactionCheckerTests.swift b/Tests/web3swiftTests/remoteTests/EtherscanTransactionCheckerTests.swift index 88de5f92b..2538719c9 100644 --- a/Tests/web3swiftTests/remoteTests/EtherscanTransactionCheckerTests.swift +++ b/Tests/web3swiftTests/remoteTests/EtherscanTransactionCheckerTests.swift @@ -6,6 +6,10 @@ import XCTest @testable import Web3Core +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + final class EtherscanTransactionCheckerTests: XCTestCase { private var testApiKey: String { "4HVPVMV1PN6NGZDFXZIYKEZRP53IA41KVC" } private var vitaliksAddress: String { "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" } @@ -79,7 +83,11 @@ final class EtherscanTransactionCheckerErrorTests: XCTestCase { // MARK: - test double final private class URLSessionMock: URLSessionProxy { + #if os(Linux) + var response: (Data, URLResponse) = (Data(), URLResponse(url: URL(string: "/")!, mimeType: nil, expectedContentLength: 0, textEncodingName: nil)) + #else var response: (Data, URLResponse) = (Data(), URLResponse()) + #endif func data(for request: URLRequest) async throws -> (Data, URLResponse) { return response