mirror of
https://github.com/ivuorinen/monolog-gdpr-filter.git
synced 2026-02-18 05:51:52 +00:00
feat: add advanced architecture, documentation, and coverage improvements (#65)
* fix(style): resolve PHPCS line-length warnings in source files * fix(style): resolve PHPCS line-length warnings in test files * feat(audit): add structured audit logging with ErrorContext and AuditContext - ErrorContext: standardized error information with sensitive data sanitization - AuditContext: structured context for audit entries with operation types - StructuredAuditLogger: enhanced audit logger wrapper with timing support * feat(recovery): add recovery mechanism for failed masking operations - FailureMode enum: FAIL_OPEN, FAIL_CLOSED, FAIL_SAFE modes - RecoveryStrategy interface and RecoveryResult value object - RetryStrategy: exponential backoff with configurable attempts - FallbackMaskStrategy: type-aware fallback values * feat(strategies): add CallbackMaskingStrategy for custom masking logic - Wraps custom callbacks as MaskingStrategy implementations - Factory methods: constant(), hash(), partial() for common use cases - Supports exact match and prefix match for field paths * docs: add framework integration guides and examples - symfony-integration.md: Symfony service configuration and Monolog setup - psr3-decorator.md: PSR-3 logger decorator pattern implementation - framework-examples.md: CakePHP, CodeIgniter 4, Laminas, Yii2, PSR-15 - docker-development.md: Docker development environment guide * chore(docker): add Docker development environment - Dockerfile: PHP 8.2-cli-alpine with Xdebug for coverage - docker-compose.yml: development services with volume mounts * feat(demo): add interactive GDPR pattern tester playground - PatternTester.php: pattern testing utility with strategy support - index.php: web API endpoint with JSON response handling - playground.html: interactive web interface for testing patterns * docs(todo): update with completed medium priority items - Mark all PHPCS warnings as fixed (81 → 0) - Document new Audit and Recovery features - Update test count to 1,068 tests with 2,953 assertions - Move remaining items to low priority * feat: add advanced architecture, documentation, and coverage improvements - Add architecture improvements: - ArrayAccessorInterface and DotArrayAccessor for decoupled array access - MaskingOrchestrator for single-responsibility masking coordination - GdprProcessorBuilder for fluent configuration - MaskingPluginInterface and AbstractMaskingPlugin for plugin architecture - PluginAwareProcessor for plugin hook execution - AuditLoggerFactory for instance-based audit logger creation - Add advanced features: - SerializedDataProcessor for handling print_r/var_export/serialize output - KAnonymizer with GeneralizationStrategy for GDPR k-anonymity - RetentionPolicy for configurable data retention periods - StreamingProcessor for memory-efficient large log processing - Add comprehensive documentation: - docs/performance-tuning.md - benchmarking, optimization, caching - docs/troubleshooting.md - common issues and solutions - docs/logging-integrations.md - ELK, Graylog, Datadog, etc. - docs/plugin-development.md - complete plugin development guide - Improve test coverage (84.41% → 85.07%): - ConditionalRuleFactoryInstanceTest (100% coverage) - GdprProcessorBuilderEdgeCasesTest (100% coverage) - StrategyEdgeCasesTest for ReDoS detection and type parsing - 78 new tests, 119 new assertions - Update TODO.md with current statistics: - 141 PHP files, 1,346 tests, 85.07% line coverage * chore: tests, update actions, sonarcloud issues * chore: rector * fix: more sonarcloud fixes * chore: more fixes * refactor: copilot review fix * chore: rector
This commit is contained in:
197
tests/Plugins/AbstractMaskingPluginTest.php
Normal file
197
tests/Plugins/AbstractMaskingPluginTest.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Plugins;
|
||||
|
||||
use Ivuorinen\MonologGdprFilter\Contracts\MaskingPluginInterface;
|
||||
use Ivuorinen\MonologGdprFilter\Plugins\AbstractMaskingPlugin;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(AbstractMaskingPlugin::class)]
|
||||
final class AbstractMaskingPluginTest extends TestCase
|
||||
{
|
||||
public function testImplementsInterface(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$this->assertInstanceOf(MaskingPluginInterface::class, $plugin);
|
||||
}
|
||||
|
||||
public function testDefaultPriority(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$this->assertSame(100, $plugin->getPriority());
|
||||
}
|
||||
|
||||
public function testCustomPriority(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(50);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$this->assertSame(50, $plugin->getPriority());
|
||||
}
|
||||
|
||||
public function testPreProcessContextReturnsUnchanged(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$context = ['key' => 'value'];
|
||||
$result = $plugin->preProcessContext($context);
|
||||
|
||||
$this->assertSame($context, $result);
|
||||
}
|
||||
|
||||
public function testPostProcessContextReturnsUnchanged(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$context = ['key' => 'value'];
|
||||
$result = $plugin->postProcessContext($context);
|
||||
|
||||
$this->assertSame($context, $result);
|
||||
}
|
||||
|
||||
public function testPreProcessMessageReturnsUnchanged(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$message = 'test message';
|
||||
$result = $plugin->preProcessMessage($message);
|
||||
|
||||
$this->assertSame($message, $result);
|
||||
}
|
||||
|
||||
public function testPostProcessMessageReturnsUnchanged(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$message = 'test message';
|
||||
$result = $plugin->postProcessMessage($message);
|
||||
|
||||
$this->assertSame($message, $result);
|
||||
}
|
||||
|
||||
public function testGetPatternsReturnsEmptyArray(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$this->assertSame([], $plugin->getPatterns());
|
||||
}
|
||||
|
||||
public function testGetFieldPathsReturnsEmptyArray(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
};
|
||||
|
||||
$this->assertSame([], $plugin->getFieldPaths());
|
||||
}
|
||||
|
||||
public function testCanOverridePreProcessContext(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
|
||||
public function preProcessContext(array $context): array
|
||||
{
|
||||
$context['added'] = true;
|
||||
return $context;
|
||||
}
|
||||
};
|
||||
|
||||
$result = $plugin->preProcessContext(['original' => 'value']);
|
||||
|
||||
$this->assertTrue($result['added']);
|
||||
$this->assertSame('value', $result['original']);
|
||||
}
|
||||
|
||||
public function testCanOverridePreProcessMessage(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
|
||||
public function preProcessMessage(string $message): string
|
||||
{
|
||||
return strtoupper($message);
|
||||
}
|
||||
};
|
||||
|
||||
$this->assertSame('HELLO', $plugin->preProcessMessage('hello'));
|
||||
}
|
||||
|
||||
public function testCanOverrideGetPatterns(): void
|
||||
{
|
||||
$plugin = new class extends AbstractMaskingPlugin {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'test-plugin';
|
||||
}
|
||||
|
||||
public function getPatterns(): array
|
||||
{
|
||||
return ['/secret/' => '[REDACTED]'];
|
||||
}
|
||||
};
|
||||
|
||||
$patterns = $plugin->getPatterns();
|
||||
|
||||
$this->assertArrayHasKey('/secret/', $patterns);
|
||||
$this->assertSame('[REDACTED]', $patterns['/secret/']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user