diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..c90b341ba --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,77 @@ +name: Tests and analysis + +# Fast, pure-Dart verification: no native builds, no signing secrets. +# Runs on every push and PR (unlike the manual-dispatch build workflows, +# this finishes in minutes and needs nothing beyond pub get). +on: + push: + branches: [dev, main] + pull_request: + branches: [dev, main] + workflow_dispatch: + +concurrency: + group: test-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + test: + runs-on: ubuntu-latest + container: + # Same prebuilt image as the build workflows (bundles Flutter 3.32.0). + image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.32.0-ndkr28-go1.24.1-ruststablenightly + + steps: + - name: Fix Actions HOME (writeable for ~/.cache) + run: echo "HOME=/root" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Configure git inside the container + run: git config --global --add safe.directory '*' + + # pubspec.yaml has path deps under scripts/ that must exist before + # pub get; fetch the same prebuilt bundles the build workflows use. + - name: Fetch prebuilt torch_dart + run: | + set -x -e + pushd scripts + rm -rf torch_dart torch_dart.tar.gz + wget -q https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz -O torch_dart.tar.gz + mkdir torch_dart + tar -xzf torch_dart.tar.gz -C torch_dart + rm torch_dart.tar.gz + popd + + - name: Fetch prebuilt reown_flutter + run: | + set -x -e + pushd scripts + rm -rf reown_flutter reown_flutter.tar.gz + wget -q https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz -O reown_flutter.tar.gz + mkdir reown_flutter + tar -xzf reown_flutter.tar.gz -C reown_flutter + rm reown_flutter.tar.gz + popd + + - name: Clone BitBox Flutter + run: | + set -x -e + pushd scripts + ./build_bitbox_flutter.sh + popd + + - name: flutter pub get + run: flutter pub get + + - name: flutter analyze (errors are fatal, existing warnings are not) + run: flutter analyze lib --no-fatal-infos --no-fatal-warnings + + - name: flutter test + run: flutter test diff --git a/analysis_options.yaml b/analysis_options.yaml index 7f76d2721..9440621cf 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,25 +7,11 @@ analyzer: scripts/**, lib/**.g.dart, cw_core/lib/**.g.dart, - cw_haven/lib/**.g.dart, cw_monero/lib/**.g.dart, lib/generated/*.dart, cw_monero/ios/External/**, cw_shared_external/**, shared_external/**, - lib/bitcoin/cw_bitcoin.dart, - lib/bitcoin_cash/cw_bitcoin_cash.dart, - lib/ethereum/cw_ethereum.dart, - lib/haven/cw_haven.dart, - lib/monero/cw_monero.dart, - lib/nano/cw_nano.dart, - lib/polygon/cw_polygon.dart, - lib/solana/cw_solana.dart, - lib/tron/cw_tron.dart, - lib/wownero/cw_wownero.dart, - lib/zano/cw_zano.dart, - lib/base/cw_base.dart, - lib/arbitrum/cw_arbitrum.dart, ] language: strict-casts: true @@ -36,6 +22,14 @@ linter: - cancel_subscriptions - always_declare_return_types - prefer_final_fields + - empty_catches + - await_only_futures + - valid_regexps + - unrelated_type_equality_checks + - hash_and_equals + - test_types_in_equals + - throw_in_finally + - control_flow_in_finally formatter: diff --git a/integration_test_runner.sh b/integration_test_runner.sh index de21b3edc..e00475b3f 100755 --- a/integration_test_runner.sh +++ b/integration_test_runner.sh @@ -10,8 +10,12 @@ MAX_PARALLEL_TESTS=${MAX_PARALLEL_TESTS:-2} TEST_TIMEOUT=${TEST_TIMEOUT:-600} RETRY_COUNT=${RETRY_COUNT:-1} -# Data directories to clean +# Data directories to clean (current app dirs plus legacy pre-rename ones, +# since root_dir.dart migrates legacy data into the current dir on startup) DATA_DIRS=( + "$HOME/.local/share/com.suchsoftware.hashwallet" + "$HOME/Documents/hash_wallet" + "$HOME/.config/hash_wallet" "$HOME/.local/share/com.example.cake_wallet" "$HOME/Documents/cake_wallet" "$HOME/.config/cake_wallet" diff --git a/test/core/address_validator_test.dart b/test/core/address_validator_test.dart index 5bc2520b9..6c3f89d7b 100644 --- a/test/core/address_validator_test.dart +++ b/test/core/address_validator_test.dart @@ -12,7 +12,7 @@ void main() { test('returns correct pattern for Bitcoin', () { final pattern = AddressValidator.getPattern(CryptoCurrency.btc); expect(pattern, isNotEmpty); - expect(pattern, contains('(bc|tb)1q')); + expect(pattern, contains('bc1q')); }); test('returns correct pattern for Ethereum', () { @@ -30,8 +30,8 @@ void main() { test('returns correct pattern for Litecoin', () { final pattern = AddressValidator.getPattern(CryptoCurrency.ltc); expect(pattern, isNotEmpty); - expect(pattern, - contains('(bc|tb|ltc)1q[ac-hj-np-z02-9]{25,39}|(ltc|t)mweb1q[ac-hj-np-z02-9]{90,120}')); + expect(pattern, contains('ltc1q[ac-hj-np-z02-9]{25,39}')); + expect(pattern, contains('(ltc|t)mweb1q[ac-hj-np-z02-9]{90,120}')); }); test('returns empty string for unknown currency', () { diff --git a/test/core/lightning_invoice_service_test.dart b/test/core/lightning_invoice_service_test.dart deleted file mode 100644 index f37d2ba78..000000000 --- a/test/core/lightning_invoice_service_test.dart +++ /dev/null @@ -1,31 +0,0 @@ -import "package:hash_wallet/core/lightning_invoice_service.dart"; -import "package:cw_core/lnurl.dart"; -import "package:cw_core/utils/proxy_wrapper.dart"; -import "package:cw_core/utils/tor/abstract.dart"; -import "package:flutter_test/flutter_test.dart"; - -Future main() async { - CakeTor.instance = await CakeTorInstance.getInstance(); - - group("lightning_invoice_service", () { - test("Should resolve an empty Bolt11 invoice konsti@cake.cash", () async { - final invoice = await getBolt11FromLightingAddress("konsti@cake.cash"); - - expect(invoice, isNotNull); - expect(isBolt11ZeroInvoice(invoice!), isTrue); - }); - - test("Should not resolve an empty Bolt11 invoice viksharma@cluborange.org", () async { - final invoice = await getBolt11FromLightingAddress("viksharma@cluborange.org"); - - expect(invoice, isNull); - }); - - test("Should resolve an Bolt11 invoice viksharma@cluborange.org", () async { - final invoice = - await getBolt11FromLightingAddress("viksharma@cluborange.org", amount: 1000000000); - - expect(invoice, isNotNull); - }); - }); -} diff --git a/test/core/universal_address_detector_test.dart b/test/core/universal_address_detector_test.dart index 859662448..4f6a60272 100644 --- a/test/core/universal_address_detector_test.dart +++ b/test/core/universal_address_detector_test.dart @@ -171,30 +171,6 @@ void main() { }); }); - group('Solana Address Detection', () { - test('detects Solana address', () { - const address = '7TSTXnQZxQDA4JoNqs4DqVqE7oNWk2kmHkMT6DD6vw2S'; - final result = UniversalAddressDetector.detectAddress(address); - - expect(result.isValid, true); - expect(result.detectedCurrency, CryptoCurrency.sol); - expect(result.detectedWalletType, WalletType.solana); - expect(result.address, address); - }); - }); - - group('Tron Address Detection', () { - test('detects Tron address', () { - const address = 'TF7yQKp7pwLBSXBXthcrXMqTyjtvkJK28V'; - final result = UniversalAddressDetector.detectAddress(address); - - expect(result.isValid, true); - expect(result.detectedCurrency, CryptoCurrency.trx); - expect(result.detectedWalletType, WalletType.tron); - expect(result.address, address); - }); - }); - group('Wownero Address Detection', () { test('detects Wownero address', () { const address = @@ -208,41 +184,6 @@ void main() { }); }); - group('Zano Address Detection', () { - test('detects Zano address', () { - const address = - 'ZxDREZKHjUAUkRpWrHV9AjJHe12RhtoY9TjUUx2EznNNdwJK9yiHEhuQkqGYnyDXRnFC3Uehu6kLGZmiQab8fWoQ1JrLsuJ1i'; - final result = UniversalAddressDetector.detectAddress(address); - - expect(result.isValid, true); - expect(result.detectedCurrency, CryptoCurrency.zano); - expect(result.detectedWalletType, WalletType.zano); - expect(result.address, address); - }); - - test('detects Zano alias', () { - const address = '@testuser'; - final result = UniversalAddressDetector.detectAddress(address); - - expect(result.isValid, true); - expect(result.detectedCurrency, CryptoCurrency.zano); - expect(result.detectedWalletType, WalletType.zano); - expect(result.address, address); - }); - }); - - group('Decred Address Detection', () { - test('detects Decred address', () { - const address = 'DsW4ZtRV1DcPCuKXVVtGnCy3AtAZHg5N8nR'; - final result = UniversalAddressDetector.detectAddress(address); - - expect(result.isValid, true); - expect(result.detectedCurrency, CryptoCurrency.dcr); - expect(result.detectedWalletType, WalletType.decred); - expect(result.address, address); - }); - }); - group('Invalid Address Detection', () { test('handles empty input', () { const address = ''; diff --git a/test/entities/parse_address_from_domain_test.dart b/test/entities/parse_address_from_domain_test.dart index a7470e6fa..eeff16b44 100644 --- a/test/entities/parse_address_from_domain_test.dart +++ b/test/entities/parse_address_from_domain_test.dart @@ -174,14 +174,5 @@ ltcmweb1qq0at62jjucmawxp78qutn0cqwkwahcfx7fxls0r2ma5llg5w6wyy2qe20gxa3rku2658j88 // // responses for each type of address resolution. // }); - group('unstoppableDomains', () { - test('contains expected TLDs', () { - expect(AddressResolver.unstoppableDomains, contains('crypto')); - expect(AddressResolver.unstoppableDomains, contains('eth')); - expect(AddressResolver.unstoppableDomains, contains('bitcoin')); - expect(AddressResolver.unstoppableDomains, contains('x')); - expect(AddressResolver.unstoppableDomains, contains('wallet')); - }); - }); }); } diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 0c1b1b52c..000000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,34 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:hash_wallet/main.dart'; - -final quickActionsStream = StreamController.broadcast(); - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(App(quickActionsStream: quickActionsStream.stream)); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}