Skip to content

Commit

Permalink
tokens fetch from external api unit tests (#1331)
Browse files Browse the repository at this point in the history
* upgrade tokens unit tests WIP

* add unit test for tokens

* fix lint

* remove console log
  • Loading branch information
GuticaStefan authored Sep 12, 2024
1 parent b2ed900 commit 28dce31
Showing 1 changed file with 127 additions and 13 deletions.
140 changes: 127 additions & 13 deletions src/test/unit/services/tokens.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ import { TransferService } from "src/endpoints/transfers/transfer.service";
import { MexPairService } from "src/endpoints/mex/mex.pair.service";
import * as fs from 'fs';
import * as path from 'path';
import { ApiService } from "@multiversx/sdk-nestjs-http";
import { ApiService, ApiUtils } from "@multiversx/sdk-nestjs-http";
import { Token } from "src/endpoints/tokens/entities/token";
import { NftCollection } from "src/endpoints/collections/entities/nft.collection";
import { EsdtSupply } from "src/endpoints/esdt/entities/esdt.supply";
import { TokenDetailed } from "src/endpoints/tokens/entities/token.detailed";
import { NumberUtils } from "@multiversx/sdk-nestjs-common";

describe('Token Service', () => {
let tokenService: TokenService;
Expand All @@ -35,6 +40,8 @@ describe('Token Service', () => {
let assetsService: AssetsService;
let apiService: ApiService;
let apiConfigService: ApiConfigService;
let cacheService: CacheService;
let dataApiService: DataApiService;

beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
Expand All @@ -60,6 +67,11 @@ describe('Token Service', () => {
{
getOrSet: jest.fn(),
get: jest.fn(),
batchSet: jest.fn(),
getLocal: jest.fn(),
deleteInCache: jest.fn(),
batchGetManyRemote: jest.fn(),
batchApplyAll: jest.fn(),
},
},
{
Expand Down Expand Up @@ -146,12 +158,14 @@ describe('Token Service', () => {
}).compile();

tokenService = moduleRef.get<TokenService>(TokenService);
cacheService = moduleRef.get<CacheService>(CacheService);
esdtService = moduleRef.get<EsdtService>(EsdtService);
collectionService = moduleRef.get<CollectionService>(CollectionService);
indexerService = moduleRef.get<IndexerService>(IndexerService);
assetsService = moduleRef.get<AssetsService>(AssetsService);
apiService = moduleRef.get<ApiService>(ApiService);
apiConfigService = moduleRef.get<ApiConfigService>(ApiConfigService);
dataApiService = moduleRef.get<DataApiService>(DataApiService);
});

afterEach(() => {
Expand Down Expand Up @@ -617,18 +631,118 @@ describe('Token Service', () => {
canWipe: true,
},
];

it('should return values from external api', async () => {
tokenService['cachingService'].getOrSet = jest.fn().mockImplementation((_, callback) => callback());
jest.spyOn(apiConfigService, 'isTokensFetchFeatureEnabled').mockReturnValue(true);
jest.spyOn(apiConfigService, 'getTokensFetchServiceUrl').mockReturnValue('https://testnet-api.multiversx.com');
jest.spyOn(apiService, 'get').mockResolvedValueOnce({data: mockTokens});

const result = await tokenService.getAllTokens();
expect(result).toEqual(mockTokens);
expect(apiService.get).toHaveBeenCalledTimes(1);
expect(esdtService.getAllFungibleTokenProperties).not.toHaveBeenCalled();
expect(collectionService.getNftCollections).not.toHaveBeenCalled();
describe('getAllTokens', () => {
it('should return nodes from API when isNodesFetchFeatureEnabled is true', async () => {
const mockTokens: Partial<Token>[] = [{ identifier: 'mockIdentifier' }];
const url = 'https://testnet-api.multiversx.com';

jest.spyOn(apiConfigService, 'isTokensFetchFeatureEnabled').mockReturnValue(true);
jest.spyOn(apiConfigService, 'getTokensFetchServiceUrl').mockReturnValue(url);
jest.spyOn(apiService, 'get').mockResolvedValue({ data: mockTokens });
// eslint-disable-next-line require-await
jest.spyOn(cacheService, 'getOrSet').mockImplementation(async (_key, getter) => getter());

const result = await tokenService.getAllTokens();

expect(apiConfigService.isTokensFetchFeatureEnabled).toHaveBeenCalled();
expect(apiService.get).toHaveBeenCalledWith(`${url}/tokens`, { params: { size: 10000 } });
expect(result).toEqual(mockTokens);
});

it('should return tokens from other sources when isTokensFetchFeatureEnabled is false', async () => {

const mockTokenProperties: Partial<TokenProperties>[] = [{ identifier: 'mockIdentifier' }];
let mockTokens: Partial<TokenDetailed>[] = mockTokenProperties.map(properties => ApiUtils.mergeObjects(new TokenDetailed(), properties));
const mockTokenAssets: Partial<TokenAssets> = { name: 'mockName' };
const mockNftCollections: Partial<NftCollection>[] = [{ collection: 'mockCollection' }];
const mockTokenSupply: Partial<EsdtSupply> = { totalSupply: '1000000000000000000', circulatingSupply: '500000000000000000' };

jest.spyOn(apiConfigService, 'isTokensFetchFeatureEnabled').mockReturnValue(false);
jest.spyOn(esdtService, 'getAllFungibleTokenProperties').mockResolvedValue(mockTokenProperties as TokenProperties[]);
jest.spyOn(assetsService, 'getTokenAssets').mockResolvedValue(mockTokenAssets as TokenAssets);
jest.spyOn(collectionService, 'getNftCollections').mockResolvedValue(mockNftCollections as NftCollection[]);

jest.spyOn(tokenService as any, 'batchProcessTokens').mockImplementation(() => Promise.resolve());
jest.spyOn(tokenService as any, 'applyMexLiquidity').mockImplementation(() => Promise.resolve());
jest.spyOn(tokenService as any, 'applyMexPrices').mockImplementation(() => Promise.resolve());
jest.spyOn(tokenService as any, 'applyMexPairType').mockImplementation(() => Promise.resolve());
jest.spyOn(tokenService as any, 'applyMexPairTradesCount').mockImplementation(() => Promise.resolve());
jest.spyOn(cacheService as any, 'batchApplyAll').mockImplementation(() => Promise.resolve());
jest.spyOn(dataApiService, 'getEsdtTokenPrice').mockResolvedValue(100);
jest.spyOn(tokenService as any, 'fetchTokenDataFromUrl').mockResolvedValue(100);
jest.spyOn(esdtService, 'getTokenSupply').mockResolvedValue(mockTokenSupply as EsdtSupply);

// eslint-disable-next-line require-await
jest.spyOn(cacheService, 'getOrSet').mockImplementation(async (_key, getter) => getter());

const result = await tokenService.getAllTokens();

expect(apiConfigService.isTokensFetchFeatureEnabled).toHaveBeenCalled();
expect(esdtService.getAllFungibleTokenProperties).toHaveBeenCalled();

mockTokens.forEach((mockToken) => {
expect(assetsService.getTokenAssets).toHaveBeenCalledWith(mockToken.identifier);
});

expect(esdtService.getAllFungibleTokenProperties).toHaveBeenCalled();
mockTokens.forEach(mockToken => {
expect(assetsService.getTokenAssets).toHaveBeenCalledWith(mockToken.identifier);
mockToken.name = mockTokenAssets.name;
});
expect(assetsService.getTokenAssets).toHaveBeenCalledTimes(mockTokens.length);


expect((collectionService as any).getNftCollections).toHaveBeenCalledWith(expect.anything(), { type: [TokenType.MetaESDT] });
mockNftCollections.forEach(collection => {
mockTokens.push(new TokenDetailed({
type: TokenType.MetaESDT,
identifier: collection.collection,
name: collection.name,
timestamp: collection.timestamp,
owner: collection.owner,
decimals: collection.decimals,
canFreeze: collection.canFreeze,
canPause: collection.canPause,
canTransferNftCreateRole: collection.canTransferNftCreateRole,
canWipe: collection.canWipe,
canAddSpecialRoles: collection.canAddSpecialRoles,
canChangeOwner: collection.canChangeOwner,
canUpgrade: collection.canUpgrade,
}));
});

expect((tokenService as any).batchProcessTokens).toHaveBeenCalledWith(mockTokens);
expect((tokenService as any).applyMexLiquidity).toHaveBeenCalledWith(mockTokens.filter(x => x.type !== TokenType.MetaESDT));
expect((tokenService as any).applyMexPrices).toHaveBeenCalledWith(mockTokens.filter(x => x.type !== TokenType.MetaESDT));
expect((tokenService as any).applyMexPairType).toHaveBeenCalledWith(mockTokens.filter(x => x.type !== TokenType.MetaESDT));
expect((tokenService as any).applyMexPairTradesCount).toHaveBeenCalledWith(mockTokens.filter(x => x.type !== TokenType.MetaESDT));
expect((cacheService as any).batchApplyAll).toHaveBeenCalled();
mockTokens.forEach(mockToken => {
const priceSourcetype = mockToken.assets?.priceSource?.type;
if (priceSourcetype === 'dataApi') {
expect(dataApiService.getEsdtTokenPrice).toHaveBeenCalledWith(mockToken.identifier);
} else if (priceSourcetype === 'customUrl' && mockToken.assets?.priceSource?.url) {
const pathToPrice = mockToken.assets?.priceSource?.path ?? "0.usdPrice";
expect((tokenService as any).fetchTokenDataFromUrl).toHaveBeenCalledWith(mockToken.assets?.priceSource?.url, pathToPrice);
}

if (mockToken.price) {
expect(esdtService.getTokenSupply).toHaveBeenCalledWith(mockToken.identifier);
mockToken.supply = mockTokenSupply.totalSupply;

if (mockToken.circulatingSupply) {
mockToken.marketCap = mockToken.price * NumberUtils.denominateString(mockToken.circulatingSupply.toString(), mockToken.decimals);
}
}
});

mockTokens = mockTokens.sortedDescending(
token => token.assets ? 1 : 0,
token => token.isLowLiquidity ? 0 : (token.marketCap ?? 0),
token => token.transactions ?? 0,
);
expect(result).toEqual(mockTokens);
});
});

it('should return values from cache', async () => {
Expand Down

0 comments on commit 28dce31

Please sign in to comment.