From 53aaeb6f99605dcbf74c092344f87377ed34f207 Mon Sep 17 00:00:00 2001
From: Alejo Acosta <alejoacos@gmail.com>
Date: Wed, 27 Nov 2024 17:02:37 -0300
Subject: [PATCH] update qiwallet aggregation unit test to use mock provider

---
 .../unit/qihdwallet-aggregate.unit.test.ts    | 140 +++++-------------
 testcases/qi-wallet-aggregate.json.gz         | Bin 604 -> 551 bytes
 2 files changed, 39 insertions(+), 101 deletions(-)

diff --git a/src/_tests/unit/qihdwallet-aggregate.unit.test.ts b/src/_tests/unit/qihdwallet-aggregate.unit.test.ts
index 72233e71..01510da5 100644
--- a/src/_tests/unit/qihdwallet-aggregate.unit.test.ts
+++ b/src/_tests/unit/qihdwallet-aggregate.unit.test.ts
@@ -3,21 +3,9 @@ import { loadTests } from '../utils.js';
 import { QiHDWallet } from '../../wallet/qi-hdwallet.js';
 import { Mnemonic } from '../../wallet/mnemonic.js';
 import { Zone } from '../../constants/zones.js';
-import { UTXO } from '../../quais.js';
-
-// Custom error class for controlled exit
-class TestCompletionError extends Error {
-    constructor(public readonly capturedArgs: any[]) {
-        super('Test completed successfully');
-        this.name = 'TestCompletionError';
-    }
-}
-
-function sortByDenomination(inputs: UTXO[], order: 'asc' | 'desc' = 'asc') {
-    return inputs.sort((a, b) =>
-        order === 'asc' ? (a.denomination || 0) - (b.denomination || 0) : (b.denomination || 0) - (a.denomination || 0),
-    );
-}
+import { QiTransactionResponse } from '../../quais.js';
+import { MockProvider } from './mockProvider.js';
+import { TxInput, TxOutput } from '../../transaction/utxo.js';
 
 interface AggregateTestCase {
     mnemonic: string;
@@ -39,39 +27,37 @@ interface AggregateTestCase {
     }>;
     fee: number;
     expected: {
-        selection: {
-            inputs: Array<{
-                txhash: string;
-                index: number;
-                address: string;
-                denomination: number;
-            }>;
-            spendOutputs: Array<{
-                denomination: number;
-            }>;
-            changeOutputs: Array<{
-                denomination: number;
-            }>;
-        };
-        inputPubKeys: string[];
-        sendAddresses: string[];
-        changeAddresses: string[];
+        txInputs: Array<TxInput>;
+        txOutputs: Array<TxOutput>;
     };
 }
 
+// helper method to sort txInputs by txhash
+const sortTxInputs = (txInputs: Array<TxInput> | undefined) => {
+    return (txInputs || []).sort((a, b) => a.txhash.localeCompare(b.txhash));
+};
+
+// helper method to sort txOutputs by address and filtering out 'lock'
+const sortTxOutputs = (txOutputs: Array<TxOutput> | undefined) => {
+    return (txOutputs || [])
+        .map((txOutput) => ({
+            address: txOutput.address.toLowerCase(),
+            denomination: txOutput.denomination,
+        }))
+        .sort((a, b) => a.address.localeCompare(b.address));
+};
+
 describe('QiHDWallet.aggregate', () => {
     const testCases = loadTests<AggregateTestCase>('qi-wallet-aggregate');
 
     testCases.forEach((testCase) => {
-        it(`should correctly aggregate UTXOs for wallet with mnemonic`, async () => {
-            // Create wallet from mnemonic
+        it(`should correctly aggregate UTXOs for QiHDWallet`, async () => {
             const mnemonic = Mnemonic.fromPhrase(testCase.mnemonic);
             const wallet = QiHDWallet.fromMnemonic(mnemonic);
 
-            // Mock provider with minimal implementation
-            wallet.connect({
-                getNetwork: async () => ({ chainId: BigInt(1) }),
-            } as any);
+            const mockProvider = new MockProvider({ network: BigInt(1) });
+
+            wallet.connect(mockProvider);
 
             // Add addresses to wallet before importing outpoints
             for (const addressToAdd of testCase.addressesToAdd) {
@@ -81,72 +67,24 @@ describe('QiHDWallet.aggregate', () => {
             // Import test outpoints
             wallet.importOutpoints(testCase.outpointInfos);
 
-            // Spy on prepareTransaction and throw custom error to exit early
-            wallet['prepareTransaction'] = async (...args) => {
-                throw new TestCompletionError(args);
-            };
-
-            try {
-                await wallet.aggregate(testCase.zone as any);
-                assert.fail('Expected TestCompletionError to be thrown');
-            } catch (error) {
-                if (error instanceof TestCompletionError) {
-                    const [selection, inputPubKeys, sendAddresses, changeAddresses] = error.capturedArgs;
-
-                    const sortedInputs = sortByDenomination(selection.inputs, 'desc');
-                    const sortedExpectedInputs = sortByDenomination(
-                        testCase.expected.selection.inputs as UTXO[],
-                        'desc',
-                    );
+            const txResponse = (await wallet.aggregate(testCase.zone)) as QiTransactionResponse;
 
-                    // Verify selection with complete input properties
-                    assert.deepStrictEqual(
-                        sortedInputs.map((input: UTXO) => ({
-                            txhash: input.txhash,
-                            index: input.index,
-                            address: input.address,
-                            denomination: input.denomination,
-                        })),
-                        sortedExpectedInputs,
-                        `inputs: expected: ${JSON.stringify(sortedExpectedInputs, null, 2)}, \nactual: ${JSON.stringify(sortedInputs, null, 2)}`,
-                    );
+            // assert txResponse is not null or undefined
+            assert(txResponse !== null && txResponse !== undefined, 'txResponse is null or undefined');
 
-                    // Verify spendOutputs
-                    assert.deepStrictEqual(
-                        selection.spendOutputs.map((output: UTXO) => ({ denomination: output.denomination })),
-                        testCase.expected.selection.spendOutputs,
-                        `spendOutputs: expected: ${JSON.stringify(testCase.expected.selection.spendOutputs, null, 2)}, \nactual: ${JSON.stringify(selection.spendOutputs, null, 2)}`,
-                    );
+            // assert expected txInputs are equal to captured txInputs
+            assert.deepStrictEqual(
+                sortTxInputs(txResponse.txInputs),
+                sortTxInputs(testCase.expected.txInputs),
+                `txInputs: expected: ${JSON.stringify(testCase.expected.txInputs, null, 2)}, \nactual: ${JSON.stringify(txResponse.txInputs, null, 2)}`,
+            );
 
-                    // Verify changeOutputs
-                    assert.deepStrictEqual(
-                        selection.changeOutputs.map((output: UTXO) => ({ denomination: output.denomination })),
-                        testCase.expected.selection.changeOutputs,
-                        `changeOutputs: expected: ${JSON.stringify(testCase.expected.selection.changeOutputs, null, 2)}, \nactual: ${JSON.stringify(selection.changeOutputs, null, 2)}`,
-                    );
-
-                    // Verify input public keys
-                    assert.deepStrictEqual(
-                        inputPubKeys,
-                        testCase.expected.inputPubKeys,
-                        `inputPubKeys: expected: ${JSON.stringify(testCase.expected.inputPubKeys, null, 2)}, \nactual: ${JSON.stringify(inputPubKeys, null, 2)}`,
-                    );
-
-                    // Verify addresses
-                    assert.deepStrictEqual(
-                        sendAddresses,
-                        testCase.expected.sendAddresses,
-                        `sendAddresses: expected: ${JSON.stringify(testCase.expected.sendAddresses, null, 2)}, \nactual: ${JSON.stringify(sendAddresses, null, 2)}`,
-                    );
-                    assert.deepStrictEqual(
-                        changeAddresses,
-                        testCase.expected.changeAddresses,
-                        `changeAddresses: expected: ${JSON.stringify(testCase.expected.changeAddresses, null, 2)}, \nactual: ${JSON.stringify(changeAddresses, null, 2)}`,
-                    );
-                } else {
-                    throw error;
-                }
-            }
+            // assert expected txOutputs are equal to captured txOutputs
+            assert.deepStrictEqual(
+                sortTxOutputs(txResponse.txOutputs),
+                sortTxOutputs(testCase.expected.txOutputs),
+                `txOutputs: expected: ${JSON.stringify(testCase.expected.txOutputs, null, 2)}, \nactual: ${JSON.stringify(txResponse.txOutputs, null, 2)}`,
+            );
         });
     });
 });
diff --git a/testcases/qi-wallet-aggregate.json.gz b/testcases/qi-wallet-aggregate.json.gz
index af0bab08fac376f85e263908dd98f96c2e56891d..5e53107e72c0ba0dc91c3c6b6fa84a3fbc761dd4 100644
GIT binary patch
delta 534
zcmV+x0_pwS1g8WCABzYGp>{`+2OfV-kDD+MJ(FKyaoz*in2+AFn?}m1m)@!z3?76P
z<3YhBq}BfS8WOfyb`O=R-AYmBfbm#<Z{D*sv-q*_el0xDuMN~_RN;RiKGY2!VNVLg
z9XTk}%8*)Vt<aDoq9*nT`T#_k0}iXZ21KF}7KrF<g?mzqx)z$WLR+%O)9`;=se{AJ
z)!tSF^{H-DL7>i2o=62+Bgp|u(ss%c)uJB2YSn`z7J5tC2Ce@vRQeMQ3{?hw5S%mE
z*#?!d8&hKIj+`cMRCRUc(&vol+rAQQH9`DSgku2%s4^0IhZ#%>2?lHBJaMjF=QS3)
zVLxu4&skqcxrer$YRbxxM?8O{Oz>qGaJm#>OxGz-f#w<IY0RD^TV1%{8ZrPD1$LwA
zk?73xDVET?CG%i+OXd;XEtv<uTQZOMuPurHuO;rY9Y@iH3EJ<tlyWwUu5P_9Y;#e^
zQvMpQgn>GAR*YNd8z?Nu7q^|+8`E_5W$H~_A2jCPK7{8tUA}|Ug@=FU5Tps^f)`M7
zkwPBjB~5Zx1`tWg<2YszMFr(DPa#VJE=qR}z$h&lOC$dbdwIDJQA2M^4ZA5d(M_r0
zH>D>2d^O{;eVBUPH#cqGZ+5c|pO*PjBuPdegLRgzSCX%y=#hObN{A(YDruN77W~mO
Yn<n8a`l%&9FSfq{Mk@wy2QLi(0IRh05C8xG

delta 587
zcmV-R0<`_71l$A%ABzYG>EJz)2OfVtZ`&{so$X&iXzn19qAX=|;u;7#v_rST10|j+
zA{0-sC|hok|6b}Vv7HPBf+T?91N6Zo`0n1j18@FoKKnbL&Aie;iAH7K4e_9?a1UFO
zAwH0uLahubmDUOk*&}LV_n>z`l-XhTb;p26G{OQAjV*9XN>P?VlUis?ws?Q&eham8
zn5o*@f}lQ>mC6W|Dar#WKx-r^Ku+pLS)yvx9ayPaki<f3NnN4!zI2)XK?7Zue(U>#
z2OC?VGWOo&Si3F9Y1Xqk+H>x~GxKa)h`N{{-ef_aKnJRfgw|pDlS6{RQaMeWYR7(!
z*+aMPmyd(j6H;!WuBV)mJm7yJPbd?78TgzoMG(<-%wwQwLU|mqJIPjK?3aRcfLVr3
z&$<sZ#Q6|O=v9()V^>MeExbx{Zu~0AxkdjiN%XmrxYwp1MPnqWf8tWgvsrX>>NK(Y
zksXWqDO?U+=)g(QFQKg<vmht6y&AN03=)h%l&Ko~GzF*F?=<#a7Uq9#V!zVsgMX~#
zyRe}b#fDuJoA9F8@QY#-eH@!;WYd2(<K<abVB~i<fL_1v&LD!<AhJT3-*AMnd7f~?
zP5Prrzro=dzhgsF@Z$yLf@hF(5knfLIbEbI_aT&&M^VHe3^U4Q8bh-1xyao<0HZW#
zES{SBiuD;-^QZc|@o78ca+=sDCHCVyU5dpbp|}1zN!BaLS7CU|zGgW@lHcVtSTN=v
ZgAKiO0@roZ<9zO(%|C@Rh~;Sy00008A^`vZ