-
Notifications
You must be signed in to change notification settings - Fork 0
Testing
Tests run with Vitest (npm test → vitest run). Configuration: vitest.config.ts includes tests/**/*.test.ts with a 30-second timeout and .ts extension priority.
There is no tests/helpers/mock-server.test.ts — the helpers are support code consumed by the integration tests.
tests/
├── helpers/
│ └── mock-server.ts # fetch mock + MCP-over-HTTP test harness
└── integration/
├── config.test.ts # resolveConfig resolution ladder
├── errors.test.ts # status/network mappers
├── server.test.ts # /health, initialize, tools/list, invalid session
└── tools.test.ts # all four tools: success + 401 paths
There are no unit tests per tool file and no stdio entrypoint tests — coverage is integration-level, exercising tools through the real HTTP transport.
Provides two layers of fakes:
Monkeypatches globalThis.fetch with a vi.fn that matches request URLs against registered handlers (string includes or RegExp). Supports:
-
respond(pattern, response)— persistent handler. -
respondOnce(pattern, response)— one-shot (setsconsumed = true). -
restore()— restores the originalfetch. -
callCount()— number of intercepted fetch calls.
Responses are wrapped in a real Response with Content-Type: application/json plus any extra headers (used to test the 429 Retry-After path in errors.test.ts).
startMcpTestServer(app) spins the Express app up on an ephemeral port and exposes:
-
request(mcpReq)— POSTs a JSON-RPC request to/mcpwithAccept: application/json, text/event-streamand tracksmcp-session-id. Parses SSEdata:lines viaparseSse. -
initSession(server)— sendsinitialize(protocolVersion2025-03-26) thennotifications/initialized. -
callTool(server, name, args)— sendstools/calland returnsresult, throwing onres.error.
This lets tests drive the server exactly as a real MCP client would, without spawning a process.
Integration tests call createApp(TEST_CONFIG) directly, where TEST_CONFIG is a fixed object with apiKey, apiUrl, port, and configSource: "env". This bypasses resolveConfig, so tests are deterministic regardless of the host's ~/.chronova.cfg.
-
server.test.ts—/healthreturns{ status: "ok", version: "1.1.1" };initializereturnsserverInfo.name = "chronova-mcp"andversion = "1.1.1";tools/listreturns exactly 4 tools with the expected sorted names; every tool hasannotations.readOnlyHint: trueand aninputSchema.type = "object"; an unknownMcp-Session-Idyields HTTP 400 with "Invalid or expired session ID". -
tools.test.ts— for each tool: a happy path asserting parsed JSON content, a 401 path assertingisError: trueand the "Unauthorized" message; plus parameter-passthrough checks (e.g.get_productivity_summarywithproject,get_recent_activitywith filters/pagination). -
config.test.ts—resolveConfigpriority: env wins over~/.chronova.cfg, which wins over~/.wakatime.cfg, which wins overnone; uses injectedreadFile/getHomeDir/envso no real filesystem access. -
errors.test.ts—mapHttpStatusToErrorfor 401/404/429/5xx/generic; 429retryAfterfromRetry-Afterand fromX-RateLimit-Reset;mapNetworkErrorproducesCONNECTION_ERROR.
npm test # vitest run (CI mode)
npx vitest # watch mode
npm run type-check # tsc --noEmit, no testsNo test runner script is needed beyond vitest run; there is no separate e2e suite or coverage threshold configured.