Skip to content

CI: add MCP tool + e2e integration test coverage (Zod, Express, rate-limit) - #49

Merged
allocsys merged 1 commit into
mainfrom
test/mcp-integration-coverage
Jul 28, 2026
Merged

CI: add MCP tool + e2e integration test coverage (Zod, Express, rate-limit)#49
allocsys merged 1 commit into
mainfrom
test/mcp-integration-coverage

Conversation

@allocsys

Copy link
Copy Markdown
Owner

Closes the integration-test gap ahead of the pending zod 3->4, express 4->5, and express-rate-limit 7->8 bumps (see Notion entity_id madmcp-ci-integration-test-plan).

Gap this closes

The 6 existing test files only cover pure functions (github-client retry/backoff, security IP/CIDR + safeEqual, tar parsing, narrow validation files). Nothing registered an MCP tool via server.tool(...) and sent it through real Zod validation, nothing hit an Express route/middleware chain end to end, and nothing exercised express-rate-limit under real traffic.

What's added

  • server.js — refactor only, no behavior change: exports { app, mcpServer }, gates app.listen(...) behind NODE_ENV !== "test". Middleware order (mcpLimiter -> requireMcpKey -> requireAllowedIp -> handleMcp) is unchanged.
  • package.json — adds supertest (^7.2.2) as a devDependency.
  • test/mcp-integration.test.js — real Zod validation path using the actual mcpServer (not a mock), via InMemoryTransport.createLinkedPair() + SDK Client. Uses get_repo:
    • valid { owner, repo } passes Zod and reaches the handler (surfaces the expected downstream GITHUB_TOKEN error, not a validation error — proving args parsed/coerced correctly)
    • invalid args (missing required repo) are rejected at the validation layer (-32602, "Invalid arguments") and never reach the handler
  • test/server-e2e.test.js — supertest against the exported app:
    • GET /health → 200, no auth
    • POST /mcp with no key (even from an allowlisted IP) → 401 (key check runs before IP check)
    • POST /mcp with a valid key from a disallowed IP → 403
    • 30 unauthenticated requests → 401, 31st → 429 (rate limiter), using a vi.resetModules()-isolated fresh app instance so this doesn't inherit request count from the earlier tests in the same file

No changes needed to .github/workflows/ci.ymlnpm test already runs every test/*.test.js file.

Verified locally

  • npm ci && npm test → 88/88 passing
  • npm run lint → clean
  • node --check on all source files → clean
  • Manual node server.js + curl /health → passes (confirms the listen-gating change doesn't break normal boot)

Note: package-lock.json is not updated in this PR (CI already falls back to npm install when the lockfile is stale/missing, per the existing Install dependencies step) — worth regenerating separately via the repo's "Generate lockfile" workflow.

- server.js: export {app, mcpServer}; gate app.listen() behind
  NODE_ENV!=test (no behavior change for normal boot).
- package.json: add supertest devDependency (^7.2.2) for e2e HTTP tests.
- test/mcp-integration.test.js: real Zod validation path via
  InMemoryTransport + Client against the actual mcpServer/get_repo tool
  (valid args reach the handler; invalid args are rejected pre-handler).
- test/server-e2e.test.js: supertest against the exported app -- /health,
  requireMcpKey/requireAllowedIp ordering, and express-rate-limit
  (30 allowed, 31st -> 429, via a module-reset fresh limiter instance).

Verified locally: npm ci && npm test (88/88 passing) && npm run lint
(clean) && node --check on all source files && manual server boot +
GET /health smoke test, matching every step of .github/workflows/ci.yml.
No changes needed to the CI workflow itself.

package-lock.json intentionally not touched here -- CI already falls
back to `npm install` when the lockfile is missing/out of sync (see
ci.yml's Install dependencies step), so a stale lockfile doesn't break
CI; regenerate via the repo's "Generate lockfile" workflow separately
if desired.
@allocsys
allocsys merged commit 2a35786 into main Jul 28, 2026
2 checks passed
@allocsys
allocsys deleted the test/mcp-integration-coverage branch July 28, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant