171 lines
7.8 KiB
PHP
171 lines
7.8 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use App\Services\MonthlyPartitionManager;
|
||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||
use Illuminate\Support\Carbon;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Tests\Concerns\SharesSupplierPdo;
|
||
|
||
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
|
||
// ensureMonth теперь делает CREATE через pgsql_supplier (см. MonthlyPartitionManager::DDL_CONNECTION).
|
||
// Без SharesSupplierPdo DDL уйдёт мимо test-транзакции и партиции протечь в test DB.
|
||
|
||
function partitionExists(string $name): bool
|
||
{
|
||
return DB::selectOne(
|
||
"SELECT 1 AS ok FROM pg_class WHERE relname = ? AND relkind = 'r'",
|
||
[$name],
|
||
) !== null;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Existing tests (deals — business table, received_at key)
|
||
// ---------------------------------------------------------------------------
|
||
|
||
test('ensureRange создаёт месячные партиции deals под диапазон', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
|
||
$created = $manager->ensureRange(
|
||
'deals',
|
||
Carbon::parse('2024-02-15'),
|
||
Carbon::parse('2024-04-03'),
|
||
);
|
||
|
||
expect($created)->toBeGreaterThanOrEqual(3)
|
||
->and(partitionExists('deals_y2024_m02'))->toBeTrue()
|
||
->and(partitionExists('deals_y2024_m03'))->toBeTrue()
|
||
->and(partitionExists('deals_y2024_m04'))->toBeTrue();
|
||
});
|
||
|
||
test('ensureRange идемпотентна — повторный вызов не падает', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
|
||
$manager->ensureRange('deals', Carbon::parse('2024-02-15'), Carbon::parse('2024-02-20'));
|
||
$secondRun = $manager->ensureRange('deals', Carbon::parse('2024-02-15'), Carbon::parse('2024-02-20'));
|
||
|
||
expect($secondRun)->toBe(0); // всё уже существует
|
||
});
|
||
|
||
test('ensureRange отвергает неизвестную таблицу', function (): void {
|
||
app(MonthlyPartitionManager::class)->ensureRange('orders', now(), now());
|
||
})->throws(InvalidArgumentException::class);
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Hole #2 tests: audit tables (created_at key)
|
||
// ---------------------------------------------------------------------------
|
||
|
||
test('ensureMonth создаёт партицию auth_log (created_at)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$month = Carbon::parse('2024-03-01');
|
||
|
||
$manager->ensureMonth('auth_log', $month);
|
||
|
||
expect(partitionExists('auth_log_y2024_m03'))->toBeTrue();
|
||
});
|
||
|
||
test('ensureMonth создаёт партицию activity_log (created_at)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$manager->ensureMonth('activity_log', Carbon::parse('2024-03-01'));
|
||
|
||
expect(partitionExists('activity_log_y2024_m03'))->toBeTrue();
|
||
});
|
||
|
||
test('ensureMonth создаёт партицию tenant_operations_log (created_at)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$manager->ensureMonth('tenant_operations_log', Carbon::parse('2024-03-01'));
|
||
|
||
expect(partitionExists('tenant_operations_log_y2024_m03'))->toBeTrue();
|
||
});
|
||
|
||
test('ensureMonth создаёт партицию webhook_log (received_at)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$manager->ensureMonth('webhook_log', Carbon::parse('2024-03-01'));
|
||
|
||
expect(partitionExists('webhook_log_y2024_m03'))->toBeTrue();
|
||
});
|
||
|
||
test('ensureMonth создаёт партицию balance_transactions (created_at)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$manager->ensureMonth('balance_transactions', Carbon::parse('2024-03-01'));
|
||
|
||
expect(partitionExists('balance_transactions_y2024_m03'))->toBeTrue();
|
||
});
|
||
|
||
test('ensureMonth создаёт партицию pd_processing_log (created_at)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$manager->ensureMonth('pd_processing_log', Carbon::parse('2024-03-01'));
|
||
|
||
expect(partitionExists('pd_processing_log_y2024_m03'))->toBeTrue();
|
||
});
|
||
|
||
test('ensureMonth создаёт партицию saas_admin_audit_log (created_at)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$manager->ensureMonth('saas_admin_audit_log', Carbon::parse('2024-03-01'));
|
||
|
||
expect(partitionExists('saas_admin_audit_log_y2024_m03'))->toBeTrue();
|
||
});
|
||
|
||
test('partitionName возвращает правильный формат', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
|
||
expect($manager->partitionName('auth_log', Carbon::parse('2026-05-15')))
|
||
->toBe('auth_log_y2026_m05');
|
||
|
||
expect($manager->partitionName('deals', Carbon::parse('2024-01-01')))
|
||
->toBe('deals_y2024_m01');
|
||
});
|
||
|
||
test('listPartitions возвращает созданные партиции', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$manager->ensureMonth('auth_log', Carbon::parse('2024-04-01'));
|
||
$manager->ensureMonth('auth_log', Carbon::parse('2024-05-01'));
|
||
|
||
$partitions = $manager->listPartitions('auth_log');
|
||
|
||
expect($partitions)->toContain('auth_log_y2024_m04')
|
||
->toContain('auth_log_y2024_m05');
|
||
});
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Slepok routing: project_routing_snapshots (snapshot_date key, Этап 2, 27.05.2026)
|
||
// ---------------------------------------------------------------------------
|
||
|
||
test('PARTITIONED_TABLES включает project_routing_snapshots с ключом snapshot_date', function (): void {
|
||
expect(MonthlyPartitionManager::PARTITIONED_TABLES)
|
||
->toHaveKey('project_routing_snapshots')
|
||
->and(MonthlyPartitionManager::PARTITIONED_TABLES['project_routing_snapshots'])
|
||
->toBe('snapshot_date');
|
||
});
|
||
|
||
test('ensureMonth создаёт партицию project_routing_snapshots (snapshot_date)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
$manager->ensureMonth('project_routing_snapshots', Carbon::parse('2024-07-01'));
|
||
|
||
expect(partitionExists('project_routing_snapshots_y2024_m07'))->toBeTrue();
|
||
});
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Task 0.5 (imitation harness): migrate:fresh resilience
|
||
// ensureMonth must skip gracefully when parent table does not yet exist.
|
||
// Without this guard, partitions:create-months called from migration 0001
|
||
// crashes on tables added by later delta-migrations (project_routing_snapshots,
|
||
// lead_region_resolution_log) because their DDL runs after the initial schema load.
|
||
// ---------------------------------------------------------------------------
|
||
|
||
// Task 0.5 (imitation harness): regression guard for migrate:fresh resilience.
|
||
// Migration 0001_01_01_000000_load_initial_schema calls Artisan::call('partitions:create-months')
|
||
// which uses MonthlyPartitionManager::ensureMonth for EVERY table in PARTITIONED_TABLES.
|
||
// Tables added by later delta-migrations (project_routing_snapshots, lead_region_resolution_log)
|
||
// do not exist when 0001 runs → without the parent-exists guard, migrate:fresh crashes.
|
||
// This test documents the regression requirement: ensureMonth MUST be callable for 'deals'
|
||
// (an existing table) and MUST return bool (not throw) after the guard is added.
|
||
test('ensureMonth для deals возвращает bool (guard регрессия migrate:fresh)', function (): void {
|
||
$manager = app(MonthlyPartitionManager::class);
|
||
// deals таблица создана в schema.sql — партиция либо уже существует, либо создаётся.
|
||
$result = $manager->ensureMonth('deals', Carbon::parse('2025-01-01'));
|
||
expect($result)->toBeBool();
|
||
// Связано: 0001_01_01_000000_load_initial_schema вызывает partitions:create-months.
|
||
})->group('imitation');
|