Skip to content

AI Read API (mcp-api) โ€” Test Cases

This page is the test plan for apps/mcp-api/ in guinness-ai-v2. Every row in io-definition.md โ€” endpoints, query parameters, DocumentDB operations, and error classes โ€” must be traceable to at least one test case here. If a contract clause has no test, that is the highest-priority bug to fix.

Convention. Test IDs follow AM-<layer>-<NN>. Layers: U (unit, no I/O), C (integration, real DocumentDB testcontainer, mocked auth), E (E2E, real cloud). Each case states Given / When / Then explicitly.


Test Layers and Tools

Layer Scope Tools
Unit (U) Pure functions: auth validation, query parameter parsing, DocumentDB filter builders, response builders pytest, no network. DocumentDB / httpx are mocked at the boundary
Integration (C) Real testcontainer Mongo (DocumentDB API-compatible mongo:7 image) + full request lifecycle using a token that is valid by default pytest, testcontainers[mongodb], httpx TestClient. Requires Docker
End-to-end (E) Real MCP Server โ†’ AI Read API โ†’ DocumentDB round-trip (dev environment) Manual / smoke only

Shared Fixtures

Fixture Provides Used by
valid_token The AI_SERVICE_TOKEN value for tests U, C
design_doc(**overrides) A well-formed DocumentDB design document U, C
code_doc(**overrides) A well-formed DocumentDB code document with _id, name, source_code, and visual.metadata.image_url U, C
seed_design(collection, doc) Inserts a design document into the testcontainer Mongo C
seed_design_batch(collection, count, project_id) Inserts N design documents with the specified project_id C
seed_code_batch(collection, count, project_id) Inserts N code documents with the specified project_id C

Coverage Matrix

Happy path Failure path Idempotence
Auth (token) AM-U-01 AM-U-02, AM-U-03 โ€”
Parameter validation AM-U-04 AM-U-05 โ€”
GET /internal/designs/{design_id} AM-U-06, AM-C-01 AM-C-02 AM-C-05
GET /internal/designs AM-U-07, AM-C-03 โ€” AM-C-05
GET /internal/code AM-U-08, AM-C-04 โ€” AM-C-05
GET /internal/code/{code_id} AM-U-09, AM-C-06 AM-C-07 AM-C-05
Pagination (limit/offset) AM-U-10 โ€” โ€”
Invalid limit (negative / non-int) AM-U-11 โ€” โ€”
Empty list result AM-C-08 โ€” โ€”
DocumentDB error โ€” AM-C-09 โ€”

Unit (U) Tests

No network. Pure Python.

AM-U-01 โ€” Valid X-AI-Service-Token is accepted

  • Given A request with an X-AI-Service-Token that matches the configured environment variable value.
  • When The auth middleware validates the token.
  • Then Validation passes (no 401 is thrown).

AM-U-02 โ€” Missing X-AI-Service-Token โ†’ 401

  • Given A request with no X-AI-Service-Token header.
  • When The auth middleware validates.
  • Then HTTP 401, { ok: false, error: { code: "UNAUTHORIZED" } }.

AM-U-03 โ€” Wrong token โ†’ 401

  • Given A request with an X-AI-Service-Token that does not match the environment variable value.
  • When The auth middleware validates.
  • Then HTTP 401, { ok: false, error: { code: "UNAUTHORIZED" } }.
  • And The comparison is timing-safe (verify that the implementation uses hmac.compare_digest or equivalent โ€” no short-circuiting).

AM-U-04 โ€” Route is selected by path

  • Given Request paths /internal/designs, /internal/designs/{design_id}, /internal/code, and /internal/code/{code_id}.
  • When The router determines which handler to execute.
  • Then It selects list-designs, get-design-detail, list-code, and get-code-detail respectively.

AM-U-05 โ€” Missing project_id on list endpoints โ†’ 400

  • Given GET /internal/designs or GET /internal/code without project_id.
  • When The router validates parameters.
  • Then HTTP 400, { ok: false, error: { code: "BAD_REQUEST" } }.

AM-U-06 โ€” design_id query builds the correct DocumentDB filter

  • Given design_id = "1_abc_def".
  • When The repository builds the query.
  • Then The filter is {"_id": "1_abc_def"}.
  • And The operation is find_one (not find).

AM-U-07 โ€” list-designs builds the correct filter with projection

  • Given project_id = 42, organization_id = 1, limit = 10, offset = 5.
  • When The repository builds the design list query.
  • Then The filter is {"project_id": 42, "organization_id": 1}.
  • And The projection excludes every vector_embedding field.
  • And The cursor has .skip(5).limit(10).

AM-U-08 โ€” list-code builds the correct filter with projection

  • Given project_id = 42, organization_id = 1, limit = 10, offset = 5.
  • When The repository builds the code list query.
  • Then The filter is {"project_id": 42}.
  • And The projection includes _id, organization_id, project_id, name, type, based_on, and visual.metadata.image_url.
  • And The projection excludes source_code, css_code, and every vector_embedding field.
  • And The cursor has .skip(5).limit(10).

AM-U-09 โ€” code detail builds the correct DocumentDB filter

  • Given code_id = "660e8400-e29b-41d4-a716-446655440001".
  • When The repository builds the query.
  • Then The filter is {"_id": "660e8400-e29b-41d4-a716-446655440001"}.
  • And The operation is find_one.
  • And The projection excludes every vector_embedding field.

AM-U-10 โ€” limit/offset are applied correctly

  • Parameterized:
  • No limit โ†’ defaults to 20.
  • limit = 200 โ†’ capped to 100.
  • No offset โ†’ defaults to 0.
  • offset = 50 โ†’ .skip(50).
  • Then The correct values are passed to the DocumentDB cursor.

AM-U-11 โ€” Invalid limit falls back to default

  • Parameterized:
  • limit = -1 โ†’ falls back to default 20.
  • limit = 0 โ†’ falls back to default 20.
  • limit = "abc" (non-integer string) โ†’ falls back to default 20.
  • Then .limit(20) is passed to the DocumentDB cursor.
  • And No error is returned โ€” invalid values are silently corrected per the I/O contract.

AM-U-12 โ€” offset exceeds total count โ†’ empty page

  • Given project_id = 42 with 5 documents in the code collection.
  • When GET /internal/code?project_id=42&offset=100.
  • Then HTTP 200.
  • { ok: true, data: { project_id: 42, total: 5, items: [] } }.
  • And total reflects the actual count (5), not 0.

Integration (C) Tests

Real testcontainer Mongo (DocumentDB API-compatible). Auth is mocked (token valid by default).

AM-C-01 โ€” GET by design_id โ€” document found

  • Given:
  • A document in the design collection with _id = "1_abc_def", project_id = 42, name = "Login Screen", visual.metadata.image_url = "s3://".
  • A valid auth token.
  • When GET /internal/designs/1_abc_def.
  • Then:
  • HTTP 200.
  • { ok: true, data: { id: "1_abc_def", project_id: 42, name: "Login Screen", image_url: "s3://..." } }.
  • data does not contain vector_embedding or any other internal fields.

AM-C-02 โ€” GET by design_id โ€” document not found

  • Given:
  • No document with _id = "nonexistent" in the design collection.
  • A valid auth token.
  • When GET /internal/designs/nonexistent.
  • Then:
  • HTTP 404.
  • { ok: false, data: null, error: { code: "NOT_FOUND", message: "Design not found" } }.

AM-C-03 โ€” list-designs by project_id with pagination

  • Given:
  • 25 documents with project_id = 42 in the design collection.
  • A valid auth token.
  • When GET /internal/designs?project_id=42&limit=10&offset=5.
  • Then:
  • HTTP 200.
  • { ok: true, data: { project_id: 42, total: 25, items: [...] } }.
  • The items array contains exactly 10 items.
  • No vector_embedding field is leaked.

AM-C-04 โ€” list-code by project_id with pagination

  • Given:
  • 25 documents with project_id = 42 in the code collection.
  • A valid auth token.
  • When GET /internal/code?project_id=42&organization_id=1&limit=10&offset=5.
  • Then:
  • HTTP 200.
  • { ok: true, data: { project_id: 42, total: 25, items: [...] } }.
  • The items array contains exactly 10 items (items 6โ€“15 of the collection).
  • Each code item has id, project_id, organization_id, name, and image_url.
  • Code list items do not contain source_code or css_code.
  • No vector_embedding field is leaked.

AM-C-05 โ€” Repeated identical requests return the same data

  • Given The setup from AM-C-01 (design found).
  • When The same GET /internal/designs/1_abc_def is called twice.
  • Then Both responses are identical (stateless, idempotent).

AM-C-06 โ€” get-code-detail โ€” document found

  • Given:
  • A document in the code collection with _id = "660e8400-e29b-41d4-a716-446655440001", project_id = 42, organization_id = 1, name = "Button", and source_code = "...".
  • A valid auth token.
  • When GET /internal/code/660e8400-e29b-41d4-a716-446655440001.
  • Then:
  • HTTP 200.
  • { ok: true, data: { code: { _id: "...", project_id: 42, organization_id: 1, source_code: "..." } } }.
  • data.code does not contain vector_embedding.

AM-C-07 โ€” get-code-detail โ€” document not found

  • Given No code document with _id = "missing" and a valid auth token.
  • When GET /internal/code/missing.
  • Then HTTP 404 with { ok: false, data: null, error: { code: "NOT_FOUND" } }.

AM-C-08 โ€” list endpoint collection is empty

  • Given:
  • No documents with project_id = 99 in the code collection.
  • A valid auth token.
  • When GET /internal/code?project_id=99&organization_id=1.
  • Then:
  • HTTP 200 (not 404 โ€” an empty result is not an error).
  • { ok: true, data: { project_id: 99, total: 0, items: [] } }.

AM-C-09 โ€” DocumentDB connection error

  • Given:
  • The Mongo testcontainer is stopped / paused (simulating a connection failure).
  • A valid auth token.
  • When GET /internal/designs/1_abc_def.
  • Then:
  • HTTP 500.
  • { ok: false, data: null, error: { code: "INTERNAL_ERROR", message: "..." } }.
  • The error is logged at ERROR level (ai-mcp.query.failed).

E2E (E) Tests

Smoke test set. Run manually against real services.

AM-E-01 โ€” MCP Server โ†’ AI Read API round-trip

  • Given MCP Server configured against dev with real AI_SERVICE_URL and AI_SERVICE_TOKEN.
  • When An MCP client calls list-designs, list-code, get-design-detail, and get-code-detail with known IDs.
  • Then Within 30 seconds:
  • The MCP responses contain design and code data from DocumentDB.
  • CloudWatch shows: ai-mcp.request.received โ†’ ai-mcp.query.completed โ†’ ai-mcp.response.sent.
  • No 401 or 500 errors.

Cross-cutting Checks

Conditions that must hold across all applicable tests. Verify these during review or execution of the tests above, not as separate test rows.

  • No Postgres connections are opened. Run tests with Postgres unreachable (or set a guard environment variable). Incorrect driver imports surface immediately.
  • Responses always follow the { ok, data, error } envelope. Assert envelope structure in all tests โ€” error responses must also conform.
  • Auth is timing-safe. Verify that the implementation uses hmac.compare_digest or equivalent โ€” do not use == for secret string comparison.
  • No DocumentDB writes in any test. Assert that collection.count_documents() is unchanged before and after each test.
  • No vector_embedding field is leaked. In every test that asserts response data, verify that the output does not contain a vector_embedding key.
  • Detail routes do not return total or an items array. List routes return paginated lists; detail routes return { design: ... } or { code: ... }.
  • Error responses do not include data. When ok = false, data must be null (not omitted, not an empty object).

Intentionally Out of Scope

Listed explicitly so that future tests are placed in the correct bucket.

  • MCP Server auth and tool routing. Governed by the mcp/ test case design. This API only validates its own X-AI-Service-Token.
  • DocumentDB index management. Governed by packages/models/. This API assumes indexes exist.
  • SQS interactions. This API is HTTP only. SQS is not involved.
  • CORS / CloudFront configuration. Infrastructure level. This API is internal to the VPC only.
  • Rate limiting / throttling. TBD. No tests until specified.
  • Generated-code endpoints. Future endpoints. Not in this contract.
  • Data migration or seeding. Operational concern. Test fixtures manage their own data.

  • I/O Definition โ€” the contract these tests enforce
  • Overview โ€” request flow, DocumentDB query patterns, and auth details