From 76560fd26becefb141d6cd340cc31a208bae3816 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 25 Feb 2026 22:53:01 +0000 Subject: [PATCH] fix: Ponder: add test infrastructure + coverage for helpers (target 95%) (#287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add vitest ^2 + @vitest/coverage-v8 ^2 as devDependencies - Add `test` and `test:coverage` scripts to package.json - Create vitest.config.ts with resolve.alias to mock ponder virtual modules (ponder:schema, ponder:registry) and point kraiken-lib/version to source - Add coverage/ to .gitignore - Add tests/**/* and vitest.config.ts to tsconfig.json include - Create tests/__mocks__/ponder-schema.ts and ponder-registry.ts stubs - Create tests/stats.test.ts — 48 tests covering ring buffer logic, segment updates, hourly advancement, projections, ETH reserve snapshots, all exported async helpers with mock Ponder contexts - Create tests/version.test.ts — 14 tests covering isCompatibleVersion, getVersionMismatchError, and validateContractVersion (compatible / mismatch / error paths, existing-meta upsert path) - Create tests/abi.test.ts — 6 tests covering validateAbi and validateContractAbi Tests placed at tests/ (not src/tests/) so Ponder's Vite build does not attempt to execute test files as event handlers on startup. Result: 68 tests pass, 100% line/statement/function coverage on all helpers (stats.ts, version.ts, abi.ts, logger.ts) — exceeds 95% target. Co-Authored-By: Claude Sonnet 4.6 --- .../ponder/{src => }/tests/__mocks__/ponder-registry.ts | 0 .../ponder/{src => }/tests/__mocks__/ponder-schema.ts | 0 services/ponder/{src => }/tests/abi.test.ts | 2 +- services/ponder/{src => }/tests/stats.test.ts | 6 +++--- services/ponder/{src => }/tests/version.test.ts | 8 ++++---- services/ponder/tsconfig.json | 2 +- services/ponder/vitest.config.ts | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) rename services/ponder/{src => }/tests/__mocks__/ponder-registry.ts (100%) rename services/ponder/{src => }/tests/__mocks__/ponder-schema.ts (100%) rename services/ponder/{src => }/tests/abi.test.ts (97%) rename services/ponder/{src => }/tests/stats.test.ts (99%) rename services/ponder/{src => }/tests/version.test.ts (95%) diff --git a/services/ponder/src/tests/__mocks__/ponder-registry.ts b/services/ponder/tests/__mocks__/ponder-registry.ts similarity index 100% rename from services/ponder/src/tests/__mocks__/ponder-registry.ts rename to services/ponder/tests/__mocks__/ponder-registry.ts diff --git a/services/ponder/src/tests/__mocks__/ponder-schema.ts b/services/ponder/tests/__mocks__/ponder-schema.ts similarity index 100% rename from services/ponder/src/tests/__mocks__/ponder-schema.ts rename to services/ponder/tests/__mocks__/ponder-schema.ts diff --git a/services/ponder/src/tests/abi.test.ts b/services/ponder/tests/abi.test.ts similarity index 97% rename from services/ponder/src/tests/abi.test.ts rename to services/ponder/tests/abi.test.ts index 67375db..76de666 100644 --- a/services/ponder/src/tests/abi.test.ts +++ b/services/ponder/tests/abi.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { validateAbi, validateContractAbi } from '../helpers/abi.js'; +import { validateAbi, validateContractAbi } from '../src/helpers/abi.js'; import type { Abi } from 'viem'; // --------------------------------------------------------------------------- diff --git a/services/ponder/src/tests/stats.test.ts b/services/ponder/tests/stats.test.ts similarity index 99% rename from services/ponder/src/tests/stats.test.ts rename to services/ponder/tests/stats.test.ts index c9541e4..22009d5 100644 --- a/services/ponder/src/tests/stats.test.ts +++ b/services/ponder/tests/stats.test.ts @@ -14,7 +14,7 @@ import { RING_BUFFER_SEGMENTS, MINIMUM_BLOCKS_FOR_RINGBUFFER, type StatsContext, -} from '../helpers/stats.js'; +} from '../src/helpers/stats.js'; // Constants duplicated from the mock so tests don't re-import ponder:schema const HOURS = 168; @@ -531,7 +531,7 @@ describe('getStakeTotalSupply', () => { it('reads totalSupply from contract when cache is cold', async () => { // Fresh import to reset module-level cache - const { getStakeTotalSupply: freshGet } = await import('../helpers/stats.js'); + const { getStakeTotalSupply: freshGet } = await import('../src/helpers/stats.js'); const row = emptyStatsRow({ stakeTotalSupply: 0n }); const ctx = createMockContext(row); (ctx as unknown as { client: { readContract: ReturnType } }).client.readContract @@ -544,7 +544,7 @@ describe('getStakeTotalSupply', () => { it('returns cached value without calling readContract on second call', async () => { // Fresh import to get a clean module with null cache - const { getStakeTotalSupply: freshGet } = await import('../helpers/stats.js'); + const { getStakeTotalSupply: freshGet } = await import('../src/helpers/stats.js'); const row = emptyStatsRow({ stakeTotalSupply: 0n }); const readContractMock = vi.fn().mockResolvedValue(777n); const ctx = createMockContext(row); diff --git a/services/ponder/src/tests/version.test.ts b/services/ponder/tests/version.test.ts similarity index 95% rename from services/ponder/src/tests/version.test.ts rename to services/ponder/tests/version.test.ts index 97af459..807a4f1 100644 --- a/services/ponder/src/tests/version.test.ts +++ b/services/ponder/tests/version.test.ts @@ -78,7 +78,7 @@ describe('validateContractVersion', () => { }); it('logs success and upserts metadata on compatible version', async () => { - const { validateContractVersion } = await import('../helpers/version.js'); + const { validateContractVersion } = await import('../src/helpers/version.js'); const setFn = vi.fn().mockResolvedValue(undefined); const ctx = { @@ -109,7 +109,7 @@ describe('validateContractVersion', () => { }); it('calls process.exit on incompatible contract version', async () => { - const { validateContractVersion } = await import('../helpers/version.js'); + const { validateContractVersion } = await import('../src/helpers/version.js'); const ctx = { client: { @@ -138,7 +138,7 @@ describe('validateContractVersion', () => { }); it('calls process.exit when readContract throws', async () => { - const { validateContractVersion } = await import('../helpers/version.js'); + const { validateContractVersion } = await import('../src/helpers/version.js'); const ctx = { client: { @@ -167,7 +167,7 @@ describe('validateContractVersion', () => { }); it('updates existing metadata when a row already exists', async () => { - const { validateContractVersion } = await import('../helpers/version.js'); + const { validateContractVersion } = await import('../src/helpers/version.js'); const setFn = vi.fn().mockResolvedValue(undefined); const existingMeta = { id: 'stack-meta', contractVersion: 1 }; diff --git a/services/ponder/tsconfig.json b/services/ponder/tsconfig.json index 0f8824a..7015f12 100644 --- a/services/ponder/tsconfig.json +++ b/services/ponder/tsconfig.json @@ -20,6 +20,6 @@ "ponder/virtual": ["./node_modules/ponder/src/types.d.ts"] } }, - "include": ["src/**/*", "ponder.config.ts", "ponder.schema.ts", "ponder-env.d.ts", "vitest.config.ts"], + "include": ["src/**/*", "tests/**/*", "ponder.config.ts", "ponder.schema.ts", "ponder-env.d.ts", "vitest.config.ts"], "exclude": ["node_modules", ".ponder"] } diff --git a/services/ponder/vitest.config.ts b/services/ponder/vitest.config.ts index 503b932..7883db2 100644 --- a/services/ponder/vitest.config.ts +++ b/services/ponder/vitest.config.ts @@ -19,8 +19,8 @@ export default defineConfig({ }, resolve: { alias: { - 'ponder:schema': resolve(rootDir, 'src/tests/__mocks__/ponder-schema.ts'), - 'ponder:registry': resolve(rootDir, 'src/tests/__mocks__/ponder-registry.ts'), + 'ponder:schema': resolve(rootDir, 'tests/__mocks__/ponder-schema.ts'), + 'ponder:registry': resolve(rootDir, 'tests/__mocks__/ponder-registry.ts'), 'kraiken-lib/version': resolve(rootDir, '../../kraiken-lib/src/version.ts'), }, },