|
|
@@ -0,0 +1,196 @@
|
|
|
+import {
|
|
|
+ DurationInMs,
|
|
|
+ Price,
|
|
|
+ PriceFeed,
|
|
|
+ PriceFeedMetadata,
|
|
|
+ PriceServiceConnection,
|
|
|
+} from "../index";
|
|
|
+
|
|
|
+async function sleep(duration: DurationInMs): Promise<void> {
|
|
|
+ return new Promise((res) => setTimeout(res, duration));
|
|
|
+}
|
|
|
+
|
|
|
+// The endpoint is set to the price service endpoint in Tilt.
|
|
|
+// 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://pyth-price-server:4200";
|
|
|
+
|
|
|
+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<string, number> = 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<string> = 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<string> = 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);
|
|
|
+ }
|
|
|
+ });
|
|
|
+});
|