Files
agiledancers/docs/validation/S16-validation.md
Wanja Krah c6a495a93c
All checks were successful
Build, test and publish / verify (push) Successful in 8m54s
S19: consume versioned engineering resources and move landscape documentation
2026-11-08 15:23:10 +01:00

12 KiB
Raw Blame History

Historical validation evidence from application commit bd898e92d22f6c412e671908770f4d89c8ae12bc. Commands retain their original paths; current tooling is documented in README.md.

S16 implementation validation

Delivered behavior

PostgreSQL owns normalized school, dancer, coach, group, membership, style, show, daypart, appearance and ordered-assignment tables, plus the latest scheduling run. Flyway V1 creates the schema, persistent identifier sequence, cross-school-safe foreign keys and row constraints. School keys, runtime identifiers, relationship identity, coach links, draft assignments and finalized state survive reload.

Application persistence uses JPA/Hibernate ORM with mapped entities in repository.entity and EntityManager repositories. Relationships, element collections, daypart order and orphan removal are managed by Hibernate; domain objects and API responses remain framework independent. The hand-written JDBC query/update layer and row readers have been removed.

The HTTP interceptor runs each school operation in a short repeatable-read JTA transaction under the existing domain monitor. Reads convert managed entities into detached domain snapshots. Hibernate's school @Version checks optimistic changes; a change stamp marks child-only edits so the school revision advances exactly once. Missing or stale caller revisions conflict before applying edits, and stale detached JPA entities also fail. Successful graph merges flush before commit, and status publication remains after durable commit. A single school-data endpoint gives the editor a consistent data/revision snapshot. Open editors retain their original revision across background refreshes; conflict responses preserve their input and require reload before retry. Replacement imports retain the target school ID and check the revision returned by preview. Existing JSON import/export semantics remain.

Domain-to-view projection now uses MapStruct 1.6.3 in the REST layer. The generated CDI DomainViewMapper covers schools, dancers, groups, coaches, dayparts and shows. A shared ID conversion projects relationships to ordered ID lists; validation results remain an explicit argument and preferred-daypart defaults delegate to the existing application rule. View record constructors retain their defensive copies. Entity to domain graph construction remains explicit in the persistence adapter. unmappedTargetPolicy = ERROR rejects missing target fields at compilation. A compiler probe with the actual mapper and an added unmapped SchoolView component confirms this failure, without modifying maintained sources. MapStruct's generated implementation is marked lombok.Generated, preserving the existing generated-code scope of coverage and architecture checks; handwritten mapper helpers remain covered and checked.

Flyway remains the schema authority; Hibernate validates rather than generates DDL. V1 is unchanged. V2 preserves existing rows, adds the aggregate change stamp and a JPA-managed identifier counter seeded above previously allocated IDs, and defers date/type uniqueness until commit so Hibernate can reorder retained dayparts safely. Identifier allocation takes a pessimistic lock on the counter row. Coach/dancer and group/coach associations use Hibernate composite join mappings to share the owning school key while letting Hibernate update the linked member column directly.

Run acceptance checks the school revision and persists the run before launch or acknowledgement. Acceptance itself does not change scheduling inputs. Background computation holds neither a database transaction nor the domain monitor. Progress commits before publication. Successful assignment replacement, school revision and terminal run status commit together; publication callbacks run only after commit. Deferred constraint failures roll back both assignments and status without publishing success. Database failure never falls back to acknowledged memory-only writes. Disconnected status subscribers cannot turn a committed result into a solver failure.

Startup recovery marks persisted Running runs Interrupted before serving status, retains their IDs, progress and previous saved assignments, and creates no old run guards. The frontend recognizes Interrupted and retains S12's reconnect/reload notification behavior. Timestamps use PostgreSQL microsecond precision consistently in live and recovered snapshots.

Verification

  • Maven clean verify: all 72 backend tests and 19 standards-checker regression tests pass. The JPA integration run also verified Flyway V1 and V2 and Hibernate schema validation against an empty database, as well as an upgrade of the populated V1 test database.
  • Backend line coverage: 1763/1827 (96.50%), above the 80% gate.
  • Frontend: 32 unit tests, production build and ESLint pass (unchanged by the MapStruct-only backend change).
  • Generated-mapper CDI tests cover ordered relationship IDs, an unlinked coach, nested assignment order, defensive copies, explicit validation messages and the configured/default preferred-daypart rule. The compile-time unmapped-field probe passes with the expected compiler error.
  • Playwright: the three existing real REST/browser scenarios pass with PostgreSQL; a fourth scenario verifies two screens editing the same revision, conflict handling with retained form input, and successful reload/retry.
  • PostgreSQL tests cover simultaneous edits (one succeeds, one conflicts), missing revisions, relational graph recovery, IDs and ordered assignments, finalized state, deferred commit failure without success publication, failed replacement import rollback, stale scheduling inputs and interrupted-run recovery. JPA-specific tests verify stale detached entities, exactly one aggregate version increment for child-only edits, and reordered dayparts retaining their assignments.
  • tooling/persistence/restart_test.py runs the packaged production backend against an isolated persistent PostgreSQL container. It passes graceful restart and SIGKILL interruption checks, verifies a completed saved result after every restart, checks stable recovered IDs through HTTP and SSE, starts a new run after interruption, rejects mutations during a database outage, and verifies startup failure for an unreachable database and a deliberately failing Flyway migration.
  • CI helper tests: 17 pass. Release and actual deployment-shell behavior tests: 32 pass. Shell syntax and Java/frontend formatting checks pass.

The packaged-process checks deliberately stop/restart only the named disposable test database. Production deployment was not performed. Database provisioning and transition instructions are in docs/persistent-storage.md; export every school before stopping the old in-memory release. Production Compose retains its database volume outside application replacement. CI supplies a separate disposable browser database and removes its anonymous volume during cleanup.

Manual standards assessment

No waivers or weaker gates were added. Architecture tests pass. Persistence entities and their embeddable keys belong to the repository layer, while the scheduling records remain in model.scheduling. Mutable classes, no-argument construction, setters and replaceable collections are deliberate JPA lifecycle requirements; Hibernate installs managed collection wrappers and hydrates fields. Composite key values are assigned before persistence and are not mutated afterward. None of these entities or lazy collections escapes into the solver or the API. Existing immutable snapshot records still defensively copy mutable collections.

Retained review signals in changed code:

  • Database fan-out 27 and transaction cyclomatic complexity 8: JPA/JTA lifecycle, rollback and heuristic failures, optimistic-conflict classification, and committed publication are handled in one transaction owner. Its two injected dependencies remain below the review signal.
  • SchoolWriter fan-out 17: one adapter builds and merges the school graph, removes deleted-show statuses, checks JPA revisions and flushes changes. PeopleMapping, GroupMapping and ShowMapping separate domain conversion from ORM lifecycle.
  • Entity fan-out reflects explicit relationship and collection annotations: DayPartEntity 22, GroupEntity 24, RunEntity 17, SchoolEntity 18 and ShowEntity 23. Each maps one existing table/aggregate boundary; splitting fields merely to lower annotation-driven fan-out would obscure the relational mappings. Entity collections are mutable only within the persistence adapter and Hibernate session.
  • SchoolTransactions fan-out 19 and request cyclomatic complexity 9: four collaborators cover the detached domain monitor, transaction owner, loader and writer. The branch structure distinguishes reads, run acceptance and domain mutations and maps storage failures separately from optimistic conflicts. SchoolChange's four components keep the revision and operation classification together.
  • SchedulingRuns fan-out 31: it coordinates immutable input/progress/run/result records, the solver port, durable lifecycle operations and subscriber callbacks; its three injected collaborators remain below the dependency review signal.
  • SchoolManagementService fan-out 38, 30 public methods, beginScheduling cyclomatic complexity 8 and the existing five-parameter updateShow are retained: aggregate use cases and validations remain together. Load installs detached state and resets transient input counters under the existing monitor.
  • REST resource fan-out is 18–28 (DanceSchoolResource 25): resources combine HTTP annotations, use cases, request/view contracts and the injected projection mapper. The consistent data endpoint assembles views inside the transaction. DomainViewMapper fan-out 25 represents six explicit source/target projections plus the MapStruct annotations and two small shared helpers. DomainViewMapperTest fan-out 22 exercises the generated CDI mapper's API edge cases. SchoolView's six components include the revision; SchoolDataView's five components represent one editor snapshot. ImportPreview's seventh component identifies the target revision for replacement.
  • PersistenceTest fan-out 47, seven injected fixtures, 335 code lines and methods of 41/33/42 lines: these cross real ORM, graph and transaction boundaries, including detached-entity conflicts, commit-time failure, failed import, child versioning and reordered assignments. The fixtures remain below all blocking limits. Existing changed REST/SSE/background fixture metrics retain their S12/S7 rationale.

One active backend remains the supported deployment model. Global execution capacity, distributed workers, database backup/restore and recovery after database volume loss remain outside S16. S17 owns backup/restore.

Review follow-up: database recovery during calculation

Terminal scheduling outcomes whose transaction fails remain pending in their worker. The worker retries storage once per second without holding the school monitor between attempts, retaining its run guard until the terminal status commits. Publication still happens only after commit. Shutdown interrupts the wait; startup recovery remains responsible for persisted Running statuses after a process restart. If the show was removed during the outage, the pending outcome is discarded rather than recreating its status. The finish method's cognitive complexity 12 and cyclomatic complexity 8 distinguish retryable storage failures, removed shows and shutdown; these lifecycle decisions stay in the run owner. SchedulingRuns fan-out is now 34 and its test fixture fan-out 35: the added storage exception, interruption handling and outage fixture belong to this existing persistence lifecycle boundary.

The regression test simulates unavailable storage during result persistence, verifies that no terminal status is published prematurely and duplicate starts remain rejected, then restores storage without restarting the backend and checks the stable run ID, persisted failure status and released guard. Public transaction/callback documentation now specifies transaction ownership, callback timing and exception behavior.

Follow-up verification: clean Maven verify against PostgreSQL passes all 73 backend tests and 19 standards-checker tests. Standards and the coverage gate pass; backend line coverage is 1769/1841 (96.09%).