Inventory OpenAPI omits implemented error responses
Summary and impact
The inventory contract lists only success, 401, and 403. Its implemented
validation, not-found, and stock-conflict responses are absent. Generated clients
and documented-response coverage therefore miss legitimate business outcomes.
This is a documentation defect; observed rejection behavior is correct.
Environment and evidence
Observed 2026-09-05 with curl. Live contracts fetched from both
https://awesome.byst.re/v3/api-docs and
https://aitesters.byst.re/v3/api-docs have the same four inventory operations:
GET list/detail/movements document 200/401/403; POST adjustments documents
201/401/403. The repository api-docs.json contains no inventory operations.
Authenticated reproduction used https://aitesters.byst.re. Configured staging
admin credentials were rejected on awesome (422); authenticated awesome
behavior remains unverified. All four awesome unauthenticated calls returned
401 {"message":"Unauthorized"}.
| Staging request | Actual body/status | Frequency |
|---|---|---|
| POST adjustment with delta 0 | 400 {"deltaNonZero":"delta must not be zero"} | 3/3 |
| GET inventory/-1 | 404 {"message":"Product not found"} | 3/3 |
| POST delta -9 when available=8 | 409 {"message":"Insufficient stock"} | 3/3 |
| Reuse requestId with changed delta | 409 {"message":"requestId already used with different payload"} | 3/3 |
| GET item with lowStockThreshold=0 | 400 {"error":"lowStockThreshold must be at least 1"} | 1/1 |
Positive control: generated product 28 started at 5; adjustment movement 37
changed it to 8. Repeating its exact payload returned 201 with the same id,
timestamp, and quantityAfter. Rejected requests left stock at 8. Order 20 then
reduced stock to 6 and cancellation restored 8. Final history contained exactly
INITIAL_STOCK, ADMIN_ADJUSTMENT, ORDER_DEDUCTED, ORDER_RESTORED (newest first
in the response).
Copyable reproduction
Use a valid staging administrator token in TOKEN and an owned disposable product
created with stockQuantity=5 in PRODUCT_ID; never use seeded products.
BASE=https://aitesters.byst.re
REQUEST_ID=$(uuidgen)
ADJUSTMENT=$(printf '{"delta":3,"reason":"Inventory exploration","requestId":"%s"}' "$REQUEST_ID")
curl -i "$BASE/api/v1/admin/inventory/$PRODUCT_ID/adjustments" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d "$ADJUSTMENT"
# Retry the command above: same movement, stock remains 8.
curl -i "$BASE/api/v1/admin/inventory/$PRODUCT_ID/adjustments" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d "$(printf '{"delta":4,"reason":"Inventory exploration","requestId":"%s"}' "$REQUEST_ID")"
curl -i "$BASE/api/v1/admin/inventory/-1" -H "Authorization: Bearer $TOKEN"
curl -i "$BASE/api/v1/admin/inventory/$PRODUCT_ID?lowStockThreshold=0" \
-H "Authorization: Bearer $TOKEN"
# Use fresh request ids for separate invalid adjustment scenarios:
curl -i "$BASE/api/v1/admin/inventory/$PRODUCT_ID/adjustments" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d "$(printf '{"delta":0,"reason":"invalid","requestId":"%s"}' "$(uuidgen)")"
curl -i "$BASE/api/v1/admin/inventory/$PRODUCT_ID/adjustments" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d "$(printf '{"delta":-9,"reason":"underflow","requestId":"%s"}' "$(uuidgen)")"
# Remove only the owned disposable product after reproduction.
curl -i -X DELETE "$BASE/api/v1/products/$PRODUCT_ID" -H "Authorization: Bearer $TOKEN"
Expected: OpenAPI explicitly describes these errors and their actual JSON shapes. Actual: none of these error statuses appears on the relevant inventory operations.
Investigation and regression guidance
Local backend revision 8cb264a: AdminInventoryController declares only success
plus class-level authentication errors. InventoryService.adjust/get and
InventoryAdjustmentDto explicitly implement the outcomes above;
GlobalExceptionHandlerController maps them to the observed error bodies.
Add operation-specific 400/404/409 documentation and explicit error schemas.
Follow-up curl checks confirmed missing-product history and adjustment 404,
invalid list threshold 400, missing adjustment fields/blank or oversized reason
400, and quantity overflow 409 (each 1/1 on staging). Product 29 and its
generated user were both cleaned up with 204.
Add OpenAPI regression coverage for both status presence and error schema shape.
L19 behavioral automation can proceed against staging and should assert these intended errors. Do not make incomplete Swagger documentation an accepted contract assertion. Authenticated course-environment parity needs valid admin access before it can be claimed.
Cleanup
Generated user invcde56dc8e03744ce and its order were deleted (204), then
product 28 was deleted (204); the migration cascades its movement history.
No known leftover generated entities. No shared products were changed.
