// Author: fruit.com.cn import 'package:flutter_test/flutter_test.dart'; import 'package:fruitshorts/core/data/models/wallet.dart'; import 'package:fruitshorts/core/data/remote/api_exception.dart'; import 'package:fruitshorts/core/data/repositories/commerce_repository.dart'; import 'package:fruitshorts/core/data/services/wallet_controller.dart'; void main() { test('should_expose_server_balance_when_wallet_loads', () async { final controller = WalletController( FakeCommerceRepository(snapshot: _snapshot(balance: 320)), ); await controller.load(); expect(controller.balance, 320); expect(controller.ledger.single.changeAmount, -50); expect(controller.error, isNull); }); test('should_update_balance_and_clear_error_when_unlock_succeeds', () async { final repository = FakeCommerceRepository( snapshot: _snapshot(balance: 320), unlockResult: const UnlockResult( unlocked: true, accessType: 'UNLOCKED', coinCost: 50, coinBalance: 270, ), ); final controller = WalletController(repository); await controller.load(); final result = await controller.unlock('episode-9'); expect(result?.unlocked, isTrue); expect(controller.balance, 270); expect(controller.unlocking, isFalse); expect(controller.unlockError, isNull); }); test('should_keep_balance_and_expose_message_when_unlock_is_rejected', () async { final controller = WalletController( FakeCommerceRepository( snapshot: _snapshot(balance: 20), unlockError: const ApiException(100002, '金币余额不足'), ), ); await controller.load(); final result = await controller.unlock('episode-9'); expect(result, isNull); expect(controller.balance, 20); expect(controller.unlockError, '金币余额不足'); }); } WalletSnapshot _snapshot({required int balance}) => WalletSnapshot( wallet: CoinWallet( coinBalance: balance, totalRechargedCoin: 500, totalConsumedCoin: 180, totalRefundedCoin: 0, ), ledger: [ CoinLedgerEntry( id: '1', changeAmount: -50, balanceAfter: balance, bizType: 'UNLOCK', remark: '解锁第 3 集', createdAt: DateTime(2026, 10, 19), ), ], ); class FakeCommerceRepository implements CommerceRepository { FakeCommerceRepository({ required this.snapshot, this.unlockResult, this.unlockError, }); final WalletSnapshot snapshot; final UnlockResult? unlockResult; final ApiException? unlockError; final List unlockedEpisodes = []; @override Future fetchWallet() async => snapshot; @override Future unlock(String episodeId) async { unlockedEpisodes.add(episodeId); if (unlockError != null) throw unlockError!; return unlockResult!; } }