Skip to content

test: add FastAPI mock backend + Kind-based e2e harness#209

Open
dimittal wants to merge 1 commit intomainfrom
feat/mock-backend-test-harness
Open

test: add FastAPI mock backend + Kind-based e2e harness#209
dimittal wants to merge 1 commit intomainfrom
feat/mock-backend-test-harness

Conversation

@dimittal
Copy link
Copy Markdown
Contributor

Summary

  • Adds mock_backend/ — a FastAPI service that simulates the DrDroid cloud backend so the local VPC agent can be tested in isolation. Issues its own bearer token, records every inbound call as JSONL, and exposes an admin API for seeding connection-test + playbook-task scenarios.
  • One-shot orchestrator mock_backend/deploy_kind.sh brings up Kind, builds (or auto-reuses) the agent image, deploys both the mock and the real agent via helm pointed at the in-cluster mock service, seeds scenarios, and runs the validator. Self-contained — no dependency on the per-user gitignored deploy_local.sh.
  • e2e.py validates more than transport: it pulls the agent's kubectl get output from the recordings, asserts cluster invariants, and (when host kubectl is on PATH) diffs the set of metadata.name values against a host snapshot for ground-truth verification.
  • Project-scoped slash command /test-vpc-agent at .claude/commands/test-vpc-agent.md so the loop is one keystroke from any session.

What's actually tested

Default scenarios (mock_backend/scenarios/default.json):

  • 1 ASSET_REFRESH task on the native Kubernetes connector.
  • 4 real kubectl tasks: get pods -n drdroid, get svc -n drdroid, get namespaces, get events -n drdroid.

Validator checks (20/20 passing on Kind):

  • Startup GET /connectors/proxy/ping, heartbeat POST /connectors/proxy/ping, connection-test poll, playbook-task poll, result round-trip — every endpoint in the agent's wire contract.
  • ASSET_REFRESH produces ≥1 batch at /connectors/proxy/connector/metadata/register.
  • For each kubectl task: output is parseable JSON, deployment-stable items are present (mock pod in get pods, mock service in get svc, drdroid ns in get namespaces, events non-empty), and the agent's name-set matches the host's kubectl snapshot exactly.

Usage

./mock_backend/deploy_kind.sh                  # build (or auto-reuse) + deploy + seed + validate
./mock_backend/deploy_kind.sh --reuse-image    # force-skip both image rebuilds (fastest)
./mock_backend/deploy_kind.sh --rebuild-agent  # force agent image rebuild after code changes
./mock_backend/deploy_kind.sh --reset-only     # tear down mock + agent, leave cluster

You don't bring a token — the mock issues one and the script wires it into helm. Auto-reuse: when drd-vpc-agent:local-<commit> already exists locally, the alpine 20-min build is skipped and a full test loop is ~30s.

Notable nits handled

  • helm/credentials-secret.yaml lives at the chart root (not under templates/) so helm install doesn't apply it. deploy_kind.sh creates the secret from credentials/secrets.yaml (or an empty placeholder) before the helm step — without this, agent pods hang in Init:0/1 with FailedMount.
  • Connection-test scenarios are filtered by what's actually in credentials/secrets.yaml (the agent silently no-ops requests for unknown connectors), so empty local setups still hit all-green.
  • .claude/settings.local.json added to .gitignore (per-user permissions; .claude/commands/ stays tracked).

Test plan

  • ./mock_backend/deploy_kind.sh --reuse-image from a clean Kind state — 20/20 checks pass.
  • Host-side kubectl get pods/svc/namespaces -o json matches the agent's reported name-sets exactly.
  • ASSET_REFRESH produces 4 metadata batches at /connector/metadata/register.
  • Mock recovers state across reruns (queues + recordings reset; tokens persist).
  • Empty credentials/secrets.yaml does not cause spurious failures.

🤖 Generated with Claude Code

Lets the local VPC agent be tested against a stand-in for the DrDroid
cloud backend without hitting prod. The mock issues its own bearer
token, records every inbound request as JSONL, and lets tests seed
connection-test + playbook-task scenarios via an admin API.

What's in mock_backend/:

- app.py / storage.py: FastAPI service implementing the agent's wire
  contract — GET/POST /connectors/proxy/ping, /register,
  /connector/connection/{tests,results}, /connector/metadata/register,
  /playbooks-engine/proxy/execution/{tasks,results}.
- Dockerfile + k8s/manifests.yaml: tiny python:3.12-alpine image
  deployed as a Service in the drdroid namespace; agent pods reach it
  over cluster DNS at drd-vpc-agent-mock.drdroid.svc.cluster.local:8080.
- deploy_kind.sh: one-shot orchestrator. Ensures Kind cluster, builds
  (or auto-reuses) the agent image, mints a token, applies the
  credentials-secret the helm chart needs, deploys the agent via helm,
  seeds scenarios, runs e2e.py. Self-contained — does not depend on
  the per-user gitignored deploy_local.sh.
- e2e.py: validator. Asserts startup ping, heartbeat, queue drain,
  result round-trip, and ASSET_REFRESH metadata batches. For each
  kubectl scenario it parses the agent's output, asserts stable
  cluster invariants are present, and (when host kubectl is
  available) diffs the set of metadata.name values against a host
  snapshot.
- scenarios/default.json: 1 ASSET_REFRESH + 4 real kubectl tasks
  (get pods/svc/namespaces/events on drdroid). Connection-tests are
  filtered by what's in credentials/secrets.yaml so empty configs
  don't trigger spurious failures.

Project-scoped slash command at .claude/commands/test-vpc-agent.md so
/test-vpc-agent runs the full loop. .claude/settings.local.json added
to .gitignore.

Validated end-to-end on Kind: 20/20 checks pass, including exact
host-vs-agent name-set match for pods, services, and namespaces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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