Skip to content

Commit 26e6a97

Browse files
hvasconcelosvhcsilvaVitor Hugoclaude
authored
Add comprehensive unit tests for Chain Cast (#17)
* Working * Merge pull request #12 from layerx-labs/hcv_migration_to_bun Migration to Bun Runtime * Replace dappkit and ethers with viem (#13) * Bun Runtime * ChainCast * ci * ci-cd * Opus is a beast 😱 * [TKAI-4349] create a webhook to send swap events tkai bepro to zapier (#14) * chore: add missing dependency * feat: add json parse instruction * refactor: fix template args * refactor: add lib to convert bigint to string on objects * refactor: undo change * chore: remove web3 * refactor: use viem instead of web3 * feat: improve VM error logging with context and backtrace Add detailed error and halt logging to the Virtual Machine with: - Cast ID, event name, and block number context - Instruction that caused the halt/error - Full backtrace of executed instructions with args - Differentiation between errors and intentional halts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Vitor Hugo <vhcsilva@gmail.com> Co-authored-by: Helder Vasconcelos <heldervasc@bearstouch.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * Fix CI: upgrade setup-bun to v2 and codecov-action to v5 - setup-bun v1 has caching bugs causing HTTP 400 errors - codecov-action v5 is the latest stable version 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Upload * Add unit tests for mocks, utilities, and instructions - Add mock utilities for Prisma, viem client, external services, and logging - Add test fixtures for events, casts, and programs - Add unit tests for utility functions (getVariableFromPath) - Add unit tests for core library (Stack, ChainCastProgram, ChainCastVirtualMachine) - Add unit tests for all 13 instruction plugins: - debug, set, filter-events, condition - transform-string, transform-number, transform-array, transform-object, transform-template - webhook, bullmq, elasticsearch, spreadsheet Tests: 286 passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Add test scripts and CI/CD test execution - Add test:watch script for continuous test running during development - Add test:debug script with --inspect-brk for debugging tests - Update CI/CD pipeline to run tests after build - Add coverage reporting to Codecov (no threshold enforcement) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Add service tests for ChainCastSecretManager - Add comprehensive tests for secret manager CRUD operations - Test addSecrets, addSecret, deleteSecret, updateSecret, getSecret, getSecrets - Add integration scenario tests for typical cast workflows Tests: 302 passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Add contract cast, listener, retriever, manager and GraphQL resolver tests - Create contract-cast.test.ts with tests for EVMContractCast - Constructor, getters, loadSecrets, loadProgram - Status management, onEvent handling - Event recovery progress tracking - Create contract-listener.test.ts with tests for EVMContractListener - Event extraction from ABI, startListening/stopListening - Handler management and connection callbacks - Create contract-event-retriever.test.ts with tests for EVMContractEventRetriever - Batch event fetching and recovery - Progress reporting and stop condition handling - Event format conversion (viem to Web3Event) - Create chaincast-manager.test.ts with tests for ChainCastManager - Instruction registration - Cast lifecycle management (start, stop, add, delete, restart) - Database integration - Create GraphQL resolver tests - contract-cast.test.ts: contractCast query, CRUD mutations - secret.test.ts: create, update, delete secret mutations Total: 410 tests passing, 86.05% line coverage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fixed * Fix program tests for cross-platform compatibility Move Zod schemas outside mock classes and use explicit constructors to ensure proper initialization order across different Bun environments (macOS vs Linux). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix program tests: use actual instruction classes Replace mock instruction classes with actual Debug and Set instruction classes from the codebase to ensure cross-platform compatibility. The mock classes had constructor/property initialization issues that caused different behavior on Linux vs macOS in Bun. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix cross-platform base64 encoding in program tests - Replace btoa() with Buffer.from().toString('base64') in tests - Change toString('ascii') to toString('utf-8') in program decoder - Add encodeInstructions() helper for consistent encoding The btoa/ascii combination behaved inconsistently between macOS and Linux in GitHub Actions. Using Buffer with utf-8 encoding ensures reliable cross-platform behavior. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fixed --------- Co-authored-by: Vitor Hugo <9003126+vhcsilva@users.noreply.github.com> Co-authored-by: Vitor Hugo <vhcsilva@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent fbc4cd6 commit 26e6a97

36 files changed

+7521
-4
lines changed

.github/workflows/ci-cd.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ jobs:
3232
- name: Build project
3333
run: bun run build
3434

35+
- name: Run tests
36+
run: bun test
37+
38+
- name: Run tests with coverage
39+
run: bun test --coverage
40+
41+
- name: Upload coverage to Codecov
42+
uses: codecov/codecov-action@v5
43+
with:
44+
fail_ci_if_error: false
45+
3546
docker:
3647
runs-on: ubuntu-latest
3748
needs: test

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
"start": "NODE_ENV=production bun src/main.ts",
1010
"build": "bun install && bunx prisma generate",
1111
"test": "bun test",
12+
"test:watch": "bun test --watch",
1213
"test:coverage": "bun test --coverage",
14+
"test:debug": "bun test --inspect-brk",
1315
"lint": "biome lint .",
1416
"lint:fix": "biome lint --write .",
1517
"lint:unsafe": "biome lint --write --unsafe .",
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import { mock } from 'bun:test';
2+
3+
/**
4+
* Mock for axios HTTP client
5+
*/
6+
export const mockAxios = {
7+
post: mock(() =>
8+
Promise.resolve({
9+
status: 200,
10+
statusText: 'OK',
11+
data: { success: true },
12+
headers: {},
13+
config: {},
14+
})
15+
),
16+
get: mock(() =>
17+
Promise.resolve({
18+
status: 200,
19+
statusText: 'OK',
20+
data: {},
21+
headers: {},
22+
config: {},
23+
})
24+
),
25+
put: mock(() =>
26+
Promise.resolve({
27+
status: 200,
28+
statusText: 'OK',
29+
data: {},
30+
headers: {},
31+
config: {},
32+
})
33+
),
34+
delete: mock(() =>
35+
Promise.resolve({
36+
status: 200,
37+
statusText: 'OK',
38+
data: {},
39+
headers: {},
40+
config: {},
41+
})
42+
),
43+
create: mock(() => mockAxios),
44+
defaults: {
45+
headers: {
46+
common: {},
47+
},
48+
},
49+
};
50+
51+
/**
52+
* Mock for Elasticsearch client
53+
*/
54+
export const mockElasticsearchClient = {
55+
index: mock(() =>
56+
Promise.resolve({
57+
_index: 'test-index',
58+
_id: 'mock-doc-id',
59+
_version: 1,
60+
result: 'created',
61+
})
62+
),
63+
search: mock(() =>
64+
Promise.resolve({
65+
hits: {
66+
total: { value: 0 },
67+
hits: [],
68+
},
69+
})
70+
),
71+
delete: mock(() =>
72+
Promise.resolve({
73+
result: 'deleted',
74+
})
75+
),
76+
bulk: mock(() =>
77+
Promise.resolve({
78+
errors: false,
79+
items: [],
80+
})
81+
),
82+
ping: mock(() => Promise.resolve(true)),
83+
};
84+
85+
/**
86+
* Creates a mock Elasticsearch Client class
87+
*/
88+
export function createMockElasticsearchClient() {
89+
return mockElasticsearchClient;
90+
}
91+
92+
/**
93+
* Mock BullMQ Queue class
94+
*/
95+
export class MockQueue {
96+
name: string;
97+
98+
constructor(name: string) {
99+
this.name = name;
100+
}
101+
102+
add = mock((jobName: string, data: unknown) =>
103+
Promise.resolve({
104+
id: 'mock-job-id',
105+
name: jobName,
106+
data,
107+
timestamp: Date.now(),
108+
})
109+
);
110+
111+
addBulk = mock((jobs: Array<{ name: string; data: unknown }>) =>
112+
Promise.resolve(
113+
jobs.map((job, i) => ({
114+
id: `mock-job-id-${i}`,
115+
name: job.name,
116+
data: job.data,
117+
timestamp: Date.now(),
118+
}))
119+
)
120+
);
121+
122+
close = mock(() => Promise.resolve());
123+
pause = mock(() => Promise.resolve());
124+
resume = mock(() => Promise.resolve());
125+
126+
getJobs = mock(() => Promise.resolve([]));
127+
getJobCounts = mock(() =>
128+
Promise.resolve({
129+
waiting: 0,
130+
active: 0,
131+
completed: 0,
132+
failed: 0,
133+
delayed: 0,
134+
})
135+
);
136+
}
137+
138+
/**
139+
* Mock Google Sheets API
140+
*/
141+
export const mockGoogleSheetsApi = {
142+
spreadsheets: {
143+
values: {
144+
append: mock(() =>
145+
Promise.resolve({
146+
data: {
147+
spreadsheetId: 'mock-spreadsheet-id',
148+
tableRange: 'Sheet1!A1:D1',
149+
updates: {
150+
updatedRows: 1,
151+
updatedColumns: 4,
152+
updatedCells: 4,
153+
},
154+
},
155+
})
156+
),
157+
get: mock(() =>
158+
Promise.resolve({
159+
data: {
160+
values: [],
161+
},
162+
})
163+
),
164+
update: mock(() =>
165+
Promise.resolve({
166+
data: {
167+
updatedRows: 1,
168+
},
169+
})
170+
),
171+
},
172+
},
173+
};
174+
175+
/**
176+
* Mock for google.auth.GoogleAuth
177+
*/
178+
export class MockGoogleAuth {
179+
credentials: unknown;
180+
181+
constructor(options: { credentials: unknown; scopes: string[] }) {
182+
this.credentials = options.credentials;
183+
}
184+
185+
getClient = mock(() => Promise.resolve({}));
186+
}
187+
188+
/**
189+
* Mock for googleapis.google.sheets
190+
*/
191+
export const mockGoogleSheets = mock(() => mockGoogleSheetsApi);
192+
193+
/**
194+
* Mock fetch function for webhook tests
195+
*/
196+
export const mockFetch = mock(() =>
197+
Promise.resolve({
198+
ok: true,
199+
status: 200,
200+
statusText: 'OK',
201+
json: () => Promise.resolve({ success: true }),
202+
text: () => Promise.resolve('OK'),
203+
})
204+
);
205+
206+
/**
207+
* Mock Redis connection for BullMQ
208+
*/
209+
export const mockRedisConnection = {
210+
host: 'localhost',
211+
port: 6379,
212+
};

0 commit comments

Comments
 (0)