Skip to content

Commit

Permalink
test(api): api package test + few adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
sogunshola committed Jul 20, 2022
1 parent 386263e commit 6eae2a9
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 12 deletions.
7 changes: 7 additions & 0 deletions .changeset/brave-bats-destroy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@moralisweb3/api': major
'@moralisweb3/evm-api': major
'@moralisweb3/integration': major
---

test on api utils and few optimisations
9 changes: 6 additions & 3 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
module.exports = {
// Map workspaces to their source code so that Jest can resolve them correctly.
moduleNameMapper: {
'^@moralisweb3/evm-connector-utils$': '<rootDir>/../evmConnectors/EvmConnectorUtils/src',
'^@moralisweb3/evm-metamask-connector$': '<rootDir>/../evmConnectors/EvmMetamaskConnector/src',
'^@moralisweb3/evm-walletconnect-connector$': '<rootDir>/../evmConnectors/EvmWalletconnectConnector/src',
'^@moralisweb3/evm-api': '<rootDir>/../evmApi/src',
'^@moralisweb3/api': '<rootDir>/../api/src',
'^@moralisweb3/core': '<rootDir>/../core/src',
'^@moralisweb3/(.*)$': '<rootDir>/../$1/src',
},
modulePaths: ['<rootDir>'],
preset: 'ts-jest/presets/default',
collectCoverageFrom: ['**/src/**/*.{js,ts,jsx,tsx}'],
coverageThreshold: null,
globals: {
'ts-jest': {
tsConfig: '<rootDir>/../../tsconfig.package.json',
},
},
};
4 changes: 4 additions & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"dist"
],
"scripts": {
"test": "yarn jest --runInBand --detectOpenHandles --forceExit",
"test:ci": "yarn test --ci",
"test:coverage": "yarn run test --coverage",
"test:watch": "yarn run test --watch",
"format": "prettier . \"**/*.+(js|ts|json)\" --write",
"lint": "eslint . --ext .js,.ts,.tsx,jsx",
"format:check": "prettier . \"**/*.+(js|ts|json)\" --check",
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/ApiResultAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ApiErrorCode, EvmAddress, EvmChain, MoralisApiError } from '@moralisweb
import { MoralisDataObject } from '@moralisweb3/core';

// TODO: make part of core config? The challenge in that case is to make sure it is Typed correctly
enum ApiFormatType {
export enum ApiFormatType {
// Return the data directly, as is provided by the API
RAW = 'raw',
// Return the formatted result of all moralis DataTypes
Expand Down
14 changes: 10 additions & 4 deletions packages/api/src/MoralisApi.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { ApiModule, MoralisCore } from '@moralisweb3/core';
import { Module, MoralisCore, MoralisCoreProvider } from '@moralisweb3/core';
import { ApiConfigSetup } from './config/ApiConfigSetup';

export class MoralisApi extends ApiModule {
public constructor(moduleName: string, core: MoralisCore, baseUrl: string) {
super(moduleName, core, baseUrl);
export class MoralisApi extends Module {
public static readonly moduleName = 'api';

public static create(core?: MoralisCore): MoralisApi {
return new MoralisApi(core ?? MoralisCoreProvider.getDefault());
}

public constructor(core: MoralisCore) {
super(MoralisApi.moduleName, core);
}

public setup() {
Expand Down
2 changes: 2 additions & 0 deletions packages/api/src/test/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const MOCK_API_KEY = 'test-api-key';
export const API_ROOT = 'https://deep-index.moralis.io/api/v2';
27 changes: 27 additions & 0 deletions packages/api/src/test/mockRequests/endpointWeights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable no-console */
import { rest } from 'msw';
import { API_ROOT, MOCK_API_KEY } from '../config';

export const mockEndpointWeightss = 'getBlock';

export const mockEndpointWeights = rest.get(`${API_ROOT}/info/endpointWeights`, (req, res, ctx) => {
const apiKey = req.headers.get('x-api-key');

if (apiKey !== MOCK_API_KEY) {
return res(ctx.status(401));
}

const value = mockEndpointWeightss;

if (!value) {
return res(ctx.status(404));
}

return res(
ctx.status(200),
ctx.json({
endpoint: value,
weight: '8',
}),
);
});
34 changes: 34 additions & 0 deletions packages/api/src/test/mockRequests/getContractEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { rest } from 'msw';
import { API_ROOT, MOCK_API_KEY } from '../config';

export const mockGetContractEvents = rest.post(`${API_ROOT}/:address/events`, (req, res, ctx) => {
const apiKey = req.headers.get('x-api-key');

if (apiKey !== MOCK_API_KEY) {
return res(ctx.status(401));
}

return res(
ctx.status(200),
ctx.json({
total: 10,
page: 0,
page_size: 2,
cursor: 'cursor_string',
result: [
{
transaction_hash: '0x2d30ca6f024dbc1307ac8a1a44ca27de6f797ec22ef20627a1307243b0ab7d09',
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
block_timestamp: '2021-04-02T10:07:54.000Z',
block_number: '12526958',
block_hash: '0x0372c302e3c52e8f2e15d155e2c545e6d802e479236564af052759253b20fd86',
data: {
from: '0x54ff6974c715956a5049a123408bff91fbe29f01',
to: '0x74de5d4fcbf63e00296fd95d33236b9794016631',
value: '260103496340000000000',
},
},
],
}),
);
});
91 changes: 91 additions & 0 deletions packages/api/src/test/resolver.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { ApiFormatType } from '../ApiResultAdapter';
import { setupApi, cleanApi, MockApi } from './setup';

const endpointWeightsRawResult = {
endpoint: 'getBlock',
weight: '8',
};

const endpointWeightsTransformedResult = {
endpoint: 'getBlock',
weight: 8,
};

const eventRawResult = [
{
transaction_hash: '0x2d30ca6f024dbc1307ac8a1a44ca27de6f797ec22ef20627a1307243b0ab7d09',
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
block_timestamp: '2021-04-02T10:07:54.000Z',
block_number: '12526958',
block_hash: '0x0372c302e3c52e8f2e15d155e2c545e6d802e479236564af052759253b20fd86',
data: {
from: '0x54ff6974c715956a5049a123408bff91fbe29f01',
to: '0x74de5d4fcbf63e00296fd95d33236b9794016631',
value: '260103496340000000000',
},
},
];

const expectedAddress = '0xdAC17F958D2ee523a2206206994597C13D831ec7';

const ABI = {
anonymous: false,
inputs: [
{ indexed: true, name: 'from', type: 'address' },
{ indexed: true, name: 'to', type: 'address' },
{ indexed: false, name: 'value', type: 'uint256' },
],
name: 'Transfer',
type: 'event',
};

describe('ApiResolver', () => {
let api: MockApi;
beforeAll(() => {
api = setupApi();
});

afterAll(() => {
cleanApi();
});
it('should test api resolver functions with get request', async () => {
const resolver = await api.endpoints.endpointWeights();
expect(resolver.raw).toStrictEqual(endpointWeightsRawResult);
expect(resolver.toJSON()).toStrictEqual(endpointWeightsRawResult);
expect(resolver.result).toStrictEqual(endpointWeightsTransformedResult);
expect(resolver.format(ApiFormatType.NORMAL)).toStrictEqual(endpointWeightsTransformedResult);
expect(resolver.format(ApiFormatType.RAW)).toStrictEqual(endpointWeightsRawResult);
expect(() => resolver.format('legacy' as any)).toThrowErrorMatchingInlineSnapshot(
`"[A0001] provided formatType not supported"`,
);
});

it('should test api resolver functions with post request and pagination', async () => {
const resolver = await api.endpoints.getContractEvents({
chain: 'eth',
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
topic: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
limit: 2,
abi: ABI,
});
expect(resolver.raw.total).toStrictEqual(10);
expect(resolver.raw.page_size).toStrictEqual(2);
expect(resolver.raw.result).toStrictEqual(eventRawResult);
expect(resolver.result[0].address.format()).toBe(expectedAddress.toLowerCase());
});

it('should test next call', async () => {
const resolver = await api.endpoints.getContractEvents({
chain: 'eth',
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
topic: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
limit: 3,
abi: ABI,
});
const callSpy = jest.fn(async () => await resolver.next());
const result = await callSpy();
expect(result.raw.total).toStrictEqual(10);
expect(result.raw.result).toStrictEqual(eventRawResult);
expect(callSpy).toBeCalledTimes(1);
});
});
122 changes: 122 additions & 0 deletions packages/api/src/test/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { MoralisApi } from './../MoralisApi';
import { ApiModule, EvmAddress, EvmAddressish, EvmChainish, MoralisCore, MoralisCoreProvider } from '@moralisweb3/core';
import { setupServer } from 'msw/node';
import { ApiResolver, BodyType } from '../Resolver';
import { API_ROOT, MOCK_API_KEY } from './config';
import { mockEndpointWeights } from './mockRequests/endpointWeights';
import { mockGetContractEvents } from './mockRequests/getContractEvents';
import { BASE_URL } from '@moralisweb3/evm-api';
import { resolveDefaultChain } from '../utils';
import { ApiPaginatedOptions, ApiPaginatedResolver, ApiPaginatedResponse } from '../PaginatedResolver';

interface EndpointWeight {
endpoint: string;
weight: string;
}

interface Params extends ApiPaginatedOptions {
chain?: EvmChainish;
address: EvmAddressish;
abi: unknown;
}

type ApiResult = {
transaction_hash: string;
address: string;
block_timestamp: string;
block_number: string;
block_hash: string;
data: {
from?: string | undefined;
to?: string | undefined;
value?: string | undefined;
};
}[];

export class MockApi extends ApiModule {
public static readonly moduleName = 'mockApi';

public static create(core?: MoralisCore): MockApi {
return new MockApi(core ?? MoralisCoreProvider.getDefault());
}

public constructor(core: MoralisCore) {
super(MockApi.moduleName, core, API_ROOT);
}

get endpoints() {
return {
endpointWeights: () =>
new ApiResolver({
name: 'endpointWeights',
getUrl: () => `${API_ROOT}/info/endpointWeights`,
apiToResult: (data: EndpointWeight) => ({
endpoint: data.endpoint,
weight: parseInt(data.weight),
}),
resultToJson: (data) => ({
endpoint: data.endpoint,
weight: data.weight.toString(),
}),
parseParams: (params) => params,
}).fetch({}),

getContractEvents: new ApiPaginatedResolver({
name: 'getContractEvents',
getUrl: (params: Params) => `${BASE_URL}/${params.address}/events`,
// TODO: remove PaginatedResponse when api squad make swagger update
apiToResult: (data: ApiPaginatedResponse<ApiResult>) =>
data.result.map((event) => ({
...event,
address: EvmAddress.create(event.address),
})),
resultToJson: (data) => data,
parseParams: (params: Params) => ({
chain: resolveDefaultChain(params.chain).apiHex,
from_block: params.fromBlock,
to_block: params.toBlock,
from_date: params.toDate,
to_date: params.toDate,
providerUrl: params.providerUrl,
topic: params.topic,
limit: params.limit,
offset: params.offset,
subdomain: params.subdomain,
address: EvmAddress.create(params.address).lowercase,
abi: params.abi,
}),
method: 'post',
bodyParams: ['abi'],
bodyType: BodyType.BODY,
}).fetch,
};
}

public setup() {
//
}

public start(): void | Promise<void> {
// Nothing...
}
}

const mockServer = setupServer(mockEndpointWeights, mockGetContractEvents);
export function setupApi() {
const core = MoralisCoreProvider.getDefault();
const api = MoralisApi.create(core);
const mockApi = MockApi.create(core);
core.registerModules([api, mockApi]);
core.start({
apiKey: MOCK_API_KEY,
});
mockServer.listen({
onUnhandledRequest: 'warn',
});

return mockApi;
}

export function cleanApi() {
mockServer.close();
}
13 changes: 10 additions & 3 deletions packages/evmApi/src/EvmApi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { MoralisApi } from '@moralisweb3/api';
import { MoralisCore, MoralisCoreProvider } from '@moralisweb3/core';
import { ApiModule, MoralisCore, MoralisCoreProvider } from '@moralisweb3/core';
import {
getTokenAllowanceResolver,
getTokenPriceResolver,
Expand Down Expand Up @@ -45,7 +44,7 @@ import { uploadFolderResolver } from './resolvers/storage';

export const BASE_URL = 'https://deep-index.moralis.io/api/v2';

export class MoralisEvmApi extends MoralisApi {
export class MoralisEvmApi extends ApiModule {
public static readonly moduleName = 'evmApi';

public static create(core?: MoralisCore): MoralisEvmApi {
Expand Down Expand Up @@ -123,4 +122,12 @@ export class MoralisEvmApi extends MoralisApi {
uploadFolder: uploadFolderResolver.fetch,
};
}

public setup() {
// Nothing...
}

public start(): void | Promise<void> {
// Nothing...
}
}
4 changes: 3 additions & 1 deletion packages/integration/test/evmApi/setup.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { MoralisApi } from '@moralisweb3/api';
import { MoralisCoreProvider } from '@moralisweb3/core';
import MoralisEvmApi from '@moralisweb3/evm-api';
import { MOCK_API_KEY } from '../../mockRequests/config';
import { mockServer } from '../../mockRequests/mockRequests';

export function setupEvmApi(): MoralisEvmApi {
const core = MoralisCoreProvider.getDefault();
const api = MoralisApi.create(core);
const envApi = MoralisEvmApi.create(core);
core.registerModule(envApi);
core.registerModules([api, envApi]);
core.start({
apiKey: MOCK_API_KEY,
});
Expand Down

0 comments on commit 6eae2a9

Please sign in to comment.