2026-05-29 17:13:34 +03:00
|
|
|
<?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);
|
|
|
|
|
|
2026-05-29 17:46:23 +03:00
|
|
|
use App\Console\Commands\VerifyAuditChains;
|
2026-05-29 17:13:34 +03:00
|
|
|
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');
|
2026-05-29 17:46:23 +03:00
|
|
|
})->throws(InvalidArgumentException::class);
|
2026-05-29 17:13:34 +03:00
|
|
|
|
|
|
|
|
it('VerifyAuditChains command class exists and is registered', function (): void {
|
2026-05-29 17:46:23 +03:00
|
|
|
expect(class_exists(VerifyAuditChains::class))->toBeTrue();
|
2026-05-29 17:13:34 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('VerifyAuditChains does not have private TABLE_CONFIG const after ADR-018 refactor', function (): void {
|
2026-05-29 17:46:23 +03:00
|
|
|
$reflection = new ReflectionClass(VerifyAuditChains::class);
|
2026-05-29 17:13:34 +03:00
|
|
|
$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');
|
|
|
|
|
});
|