refactor: replace hardcoded strings with constant references (#100)

* fix(tests): remove error_log calls and clean up ComprehensiveValidationTest

* refactor: replace hardcoded strings with MaskConstants and TestConstants references

* fix(streaming): replace overcounting '[' heuristic with proper mask detection

StreamingProcessor::getStatistics() was counting any message containing '['
as masked, causing false positives. Now checks for specific mask constants
(MASK_GENERIC, MASK_BRACKETS, MASK_REDACTED_BRACKETS) instead.

Also adds MASK_REDACTED_BRACKETS constant to MaskConstants and removes
the now-unnecessary UnusedFunctionCall psalm suppression.

* refactor(tests): replace remaining hardcoded literals with constant references

Add new constants to TestConstants (MASK_REDACTED_PLAIN, MASK_SECRET_BRACKETS,
MASK_SSN_BRACKETS, PATTERN_REDOS_NESTED_STAR, FIELD_USER_SSN, FIELD_USER_DATA)
and replace all matching literals across 21 test files.

Also removes dead memory_get_usage() call and uses existing
TestConstants::IP_ADDRESS_PUBLIC for hardcoded IP.

* fix(streaming): replace mask-token heuristic with accurate record comparison in getStatistics()

The previous implementation only detected masking when specific mask tokens
appeared in the message, missing cases where context was masked or different
mask values were used. Compare original vs processed records instead.

* refactor(tests): add PATTERN_EMAIL_SIMPLE, MASK_CARD_BRACKETS, EXPECTED_SSN_MASKED constants

Replace cross-file duplicate literals with TestConstants references:
- Email regex (4 files), '[CARD]' (2 files), 'SSN: [SSN]' (2 files)

* fix(streaming): bypass audit logger in getStatistics() by calling orchestrator directly

getStatistics() previously routed through processStream()/processChunk() which
triggered the audit logger for each record. A read-only statistics method should
not produce audit side-effects. Now calls orchestrator.process() directly and
processes records one at a time without materializing the entire iterable.

* refactor(tests): fix test quality issues and add PATTERN_CREDIT_CARD constant

- Replace fail() message that leaked sensitive terms with count-only message
- Replace bare 'EMAIL' string with MaskConstants::MASK_EMAIL for consistency
- Remove error_log() debug output from CriticalBugRegressionTest
- Add TestConstants::PATTERN_CREDIT_CARD and replace inline regex in 3 files
This commit is contained in:
2026-03-08 13:50:17 +02:00
committed by GitHub
parent e58397a75d
commit b0925ce489
51 changed files with 386 additions and 430 deletions

View File

@@ -25,13 +25,13 @@ final class StreamingProcessorTest extends TestCase
$processor = new StreamingProcessor($this->createOrchestrator(), 10);
$records = [
['message' => 'test message', 'context' => []],
[TestConstants::FIELD_MESSAGE => TestConstants::MESSAGE_TEST_LOWERCASE, 'context' => []],
];
$results = iterator_to_array($processor->processStream($records));
$this->assertCount(1, $results);
$this->assertSame(MaskConstants::MASK_GENERIC . ' message', $results[0]['message']);
$this->assertSame(MaskConstants::MASK_GENERIC . ' message', $results[0][TestConstants::FIELD_MESSAGE]);
}
public function testProcessStreamMultipleRecords(): void
@@ -39,9 +39,9 @@ final class StreamingProcessorTest extends TestCase
$processor = new StreamingProcessor($this->createOrchestrator(), 10);
$records = [
['message' => 'test one', 'context' => []],
['message' => 'test two', 'context' => []],
['message' => 'test three', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test one', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test two', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test three', 'context' => []],
];
$results = iterator_to_array($processor->processStream($records));
@@ -54,11 +54,11 @@ final class StreamingProcessorTest extends TestCase
$processor = new StreamingProcessor($this->createOrchestrator(), 2);
$records = [
['message' => 'test 1', 'context' => []],
['message' => 'test 2', 'context' => []],
['message' => 'test 3', 'context' => []],
['message' => 'test 4', 'context' => []],
['message' => 'test 5', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test 1', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test 2', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test 3', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test 4', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test 5', 'context' => []],
];
$results = iterator_to_array($processor->processStream($records));
@@ -71,12 +71,12 @@ final class StreamingProcessorTest extends TestCase
$processor = new StreamingProcessor($this->createOrchestrator(), 10);
$records = [
['message' => 'message', 'context' => ['key' => 'test value']],
[TestConstants::FIELD_MESSAGE => TestConstants::FIELD_MESSAGE, 'context' => ['key' => TestConstants::VALUE_TEST]],
];
$results = iterator_to_array($processor->processStream($records));
$this->assertSame(MaskConstants::MASK_GENERIC . ' value', $results[0]['context']['key']);
$this->assertSame(MaskConstants::MASK_GENERIC . TestConstants::VALUE_SUFFIX, $results[0]['context']['key']);
}
public function testProcessStreamWithGenerator(): void
@@ -84,9 +84,9 @@ final class StreamingProcessorTest extends TestCase
$processor = new StreamingProcessor($this->createOrchestrator(), 2);
$generator = (function () {
yield ['message' => 'test a', 'context' => []];
yield ['message' => 'test b', 'context' => []];
yield ['message' => 'test c', 'context' => []];
yield [TestConstants::FIELD_MESSAGE => 'test a', 'context' => []];
yield [TestConstants::FIELD_MESSAGE => 'test b', 'context' => []];
yield [TestConstants::FIELD_MESSAGE => 'test c', 'context' => []];
})();
$results = iterator_to_array($processor->processStream($generator));
@@ -104,7 +104,7 @@ final class StreamingProcessorTest extends TestCase
file_put_contents($tempFile, "test line 1\ntest line 2\ntest line 3\n");
try {
$lineParser = fn(string $line): array => ['message' => $line, 'context' => []];
$lineParser = fn(string $line): array => [TestConstants::FIELD_MESSAGE => $line, 'context' => []];
$results = [];
foreach ($processor->processFile($tempFile, $lineParser) as $result) {
@@ -112,7 +112,7 @@ final class StreamingProcessorTest extends TestCase
}
$this->assertCount(3, $results);
$this->assertStringContainsString(MaskConstants::MASK_GENERIC, $results[0]['message']);
$this->assertStringContainsString(MaskConstants::MASK_GENERIC, $results[0][TestConstants::FIELD_MESSAGE]);
} finally {
unlink($tempFile);
}
@@ -127,7 +127,7 @@ final class StreamingProcessorTest extends TestCase
file_put_contents($tempFile, "test line 1\n\n\ntest line 2\n");
try {
$lineParser = fn(string $line): array => ['message' => $line, 'context' => []];
$lineParser = fn(string $line): array => [TestConstants::FIELD_MESSAGE => $line, 'context' => []];
$results = iterator_to_array($processor->processFile($tempFile, $lineParser));
@@ -144,7 +144,7 @@ final class StreamingProcessorTest extends TestCase
$this->expectException(StreamingOperationFailedException::class);
$this->expectExceptionMessage('Cannot open input file for streaming:');
iterator_to_array($processor->processFile('/nonexistent/path/file.log', fn(string $l): array => ['message' => $l, 'context' => []]));
iterator_to_array($processor->processFile('/nonexistent/path/file.log', fn(string $l): array => [TestConstants::FIELD_MESSAGE => $l, 'context' => []]));
}
public function testProcessToFile(): void
@@ -152,15 +152,15 @@ final class StreamingProcessorTest extends TestCase
$processor = new StreamingProcessor($this->createOrchestrator(), 10);
$records = [
['message' => 'test line 1', 'context' => []],
['message' => 'test line 2', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test line 1', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test line 2', 'context' => []],
];
$outputFile = tempnam(sys_get_temp_dir(), 'gdpr_output_');
$this->assertIsString($outputFile, 'Failed to create temp file');
try {
$formatter = fn(array $record): string => $record['message'];
$formatter = fn(array $record): string => $record[TestConstants::FIELD_MESSAGE];
$count = $processor->processToFile($records, $outputFile, $formatter);
$this->assertSame(2, $count);
@@ -188,15 +188,33 @@ final class StreamingProcessorTest extends TestCase
$processor = new StreamingProcessor($this->createOrchestrator(), 10);
$records = [
['message' => 'test masked', 'context' => []],
['message' => 'no sensitive data', 'context' => []],
['message' => 'another test here', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'test masked', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'no sensitive data', 'context' => []],
[TestConstants::FIELD_MESSAGE => 'another test here', 'context' => []],
];
$stats = $processor->getStatistics($records);
$this->assertSame(3, $stats['processed']);
$this->assertGreaterThan(0, $stats['masked']); // At least some should be masked
$this->assertSame(2, $stats[TestConstants::DATA_MASKED]);
}
public function testGetStatisticsDoesNotTriggerAuditLogger(): void
{
$auditCalled = false;
$auditLogger = function () use (&$auditCalled): void {
$auditCalled = true;
};
$processor = new StreamingProcessor($this->createOrchestrator(), 10, $auditLogger);
$records = [
[TestConstants::FIELD_MESSAGE => TestConstants::DATA_TEST_DATA, 'context' => []],
];
$processor->getStatistics($records);
$this->assertFalse($auditCalled);
}
public function testSetAuditLogger(): void
@@ -209,7 +227,7 @@ final class StreamingProcessorTest extends TestCase
$processor = new StreamingProcessor($this->createOrchestrator(), 1);
$processor->setAuditLogger($auditLogger);
$records = [['message' => 'test', 'context' => []]];
$records = [[TestConstants::FIELD_MESSAGE => 'test', 'context' => []]];
iterator_to_array($processor->processStream($records));
$this->assertNotEmpty($logs);
@@ -235,7 +253,7 @@ final class StreamingProcessorTest extends TestCase
$records = [];
for ($i = 1; $i <= 500; $i++) {
$records[] = ['message' => "test record {$i}", 'context' => []];
$records[] = [TestConstants::FIELD_MESSAGE => "test record {$i}", 'context' => []];
}
$count = 0;