Files
portal/app/tests/Feature/Console/SeedShowcaseCommandTest.php
T

116 lines
5.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use App\Models\AutopodborCompetitor;
use App\Models\AutopodborSource;
use App\Models\Deal;
use App\Models\Project;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
it('заводит демо-клиента с красивыми данными и выдуманными телефонами', function () {
$this->artisan('demo:showcase')->assertExitCode(0);
$tenant = Tenant::query()->where('organization_name', 'Стоматология Улыбка')->first();
expect($tenant)->not->toBeNull();
expect(User::query()->where('tenant_id', $tenant->id)->where('email', 'demo@liderra.ru')->exists())->toBeTrue();
expect(Project::query()->where('tenant_id', $tenant->id)->count())->toBe(3);
expect(Deal::query()->where('tenant_id', $tenant->id)->count())->toBe(25);
foreach (Deal::query()->where('tenant_id', $tenant->id)->pluck('phone') as $phone) {
expect($phone)->toStartWith('7999000'); // 🔴 ПДн: несуществующий диапазон
}
});
it('заводит конкурентное поле стоматологии — конкуренты, источники, дубль и три пометки номеров', function () {
$this->artisan('demo:showcase')->assertExitCode(0);
$tenant = Tenant::query()->where('organization_name', 'Стоматология Улыбка')->first();
expect($tenant)->not->toBeNull();
$competitors = AutopodborCompetitor::query()->where('tenant_id', $tenant->id)->get();
expect($competitors->count())->toBe(8); // 6 в поле (в т.ч. дубль) + 2 в предложениях
$field = $competitors->where('box', 'field');
$proposals = $competitors->where('box', 'proposal');
expect($field->count())->toBe(6)
->and($proposals->count())->toBe(2);
// Все имена — выдуманные стоматологии, ни одна не про займы/авто (старый посторонний демо-тенант).
foreach ($competitors as $c) {
expect($c->name)->not->toContain('Ломбард')
->and($c->name)->not->toContain('Авто')
->and($c->site_url)->not->toBeNull()
->and($c->site_url)->toEndWith('-demo.ru');
}
// Настоящий дубль: два конкурента с одинаковым site_url («Белый Зуб» / «Белый Зуб (доп. филиал)»).
$sameSite = $field->where('site_url', 'belyi-zub-demo.ru');
expect($sameSite->count())->toBe(2);
// Три штатные пометки у найденных номеров: настоящий / подменный / требует проверки.
$sources = AutopodborSource::query()
->whereIn('competitor_id', $field->pluck('id'))
->where('signal_type', 'call')
->get();
expect($sources->pluck('phone_kind')->unique()->sort()->values()->all())
->toBe(['real', 'substitute', 'uncertain']);
// Телефоны источников — только выдуманные (+7 999 000-XX-XX), ПДн исключены.
foreach ($sources as $s) {
expect($s->identifier)->toStartWith('7999000');
}
});
it('главный проект выглядит живым: sync_status агрегируется в ok через демо-привязку к поставщику', function () {
$this->artisan('demo:showcase')->assertExitCode(0);
$tenant = Tenant::query()->where('organization_name', 'Стоматология Улыбка')->first();
$working = Project::query()
->where('tenant_id', $tenant->id)
->where('name', 'Стоматология — Москва')
->with(['supplierB1', 'supplierB2', 'supplierB3'])
->firstOrFail();
// Поле, которое реально управляет плашкой на карточке (ProjectCard.vue syncStatusLabel):
// aggregateSyncStatus() читает sync_status у связанных supplier_projects (B1/B2/B3).
expect($working->supplier_b1_project_id)->not->toBeNull()
->and($working->aggregateSyncStatus())->toBe('ok')
->and($working->supplierB1->sync_status)->toBe('ok');
// Данные поставщика в демо — явно выдуманные, не настоящий домен/id.
expect($working->supplierB1->unique_key)->toContain('demo')
->and($working->supplierB1->supplier_external_id)->toContain('DEMO');
// Второй и третий проекты остаются честными (не «ok») — пауза и блок по балансу.
$paused = Project::query()->where('tenant_id', $tenant->id)->where('name', 'Импланты — Санкт-Петербург')->firstOrFail();
expect($paused->is_active)->toBeFalse()
->and($paused->aggregateSyncStatus())->not->toBe('ok');
$blocked = Project::query()->where('tenant_id', $tenant->id)->where('name', 'Отбеливание — Казань')->firstOrFail();
expect($blocked->preflight_blocked_at)->not->toBeNull()
->and($blocked->aggregateSyncStatus())->not->toBe('ok');
});
it('не запускается на боевом окружении', function () {
config(['app.env' => 'production']);
$this->artisan('demo:showcase')->assertExitCode(1);
});
it('запускается дважды без ошибок (данные не дублируются)', function () {
$this->artisan('demo:showcase')->assertExitCode(0);
$this->artisan('demo:showcase')->assertExitCode(0);
$tenant = Tenant::query()->where('organization_name', 'Стоматология Улыбка')->first();
expect(Project::query()->where('tenant_id', $tenant->id)->count())->toBe(3)
->and(Deal::query()->where('tenant_id', $tenant->id)->count())->toBe(25)
->and(AutopodborCompetitor::query()->where('tenant_id', $tenant->id)->count())->toBe(8);
});