Skip to content

Commit

Permalink
add unit test for qiwallet scan() method
Browse files Browse the repository at this point in the history
  • Loading branch information
alejoacosta74 authored and rileystephens28 committed Dec 30, 2024
1 parent 7e579a7 commit bf21419
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 32 deletions.
77 changes: 45 additions & 32 deletions src/_tests/unit/mockProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export class MockProvider implements Provider {
private _transactions: Map<string, TransactionResponse> = new Map();
private _blocks: Map<string, Block> = new Map();
private _balances: Map<string, bigint> = new Map();
private _lockedBalances: Map<string, bigint> = new Map();
private _outpoints: Map<string, Array<Outpoint>> = new Map();
private _eventHandlers: Map<
string,
Array<{
Expand All @@ -54,11 +56,27 @@ export class MockProvider implements Provider {
this._transactions.set(hash.toLowerCase(), tx);
}

public setOutpoints(address: string, outpoints: Array<Outpoint>): void {
this._outpoints.set(address.toLowerCase(), outpoints);
}

// Implementation of Provider interface methods
async getNetwork(): Promise<Network> {
return this._network;
}

public setBlock(key: string, block: Block): void {
this._blocks.set(key, block);
}

async getBlock(shard: Shard, blockHashOrBlockTag: BlockTag | string, prefetchTxs?: boolean): Promise<null | Block> {
if (typeof blockHashOrBlockTag === 'string') {
return this._blocks.get(blockHashOrBlockTag) ?? null;
}
// Handle block tag (latest, pending, etc.)
throw new Error('Method not implemented.');
}

async getBlockNumber(shard: Shard): Promise<number> {
return this._blockNumber;
}
Expand Down Expand Up @@ -87,17 +105,8 @@ export class MockProvider implements Provider {
throw new Error('Method not implemented.');
}

async getOutpointsByAddress(): Promise<Array<Outpoint>> {
// TODO: Implement
throw new Error('Method not implemented.');
}

async getBlock(shard: Shard, blockHashOrBlockTag: BlockTag | string, prefetchTxs?: boolean): Promise<null | Block> {
if (typeof blockHashOrBlockTag === 'string') {
return this._blocks.get(blockHashOrBlockTag) ?? null;
}
// Handle block tag (latest, pending, etc.)
throw new Error('Method not implemented.');
async getOutpointsByAddress(address: AddressLike): Promise<Array<Outpoint>> {
return this._outpoints.get(address.toString().toLowerCase()) ?? [];
}

async estimateFeeForQi(tx: QiPerformActionTransaction): Promise<bigint> {
Expand Down Expand Up @@ -233,64 +242,68 @@ export class MockProvider implements Provider {
throw new Error('Method not implemented.');
}

async getLockedBalance(): Promise<bigint> {
throw new Error('Method not implemented.');
public setLockedBalance(address: AddressLike, balance: bigint): void {
this._lockedBalances.set(address.toString().toLowerCase(), balance);
}

async getLockedBalance(address: AddressLike): Promise<bigint> {
return this._lockedBalances.get(address.toString().toLowerCase()) ?? BigInt(0);
}
async getTransactionCount(): Promise<number> {
throw new Error('Method not implemented.');
throw new Error('getTransactionCount: Method not implemented.');
}
async getCode(): Promise<string> {
throw new Error('Method not implemented.');
throw new Error('getCode: Method not implemented.');
}
async getStorage(): Promise<string> {
throw new Error('Method not implemented.');
throw new Error('getStorage: Method not implemented.');
}
async estimateGas(): Promise<bigint> {
throw new Error('Method not implemented.');
throw new Error('estimateGas: Method not implemented.');
}
async createAccessList(): Promise<AccessList> {
throw new Error('Method not implemented.');
throw new Error('createAccessList: Method not implemented.');
}
async call(): Promise<string> {
throw new Error('Method not implemented.');
throw new Error('call: Method not implemented.');
}
async getTransaction(): Promise<null | TransactionResponse> {
throw new Error('Method not implemented.');
throw new Error('getTransaction: Method not implemented.');
}
async getTransactionReceipt(): Promise<null | TransactionReceipt> {
throw new Error('Method not implemented.');
throw new Error('getTransactionReceipt: Method not implemented.');
}
async getTransactionResult(): Promise<null | string> {
throw new Error('Method not implemented.');
throw new Error('getTransactionResult: Method not implemented.');
}
async getLogs(): Promise<Array<Log>> {
throw new Error('Method not implemented.');
throw new Error('getLogs: Method not implemented.');
}
async waitForTransaction(): Promise<null | TransactionReceipt> {
throw new Error('Method not implemented.');
throw new Error('waitForTransaction: Method not implemented.');
}
async waitForBlock(): Promise<Block> {
throw new Error('Method not implemented.');
throw new Error('waitForBlock: Method not implemented.');
}
async getProtocolExpansionNumber(): Promise<number> {
throw new Error('Method not implemented.');
throw new Error('getProtocolExpansionNumber: Method not implemented.');
}
async getTxPoolContent(zone: Zone): Promise<txpoolContentResponse> {
throw new Error('Method not implemented.');
throw new Error('getTxPoolContent: Method not implemented.');
}
async txPoolInspect(zone: Zone): Promise<txpoolInspectResponse> {
throw new Error('Method not implemented.');
throw new Error('txPoolInspect: Method not implemented.');
}
async getQiRateAtBlock(): Promise<bigint> {
throw new Error('Method not implemented.');
throw new Error('getQiRateAtBlock: Method not implemented.');
}
async getLatestQiRate(): Promise<bigint> {
throw new Error('Method not implemented.');
throw new Error('getLatestQiRate: Method not implemented.');
}
async getQuaiRateAtBlock(): Promise<bigint> {
throw new Error('Method not implemented.');
throw new Error('getQuaiRateAtBlock: Method not implemented.');
}
async getLatestQuaiRate(): Promise<bigint> {
throw new Error('Method not implemented.');
throw new Error('getLatestQuaiRate: Method not implemented.');
}
}
109 changes: 109 additions & 0 deletions src/_tests/unit/qihdwallet-scan.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import assert from 'assert';
import { loadTests } from '../utils.js';
import { Mnemonic, QiHDWallet, Zone, OutpointInfo, Block, QiAddressInfo } from '../../index.js';
import { Outpoint } from '../../transaction/utxo.js';
import { MockProvider } from './mockProvider.js';

process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});

import dotenv from 'dotenv';
const env = process.env.NODE_ENV || 'development';
dotenv.config({ path: `.env.${env}` });
dotenv.config({ path: `.env`, override: false });

interface ScanTestCase {
name: string;
mnemonic: string;
provider_outpoints: Array<{
address: string;
outpoints: Array<Outpoint>;
}>;
provider_locked_balance: Array<{
address: string;
balance: number;
}>;
provider_balance: Array<{
address: string;
balance: number;
}>;
provider_blocks: Array<{
key: string;
block: Block;
}>;
expected_external_addresses: Array<QiAddressInfo>;
expected_change_addresses: Array<QiAddressInfo>;
expected_outpoints_info: Array<OutpointInfo>;
expected_balance: number;
}

describe('QiHDWallet scan', async function () {
const tests = loadTests<ScanTestCase>('qi-wallet-scan');

for (const test of tests) {
this.timeout(1200000);

const mockProvider = new MockProvider();

// set the provider outpoints
for (const outpoint of test.provider_outpoints) {
mockProvider.setOutpoints(outpoint.address, outpoint.outpoints);
}

// set the provider blocks
for (const block of test.provider_blocks) {
mockProvider.setBlock(block.key, block.block);
}

// set the provider locked balace
for (const lockedBalance of test.provider_locked_balance) {
mockProvider.setLockedBalance(lockedBalance.address, BigInt(lockedBalance.balance));
}

// set the provider balance
for (const balance of test.provider_balance) {
mockProvider.setBalance(balance.address, BigInt(balance.balance));
}

const mnemonic = Mnemonic.fromPhrase(test.mnemonic);
const wallet = QiHDWallet.fromMnemonic(mnemonic);
wallet.connect(mockProvider);
it('it scans the wallet with no errors', async function () {
try {
await wallet.scan(Zone.Cyprus1);
assert.ok(true, '====> TESTING: scan completed');
} catch (error) {
console.error('====> TESTING: error: ', error);
assert.fail('====> TESTING: error: ', error);
}
});
it('validates expected external addresses', async function () {
const externalAddresses = wallet.getAddressesForZone(Zone.Cyprus1);
const sortedExternalAddresses = externalAddresses.sort((a, b) => a.address.localeCompare(b.address));
const sortedExpectedExternalAddresses = test.expected_external_addresses.sort((a, b) =>
a.address.localeCompare(b.address),
);
assert.deepEqual(sortedExternalAddresses, sortedExpectedExternalAddresses);
});

it('validates expected change addresses', async function () {
const changeAddresses = wallet.getChangeAddressesForZone(Zone.Cyprus1);
const sortedChangeAddresses = changeAddresses.sort((a, b) => a.address.localeCompare(b.address));
const sortedExpectedChangeAddresses = test.expected_change_addresses.sort((a, b) =>
a.address.localeCompare(b.address),
);
assert.deepEqual(sortedChangeAddresses, sortedExpectedChangeAddresses);
});

it('validates wallet balance', async function () {
const balance = await wallet.getBalanceForZone(Zone.Cyprus1);
assert.equal(balance.toString(), test.expected_balance.toString());
});

it('validates expected outpoints info', async function () {
assert.deepEqual(wallet.getOutpoints(Zone.Cyprus1), test.expected_outpoints_info);
});
}
});
109 changes: 109 additions & 0 deletions src/_tests/unit/qihdwallet-scan.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import assert from 'assert';
import { loadTests } from '../utils.js';
import { Mnemonic, QiHDWallet, Zone, OutpointInfo, Block, QiAddressInfo } from '../../index.js';
import { Outpoint } from '../../transaction/utxo.js';
import { MockProvider } from './mockProvider.js';

process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});

import dotenv from 'dotenv';
const env = process.env.NODE_ENV || 'development';
dotenv.config({ path: `.env.${env}` });
dotenv.config({ path: `.env`, override: false });

interface ScanTestCase {
name: string;
nemonic: string;
provider_outpoints: Array<{
address: string;
outpoints: Array<Outpoint>;
}>;
provider_locked_balance: Array<{
address: string;
balance: number;
}>;
provider_balance: Array<{
address: string;
balance: number;
}>;
provider_blocks: Array<{
key: string;
block: Block;
}>;
expected_external_addresses: Array<QiAddressInfo>;
expected_change_addresses: Array<QiAddressInfo>;
expected_outpoints_info: Array<OutpointInfo>;
expected_balance: number;
}

describe('QiHDWallet scan', async function () {
const tests = loadTests<ScanTestCase>('qi-wallet-scan');

for (const test of tests) {
this.timeout(1200000);

const mockProvider = new MockProvider();

// set the provider outpoints
for (const outpoint of test.provider_outpoints) {
mockProvider.setOutpoints(outpoint.address, outpoint.outpoints);
}

// set the provider blocks
for (const block of test.provider_blocks) {
mockProvider.setBlock(block.key, block.block);
}

// set the provider locked balace
for (const lockedBalance of test.provider_locked_balance) {
mockProvider.setLockedBalance(lockedBalance.address, BigInt(lockedBalance.balance));
}

// set the provider balance
for (const balance of test.provider_balance) {
mockProvider.setBalance(balance.address, BigInt(balance.balance));
}

const mnemonic = Mnemonic.fromPhrase(test.nemonic);
const wallet = QiHDWallet.fromMnemonic(mnemonic);
wallet.connect(mockProvider);
it('it scans the wallet with no errors', async function () {
try {
await wallet.scan(Zone.Cyprus1);
assert.ok(true, '====> TESTING: scan completed');
} catch (error) {
console.error('====> TESTING: error: ', error);
assert.fail('====> TESTING: error: ', error);
}
});
it('validates expected external addresses', async function () {
const externalAddresses = wallet.getAddressesForZone(Zone.Cyprus1);
const sortedExternalAddresses = externalAddresses.sort((a, b) => a.address.localeCompare(b.address));
const sortedExpectedExternalAddresses = test.expected_external_addresses.sort((a, b) =>
a.address.localeCompare(b.address),
);
assert.deepEqual(sortedExternalAddresses, sortedExpectedExternalAddresses);
});

it('validates expected change addresses', async function () {
const changeAddresses = wallet.getChangeAddressesForZone(Zone.Cyprus1);
const sortedChangeAddresses = changeAddresses.sort((a, b) => a.address.localeCompare(b.address));
const sortedExpectedChangeAddresses = test.expected_change_addresses.sort((a, b) =>
a.address.localeCompare(b.address),
);
assert.deepEqual(sortedChangeAddresses, sortedExpectedChangeAddresses);
});

it('validates wallet balance', async function () {
const balance = await wallet.getBalanceForZone(Zone.Cyprus1);
assert.equal(balance.toString(), test.expected_balance.toString());
});

it('validates expected outpoints info', async function () {
assert.deepEqual(wallet.getOutpoints(Zone.Cyprus1), test.expected_outpoints_info);
});
}
});
Binary file added testcases/qi-wallet-scan.json.gz
Binary file not shown.

0 comments on commit bf21419

Please sign in to comment.