feat(backend): stabilize draft editor flow and complete smoke regression baseline
- add editor entry flow with editor context and ensure-draft bootstrap - add draft summary read model and single-record draft read endpoints - add typed draft, edit and publish conflicts with validation errors - add pricing diagnostics and publish readiness endpoints - fix Decimal serialization in seat price and test preview flows - harden draft lifecycle guards for published vs draft current version - update API map and smoke regression checklist - add backend README and smoke regression script
This commit is contained in:
330
backend/README.md
Normal file
330
backend/README.md
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
# svg-service backend
|
||||||
|
|
||||||
|
Backend for SVG scheme upload, draft editing, pricing, publish preview, and publish lifecycle.
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- Python 3.11
|
||||||
|
- FastAPI
|
||||||
|
- SQLAlchemy async
|
||||||
|
- PostgreSQL 16
|
||||||
|
- Docker Compose
|
||||||
|
|
||||||
|
## Runtime
|
||||||
|
|
||||||
|
Default backend port: `9020`
|
||||||
|
|
||||||
|
Health check:
|
||||||
|
|
||||||
|
- `GET /healthz`
|
||||||
|
|
||||||
|
Main API prefix:
|
||||||
|
|
||||||
|
- `/api/v1`
|
||||||
|
|
||||||
|
Auth header:
|
||||||
|
|
||||||
|
- `X-API-Key`
|
||||||
|
|
||||||
|
Default local admin key:
|
||||||
|
|
||||||
|
- `admin-local-dev-key`
|
||||||
|
|
||||||
|
## Core lifecycle
|
||||||
|
|
||||||
|
The backend works with a scheme lifecycle:
|
||||||
|
|
||||||
|
1. Upload SVG
|
||||||
|
2. Normalize and persist structure
|
||||||
|
3. Work in current draft
|
||||||
|
4. Create / update pricing
|
||||||
|
5. Build pricing snapshot
|
||||||
|
6. Inspect publish preview / readiness
|
||||||
|
7. Publish current draft
|
||||||
|
8. If editing is needed after publish, create or ensure a new draft again
|
||||||
|
|
||||||
|
## Main concepts
|
||||||
|
|
||||||
|
### Scheme
|
||||||
|
Top-level business entity.
|
||||||
|
|
||||||
|
### Scheme version
|
||||||
|
Concrete version of the scheme.
|
||||||
|
A version can be `draft` or `published`.
|
||||||
|
|
||||||
|
### Current version
|
||||||
|
The version referenced by the scheme registry as active current.
|
||||||
|
|
||||||
|
### Draft
|
||||||
|
Editable current version.
|
||||||
|
All editor mutations and draft pricing operations must target a current draft version only.
|
||||||
|
|
||||||
|
### Published version
|
||||||
|
Non-editable current version.
|
||||||
|
If current version is published, editor flow must first create or ensure a new draft.
|
||||||
|
|
||||||
|
### Upload artifacts
|
||||||
|
Stored technical artifacts, including:
|
||||||
|
|
||||||
|
- original svg
|
||||||
|
- sanitized svg
|
||||||
|
- normalized json
|
||||||
|
- display svg
|
||||||
|
- publish preview json
|
||||||
|
|
||||||
|
## Editor entry flow
|
||||||
|
|
||||||
|
Use this flow from frontend or operator scripts.
|
||||||
|
|
||||||
|
### 1. Inspect editor state
|
||||||
|
|
||||||
|
`GET /api/v1/schemes/{scheme_id}/editor/context`
|
||||||
|
|
||||||
|
Response tells whether:
|
||||||
|
|
||||||
|
- current version is draft
|
||||||
|
- editor is available
|
||||||
|
- a new draft should be created
|
||||||
|
- recommended action is `use_current_draft` or `create_draft`
|
||||||
|
|
||||||
|
### 2. Ensure editable draft
|
||||||
|
|
||||||
|
`POST /api/v1/schemes/{scheme_id}/draft/ensure`
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
|
||||||
|
- if current version is already draft: returns it with `created=false`
|
||||||
|
- if current version is published: clones current version into a new current draft and returns it with `created=true`
|
||||||
|
|
||||||
|
Returned `scheme_version_id` must be reused as:
|
||||||
|
|
||||||
|
- `expected_scheme_version_id`
|
||||||
|
|
||||||
|
for draft reads and mutations.
|
||||||
|
|
||||||
|
## Optimistic concurrency
|
||||||
|
|
||||||
|
Mutable draft flows support optimistic concurrency through query params:
|
||||||
|
|
||||||
|
- `expected_current_scheme_version_id`
|
||||||
|
- `expected_scheme_version_id`
|
||||||
|
|
||||||
|
These guards prevent frontend/editor from mutating a stale draft after another version switch.
|
||||||
|
|
||||||
|
Typical typed conflict payload:
|
||||||
|
|
||||||
|
- `stale_current_version`
|
||||||
|
- `stale_draft_version`
|
||||||
|
- `draft_not_editable`
|
||||||
|
- `publish_not_ready`
|
||||||
|
|
||||||
|
## Main operator routes
|
||||||
|
|
||||||
|
## System
|
||||||
|
|
||||||
|
- `GET /healthz`
|
||||||
|
- `GET /api/v1/ping`
|
||||||
|
- `GET /api/v1/db/ping`
|
||||||
|
- `GET /api/v1/manifest`
|
||||||
|
|
||||||
|
## Uploads
|
||||||
|
|
||||||
|
- `POST /api/v1/schemes/upload`
|
||||||
|
- `GET /api/v1/uploads`
|
||||||
|
- `GET /api/v1/uploads/{upload_id}`
|
||||||
|
- `GET /api/v1/uploads/{upload_id}/normalized`
|
||||||
|
|
||||||
|
## Scheme registry
|
||||||
|
|
||||||
|
- `GET /api/v1/schemes`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/current`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/versions`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/versions`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/publish/validation`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/publish-readiness`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/publish`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/unpublish`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/rollback`
|
||||||
|
|
||||||
|
## Editor / draft
|
||||||
|
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/editor/context`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/draft/ensure`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/summary`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/structure`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/validation`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/compare-preview`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id}`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id}`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/draft/sectors`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/draft/groups`
|
||||||
|
- `DELETE /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id}`
|
||||||
|
- `DELETE /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}`
|
||||||
|
- `PATCH /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id}`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/draft/seats/bulk`
|
||||||
|
- `PATCH /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id}`
|
||||||
|
- `PATCH /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/draft/repair-references`
|
||||||
|
|
||||||
|
## Pricing
|
||||||
|
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/pricing`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/pricing/categories`
|
||||||
|
- `PUT /api/v1/schemes/{scheme_id}/pricing/categories/{pricing_category_id}`
|
||||||
|
- `DELETE /api/v1/schemes/{scheme_id}/pricing/categories/{pricing_category_id}`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/pricing/rules`
|
||||||
|
- `PUT /api/v1/schemes/{scheme_id}/pricing/rules/{price_rule_id}`
|
||||||
|
- `DELETE /api/v1/schemes/{scheme_id}/pricing/rules/{price_rule_id}`
|
||||||
|
|
||||||
|
## Pricing diagnostics
|
||||||
|
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/pricing/coverage`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/pricing/unpriced-seats`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/pricing/explain/{seat_id}`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/pricing/rules/diagnostics`
|
||||||
|
|
||||||
|
## Publish preview
|
||||||
|
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/draft/pricing/snapshot`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/draft/publish-preview`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/draft/remap/preview`
|
||||||
|
- `POST /api/v1/schemes/{scheme_id}/draft/remap/apply`
|
||||||
|
|
||||||
|
## Structure read model
|
||||||
|
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/current/sectors`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/current/groups`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/current/seats`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/current/seats/{seat_id}/price`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/current/svg`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/current/svg/display`
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/current/svg/display/meta`
|
||||||
|
|
||||||
|
## Test mode
|
||||||
|
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/test/seats/{seat_id}`
|
||||||
|
|
||||||
|
## Audit
|
||||||
|
|
||||||
|
- `GET /api/v1/schemes/{scheme_id}/audit`
|
||||||
|
|
||||||
|
## Admin / ops
|
||||||
|
|
||||||
|
- `GET /api/v1/admin/schemes/{scheme_id}/current/artifacts`
|
||||||
|
- `GET /api/v1/admin/schemes/{scheme_id}/current/validation`
|
||||||
|
- `POST /api/v1/admin/schemes/{scheme_id}/current/display/regenerate`
|
||||||
|
- `POST /api/v1/admin/display/backfill`
|
||||||
|
- `GET /api/v1/admin/artifacts/publish-preview/audit`
|
||||||
|
- `POST /api/v1/admin/artifacts/publish-preview/cleanup`
|
||||||
|
|
||||||
|
## Typical local flow
|
||||||
|
|
||||||
|
## 1. Read current version
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
`GET /api/v1/schemes/{scheme_id}/current`
|
||||||
|
|
||||||
|
## 2. Ensure draft
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
`POST /api/v1/schemes/{scheme_id}/draft/ensure`
|
||||||
|
|
||||||
|
Store returned:
|
||||||
|
|
||||||
|
- `scheme_version_id`
|
||||||
|
|
||||||
|
## 3. Read draft state
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
- `GET /draft/summary?expected_scheme_version_id=...`
|
||||||
|
- `GET /draft/structure?expected_scheme_version_id=...`
|
||||||
|
- `GET /draft/validation?expected_scheme_version_id=...`
|
||||||
|
- `GET /draft/compare-preview?expected_scheme_version_id=...`
|
||||||
|
|
||||||
|
## 4. Perform editor mutations
|
||||||
|
|
||||||
|
Pass:
|
||||||
|
|
||||||
|
- `expected_scheme_version_id={draft_scheme_version_id}`
|
||||||
|
|
||||||
|
on every mutation route.
|
||||||
|
|
||||||
|
## 5. Inspect pricing quality
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
- `GET /pricing/coverage`
|
||||||
|
- `GET /pricing/unpriced-seats`
|
||||||
|
- `GET /pricing/explain/{seat_id}`
|
||||||
|
- `GET /pricing/rules/diagnostics`
|
||||||
|
|
||||||
|
## 6. Create pricing snapshot
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
`POST /draft/pricing/snapshot?expected_scheme_version_id=...`
|
||||||
|
|
||||||
|
## 7. Inspect readiness
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
`GET /draft/publish-readiness?expected_scheme_version_id=...`
|
||||||
|
|
||||||
|
## 8. Publish
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
`POST /publish?expected_scheme_version_id=...`
|
||||||
|
|
||||||
|
## Typed error expectations
|
||||||
|
|
||||||
|
Examples of stable typed errors already used in the service:
|
||||||
|
|
||||||
|
### Draft concurrency/state
|
||||||
|
- `stale_current_version`
|
||||||
|
- `stale_draft_version`
|
||||||
|
- `draft_not_editable`
|
||||||
|
|
||||||
|
### Editor validation
|
||||||
|
- `duplicate_seat_id`
|
||||||
|
- `duplicate_seat_id_in_payload`
|
||||||
|
- `duplicate_sector_id`
|
||||||
|
- `duplicate_group_id`
|
||||||
|
- `duplicate_sector_element_id`
|
||||||
|
- `duplicate_group_element_id`
|
||||||
|
- `unknown_sector_id`
|
||||||
|
- `unknown_group_id`
|
||||||
|
- `unknown_sector_ids`
|
||||||
|
- `unknown_group_ids`
|
||||||
|
- `unknown_target_sector_id`
|
||||||
|
- `unknown_target_group_id`
|
||||||
|
- `remap_filter_required`
|
||||||
|
|
||||||
|
### Pricing / publish
|
||||||
|
- `invalid_amount`
|
||||||
|
- `publish_not_ready`
|
||||||
|
|
||||||
|
## Draft summary semantics
|
||||||
|
|
||||||
|
`GET /draft/summary` is the compact route for editor bootstrap.
|
||||||
|
|
||||||
|
It returns:
|
||||||
|
|
||||||
|
- current draft counters
|
||||||
|
- validation summary
|
||||||
|
- structure diff summary
|
||||||
|
- publish readiness summary
|
||||||
|
|
||||||
|
This route is intended for frontend side panels / header status / quick preflight.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Draft-only routes must not mutate a published current version.
|
||||||
|
- Published current version should require `draft/ensure` before any editor mutation.
|
||||||
|
- Publish readiness can fail even if validation passes, for example when pricing snapshot is missing.
|
||||||
|
- `api-map.md` and `smoke-regression.md` must be updated together with route changes.
|
||||||
@@ -244,7 +244,7 @@ async def get_effective_seat_price(scheme_id: str, seat_id: str, role: str = Dep
|
|||||||
matched_rule_level=matched_rule_level,
|
matched_rule_level=matched_rule_level,
|
||||||
matched_target_ref=rule["target_ref"],
|
matched_target_ref=rule["target_ref"],
|
||||||
pricing_category_id=rule["pricing_category_id"],
|
pricing_category_id=rule["pricing_category_id"],
|
||||||
amount=rule["amount"],
|
amount=str(rule["amount"]),
|
||||||
currency=rule["currency"],
|
currency=rule["currency"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
- GET /api/v1/schemes/{scheme_id}/versions
|
- GET /api/v1/schemes/{scheme_id}/versions
|
||||||
- POST /api/v1/schemes/{scheme_id}/versions
|
- POST /api/v1/schemes/{scheme_id}/versions
|
||||||
- GET /api/v1/schemes/{scheme_id}/publish/validation
|
- GET /api/v1/schemes/{scheme_id}/publish/validation
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/publish-readiness
|
||||||
- POST /api/v1/schemes/{scheme_id}/publish
|
- POST /api/v1/schemes/{scheme_id}/publish
|
||||||
- POST /api/v1/schemes/{scheme_id}/unpublish
|
- POST /api/v1/schemes/{scheme_id}/unpublish
|
||||||
- POST /api/v1/schemes/{scheme_id}/rollback
|
- POST /api/v1/schemes/{scheme_id}/rollback
|
||||||
@@ -58,7 +59,6 @@
|
|||||||
## app/api/routes/publish.py
|
## app/api/routes/publish.py
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/pricing/snapshot
|
- POST /api/v1/schemes/{scheme_id}/draft/pricing/snapshot
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview
|
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/publish-readiness
|
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/remap/preview
|
- POST /api/v1/schemes/{scheme_id}/draft/remap/preview
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/remap/apply
|
- POST /api/v1/schemes/{scheme_id}/draft/remap/apply
|
||||||
|
|
||||||
@@ -94,4 +94,4 @@
|
|||||||
- This file is an operational route index, not a generated OpenAPI export.
|
- This file is an operational route index, not a generated OpenAPI export.
|
||||||
- Update this map in the same change set when adding, removing, renaming, or moving routes.
|
- Update this map in the same change set when adding, removing, renaming, or moving routes.
|
||||||
- Query guards such as expected_current_scheme_version_id / expected_scheme_version_id are part of the operational contract for optimistic concurrency on mutable flows.
|
- Query guards such as expected_current_scheme_version_id / expected_scheme_version_id are part of the operational contract for optimistic concurrency on mutable flows.
|
||||||
- Draft/editor routes may legally return 409 draft_not_editable when current version is already published and no editable draft exists yet.
|
- Draft editor flow starts from editor/context and draft/ensure, not from direct blind mutation calls.
|
||||||
|
|||||||
@@ -31,19 +31,78 @@ export SCHEME_ID="82086336d385427f9d56244f9e1dd772"
|
|||||||
- GET /api/v1/schemes/{scheme_id}/current -> 200
|
- GET /api/v1/schemes/{scheme_id}/current -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/versions -> 200
|
- GET /api/v1/schemes/{scheme_id}/versions -> 200
|
||||||
|
|
||||||
## 3. Structure read model
|
Validate:
|
||||||
|
- scheme_id is stable
|
||||||
|
- current version exists
|
||||||
|
- version list contains current version
|
||||||
|
- status and counts are consistent
|
||||||
|
|
||||||
|
## 3. Editor entry flow
|
||||||
|
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/editor/context -> 200
|
||||||
|
- POST /api/v1/schemes/{scheme_id}/draft/ensure -> 200
|
||||||
|
|
||||||
|
Validate:
|
||||||
|
- editor context returns current_scheme_version_id
|
||||||
|
- editor context distinguishes draft vs published state correctly
|
||||||
|
- ensure endpoint is idempotent on current draft
|
||||||
|
- ensure endpoint creates a new draft from published current when needed
|
||||||
|
- returned scheme_version_id is reusable as expected_scheme_version_id
|
||||||
|
|
||||||
|
## 4. Draft read model
|
||||||
|
|
||||||
|
Using current draft version id from draft/ensure:
|
||||||
|
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/summary?expected_scheme_version_id={draft_version_id} -> 200
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/structure?expected_scheme_version_id={draft_version_id} -> 200
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/validation?expected_scheme_version_id={draft_version_id} -> 200
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/compare-preview?expected_scheme_version_id={draft_version_id} -> 200
|
||||||
|
|
||||||
|
Validate:
|
||||||
|
- summary returns total_seats / total_sectors / total_groups
|
||||||
|
- summary returns validation_summary / structure_diff_summary / publish_readiness
|
||||||
|
- structure returns lists for seats / sectors / groups
|
||||||
|
- validation is deterministic
|
||||||
|
- compare preview returns stable diff structure
|
||||||
|
- stale expected_scheme_version_id returns typed 409 conflict
|
||||||
|
|
||||||
|
## 5. Draft entity reads
|
||||||
|
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id} -> 200
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id} -> 200
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id} -> 200
|
||||||
|
|
||||||
|
Validate:
|
||||||
|
- record endpoints return exact draft entities
|
||||||
|
- unknown record id returns 404
|
||||||
|
- stale expected_scheme_version_id returns typed 409 conflict
|
||||||
|
|
||||||
|
## 6. Structure read model
|
||||||
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/current/sectors -> 200
|
- GET /api/v1/schemes/{scheme_id}/current/sectors -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/current/groups -> 200
|
- GET /api/v1/schemes/{scheme_id}/current/groups -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/current/seats -> 200
|
- GET /api/v1/schemes/{scheme_id}/current/seats -> 200
|
||||||
|
|
||||||
## 4. SVG / display pipeline
|
Validate:
|
||||||
|
- total counts are non-negative
|
||||||
|
- known sample scheme returns expected object lists
|
||||||
|
- seats contain seat_id / sector_id / group_id contract where applicable
|
||||||
|
|
||||||
|
## 7. SVG / display pipeline
|
||||||
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/current/svg -> 200
|
- GET /api/v1/schemes/{scheme_id}/current/svg -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/current/svg/display -> 200
|
- GET /api/v1/schemes/{scheme_id}/current/svg/display -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/current/svg/display/meta -> 200
|
- GET /api/v1/schemes/{scheme_id}/current/svg/display/meta -> 200
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/current/svg/display?mode=optimized -> 200 or explicit controlled failure
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/current/svg/display/meta?mode=optimized -> 200 or explicit controlled failure
|
||||||
|
|
||||||
## 5. Pricing read model / diagnostics
|
Validate:
|
||||||
|
- response content type for svg endpoints is image/svg+xml
|
||||||
|
- meta returns scheme_id, scheme_version_id, view_box, width, height
|
||||||
|
- no 500 on passthrough mode
|
||||||
|
- unsupported mode returns 422
|
||||||
|
|
||||||
|
## 8. Pricing read model
|
||||||
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/pricing -> 200
|
- GET /api/v1/schemes/{scheme_id}/pricing -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/pricing/coverage -> 200
|
- GET /api/v1/schemes/{scheme_id}/pricing/coverage -> 200
|
||||||
@@ -51,71 +110,113 @@ export SCHEME_ID="82086336d385427f9d56244f9e1dd772"
|
|||||||
- GET /api/v1/schemes/{scheme_id}/pricing/explain/{seat_id} -> 200
|
- GET /api/v1/schemes/{scheme_id}/pricing/explain/{seat_id} -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/pricing/rules/diagnostics -> 200
|
- GET /api/v1/schemes/{scheme_id}/pricing/rules/diagnostics -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/current/seats/{seat_id}/price -> 200 for priced seat
|
- GET /api/v1/schemes/{scheme_id}/current/seats/{seat_id}/price -> 200 for priced seat
|
||||||
- GET /api/v1/schemes/{scheme_id}/test/seats/{seat_id} -> 200 for priced and unpriced seat
|
- GET /api/v1/schemes/{scheme_id}/test/seats/{seat_id} -> 200 for known seat
|
||||||
|
|
||||||
## 6. Editor entry workflow
|
|
||||||
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/editor/context -> 200 always
|
|
||||||
- if context.needs_new_draft=true -> POST /api/v1/schemes/{scheme_id}/draft/ensure -> 200
|
|
||||||
- after ensure -> GET /api/v1/schemes/{scheme_id}/editor/context -> 200 and editable=true
|
|
||||||
|
|
||||||
Validate:
|
Validate:
|
||||||
- published current version does not break editor bootstrap
|
- pricing bundle contains categories and rules arrays
|
||||||
- ensure returns created_new_draft=true when current was published
|
- coverage values are internally consistent
|
||||||
- ensure returns created_new_draft=false when current was already draft
|
- unpriced seats list explains reason_code / reason_message
|
||||||
|
- explain endpoint shows matched rule for priced seat and null for unpriced seat
|
||||||
|
- diagnostics returns orphan/active rule visibility
|
||||||
|
- test seat preview explains selectable / has_price state
|
||||||
|
- priced test seat amount is serialized as string
|
||||||
|
|
||||||
## 7. Draft editor read model
|
## 9. Draft mutations and validation guards
|
||||||
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/summary -> 200 when current version is draft
|
For current draft version:
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/structure -> 200 when current version is draft
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/validation -> 200 when current version is draft
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/compare-preview -> 200 when current version is draft
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id} -> 200
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id} -> 200
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id} -> 200
|
|
||||||
|
|
||||||
## 8. Draft mutations
|
- POST /api/v1/schemes/{scheme_id}/draft/sectors -> 200 or 422
|
||||||
|
- POST /api/v1/schemes/{scheme_id}/draft/groups -> 200 or 422
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/sectors -> 200 or typed 422 conflict
|
- PATCH /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id} -> 200 or 422
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/groups -> 200 or typed 422 conflict
|
- POST /api/v1/schemes/{scheme_id}/draft/seats/bulk -> 200 or 422
|
||||||
- PATCH /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id} -> 200 or typed 422 validation error
|
- POST /api/v1/schemes/{scheme_id}/draft/remap/preview -> 200 or 422
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/seats/bulk -> 200 or typed 422 validation error
|
- POST /api/v1/schemes/{scheme_id}/draft/remap/apply -> 200 or 422
|
||||||
- PATCH /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id} -> 200
|
|
||||||
- PATCH /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id} -> 200
|
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/repair-references -> 200
|
- POST /api/v1/schemes/{scheme_id}/draft/repair-references -> 200
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/remap/preview -> 200 or typed 422 validation error
|
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/remap/apply -> 200 or typed 422 validation error
|
|
||||||
|
|
||||||
## 9. Publish preview / readiness
|
Validate:
|
||||||
|
- duplicate ids return typed 422
|
||||||
|
- duplicate element binding returns typed 422
|
||||||
|
- unknown sector/group references return typed 422
|
||||||
|
- remap without filters returns typed 422
|
||||||
|
- stale expected_scheme_version_id returns typed 409
|
||||||
|
- published current version rejects draft mutations with typed draft_not_editable conflict
|
||||||
|
|
||||||
|
## 10. Draft publish preview
|
||||||
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/publish/validation -> 200
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/publish-readiness -> 200 when current version is draft
|
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/pricing/snapshot -> 200 when scheme is in draft
|
- POST /api/v1/schemes/{scheme_id}/draft/pricing/snapshot -> 200 when scheme is in draft
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview?refresh=true -> 200
|
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview?refresh=true -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview -> 200
|
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview -> 200
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview?refresh=true&baseline_scheme_version_id={published_version_id} -> 200
|
||||||
|
|
||||||
## 10. Publish lifecycle
|
Validate:
|
||||||
|
- refresh and cached read both succeed
|
||||||
|
- preview summary contains is_publishable / has_structure_changes / has_artifacts / snapshot_available
|
||||||
|
- pricing_coverage is internally consistent
|
||||||
|
- baseline override returns override strategy when explicit baseline is provided
|
||||||
|
- preview retention does not grow unbounded for same version+variant
|
||||||
|
|
||||||
- POST /api/v1/schemes/{scheme_id}/publish -> 200 when draft is ready
|
## 11. Publish readiness and publish flow
|
||||||
- POST /api/v1/schemes/{scheme_id}/publish with stale expected_scheme_version_id -> 409
|
|
||||||
- POST /api/v1/schemes/{scheme_id}/unpublish -> 200
|
|
||||||
- POST /api/v1/schemes/{scheme_id}/rollback -> 200
|
|
||||||
|
|
||||||
## 11. Admin / ops
|
For current draft version:
|
||||||
|
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/publish-readiness -> 200
|
||||||
|
- POST /api/v1/schemes/{scheme_id}/publish?expected_scheme_version_id={draft_version_id} -> 200 or 409
|
||||||
|
|
||||||
|
Validate:
|
||||||
|
- readiness explicitly shows snapshot_available and pricing gate state
|
||||||
|
- publish with stale expected version returns typed 409
|
||||||
|
- publish without draft state returns typed 409
|
||||||
|
- publish success updates current status to published
|
||||||
|
- audit trail contains scheme.published event
|
||||||
|
|
||||||
|
## 12. Admin / ops
|
||||||
|
|
||||||
- GET /api/v1/admin/schemes/{scheme_id}/current/artifacts -> 200
|
- GET /api/v1/admin/schemes/{scheme_id}/current/artifacts -> 200
|
||||||
- GET /api/v1/admin/schemes/{scheme_id}/current/validation -> 200
|
- GET /api/v1/admin/schemes/{scheme_id}/current/validation -> 200
|
||||||
- GET /api/v1/admin/artifacts/publish-preview/audit -> 200
|
- GET /api/v1/admin/artifacts/publish-preview/audit -> 200
|
||||||
- POST /api/v1/admin/artifacts/publish-preview/cleanup?dry_run=true -> 200
|
- POST /api/v1/admin/artifacts/publish-preview/cleanup?dry_run=true -> 200
|
||||||
|
|
||||||
## 12. Audit trail
|
Optional:
|
||||||
|
- POST /api/v1/admin/schemes/{scheme_id}/current/display/regenerate?mode=passthrough -> 200
|
||||||
|
- POST /api/v1/admin/display/backfill?mode=passthrough&limit=10&only_missing=true -> 200
|
||||||
|
|
||||||
|
Validate:
|
||||||
|
- audit endpoint does not report orphan files or missing files for DB rows in normal state
|
||||||
|
- validation report is readable and deterministic
|
||||||
|
- admin routes do not produce 500 for healthy scheme state
|
||||||
|
|
||||||
|
## 13. Audit trail
|
||||||
|
|
||||||
- GET /api/v1/schemes/{scheme_id}/audit -> 200
|
- GET /api/v1/schemes/{scheme_id}/audit -> 200
|
||||||
|
|
||||||
## 13. Fail criteria
|
Validate:
|
||||||
|
- recent publish preview / pricing / version / publish events are present when corresponding operations were run
|
||||||
|
- audit total is non-negative
|
||||||
|
- event payloads stay JSON-serializable
|
||||||
|
|
||||||
Regression is considered failed if:
|
## 14. Fail criteria
|
||||||
|
|
||||||
|
Regression is considered failed if any of the following happen:
|
||||||
|
|
||||||
|
- health or db ping fails
|
||||||
- any stable read endpoint returns 500
|
- any stable read endpoint returns 500
|
||||||
- published current version cannot be converted to draft through ensure flow
|
- passthrough display endpoint fails on known-good sample
|
||||||
- draft editor summary/read endpoints return inconsistent data
|
- publish preview refresh or cached read returns 500
|
||||||
- publish-state mutation guard is bypassed
|
- publish readiness returns 500
|
||||||
|
- editor context or draft ensure returns 500
|
||||||
|
- draft summary / structure / validation / compare-preview returns 500
|
||||||
|
- pricing bundle or diagnostics contract changes unexpectedly
|
||||||
|
- admin audit/cleanup endpoints fail on healthy environment
|
||||||
|
- artifact retention grows without bound for repeated preview refresh on same variant
|
||||||
|
|
||||||
|
## 15. Operator note
|
||||||
|
|
||||||
|
Run this checklist after:
|
||||||
|
- schema changes
|
||||||
|
- pricing schema/repository refactors
|
||||||
|
- artifact lifecycle changes
|
||||||
|
- display pipeline changes
|
||||||
|
- route reorganization
|
||||||
|
- startup/import/config changes
|
||||||
|
- draft lifecycle changes
|
||||||
|
- publish readiness changes
|
||||||
|
|||||||
201
backend/scripts/smoke_regression.sh
Executable file
201
backend/scripts/smoke_regression.sh
Executable file
@@ -0,0 +1,201 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
API_URL="${API_URL:-http://127.0.0.1:9020}"
|
||||||
|
API_KEY="${API_KEY:-admin-local-dev-key}"
|
||||||
|
SCHEME_ID="${SCHEME_ID:-82086336d385427f9d56244f9e1dd772}"
|
||||||
|
|
||||||
|
WORKDIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$WORKDIR"' EXIT
|
||||||
|
|
||||||
|
HDR_AUTH=(-H "X-API-Key: ${API_KEY}")
|
||||||
|
HDR_JSON=(-H "Content-Type: application/json")
|
||||||
|
|
||||||
|
step() {
|
||||||
|
echo
|
||||||
|
echo "===== $1 ====="
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo
|
||||||
|
echo "[FAIL] $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
request() {
|
||||||
|
local name="$1"
|
||||||
|
local method="$2"
|
||||||
|
local url="$3"
|
||||||
|
local expected="$4"
|
||||||
|
local body="${5:-}"
|
||||||
|
local outfile="${WORKDIR}/${name}.body"
|
||||||
|
local codefile="${WORKDIR}/${name}.code"
|
||||||
|
|
||||||
|
if [[ -n "$body" ]]; then
|
||||||
|
curl -sS \
|
||||||
|
-X "$method" \
|
||||||
|
"${HDR_AUTH[@]}" \
|
||||||
|
"${HDR_JSON[@]}" \
|
||||||
|
-o "$outfile" \
|
||||||
|
-w "%{http_code}" \
|
||||||
|
"$url" \
|
||||||
|
--data "$body" > "$codefile"
|
||||||
|
else
|
||||||
|
curl -sS \
|
||||||
|
-X "$method" \
|
||||||
|
"${HDR_AUTH[@]}" \
|
||||||
|
-o "$outfile" \
|
||||||
|
-w "%{http_code}" \
|
||||||
|
"$url" > "$codefile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local code
|
||||||
|
code="$(cat "$codefile")"
|
||||||
|
|
||||||
|
echo "[$method] $url -> $code"
|
||||||
|
cat "$outfile"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ "$code" != "$expected" ]]; then
|
||||||
|
fail "Unexpected HTTP status for ${name}: expected ${expected}, got ${code}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
json_get() {
|
||||||
|
local file="$1"
|
||||||
|
local expr="$2"
|
||||||
|
python3 - <<PY
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
payload = json.loads(Path("$file").read_text())
|
||||||
|
value = payload
|
||||||
|
for part in "$expr".split("."):
|
||||||
|
if part.isdigit():
|
||||||
|
value = value[int(part)]
|
||||||
|
else:
|
||||||
|
value = value[part]
|
||||||
|
print(value if value is not None else "")
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_json_value() {
|
||||||
|
local file="$1"
|
||||||
|
local expr="$2"
|
||||||
|
local expected="$3"
|
||||||
|
local actual
|
||||||
|
actual="$(json_get "$file" "$expr")"
|
||||||
|
if [[ "$actual" != "$expected" ]]; then
|
||||||
|
fail "JSON assertion failed for ${expr}: expected '${expected}', got '${actual}'"
|
||||||
|
fi
|
||||||
|
echo "[OK] ${expr}=${actual}"
|
||||||
|
}
|
||||||
|
|
||||||
|
step "health"
|
||||||
|
curl -sS -i "${API_URL}/healthz" || fail "Health endpoint is unavailable"
|
||||||
|
|
||||||
|
step "system"
|
||||||
|
request "ping" "GET" "${API_URL}/api/v1/ping" "200"
|
||||||
|
request "db_ping" "GET" "${API_URL}/api/v1/db/ping" "200"
|
||||||
|
request "manifest" "GET" "${API_URL}/api/v1/manifest" "200"
|
||||||
|
|
||||||
|
step "scheme current"
|
||||||
|
request "current" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/current" "200"
|
||||||
|
CURRENT_VERSION_ID="$(json_get "${WORKDIR}/current.body" "scheme_version_id")"
|
||||||
|
CURRENT_STATUS="$(json_get "${WORKDIR}/current.body" "status")"
|
||||||
|
echo "CURRENT_VERSION_ID=${CURRENT_VERSION_ID}"
|
||||||
|
echo "CURRENT_STATUS=${CURRENT_STATUS}"
|
||||||
|
|
||||||
|
step "editor context"
|
||||||
|
request "editor_context" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/editor/context" "200"
|
||||||
|
|
||||||
|
step "ensure draft"
|
||||||
|
request "ensure_draft" "POST" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/ensure" "200"
|
||||||
|
DRAFT_VERSION_ID="$(json_get "${WORKDIR}/ensure_draft.body" "scheme_version_id")"
|
||||||
|
DRAFT_CREATED="$(json_get "${WORKDIR}/ensure_draft.body" "created")"
|
||||||
|
echo "DRAFT_VERSION_ID=${DRAFT_VERSION_ID}"
|
||||||
|
echo "DRAFT_CREATED=${DRAFT_CREATED}"
|
||||||
|
|
||||||
|
step "draft summary"
|
||||||
|
request "draft_summary" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/summary?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
assert_json_value "${WORKDIR}/draft_summary.body" "scheme_version_id" "${DRAFT_VERSION_ID}"
|
||||||
|
assert_json_value "${WORKDIR}/draft_summary.body" "status" "draft"
|
||||||
|
|
||||||
|
step "draft structure"
|
||||||
|
request "draft_structure" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/structure?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
assert_json_value "${WORKDIR}/draft_structure.body" "scheme_version_id" "${DRAFT_VERSION_ID}"
|
||||||
|
SEAT_RECORD_ID="$(json_get "${WORKDIR}/draft_structure.body" "seats.0.seat_record_id")"
|
||||||
|
SECTOR_RECORD_ID="$(json_get "${WORKDIR}/draft_structure.body" "sectors.0.sector_record_id")"
|
||||||
|
GROUP_RECORD_ID="$(json_get "${WORKDIR}/draft_structure.body" "groups.0.group_record_id")"
|
||||||
|
PRICED_SEAT_ID="$(json_get "${WORKDIR}/draft_structure.body" "seats.0.seat_id")"
|
||||||
|
UNPRICED_SEAT_ID="$(json_get "${WORKDIR}/draft_structure.body" "seats.2.seat_id")"
|
||||||
|
echo "SEAT_RECORD_ID=${SEAT_RECORD_ID}"
|
||||||
|
echo "SECTOR_RECORD_ID=${SECTOR_RECORD_ID}"
|
||||||
|
echo "GROUP_RECORD_ID=${GROUP_RECORD_ID}"
|
||||||
|
echo "PRICED_SEAT_ID=${PRICED_SEAT_ID}"
|
||||||
|
echo "UNPRICED_SEAT_ID=${UNPRICED_SEAT_ID}"
|
||||||
|
|
||||||
|
step "draft validation"
|
||||||
|
request "draft_validation" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/validation?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
assert_json_value "${WORKDIR}/draft_validation.body" "status" "draft"
|
||||||
|
|
||||||
|
step "draft compare preview"
|
||||||
|
request "draft_compare" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/compare-preview?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
|
||||||
|
step "stale draft conflict"
|
||||||
|
request "draft_summary_stale" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/summary?expected_scheme_version_id=deadbeefdeadbeefdeadbeefdeadbeef" "409"
|
||||||
|
assert_json_value "${WORKDIR}/draft_summary_stale.body" "detail.code" "stale_draft_version"
|
||||||
|
|
||||||
|
step "draft record reads"
|
||||||
|
request "draft_seat_record" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/seats/records/${SEAT_RECORD_ID}?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
request "draft_sector_record" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/sectors/records/${SECTOR_RECORD_ID}?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
request "draft_group_record" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/groups/records/${GROUP_RECORD_ID}?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
request "draft_unknown_record" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/seats/records/deadbeefdeadbeefdeadbeefdeadbeef" "404"
|
||||||
|
|
||||||
|
step "structure read model"
|
||||||
|
request "current_sectors" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/current/sectors" "200"
|
||||||
|
request "current_groups" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/current/groups" "200"
|
||||||
|
request "current_seats" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/current/seats" "200"
|
||||||
|
|
||||||
|
step "svg display pipeline"
|
||||||
|
request "current_svg_meta" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/current/svg/display/meta" "200"
|
||||||
|
|
||||||
|
step "pricing read model"
|
||||||
|
request "pricing_bundle" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing" "200"
|
||||||
|
request "pricing_coverage" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/coverage" "200"
|
||||||
|
request "pricing_unpriced" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/unpriced-seats" "200"
|
||||||
|
request "pricing_explain_priced" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/explain/${PRICED_SEAT_ID}" "200"
|
||||||
|
request "pricing_explain_unpriced" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/explain/${UNPRICED_SEAT_ID}" "200"
|
||||||
|
request "pricing_diagnostics" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/rules/diagnostics" "200"
|
||||||
|
request "seat_price" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/current/seats/${PRICED_SEAT_ID}/price" "200"
|
||||||
|
request "test_mode_priced" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/test/seats/${PRICED_SEAT_ID}" "200"
|
||||||
|
request "test_mode_unpriced" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/test/seats/${UNPRICED_SEAT_ID}" "200"
|
||||||
|
|
||||||
|
step "typed validation errors"
|
||||||
|
request "invalid_amount" "POST" "${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/rules?expected_scheme_version_id=${DRAFT_VERSION_ID}" "422" '{"pricing_category_id":"deadbeefdeadbeefdeadbeefdeadbeef","target_type":"seat","target_ref":"seat-x","amount":"bad","currency":"RUB"}'
|
||||||
|
assert_json_value "${WORKDIR}/invalid_amount.body" "detail.code" "invalid_amount"
|
||||||
|
|
||||||
|
request "remap_no_filters" "POST" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/remap/preview?expected_scheme_version_id=${DRAFT_VERSION_ID}" "422" '{"seat_record_ids":null,"from_sector_id":null,"to_sector_id":"vip","from_group_id":null,"to_group_id":null}'
|
||||||
|
assert_json_value "${WORKDIR}/remap_no_filters.body" "detail.code" "remap_filter_required"
|
||||||
|
|
||||||
|
step "draft pricing snapshot"
|
||||||
|
request "draft_snapshot" "POST" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/pricing/snapshot?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
|
||||||
|
step "publish readiness"
|
||||||
|
request "publish_readiness" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/publish-readiness?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
|
||||||
|
step "publish preview"
|
||||||
|
request "publish_preview_refresh" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/publish-preview?refresh=true&expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
request "publish_preview_cached" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/publish-preview?expected_scheme_version_id=${DRAFT_VERSION_ID}" "200"
|
||||||
|
|
||||||
|
step "admin ops"
|
||||||
|
request "admin_artifacts" "GET" "${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/current/artifacts" "200"
|
||||||
|
request "admin_validation" "GET" "${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/current/validation" "200"
|
||||||
|
request "admin_preview_audit" "GET" "${API_URL}/api/v1/admin/artifacts/publish-preview/audit" "200"
|
||||||
|
request "admin_preview_cleanup" "POST" "${API_URL}/api/v1/admin/artifacts/publish-preview/cleanup?dry_run=true" "200"
|
||||||
|
|
||||||
|
step "audit trail"
|
||||||
|
request "audit" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/audit" "200"
|
||||||
|
|
||||||
|
step "done"
|
||||||
|
echo "[OK] smoke regression completed successfully"
|
||||||
Reference in New Issue
Block a user