Skip to content

Commit 4018959

Browse files
authored
feat: configure testnet recordings
* feat: configure testnet recording and bun debugging This enables debugging the recording process and captures real testnet API responses for mock server playback. Adds VS Code Bun debugger configuration for server * fix: updated launch debug file with client arg. Reran the har recordings * fix: changed url for testnet only calling * fix: remove problematic response headers during replay - Strip transfer-encoding and content-encoding headers in beforeReplay hook to prevent conflicts with content-length and decompression errors - Replace server tests with replay-focused tests - Update unrecorded endpoint test to use non-existent account address - Re-record HAR files with consistent API token format * refactor: separate recording and replay into distinct CLI tools - Split bin/server.ts (replay-only) and bin/record.ts (recording-only) - Update startServer() to remove mode parameter, always replays - Created tests for replay and recording - Update tests to use new API signatures - Add debug configurations for both tools * refactor: added recordings back to server start * refactor: record test to just test the record function Removed the bin record file since recording happens on every server start
1 parent b5b52ea commit 4018959

File tree

14 files changed

+3034
-69
lines changed

14 files changed

+3034
-69
lines changed

resources/mock-server/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
3232

3333
# Finder (MacOS) folder config
3434
.DS_Store
35+
36+
37+
.tmp/
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"type": "bun",
9+
"request": "launch",
10+
"name": "Debug Replay Server",
11+
"program": "${workspaceFolder}/bin/server.ts",
12+
"args": ["algod"], // Change to: algod, indexer, or kmd
13+
"cwd": "${workspaceFolder}",
14+
"stopOnEntry": false,
15+
"watchMode": false,
16+
"env": {
17+
"NODE_ENV": "development",
18+
"LOG_LEVEL": "debug"
19+
}
20+
},
21+
{
22+
"type": "bun",
23+
"request": "launch",
24+
"name": "Debug Record",
25+
"program": "${workspaceFolder}/bin/record.ts",
26+
"args": ["algod", "record-new"], // Change client: algod, indexer, kmd | mode: record-new, record-overwrite
27+
"cwd": "${workspaceFolder}",
28+
"stopOnEntry": false,
29+
"watchMode": false,
30+
"env": {
31+
"NODE_ENV": "development"
32+
}
33+
}
34+
]
35+
}

resources/mock-server/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,10 @@ To run:
1212
bun run index.ts
1313
```
1414

15+
## Debugging
16+
17+
To debug in VS Code/Cursor, install the [Bun extension](https://marketplace.visualstudio.com/items?itemName=oven.bun-vscode) (`oven.bun-vscode`), then press F5 to start the debugger.
18+
19+
---
20+
1521
This project was created using `bun init` in bun v1.2.15. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.

resources/mock-server/__test__/__snapshots__/server.test.ts.snap renamed to resources/mock-server/__test__/__snapshots__/replay.test.ts.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ NodeStatusResponse {
1313
"catchpointVerifiedKvs": 0,
1414
"catchupTime": 0n,
1515
"lastCatchpoint": "",
16-
"lastRound": 17n,
16+
"lastRound": 57490072n,
1717
"lastVersion": "https://github.com/algorandfoundation/specs/tree/953304de35264fc3ef91bcd05c123242015eeaed",
1818
"nextVersion": "https://github.com/algorandfoundation/specs/tree/953304de35264fc3ef91bcd05c123242015eeaed",
19-
"nextVersionRound": 18n,
19+
"nextVersionRound": 57490073n,
2020
"nextVersionSupported": true,
2121
"stoppedAtUnsupportedRound": false,
22-
"timeSinceLastRound": 152696395985404n,
22+
"timeSinceLastRound": 998402345n,
2323
"upgradeDelay": undefined,
2424
"upgradeNextProtocolVoteBefore": undefined,
2525
"upgradeNoVotes": undefined,
@@ -42,8 +42,8 @@ HealthCheck {
4242
"dbAvailable": true,
4343
"errors": undefined,
4444
"isMigrating": false,
45-
"message": "0",
46-
"round": 0n,
45+
"message": "57490591",
46+
"round": 57490591n,
4747
"version": "3.9.0",
4848
}
4949
`;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { describe, it, expect, afterEach } from "vitest";
2+
import { record } from "../src/index";
3+
import { Algodv2 } from "algosdk";
4+
import fs from "fs";
5+
import path from "path";
6+
7+
const TEST_RECORDINGS_DIR = path.resolve(__dirname, "../recordings-test");
8+
9+
describe("Recording Tests", () => {
10+
afterEach(async () => {
11+
// Clean up test recordings directory completely
12+
if (fs.existsSync(TEST_RECORDINGS_DIR)) {
13+
fs.rmSync(TEST_RECORDINGS_DIR, { recursive: true, force: true });
14+
}
15+
});
16+
17+
it("should record in record-new mode", async () => {
18+
// Create test recordings directory
19+
fs.mkdirSync(TEST_RECORDINGS_DIR, { recursive: true });
20+
21+
// Create recording with record-new using TestNet
22+
await record(
23+
"algod",
24+
async () => {
25+
const algod = new Algodv2(
26+
"a".repeat(64),
27+
"https://testnet-api.4160.nodely.dev",
28+
443
29+
);
30+
await algod.status().do();
31+
},
32+
"record-new",
33+
TEST_RECORDINGS_DIR
34+
);
35+
36+
// Verify HAR file was created
37+
const harFiles = fs.readdirSync(TEST_RECORDINGS_DIR, { recursive: true });
38+
const harFileName = harFiles.find((f) =>
39+
f.toString().endsWith("recording.har")
40+
);
41+
expect(harFileName).toBeDefined();
42+
43+
const harPath = path.join(TEST_RECORDINGS_DIR, harFileName!.toString());
44+
expect(fs.existsSync(harPath)).toBe(true);
45+
46+
// Verify HAR file has entries
47+
const content = fs.readFileSync(harPath, "utf-8");
48+
const har = JSON.parse(content);
49+
expect(har.log.entries.length).toBeGreaterThan(0);
50+
});
51+
});

resources/mock-server/__test__/server.test.ts renamed to resources/mock-server/__test__/replay.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { startServer, type ServerInstance } from "../src/server";
33
import { Algodv2, Indexer, Kmd } from "algosdk";
44

55
const PollyError = "PollyError";
6+
const NON_EXISTENT_ADDRESS = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ";
67

78
describe("Algod Mock Server", () => {
89
let algodServer: ServerInstance;
@@ -24,7 +25,7 @@ describe("Algod Mock Server", () => {
2425

2526
it("should fail with unrecorded endpoint", async () => {
2627
try {
27-
await algodClient.genesis().do();
28+
await algodClient.accountInformation(NON_EXISTENT_ADDRESS).do();
2829
} catch (error: any) {
2930
expect(Buffer.from(error.response.body).toString()).toContain(PollyError);
3031
return;
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import type { Client } from "../src/index.ts";
22
import { startServer } from "../src/server.ts";
33

4-
const client = process.argv[2];
5-
const server = await startServer(client as Client);
4+
const client = process.argv[2] as Client;
5+
const recordingsDir = process.argv[3];
66

7-
await server.listen;
7+
console.log(`Starting ${client} mock server in replay mode...`);
8+
if (recordingsDir) {
9+
console.log(`Recordings directory: ${recordingsDir}`);
10+
}
11+
12+
const server = await startServer(client, recordingsDir);
13+
14+
console.log(`✓ Server listening on port ${server.port}`);

resources/mock-server/bun.lock

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"name": "pollyjs-server",
66
"dependencies": {
77
"@pollyjs/adapter-fetch": "^6.0.7",
8-
"@pollyjs/adapter-node-http": "^6.0.6",
98
"@pollyjs/core": "^6.0.6",
109
"@pollyjs/persister-fs": "^6.0.6",
1110
"algosdk": "^3.5.2",
@@ -94,8 +93,6 @@
9493

9594
"@pollyjs/adapter-fetch": ["@pollyjs/[email protected]", "", { "dependencies": { "@pollyjs/adapter": "^6.0.6", "@pollyjs/utils": "^6.0.6", "to-arraybuffer": "^1.0.1" } }, "sha512-kv44DROx/2qzlcgS71EccGr2/I5nK40Xt92paGNI+1/Kmz290bw/ykt8cvXDg4O4xCc9Fh/jXeAkS7qwGpCx2g=="],
9695

97-
"@pollyjs/adapter-node-http": ["@pollyjs/[email protected]", "", { "dependencies": { "@pollyjs/adapter": "^6.0.6", "@pollyjs/utils": "^6.0.6", "lodash-es": "^4.17.21", "nock": "^13.2.1" } }, "sha512-jdJG7oncmSHZAtVMmRgOxh5A56b7G8H9ULlk/ZaVJ+jNrlFXhLmPpx8OQoSF4Cuq2ugdiWmwmAjFXHStcpY3Mw=="],
98-
9996
"@pollyjs/core": ["@pollyjs/[email protected]", "", { "dependencies": { "@pollyjs/utils": "^6.0.6", "@sindresorhus/fnv1a": "^2.0.1", "blueimp-md5": "^2.19.0", "fast-json-stable-stringify": "^2.1.0", "is-absolute-url": "^3.0.3", "lodash-es": "^4.17.21", "loglevel": "^1.8.0", "route-recognizer": "^0.3.4", "slugify": "^1.6.3" } }, "sha512-1ZZcmojW8iSFmvHGeLlvuudM3WiDV842FsVvtPAo3HoAYE6jCNveLHJ+X4qvonL4enj1SyTF3hXA107UkQFQrA=="],
10097

10198
"@pollyjs/node-server": ["@pollyjs/[email protected]", "", { "dependencies": { "@pollyjs/utils": "^6.0.6", "body-parser": "^1.19.0", "cors": "^2.8.5", "express": "^4.17.1", "fs-extra": "^10.0.0", "http-graceful-shutdown": "^3.1.5", "morgan": "^1.10.0", "nocache": "^3.0.1" } }, "sha512-nkP1+hdNoVOlrRz9R84haXVsaSmo8Xmq7uYK9GeUMSLQy4Fs55ZZ9o2KI6vRA8F6ZqJSbC31xxwwIoTkjyP7Vg=="],
@@ -352,8 +349,6 @@
352349

353350
"json-schema-traverse": ["[email protected]", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
354351

355-
"json-stringify-safe": ["[email protected]", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="],
356-
357352
"jsonfile": ["[email protected]", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
358353

359354
"light-my-request": ["[email protected]", "", { "dependencies": { "cookie": "^1.0.1", "process-warning": "^4.0.0", "set-cookie-parser": "^2.6.0" } }, "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A=="],
@@ -390,8 +385,6 @@
390385

391386
"nocache": ["[email protected]", "", {}, "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw=="],
392387

393-
"nock": ["[email protected]", "", { "dependencies": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", "propagate": "^2.0.0" } }, "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ=="],
394-
395388
"object-assign": ["[email protected]", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
396389

397390
"object-inspect": ["[email protected]", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
@@ -426,8 +419,6 @@
426419

427420
"process-warning": ["[email protected]", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
428421

429-
"propagate": ["[email protected]", "", {}, "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag=="],
430-
431422
"proxy-addr": ["[email protected]", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
432423

433424
"pump": ["[email protected]", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"folders": [
3+
{
4+
"name": "mock-server",
5+
"path": "."
6+
}
7+
],
8+
// Workspace-specific VS Code settings
9+
"settings": {
10+
// Path to the TypeScript language server to use for IntelliSense and type checking
11+
// Points to the workspace's local typescript package instead of VS Code's built-in version
12+
"typescript.tsdk": "node_modules/typescript/lib",
13+
14+
// When true, VS Code will prompt to use the workspace TypeScript version
15+
// This ensures consistency between CLI builds and editor type checking
16+
"typescript.enablePromptUseWorkspaceTsdk": true
17+
}
18+
}

0 commit comments

Comments
 (0)