Skip to content

Commit

Permalink
add tests for providers, managers & utils
Browse files Browse the repository at this point in the history
  • Loading branch information
avernikoz committed Feb 12, 2024
1 parent e2825e8 commit ed584e5
Showing 15 changed files with 1,367 additions and 0 deletions.
12 changes: 12 additions & 0 deletions tests/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SuiClient } from "@mysten/sui.js/client";
import { CacheOptions } from "../src/providers/types";

export const SUI_COIN_TYPE = "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI";
export const USDC_COIN_TYPE = "0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN";
export const suiProviderUrl = "https://sui-rpc.publicnode.com";
export const cacheOptions: CacheOptions = {
updateIntervalInMs: 60_000 * 30, // 30 min
};
// Public key for tests, nothing special
export const publicKey = "0x046f718ca3fdd519f6d21ad5b18a7fafaafeb85fd311ef8b99db22df7ec15d5d";
export const provider = new SuiClient({ url: suiProviderUrl });
74 changes: 74 additions & 0 deletions tests/managers/coinLazyLoading.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { cacheOptions, suiProviderUrl } from "../constants";
import { CoinManagerSingleton } from "../../src/managers/CoinManager";
import { Provider, Providers } from "../../src/managers/types";
import { AftermathSingleton } from "../../src/providers/aftermath/aftermath";
import { CetusSingleton } from "../../src/providers/cetus/cetus";
import { clmmMainnet } from "../../src/providers/cetus/config";
import { FlowxSingleton } from "../../src/providers/flowx/flowx";
import { TurbosSingleton } from "../../src/providers/turbos/turbos";

jest.setTimeout(60000);

describe("CoinManager & Event Emitter & lazy loading", () => {
it("should fill coin manager cache asynchronously", async () => {
const turbos: TurbosSingleton = await TurbosSingleton.getInstance({ suiProviderUrl, cacheOptions });
const cetus: CetusSingleton = await CetusSingleton.getInstance({
sdkOptions: clmmMainnet,
cacheOptions,
suiProviderUrl,
});
const aftermath: AftermathSingleton = await AftermathSingleton.getInstance({ cacheOptions });
const flowx: FlowxSingleton = await FlowxSingleton.getInstance({ cacheOptions });
const providers: Providers = [turbos, cetus, aftermath, flowx];
const coinManager: CoinManagerSingleton = CoinManagerSingleton.getInstance(providers);

providers.forEach((provider: Provider) => {
const events = provider.getEvents();
const buffer = provider.getBuffer();

expect(buffer.length).toStrictEqual(0);
expect(Object.keys(events).length).toStrictEqual(1);
});

const allCoinsCacheBefore = coinManager.getAllCoins();
const coinsByProviderNameCacheBefore = coinManager.getCoinsByProviderMap();

expect(allCoinsCacheBefore.size).toStrictEqual(0);
expect(coinsByProviderNameCacheBefore.size).toStrictEqual(0);

const maxWaitingAttemps = 10;
let currentAttemp = 0;

while (currentAttemp < maxWaitingAttemps) {
const allProvidersAreInited: boolean = providers.every((provider) => provider.getCoins().data.length !== 0);

if (!allProvidersAreInited) {
await new Promise((r) => setTimeout(r, 5000));
currentAttemp++;
} else {
break;
}
}

if (currentAttemp === 10) {
throw new Error(
"Not all providers were inited. Find out the reason " +
"or try to increase maxWaitingAttemps or waiting time in setTimeout",
);
}

providers.forEach((provider: Provider) => {
const events = provider.getEvents();
const buffer = provider.getBuffer();

expect(buffer.length).toStrictEqual(1);
expect(Object.keys(events).length).toStrictEqual(1);
});

const allCoinsCacheAfter = coinManager.getAllCoins();
const coinsByProviderNameCacheAfter = coinManager.getCoinsByProviderMap();

expect(allCoinsCacheAfter.size).toBeGreaterThan(0);
expect(coinsByProviderNameCacheAfter.size).toStrictEqual(providers.length);
});
});
53 changes: 53 additions & 0 deletions tests/managers/coinNoLazyLoading.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { cacheOptions, suiProviderUrl } from "../constants";
import { CoinManagerSingleton } from "../../src/managers/CoinManager";
import { Provider, Providers } from "../../src/managers/types";
import { AftermathSingleton } from "../../src/providers/aftermath/aftermath";
import { CetusSingleton } from "../../src/providers/cetus/cetus";
import { clmmMainnet } from "../../src/providers/cetus/config";
import { FlowxSingleton } from "../../src/providers/flowx/flowx";
import { TurbosSingleton } from "../../src/providers/turbos/turbos";

jest.setTimeout(60000);

describe("CoinManager & Event Emitter & no lazy loading", () => {
it("should fill coin manager cache synchronously from buffer", async () => {
const turbos: TurbosSingleton = await TurbosSingleton.getInstance({
suiProviderUrl,
cacheOptions,
lazyLoading: false,
});
const cetus: CetusSingleton = await CetusSingleton.getInstance({
sdkOptions: clmmMainnet,
cacheOptions,
suiProviderUrl,
lazyLoading: false,
});
const aftermath: AftermathSingleton = await AftermathSingleton.getInstance({ cacheOptions, lazyLoading: false });
const flowx: FlowxSingleton = await FlowxSingleton.getInstance({ cacheOptions, lazyLoading: false });
const providers: Providers = [turbos, cetus, aftermath, flowx];

providers.forEach((provider: Provider) => {
const events = provider.getEvents();
const buffer = provider.getBuffer();

expect(buffer.length).toBeGreaterThan(0);
expect(Object.keys(events).length).toStrictEqual(0);
});

const coinManager: CoinManagerSingleton = CoinManagerSingleton.getInstance(providers);

const allCoinsCache = coinManager.getAllCoins();
const coinsByProviderNameCache = coinManager.getCoinsByProviderMap();

expect(allCoinsCache.size).toBeGreaterThan(0);
expect(coinsByProviderNameCache.size).toStrictEqual(providers.length);

providers.forEach((provider: Provider) => {
const events = provider.getEvents();
const buffer = provider.getBuffer();

expect(buffer.length).toStrictEqual(0);
expect(Object.keys(events).length).toStrictEqual(1);
});
});
});
79 changes: 79 additions & 0 deletions tests/managers/mocked/getFiltredProviders.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { CommonCoinData } from "../../../src/managers/types";
import { LONG_SUI_COIN_TYPE, SHORT_SUI_COIN_TYPE } from "../../../src/providers/common";
import { CommonPoolData } from "../../../src/providers/types";
import { hasPath } from "../../../src/providers/utils/hasPath";

type MockedPoolProvider = {
providerName: string;
isSmartRoutingAvailable: boolean;
getPaths: () => Map<string, CommonPoolData>;
};

export const getFiltredProviders = ({
poolProviders,
coinsByProviderMap,
tokenFrom,
tokenTo,
}: {
poolProviders: MockedPoolProvider[];
coinsByProviderMap: Map<string, Map<string, CommonCoinData>>;
tokenFrom: string;
tokenTo: string;
}) => {
const tokenFromIsSui: boolean = tokenFrom === SHORT_SUI_COIN_TYPE || tokenFrom === LONG_SUI_COIN_TYPE;
const tokenToIsSui: boolean = tokenTo === SHORT_SUI_COIN_TYPE || tokenTo === LONG_SUI_COIN_TYPE;

const filtredProviders = poolProviders.filter((poolProvider) => {
const providerCoins = coinsByProviderMap.get(poolProvider.providerName);

if (!providerCoins) {
console.warn(`[getFiltredProviders] No coins found for such provider ${poolProvider.providerName}`);
return false;
}

// Check that provider has one of the variants of SUI token
const providerCoinsHaveSui = providerCoins.has(SHORT_SUI_COIN_TYPE) || providerCoins.has(LONG_SUI_COIN_TYPE);
// Check if input tokenFrom/tokenTo is SUI
const tokenFromOrTokenToIsSui = tokenFromIsSui || tokenToIsSui;
// If SUI token is tokenTo, we return tokenFrom, and vice versa
const notSuiTokenInInputTokens: string = tokenFromIsSui ? tokenTo : tokenFrom;

if (tokenFromOrTokenToIsSui) {
// Provider tokens doesn't have SUI and doesn't have the second token in pair (tokenFrom/tokenTo)
const providerDoesntHaveAnyToken = !providerCoinsHaveSui || !providerCoins.has(notSuiTokenInInputTokens);

if (providerDoesntHaveAnyToken) {
return false;
}
} else {
// If no SUI token present in tokenFrom/tokenTo, just check that provider has both tokens
if (!providerCoins.has(tokenFrom) || !providerCoins.has(tokenTo)) {
return false;
}
}

// If provider doesn't support smart-routing, than we have to check that provider has direct path with both tokens
if (!poolProvider.isSmartRoutingAvailable) {
const paths: Map<string, CommonPoolData> = poolProvider.getPaths();

if (tokenFromOrTokenToIsSui) {
const providerHasNoPathWithShortSui = !hasPath(SHORT_SUI_COIN_TYPE, notSuiTokenInInputTokens, paths);
const providerHasNoPathWithLongSui = !hasPath(LONG_SUI_COIN_TYPE, notSuiTokenInInputTokens, paths);

if (providerHasNoPathWithShortSui && providerHasNoPathWithLongSui) {
return false;
}
} else {
const providerHasNoPathWithRegularCoins = !hasPath(tokenFrom, tokenTo, paths);

if (providerHasNoPathWithRegularCoins) {
return false;
}
}
}

return true;
});

return filtredProviders;
};
Loading

0 comments on commit ed584e5

Please sign in to comment.