[price-service/client] Add e2 test (#559)
This commit is contained in:
parent
fc08ec277e
commit
3f7cffd5b2
13
Tiltfile
13
Tiltfile
|
@ -338,3 +338,16 @@ k8s_resource(
|
|||
labels = ["solana"],
|
||||
trigger_mode = trigger_mode,
|
||||
)
|
||||
|
||||
# Pyth Price Client JS e2e test
|
||||
docker_build(
|
||||
ref = "pyth-price-client-js",
|
||||
context = ".",
|
||||
dockerfile = "price_service/client/js/Dockerfile",
|
||||
)
|
||||
k8s_yaml_with_ns("tilt_devnet/k8s/pyth-price-client-js.yaml")
|
||||
k8s_resource(
|
||||
"pyth-price-client-js",
|
||||
resource_deps = ["pyth-price-server"],
|
||||
labels = ["pyth"]
|
||||
)
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Defined in tilt_devnet/docker_images/Dockerfile.lerna
|
||||
FROM lerna
|
||||
|
||||
USER root
|
||||
RUN apt-get update && apt-get install -y ncat
|
||||
|
||||
WORKDIR /home/node/
|
||||
USER 1000
|
||||
|
||||
COPY --chown=1000:1000 price_service/client/js price_service/client/js
|
||||
COPY --chown=1000:1000 price_service/sdk/js price_service/sdk/js
|
||||
|
||||
RUN npx lerna run build --scope="@pythnetwork/price-service-client" --include-dependencies
|
||||
|
||||
WORKDIR /home/node/price_service/client/js
|
||||
|
||||
ENTRYPOINT ["npm"]
|
|
@ -13,7 +13,8 @@
|
|||
],
|
||||
"repository": "https://github.com/pyth-network/pyth-crosschain",
|
||||
"scripts": {
|
||||
"test": "jest --passWithNoTests",
|
||||
"test": "jest --testPathIgnorePatterns=.*.e2e.test.ts --passWithNoTests",
|
||||
"test:e2e": "jest --testPathPattern=.*.e2e.test.ts",
|
||||
"build": "tsc",
|
||||
"example": "npm run build && node lib/examples/PriceServiceClient.js",
|
||||
"format": "prettier --write \"src/**/*.ts\"",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -6,6 +6,7 @@ export {
|
|||
|
||||
export {
|
||||
HexString,
|
||||
PriceFeedMetadata,
|
||||
PriceFeed,
|
||||
Price,
|
||||
UnixTimestamp,
|
||||
|
|
|
@ -114,7 +114,7 @@ mapping_reload_interval_mins: 1 # Very fast for testing purposes
|
|||
min_rpc_interval_ms: 0 # RIP RPC
|
||||
max_batch_jobs: 1000 # Where we're going there's no oomkiller
|
||||
default_attestation_conditions:
|
||||
min_interval_secs: 60
|
||||
min_interval_secs: 10
|
||||
symbol_groups:
|
||||
- group_name: fast_interval_only
|
||||
conditions:
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pyth-price-client-js
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: pyth-price-client-js
|
||||
serviceName: pyth-price-client-js
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: pyth-price-client-js
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 0
|
||||
containers:
|
||||
- name: tests
|
||||
image: pyth-price-client-js
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- "npm run test:e2e && nc -lk 0.0.0.0 2000"
|
||||
readinessProbe:
|
||||
periodSeconds: 5
|
||||
failureThreshold: 300
|
||||
tcpSocket:
|
||||
port: 2000
|
||||
resources:
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 1Gi
|
Loading…
Reference in New Issue