Test Coverage Matrix — Webhook Endpoint
ID
Element
Where (mockup / file)
Test ID(s)
IE-001
POST /webhooks/shopify/product-update endpoint
mockup-b · app/routers/webhooks.py
WH-E2E-001, WH-INT-001, WH-UX-B-001
IE-002
JSON request body (id + title required)
mockup-c · app/models.py
WH-UNIT-001, WH-UNIT-003, WH-UNIT-004, WH-UX-C-001
IE-003
JSON request body (optional fields)
mockup-c · app/models.py
WH-UNIT-009, WH-UX-C-003
IE-004
Extra/nested fields (variants, images, metafields)
mockup-c · app/models.py
WH-UNIT-005, WH-UX-C-003
IE-005
structlog JSON log output
mockup-d · app/logging_config.py
WH-UNIT-002, WH-UNIT-012, WH-UX-D-001
IE-006
HTTP response body {"status": "received"}
mockup-e · app/routers/webhooks.py
WH-UNIT-001, WH-UX-E-002
IE-007
Content-Type header on request
mockup-b · spec §14
WH-UNIT-011, WH-UX-B-002
IE-008
Empty request body
spec §4 · spec §14
WH-UNIT-010
IE-009
Malformed JSON body
mockup-g · spec §4
WH-UNIT-006, WH-UX-G-002
IE-010
null value for required id field
spec §14
WH-UNIT-007
IE-011
String value for int id field
spec §14
WH-UNIT-008
ID
From → To
Trigger
Test ID(s)
ST-001
IDLE → REQUEST_RECEIVED
POST to /webhooks/shopify/product-update
WH-E2E-001, WH-E2E-002, WH-UX-B-001
ST-002
REQUEST_RECEIVED → PARSING
Uvicorn/FastAPI reads body
WH-UX-B-003
ST-003
PARSING → VALIDATING
JSON deserialized, passed to Pydantic
WH-UX-C-001
ST-004
VALIDATING → LOGGING
Pydantic validation passes
WH-E2E-002, WH-INT-001, WH-UX-D-001
ST-005
VALIDATING → ERROR_422
Pydantic validation fails (missing field, type mismatch)
WH-E2E-003, WH-INT-002, WH-UX-G-001
ST-006
PARSING → ERROR_422
Malformed JSON cannot be deserialized
WH-UNIT-006, WH-UX-G-002
ST-007
LOGGING → SUCCESS_200
structlog emits JSON line, handler returns 200
WH-E2E-002, WH-UX-E-001
ST-008
ERROR_422 → IDLE (recovery)
Client sends a new valid request after error
WH-E2E-004
ST-009
SUCCESS_200 → IDLE
Connection closed, ready for next request
WH-E2E-001
ID
Case
Trigger
Recovery
Test ID(s)
EC-001
Missing required id field
POST {"title": "Widget"}
Fix payload, resend
WH-UNIT-003, WH-UX-G-001, WH-E2E-003
EC-002
Missing required title field
POST {"id": 123}
Fix payload, resend
WH-UNIT-004, WH-UX-G-001
EC-003
Malformed JSON body
POST "{not valid json}"
Send valid JSON
WH-UNIT-006, WH-UX-G-002
EC-004
Empty request body
POST with no body
Send valid JSON body
WH-UNIT-010
EC-005
null value for id
POST {"id": null, "title": "X"}
Send valid int id
WH-UNIT-007
EC-006
String instead of int for id
POST {"id": "abc", "title": "X"}
Send valid int id
WH-UNIT-008
EC-007
Missing Content-Type header
POST valid JSON without Content-Type
None needed — FastAPI still parses
WH-UNIT-011
EC-008
Large payload (>1MB with many variants)
POST with large nested arrays
Accepted (extra=allow), logged as-is
WH-E2E-005
EC-009
Concurrent requests
Multiple POSTs simultaneously
Stateless — each handled independently
WH-INT-004
EC-010
structlog misconfiguration
structlog outputs non-JSON or duplicate lines
Check structlog processor chain
WH-UNIT-012
4. Mockup states reachable by user action
Mockup
State
Reachable via
Test ID(s)
A
Idle — service running, port 8080
App startup / before any requests
WH-UX-A-001, WH-UX-A-002, WH-UX-A-003
B
Webhook received — POST arrives
POST /webhooks/shopify/product-update
WH-UX-B-001, WH-UX-B-002, WH-UX-B-003
C
Validation — Pydantic model validates
Automatically after JSON parsing
WH-UX-C-001, WH-UX-C-002, WH-UX-C-003
D
Active logging — structlog writes JSON
Validation passes, handler logs payload
WH-UX-D-001, WH-UX-D-002, WH-UX-D-003
E
Success — 200 OK returned
After logging completes
WH-UX-E-001, WH-UX-E-002, WH-UX-E-003
G
Error — 422 returned
Invalid/malformed payload
WH-UX-G-001, WH-UX-G-002, WH-UX-G-003
Note: Mockup F (Health Check) is out of scope for the Webhook endpoint area — covered by a separate area-scoped run.
5. Cross-state recovery flows
ID
Scenario
Test ID(s)
CSR-001
Error (422) → new valid request → 200
WH-E2E-004
CSR-002
Success (200) → another valid request → 200
WH-E2E-001
CSR-003
Success (200) → invalid request → 422
WH-E2E-001
CSR-004
Idle → valid webhook → success → idle → valid webhook → success
WH-E2E-002
CSR-005
Idle → invalid webhook → error → idle → valid webhook → success
WH-E2E-003, WH-E2E-004