Files
svg-backend/backend/docs/smoke-regression.md
greebo 210981c953 test(backend): split smoke regression into core and pricing publish flows
separate smoke coverage into core backend checks and pricing publish flow checks

make regression runs more focused and easier to maintain
improve troubleshooting when a smoke stage fails
2026-03-20 13:25:32 +03:00

357 lines
13 KiB
Markdown

# Smoke regression checklist
This file is the backend manual regression baseline for svg-service.
## Preconditions
- docker compose stack is up
- backend responds on port 9020
- valid admin API key is available
- stable SVG fixture exists in repository, e.g. `sample-contract.svg`
## Environment
Use these variables in shell:
export API_URL="http://127.0.0.1:9020"
export API_KEY="admin-local-dev-key"
export FIXTURE_SVG_PATH="/home/adminko/svg-service/sample-contract.svg"
## Main scripts
Primary operator regressions:
- `backend/scripts/smoke_core.sh`
- `backend/scripts/smoke_pricing_publish.sh`
- `backend/scripts/smoke_regression.sh`
- `backend/scripts/editor_mutation_regression.sh`
The scripts are expected to fail fast on any contract break or unexpected 5xx.
`smoke_regression.sh` is now an orchestration wrapper:
- first runs `smoke_core.sh`
- then runs `smoke_pricing_publish.sh`
- returns non-zero if either scenario fails
## Scenario split
### Core smoke on clean DB
Use:
- `backend/scripts/smoke_core.sh`
This scenario is designed for a fully clean database.
It uploads a fresh SVG fixture, resolves the created `scheme_id`, validates current/draft read models, validates empty pricing state, and then runs `editor_mutation_regression.sh` on the same fresh scheme.
Important:
- it does not require pre-existing `scheme_id`
- it does not require pricing categories or price rules
- it does not require publish snapshot or published baseline
- empty pricing on a fresh upload is a valid state, not a failure
### Pricing/publish smoke with fixture setup
Use:
- `backend/scripts/smoke_pricing_publish.sh`
This scenario also uploads a fresh SVG fixture, then prepares its own pricing fixture before validating pricing and publish flow.
Important:
- it creates its own pricing category
- it creates its own pricing rule
- it intentionally checks both a priced seat and an unpriced seat on the same fresh scheme
- it does not rely on historical pricing IDs, rules, or old schemes
## 1. Health / system
- GET /healthz -> 200 (smoke uses a bounded retry/wait loop and fails explicitly if the API never becomes ready)
- GET /api/v1/ping -> 200
- GET /api/v1/db/ping -> 200
- GET /api/v1/manifest -> 200
## 2. Core smoke coverage
`smoke_core.sh` checks:
- GET /healthz -> 200
- GET /api/v1/ping -> 200
- GET /api/v1/db/ping -> 200
- GET /api/v1/manifest -> 200
- POST /api/v1/schemes/upload -> 200
- GET /api/v1/schemes -> 200 and resolves the fresh `scheme_id`
- GET /api/v1/schemes/{scheme_id} -> 200
- GET /api/v1/schemes/{scheme_id}/versions -> 200
- GET /api/v1/schemes/{scheme_id}/current -> 200
- GET /api/v1/schemes/{scheme_id}/editor/context -> 200
- POST /api/v1/schemes/{scheme_id}/draft/ensure -> 200
- GET /api/v1/schemes/{scheme_id}/draft/summary -> 200
- GET /api/v1/schemes/{scheme_id}/draft/structure -> 200
- GET /api/v1/schemes/{scheme_id}/draft/validation -> 200
- GET /api/v1/schemes/{scheme_id}/draft/compare-preview -> 200
- GET draft entities by record id -> 200
- stale `expected_scheme_version_id` conflict -> 409 with typed `stale_draft_version`
- GET current sectors/groups/seats -> 200
- GET current SVG display meta -> 200
- GET pricing bundle -> 200 with empty categories/rules
- GET pricing coverage -> 200 with zero priced seats
- GET pricing explain/{seat_id} -> 200 with `no_price_rule`
- GET pricing rules diagnostics -> 200 with empty state
- GET audit -> 200
- `backend/scripts/editor_mutation_regression.sh` on the same fresh scheme
Validate:
- fresh upload is readable immediately through current/draft/editor endpoints
- empty pricing is accepted as normal state for a newly uploaded scheme
- no endpoint in core smoke returns 500
## 3. Pricing/publish smoke coverage
`smoke_pricing_publish.sh` checks:
- POST /api/v1/schemes/upload -> 200
- GET current / POST draft ensure on the fresh scheme -> 200
- POST pricing category -> 200
- POST price rule -> 200
- GET pricing bundle -> 200 with created fixture data
- GET pricing coverage -> 200 with both priced and unpriced seats present
- GET pricing explain/{priced_seat_id} -> 200 with matched rule
- GET pricing explain/{unpriced_seat_id} -> 200 with `no_price_rule`
- GET current/seats/{priced_seat_id}/price -> 200
- GET test/seats/{priced_seat_id} -> 200
- GET test/seats/{unpriced_seat_id} -> 200
- POST draft/pricing/snapshot -> 200
- GET draft/publish-readiness -> 200
- GET draft/publish-preview?refresh=true -> 200
- GET draft/publish-preview -> 200
- POST publish -> 200
- GET scheme detail/current after publish -> 200 and published state
- GET audit -> 200 and contains `scheme.published`
Validate:
- fixture setup is fully self-contained
- priced-seat checks happen only after explicit pricing fixture creation
- publish flow is validated on a fresh scheme, not on historical DB data
## 4. Legacy endpoint families
The sections below remain the API baseline by area, but regression execution is now split between clean-DB core smoke and pricing/publish smoke.
## 5. Scheme registry
- GET /api/v1/schemes -> 200
- GET /api/v1/schemes/{scheme_id} -> 200
- GET /api/v1/schemes/{scheme_id}/current -> 200
- GET /api/v1/schemes/{scheme_id}/versions -> 200
Validate:
- scheme_id is stable
- current version exists
- version list contains current version
- status and counts are consistent
## 6. 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
## 7. 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
## 8. 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
## 9. Structure read model
- 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/seats -> 200
Validate:
- total counts are non-negative
- known sample scheme returns expected object lists
- seats contain seat_id / sector_id / group_id contract where applicable
## 10. SVG / display pipeline
- 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/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
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
## 11. Pricing read model
- GET /api/v1/schemes/{scheme_id}/pricing -> 200
- GET /api/v1/schemes/{scheme_id}/pricing/coverage -> 200
- GET /api/v1/schemes/{scheme_id}/pricing/unpriced-seats -> 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}/current/seats/{seat_id}/price -> 200 only after pricing fixture exists
- GET /api/v1/schemes/{scheme_id}/test/seats/{seat_id} -> 200 for known seat
Validate:
- fresh clean upload is allowed to have `categories=[]` and `rules=[]`
- fresh clean upload is allowed to have zero priced seats and `no_price_rule` explanations
- priced seat checks belong to pricing/publish smoke after fixture setup
- diagnostics returns stable empty state with zero rules on clean upload
- diagnostics returns matched seat visibility after fixture setup
- priced test seat amount is serialized as string when pricing exists
## 12. Draft mutation regression
Use:
- `backend/scripts/editor_mutation_regression.sh`
This script checks:
- create sector
- create group
- patch seat
- bulk seat update
- patch sector
- patch group
- duplicate entity validation paths
- stale draft conflict
- remap preview validation path
- repair references
- delete created sector/group
- post-mutation read-model consistency
Validate:
- created entities are returned by API
- patched draft records are actually changed
- bulk update changes persisted fields
- duplicate ids return 422
- stale expected_scheme_version_id returns typed 409
- remap preview without filters returns typed 422
- post-mutation summary / validation / compare-preview remain readable and deterministic
## 13. Draft publish preview
- 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 -> 200
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview?refresh=true&baseline_scheme_version_id={published_version_id} -> 200
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
## 14. Publish readiness and publish flow
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
## 15. Admin / ops
- GET /api/v1/admin/schemes/{scheme_id}/current/artifacts -> 200
- GET /api/v1/admin/schemes/{scheme_id}/current/validation -> 200
- GET /api/v1/admin/artifacts/publish-preview/audit -> 200
- POST /api/v1/admin/artifacts/publish-preview/cleanup?dry_run=true -> 200
- GET /api/v1/admin/schemes/{scheme_id}/pricing/categories/cleanup-preview -> 200
- POST /api/v1/admin/schemes/{scheme_id}/pricing/categories/cleanup with dry_run=true -> 200
Validate:
- artifact audit does not report orphan files or missing files for DB rows in normal state
- validation report is readable and deterministic
- pricing cleanup preview returns matched candidates and safe_to_delete_count
- pricing cleanup dry-run returns deleted_count=0
- idempotent cleanup is valid in both states: `matched_total=0` with `would_delete_count=0`, or `matched_total>0` with `would_delete_count>0`
- smoke does not require cleanup dry-run to always find something to delete
- admin routes do not produce 500 for healthy scheme state
## 16. Audit trail
- GET /api/v1/schemes/{scheme_id}/audit -> 200
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
## 17. Fail criteria
Regression is considered failed if any of the following happen:
- health or db ping fails
- any stable read endpoint returns 500
- passthrough display endpoint fails on known-good sample
- publish preview refresh or cached read returns 500
- publish readiness returns 500
- editor context or draft ensure returns 500
- draft summary / structure / validation / compare-preview returns 500
- editor mutation regression returns non-zero exit code
- clean upload empty pricing state is treated as a failure
- pricing bundle or diagnostics contract changes unexpectedly
- admin audit/cleanup endpoints fail on healthy environment
- pricing cleanup dry-run mutates data
- artifact retention grows without bound for repeated preview refresh on same variant
## 18. 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
- admin cleanup changes
- editor mutation changes