fix(tests): sync 3 stale эпик-тестов + schema.sql header под Plans 1-3 (v8.26)

Три pre-existing красных теста (ЭТАЛОН §6 «deferred») приведены к реальной
схеме v8.26 после project-migration-redesign Plans 1-3:
- SchemaDeltaTest: 64→65 base tables, 121→123 indexes (project_supplier_links
  pivot + supplier_projects_platform_key_subject_unique).
- SupplierProjectsAccessTest: unique-constraint (platform, unique_key) →
  (platform, unique_key, subject_code) — per-субъект экспорт (Plan 1).
- SupplierLeadFlowTest: routing eligibility теперь через pivot
  project_supplier_links (LeadRouter), не legacy supplier_b1_project_id —
  добавлены linkProjectToSupplier() связи.
- schema.sql header: v8.25→v8.26 + метрики (CHANGELOG уже содержал v8.26).

Production-код не менялся — тесты отставали от уже-смердженных Plans 1-3.
Pest full 1013/1010 passed/3 skipped/0 failed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-05-20 19:23:13 +03:00
parent a0e18a1dd8
commit 95ee6644f7
4 changed files with 27 additions and 14 deletions
@@ -36,7 +36,7 @@ it('end-to-end: 1 webhook → 3 deal copies for 3 active tenants', function ():
for ($i = 0; $i < 3; $i++) {
$t = Tenant::factory()->create(['balance_leads' => 100]);
$tenants->push($t);
$projects->push(Project::factory()->create([
$project = Project::factory()->create([
'tenant_id' => $t->id,
'supplier_b1_project_id' => $supplier->id,
'signal_type' => 'site',
@@ -49,18 +49,24 @@ it('end-to-end: 1 webhook → 3 deal copies for 3 active tenants', function ():
'region_mode' => 'include',
'daily_limit_target' => 10,
'effective_daily_limit_today' => null,
]));
]);
$projects->push($project);
// v8.26 (Plan 1-2): LeadRouter eligibility — через pivot project_supplier_links,
// не legacy supplier_b1_project_id. Без pivot-связи проект не eligible → 0 сделок.
linkProjectToSupplier($project, $supplier);
}
// 4-й tenant — paused
// 4-й tenant — paused (is_active=false). Связь в pivot есть, чтобы проверялся
// именно фильтр is_active, а не отсутствие связи.
$pausedTenant = Tenant::factory()->create(['balance_leads' => 100]);
Project::factory()->create([
$pausedProject = Project::factory()->create([
'tenant_id' => $pausedTenant->id,
'supplier_b1_project_id' => $supplier->id,
'signal_type' => 'site',
'signal_identifier' => 'vashinvestor.ru',
'is_active' => false,
]);
linkProjectToSupplier($pausedProject, $supplier);
$vid = 432176649;
$response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
@@ -27,19 +27,23 @@ test('supplier_projects table exists with required columns', function () {
}
});
test('supplier_projects has unique constraint on (platform, unique_key)', function () {
test('supplier_projects has unique constraint on (platform, unique_key, subject_code)', function () {
// v8.26 (project-migration-redesign Plan 1): per-субъект экспорт — composite unique
// расширен до (platform, unique_key, subject_code) NULLS NOT DISTINCT. Старый
// 2-колоночный индекс supplier_projects_platform_unique_key_unique заменён.
$idx = DB::selectOne(
"SELECT indexdef
FROM pg_indexes
WHERE tablename = 'supplier_projects'
AND indexname = 'supplier_projects_platform_unique_key_unique'"
AND indexname = 'supplier_projects_platform_key_subject_unique'"
);
expect($idx)->not->toBeNull();
expect($idx->indexdef)
->toContain('UNIQUE')
->toContain('platform')
->toContain('unique_key');
->toContain('unique_key')
->toContain('subject_code');
});
test('supplier_projects platform check constraint allows only B1, B2, B3', function () {
@@ -59,26 +59,28 @@ it('supplier_csv_reconcile_log table exists with required columns and status CHE
]))->toThrow(QueryException::class);
});
it('schema.sql v8.25 has correct metrics — 64 base tables, 121 indexes, 40 RLS policies', function () {
it('schema.sql v8.26 has correct metrics — 65 base tables, 123 indexes, 40 RLS policies', function () {
// Замена destructive `migrate:fresh` (cross-test coupling: после DROP CASCADE остальные
// Feature-тесты в той же сессии видели пустую БД). Static parse `db/schema.sql` —
// источник истины метрик из spec §2.4 / db/CHANGELOG_schema.md v8.25.
// источник истины метрик из spec §2.4 / db/CHANGELOG_schema.md v8.26.
// v8.21 (Sprint 4): +1 таблица import_unknown_statuses, +1 индекс, +1 RLS-политика.
// v8.22 (Plan 6/C9): +1 GIN-индекс idx_projects_regions.
// v8.25 (supplier-failover): +1 таблица supplier_manual_sync_queue, +2 индекса.
// v8.26 (project-migration-redesign Plans 1-3): +1 таблица project_supplier_links (M:N pivot)
// + 2 индекса (supplier_projects_platform_key_subject_unique, idx_psl_*).
$schemaPath = dirname(base_path()).DIRECTORY_SEPARATOR.'db'.DIRECTORY_SEPARATOR.'schema.sql';
expect(is_file($schemaPath) && is_readable($schemaPath))->toBeTrue();
$schema = file_get_contents($schemaPath);
expect($schema)->not->toBeFalse();
// 64 base tables = все CREATE TABLE минус 12 партиций (PARTITION OF).
// 65 base tables = все CREATE TABLE минус 12 партиций (PARTITION OF).
$createTables = preg_match_all('/^CREATE TABLE\b/m', $schema);
$partitionOf = preg_match_all('/CREATE TABLE\s+\w+\s+PARTITION OF\b/m', $schema);
$baseTables = $createTables - $partitionOf;
expect($baseTables)->toBe(64);
expect($baseTables)->toBe(65);
$createIndexes = preg_match_all('/^CREATE\s+(?:UNIQUE\s+)?INDEX\b/m', $schema);
expect($createIndexes)->toBe(121); // v8.25: +2 idx_smsq_status_created, idx_smsq_project
expect($createIndexes)->toBe(123); // v8.26: +2 supplier_projects_platform_key_subject_unique, idx_psl_*
$createPolicies = preg_match_all('/^CREATE\s+POLICY\b/m', $schema);
expect($createPolicies)->toBe(40);
+3 -2
View File
@@ -1,7 +1,8 @@
-- =============================================================================
-- schema.sql — единая схема БД для SaaS-аналога crm.bp-gr.ru («Лидерра»)
-- Версия: v8.25 (19.05.2026 — supplier_manual_sync_queue: SaaS-level Tier 3 очередь резерва канала миграции проектов)
-- Метрики: 64 базовые таблицы (62 regular + 2 partitioned parents: deals + supplier_lead_costs) + 12 партиций / 121 индекс / 40 RLS-политик / 5 функций / 13 триггеров
-- Версия: v8.26 (20.05.2026 — project-migration-redesign Plans 1-3: supplier_projects.subject_code (per-субъект экспорт) + project_supplier_links (M:N pivot projects↔supplier_projects) + deals.subject_code + CHECK chk_deals_subject_code + seed system_settings.supplier_export_mode)
-- Метрики: 65 базовые таблицы (63 regular + 2 partitioned parents: deals + supplier_lead_costs) + 12 партиций / 123 индекса / 40 RLS-политик / 5 функций / 13 триггеров
-- Базовая версия: v8.25 (19.05.2026 — supplier_manual_sync_queue: SaaS-level Tier 3 очередь резерва канала миграции проектов)
-- Базовая версия: v8.24 (18.05.2026 — supplier_leads.vid → nullable для CSV-recovered лидов (Путь 2))
-- Базовая версия: v8.20 (11.05.2026 — Plan 5 frontend projects UI: projects.archived_at TIMESTAMPTZ NULL для soft archive flow; tenants.limits JSONB NOT NULL DEFAULT '{}' для per-tenant project/user лимитов)
-- Базовая версия: v8.19 (11.05.2026 — Plan 4 billing+csv+admin: tenants.delivered_in_month, lead_charges.charge_source + CHECK, supplier_leads.recovered_from_csv_at, supplier_csv_reconcile_log)