diff --git a/packages/coin-support-utils/package.json b/packages/coin-support-utils/package.json index ef33dbe45..0aebad0e2 100644 --- a/packages/coin-support-utils/package.json +++ b/packages/coin-support-utils/package.json @@ -9,13 +9,14 @@ "scripts": { "lint": "eslint --ext .ts,tsx,js,jsx src/ --fix", "lint:check": "eslint --ext .ts,tsx,js,jsx src/", - "pretty": "prettier --write \"src/**/*.ts?(x)\"", - "pretty:check": "prettier --check \"src/**/*.ts?(x)\"", + "pretty": "prettier --write \"src/**/*.ts?(x)\" \"tests/**/*.ts?(x)\"", + "pretty:check": "prettier --check \"src/**/*.ts?(x)\" \"tests/**/*.ts?(x)\"", "build": "rimraf dist && pnpm build:esm && pnpm build:cjs", "build:cjs": "tsc -p tsconfig_cjs.json", "build:esm": "tsc -p tsconfig.json", "build:dirty": "pnpm build:esm", - "pre-commit": "lint-staged" + "pre-commit": "lint-staged", + "test": "jest" }, "devDependencies": { "@cypherock/eslint-config": "workspace:^", diff --git a/packages/coin-support-utils/src/createAccount/createAccount.ts b/packages/coin-support-utils/src/createAccount/createAccount.ts index b627545fe..2947119a2 100644 --- a/packages/coin-support-utils/src/createAccount/createAccount.ts +++ b/packages/coin-support-utils/src/createAccount/createAccount.ts @@ -77,16 +77,15 @@ export function makeCreateAccountsObservable< assetId: params.coinId, }); - const derivationPathsPerScheme = await generateDerivationPathsPerScheme( - { - ...params, - limit: params.derivationPathLimit, - existingAccounts, - }, - ); - if (finished) return; + const derivationPathsPerScheme = generateDerivationPathsPerScheme({ + ...params, + limit: params.derivationPathLimit, + existingAccounts, + }); app = await params.createApp(params.connection); + if (finished) return; + const addressesPerScheme = await generateAddressesPerScheme({ ...params, app, diff --git a/packages/coin-support-utils/src/createAccount/generateAddresses.ts b/packages/coin-support-utils/src/createAccount/generateAddresses.ts index cd30ed054..44ab2566b 100644 --- a/packages/coin-support-utils/src/createAccount/generateAddresses.ts +++ b/packages/coin-support-utils/src/createAccount/generateAddresses.ts @@ -30,7 +30,7 @@ export interface IGenerateAddressesPerSchemeParams export async function generateAddressesPerScheme( params: IGenerateAddressesPerSchemeParams, ) { - const { derivationPathsPerScheme } = params; + const { derivationPathsPerScheme, getAddressesFromDevice } = params; const allDerivationPaths = Object.values(derivationPathsPerScheme).reduce( (a, b) => [...a, ...b], [], @@ -40,7 +40,7 @@ export async function generateAddressesPerScheme( index: startIndex, })); - const addresses = await params.getAddressesFromDevice({ + const addresses = await getAddressesFromDevice({ ...params, derivationPaths: mappedDerivationPaths, }); diff --git a/packages/coin-support-utils/src/createAccount/schemes.ts b/packages/coin-support-utils/src/createAccount/schemes.ts index 2d077ed6d..53dadbcae 100644 --- a/packages/coin-support-utils/src/createAccount/schemes.ts +++ b/packages/coin-support-utils/src/createAccount/schemes.ts @@ -1,5 +1,4 @@ import { - ICreateAccountParams, IDerivationPathGenerator, IDerivationScheme, } from '@cypherock/coin-support-interfaces'; @@ -31,25 +30,25 @@ export const createDerivationPathGenerator = return derivationPaths; }; -export interface IGenerateDerivationPathsPerSchemeParams - extends ICreateAccountParams { +export interface IGenerateDerivationPathsPerSchemeParams { derivationPathSchemes: Record; limit: number; existingAccounts: IAccount[]; } -export const generateDerivationPathsPerScheme = async ( +export const generateDerivationPathsPerScheme = ( params: IGenerateDerivationPathsPerSchemeParams, ) => { const { derivationPathSchemes, limit, existingAccounts } = params; const existingDerivationPaths = existingAccounts.map(e => e.derivationPath); - const derivationSchemeNames = Object.keys(derivationPathSchemes).filter( - n => !!derivationPathSchemes[n], - ); + const derivationSchemes = Object.entries(derivationPathSchemes).filter( + ([, value]) => value !== undefined, + ) as [string, IDerivationScheme][]; + const pathLimitPerDerivationScheme = Math.floor( - limit / derivationSchemeNames.length, + limit / derivationSchemes.length, ); const derivedPathsPerScheme: Record< @@ -58,10 +57,7 @@ export const generateDerivationPathsPerScheme = async ( > = {}; const derivedPaths: string[] = []; - for (const schemeName of derivationSchemeNames) { - const derivationPathSchemeDetails = derivationPathSchemes[schemeName]; - if (!derivationPathSchemeDetails) continue; - + for (const [schemeName, derivationPathSchemeDetails] of derivationSchemes) { const paths = derivationPathSchemeDetails.generator( // This is done because there can be overlapping derivation paths // between different schemes diff --git a/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/index.ts b/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/index.ts new file mode 100644 index 000000000..9dab336a9 --- /dev/null +++ b/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/index.ts @@ -0,0 +1,6 @@ +import { IFixtures } from './types'; +import { valid } from './valid'; + +export const fixtures: IFixtures = { + valid, +}; diff --git a/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/types.ts b/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/types.ts new file mode 100644 index 000000000..05274fc89 --- /dev/null +++ b/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/types.ts @@ -0,0 +1,9 @@ +export interface MapDerivationPathTestCases { + name: string; + input: string; + output: number[]; +} + +export interface IFixtures { + valid: MapDerivationPathTestCases[]; +} diff --git a/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/valid.ts b/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/valid.ts new file mode 100644 index 000000000..37b9135b1 --- /dev/null +++ b/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/__fixtures__/valid.ts @@ -0,0 +1,19 @@ +import { MapDerivationPathTestCases } from './types'; + +export const valid: MapDerivationPathTestCases[] = [ + { + name: 'should map derivation path with single index', + input: "m/44'", + output: [2147483692], + }, + { + name: 'should map derivation path with multiple index', + input: "m/44'/0'/0'/0/0", + output: [2147483692, 2147483648, 2147483648, 0, 0], + }, + { + name: 'should map derivation path with multiple index without m prefix', + input: "44'/0'/0'/0/0", + output: [2147483692, 2147483648, 2147483648, 0, 0], + }, +]; diff --git a/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/index.ts b/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/index.ts new file mode 100644 index 000000000..9113bed7e --- /dev/null +++ b/packages/coin-support-utils/tests/01.common/01.mapDerivationPath/index.ts @@ -0,0 +1,12 @@ +import { describe, test } from '@jest/globals'; +import { fixtures } from './__fixtures__'; +import { mapDerivationPath } from '../../../src'; + +describe('mapDerivationPath', () => { + fixtures.valid.forEach(({ name, input, output }) => { + test(name, () => { + const result = mapDerivationPath(input); + expect(result).toStrictEqual(output); + }); + }); +}); diff --git a/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/index.ts b/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/index.ts new file mode 100644 index 000000000..9dab336a9 --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/index.ts @@ -0,0 +1,6 @@ +import { IFixtures } from './types'; +import { valid } from './valid'; + +export const fixtures: IFixtures = { + valid, +}; diff --git a/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/types.ts b/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/types.ts new file mode 100644 index 000000000..c48db1d6b --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/types.ts @@ -0,0 +1,16 @@ +export interface DerivationPathGeneratorTestCases { + name: string; + input: { + basePath: string; + existingDerivationPaths: string[]; + limit: number; + }; + output: { + derivationPath: string; + index: number; + }[]; +} + +export interface IFixtures { + valid: DerivationPathGeneratorTestCases[]; +} diff --git a/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/valid.ts b/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/valid.ts new file mode 100644 index 000000000..fca6041b1 --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/__fixtures__/valid.ts @@ -0,0 +1,67 @@ +import { DerivationPathGeneratorTestCases } from './types'; + +export const valid: DerivationPathGeneratorTestCases[] = [ + { + name: 'should generate derivation path from zero index', + input: { + basePath: "m/44'/0'/0'/0/i", + existingDerivationPaths: [], + limit: 3, + }, + output: [ + { + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + { + derivationPath: "m/44'/0'/0'/0/1", + index: 1, + }, + { + derivationPath: "m/44'/0'/0'/0/2", + index: 2, + }, + ], + }, + { + name: 'should generate derivation path excluding existing derivation path', + input: { + basePath: "m/44'/0'/0'/0/i", + existingDerivationPaths: ["m/44'/0'/0'/0/1"], + limit: 2, + }, + output: [ + { + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + { + derivationPath: "m/44'/0'/0'/0/2", + index: 2, + }, + ], + }, + { + name: 'should return basePath if it does not include i', + input: { + basePath: "m/44'/0'/0'/0/0", + existingDerivationPaths: [], + limit: 2, + }, + output: [ + { + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + ], + }, + { + name: 'should return return empty array if base path is alreadty derived and it does not include i', + input: { + basePath: "m/44'/0'/0'/0/0", + existingDerivationPaths: ["m/44'/0'/0'/0/0"], + limit: 2, + }, + output: [], + }, +]; diff --git a/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/index.ts b/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/index.ts new file mode 100644 index 000000000..4879c352d --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/01.createDerivationPathGenerator/index.ts @@ -0,0 +1,14 @@ +import { describe, test } from '@jest/globals'; +import { createDerivationPathGenerator } from '../../../src/createAccount'; +import { fixtures } from './__fixtures__'; + +describe('01. Create Derivation Path Generator', () => { + fixtures.valid.forEach(({ name, input, output }) => { + test(name, () => { + const generator = createDerivationPathGenerator(input.basePath); + const result = generator(input.existingDerivationPaths, input.limit); + expect(generator).toBeInstanceOf(Function); + expect(result).toStrictEqual(output); + }); + }); +}); diff --git a/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/index.ts b/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/index.ts new file mode 100644 index 000000000..9dab336a9 --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/index.ts @@ -0,0 +1,6 @@ +import { IFixtures } from './types'; +import { valid } from './valid'; + +export const fixtures: IFixtures = { + valid, +}; diff --git a/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/types.ts b/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/types.ts new file mode 100644 index 000000000..d328ea78f --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/types.ts @@ -0,0 +1,17 @@ +import { IGenerateDerivationPathsPerSchemeParams } from '../../../../src/createAccount'; + +export interface DerivationPathPerSchemeGeneratorTestCases { + name: string; + input: IGenerateDerivationPathsPerSchemeParams; + output: Record< + string, + { + derivationPath: string; + index: number; + }[] + >; +} + +export interface IFixtures { + valid: DerivationPathPerSchemeGeneratorTestCases[]; +} diff --git a/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/valid.ts b/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/valid.ts new file mode 100644 index 000000000..c8a3ca1cf --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/__fixtures__/valid.ts @@ -0,0 +1,139 @@ +import { createDerivationPathGenerator } from '../../../../src/createAccount'; +import { accounts } from '../../../__fixtures__'; +import { DerivationPathPerSchemeGeneratorTestCases } from './types'; + +export const valid: DerivationPathPerSchemeGeneratorTestCases[] = [ + { + name: 'should generate derivation paths from index zero', + input: { + derivationPathSchemes: { + legacy: { + threshold: 2, + newAccountLimit: 0, // this is getting ignored + name: 'legacy', + generator: createDerivationPathGenerator("m/44'/0'/0'/0/i"), + }, + }, + limit: 4, + existingAccounts: [], + }, + output: { + legacy: [ + { + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + { + derivationPath: "m/44'/0'/0'/0/1", + index: 1, + }, + { + derivationPath: "m/44'/0'/0'/0/2", + index: 2, + }, + { + derivationPath: "m/44'/0'/0'/0/3", + index: 3, + }, + ], + }, + }, + { + name: 'should ignore undefined derivation path scheme', + input: { + derivationPathSchemes: { + legacy: undefined, + }, + limit: 4, + existingAccounts: [], + }, + output: {}, + }, + { + name: 'should generate derivation paths from index zero with multiple schemes', + input: { + derivationPathSchemes: { + legacy: { + name: 'legacy', + generator: createDerivationPathGenerator("m/44'/0'/i'"), + threshold: 2, + newAccountLimit: 0, // this is getting ignored + }, + segwit: { + name: 'segwit', + generator: createDerivationPathGenerator(`m/49'/0'/i'`), + threshold: 2, + newAccountLimit: 0, // this is getting ignored + }, + }, + limit: 4, + existingAccounts: [], + }, + output: { + legacy: [ + { + derivationPath: "m/44'/0'/0'", + index: 0, + }, + { + derivationPath: "m/44'/0'/1'", + index: 1, + }, + ], + segwit: [ + { + derivationPath: "m/49'/0'/0'", + index: 0, + }, + { + derivationPath: "m/49'/0'/1'", + index: 1, + }, + ], + }, + }, + { + name: 'should generate derivation paths from index zero with multiple schemes ignoring existing accounts', + input: { + derivationPathSchemes: { + legacy: { + name: 'legacy', + generator: createDerivationPathGenerator("m/44'/0'/i'"), + threshold: 2, + newAccountLimit: 0, // this is getting ignored + }, + segwit: { + name: 'segwit', + generator: createDerivationPathGenerator(`m/49'/0'/i'`), + threshold: 2, + newAccountLimit: 0, // this is getting ignored + }, + somethingRandom: undefined, + }, + limit: 4, + existingAccounts: accounts, + }, + output: { + legacy: [ + { + derivationPath: "m/44'/0'/1'", + index: 1, + }, + { + derivationPath: "m/44'/0'/2'", + index: 2, + }, + ], + segwit: [ + { + derivationPath: "m/49'/0'/0'", + index: 0, + }, + { + derivationPath: "m/49'/0'/1'", + index: 1, + }, + ], + }, + }, +]; diff --git a/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/index.ts b/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/index.ts new file mode 100644 index 000000000..35c2182bd --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/02.generateDerivationPathPerScheme/index.ts @@ -0,0 +1,12 @@ +import { describe, test } from '@jest/globals'; +import { fixtures } from './__fixtures__'; +import { generateDerivationPathsPerScheme } from '../../../src'; + +describe('02. Generate Derivation Path Per Scheme', () => { + fixtures.valid.forEach(({ name, input, output }) => { + test(name, () => { + const result = generateDerivationPathsPerScheme(input); + expect(result).toStrictEqual(output); + }); + }); +}); diff --git a/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/index.ts b/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/index.ts new file mode 100644 index 000000000..9dab336a9 --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/index.ts @@ -0,0 +1,6 @@ +import { IFixtures } from './types'; +import { valid } from './valid'; + +export const fixtures: IFixtures = { + valid, +}; diff --git a/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/types.ts b/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/types.ts new file mode 100644 index 000000000..0facb59a4 --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/types.ts @@ -0,0 +1,15 @@ +import { IAccount } from '@cypherock/db-interfaces'; + +export interface AccountNameGeneratorTestCases { + name: string; + input: { + coinName: string; + existingAccounts: IAccount[]; + schemeNames: string[]; + }; + output: string[]; +} + +export interface IFixtures { + valid: AccountNameGeneratorTestCases[]; +} diff --git a/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/valid.ts b/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/valid.ts new file mode 100644 index 000000000..d3054b1f3 --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/__fixtures__/valid.ts @@ -0,0 +1,32 @@ +import { accounts } from '../../../__fixtures__'; +import { AccountNameGeneratorTestCases } from './types'; + +export const valid: AccountNameGeneratorTestCases[] = [ + { + name: 'should generate derivation paths from index zero for same scheme', + input: { + coinName: 'Bitcoin', + schemeNames: ['legacy', 'legacy'], + existingAccounts: [], + }, + output: ['Bitcoin 1', 'Bitcoin 2'], + }, + { + name: 'should generate derivation paths from index zero for different schemes', + input: { + coinName: 'Bitcoin', + schemeNames: ['legacy', 'taproot', 'nativeSegwit', 'nativeSegwit'], + existingAccounts: [], + }, + output: ['Bitcoin 1', 'Bitcoin 1', 'Bitcoin 1', 'Bitcoin 2'], + }, + { + name: 'should generate derivation paths filtering existing accounts for different schemes', + input: { + coinName: 'Bitcoin', + schemeNames: ['legacy', 'something random'], + existingAccounts: accounts, + }, + output: ['Bitcoin 2', 'Bitcoin 1'], + }, +]; diff --git a/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/index.ts b/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/index.ts new file mode 100644 index 000000000..01cf313f7 --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/03.createAccountNameGenerator/index.ts @@ -0,0 +1,15 @@ +import { describe, test } from '@jest/globals'; +import { createAccountNameGenerator } from '../../../src'; +import { fixtures } from './__fixtures__'; + +describe('createAccountNameGenerator', () => { + fixtures.valid.forEach(({ name, input, output }) => { + test(name, () => { + const generator = createAccountNameGenerator(input.coinName); + input.schemeNames.forEach((schemeName, i) => { + const result = generator(schemeName, input.existingAccounts); + expect(result).toBe(output[i]); + }); + }); + }); +}); diff --git a/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/index.ts b/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/index.ts new file mode 100644 index 000000000..9dab336a9 --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/index.ts @@ -0,0 +1,6 @@ +import { IFixtures } from './types'; +import { valid } from './valid'; + +export const fixtures: IFixtures = { + valid, +}; diff --git a/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/types.ts b/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/types.ts new file mode 100644 index 000000000..b81e8876e --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/types.ts @@ -0,0 +1,24 @@ +export interface AddressesPerSchemeGeneratorTestCases { + name: string; + input: { + derivationPathsPerScheme: Record< + string, + { + derivationPath: string; + index: number; + }[] + >; + }; + output: Record< + string, + { + address: string; + derivationPath: string; + index: number; + }[] + >; +} + +export interface IFixtures { + valid: AddressesPerSchemeGeneratorTestCases[]; +} diff --git a/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/valid.ts b/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/valid.ts new file mode 100644 index 000000000..8a70c340f --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/__fixtures__/valid.ts @@ -0,0 +1,178 @@ +import { mapDerivationPath } from '../../../../src'; +import { getAddressFromPath } from '../../../__fixtures__/derivationPathAddressMap'; +import { AddressesPerSchemeGeneratorTestCases } from './types'; + +export const valid: AddressesPerSchemeGeneratorTestCases[] = [ + { + name: 'should generate addresses with one scheme, derivation path and one index', + input: { + derivationPathsPerScheme: { + legacy: [ + { + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + ], + }, + }, + output: { + legacy: [ + { + address: getAddressFromPath(mapDerivationPath("m/44'/0'/0'/0/0"))!, + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + ], + }, + }, + { + name: 'should generate addresses per scheme with one derivation path and one index', + input: { + derivationPathsPerScheme: { + legacy: [ + { + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + ], + nativeSegwit: [ + { + derivationPath: "m/84'/0'/0'/0/0", + index: 0, + }, + ], + segwit: [ + { + derivationPath: "m/49'/0'/0'/0/0", + index: 0, + }, + ], + }, + }, + output: { + legacy: [ + { + address: getAddressFromPath(mapDerivationPath("m/44'/0'/0'/0/0"))!, + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + ], + nativeSegwit: [ + { + address: getAddressFromPath(mapDerivationPath("m/84'/0'/0'/0/0"))!, + derivationPath: "m/84'/0'/0'/0/0", + index: 0, + }, + ], + segwit: [ + { + address: getAddressFromPath(mapDerivationPath("m/49'/0'/0'/0/0"))!, + derivationPath: "m/49'/0'/0'/0/0", + index: 0, + }, + ], + }, + }, + { + name: 'should generate addresses per scheme with multiple derivation paths and indexes', + input: { + derivationPathsPerScheme: { + legacy: [ + { + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + { + derivationPath: "m/44'/0'/0'/0/1", + index: 1, + }, + { + derivationPath: "m/44'/0'/0'/0/2", + index: 2, + }, + ], + nativeSegwit: [ + { + derivationPath: "m/84'/0'/0'/0/0", + index: 0, + }, + { + derivationPath: "m/84'/0'/0'/0/1", + index: 1, + }, + { + derivationPath: "m/84'/0'/0'/0/2", + index: 2, + }, + ], + segwit: [ + { + derivationPath: "m/49'/0'/0'/0/0", + index: 0, + }, + { + derivationPath: "m/49'/0'/0'/0/1", + index: 1, + }, + { + derivationPath: "m/49'/0'/0'/0/2", + index: 2, + }, + ], + }, + }, + output: { + legacy: [ + { + address: getAddressFromPath(mapDerivationPath("m/44'/0'/0'/0/0"))!, + derivationPath: "m/44'/0'/0'/0/0", + index: 0, + }, + { + address: getAddressFromPath(mapDerivationPath("m/44'/0'/0'/0/1"))!, + derivationPath: "m/44'/0'/0'/0/1", + index: 1, + }, + { + address: getAddressFromPath(mapDerivationPath("m/44'/0'/0'/0/2"))!, + derivationPath: "m/44'/0'/0'/0/2", + index: 2, + }, + ], + nativeSegwit: [ + { + address: getAddressFromPath(mapDerivationPath("m/84'/0'/0'/0/0"))!, + derivationPath: "m/84'/0'/0'/0/0", + index: 0, + }, + { + address: getAddressFromPath(mapDerivationPath("m/84'/0'/0'/0/1"))!, + derivationPath: "m/84'/0'/0'/0/1", + index: 1, + }, + { + address: getAddressFromPath(mapDerivationPath("m/84'/0'/0'/0/2"))!, + derivationPath: "m/84'/0'/0'/0/2", + index: 2, + }, + ], + segwit: [ + { + address: getAddressFromPath(mapDerivationPath("m/49'/0'/0'/0/0"))!, + derivationPath: "m/49'/0'/0'/0/0", + index: 0, + }, + { + address: getAddressFromPath(mapDerivationPath("m/49'/0'/0'/0/1"))!, + derivationPath: "m/49'/0'/0'/0/1", + index: 1, + }, + { + address: getAddressFromPath(mapDerivationPath("m/49'/0'/0'/0/2"))!, + derivationPath: "m/49'/0'/0'/0/2", + index: 2, + }, + ], + }, + }, +]; diff --git a/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/index.ts b/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/index.ts new file mode 100644 index 000000000..c4a30743e --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/04.generateAddressesPerScheme/index.ts @@ -0,0 +1,21 @@ +import { describe, test } from '@jest/globals'; + +import { fixtures } from './__fixtures__'; +import { TestApp, getAddressesFromDeviceMock } from '../../__mocks__'; +import { + IGenerateAddressesPerSchemeParams, + generateAddressesPerScheme, +} from '../../../src'; + +describe('04. Generate Addresses Per Scheme', () => { + fixtures.valid.forEach(({ name, input, output }) => { + test(name, async () => { + const params: IGenerateAddressesPerSchemeParams = { + derivationPathsPerScheme: input.derivationPathsPerScheme, + getAddressesFromDevice: getAddressesFromDeviceMock, + } as any; + const result = await generateAddressesPerScheme(params); + expect(result).toStrictEqual(output); + }); + }); +}); diff --git a/packages/coin-support-utils/tests/02.createAccount/05.makeCreateAccountsObservable/index.ts b/packages/coin-support-utils/tests/02.createAccount/05.makeCreateAccountsObservable/index.ts new file mode 100644 index 000000000..5b99d27ba --- /dev/null +++ b/packages/coin-support-utils/tests/02.createAccount/05.makeCreateAccountsObservable/index.ts @@ -0,0 +1,210 @@ +import { ICreateAccountEvent } from '@cypherock/coin-support-interfaces'; +import { describe, test } from '@jest/globals'; +import { Observer, Subscription } from 'rxjs'; +import { makeCreateAccountsObservable } from '../../../src'; +import { + createAccountParams, + createAppMock, + db, + getAddressesFromDeviceImplementation, + getAddressesFromDeviceMock, + testApp, +} from '../../__mocks__'; + +/** + * @todo Increase branch coverage + */ +describe('makeCreateAccountsObservable', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + + beforeEach(() => { + jest.restoreAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + jest.resetAllMocks(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + test('should create new accounts', done => { + db.account.getAll.mockResolvedValue([]); + + const waitInMSBetweenEachAccountAPI: number | undefined = 250; + + const params = createAccountParams(db, waitInMSBetweenEachAccountAPI); + const createAccountsObservable = makeCreateAccountsObservable(params); + + const derivationSchemeCount = Object.values( + params.derivationPathSchemes, + ).filter(Boolean).length; + + const totalNewAccountLimit = Object.values( + params.derivationPathSchemes, + ).reduce((acc, scheme) => acc + (scheme?.newAccountLimit ?? 0), 0); + + const newAccountsCount = Math.min( + totalNewAccountLimit, + Math.floor(params.derivationPathLimit / derivationSchemeCount) * + derivationSchemeCount, + ); + + expect.assertions(newAccountsCount + 5); + + const observer: Observer = { + next: value => { + if (value.type === 'Account') { + expect(value.account).toBeDefined(); + } + }, + complete: () => { + expect(db.account.getAll).toHaveBeenCalledTimes(1); + expect(params.createApp).toHaveBeenCalledTimes(1); + expect(params.getAddressesFromDevice).toHaveBeenCalledTimes(1); + expect(params.getBalanceAndTxnCount).toHaveBeenCalledTimes( + newAccountsCount, + ); + expect(params.createAccountFromAddress).toHaveBeenCalledTimes( + newAccountsCount, + ); + done(); + }, + error: done, + }; + + createAccountsObservable.subscribe(observer); + + for (let i = 0; i < newAccountsCount; i += 1) { + jest.advanceTimersByTimeAsync(waitInMSBetweenEachAccountAPI ?? 500); + } + }); + + test('should unsubscribe after creating app', done => { + let subscription: Subscription | undefined; + db.account.getAll.mockResolvedValue([]); + + const waitInMSBetweenEachAccountAPI: number | undefined = 250; + + const params = createAccountParams(db, waitInMSBetweenEachAccountAPI); + + createAppMock.mockImplementation(async () => { + subscription?.unsubscribe(); + return testApp; + }); + + const createAccountsObservable = makeCreateAccountsObservable(params); + + const derivationSchemeCount = Object.values( + params.derivationPathSchemes, + ).filter(Boolean).length; + + const totalNewAccountLimit = Object.values( + params.derivationPathSchemes, + ).reduce((acc, scheme) => acc + (scheme?.newAccountLimit ?? 0), 0); + + const newAccountsCount = Math.min( + totalNewAccountLimit, + Math.floor(params.derivationPathLimit / derivationSchemeCount) * + derivationSchemeCount, + ); + + expect.assertions(5); + + const observer: Observer = { + next: () => { + // shouldn't reach here + expect(true).toBe(false); + }, + complete: () => { + // shouldn't reach here + expect(true).toBe(false); + }, + error: () => { + // shouldn't reach here + expect(true).toBe(false); + }, + }; + + subscription = createAccountsObservable.subscribe(observer); + + setTimeout(() => { + expect(db.account.getAll).toHaveBeenCalledTimes(1); + expect(params.createApp).toHaveBeenCalledTimes(1); + expect(params.getAddressesFromDevice).toHaveBeenCalledTimes(0); + expect(params.getBalanceAndTxnCount).toHaveBeenCalledTimes(0); + expect(params.createAccountFromAddress).toHaveBeenCalledTimes(0); + done(); + }, 0); + + for (let i = 0; i < newAccountsCount; i += 1) { + jest.advanceTimersByTimeAsync(waitInMSBetweenEachAccountAPI ?? 500); + } + }); + + test('should unsubscribe after generating address from device', done => { + let subscription: Subscription | undefined; + db.account.getAll.mockResolvedValue([]); + + const waitInMSBetweenEachAccountAPI: number | undefined = 250; + + const params = createAccountParams(db, waitInMSBetweenEachAccountAPI); + + getAddressesFromDeviceMock.mockImplementation(async (...args) => { + subscription?.unsubscribe(); + return getAddressesFromDeviceImplementation(...args); + }); + + const createAccountsObservable = makeCreateAccountsObservable(params); + + const derivationSchemeCount = Object.values( + params.derivationPathSchemes, + ).filter(Boolean).length; + + const totalNewAccountLimit = Object.values( + params.derivationPathSchemes, + ).reduce((acc, scheme) => acc + (scheme?.newAccountLimit ?? 0), 0); + + const newAccountsCount = Math.min( + totalNewAccountLimit, + Math.floor(params.derivationPathLimit / derivationSchemeCount) * + derivationSchemeCount, + ); + + expect.assertions(5); + + const observer: Observer = { + next: () => { + // shouldn't reach here + expect(true).toBe(false); + }, + complete: () => { + // shouldn't reach here + expect(true).toBe(false); + }, + error: () => { + // shouldn't reach here + expect(true).toBe(false); + }, + }; + + subscription = createAccountsObservable.subscribe(observer); + + setTimeout(() => { + expect(db.account.getAll).toHaveBeenCalledTimes(1); + expect(params.createApp).toHaveBeenCalledTimes(1); + expect(params.getAddressesFromDevice).toHaveBeenCalledTimes(1); + expect(params.getBalanceAndTxnCount).toHaveBeenCalledTimes(0); + expect(params.createAccountFromAddress).toHaveBeenCalledTimes(0); + done(); + }, 0); + + for (let i = 0; i < newAccountsCount; i += 1) { + jest.advanceTimersByTimeAsync(waitInMSBetweenEachAccountAPI ?? 500); + } + }); +}); diff --git a/packages/coin-support-utils/tests/03.db/01.account/01.getUniqueAccountQuery/index.ts b/packages/coin-support-utils/tests/03.db/01.account/01.getUniqueAccountQuery/index.ts new file mode 100644 index 000000000..12a11870b --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/01.getUniqueAccountQuery/index.ts @@ -0,0 +1,20 @@ +import { describe, test } from '@jest/globals'; +import { accounts } from '../../../__fixtures__'; +import { getUniqueAccountQuery } from '../../../../src'; + +describe('getUniqueAccountQuery', () => { + test('should return a query object with the unique fields of the account', () => { + accounts.forEach(account => { + const accountQuery = getUniqueAccountQuery(account); + expect(accountQuery).toBeTruthy(); + expect(accountQuery.walletId).toBe(account.walletId); + expect(accountQuery.assetId).toBe(account.assetId); + expect(accountQuery.familyId).toBe(account.familyId); + expect(accountQuery.parentAccountId).toBe(account.parentAccountId); + expect(accountQuery.parentAssetId).toBe(account.parentAssetId); + expect(accountQuery.type).toBe(account.type); + expect(accountQuery.derivationPath).toBe(account.derivationPath); + expect(accountQuery.derivationScheme).toBe(account.derivationScheme); + }); + }); +}); diff --git a/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/__fixtures__/index.ts b/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/__fixtures__/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/__fixtures__/type.ts b/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/__fixtures__/type.ts new file mode 100644 index 000000000..fe09bd20e --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/__fixtures__/type.ts @@ -0,0 +1,22 @@ +import { IAccount } from '@cypherock/db-interfaces'; + +type CoinType = any; + +export interface GetAccountAndCoinTestCase { + input: { + accountId: string; + db: { + account: IAccount[]; + }; + coinList: Record; + }; + output: { + account: IAccount; + parentAccount: IAccount; + coin: Exclude; + }; +} + +export interface IFixtures { + valid: GetAccountAndCoinTestCase[]; +} diff --git a/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/__fixtures__/valid.ts b/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/__fixtures__/valid.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/index.ts b/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/index.ts new file mode 100644 index 000000000..40a75afb4 --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/02.getAccountAndCoin/index.ts @@ -0,0 +1,5 @@ +import { describe } from '@jest/globals'; + +describe('getAccountAndCoin', () => { + // getAccountAndCoin +}); diff --git a/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/__fixtures__/index.ts b/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/__fixtures__/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/__fixtures__/type.ts b/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/__fixtures__/type.ts new file mode 100644 index 000000000..f540aad11 --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/__fixtures__/type.ts @@ -0,0 +1,3 @@ +export interface IFixtures { + valid: any; +} diff --git a/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/__fixtures__/valid.ts b/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/__fixtures__/valid.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/index.ts b/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/index.ts new file mode 100644 index 000000000..8fc7e8342 --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/03.insertAccountIfNotExists/index.ts @@ -0,0 +1,5 @@ +import { describe } from '@jest/globals'; + +describe('insertAccountIfNotExists', () => { + // insertAccountIfNotExists +}); diff --git a/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/__fixtures__/index.ts b/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/__fixtures__/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/__fixtures__/type.ts b/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/__fixtures__/type.ts new file mode 100644 index 000000000..f540aad11 --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/__fixtures__/type.ts @@ -0,0 +1,3 @@ +export interface IFixtures { + valid: any; +} diff --git a/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/__fixtures__/valid.ts b/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/__fixtures__/valid.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/index.ts b/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/index.ts new file mode 100644 index 000000000..ea27c031b --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/04.insertOrUpdateAccounts/index.ts @@ -0,0 +1,5 @@ +import { describe } from '@jest/globals'; + +describe('insertOrUpdateAccounts', () => { + // insertOrUpdateAccounts +}); diff --git a/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/__fixtures__/index.ts b/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/__fixtures__/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/__fixtures__/type.ts b/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/__fixtures__/type.ts new file mode 100644 index 000000000..f540aad11 --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/__fixtures__/type.ts @@ -0,0 +1,3 @@ +export interface IFixtures { + valid: any; +} diff --git a/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/__fixtures__/valid.ts b/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/__fixtures__/valid.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/index.ts b/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/index.ts new file mode 100644 index 000000000..5327f401f --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/05.updateAccount/index.ts @@ -0,0 +1,5 @@ +import { describe } from '@jest/globals'; + +describe('updateAccount', () => { + // updateAccount +}); diff --git a/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/__fixtures__/index.ts b/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/__fixtures__/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/__fixtures__/type.ts b/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/__fixtures__/type.ts new file mode 100644 index 000000000..f540aad11 --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/__fixtures__/type.ts @@ -0,0 +1,3 @@ +export interface IFixtures { + valid: any; +} diff --git a/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/__fixtures__/valid.ts b/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/__fixtures__/valid.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/index.ts b/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/index.ts new file mode 100644 index 000000000..ae2decf10 --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/06.hideAccount/index.ts @@ -0,0 +1,5 @@ +import { describe } from '@jest/globals'; + +describe('hideAccount', () => { + // hideAccount +}); diff --git a/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/__fixtures__/index.ts b/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/__fixtures__/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/__fixtures__/type.ts b/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/__fixtures__/type.ts new file mode 100644 index 000000000..f540aad11 --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/__fixtures__/type.ts @@ -0,0 +1,3 @@ +export interface IFixtures { + valid: any; +} diff --git a/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/__fixtures__/valid.ts b/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/__fixtures__/valid.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/index.ts b/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/index.ts new file mode 100644 index 000000000..eb3065f4e --- /dev/null +++ b/packages/coin-support-utils/tests/03.db/01.account/07.unhideOrInsertAccountIfNotExists/index.ts @@ -0,0 +1,5 @@ +import { describe } from '@jest/globals'; + +describe('unhideOrInsertAccountIfNotExists', () => { + // unhideOrInsertAccountIfNotExists +}); diff --git a/packages/coin-support-utils/tests/__fixtures__/derivationPathAddressMap.ts b/packages/coin-support-utils/tests/__fixtures__/derivationPathAddressMap.ts new file mode 100644 index 000000000..8001f126f --- /dev/null +++ b/packages/coin-support-utils/tests/__fixtures__/derivationPathAddressMap.ts @@ -0,0 +1,26 @@ +import { mapDerivationPath } from '../../src'; + +const derivationPathAddressMap = new Map(); + +const pathAddressList = [ + ["m/44'/0'/0'/0/0", 'address_44_0_0_0_0'], + ["m/44'/0'/0'/0/1", 'address_44_0_0_0_1'], + ["m/44'/0'/0'/0/2", 'address_44_0_0_0_2'], + ["m/84'/0'/0'/0/0", 'address_84_0_0_0_0'], + ["m/84'/0'/0'/0/1", 'address_84_0_0_0_1'], + ["m/84'/0'/0'/0/2", 'address_84_0_0_0_2'], + ["m/49'/0'/0'/0/0", 'address_49_0_0_0_0'], + ["m/49'/0'/0'/0/1", 'address_49_0_0_0_1'], + ["m/49'/0'/0'/0/2", 'address_49_0_0_0_2'], +]; + +const putPathAddressEntry = (path: string, address: string) => { + derivationPathAddressMap.set(mapDerivationPath(path).join('#'), address); +}; + +pathAddressList.forEach(([path, address]) => { + putPathAddressEntry(path, address); +}); + +export const getAddressFromPath = (path: number[]) => + derivationPathAddressMap.get(path.join('#')); diff --git a/packages/coin-support-utils/tests/__fixtures__/index.ts b/packages/coin-support-utils/tests/__fixtures__/index.ts new file mode 100644 index 000000000..72b6885ea --- /dev/null +++ b/packages/coin-support-utils/tests/__fixtures__/index.ts @@ -0,0 +1,2 @@ +export * from './derivationPathAddressMap'; +export * from './wallet'; diff --git a/packages/coin-support-utils/tests/__fixtures__/wallet.ts b/packages/coin-support-utils/tests/__fixtures__/wallet.ts new file mode 100644 index 000000000..d7d91028f --- /dev/null +++ b/packages/coin-support-utils/tests/__fixtures__/wallet.ts @@ -0,0 +1,67 @@ +import { IAccount } from '@cypherock/db-interfaces'; + +export const walletId = + 'c372af88f64e0a40439f97ee98a3a0a03e9b2ac348b464d0cab7f32ee8482298'; +export const assetId = { + bitcoin: 'bitcoin', +}; + +export const familyId = { + bitcoin: 'bitcoin', +}; + +export const accounts: IAccount[] = [ + { + name: 'Bitcoin 1', + xpubOrAddress: '', + balance: '0', + unit: 'BTC', + derivationPath: "m/84'/0'/0'", + type: 'account', + familyId: familyId.bitcoin, + assetId: assetId.bitcoin, + parentAssetId: assetId.bitcoin, + walletId, + derivationScheme: 'nativeSegwit', + __version: 0, + __id: 'da51cef8-aa25-4667-b2da-9b4ef78dcee2', + extraData: { + unconfirmedBalance: '0', + derivationScheme: 'nativeSegwit', + }, + }, + { + name: 'Bitcoin 1', + xpubOrAddress: '', + balance: '0', + unit: 'BTC', + derivationPath: "m/86'/0'/0'", + type: 'account', + familyId: familyId.bitcoin, + assetId: assetId.bitcoin, + parentAssetId: assetId.bitcoin, + walletId, + derivationScheme: 'taproot', + __version: 0, + __id: 'da51cef8-aa25-4667-b2da-9b4ef78dcee3', + }, + { + name: 'Bitcoin 1', + xpubOrAddress: '', + balance: '20000', + unit: 'BTC', + derivationPath: "m/44'/0'/0'", + type: 'account', + familyId: familyId.bitcoin, + assetId: assetId.bitcoin, + parentAssetId: assetId.bitcoin, + walletId, + derivationScheme: 'legacy', + __version: 0, + __id: '54d86310-66e1-4ca5-ae6a-d7b6cd671eba', + extraData: { + unconfirmedBalance: '0', + derivationScheme: 'legacy', + }, + }, +]; diff --git a/packages/coin-support-utils/tests/__mocks__/app.ts b/packages/coin-support-utils/tests/__mocks__/app.ts new file mode 100644 index 000000000..59869e6fe --- /dev/null +++ b/packages/coin-support-utils/tests/__mocks__/app.ts @@ -0,0 +1,9 @@ +import { jest } from '@jest/globals'; + +export interface TestApp { + abort: () => Promise; +} + +export const testApp: TestApp = { + abort: jest.fn(() => Promise.resolve()), +}; diff --git a/packages/coin-support-utils/tests/__mocks__/createAccountParams.ts b/packages/coin-support-utils/tests/__mocks__/createAccountParams.ts new file mode 100644 index 000000000..36d14d9ea --- /dev/null +++ b/packages/coin-support-utils/tests/__mocks__/createAccountParams.ts @@ -0,0 +1,43 @@ +import { IDatabase } from '@cypherock/db-interfaces'; +import { getAddressesFromDeviceMock } from '.'; +import { + IMakeCreateAccountsObservableParams, + createDerivationPathGenerator, +} from '../../src/createAccount'; +import { TestApp } from './app'; +import { createAppMock } from './createApp'; +import { getBalanceAndTxnCountMock } from './getBalanceAndTxnCount'; + +export const createAccountParams = ( + db: IDatabase, + waitInMSBetweenEachAccountAPI?: number, + thresholdPerScheme?: number, + newAccountLimitPerScheme?: number, + derivationPathLimit?: number, +): IMakeCreateAccountsObservableParams => ({ + createApp: createAppMock, + derivationPathSchemes: { + legacy: { + name: 'legacy', + generator: createDerivationPathGenerator("m/44'/0'/0'/0/i"), + threshold: thresholdPerScheme ?? 2, + newAccountLimit: newAccountLimitPerScheme ?? 2, + }, + nativeSegwit: undefined, + segwit: { + name: 'segwit', + generator: createDerivationPathGenerator(`m/49'/0'/0'/0/i`), + threshold: thresholdPerScheme ?? 2, + newAccountLimit: newAccountLimitPerScheme ?? 2, + }, + }, + derivationPathLimit: derivationPathLimit ?? 2, + getBalanceAndTxnCount: getBalanceAndTxnCountMock, + getAddressesFromDevice: getAddressesFromDeviceMock, + createAccountFromAddress: jest.fn().mockResolvedValue({}), + coinId: 'testCoin', + walletId: 'testWallet', + db, + connection: {} as any, + waitInMSBetweenEachAccountAPI, +}); diff --git a/packages/coin-support-utils/tests/__mocks__/createApp.ts b/packages/coin-support-utils/tests/__mocks__/createApp.ts new file mode 100644 index 000000000..407265d3b --- /dev/null +++ b/packages/coin-support-utils/tests/__mocks__/createApp.ts @@ -0,0 +1,7 @@ +import { jest } from '@jest/globals'; +import { IMakeCreateAccountsObservableParams } from '../../src'; +import { TestApp, testApp } from '.'; + +export const createAppMock = jest + .fn['createApp']>() + .mockResolvedValue(testApp); diff --git a/packages/coin-support-utils/tests/__mocks__/db.ts b/packages/coin-support-utils/tests/__mocks__/db.ts new file mode 100644 index 000000000..75c7701d1 --- /dev/null +++ b/packages/coin-support-utils/tests/__mocks__/db.ts @@ -0,0 +1,8 @@ +import { IDatabase } from '@cypherock/db-interfaces'; +import { jest } from '@jest/globals'; + +export const db: jest.MockedObject = { + account: { + getAll: jest.fn(), + }, +} as any; diff --git a/packages/coin-support-utils/tests/__mocks__/getAddressesFromDevice.ts b/packages/coin-support-utils/tests/__mocks__/getAddressesFromDevice.ts new file mode 100644 index 000000000..4a7a47de9 --- /dev/null +++ b/packages/coin-support-utils/tests/__mocks__/getAddressesFromDevice.ts @@ -0,0 +1,14 @@ +import { TestApp } from '.'; +import { IGetAddressesFromDeviceParams } from '../../src'; +import { getAddressFromPath } from '../__fixtures__/derivationPathAddressMap'; + +export const getAddressesFromDeviceImplementation = async ({ + derivationPaths, +}: IGetAddressesFromDeviceParams): Promise => + derivationPaths.map( + ({ derivationPath }) => getAddressFromPath(derivationPath)!, + ); + +export const getAddressesFromDeviceMock = jest.fn( + getAddressesFromDeviceImplementation, +); diff --git a/packages/coin-support-utils/tests/__mocks__/getBalanceAndTxnCount.ts b/packages/coin-support-utils/tests/__mocks__/getBalanceAndTxnCount.ts new file mode 100644 index 000000000..2fd6e61bc --- /dev/null +++ b/packages/coin-support-utils/tests/__mocks__/getBalanceAndTxnCount.ts @@ -0,0 +1,7 @@ +import { jest } from '@jest/globals'; +import { IMakeCreateAccountsObservableParams } from '../../src'; +import { TestApp } from '.'; + +export const getBalanceAndTxnCountMock = jest + .fn['getBalanceAndTxnCount']>() + .mockResolvedValue({ balance: '0', txnCount: 0 }); diff --git a/packages/coin-support-utils/tests/__mocks__/index.ts b/packages/coin-support-utils/tests/__mocks__/index.ts new file mode 100644 index 000000000..715530bc5 --- /dev/null +++ b/packages/coin-support-utils/tests/__mocks__/index.ts @@ -0,0 +1,7 @@ +export * from './app'; +export * from './observer'; +export * from './db'; +export * from './getAddressesFromDevice'; +export * from './getBalanceAndTxnCount'; +export * from './createApp'; +export * from './createAccountParams'; diff --git a/packages/coin-support-utils/tests/__mocks__/observer.ts b/packages/coin-support-utils/tests/__mocks__/observer.ts new file mode 100644 index 000000000..b1e62422d --- /dev/null +++ b/packages/coin-support-utils/tests/__mocks__/observer.ts @@ -0,0 +1,5 @@ +export const mockObserver = { + next: jest.fn(), + error: jest.fn(), + complete: jest.fn(), +};