[price-service/client] Add e2 test (#559)

This commit is contained in:
Ali Behjati 2023-02-06 10:04:52 +01:00 committed by GitHub
parent fc08ec277e
commit 3f7cffd5b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 262 additions and 2 deletions

View File

@ -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"]
)

View File

@ -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"]

View File

@ -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\"",

View File

@ -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);
}
});
});

View File

@ -6,6 +6,7 @@ export {
export {
HexString,
PriceFeedMetadata,
PriceFeed,
Price,
UnixTimestamp,

View File

@ -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:

View File

@ -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