Skip to content

Commit 1c408ed

Browse files
authored
feat: support multiple clients (#6)
1 parent 7f2b6ab commit 1c408ed

File tree

12 files changed

+431
-43
lines changed

12 files changed

+431
-43
lines changed

resources/mock-server/Dockerfile

Lines changed: 0 additions & 4 deletions
This file was deleted.

resources/mock-server/__test__/__snapshots__/server.test.ts.snap

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3-
exports[`PollyJS Server > should work with the recorded status request 1`] = `
3+
exports[`Algod Mock Server > should work with the recorded status request 1`] = `
44
NodeStatusResponse {
55
"catchpoint": "",
66
"catchpointAcquiredBlocks": 0,
@@ -30,3 +30,38 @@ NodeStatusResponse {
3030
"upgradeYesVotes": undefined,
3131
}
3232
`;
33+
34+
exports[`Indexer Mock Server > should work with the recorded health check request 1`] = `
35+
HealthCheck {
36+
"data": UntypedValue {
37+
"data": Map {
38+
"migration-required" => false,
39+
"read-only-mode" => true,
40+
},
41+
},
42+
"dbAvailable": true,
43+
"errors": undefined,
44+
"isMigrating": false,
45+
"message": "0",
46+
"round": 0n,
47+
"version": "3.9.0",
48+
}
49+
`;
50+
51+
exports[`KMD Mock Server > should work with the recorded list wallets request 1`] = `
52+
{
53+
"wallets": [
54+
{
55+
"driver_name": "sqlite",
56+
"driver_version": 1,
57+
"id": "0bd36782f30300ed34c0e12a4476736c",
58+
"mnemonic_ux": false,
59+
"name": "unencrypted-default-wallet",
60+
"supported_txs": [
61+
"pay",
62+
"keyreg",
63+
],
64+
},
65+
],
66+
}
67+
`;
Lines changed: 124 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import { describe, it, expect, beforeAll, afterAll } from "vitest";
22
import { startServer, type ServerInstance } from "../src/server";
3-
import { Algodv2 } from "algosdk";
3+
import { Algodv2, Indexer, Kmd } from "algosdk";
44

5-
const NO_RECORDING = "Recording for the following request is not found";
5+
const PollyError = "PollyError";
66

7-
describe("PollyJS Server", () => {
8-
let server: ServerInstance;
7+
describe("Algod Mock Server", () => {
8+
let algodServer: ServerInstance;
99
let algodClient: Algodv2;
1010

1111
beforeAll(async () => {
12-
server = await startServer();
13-
algodClient = new Algodv2("a".repeat(64), "http://localhost", server.port);
12+
algodServer = await startServer("algod");
13+
algodClient = new Algodv2(
14+
"a".repeat(64),
15+
"http://localhost",
16+
algodServer.port
17+
);
1418
});
1519

1620
it("should work with the recorded status request", async () => {
@@ -19,15 +23,125 @@ describe("PollyJS Server", () => {
1923
});
2024

2125
it("should fail with unrecorded endpoint", async () => {
22-
await expect(algodClient.genesis().do()).rejects.toThrowError(NO_RECORDING);
26+
try {
27+
await algodClient.genesis().do();
28+
} catch (error: any) {
29+
expect(Buffer.from(error.response.body).toString()).toContain(PollyError);
30+
return;
31+
}
32+
33+
expect.unreachable("PollyJS should have thrown an error");
34+
});
35+
36+
it("should fail with a different header", async () => {
37+
const client = new Algodv2(
38+
"b".repeat(64),
39+
"http://localhost",
40+
algodServer.port
41+
);
42+
try {
43+
await client.status().do();
44+
} catch (error: any) {
45+
expect(Buffer.from(error.response.body).toString()).toContain(PollyError);
46+
return;
47+
}
48+
49+
expect.unreachable("PollyJS should have thrown an error");
50+
});
51+
52+
afterAll(async () => {
53+
await algodServer.close();
54+
});
55+
});
56+
57+
describe("Indexer Mock Server", () => {
58+
let indexerServer: ServerInstance;
59+
let indexerClient: Indexer;
60+
61+
beforeAll(async () => {
62+
indexerServer = await startServer("indexer");
63+
indexerClient = new Indexer(
64+
"a".repeat(64),
65+
"http://localhost",
66+
indexerServer.port
67+
);
68+
});
69+
70+
it("should work with the recorded health check request", async () => {
71+
const status = await indexerClient.makeHealthCheck().do();
72+
expect(status).matchSnapshot();
73+
});
74+
75+
it("should fail with unrecorded endpoint", async () => {
76+
try {
77+
await indexerClient.lookupBlock(1000).do();
78+
} catch (error: any) {
79+
expect(Buffer.from(error.response.body).toString()).toContain(PollyError);
80+
return;
81+
}
82+
83+
expect.unreachable("PollyJS should have thrown an error");
2384
});
2485

2586
it("should fail with a different header", async () => {
26-
const client = new Algodv2("b".repeat(64), "http://localhost", server.port);
27-
await expect(client.status().do()).rejects.toThrowError(NO_RECORDING);
87+
const client = new Indexer(
88+
"b".repeat(64),
89+
"http://localhost",
90+
indexerServer.port
91+
);
92+
try {
93+
await client.makeHealthCheck().do();
94+
} catch (error: any) {
95+
expect(Buffer.from(error.response.body).toString()).toContain(PollyError);
96+
return;
97+
}
98+
99+
expect.unreachable("PollyJS should have thrown an error");
100+
});
101+
102+
afterAll(async () => {
103+
await indexerServer.close();
104+
});
105+
});
106+
107+
describe("KMD Mock Server", () => {
108+
let kmdServer: ServerInstance;
109+
let kmdClient: Kmd;
110+
111+
beforeAll(async () => {
112+
kmdServer = await startServer("kmd");
113+
kmdClient = new Kmd("a".repeat(64), "http://localhost", kmdServer.port);
114+
});
115+
116+
it("should work with the recorded list wallets request", async () => {
117+
const status = await kmdClient.listWallets();
118+
expect(status).matchSnapshot();
119+
});
120+
121+
it("should fail with unrecorded endpoint", async () => {
122+
try {
123+
await kmdClient.versions();
124+
} catch (error: any) {
125+
expect(Buffer.from(error.response.body).toString()).toContain(PollyError);
126+
return;
127+
}
128+
129+
expect.unreachable("PollyJS should have thrown an error");
130+
});
131+
132+
it("should fail with a different header", async () => {
133+
const client = new Kmd("b".repeat(64), "http://localhost", kmdServer.port);
134+
try {
135+
await client.listWallets();
136+
} catch (error: any) {
137+
expect(Buffer.from(error.response.body).toString()).toContain(PollyError);
138+
return;
139+
}
140+
141+
expect.unreachable("PollyJS should have thrown an error");
28142
});
29143

30144
afterAll(async () => {
31-
await server.close();
145+
await kmdServer.close();
32146
});
33147
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM oven/bun:1
2+
COPY . .
3+
RUN bun install
4+
ENTRYPOINT ["bun", "bin/server.ts", "algod"]
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import type { Client } from "../src/index.ts";
12
import { startServer } from "../src/server.ts";
23

3-
const server = await startServer();
4+
const client = process.argv[2];
5+
const server = await startServer(client as Client);
6+
47
await server.listen;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM oven/bun:1
2+
COPY . .
3+
RUN bun install
4+
ENTRYPOINT ["bun", "bin/server.ts", "indexer"]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM oven/bun:1
2+
COPY . .
3+
RUN bun install
4+
ENTRYPOINT ["bun", "bin/server.ts", "kmd"]
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{
2+
"log": {
3+
"_recordingName": "indexer",
4+
"creator": {
5+
"comment": "persister:fs",
6+
"name": "Polly.JS",
7+
"version": "6.0.6"
8+
},
9+
"entries": [
10+
{
11+
"_id": "2cf0c19c224110cc57afdd9a36893017",
12+
"_order": 0,
13+
"cache": {},
14+
"request": {
15+
"bodySize": 0,
16+
"cookies": [],
17+
"headers": [
18+
{
19+
"name": "accept",
20+
"value": "application/json"
21+
},
22+
{
23+
"name": "x-indexer-api-token",
24+
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
25+
}
26+
],
27+
"headersSize": 158,
28+
"httpVersion": "HTTP/1.1",
29+
"method": "GET",
30+
"queryString": [],
31+
"url": "http://localhost:8980/health"
32+
},
33+
"response": {
34+
"bodySize": 143,
35+
"content": {
36+
"mimeType": "application/json",
37+
"size": 143,
38+
"text": "{\"data\":{\"migration-required\":false,\"read-only-mode\":true},\"db-available\":true,\"is-migrating\":false,\"message\":\"0\",\"round\":0,\"version\":\"3.9.0\"}\n"
39+
},
40+
"cookies": [],
41+
"headers": [
42+
{
43+
"name": "connection",
44+
"value": "keep-alive"
45+
},
46+
{
47+
"name": "content-length",
48+
"value": "143"
49+
},
50+
{
51+
"name": "content-type",
52+
"value": "application/json"
53+
},
54+
{
55+
"name": "date",
56+
"value": "Wed, 12 Nov 2025 18:52:25 GMT"
57+
},
58+
{
59+
"name": "vary",
60+
"value": "Origin"
61+
}
62+
],
63+
"headersSize": 130,
64+
"httpVersion": "HTTP/1.1",
65+
"redirectURL": "",
66+
"status": 200,
67+
"statusText": "OK"
68+
},
69+
"startedDateTime": "2025-11-12T18:52:25.069Z",
70+
"time": 13,
71+
"timings": {
72+
"blocked": -1,
73+
"connect": -1,
74+
"dns": -1,
75+
"receive": 0,
76+
"send": 0,
77+
"ssl": -1,
78+
"wait": 13
79+
}
80+
}
81+
],
82+
"pages": [],
83+
"version": "1.2"
84+
}
85+
}

0 commit comments

Comments
 (0)