Contributing

This document covers how to run the three test tracks locally.

Prerequisites

ToolRequired forInstall
Rust toolchainBuild + unit testsrustup.rs
Docker + ComposeAll integration testsdocs.docker.com
oasdiffLevel 1 diff validationgo install github.com/tufin/oasdiff@latest or brew install oasdiff
Node.js + npmLevel 2nodejs.org
PlaywrightLevel 2npx playwright install --with-deps chromium
VHSDemo GIFbrew install vhs or charm apt repo
ffmpeg, gifski, gifsicleDemo GIF optimizationSystem package manager

Build

cargo build --release
# Binary: target/release/mitm2openapi

Unit Tests

cargo test

Level 1 — Petstore Golden Test (~2 min)

Full pipeline (compose up, seed, discover, generate, diff, teardown):

tests/integration/level1/run.sh

Strict mode (--fail-on WARN instead of BREAKING):

tests/integration/level1/run.sh --strict

Manual step-by-step:

cd tests/integration/level1
docker compose up -d
# Wait for petstore healthcheck...
../../ci/petstore/seed.sh
# Run mitm2openapi discover/generate against the proxy
# ...
docker compose down -v

Gotcha: The seed script sends requests through the mitmproxy proxy to petstore:8080 (Docker service name), not localhost. This is intentional — traffic must flow through the proxy to be captured.

Level 2 — crAPI + Playwright (~8 min)

cd tests/integration/level2

# Start crAPI stack (identity + community + workshop + web + mongo + postgres + mailhog + mitmproxy)
make up
# No seed needed — crAPI auto-seeds on first boot

# Run Playwright scenarios
npm install
npx playwright install --with-deps chromium
npx playwright test

# Cleanup
make down

Port conflict: Level 1 and Level 2 both use port 8080 (for different services). Do not run both stacks simultaneously.

Demo GIF (Phase 2 terminal recording)

cd ci/demo
make phase2        # VHS recording
make gif           # gifski + gifsicle optimization
make clean         # remove outputs

Phase 2 uses a real capture, not a committed fixture. The GHA workflow copies tests/integration/level2/out/crapi.flow (produced by Phase 1) into ci/demo/out/demo.flow before running the tape. Locally, do the same: run Phase 1 first, then cp tests/integration/level2/out/crapi.flow ci/demo/out/demo.flow.

Filtering discover output

Captures from real apps include static assets (/static/css/main.*.css, /images/*.svg, etc.) which bloat the generated OpenAPI spec. Two flags on discover handle this:

mitm2openapi discover \
  -i capture.flow -o templates.yaml -p http://api.example.com \
  --exclude-patterns '/static/**,/images/**,*.css,*.js,*.svg,*.png,*.jpg' \
  --include-patterns '/api/**,/v2/**'
  • --exclude-patterns: paths matching any glob are dropped entirely (not even emitted as ignore:).
  • --include-patterns: paths matching any glob are emitted without the ignore: prefix (i.e. auto-activated for generate). Everything else still gets ignore: for manual review.

Globs: * matches a single path segment, ** matches any subtree.

Together they let generate run with no intermediate sed or editor step — useful for automated pipelines like the demo GIF.

Ports Reference

StackServicePort
Level 1Petstore8080
Level 1mitmproxy8081
Level 2crAPI web8888
Level 2mailhog8025
Level 2mitmproxy8080
DemoSwagger UI8088

Cleanup

All compose stacks use docker compose down -v to remove containers and volumes.

CI Workflows

WorkflowTriggerNotes
integration-level1.ymlEvery PRNaive (required) + strict (informational)
integration-level2.ymlNightly + manual dispatchFull crAPI + Playwright
demo-gif.ymlPush to main (path-filtered) + manual dispatchTerminal recording