From a83581612aa5f18f0c2ec3edbb79419dda963240 Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Mon, 2 Mar 2026 16:00:36 -0300 Subject: [PATCH] fix: add null check matchesStrategy --- .github/workflows/push.yml | 9 + app/Audit/AuditLogFormatterFactory.php | 9 +- .../AuditLogFormatterFactoryTest.php | 157 ++++++++++++++++++ 3 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 tests/OpenTelemetry/AuditLogFormatterFactoryTest.php diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 4e0728023..9e4f70158 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -30,6 +30,8 @@ jobs: run: | echo "running OpenTelemetry Formatter tests" vendor/bin/phpunit tests/OpenTelemetry/Formatters/ --log-junit results_opentelemetry_tests.xml + echo "running AuditLogFormatterFactoryTest" + vendor/bin/phpunit tests/OpenTelemetry/AuditLogFormatterFactoryTest.php --log-junit results_audit_formatter_tests.xml - name: Upload OpenTelemetry Tests Output uses: actions/upload-artifact@v4 @@ -38,6 +40,13 @@ jobs: path: results_opentelemetry_tests.xml retention-days: 5 + - name: Upload AuditLogFormatterFactory Tests Output + uses: actions/upload-artifact@v4 + with: + name: results_audit_formatter_tests + path: results_audit_formatter_tests.xml + retention-days: 5 + integration-tests: runs-on: ubuntu-latest strategy: diff --git a/app/Audit/AuditLogFormatterFactory.php b/app/Audit/AuditLogFormatterFactory.php index a66d2032b..b0e828d53 100644 --- a/app/Audit/AuditLogFormatterFactory.php +++ b/app/Audit/AuditLogFormatterFactory.php @@ -163,8 +163,13 @@ private function getFormatterByContext(object $subject, string $event_type, Audi private function matchesStrategy(array $strategy, AuditContext $ctx): bool { - if (isset($strategy['route']) && !$this->routeMatches($strategy['route'], $ctx->rawRoute)) { - return false; + if (isset($strategy['route'])) { + if ($ctx->rawRoute === null) { + return false; + } + if (!$this->routeMatches($strategy['route'], $ctx->rawRoute)) { + return false; + } } return true; diff --git a/tests/OpenTelemetry/AuditLogFormatterFactoryTest.php b/tests/OpenTelemetry/AuditLogFormatterFactoryTest.php new file mode 100644 index 000000000..2569bdfb4 --- /dev/null +++ b/tests/OpenTelemetry/AuditLogFormatterFactoryTest.php @@ -0,0 +1,157 @@ +factory = new AuditLogFormatterFactory(); + } + + + public function testMatchesStrategyHandlesNullRawRouteWithRouteRequired(): void + { + $strategy = [ + 'route' => self::ROUTE_CREATE_EVENT, + 'formatter' => self::FORMATTER_CLASS + ]; + + $ctx = new AuditContext( + userId: null, + userEmail: null, + userFirstName: null, + userLastName: null, + uiApp: null, + uiFlow: null, + route: null, + rawRoute: null, // null rawRoute from console command + httpMethod: null, + clientIp: null, + userAgent: null + ); + + $reflection = new \ReflectionClass($this->factory); + $method = $reflection->getMethod('matchesStrategy'); + $method->setAccessible(true); + + $result = $method->invoke($this->factory, $strategy, $ctx); + $this->assertFalse($result, 'matchesStrategy should return false when rawRoute is null and route is required'); + } + + + public function testMatchesStrategyReturnsTrueWhenNoRouteRequiredAndRawRouteNull(): void + { + $strategy = [ + 'formatter' => self::FORMATTER_CLASS + ]; + + $ctx = new AuditContext( + userId: null, + userEmail: null, + userFirstName: null, + userLastName: null, + uiApp: null, + uiFlow: null, + route: null, + rawRoute: null, + httpMethod: null, + clientIp: null, + userAgent: null + ); + + $reflection = new \ReflectionClass($this->factory); + $method = $reflection->getMethod('matchesStrategy'); + $method->setAccessible(true); + + $result = $method->invoke($this->factory, $strategy, $ctx); + $this->assertTrue($result, 'matchesStrategy should return true when no route is required'); + } + + + public function testMatchesStrategyReturnsTrueWhenRouteMatches(): void + { + $strategy = [ + 'route' => self::ROUTE_CREATE_EVENT, + 'formatter' => self::FORMATTER_CLASS + ]; + + $ctx = new AuditContext( + userId: null, + userEmail: null, + userFirstName: null, + userLastName: null, + uiApp: null, + uiFlow: null, + route: null, + rawRoute: self::ROUTE_CREATE_EVENT, // matching rawRoute + httpMethod: null, + clientIp: null, + userAgent: null + ); + + $reflection = new \ReflectionClass($this->factory); + $method = $reflection->getMethod('matchesStrategy'); + $method->setAccessible(true); + + $result = $method->invoke($this->factory, $strategy, $ctx); + $this->assertTrue($result, 'matchesStrategy should return true when routes match'); + } + + + public function testMatchesStrategyReturnsFalseWhenRouteDoesNotMatch(): void + { + $strategy = [ + 'route' => self::ROUTE_CREATE_EVENT, + 'formatter' => self::FORMATTER_CLASS + ]; + + $ctx = new AuditContext( + userId: null, + userEmail: null, + userFirstName: null, + userLastName: null, + uiApp: null, + uiFlow: null, + route: null, + rawRoute: self::ROUTE_UPDATE_PRESENTATION, // non-matching rawRoute + httpMethod: null, + clientIp: null, + userAgent: null + ); + + $reflection = new \ReflectionClass($this->factory); + $method = $reflection->getMethod('matchesStrategy'); + $method->setAccessible(true); + + $result = $method->invoke($this->factory, $strategy, $ctx); + $this->assertFalse($result, 'matchesStrategy should return false when routes do not match'); + } +}