UNPKG

@ledgerhq/coin-hedera

Version:
467 lines (404 loc) 13.5 kB
import network from "@ledgerhq/live-network"; import BigNumber from "bignumber.js"; import { getMockResponse } from "../test/fixtures/network.fixture"; import { hgraphClient } from "./hgraph"; jest.mock("@ledgerhq/live-network"); const mockedNetwork = jest.mocked(network); const getRequestData = (callIndex = 0) => (mockedNetwork.mock.calls[callIndex][0] as { data: { query: string; variables: any } }).data; describe("getLatestIndexedConsensusTimestamp", () => { beforeEach(() => { jest.resetAllMocks(); }); it("should fetch and return the latest indexed consensus timestamp", async () => { const mockTimestamp = "1234567890123456789"; mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { ethereum_transaction: [{ consensus_timestamp: mockTimestamp }], }, }), ); const result = await hgraphClient.getLatestIndexedConsensusTimestamp(); expect(mockedNetwork).toHaveBeenCalledTimes(1); expect(result).toEqual(new BigNumber(mockTimestamp)); }); it("should throw error when API returns errors", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ errors: [{ message: "Database error" }], }), ); await expect(hgraphClient.getLatestIndexedConsensusTimestamp()).rejects.toThrow(); }); it("should throw error when no transactions found", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { ethereum_transaction: [], }, }), ); await expect(hgraphClient.getLatestIndexedConsensusTimestamp()).rejects.toThrow(); }); }); describe("getERC20Balances", () => { beforeEach(() => { jest.resetAllMocks(); }); it("should fetch and return ERC20 balances for an account", async () => { const mockAddress = "0.0.1234"; const mockBalances = [ { token_id: "0.0.5001", balance: "1000000", balance_timestamp: "1234567890.123456789", created_timestamp: "1234567800.000000000", }, { token_id: "0.0.5002", balance: "2000000", balance_timestamp: "1234567890.123456789", created_timestamp: "1234567800.000000000", }, ]; mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { erc_token_account: mockBalances, }, }), ); const result = await hgraphClient.getERC20Balances({ address: mockAddress }); const queryVariables = getRequestData(0).variables; expect(mockedNetwork).toHaveBeenCalledTimes(1); expect(queryVariables.accountId).toBe("1234"); expect(result).toEqual(mockBalances); }); it("should extract account ID correctly from address", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { erc_token_account: [], }, }), ); await hgraphClient.getERC20Balances({ address: "0.0.9999" }); expect(getRequestData(0).variables.accountId).toBe("9999"); }); it("should throw error when API returns errors", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ errors: [{ message: "Account not found" }], }), ); await expect(hgraphClient.getERC20Balances({ address: "0.0.1234" })).rejects.toThrow(); }); }); describe("getERC20Transfers", () => { beforeEach(() => { jest.resetAllMocks(); }); it("should return empty array when no token addresses provided", async () => { const result = await hgraphClient.getERC20Transfers({ address: "0.0.1234", tokenEvmAddresses: [], fetchAllPages: true, }); expect(mockedNetwork).not.toHaveBeenCalled(); expect(result).toEqual([]); }); it("should fetch transfers with default parameters", async () => { const mockTransfer = { token_id: "0.0.5001", token_evm_address: "0xabc123", sender_evm_address: "0x111", sender_account_id: "0.0.1234", receiver_evm_address: "0x222", receiver_account_id: "0.0.5678", payer_account_id: "0.0.1234", amount: "1000", transfer_type: "transfer", consensus_timestamp: "1234567890123456789", transaction_hash: "0xhash1", }; mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [mockTransfer], }, }), ); const result = await hgraphClient.getERC20Transfers({ address: mockTransfer.payer_account_id, tokenEvmAddresses: [mockTransfer.token_evm_address], fetchAllPages: true, }); const queryVariables = getRequestData(0).variables; expect(mockedNetwork).toHaveBeenCalledTimes(1); expect(result).toEqual([mockTransfer]); expect(queryVariables.accountId).toBe("1234"); expect(queryVariables.limit).toBe(100); }); it("should keep fetching all pages when fetchAllPages is true", async () => { const mockTransfers1 = [ { consensus_timestamp: "1000000000000000000", transaction_hash: "0xhash1" }, ]; const mockTransfers2 = [ { consensus_timestamp: "2000000000000000000", transaction_hash: "0xhash2" }, ]; const mockTransfers3 = [ { consensus_timestamp: "3000000000000000000", transaction_hash: "0xhash3" }, ]; mockedNetwork .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: mockTransfers1 }, }), ) .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: mockTransfers2 }, }), ) .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: mockTransfers3 }, }), ) .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [] }, }), ); const result = await hgraphClient.getERC20Transfers({ address: "0.0.1234", tokenEvmAddresses: ["0xabc123"], limit: 1, fetchAllPages: true, }); expect(mockedNetwork).toHaveBeenCalledTimes(4); expect(result.map(t => t.consensus_timestamp)).toEqual([ "1000000000000000000", "2000000000000000000", "3000000000000000000", ]); }); it("should paginate when fetchAllPages is false", async () => { const mockTransfers1 = [ { consensus_timestamp: "1000000000000000000", transaction_hash: "0xhash1" }, { consensus_timestamp: "1100000000000000000", transaction_hash: "0xhash1a" }, ]; const mockTransfers2 = [ { consensus_timestamp: "2000000000000000000", transaction_hash: "0xhash2" }, { consensus_timestamp: "2100000000000000000", transaction_hash: "0xhash2a" }, ]; mockedNetwork .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: mockTransfers1 }, }), ) .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: mockTransfers2 }, }), ); const result = await hgraphClient.getERC20Transfers({ address: "0.0.1234", tokenEvmAddresses: ["0xabc123"], limit: 2, fetchAllPages: false, }); expect(mockedNetwork).toHaveBeenCalledTimes(1); expect(result.map(t => t.consensus_timestamp)).toEqual([ "1000000000000000000", "1100000000000000000", ]); }); it("should handle timestamp parameter correctly", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [] }, }), ); await hgraphClient.getERC20Transfers({ address: "0.0.1234", tokenEvmAddresses: ["0xabc123"], timestamp: "1234567890.123456789", fetchAllPages: true, }); const query = getRequestData(0).query; const queryVariables = getRequestData(0).variables; expect(query).toContain("consensus_timestamp: { _gt: $cursor }"); expect(queryVariables.cursor).toBe("1234567890123456789"); }); it("should use correct pagination direction for desc order", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [] }, }), ); await hgraphClient.getERC20Transfers({ address: "0.0.1234", tokenEvmAddresses: ["0xabc123"], order: "desc", fetchAllPages: false, }); const query = getRequestData(0).query; expect(query).toContain("order_by: { consensus_timestamp: desc }"); }); it("should throw error when API returns errors", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ errors: [{ message: "Query failed" }], }), ); await expect( hgraphClient.getERC20Transfers({ address: "0.0.1234", tokenEvmAddresses: ["0xabc123"], fetchAllPages: true, }), ).rejects.toThrow(); }); }); describe("getERC20TransfersByTimestampRange", () => { beforeEach(() => { jest.resetAllMocks(); }); it("should fetch transfers within timestamp range", async () => { const mockTransfers = [ { token_id: "0.0.5001", consensus_timestamp: "1500000000000000000", transaction_hash: "0xhash1", }, ]; mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: mockTransfers }, }), ); const result = await hgraphClient.getERC20TransfersByTimestampRange({ startTimestamp: "1000.000000000", endTimestamp: "2000.000000000", }); const queryVariables = getRequestData(0).variables; expect(mockedNetwork).toHaveBeenCalledTimes(1); expect(result).toEqual(mockTransfers); expect(queryVariables.startTimestamp).toBe("1000000000000"); expect(queryVariables.endTimestamp).toBe("2000000000000"); }); it("should normalize timestamps by removing dots", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [] }, }), ); await hgraphClient.getERC20TransfersByTimestampRange({ startTimestamp: "1234.567890123", endTimestamp: "5678.901234567", }); const queryVariables = getRequestData(0).variables; expect(queryVariables.startTimestamp).toBe("1234567890123"); expect(queryVariables.endTimestamp).toBe("5678901234567"); }); it("should fetch all pages until no more results", async () => { mockedNetwork .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [ { consensus_timestamp: "1100000000000000000", transaction_hash: "0xhash1" }, ], }, }), ) .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [ { consensus_timestamp: "1200000000000000000", transaction_hash: "0xhash2" }, ], }, }), ) .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [] }, }), ); const result = await hgraphClient.getERC20TransfersByTimestampRange({ startTimestamp: "1000.000000000", endTimestamp: "2000.000000000", limit: 1, }); expect(mockedNetwork).toHaveBeenCalledTimes(3); expect(result.map(t => t.consensus_timestamp)).toEqual([ "1100000000000000000", "1200000000000000000", ]); }); it("should use cursor for subsequent pages", async () => { mockedNetwork .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [ { consensus_timestamp: "1100000000000000000", transaction_hash: "0xhash1" }, ], }, }), ) .mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [] }, }), ); await hgraphClient.getERC20TransfersByTimestampRange({ startTimestamp: "1000.000000000", endTimestamp: "2000.000000000", limit: 1, }); const firstCall = mockedNetwork.mock.calls[0][0] as { data: { query: string; variables: Record<string, unknown> }; }; const secondCall = mockedNetwork.mock.calls[1][0] as { data: { query: string; variables: Record<string, unknown> }; }; // First call should use _gte with startTimestamp expect(firstCall.data.query).toContain("_gte: $startTimestamp"); expect(firstCall.data.variables).not.toHaveProperty("cursor"); // Second call should use _gt with cursor expect(secondCall.data.query).toContain("_gt: $cursor"); expect(secondCall.data.variables.cursor).toBe("1100000000000000000"); }); it("should support custom order parameter", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ data: { erc_token_transfer: [] }, }), ); await hgraphClient.getERC20TransfersByTimestampRange({ startTimestamp: "1000.000000000", endTimestamp: "2000.000000000", order: "asc", }); const query = getRequestData(0).query; expect(query).toContain("order_by: { consensus_timestamp: asc }"); }); it("should throw error when API returns errors", async () => { mockedNetwork.mockResolvedValueOnce( getMockResponse({ errors: [{ message: "Invalid timestamp range" }], }), ); await expect( hgraphClient.getERC20TransfersByTimestampRange({ startTimestamp: "1000.000000000", endTimestamp: "2000.000000000", }), ).rejects.toThrow(); }); });