Files
portal/app/tests/Feature/Import/MonthlyPartitionManagerTest.php
T

171 lines
7.8 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\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');