import { DurationInMs, Price, PriceFeed, PriceFeedMetadata, PriceServiceConnection, } from "../index"; async function sleep(duration: DurationInMs): Promise { return new Promise((res) => setTimeout(res, duration)); } // The endpoint is set to the price service endpoint. // Please note that if you change it to a mainnet/testnet endpoint // some tests might fail due to the huge response size of a request. // i.e. requesting latest price feeds or vaas of all price ids. const PRICE_SERVICE_ENDPOINT = "http://127.0.0.1:7575"; describe("Test http endpoints", () => { test("Get price feed (without verbose/binary) works", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const priceFeeds = await connection.getLatestPriceFeeds(ids); expect(priceFeeds).toBeDefined(); expect(priceFeeds!.length).toEqual(ids.length); for (const priceFeed of priceFeeds!) { expect(priceFeed.id.length).toBe(64); // 32 byte address has size 64 in hex expect(priceFeed).toBeInstanceOf(PriceFeed); expect(priceFeed.getPriceUnchecked()).toBeInstanceOf(Price); expect(priceFeed.getEmaPriceUnchecked()).toBeInstanceOf(Price); expect(priceFeed.getMetadata()).toBeUndefined(); expect(priceFeed.getVAA()).toBeUndefined(); } }); test("Get price feed with verbose flag works", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT, { priceFeedRequestConfig: { verbose: true }, }); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const priceFeeds = await connection.getLatestPriceFeeds(ids); expect(priceFeeds).toBeDefined(); expect(priceFeeds!.length).toEqual(ids.length); for (const priceFeed of priceFeeds!) { expect(priceFeed.getMetadata()).toBeInstanceOf(PriceFeedMetadata); expect(priceFeed.getVAA()).toBeUndefined(); } }); test("Get price feed with binary flag works", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT, { priceFeedRequestConfig: { binary: true }, }); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const priceFeeds = await connection.getLatestPriceFeeds(ids); expect(priceFeeds).toBeDefined(); expect(priceFeeds!.length).toEqual(ids.length); for (const priceFeed of priceFeeds!) { expect(priceFeed.getMetadata()).toBeUndefined(); expect(priceFeed.getVAA()?.length).toBeGreaterThan(0); } }); test("Get latest vaa works", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT, { priceFeedRequestConfig: { binary: true }, }); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const vaas = await connection.getLatestVaas(ids); expect(vaas.length).toBeGreaterThan(0); for (const vaa of vaas) { expect(vaa.length).toBeGreaterThan(0); } }); test("Get vaa works", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT, { priceFeedRequestConfig: { binary: true }, }); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const publishTime10SecAgo = Math.floor(new Date().getTime() / 1000) - 10; const [vaa, vaaPublishTime] = await connection.getVaa( ids[0], publishTime10SecAgo ); expect(vaa.length).toBeGreaterThan(0); expect(vaaPublishTime).toBeGreaterThanOrEqual(publishTime10SecAgo); }); }); describe("Test websocket endpoints", () => { jest.setTimeout(60 * 1000); test.concurrent( "websocket subscription works without verbose and binary", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const counter: Map = new Map(); let totalCounter = 0; await connection.subscribePriceFeedUpdates(ids, (priceFeed) => { expect(priceFeed.id.length).toBe(64); // 32 byte address has size 64 in hex expect(priceFeed.getMetadata()).toBeUndefined(); expect(priceFeed.getVAA()).toBeUndefined(); counter.set(priceFeed.id, (counter.get(priceFeed.id) ?? 0) + 1); totalCounter += 1; }); // Wait for 30 seconds await sleep(30000); connection.closeWebSocket(); expect(totalCounter).toBeGreaterThan(30); for (const id of ids) { expect(counter.get(id)).toBeDefined(); // Make sure it receives more than 1 update expect(counter.get(id)).toBeGreaterThan(1); } } ); test.concurrent("websocket subscription works with verbose", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT, { priceFeedRequestConfig: { verbose: true }, }); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const observedFeeds: Set = new Set(); await connection.subscribePriceFeedUpdates(ids, (priceFeed) => { expect(priceFeed.getMetadata()).toBeInstanceOf(PriceFeedMetadata); expect(priceFeed.getVAA()).toBeUndefined(); observedFeeds.add(priceFeed.id); }); // Wait for 20 seconds await sleep(20000); await connection.unsubscribePriceFeedUpdates(ids); for (const id of ids) { expect(observedFeeds.has(id)).toBe(true); } }); test.concurrent("websocket subscription works with binary", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT, { priceFeedRequestConfig: { binary: true }, }); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const observedFeeds: Set = new Set(); await connection.subscribePriceFeedUpdates(ids, (priceFeed) => { expect(priceFeed.getMetadata()).toBeUndefined(); expect(priceFeed.getVAA()?.length).toBeGreaterThan(0); observedFeeds.add(priceFeed.id); }); // Wait for 20 seconds await sleep(20000); connection.closeWebSocket(); for (const id of ids) { expect(observedFeeds.has(id)).toBe(true); } }); // This test only works on Hermes and is not stable because there might // be no out of order updates. Hence the last check is commented out. test.concurrent( "websocket subscription works with allow out of order", async () => { const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT, { priceFeedRequestConfig: { allowOutOfOrder: true, verbose: true }, }); const ids = await connection.getPriceFeedIds(); expect(ids.length).toBeGreaterThan(0); const observedSlots: number[] = []; await connection.subscribePriceFeedUpdates(ids, (priceFeed) => { expect(priceFeed.getMetadata()).toBeDefined(); expect(priceFeed.getVAA()).toBeUndefined(); observedSlots.push(priceFeed.getMetadata()!.slot!); }); // Wait for 20 seconds await sleep(20000); connection.closeWebSocket(); let seenOutOfOrder = false; for (let i = 1; i < observedSlots.length; i++) { if (observedSlots[i] < observedSlots[i - 1]) { seenOutOfOrder = true; } } // expect(seenOutOfOrder).toBe(true); } ); });