From 3d2608d9922bd80b6ded62371e5cc2091ccf8de5 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Sun, 26 Apr 2026 17:00:51 +0200 Subject: [PATCH] test(form-builder): write-path invariant for conflict-resolver candidate set (WS-6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asserts the RFC Q7 prerequisite: every visible form_field has a form_values row after submit (even null/empty), every absent field has none. This is the invariant the BindingConflictResolver relies on to distinguish 'explicit clear' from 'skipped by conditional logic'. Refs: RFC-WS-6.md §3 (Q7) Co-Authored-By: Claude Opus 4.7 (1M context) --- .../FormValuesWritePathInvariantTest.php | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 api/tests/Feature/FormBuilder/Bindings/FormValuesWritePathInvariantTest.php diff --git a/api/tests/Feature/FormBuilder/Bindings/FormValuesWritePathInvariantTest.php b/api/tests/Feature/FormBuilder/Bindings/FormValuesWritePathInvariantTest.php new file mode 100644 index 00000000..4a7583fc --- /dev/null +++ b/api/tests/Feature/FormBuilder/Bindings/FormValuesWritePathInvariantTest.php @@ -0,0 +1,82 @@ +create(); + $field = FormField::factory()->create([ + 'form_schema_id' => $schema->id, + 'slug' => 'comments', + 'field_type' => FormFieldType::TEXT->value, + ]); + $submission = FormSubmission::factory()->create([ + 'form_schema_id' => $schema->id, + 'organisation_id' => $schema->organisation_id, + ]); + + $this->app->make(FormValueService::class)->upsertMany( + $submission, + ['comments' => ''], + null, + ); + + $row = FormValue::query() + ->withoutGlobalScopes() + ->where('form_submission_id', $submission->id) + ->where('form_field_id', $field->id) + ->first(); + $this->assertNotNull($row, 'A form_value row MUST exist for an explicitly-blank submitted field (RFC Q7 invariant).'); + } + + public function test_field_not_submitted_has_no_form_value_row(): void + { + // Absence in slugToValue → no row written. This is the "skipped by + // conditional logic" path that RFC Q7 distinguishes from explicit + // null — the resolver excludes it from the candidate set. + $schema = FormSchema::factory()->create(); + $field = FormField::factory()->create([ + 'form_schema_id' => $schema->id, + 'slug' => 'visible_field', + ]); + $submission = FormSubmission::factory()->create([ + 'form_schema_id' => $schema->id, + 'organisation_id' => $schema->organisation_id, + ]); + + $this->app->make(FormValueService::class)->upsertMany($submission, [], null); + + $row = FormValue::query() + ->withoutGlobalScopes() + ->where('form_submission_id', $submission->id) + ->where('form_field_id', $field->id) + ->first(); + $this->assertNull($row, 'Absent form_field MUST have no form_value row (RFC Q7 invariant).'); + } +}