create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); DB::statement('SET app.current_tenant_id = '.$tenant->id); $run = AutopodborRun::create(['tenant_id' => $tenant->id, 'kind' => 'study', 'status' => 'done', 'region_code' => 16, 'params' => []]); $comp = AutopodborCompetitor::create([ 'tenant_id' => $tenant->id, 'search_run_id' => $run->id, 'name' => 'Окна', 'dedup_key' => 'okna', 'box' => 'field', ]); return [$tenant, $user, $run, $comp]; } function makeSource(Tenant $t, AutopodborRun $run, AutopodborCompetitor $comp, array $attrs = []): AutopodborSource { return AutopodborSource::create(array_merge([ 'tenant_id' => $t->id, 'competitor_id' => $comp->id, 'study_run_id' => $run->id, 'signal_type' => 'site', 'identifier' => 'okna.ru', 'dedup_key' => 'site:okna.ru', 'box' => 'proposal', ], $attrs)); } it('PATCH sources/{id} — правит значение, провенанс и ящик', function () { [$tenant, $user, $run, $comp] = srcCrudSetup(); $src = makeSource($tenant, $run, $comp); $this->actingAs($user)->patchJson("/api/autopodbor/sources/{$src->id}", [ 'identifier' => 'okna-new.ru', 'provenance_label' => 'Сайт компании', 'box' => 'field', ])->assertOk() ->assertJsonPath('data.identifier', 'okna-new.ru') ->assertJsonPath('data.box', 'field'); $fresh = $src->fresh(); expect($fresh->identifier)->toBe('okna-new.ru') ->and($fresh->provenance_label)->toBe('Сайт компании'); }); it('PATCH sources/{id} — тип источника неизменяем (signal_type игнорируется)', function () { [$tenant, $user, $run, $comp] = srcCrudSetup(); $src = makeSource($tenant, $run, $comp, ['signal_type' => 'site']); $this->actingAs($user)->patchJson("/api/autopodbor/sources/{$src->id}", [ 'signal_type' => 'call', 'identifier' => 'okna2.ru', ])->assertOk(); expect($src->fresh()->signal_type)->toBe('site'); // тип не сменился }); it('PATCH sources/{id} — смена значения блокируется при активном проекте (409)', function () { [$tenant, $user, $run, $comp] = srcCrudSetup(); $project = Project::factory()->create(['tenant_id' => $tenant->id, 'is_active' => true, 'preflight_blocked_at' => null]); $src = makeSource($tenant, $run, $comp, ['created_project_id' => $project->id]); $this->actingAs($user)->patchJson("/api/autopodbor/sources/{$src->id}", [ 'identifier' => 'changed.ru', ])->assertStatus(409) ->assertJsonPath('error', 'manage_via_project'); expect($src->fresh()->identifier)->toBe('okna.ru'); }); it('PATCH sources/{id} — провенанс/ящик можно править даже при активном проекте', function () { [$tenant, $user, $run, $comp] = srcCrudSetup(); $project = Project::factory()->create(['tenant_id' => $tenant->id, 'is_active' => true, 'preflight_blocked_at' => null]); $src = makeSource($tenant, $run, $comp, ['created_project_id' => $project->id, 'box' => 'field']); $this->actingAs($user)->patchJson("/api/autopodbor/sources/{$src->id}", [ 'provenance_label' => 'Из 2ГИС', ])->assertOk(); expect($src->fresh()->provenance_label)->toBe('Из 2ГИС'); }); it('PATCH sources/{id} — чужой тенант не правит (404)', function () { [$tenant, $user, $run, $comp] = srcCrudSetup(); $src = makeSource($tenant, $run, $comp); $other = User::factory()->create(['tenant_id' => Tenant::factory()->create()->id]); $this->actingAs($other)->patchJson("/api/autopodbor/sources/{$src->id}", ['identifier' => 'x.ru']) ->assertStatus(404); }); it('DELETE sources/{id} — удаляет источник без проекта (204)', function () { [$tenant, $user, $run, $comp] = srcCrudSetup(); $src = makeSource($tenant, $run, $comp); $this->actingAs($user)->deleteJson("/api/autopodbor/sources/{$src->id}") ->assertStatus(204); expect(AutopodborSource::find($src->id))->toBeNull(); }); it('DELETE sources/{id} — блок при активном проекте (409)', function () { [$tenant, $user, $run, $comp] = srcCrudSetup(); $project = Project::factory()->create(['tenant_id' => $tenant->id, 'is_active' => true, 'preflight_blocked_at' => null]); $src = makeSource($tenant, $run, $comp, ['created_project_id' => $project->id]); $this->actingAs($user)->deleteJson("/api/autopodbor/sources/{$src->id}") ->assertStatus(409) ->assertJsonPath('error', 'has_active_project'); expect(AutopodborSource::find($src->id))->not->toBeNull(); });