|
|
6e89b0ccf7
|
test(form-builder): feature suites + integration contracts incl. FORM-02 (§31.10)
Phase 6 of S2b. 37 new tests, 820 → 857 passing across the suite.
Feature suites (api/tests/Feature/FormBuilder/):
- FormSchemaApiTest: CRUD, publish/unpublish, rotate-public-token (with
grace window), edit-lock conflict, typed-confirmation delete, 401 on
unauthenticated, 403 on outsider.
- FormFieldApiTest: create, reorder, binding-change guard (422 w/o force,
200 with force), conditional_logic cycle rejection, 401 unauth.
- FormSubmissionApiTest: draft → values → submit stores schema snapshot +
version; review records reviewer; delegation creates active row; draft
update blocked for non-subject non-delegatee (403).
- FormValueSecurityTest: FieldAccessService hides admin-only fields from
non-admin; subject-self bypass; admin-only field leaks through neither
admin list nor non-admin detail responses (§22.9 intent).
- PublicFormApiTest: portal-visible non-admin fields only; unknown token
→ 404; happy-path submission; expired-previous-token → 410; grace
window still allows submission.
- FormSchemaWebhookApiTest: url/secret NEVER returned in resources;
DeliverFormWebhookJob rejects 10.x private-ip SSRF (response_body_excerpt
logs rejection).
- FilterRegistryApiTest: response shape includes tags + form_field
sources; form_field filter registers.
Integration contract (§31.10):
- TagPickerSyncListenerTest: 5 cases proving (a) no-op on user_id=null,
(b) sync on submit, (c) deferred sync via
PersonIdentityService::confirmMatch, (d) organiser_assigned tags
preserved on rebuild, (e) idempotent rerun.
Fixes discovered while writing tests:
- SyncTagPickerSelectionsOnSubmit: removed hardcoded connection='redis'
so tests run via sync queue (QUEUE_CONNECTION fallback).
- FormSubmissionService: corrected FormSubmissionReviewed / DraftUpdated
event signatures to match S1 event classes.
- FormSubmission model: added schema_version_at_submit / snapshot /
anonymised_at / submission_duration_seconds / auto_save_count to
$fillable so bulk operations + factory states populate consistently.
- FormSchema: added version, edit_lock_user_id, edit_lock_expires_at to
$fillable; factory now sets version=1 explicitly.
- FormValueService: public submission path (actor=null) enforces
is_portal_visible=true AND is_admin_only=false at the write layer
instead of running FieldAccessService against a null user.
- MigrationRollbackTest: target the S2a drop migration by filename.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-17 21:27:27 +02:00 |
|
|
|
b3eab6e0c8
|
feat(form-builder): add core services (schema, field, submission, value, field-access, locale, tag-sync, filter, webhook, anonymisation)
S2b Phase 1 per ARCH-FORM-BUILDER.md §20.2. Ten services + supporting
exceptions, jobs, and the organisations.default_locale column needed by
FormLocaleResolver. All services log via spatie/laravel-activitylog, write
operations are transactional, queued jobs are idempotent.
- FormSchemaService: CRUD, slug, version bump, duplicate, edit-lock,
public_token rotation (7-day grace window), typed-confirmation delete.
- FormFieldService: CRUD, reorder, insertFromLibrary, binding-change guard
(§6.5), conditional_logic + section cycle detection (§8, §4.8.1),
is_filterable toggle triggers BackfillFormValueIndexedJob (§7.2, §22.10).
- FormSubmissionService: createDraft with idempotency, saveDraft (auto-save),
submit with schema snapshot + signature hash computation (§9), review,
delegate/revoke, soft delete. Fires S1 domain events (§17.1).
- FormValueService: bulk upsert with FieldAccessService RBAC (§24.2),
Pattern A/C entity mirror writes (§6.1, §6.6) with cross-entity graceful
skip for person.user_id=null.
- FieldAccessService: canRead/canWrite/filterVisibleFields honouring
role_restrictions + subject-self (§18.3, §24.1).
- FormLocaleResolver: submitter → schema → org.default_locale → 'nl' (§16.2).
- FormTagSyncService: rebuildForPerson — replaces legacy TagSyncService
deleted in S2a (§31.10).
- FilterQueryBuilder: generic filter applier for entity_column / tags /
form_field sources (§7.4–§7.5).
- FormWebhookDispatcher + DeliverFormWebhookJob: HMAC-signed delivery with
SSRF protection, exponential backoff {1m,5m,30m,2h,8h}, max 5 attempts,
dead-letter on exhaustion (§17.5).
- FormSubmissionAnonymisationService: per-field anonymisation with separate
activity log entries (§13.3, §23.4).
MigrationRollbackTest: pin the S2a drop migration by filename so future
migrations don't shift the step offset.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-17 20:47:39 +02:00 |
|