create(['email_verified_at' => now()]); $this->actingAs($owner)->postJson('/investidor/terra-duat', ['payload' => ['full_name' => 'Pessoa sintética QA', 'country' => 'MZ', 'declared_origin_country_code' => 'PT']])->assertCreated(); $case = LandAssistanceCase::firstOrFail(); $this->actingAs($owner)->get('/investidor/terra-duat/'.$case->id.'/imprimir') ->assertOk()->assertInertia(fn (Assert $page) => $page->component('land-assistance/print') ->where('country.code', 'MZ')->where('declaredOrigin.code', 'PT') ->where('case.version', 1)->where('case.payload.country', 'MZ')->where('case.payload.declared_origin_country_code', 'PT')); $this->actingAs(User::factory()->create(['email_verified_at' => now()])) ->get('/investidor/terra-duat/'.$case->id.'/imprimir')->assertNotFound(); $this->assertSame(1, $case->fresh()->version); } public function test_public_guide_and_blank_form_are_available_without_claiming_official_status(): void { $this->get('/terra-duat')->assertOk()->assertInertia(fn (Assert $page) => $page->component('land-assistance/guide')); $this->get('/terra-duat/formulario-em-branco')->assertOk()->assertInertia(fn (Assert $page) => $page->component('land-assistance/blank')); } public function test_private_draft_and_support_are_versioned_and_create_one_crm_receipt(): void { $owner = User::factory()->create(['email_verified_at' => now()]); $payload = [ 'support_kind' => 'investment', 'request_description' => 'Preciso de orientação para preparar um pedido de apoio sobre um terreno.', 'applicant_category' => 'individual', 'full_name' => 'Pessoa de Teste', 'email' => 'terra@example.test', 'phone' => null, 'preferred_contact' => 'email', 'country' => 'Moçambique', 'area_status' => 'identified', 'purpose' => 'Projecto agrícola exploratório', 'parcels' => [], 'contact_consent' => true, 'truth_declaration' => true, ]; $this->actingAs($owner)->postJson('/investidor/terra-duat', ['payload' => $payload])->assertCreated(); $case = LandAssistanceCase::firstOrFail(); $this->assertSame(1, $case->version); $this->assertSame('draft', $case->status); $this->assertDatabaseCount('land_assistance_revisions', 1); $this->postJson("/investidor/terra-duat/{$case->id}/solicitar-apoio", ['version' => 1, 'payload' => $payload])->assertAccepted(); $case->refresh(); $this->assertSame('support_requested', $case->status); $this->assertSame(2, $case->version); $this->assertNotNull($case->interest_request_id); $this->assertDatabaseCount('interest_requests', 1); $this->assertDatabaseCount('interest_submission_receipts', 1); $this->assertDatabaseCount('outbox_events', 1); $this->assertDatabaseCount('consents', 1); $this->assertSame('Terra e DUAT', InterestRequest::firstOrFail()->sector_interest); $this->actingAs(User::factory()->create(['email_verified_at' => now()])) ->getJson("/investidor/terra-duat/{$case->id}")->assertNotFound(); $this->actingAs($owner)->postJson("/investidor/terra-duat/{$case->id}/solicitar-apoio", ['version' => 2, 'payload' => $payload])->assertStatus(409); $this->assertDatabaseCount('interest_requests', 1); } public function test_phone_only_support_still_requires_contact_consent(): void { $owner = User::factory()->create(['email_verified_at' => now()]); $payload = [ 'support_kind' => 'investment', 'request_description' => 'Preciso de orientação sobre os passos de preparação do pedido.', 'applicant_category' => 'individual', 'full_name' => 'Pessoa de Teste', 'phone' => '840000000', 'preferred_contact' => 'phone', 'area_status' => 'identified', 'purpose' => 'Projecto exploratório', 'contact_consent' => false, 'truth_declaration' => true, ]; $this->actingAs($owner)->postJson('/investidor/terra-duat', ['payload' => $payload])->assertCreated(); $case = LandAssistanceCase::firstOrFail(); $this->postJson("/investidor/terra-duat/{$case->id}/solicitar-apoio", ['version' => 1, 'payload' => $payload]) ->assertUnprocessable()->assertJsonValidationErrors(['payload.contact_consent']); $this->assertDatabaseCount('interest_requests', 0); } public function test_phone_only_support_is_saved_without_a_synthetic_email(): void { $owner = User::factory()->create(['email_verified_at' => now()]); $payload = [ 'support_kind' => 'investment', 'request_description' => 'Preciso de orientação sobre a preparação do pedido.', 'applicant_category' => 'individual', 'full_name' => 'Pessoa de Teste', 'email' => '', 'phone' => '840000000', 'preferred_contact' => 'phone', 'area_status' => 'identified', 'purpose' => 'Projecto exploratório', 'contact_consent' => true, 'truth_declaration' => true, ]; $this->actingAs($owner)->postJson('/investidor/terra-duat', ['payload' => $payload])->assertCreated(); $case = LandAssistanceCase::firstOrFail(); $this->postJson("/investidor/terra-duat/{$case->id}/solicitar-apoio", ['version' => 1, 'payload' => $payload]) ->assertAccepted(); $interest = InterestRequest::firstOrFail(); $this->assertNull($interest->email); $this->assertSame('840000000', $interest->phone); $this->assertSame('support_requested', $case->fresh()->status); } public function test_email_nullable_migration_can_be_reversed_when_no_email_is_missing(): void { $migration = require base_path('database/migrations/2026_10_03_210000_make_interest_request_email_nullable.php'); $migration->down(); $this->assertSame('NO', DB::table('information_schema.columns') ->where('table_schema', 'public')->where('table_name', 'interest_requests')->where('column_name', 'email') ->value('is_nullable')); $migration->up(); $this->assertSame('YES', DB::table('information_schema.columns') ->where('table_schema', 'public')->where('table_name', 'interest_requests')->where('column_name', 'email') ->value('is_nullable')); } public function test_email_nullable_migration_refuses_rollback_with_phone_only_requests(): void { DB::table('interest_requests')->insert([ 'reference' => 'AP-QA-PHONE-ONLY-ROLLBACK', 'full_name' => 'Pessoa de Teste', 'email' => null, 'phone' => '840000000', 'investor_type' => 'individual', 'country' => 'MZ', 'sector_interest' => 'Terra e DUAT', 'message' => 'Pedido sintético de teste.', 'privacy_consent' => true, 'status' => 'received', 'created_at' => now(), 'updated_at' => now(), ]); $migration = require base_path('database/migrations/2026_10_03_210000_make_interest_request_email_nullable.php'); try { $migration->down(); $this->fail('Rollback must preserve phone-only requests.'); } catch (\RuntimeException $exception) { $this->assertStringContainsString('phone-only', $exception->getMessage()); } $this->assertSame('YES', DB::table('information_schema.columns') ->where('table_schema', 'public')->where('table_name', 'interest_requests')->where('column_name', 'email') ->value('is_nullable')); } }