From a6d65e1807978071ee85cee8c00072050bb4a338 Mon Sep 17 00:00:00 2001 From: Genaro Bonavita <98661193+genaroibc@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:28:41 -0300 Subject: [PATCH] Test v2/handlers tests (#304) * test: add tests for getBalance handler * test: add tests for tokens/chains handlers * test: add tests for public sdk methods * test: add tests for getRoute method * test: update tests * Fix code style issues with Prettier --------- Co-authored-by: Juan Manuel Villarraza Co-authored-by: Lint Action --- src/handlers/evm/index.spec.ts | 106 ++++++++++++++++++++++++++ src/index.spec.ts | 131 +++++++++++++++++++++++++++++++++ src/utils/TokensChains.spec.ts | 55 ++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 src/index.spec.ts create mode 100644 src/utils/TokensChains.spec.ts diff --git a/src/handlers/evm/index.spec.ts b/src/handlers/evm/index.spec.ts index 85db452..b5e79d3 100644 --- a/src/handlers/evm/index.spec.ts +++ b/src/handlers/evm/index.spec.ts @@ -1,14 +1,35 @@ +import { Squid } from "../../index"; + const mockValidateNativeBalance = jest.fn().mockResolvedValue({ isApproved: true }); const mockValidateTokenBalance = jest.fn().mockResolvedValue({ isApproved: true }); const mockValidateAllowance = jest.fn().mockResolvedValue("100000000000000000"); const mockGetGasData = jest.fn().mockResolvedValue({ gasLimit: "100" }); +const testIntegratorId = "test-api"; +const testBaseUrl = "https://api.uatsquidrouter.com/"; + jest.mock("./utils", () => ({ Utils: class Utils { validateNativeBalance = mockValidateNativeBalance; validateTokenBalance = mockValidateTokenBalance; validateAllowance = mockValidateAllowance; getGasData = mockGetGasData; + + getTokensBalanceSupportingMultiCall = + jest.requireActual("./utils").Utils.prototype.getTokensBalanceSupportingMultiCall; + getTokensBalanceWithoutMultiCall = + jest.requireActual("./utils").Utils.prototype.getTokensBalanceWithoutMultiCall; + }, +})); + +jest.mock("ethers", () => ({ + ...jest.requireActual("ethers"), + ethers: { + ...jest.requireActual("ethers").ethers, + Contract: class Contract { + getEthBalance = jest.fn().mockResolvedValue("100000000000000"); + balanceOf = jest.fn().mockResolvedValue("100000000"); + }, }, })); @@ -77,4 +98,89 @@ describe("EvmHandler", () => { // expect(); }); }); + + describe("getBalances", () => { + let squid: Squid; + + beforeAll(() => { + squid = new Squid({ + integratorId: testIntegratorId, + baseUrl: testBaseUrl, + }); + + return squid.init(); + }); + + it("returns balances for native tokens", async () => { + const ethToken = squid.tokens.find(t => t.symbol === "ETH" && t.chainId === "1"); + const avaxToken = squid.tokens.find(t => t.symbol === "AVAX" && t.chainId === "43114"); + const ethereumChain = squid.chains.find(c => c.chainId === "1"); + const avalancheChain = squid.chains.find(c => c.chainId === "43114"); + + if (!ethToken || !avaxToken) throw new Error("Tokens not found"); + if (!ethereumChain || !avalancheChain) throw new Error("Chain not found"); + + const balances = await handler.getBalances( + [ethToken, avaxToken], + "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664", + { + "1": ethereumChain.rpc, + "43114": avalancheChain.rpc, + }, + ); + + expect(balances).toEqual([ + { + symbol: ethToken.symbol, + address: ethToken.address, + decimals: ethToken.decimals, + chainId: ethToken.chainId, + balance: "100000000000000", + }, + { + symbol: avaxToken.symbol, + address: avaxToken.address, + decimals: avaxToken.decimals, + chainId: avaxToken.chainId, + balance: "100000000000000", + }, + ]); + }); + + it("returns balances for non-native tokens", async () => { + const usdcToken = squid.tokens.find(t => t.symbol === "USDC" && t.chainId === "42161"); + const wethToken = squid.tokens.find(t => t.symbol === "WETH" && t.chainId === "137"); + const polygonChain = squid.chains.find(c => c.chainId === "137"); + const arbitrumChain = squid.chains.find(c => c.chainId === "42161"); + + if (!usdcToken || !wethToken) throw new Error("Tokens not found"); + if (!polygonChain || !arbitrumChain) throw new Error("Chains not found"); + + const balances = await handler.getBalances( + [usdcToken, wethToken], + "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664", + { + "137": polygonChain.rpc, + "42161": arbitrumChain.rpc, + }, + ); + + expect(balances).toEqual([ + { + symbol: wethToken.symbol, + address: wethToken.address, + decimals: wethToken.decimals, + chainId: wethToken.chainId, + balance: "100000000", + }, + { + symbol: usdcToken.symbol, + address: usdcToken.address, + decimals: usdcToken.decimals, + chainId: usdcToken.chainId, + balance: "100000000", + }, + ]); + }); + }); }); diff --git a/src/index.spec.ts b/src/index.spec.ts new file mode 100644 index 0000000..6ef18b3 --- /dev/null +++ b/src/index.spec.ts @@ -0,0 +1,131 @@ +import { Squid } from "./index"; + +let squid: Squid; +const testIntegratorId = "test-api"; +const testBaseUrl = "https://api.uatsquidrouter.com/"; + +describe("Squid", () => { + beforeAll(async () => { + squid = new Squid({ + baseUrl: testBaseUrl, + integratorId: testIntegratorId, + }); + + return await squid.init(); + }); + + describe("getTokenPrice", () => { + it("should return token price", async () => { + const tokenPrice = await squid.getTokenPrice({ + tokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + chainId: "1", + }); + + expect(typeof tokenPrice).toEqual("number"); + }); + + it("should return 400 error if token address is invalid", async () => { + try { + await squid.getTokenPrice({ + tokenAddress: "0x", + chainId: "1", + }); + } catch (error) { + expect(error.response.status).toEqual(400); + } + }); + + it("should return 400 error if chain id is invalid", async () => { + try { + await squid.getTokenPrice({ + tokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + chainId: "", + }); + } catch (error) { + expect(error.response.status).toEqual(400); + } + }); + }); + + describe("getMultipleTokensPrice", () => { + it("should return price for multiple tokens", async () => { + const tokensWithPrice = await squid.getMultipleTokensPrice({ + chainId: "1", + }); + + expect(tokensWithPrice.every(tokenPrice => typeof tokenPrice.usdPrice === "number")).toBe( + true, + ); + }); + }); + + describe("getRoute", () => { + it("should return route", async () => { + const { route, integratorId, requestId } = await squid.getRoute({ + fromChain: "1", + toChain: "56", + fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + toToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + fromAddress: "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5", + toAddress: "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5", + fromAmount: "5000000000000", + slippage: 1.5, + }); + + expect(route.estimate).toBeDefined(); + expect(route.params).toBeDefined(); + expect(integratorId).toEqual(testIntegratorId); + expect(requestId).toBeDefined(); + }); + + it("should return 400 error if from or to address not provided", async () => { + try { + await squid.getRoute({ + fromChain: "1", + toChain: "56", + fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + toToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + fromAmount: "5000000000000", + slippage: 1.5, + }); + } catch (error) { + expect(error.response.status).toEqual(400); + } + }); + }); + + describe("getStatus", () => { + jest.setTimeout(10000); // increase timeout for slow tests to 10 seconds + + it("should return 404 error if transactionId is invalid", async () => { + try { + await squid.getStatus({ + requestId: "1643a99ae59c3f5164ed120812f00e38", + integratorId: testIntegratorId, + transactionId: "0x", + }); + + expect(true).toBe(false); // should fail before reaching here + } catch (error) { + expect(error.response.status).toEqual(404); + expect(error.response.data.message).toEqual("No transaction found in axelarscan"); + } + }); + + it("should return 400 error if transactionId is missing", async () => { + try { + await squid.getStatus({ + requestId: "0x", + integratorId: testIntegratorId, + transactionId: "", + }); + + expect(true).toBe(false); // should fail before reaching here + } catch (error) { + expect(error.response.status).toEqual(400); + expect(error.response.data.message).toEqual("transactionId is a required field"); + expect(error.response.data.type).toEqual("SCHEMA_VALIDATION_ERROR"); + } + }); + }); +}); diff --git a/src/utils/TokensChains.spec.ts b/src/utils/TokensChains.spec.ts new file mode 100644 index 0000000..5c352a8 --- /dev/null +++ b/src/utils/TokensChains.spec.ts @@ -0,0 +1,55 @@ +import { TokensChains } from "./TokensChains"; +import { Squid } from "../index"; + +const tokenChains = new TokensChains(); + +const testIntegratorId = "test-api"; +const testBaseUrl = "https://api.uatsquidrouter.com/"; + +describe("TokensChains", () => { + let squid: Squid; + + beforeAll(async () => { + squid = new Squid({ + integratorId: testIntegratorId, + baseUrl: testBaseUrl, + }); + + await squid.init(); + + tokenChains.tokens = squid.tokens; + tokenChains.chains = squid.chains; + }); + + describe("getChainData", () => { + it("should return chain data", async () => { + const chain = squid.chains.find(c => c.chainId === "1"); + + if (!chain) throw new Error("Chain not found"); + + const chainData = tokenChains.getChainData(chain.chainId); + + expect(chainData).toEqual(chain); + }); + + it("should throw error if chain not found", async () => { + expect(() => tokenChains.getChainData("9999999")).toThrowError(); + }); + }); + + describe("getTokenData", () => { + it("should return token data", async () => { + const token = squid.tokens.find(t => t.symbol === "WETH" && t.chainId === "1"); + + if (!token) throw new Error("Token not found"); + + const tokenData = tokenChains.getTokenData(token.address, token.chainId); + + expect(tokenData).toEqual(token); + }); + + it("should throw error if token not found", async () => { + expect(() => tokenChains.getTokenData("0x", "1")).toThrowError(); + }); + }); +});