Collector Demo & Testing Script

4 min read•docs/ops/COLLECTOR-DEMO-TESTING.md•

Collector Live Demo & Testing Guide

This guide describes how to test, demonstrate, and verify the CX Orbit Event Collector (apps/collector) during live presentations, audits, or QA.


1. Architectural Role in CX Orbit

In CX Orbit, the Collector is the sole external write ingress (ADR 0002):

  • SDKs and external integrations never write directly to the database.
  • The Collector validates incoming event batches, enforces authentication (Authorization: Bearer <WRITE_KEY>), enforces schemas, quarantines malformed payloads, handles deduplication, and streams accepted canonical facts into storage.
  • It operates on port 8080 by default (local) or via $COLLECTOR_PUBLIC_URL on staging/production.

2. Quick Verification (1-Command Demo)

To run the automated 5-step test suite live with colored terminal output:

./scripts/demo-collector.sh

This script automatically verifies:

  1. Service handshake (GET /health)
  2. Invalidation & quarantine (POST /v1/events:batch → ENVIRONMENT_MISMATCH)
  3. Valid authoritative ingestion (POST /v1/events:batch → ACCEPTED)
  4. Idempotency & deduplication (Re-send → DUPLICATE_EVENT_ID)
  5. Real-time metrics & debug fact inspection (GET /metrics, GET /v1/debug/store)

3. Step-by-Step Manual Demo Script

Use these exact commands when walking through a live interactive terminal demonstration.

Step 1: Service Health Handshake

Command:

curl -s http://localhost:8080/health | jq .

Expected Response (HTTP 200):

{
  "status": "ok",
  "service": "cx-orbit-collector",
  "environment": "development",
  "project_id": "simiaroom",
  "collector_version": "0.1.0"
}

Talking Point: "The collector is lightweight (Hono + TypeScript), isolated from the UI, and acts as the secure gatekeeper for all telemetry and domain events across Simiaroom."


Step 2: Schema & Environment Guardrail (Quarantine)

Demonstrate that invalid or mismatched data never silently corrupts the analytical store.

Command:

curl -s -X POST http://localhost:8080/v1/events:batch \
  -H "Authorization: Bearer change-me-local-server-key" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "event_id": "c03264cf-dc45-429a-8b89-40c2132d1694",
      "event_version": 1,
      "project_id": "simiaroom",
      "environment": "production",
      "source": "simiaroom_payments",
      "source_event_id": "pay_test_err_01",
      "sent_at": "2026-09-12T06:00:00Z",
      "occurred_at": "2026-09-12T06:00:00Z",
      "journey_ref": "J-05",
      "canonical_type": "payment_succeeded",
      "event_name": "payment_succeeded",
      "identity": { "user_id": "usr_demo", "anonymous_id": null, "session_id": null },
      "actor": { "type": "system", "id": "payment-gateway" },
      "entity": { "type": "payment", "id": "pay_01" },
      "properties": {
        "order_id": "ord_01",
        "booking_id": "bkg_01",
        "package_id": "pkg_01",
        "amount": 2500000,
        "currency": "IRR",
        "gateway": "zarinpal"
      }
    }]
  }' | jq .

Expected Response (HTTP 422):

{
  "status": "rejected",
  "request_id": "ing_...",
  "accepted": 0,
  "rejected": 1,
  "quarantined": 1,
  "results": [{
    "outcome": "quarantined",
    "code": "ENVIRONMENT_MISMATCH",
    "message": "environment must match collector environment (development)"
  }]
}

Talking Point: "The collector rejected a production-tagged event sent to the development environment. It generated an audit quarantine record (quarantine_id) without failing the caller or dropping data."


Step 3: Authoritative Event Ingestion (Success)

Demonstrate ingesting a valid business outcome event (e.g. payment_succeeded).

Command:

EVENT_UUID=$(uuidgen | tr '[:upper:]' '[:lower:]')

curl -s -X POST http://localhost:8080/v1/events:batch \
  -H "Authorization: Bearer change-me-local-server-key" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "event_id": "'"${EVENT_UUID}"'",
      "event_version": 1,
      "project_id": "simiaroom",
      "environment": "development",
      "source": "simiaroom_payments",
      "source_event_id": "pay_demo_success_'$(date +%s)'",
      "sent_at": "2026-09-12T06:00:00Z",
      "occurred_at": "2026-09-12T06:00:00Z",
      "journey_ref": "J-05",
      "canonical_type": "payment_succeeded",
      "event_name": "payment_succeeded",
      "identity": { "user_id": "usr_demo", "anonymous_id": null, "session_id": null },
      "actor": { "type": "system", "id": "payment-gateway" },
      "entity": { "type": "payment", "id": "pay_success" },
      "properties": {
        "order_id": "ord_100",
        "booking_id": "bkg_100",
        "package_id": "pkg_12",
        "amount": 2500000,
        "currency": "IRR",
        "gateway": "zarinpal"
      }
    }]
  }' | jq .

Expected Response (HTTP 200):

{
  "status": "ok",
  "accepted": 1,
  "duplicate": 0,
  "rejected": 0,
  "quarantined": 0,
  "results": [{
    "outcome": "accepted",
    "code": "ACCEPTED"
  }]
}

Talking Point: "The event is validated against strict Zod and JSON Schemas, validated for privacy (zero raw clinical text permitted), and committed as an immutable fact."


Step 4: Idempotency & Deduplication Proof

Re-send the exact same payload from Step 3 using the same EVENT_UUID.

Expected Response (HTTP 200):

{
  "status": "ok",
  "accepted": 0,
  "duplicate": 1,
  "rejected": 0,
  "quarantined": 0,
  "results": [{
    "outcome": "duplicate",
    "code": "DUPLICATE_EVENT_ID",
    "message": "Already ingested; canonical fact unchanged"
  }]
}

Talking Point: "If network retries, timeout recoveries, or producer replays occur, CX Orbit recognizes identical event IDs (project_id + source + source_event_id). It acknowledges receipt safely without double-counting metrics."


Step 5: Real-Time Observability & Health

Inspect pipeline metrics and the canonical debug store:

1. Live Metrics Counter:

curl -s http://localhost:8080/metrics | jq .

Shows sub-millisecond latencies, accepted batches, duplicate counts, and quarantine stats.

2. Storage Key References:

curl -s -H "Authorization: Bearer change-me-local-server-key" http://localhost:8080/v1/debug/store | jq .

Shows raw event count, canonical count, and registered event keys.

3. Web UI Alignment: Open http://localhost:3000/dq to view the live Data Health dashboard, where pipeline metrics, lag, and rejection rates update in real time.


4. Starting and Stopping Services

  • Start collector in dev mode:
    pnpm --filter @cx-orbit/collector dev
    
  • Run all unit tests for collector:
    pnpm --filter @cx-orbit/collector test
    
  • Run TypeScript typecheck:
    pnpm --filter @cx-orbit/collector typecheck