Skip to content

Commit

Permalink
test(e2e): util to check min coin value
Browse files Browse the repository at this point in the history
  • Loading branch information
mirceahasegan committed Mar 17, 2023
1 parent 2292dc5 commit cdfdc15
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 31 deletions.
22 changes: 14 additions & 8 deletions packages/e2e/jest.config.js
@@ -1,9 +1,13 @@
const project = (displayName) => ({
displayName,
const commonProjectProps = {
preset: 'ts-jest',
setupFiles: ['dotenv/config'],
testMatch: [`<rootDir>/test/${displayName}/**/*.test.ts`],
transform: { '^.+\\.test.ts?$': 'ts-jest' }
};

const project = (displayName) => ({
displayName,
testMatch: [`<rootDir>/test/${displayName}/**/*.test.ts`],
...commonProjectProps
});

// jest tests suitable to run in an environment with real ADA (not tADA)
Expand All @@ -25,17 +29,19 @@ module.exports = {
project('load-testing'),
project('local-network'),
project('long-running'),
project('measurement-util'),
project('ogmios'),
project('projection'),
project('providers'),
project('wallet'),
{
...commonProjectProps,
displayName: 'wallet-real-ada',
preset: 'ts-jest',
setupFiles: ['dotenv/config'],
testMatch: [`<rootDir>/test/wallet/SingleAddressWallet/(${realAdaTestFileNames.join('|')}).test.ts`],
transform: { '^.+\\.test.ts?$': 'ts-jest' }
testMatch: [`<rootDir>/test/wallet/SingleAddressWallet/(${realAdaTestFileNames.join('|')}).test.ts`]
},
{
...commonProjectProps,
displayName: 'utils',
testMatch: ['<rootDir>/test/measurement-util/*.test.ts', '<rootDir>/test/util.test.ts']
}
],
testTimeout: 1000 * 60 * 25
Expand Down
2 changes: 1 addition & 1 deletion packages/e2e/package.json
Expand Up @@ -23,7 +23,7 @@
"load-test-custom:wallet-init": "ts-node test/load-test-custom/wallet-init/wallet-init.test.ts",
"test": "shx echo 'test' command not implemented yet",
"test:blockfrost": "jest -c jest.config.js --forceExit --selectProjects blockfrost --runInBand --verbose",
"test:measurement-util": "jest -c jest.config.js --forceExit --selectProjects measurement-util --verbose",
"test:utils": "jest -c jest.config.js --forceExit --selectProjects utils --verbose",
"test:load-testing": "jest -c jest.config.js --forceExit --selectProjects load-testing --runInBand --verbose",
"test:long-running": "jest -c jest.config.js --forceExit --selectProjects long-running --runInBand --verbose",
"test:local-network": "jest -c jest.config.js --forceExit --selectProjects local-network --runInBand --verbose",
Expand Down
84 changes: 84 additions & 0 deletions packages/e2e/test/util.test.ts
@@ -0,0 +1,84 @@
import { BehaviorSubject, NEVER, of } from 'rxjs';
import { Cardano } from '@cardano-sdk/core';
import { ObservableWallet } from '@cardano-sdk/wallet';
import { insufficientFundsMessage, walletReady } from './util';

describe('util for e2e tests', () => {
describe('walletReady', () => {
const address = Cardano.PaymentAddress(
'addr_test1qpcncempf4svkpw0salztrsxzrfpr5ll323q5whw7lv94vyw0kz5rxvdaq6u6tslwfrrgz6l4n4lpcpnawn87yl9k6dsu4hhg2'
);

const timeoutErrStr = 'Took too long to be ready';

let wallet: ObservableWallet;
let total$: BehaviorSubject<Cardano.Value>;
let isSettled$: BehaviorSubject<boolean>;

beforeEach(() => {
total$ = new BehaviorSubject<Cardano.Value>({ coins: 1n });
isSettled$ = new BehaviorSubject<boolean>(true);
wallet = {
addresses$: of([{ address }]),
balance: { utxo: { total$ } },
syncStatus: { isSettled$ }
} as unknown as ObservableWallet;
});

it('uses 1 lovelace as minimum required balance', async () => {
const [_, { coins }] = await walletReady(wallet);

expect(coins).toEqual(1n);
});

it('returns the balance when it is equal to the required minimum', async () => {
const coins = 1_000_000n;
total$.next({ coins });
const [_, balance] = await walletReady(wallet, coins);
expect(balance.coins).toEqual(coins);
});

it('returns the balance when it is greater than the required minimum', async () => {
const coins = 1_000_001n;
total$.next({ coins });
const [_, balance] = await walletReady(wallet, coins - 1n);

expect(balance.coins).toEqual(coins);
});

it('fails with timeout if wallet is not ready', async () => {
const minCoinBalance = 0n;
isSettled$.next(false);
await expect(walletReady(wallet, minCoinBalance, 0)).rejects.toThrow(timeoutErrStr);
});

it('fails with timeout if wallet balance never resolves', async () => {
const minCoinBalance = 0n;
wallet = {
addresses$: of([{ address }]),
balance: { utxo: { total$: NEVER } },
syncStatus: { isSettled$ }
} as unknown as ObservableWallet;
await expect(walletReady(wallet, minCoinBalance, 0)).rejects.toThrow(timeoutErrStr);
});

it('fails with timeout if wallet address never resolves', async () => {
const minCoinBalance = 0n;
wallet = {
addresses$: NEVER,
balance: { utxo: { total$ } },
syncStatus: { isSettled$ }
} as unknown as ObservableWallet;
await expect(walletReady(wallet, minCoinBalance, 0)).rejects.toThrow(timeoutErrStr);
});

it('fails with insufficient funds if balance minimum not met', async () => {
const coins = 10n;
const minCoinBalance = coins + 1n;
total$.next({ coins });
await expect(walletReady(wallet, minCoinBalance)).rejects.toThrow(
insufficientFundsMessage(address, minCoinBalance, coins)
);
});
});
});
56 changes: 34 additions & 22 deletions packages/e2e/test/util.ts
@@ -1,16 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as Crypto from '@cardano-sdk/crypto';
import * as envalid from 'envalid';
import {
BALANCE_TIMEOUT_DEFAULT,
FAST_OPERATION_TIMEOUT_DEFAULT,
SYNC_TIMEOUT_DEFAULT,
TestWallet,
faucetProviderFactory,
getEnv,
networkInfoProviderFactory,
walletVariables
} from '../src';
import { Cardano, createSlotEpochCalc } from '@cardano-sdk/core';
import {
EMPTY,
Expand All @@ -28,6 +18,15 @@ import {
throwError,
timeout
} from 'rxjs';
import {
FAST_OPERATION_TIMEOUT_DEFAULT,
SYNC_TIMEOUT_DEFAULT,
TestWallet,
faucetProviderFactory,
getEnv,
networkInfoProviderFactory,
walletVariables
} from '../src';
import {
FinalizeTxProps,
InitializeTxProps,
Expand Down Expand Up @@ -62,22 +61,35 @@ export const waitForWalletStateSettle = (wallet: ObservableWallet, syncTimeout:
syncTimeout
);

export const waitForWalletBalance = (wallet: ObservableWallet) =>
firstValueFromTimed(
wallet.balance.utxo.total$.pipe(filter(({ coins }) => coins > 0)),
'Took too long to load balance',
BALANCE_TIMEOUT_DEFAULT
);
export const insufficientFundsMessage = (
address: Cardano.PaymentAddress,
min: bigint,
actual: bigint
) => `Insufficient funds at ${address}. Expected ${min}, found ${actual} lovelace.
Please use a faucet to fund the address or another address with sufficient funds`;

export const walletReady = (wallet: ObservableWallet) =>
firstValueFromTimed(
combineLatest([wallet.syncStatus.isSettled$, wallet.balance.utxo.total$]).pipe(
filter(([isSettled, balance]) => isSettled && balance.coins > 0n)
),
export const walletReady = async (
wallet: ObservableWallet,
minCoinBalance = 1n,
syncTimeout = SYNC_TIMEOUT_DEFAULT
): Promise<[boolean, Cardano.Value]> => {
const [isSettled, balance, address] = await firstValueFromTimed(
combineLatest([
wallet.syncStatus.isSettled$,
wallet.balance.utxo.total$,
wallet.addresses$.pipe(map((addresses) => addresses[0].address))
]).pipe(filter(([settled]) => settled)),
'Took too long to be ready',
SYNC_TIMEOUT_DEFAULT
syncTimeout
);

if (balance.coins < minCoinBalance) {
throw new Error(insufficientFundsMessage(address, minCoinBalance, balance.coins));
}

return [isSettled, balance];
};

const sortTxIn = (txInCollection: Cardano.TxIn[] | undefined): Cardano.TxIn[] =>
sortBy(txInCollection, ['txId', 'index']);

Expand Down

0 comments on commit cdfdc15

Please sign in to comment.