'Acme Corporation', 'contact_name' => 'John Doe', 'email' => 'john.doe@acme.com', 'phone' => '+1555123456', 'desired_subdomain' => 'acme-test-slug', 'employee_count' => 75, 'requested_features' => ['mobile_app', 'geofence', 'payroll'], 'notes' => 'Testing wizard hardening', ]; $response = $this->postJson('/api/inquiries', $payload); $response->assertStatus(201) ->assertJson(['success' => true]); $this->assertDatabaseHas('inquiries', [ 'email' => 'john.doe@acme.com', 'desired_subdomain' => 'acme-test-slug', 'employee_count' => 75, ]); $this->assertDatabaseHas('contacts', [ 'email' => 'john.doe@acme.com', 'status' => 'New', ]); } public function test_prevents_duplicate_desired_subdomains(): void { Inquiry::create([ 'company_name' => 'Existing Company', 'contact_name' => 'Existing Contact', 'email' => 'existing@acme.com', 'desired_subdomain' => 'unique-slug', 'employee_count' => 10, 'status' => 'pending_payment_approval', ]); $payload = [ 'company_name' => 'New Company', 'contact_name' => 'New Contact', 'email' => 'new@acme.com', 'desired_subdomain' => 'unique-slug', 'employee_count' => 50, ]; $response = $this->postJson('/api/inquiries', $payload); $response->assertStatus(422) ->assertJsonValidationErrors(['desired_subdomain']); } public function test_honeypot_bot_rejection(): void { $payload = [ 'company_name' => 'Spam Bot Co', 'contact_name' => 'Bot User', 'email' => 'bot@spam.com', 'desired_subdomain' => 'bot-subdomain', 'hp_website' => 'http://spam-link.com', ]; $response = $this->postJson('/api/inquiries', $payload); $response->assertStatus(422) ->assertJson(['success' => false, 'message' => 'Spam submission detected.']); } public function test_strict_module_and_bounds_validation(): void { $payload = [ 'company_name' => 'Invalid Company', 'contact_name' => 'Invalid Contact', 'email' => 'invalid@acme.com', 'desired_subdomain' => 'invalid-subdomain', 'employee_count' => 999999, // Exceeds max 10,000 'requested_features' => ['invalid_fake_module'], ]; $response = $this->postJson('/api/inquiries', $payload); $response->assertStatus(422) ->assertJsonValidationErrors(['employee_count', 'requested_features.0']); } public function test_same_user_can_resubmit_inquiry_with_same_subdomain(): void { Inquiry::create([ 'company_name' => 'Original Company', 'contact_name' => 'Same User', 'email' => 'sameuser@acme.com', 'desired_subdomain' => 'sameuser-slug', 'employee_count' => 10, 'status' => 'pending_payment_approval', ]); $payload = [ 'company_name' => 'Updated Company', 'contact_name' => 'Same User Updated', 'email' => 'sameuser@acme.com', 'desired_subdomain' => 'sameuser-slug', // Same user reusing their requested subdomain 'employee_count' => 100, 'notes' => 'Updated notes from same user', ]; $response = $this->postJson('/api/inquiries', $payload); $response->assertStatus(201) ->assertJson(['success' => true]); $this->assertDatabaseHas('inquiries', [ 'email' => 'sameuser@acme.com', 'company_name' => 'Updated Company', 'employee_count' => 100, ]); } public function test_oversized_notes_validation(): void { $payload = [ 'company_name' => 'Long Note Co', 'contact_name' => 'Long Note User', 'email' => 'longnote@acme.com', 'desired_subdomain' => 'long-note-slug', 'notes' => str_repeat('A', 2001), // Exceeds max 2000 chars ]; $response = $this->postJson('/api/inquiries', $payload); $response->assertStatus(422) ->assertJsonValidationErrors(['notes']); } public function test_creates_multiple_contact_history_entries_for_repeat_submissions(): void { $payload1 = [ 'company_name' => 'History Co', 'contact_name' => 'Repeat User', 'email' => 'repeat.user@acme.com', 'desired_subdomain' => 'history-slug', 'notes' => 'First message', ]; $this->postJson('/api/inquiries', $payload1)->assertStatus(201); $payload2 = [ 'company_name' => 'History Co', 'contact_name' => 'Repeat User', 'email' => 'repeat.user@acme.com', 'desired_subdomain' => 'history-slug', 'notes' => 'Second message', ]; $this->postJson('/api/inquiries', $payload2)->assertStatus(201); $this->assertEquals(2, Contact::where('email', 'repeat.user@acme.com')->count()); } public function test_accumulated_notes_are_truncated_to_2000_chars(): void { Inquiry::create([ 'company_name' => 'Truncate Co', 'contact_name' => 'Truncate User', 'email' => 'truncate@acme.com', 'desired_subdomain' => 'truncate-slug', 'notes' => str_repeat('A', 1500), 'status' => 'pending_payment_approval', ]); $payload = [ 'company_name' => 'Truncate Co', 'contact_name' => 'Truncate User', 'email' => 'truncate@acme.com', 'desired_subdomain' => 'truncate-slug', 'notes' => str_repeat('B', 1000), ]; $this->postJson('/api/inquiries', $payload)->assertStatus(201); $inquiry = Inquiry::where('email', 'truncate@acme.com')->first(); $this->assertEquals(2000, mb_strlen($inquiry->notes)); } public function test_cannot_alter_subdomain_slug_if_already_approved(): void { Inquiry::create([ 'company_name' => 'Approved Co', 'contact_name' => 'Approved User', 'email' => 'approved@acme.com', 'desired_subdomain' => 'approved-slug', 'status' => 'approved', ]); $payload = [ 'company_name' => 'Approved Co', 'contact_name' => 'Approved User', 'email' => 'approved@acme.com', 'desired_subdomain' => 'new-unapproved-slug', ]; $response = $this->postJson('/api/inquiries', $payload); $response->assertStatus(422) ->assertJson(['success' => false, 'message' => 'Your subdomain request has already been approved. Subdomain slug cannot be altered.']); } public function test_email_verification_flow(): void { $payload = [ 'company_name' => 'Verify Co', 'contact_name' => 'Verify User', 'email' => 'verify.user@acme.com', 'desired_subdomain' => 'verify-slug', ]; $response = $this->postJson('/api/inquiries', $payload); $response->assertStatus(201) ->assertJson(['success' => true, 'requires_verification' => true]); $inquiry = Inquiry::where('email', 'verify.user@acme.com')->first(); $this->assertNotNull($inquiry->verification_code); $this->assertNull($inquiry->email_verified_at); // Invalid verification code $verifyFail = $this->postJson('/api/inquiries/verify-email', [ 'email' => 'verify.user@acme.com', 'verification_code' => '999999', ]); $verifyFail->assertStatus(422); // Valid verification code $verifySuccess = $this->postJson('/api/inquiries/verify-email', [ 'email' => 'verify.user@acme.com', 'verification_code' => $inquiry->verification_code, ]); $verifySuccess->assertStatus(200) ->assertJson(['success' => true]); $inquiry->refresh(); $this->assertNotNull($inquiry->email_verified_at); $this->assertNull($inquiry->verification_code); } public function test_rate_limiting_throttles_excessive_submissions(): void { for ($i = 0; $i < 6; $i++) { $response = $this->postJson('/api/inquiries', [ 'company_name' => 'Company ' . $i, 'contact_name' => 'Contact ' . $i, 'email' => 'rate' . $i . '@acme.com', 'desired_subdomain' => 'rate-slug-' . $i, ]); } // 7th request within 1 minute should be rate-limited $response = $this->postJson('/api/inquiries', [ 'company_name' => 'Throttled Co', 'contact_name' => 'Throttled User', 'email' => 'throttled@acme.com', 'desired_subdomain' => 'throttled-slug', ]); $response->assertStatus(429); } }