11import 'dart:async' ;
22
33import 'package:bb_mobile/core/entities/signer_entity.dart' show SignerEntity;
4+ import 'package:bb_mobile/core/exchange/domain/entity/order.dart' ;
45import 'package:bb_mobile/core/exchange/domain/usecases/get_order_usercase.dart' ;
56import 'package:bb_mobile/core/swaps/domain/usecases/get_swap_usecase.dart' ;
67import 'package:bb_mobile/core/swaps/domain/usecases/watch_swap_usecase.dart' ;
@@ -145,6 +146,7 @@ void main() {
145146 late _MockBroadcastOriginalTransactionUsecase broadcastOriginalTransaction;
146147 late _MockGetTransactionOrderSwapUsecase getTransactionOrderSwap;
147148 late _MockWatchTransactionOrderSwapUsecase watchTransactionOrderSwap;
149+ late _MockGetOrderUsecase getOrder;
148150
149151 TransactionDetailsCubit buildCubit () => TransactionDetailsCubit (
150152 getWalletUsecase: getWallet,
@@ -155,7 +157,7 @@ void main() {
155157 getSwapUsecase: _MockGetSwapUsecase (),
156158 getPayjoinByIdUsecase: getPayjoinById,
157159 getPayjoinByTxIdUsecase: getPayjoinByTxId,
158- getOrderUsecase: _MockGetOrderUsecase () ,
160+ getOrderUsecase: getOrder ,
159161 watchSwapUsecase: _MockWatchSwapUsecase (),
160162 watchPayjoinUsecase: watchPayjoin,
161163 watchTransactionOrderSwapUsecase: watchTransactionOrderSwap,
@@ -178,6 +180,7 @@ void main() {
178180 broadcastOriginalTransaction = _MockBroadcastOriginalTransactionUsecase ();
179181 getTransactionOrderSwap = _MockGetTransactionOrderSwapUsecase ();
180182 watchTransactionOrderSwap = _MockWatchTransactionOrderSwapUsecase ();
183+ getOrder = _MockGetOrderUsecase ();
181184
182185 when (() => broadcastOriginalTransaction.canExecute (any ())).thenAnswer ((
183186 invocation,
@@ -1313,8 +1316,97 @@ void main() {
13131316 },
13141317 );
13151318 });
1319+
1320+ group ('TransactionDetailsCubit.initByOrderId transaction resolution' , () {
1321+ // _loadDetailsByOrderId looks the txid up, then hands it to
1322+ // initByWalletTxId, which looks it up again — so the usecase is called
1323+ // more than once per init. What matters is which txid was asked for, not
1324+ // how often, hence capture over a call count.
1325+ void expectResolvedTxIds (String txId) {
1326+ final resolved = verify (
1327+ () => getTransactionsByTxId.execute (captureAny ()),
1328+ ).captured;
1329+ expect (resolved, isNotEmpty);
1330+ expect (resolved, everyElement (txId));
1331+ }
1332+
1333+ test ('resolves the payjoin txid when the order has no txid yet' , () async {
1334+ // The exchange can know the payjoin it broadcast before it reports its
1335+ // own payout txid. Without the fallback the screen stays on order-only
1336+ // details, with no payjoin shown and no watcher armed.
1337+ final order = _buyOrder (payjoinTxId: 'payjoin-txid' );
1338+ when (
1339+ () => getOrder.execute (orderId: 'order-1' ),
1340+ ).thenAnswer ((_) async => order);
1341+ when (() => getTransactionsByTxId.execute (any ())).thenAnswer (
1342+ (_) async => [Transaction (payjoin: _receiver (), order: order)],
1343+ );
1344+
1345+ final cubit = buildCubit ();
1346+ addTearDown (cubit.close);
1347+ await cubit.initByOrderId ('order-1' );
1348+
1349+ expectResolvedTxIds ('payjoin-txid' );
1350+ });
1351+
1352+ test ('prefers the order txid when both are known' , () async {
1353+ final order = _buyOrder (
1354+ bitcoinTransactionId: 'payout-txid' ,
1355+ payjoinTxId: 'payjoin-txid' ,
1356+ );
1357+ when (
1358+ () => getOrder.execute (orderId: 'order-1' ),
1359+ ).thenAnswer ((_) async => order);
1360+ when (() => getTransactionsByTxId.execute (any ())).thenAnswer (
1361+ (_) async => [Transaction (payjoin: _receiver (), order: order)],
1362+ );
1363+
1364+ final cubit = buildCubit ();
1365+ addTearDown (cubit.close);
1366+ await cubit.initByOrderId ('order-1' );
1367+
1368+ expectResolvedTxIds ('payout-txid' );
1369+ });
1370+
1371+ test ('falls back to order-only details when no txid is known' , () async {
1372+ final order = _buyOrder ();
1373+ when (
1374+ () => getOrder.execute (orderId: 'order-1' ),
1375+ ).thenAnswer ((_) async => order);
1376+
1377+ final cubit = buildCubit ();
1378+ addTearDown (cubit.close);
1379+ await cubit.initByOrderId ('order-1' );
1380+
1381+ verifyNever (() => getTransactionsByTxId.execute (any ()));
1382+ expect (cubit.state.transaction? .order, order);
1383+ });
1384+ });
13161385}
13171386
1387+ Order _buyOrder ({String ? bitcoinTransactionId, String ? payjoinTxId}) =>
1388+ Order .buy (
1389+ orderId: 'order-1' ,
1390+ orderType: OrderType .buy,
1391+ message: OrderMessage (code: '' , message: '' ),
1392+ orderNumber: 1 ,
1393+ payinAmount: 100 ,
1394+ payinCurrency: 'CAD' ,
1395+ payoutAmount: 0.001 ,
1396+ payoutCurrency: 'BTC' ,
1397+ payinMethod: OrderPaymentMethod .eTransfer,
1398+ payoutMethod: OrderPaymentMethod .bitcoin,
1399+ orderStatus: OrderStatus .inProgress,
1400+ payinStatus: OrderPayinStatus .completed,
1401+ payoutStatus: OrderPayoutStatus .completed,
1402+ createdAt: DateTime .utc (2026 , 8 , 19 ),
1403+ bitcoinTransactionId: bitcoinTransactionId,
1404+ payjoinDetails: payjoinTxId == null
1405+ ? null
1406+ : OrderPayjoinDetails (txid: payjoinTxId),
1407+ isTestnet: false ,
1408+ );
1409+
13181410OrderSwapRecord _receiveOrderSwap () {
13191411 final createdAt = DateTime .utc (2026 , 8 , 10 );
13201412 return OrderSwapRecord (
0 commit comments