50b789b69f
Однократный back-fill для уже существующих проектов: для каждого site-проекта с identifier-субдоменом добавляет линки к корневым sp, если они есть в supplier_projects. Идемпотентна (явная проверка existence перед insert). --dry-run выводит ожидаемый эффект без записи. 3 feature-теста: add root link / idempotency / dry-run no-write. Spec: docs/superpowers/specs/2026-05-22-root-domain-auto-link-design.md §4.3 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
115 lines
4.4 KiB
PHP
115 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\Project;
|
|
use App\Models\SupplierProject;
|
|
use App\Models\Tenant;
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Tests\Concerns\SharesSupplierPdo;
|
|
|
|
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
|
|
|
|
it('backfill: добавляет root-link для проекта-субдомена с уже-существующими линками', function () {
|
|
$tenant = Tenant::factory()->create();
|
|
$project = Project::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'signal_type' => 'site',
|
|
'signal_identifier' => 'krasnoyarsk.carmoney.ru',
|
|
]);
|
|
|
|
$subdomainSp = SupplierProject::create([
|
|
'platform' => 'B2',
|
|
'signal_type' => 'site',
|
|
'unique_key' => 'krasnoyarsk.carmoney.ru',
|
|
'supplier_external_id' => 'ext-sub',
|
|
'current_limit' => 100,
|
|
'sync_status' => 'ok',
|
|
]);
|
|
$rootSp = SupplierProject::create([
|
|
'platform' => 'B2',
|
|
'signal_type' => 'site',
|
|
'unique_key' => 'carmoney.ru',
|
|
'supplier_external_id' => 'ext-root',
|
|
'current_limit' => 100,
|
|
'sync_status' => 'ok',
|
|
]);
|
|
|
|
DB::connection('pgsql_supplier')->table('project_supplier_links')->insert([
|
|
'project_id' => $project->id,
|
|
'supplier_project_id' => $subdomainSp->id,
|
|
'platform' => 'B2',
|
|
'subject_code' => null,
|
|
]);
|
|
|
|
$exitCode = Artisan::call('supplier:backfill-root-links');
|
|
expect($exitCode)->toBe(0);
|
|
|
|
expect(
|
|
DB::connection('pgsql_supplier')->table('project_supplier_links')
|
|
->where('project_id', $project->id)
|
|
->where('supplier_project_id', $rootSp->id)
|
|
->exists()
|
|
)->toBeTrue();
|
|
});
|
|
|
|
it('backfill: idempotent — повторный прогон ничего не добавляет', function () {
|
|
$tenant = Tenant::factory()->create();
|
|
$project = Project::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'signal_type' => 'site',
|
|
'signal_identifier' => 'client.carmoney.ru',
|
|
]);
|
|
$subSp = SupplierProject::create([
|
|
'platform' => 'B2', 'signal_type' => 'site', 'unique_key' => 'client.carmoney.ru',
|
|
'supplier_external_id' => 'ext1', 'current_limit' => 100, 'sync_status' => 'ok',
|
|
]);
|
|
SupplierProject::create([
|
|
'platform' => 'B2', 'signal_type' => 'site', 'unique_key' => 'carmoney.ru',
|
|
'supplier_external_id' => 'ext2', 'current_limit' => 100, 'sync_status' => 'ok',
|
|
]);
|
|
DB::connection('pgsql_supplier')->table('project_supplier_links')->insert([
|
|
'project_id' => $project->id, 'supplier_project_id' => $subSp->id,
|
|
'platform' => 'B2', 'subject_code' => null,
|
|
]);
|
|
|
|
Artisan::call('supplier:backfill-root-links');
|
|
$afterFirst = DB::connection('pgsql_supplier')->table('project_supplier_links')
|
|
->where('project_id', $project->id)->count();
|
|
Artisan::call('supplier:backfill-root-links');
|
|
$afterSecond = DB::connection('pgsql_supplier')->table('project_supplier_links')
|
|
->where('project_id', $project->id)->count();
|
|
|
|
expect($afterFirst)->toBe(2);
|
|
expect($afterSecond)->toBe(2);
|
|
});
|
|
|
|
it('backfill --dry-run: ничего не пишет в БД', function () {
|
|
$tenant = Tenant::factory()->create();
|
|
$project = Project::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'signal_type' => 'site',
|
|
'signal_identifier' => 'next.vashinvestor.ru',
|
|
]);
|
|
$subSp = SupplierProject::create([
|
|
'platform' => 'B2', 'signal_type' => 'site', 'unique_key' => 'next.vashinvestor.ru',
|
|
'supplier_external_id' => 'extn1', 'current_limit' => 100, 'sync_status' => 'ok',
|
|
]);
|
|
SupplierProject::create([
|
|
'platform' => 'B2', 'signal_type' => 'site', 'unique_key' => 'vashinvestor.ru',
|
|
'supplier_external_id' => 'extn2', 'current_limit' => 100, 'sync_status' => 'ok',
|
|
]);
|
|
DB::connection('pgsql_supplier')->table('project_supplier_links')->insert([
|
|
'project_id' => $project->id, 'supplier_project_id' => $subSp->id,
|
|
'platform' => 'B2', 'subject_code' => null,
|
|
]);
|
|
|
|
Artisan::call('supplier:backfill-root-links', ['--dry-run' => true]);
|
|
|
|
$count = DB::connection('pgsql_supplier')->table('project_supplier_links')
|
|
->where('project_id', $project->id)->count();
|
|
expect($count)->toBe(1);
|
|
});
|