52eebe28c5
Task 2 плана 2026-05-29-audit-rebuild-per-tenant-fix.md.
Regression-safe refactor: drop private TABLE_CONFIG const + buildRowExpression()
helper, заменить на чтение AuditChainConfig::TABLES (создан в Task 1, commit
4cfd9f6b) + AuditChainConfig::rowExpression($table). Поведение не изменилось —
тот же baseline regression Pest (9 passed pre-refactor → 10 passed post-refactor;
+1 = регрессия-guard VerifyAuditChainsTest.php flipped fail→pass; 2 pre-existing
errors orthogonal к Task 2).
VerifyAuditChainsTest.php — TDD regression guard на cleanness рефактора: проверяет
полноту AuditChainConfig::TABLES (6 таблиц), корректность rowExpression() для
всех таблиц, и отсутствие private TABLE_CONFIG const после refactor'а.
Ref: docs/adr/ADR-018-audit-chain-per-tenant-semantics.md
docs/superpowers/plans/2026-05-29-audit-rebuild-per-tenant-fix.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
66 lines
2.4 KiB
PHP
66 lines
2.4 KiB
PHP
<?php
|
|
|
|
// Tests for audit:verify-chains command — regression guard for Task 2 refactor.
|
|
// Verifies that the command uses AuditChainConfig::TABLES (shared config)
|
|
// and that AuditChainConfig::rowExpression() works for all registered tables.
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Services\Audit\AuditChainConfig;
|
|
|
|
/**
|
|
* Regression tests for VerifyAuditChains → AuditChainConfig refactor (ADR-018 Task 2).
|
|
*
|
|
* These tests do NOT require a DB connection — they verify the static config
|
|
* integrity used by both VerifyAuditChains and AuditRebuildChain.
|
|
*/
|
|
|
|
it('AuditChainConfig::TABLES registers all six expected audit tables', function (): void {
|
|
$tables = array_keys(AuditChainConfig::TABLES);
|
|
|
|
expect($tables)->toContain('auth_log')
|
|
->toContain('activity_log')
|
|
->toContain('tenant_operations_log')
|
|
->toContain('balance_transactions')
|
|
->toContain('pd_processing_log')
|
|
->toContain('saas_admin_audit_log');
|
|
|
|
expect(count($tables))->toBe(6);
|
|
});
|
|
|
|
it('AuditChainConfig::rowExpression builds ROW expression with NULL::bytea at log_hash position', function (): void {
|
|
$expr = AuditChainConfig::rowExpression('auth_log');
|
|
|
|
expect($expr)->toStartWith('ROW(')
|
|
->toContain('NULL::bytea')
|
|
->not->toContain('t.__log_hash__');
|
|
});
|
|
|
|
it('AuditChainConfig::rowExpression produces same result for all six tables', function (): void {
|
|
foreach (array_keys(AuditChainConfig::TABLES) as $table) {
|
|
$expr = AuditChainConfig::rowExpression($table);
|
|
|
|
expect($expr)
|
|
->toStartWith('ROW(')
|
|
->toContain('NULL::bytea')
|
|
->not->toContain('t.__log_hash__');
|
|
}
|
|
});
|
|
|
|
it('AuditChainConfig::rowExpression throws for unknown table', function (): void {
|
|
AuditChainConfig::rowExpression('nonexistent_table');
|
|
})->throws(\InvalidArgumentException::class);
|
|
|
|
it('VerifyAuditChains command class exists and is registered', function (): void {
|
|
expect(class_exists(\App\Console\Commands\VerifyAuditChains::class))->toBeTrue();
|
|
});
|
|
|
|
it('VerifyAuditChains does not have private TABLE_CONFIG const after ADR-018 refactor', function (): void {
|
|
$reflection = new ReflectionClass(\App\Console\Commands\VerifyAuditChains::class);
|
|
$constants = $reflection->getReflectionConstants();
|
|
$names = array_map(fn ($c) => $c->getName(), $constants);
|
|
|
|
// After Task 2 refactor, TABLE_CONFIG should be removed (delegated to AuditChainConfig::TABLES)
|
|
expect($names)->not->toContain('TABLE_CONFIG');
|
|
});
|