Queue::fake()); it('creates a site project with valid payload', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); $response = $this->actingAs($user)->postJson('/api/projects', [ 'name' => 'Окна СПб', 'signal_type' => 'site', 'signal_identifier' => 'okna-spb.ru', 'daily_limit_target' => 50, 'region_mask' => 0, 'region_mode' => 'include', 'delivery_days_mask' => 127, ]); $response->assertCreated(); expect(Project::where('signal_identifier', 'okna-spb.ru')->exists())->toBeTrue(); Queue::assertPushed(SyncSupplierProjectJob::class); }); it('rejects invalid site domain', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); $response = $this->actingAs($user)->postJson('/api/projects', [ 'name' => 'X', 'signal_type' => 'site', 'signal_identifier' => 'not a domain', 'daily_limit_target' => 50, 'region_mask' => 0, 'region_mode' => 'include', 'delivery_days_mask' => 127, ]); $response->assertStatus(422); $response->assertJsonValidationErrors(['signal_identifier']); }); it('creates a call project with valid 11-digit phone', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); $response = $this->actingAs($user)->postJson('/api/projects', [ 'name' => 'Натяжные', 'signal_type' => 'call', 'signal_identifier' => '79161234567', 'daily_limit_target' => 30, 'region_mask' => 0, 'region_mode' => 'include', 'delivery_days_mask' => 127, ]); $response->assertCreated(); }); it('rejects call signal_identifier not starting with 7', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); $response = $this->actingAs($user)->postJson('/api/projects', [ 'name' => 'X', 'signal_type' => 'call', 'signal_identifier' => '89991234567', 'daily_limit_target' => 30, 'region_mask' => 0, 'region_mode' => 'include', 'delivery_days_mask' => 127, ]); $response->assertStatus(422); }); it('creates sms project with senders + keyword', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); $response = $this->actingAs($user)->postJson('/api/projects', [ 'name' => 'Ипотека', 'signal_type' => 'sms', 'sms_senders' => ['TINKOFF'], 'sms_keyword' => 'ипотека', 'daily_limit_target' => 100, 'region_mask' => 0, 'region_mode' => 'include', 'delivery_days_mask' => 127, ]); $response->assertCreated(); $project = Project::where('name', 'Ипотека')->first(); expect($project->sms_senders)->toBe(['TINKOFF']); expect($project->sms_keyword)->toBe('ипотека'); }); it('rejects sms project without sms_senders', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); $response = $this->actingAs($user)->postJson('/api/projects', [ 'name' => 'X', 'signal_type' => 'sms', 'daily_limit_target' => 100, 'region_mask' => 0, 'region_mode' => 'include', 'delivery_days_mask' => 127, ]); $response->assertStatus(422); $response->assertJsonValidationErrors(['sms_senders']); }); it('rejects when tenant exceeds max_projects limit', function () { $tenant = Tenant::factory()->create(['limits' => ['max_projects' => 1]]); $user = User::factory()->create(['tenant_id' => $tenant->id]); Project::factory()->create(['tenant_id' => $tenant->id]); $response = $this->actingAs($user)->postJson('/api/projects', [ 'name' => 'second', 'signal_type' => 'site', 'signal_identifier' => 'second.ru', 'daily_limit_target' => 10, 'region_mask' => 0, 'region_mode' => 'include', 'delivery_days_mask' => 127, ]); $response->assertStatus(403); }); it('forces tenant_id from auth user (not from payload)', function () { $tenantA = Tenant::factory()->create(); $tenantB = Tenant::factory()->create(); $userA = User::factory()->create(['tenant_id' => $tenantA->id]); $this->actingAs($userA)->postJson('/api/projects', [ 'tenant_id' => $tenantB->id, // попытка инъекции 'name' => 'X', 'signal_type' => 'site', 'signal_identifier' => 'x.ru', 'daily_limit_target' => 10, 'region_mask' => 0, 'region_mode' => 'include', 'delivery_days_mask' => 127, ]); $project = Project::where('signal_identifier', 'x.ru')->latest()->first(); expect($project->tenant_id)->toBe($tenantA->id); });