From 1bbab234ff4521ffe25636cf6ca89112ddd61c0f Mon Sep 17 00:00:00 2001 From: Bohdan Maletskyi Date: Mon, 10 Mar 2025 19:48:32 +0200 Subject: [PATCH 1/4] CRV-1021 implemented sharing correlation id between services --- CHANGELOG.md | 4 + README.md | 8 ++ composer.json | 1 + src/Resources/config/services.xml | 5 ++ .../CorrelationIdFromHeaderExtractor.php | 17 ++++ .../CorrelationIdHttpClientDecorator.php | 34 ++++++++ src/Service/CorrelationIdProvider.php | 9 ++- .../CorrelationIdHeaderProviderTest.php | 42 ++++++++++ .../CorrelationIdHttpClientDecoratorTest.php | 76 ++++++++++++++++++ .../Service/CorrelationIdProviderTest.php | 77 +++++++++++++++++++ 10 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 src/Service/CorrelationIdFromHeaderExtractor.php create mode 100644 src/Service/CorrelationIdHttpClientDecorator.php create mode 100644 tests/Unit/Service/CorrelationIdHeaderProviderTest.php create mode 100644 tests/Unit/Service/CorrelationIdHttpClientDecoratorTest.php create mode 100644 tests/Unit/Service/CorrelationIdProviderTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a53c511..7f6938e 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 2.3.0 +### Added +- Added sharing correlation ID between services. + ## 2.2.0 ### Added - Added Symfony ^6 support. diff --git a/README.md b/README.md index 668d234..2ee39ca 100755 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ the following features: - allows grouping some exceptions by their class, independently from where they were thrown at or what are their message; - removes root prefix from messages (usually included in some exception messages); - maps context to be available with logged sentry event. +- allows sharing the same correlation_id across multiple services via an HTTP header Also recommended configuration is given to allow nice synergy between Graylog and Sentry. @@ -97,6 +98,13 @@ sentry: paysera_logging_extra: application_name: app-something # customise this to know which project message was sent from + +# Enable sharing correlation ID between requests if needed +Paysera\LoggingExtraBundle\Service\CorrelationIdHttpClientDecorator: + decorates: http_client + arguments: + - '@paysera_logging_extra.correlation_id_provider' + - '@.inner' ``` ## Usage diff --git a/composer.json b/composer.json index a5c5052..adbea65 100755 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "symfony/dependency-injection": "^3.4|^4.0|^5.0|^6.0", "symfony/expression-language": "^3.0 || ^4.0 || ^5.0 || ^6.0", "symfony/framework-bundle": "^3.4.26|^4.2.7|^5.0|^6.0", + "symfony/http-client-contracts": "^3.5", "symfony/http-kernel": "^3.4|^4.0|^5.0|^6.0", "symfony/monolog-bundle": "^3.4|^5.0" }, diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 1d3035e..59b59b9 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -9,9 +9,14 @@ + + + %paysera_logging_extra.application_name% + diff --git a/src/Service/CorrelationIdFromHeaderExtractor.php b/src/Service/CorrelationIdFromHeaderExtractor.php new file mode 100644 index 0000000..5f0842f --- /dev/null +++ b/src/Service/CorrelationIdFromHeaderExtractor.php @@ -0,0 +1,17 @@ +client = $client ?? HttpClient::create(); + $this->correlationIdProvider = $correlationIdProvider; + } + + public function request(string $method, string $url, array $options = []): ResponseInterface + { + $options['headers'] = $options['headers'] ?? []; + $options['headers'][CorrelationIdListener::HEADER_NAME] = $this->correlationIdProvider->getCorrelationId(); + + return $this->client->request($method, $url, $options); + } +} diff --git a/src/Service/CorrelationIdProvider.php b/src/Service/CorrelationIdProvider.php index e538d2c..31233d1 100644 --- a/src/Service/CorrelationIdProvider.php +++ b/src/Service/CorrelationIdProvider.php @@ -8,15 +8,22 @@ class CorrelationIdProvider { private $correlationId; private $increment; + private $correlationIdFromHeaderExtractor; - public function __construct(string $systemName) + public function __construct(string $systemName, CorrelationIdFromHeaderExtractor $correlationIdFromHeaderExtractor) { $this->correlationId = uniqid($systemName, true); $this->increment = 0; + $this->correlationIdFromHeaderExtractor = $correlationIdFromHeaderExtractor; } public function getCorrelationId(): string { + $correlationIdFromHeader = $this->correlationIdFromHeaderExtractor->getCorrelationId(); + if ($correlationIdFromHeader !== null) { + return $correlationIdFromHeader; + } + if ($this->increment === 0) { return $this->correlationId; } diff --git a/tests/Unit/Service/CorrelationIdHeaderProviderTest.php b/tests/Unit/Service/CorrelationIdHeaderProviderTest.php new file mode 100644 index 0000000..fb67c4b --- /dev/null +++ b/tests/Unit/Service/CorrelationIdHeaderProviderTest.php @@ -0,0 +1,42 @@ +provider = new CorrelationIdFromHeaderExtractor(); + } + + public function testGetHeaderReturnsNullWhenHeaderIsNotSet(): void + { + // Arrange: Ensure $_SERVER is empty + unset($_SERVER['HTTP_PAYSERA_CORRELATION_ID']); + + // Act: Call the method + $result = $this->provider->getCorrelationId(); + + // Assert: It should return null + $this->assertNull($result); + } + + public function testGetHeaderReturnsValueWhenHeaderIsSet(): void + { + // Arrange: Set the expected header in $_SERVER + $_SERVER['HTTP_PAYSERA_CORRELATION_ID'] = 'test-correlation-id'; + + // Act: Call the method + $result = $this->provider->getCorrelationId(); + + // Assert: It should return the correct value + $this->assertSame('test-correlation-id', $result); + } +} diff --git a/tests/Unit/Service/CorrelationIdHttpClientDecoratorTest.php b/tests/Unit/Service/CorrelationIdHttpClientDecoratorTest.php new file mode 100644 index 0000000..89afef2 --- /dev/null +++ b/tests/Unit/Service/CorrelationIdHttpClientDecoratorTest.php @@ -0,0 +1,76 @@ +httpClient = $this->createMock(HttpClientInterface::class); + + $requestStack = $this->createMock(RequestStack::class); + + $correlationIdProvider = $this->createMock(CorrelationIdProvider::class); + $correlationIdProvider->method('getCorrelationId')->willReturn('mock-correlation-id'); + + $this->decorator = new CorrelationIdHttpClientDecorator($correlationIdProvider, $this->httpClient); + } + + public function testSendRequestAddsCustomHeader(): void + { + $url = 'https://api.paysera.com'; + $options = ['headers' => ['Authorization' => 'Bearer mock-token']]; + + $this->httpClient + ->expects($this->once()) + ->method('request') + ->with( + $this->equalTo('GET'), + $this->equalTo($url), + $this->equalTo( + [ + 'headers' => [ + 'Authorization' => 'Bearer mock-token', + 'Paysera-Correlation-Id' => 'mock-correlation-id', + ], + ] + ) + ) + ->willReturn($this->createMock(ResponseInterface::class)); + + $this->decorator->request('GET', $url, $options); + } + + public function testSendRequestAddsCustomHeaderIfHeadersNotExists(): void + { + $url = 'https://api.paysera.com'; + + $this->httpClient + ->expects($this->once()) + ->method('request') + ->with( + $this->equalTo('GET'), + $this->equalTo($url), + $this->equalTo( + ['headers' => ['Paysera-Correlation-Id' => 'mock-correlation-id']] + ) + ) + ->willReturn($this->createMock(ResponseInterface::class)); + + $this->decorator->request('GET', $url, []); + } +} diff --git a/tests/Unit/Service/CorrelationIdProviderTest.php b/tests/Unit/Service/CorrelationIdProviderTest.php new file mode 100644 index 0000000..ec9e9e0 --- /dev/null +++ b/tests/Unit/Service/CorrelationIdProviderTest.php @@ -0,0 +1,77 @@ +correlationIdFromHeaderExtractor = $this->createMock(CorrelationIdFromHeaderExtractor::class); + $this->correlationIdProvider = new CorrelationIdProvider( + 'test-system', + $this->correlationIdFromHeaderExtractor + ); + } + + public function testInitialCorrelationId(): void + { + $uniqueId = $this->correlationIdProvider->getCorrelationId(); + $this->assertStringStartsWith( + 'test-system', + $uniqueId, + 'Initial correlation ID did not start with the system name' + ); + } + + public function testGetCorrelationIdWithoutRequest(): void + { + $this->correlationIdFromHeaderExtractor->method('getCorrelationId')->willReturn(null); + + $correlationId = $this->correlationIdProvider->getCorrelationId(); + $this->assertStringStartsWith('test-system', $correlationId); + } + + public function testGetCorrelationIdFromRequestHeader(): void + { + $this->correlationIdFromHeaderExtractor->method('getCorrelationId')->willReturn('test-correlation-id'); + + $correlationId = $this->correlationIdProvider->getCorrelationId(); + $this->assertEquals( + 'test-correlation-id', + $correlationId, + 'Correlation ID did not match the value from the header' + ); + } + + public function testIncrementedCorrelationId(): void + { + $this->correlationIdFromHeaderExtractor->method('getCorrelationId')->willReturn(null); + + $initialCorrelationId = $this->correlationIdProvider->getCorrelationId(); + + $this->correlationIdProvider->incrementIdentifier(); + $incrementedCorrelationId = $this->correlationIdProvider->getCorrelationId(); + + $this->assertNotEquals($initialCorrelationId, $incrementedCorrelationId); + $this->assertStringEndsWith('_1', $incrementedCorrelationId, 'Incremented correlation ID did not end with _1'); + + $this->correlationIdProvider->incrementIdentifier(); + $secondIncrementedCorrelationId = $this->correlationIdProvider->getCorrelationId(); + + $this->assertStringEndsWith( + '_2', + $secondIncrementedCorrelationId, + 'Correlation ID after second increment did not end with _2' + ); + } +} \ No newline at end of file From 83c950a9449eb81835fe7cf3cd35e028e4c9bcdf Mon Sep 17 00:00:00 2001 From: Maksym Haiduk Date: Tue, 6 May 2025 14:22:31 +0300 Subject: [PATCH 2/4] CRV-1021: Implement fetching header via RequestStack --- src/Resources/config/services.xml | 1 + .../CorrelationIdFromHeaderExtractor.php | 14 ++++- .../CorrelationIdFromHeaderExtractorTest.php | 53 +++++++++++++++++++ .../CorrelationIdHeaderProviderTest.php | 42 --------------- .../Service/CorrelationIdProviderTest.php | 2 +- 5 files changed, 67 insertions(+), 45 deletions(-) create mode 100644 tests/Unit/Service/CorrelationIdFromHeaderExtractorTest.php delete mode 100644 tests/Unit/Service/CorrelationIdHeaderProviderTest.php diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 59b59b9..d36a356 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -11,6 +11,7 @@ + requestStack = $requestStack; + } + public function getCorrelationId(): ?string { - $headerName = 'HTTP_' . strtoupper(str_replace('-', '_', CorrelationIdListener::HEADER_NAME)); + $request = $this->requestStack->getCurrentRequest(); + if ($request === null) { + return null; + } - return $_SERVER[$headerName] ?? null; + return $request->headers->get(CorrelationIdListener::HEADER_NAME); } } diff --git a/tests/Unit/Service/CorrelationIdFromHeaderExtractorTest.php b/tests/Unit/Service/CorrelationIdFromHeaderExtractorTest.php new file mode 100644 index 0000000..d0b8d19 --- /dev/null +++ b/tests/Unit/Service/CorrelationIdFromHeaderExtractorTest.php @@ -0,0 +1,53 @@ +requestStack = new RequestStack(); + $this->provider = new CorrelationIdFromHeaderExtractor($this->requestStack); + } + + public function testGetHeaderReturnsNullWhenNoRequestInStack(): void + { + $this->assertNull( + $this->provider->getCorrelationId() + ); + } + + public function testGetHeaderReturnsNullWhenNoCorrelationIdHeaderInRequest(): void + { + $this->requestStack->push(new Request()); + + $this->assertNull( + $this->provider->getCorrelationId() + ); + } + + public function testGetHeaderReturnsValueWhenHeaderIsSet(): void + { + $correlationId = 'test-correlation-id'; + $request = new Request(); + $request->headers->set(CorrelationIdListener::HEADER_NAME, $correlationId); + $this->requestStack->push($request); + + $this->assertEquals( + $correlationId, + $this->provider->getCorrelationId() + ); + } +} diff --git a/tests/Unit/Service/CorrelationIdHeaderProviderTest.php b/tests/Unit/Service/CorrelationIdHeaderProviderTest.php deleted file mode 100644 index fb67c4b..0000000 --- a/tests/Unit/Service/CorrelationIdHeaderProviderTest.php +++ /dev/null @@ -1,42 +0,0 @@ -provider = new CorrelationIdFromHeaderExtractor(); - } - - public function testGetHeaderReturnsNullWhenHeaderIsNotSet(): void - { - // Arrange: Ensure $_SERVER is empty - unset($_SERVER['HTTP_PAYSERA_CORRELATION_ID']); - - // Act: Call the method - $result = $this->provider->getCorrelationId(); - - // Assert: It should return null - $this->assertNull($result); - } - - public function testGetHeaderReturnsValueWhenHeaderIsSet(): void - { - // Arrange: Set the expected header in $_SERVER - $_SERVER['HTTP_PAYSERA_CORRELATION_ID'] = 'test-correlation-id'; - - // Act: Call the method - $result = $this->provider->getCorrelationId(); - - // Assert: It should return the correct value - $this->assertSame('test-correlation-id', $result); - } -} diff --git a/tests/Unit/Service/CorrelationIdProviderTest.php b/tests/Unit/Service/CorrelationIdProviderTest.php index ec9e9e0..e7c771a 100644 --- a/tests/Unit/Service/CorrelationIdProviderTest.php +++ b/tests/Unit/Service/CorrelationIdProviderTest.php @@ -74,4 +74,4 @@ public function testIncrementedCorrelationId(): void 'Correlation ID after second increment did not end with _2' ); } -} \ No newline at end of file +} From 4aab8fbe6f209240163e9e692a43f129b0292bcc Mon Sep 17 00:00:00 2001 From: Maksym Haiduk Date: Tue, 6 May 2025 15:30:50 +0300 Subject: [PATCH 3/4] CRV-1021: Make correlation id fetching from header configurable and disabled by default --- README.md | 9 +++++---- src/DependencyInjection/Configuration.php | 1 + .../PayseraLoggingExtraExtension.php | 1 + src/Resources/config/services.xml | 1 + src/Service/CorrelationIdProvider.php | 17 ++++++++++++----- .../Unit/Service/CorrelationIdProviderTest.php | 11 +++++++++-- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2ee39ca..343094d 100755 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ the following features: - adds correlation_id to correlate messages in Sentry with messages in Graylog from the same process; - allows grouping some exceptions by their class, independently from where they were thrown at or what are their message; - removes root prefix from messages (usually included in some exception messages); -- maps context to be available with logged sentry event. +- maps context to be available with logged sentry event. - allows sharing the same correlation_id across multiple services via an HTTP header Also recommended configuration is given to allow nice synergy between Graylog and Sentry. @@ -97,7 +97,8 @@ sentry: send_attempts: 1 paysera_logging_extra: - application_name: app-something # customise this to know which project message was sent from + application_name: app-something # customize this to know which project message was sent from + fetch_correlation_id_from_header: true # try to fetch correlation id from request header. false by default # Enable sharing correlation ID between requests if needed Paysera\LoggingExtraBundle\Service\CorrelationIdHttpClientDecorator: @@ -153,7 +154,7 @@ docker-compose up -d docker-compose exec sentry sentry upgrade ``` -You'll find Graylog at [http://localhost:9001/](http://localhost:9001/) and Sentry at +You'll find Graylog at [http://localhost:9001/](http://localhost:9001/) and Sentry at [http://localhost:9002/](http://localhost:9002/). Open Graylog, login with `admin` `admin`, choose `System` -> `Inputs` -> `GELF UDP` -> `Launch new input` -> @@ -174,7 +175,7 @@ php test.php ``` View logged data in Graylog and Sentry instances. Change the code for further -test scenarios or just use Graylog and Sentry to set-up and test your real +test scenarios or just use Graylog and Sentry to set-up and test your real project. Cleanup afterwards: diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index d679554..0ba5217 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -22,6 +22,7 @@ public function getConfigTreeBuilder() $children = $rootNode->children(); $children->scalarNode('application_name')->isRequired(); $children->arrayNode('grouped_exceptions')->prototype('scalar'); + $children->booleanNode('fetch_correlation_id_from_header')->defaultFalse(); return $treeBuilder; } diff --git a/src/DependencyInjection/PayseraLoggingExtraExtension.php b/src/DependencyInjection/PayseraLoggingExtraExtension.php index aa3e9c1..0fac8d3 100644 --- a/src/DependencyInjection/PayseraLoggingExtraExtension.php +++ b/src/DependencyInjection/PayseraLoggingExtraExtension.php @@ -21,5 +21,6 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('paysera_logging_extra.application_name', $config['application_name']); $container->setParameter('paysera_logging_extra.grouped_exceptions', $config['grouped_exceptions']); + $container->setParameter('paysera_logging_extra.fetch_correlation_id_from_header', $config['fetch_correlation_id_from_header']); } } diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index d36a356..7f5c1d0 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -18,6 +18,7 @@ class="Paysera\LoggingExtraBundle\Service\CorrelationIdProvider"> %paysera_logging_extra.application_name% + %paysera_logging_extra.fetch_correlation_id_from_header% diff --git a/src/Service/CorrelationIdProvider.php b/src/Service/CorrelationIdProvider.php index 31233d1..dfea88a 100644 --- a/src/Service/CorrelationIdProvider.php +++ b/src/Service/CorrelationIdProvider.php @@ -9,19 +9,26 @@ class CorrelationIdProvider private $correlationId; private $increment; private $correlationIdFromHeaderExtractor; + private $fetchFromHeader; - public function __construct(string $systemName, CorrelationIdFromHeaderExtractor $correlationIdFromHeaderExtractor) - { + public function __construct( + string $systemName, + CorrelationIdFromHeaderExtractor $correlationIdFromHeaderExtractor, + bool $fetchFromHeader + ) { $this->correlationId = uniqid($systemName, true); $this->increment = 0; $this->correlationIdFromHeaderExtractor = $correlationIdFromHeaderExtractor; + $this->fetchFromHeader = $fetchFromHeader; } public function getCorrelationId(): string { - $correlationIdFromHeader = $this->correlationIdFromHeaderExtractor->getCorrelationId(); - if ($correlationIdFromHeader !== null) { - return $correlationIdFromHeader; + if ($this->fetchFromHeader) { + $correlationIdFromHeader = $this->correlationIdFromHeaderExtractor->getCorrelationId(); + if ($correlationIdFromHeader !== null) { + return $correlationIdFromHeader; + } } if ($this->increment === 0) { diff --git a/tests/Unit/Service/CorrelationIdProviderTest.php b/tests/Unit/Service/CorrelationIdProviderTest.php index e7c771a..453e448 100644 --- a/tests/Unit/Service/CorrelationIdProviderTest.php +++ b/tests/Unit/Service/CorrelationIdProviderTest.php @@ -19,7 +19,8 @@ protected function setUp(): void $this->correlationIdFromHeaderExtractor = $this->createMock(CorrelationIdFromHeaderExtractor::class); $this->correlationIdProvider = new CorrelationIdProvider( 'test-system', - $this->correlationIdFromHeaderExtractor + $this->correlationIdFromHeaderExtractor, + false ); } @@ -43,9 +44,15 @@ public function testGetCorrelationIdWithoutRequest(): void public function testGetCorrelationIdFromRequestHeader(): void { + $correlationIdProvider = new CorrelationIdProvider( + 'test-system', + $this->correlationIdFromHeaderExtractor, + true + ); + $this->correlationIdFromHeaderExtractor->method('getCorrelationId')->willReturn('test-correlation-id'); - $correlationId = $this->correlationIdProvider->getCorrelationId(); + $correlationId = $correlationIdProvider->getCorrelationId(); $this->assertEquals( 'test-correlation-id', $correlationId, From 2fa53eb87ddae8bf46e748fad3e75c02043e519b Mon Sep 17 00:00:00 2001 From: Maksym Haiduk Date: Wed, 7 May 2025 09:23:35 +0300 Subject: [PATCH 4/4] CRV-1021: Refactor CorrelationIdProvider --- README.md | 2 +- src/DependencyInjection/Configuration.php | 2 +- .../PayseraLoggingExtraExtension.php | 2 +- src/Listener/CorrelationIdListener.php | 2 +- src/Listener/IterationEndListener.php | 7 +- src/Resources/config/services.xml | 12 -- .../services/correlation_id_provider.xml | 26 +++ .../CorrelationIdFromHeaderExtractor.php | 27 --- .../CorrelationIdHttpClientDecorator.php | 1 + src/Service/CorrelationIdProvider.php | 45 ----- .../CorrelationIdProvider.php | 54 ++++++ .../GeneratedCorrelationIdProvider.php | 50 +++++ .../RequestHeaderCorrelationIdProvider.php | 44 +++++ .../Processor/CorrelationIdProcessor.php | 2 +- .../FunctionalCorrelationIdListenerTest.php | 2 +- .../CorrelationIdFromHeaderExtractorTest.php | 53 ----- .../CorrelationIdHttpClientDecoratorTest.php | 5 +- .../CorrelationIdProviderTest.php | 183 ++++++++++++++++++ .../GeneratedCorrelationIdProviderTest.php | 97 ++++++++++ ...RequestHeaderCorrelationIdProviderTest.php | 106 ++++++++++ .../Service/CorrelationIdProviderTest.php | 84 -------- 21 files changed, 570 insertions(+), 236 deletions(-) create mode 100644 src/Resources/config/services/correlation_id_provider.xml delete mode 100644 src/Service/CorrelationIdFromHeaderExtractor.php delete mode 100644 src/Service/CorrelationIdProvider.php create mode 100644 src/Service/CorrelationIdProvider/CorrelationIdProvider.php create mode 100644 src/Service/CorrelationIdProvider/GeneratedCorrelationIdProvider.php create mode 100644 src/Service/CorrelationIdProvider/RequestHeaderCorrelationIdProvider.php delete mode 100644 tests/Unit/Service/CorrelationIdFromHeaderExtractorTest.php create mode 100644 tests/Unit/Service/CorrelationIdProvider/CorrelationIdProviderTest.php create mode 100644 tests/Unit/Service/CorrelationIdProvider/GeneratedCorrelationIdProviderTest.php create mode 100644 tests/Unit/Service/CorrelationIdProvider/RequestHeaderCorrelationIdProviderTest.php delete mode 100644 tests/Unit/Service/CorrelationIdProviderTest.php diff --git a/README.md b/README.md index 343094d..3e2a8d3 100755 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ sentry: paysera_logging_extra: application_name: app-something # customize this to know which project message was sent from - fetch_correlation_id_from_header: true # try to fetch correlation id from request header. false by default + fetch_correlation_id_from_request: true # try to fetch correlation id from request header. false by default # Enable sharing correlation ID between requests if needed Paysera\LoggingExtraBundle\Service\CorrelationIdHttpClientDecorator: diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 0ba5217..589b172 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -22,7 +22,7 @@ public function getConfigTreeBuilder() $children = $rootNode->children(); $children->scalarNode('application_name')->isRequired(); $children->arrayNode('grouped_exceptions')->prototype('scalar'); - $children->booleanNode('fetch_correlation_id_from_header')->defaultFalse(); + $children->booleanNode('fetch_correlation_id_from_request')->defaultFalse(); return $treeBuilder; } diff --git a/src/DependencyInjection/PayseraLoggingExtraExtension.php b/src/DependencyInjection/PayseraLoggingExtraExtension.php index 0fac8d3..f741284 100644 --- a/src/DependencyInjection/PayseraLoggingExtraExtension.php +++ b/src/DependencyInjection/PayseraLoggingExtraExtension.php @@ -21,6 +21,6 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('paysera_logging_extra.application_name', $config['application_name']); $container->setParameter('paysera_logging_extra.grouped_exceptions', $config['grouped_exceptions']); - $container->setParameter('paysera_logging_extra.fetch_correlation_id_from_header', $config['fetch_correlation_id_from_header']); + $container->setParameter('paysera_logging_extra.fetch_correlation_id_from_request', $config['fetch_correlation_id_from_request']); } } diff --git a/src/Listener/CorrelationIdListener.php b/src/Listener/CorrelationIdListener.php index 55c2077..07b6a1e 100644 --- a/src/Listener/CorrelationIdListener.php +++ b/src/Listener/CorrelationIdListener.php @@ -4,7 +4,7 @@ namespace Paysera\LoggingExtraBundle\Listener; -use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider; +use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider; use Symfony\Component\HttpKernel\Event\FilterResponseEvent as LegacyResponseEvent; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; diff --git a/src/Listener/IterationEndListener.php b/src/Listener/IterationEndListener.php index dc55473..4c3e678 100644 --- a/src/Listener/IterationEndListener.php +++ b/src/Listener/IterationEndListener.php @@ -4,14 +4,11 @@ namespace Paysera\LoggingExtraBundle\Listener; -use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider; +use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider; use Sentry\ClientInterface; /** * Intended for cases where the same process is reused for separate job or request processing, like in PHPPM. - * - * Changes correlation_id while still maintaining same prefix to be able to find relations in cases of bugs happening - * due to shared state between different processing cycles */ class IterationEndListener { @@ -28,7 +25,7 @@ public function __construct( public function afterIteration() { - $this->correlationIdProvider->incrementIdentifier(); + $this->correlationIdProvider->reset(); if ($this->sentryClient !== null) { $this->sentryClient->flush(); } diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 7f5c1d0..5be5bd1 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -9,18 +9,6 @@ - - - - - - %paysera_logging_extra.application_name% - - %paysera_logging_extra.fetch_correlation_id_from_header% - - diff --git a/src/Resources/config/services/correlation_id_provider.xml b/src/Resources/config/services/correlation_id_provider.xml new file mode 100644 index 0000000..6b50f35 --- /dev/null +++ b/src/Resources/config/services/correlation_id_provider.xml @@ -0,0 +1,26 @@ + + + + + + + %paysera_logging_extra.application_name% + + + + + + + + + + + %paysera_logging_extra.fetch_correlation_id_from_request% + + + diff --git a/src/Service/CorrelationIdFromHeaderExtractor.php b/src/Service/CorrelationIdFromHeaderExtractor.php deleted file mode 100644 index e6f6242..0000000 --- a/src/Service/CorrelationIdFromHeaderExtractor.php +++ /dev/null @@ -1,27 +0,0 @@ -requestStack = $requestStack; - } - - public function getCorrelationId(): ?string - { - $request = $this->requestStack->getCurrentRequest(); - if ($request === null) { - return null; - } - - return $request->headers->get(CorrelationIdListener::HEADER_NAME); - } -} diff --git a/src/Service/CorrelationIdHttpClientDecorator.php b/src/Service/CorrelationIdHttpClientDecorator.php index 9ae0a6c..0e8a6fe 100644 --- a/src/Service/CorrelationIdHttpClientDecorator.php +++ b/src/Service/CorrelationIdHttpClientDecorator.php @@ -5,6 +5,7 @@ namespace Paysera\LoggingExtraBundle\Service; use Paysera\LoggingExtraBundle\Listener\CorrelationIdListener; +use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider; use Symfony\Component\HttpClient\DecoratorTrait; use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/Service/CorrelationIdProvider.php b/src/Service/CorrelationIdProvider.php deleted file mode 100644 index dfea88a..0000000 --- a/src/Service/CorrelationIdProvider.php +++ /dev/null @@ -1,45 +0,0 @@ -correlationId = uniqid($systemName, true); - $this->increment = 0; - $this->correlationIdFromHeaderExtractor = $correlationIdFromHeaderExtractor; - $this->fetchFromHeader = $fetchFromHeader; - } - - public function getCorrelationId(): string - { - if ($this->fetchFromHeader) { - $correlationIdFromHeader = $this->correlationIdFromHeaderExtractor->getCorrelationId(); - if ($correlationIdFromHeader !== null) { - return $correlationIdFromHeader; - } - } - - if ($this->increment === 0) { - return $this->correlationId; - } - - return sprintf('%s_%s', $this->correlationId, $this->increment); - } - - public function incrementIdentifier() - { - $this->increment++; - } -} diff --git a/src/Service/CorrelationIdProvider/CorrelationIdProvider.php b/src/Service/CorrelationIdProvider/CorrelationIdProvider.php new file mode 100644 index 0000000..3572af4 --- /dev/null +++ b/src/Service/CorrelationIdProvider/CorrelationIdProvider.php @@ -0,0 +1,54 @@ +generatedCorrelationIdProvider = $generatedCorrelationIdProvider; + $this->requestHeaderCorrelationIdProvider = $requestHeaderCorrelationIdProvider; + $this->fetchFromRequest = $fetchFromRequest; + $this->isFetchedFromRequest = false; + } + + public function getCorrelationId(): string + { + if ($this->fetchFromRequest) { + $correlationIdFromRequest = $this->requestHeaderCorrelationIdProvider->getCorrelationId(); + + if ($correlationIdFromRequest !== null) { + $this->isFetchedFromRequest = true; + + return $correlationIdFromRequest; + } + } + + return $this->generatedCorrelationIdProvider->getCorrelationId(); + } + + public function reset(): void + { + if ($this->isFetchedFromRequest) { + $this->requestHeaderCorrelationIdProvider->reset(); + $this->isFetchedFromRequest = false; + + return; + } + + $this->generatedCorrelationIdProvider->increment(); + } +} diff --git a/src/Service/CorrelationIdProvider/GeneratedCorrelationIdProvider.php b/src/Service/CorrelationIdProvider/GeneratedCorrelationIdProvider.php new file mode 100644 index 0000000..564793b --- /dev/null +++ b/src/Service/CorrelationIdProvider/GeneratedCorrelationIdProvider.php @@ -0,0 +1,50 @@ +systemName = $systemName; + $this->correlationId = null; + $this->increment = 0; + $this->correlationId = uniqid($this->systemName, true); + } + + public function getCorrelationId(): string + { + if ($this->increment === 0) { + return $this->correlationId; + } + + return $this->buildIncrementedCorrelationId(); + } + + private function buildIncrementedCorrelationId(): string + { + return sprintf('%s_%s', $this->correlationId, $this->increment); + } + + /** + * Changes correlation_id while still maintaining same prefix to be able to find relations in + * cases of bugs happening due to shared state between different processing cycles + */ + public function increment(): void + { + if ($this->increment === PHP_INT_MAX) { + $this->increment = 0; + } + + $this->increment++; + } +} diff --git a/src/Service/CorrelationIdProvider/RequestHeaderCorrelationIdProvider.php b/src/Service/CorrelationIdProvider/RequestHeaderCorrelationIdProvider.php new file mode 100644 index 0000000..98a821e --- /dev/null +++ b/src/Service/CorrelationIdProvider/RequestHeaderCorrelationIdProvider.php @@ -0,0 +1,44 @@ +requestStack = $requestStack; + $this->correlationId = null; + } + + public function getCorrelationId(): ?string + { + if ($this->correlationId !== null) { + return $this->correlationId; + } + + $request = $this->requestStack->getCurrentRequest(); + if ($request === null) { + return null; + } + + $this->correlationId = $request->headers->get(CorrelationIdListener::HEADER_NAME); + + return $this->correlationId; + } + + public function reset(): void + { + $this->correlationId = null; + } +} diff --git a/src/Service/Processor/CorrelationIdProcessor.php b/src/Service/Processor/CorrelationIdProcessor.php index 5541a63..5f09298 100644 --- a/src/Service/Processor/CorrelationIdProcessor.php +++ b/src/Service/Processor/CorrelationIdProcessor.php @@ -5,7 +5,7 @@ namespace Paysera\LoggingExtraBundle\Service\Processor; use Monolog\Processor\ProcessorInterface; -use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider; +use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider; class CorrelationIdProcessor implements ProcessorInterface { diff --git a/tests/Functional/FunctionalCorrelationIdListenerTest.php b/tests/Functional/FunctionalCorrelationIdListenerTest.php index fa5bece..8f1cb68 100644 --- a/tests/Functional/FunctionalCorrelationIdListenerTest.php +++ b/tests/Functional/FunctionalCorrelationIdListenerTest.php @@ -5,7 +5,7 @@ namespace Paysera\LoggingExtraBundle\Tests\Functional; use Paysera\LoggingExtraBundle\Listener\CorrelationIdListener; -use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider; +use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider; class FunctionalCorrelationIdListenerTest extends FunctionalTestCase { diff --git a/tests/Unit/Service/CorrelationIdFromHeaderExtractorTest.php b/tests/Unit/Service/CorrelationIdFromHeaderExtractorTest.php deleted file mode 100644 index d0b8d19..0000000 --- a/tests/Unit/Service/CorrelationIdFromHeaderExtractorTest.php +++ /dev/null @@ -1,53 +0,0 @@ -requestStack = new RequestStack(); - $this->provider = new CorrelationIdFromHeaderExtractor($this->requestStack); - } - - public function testGetHeaderReturnsNullWhenNoRequestInStack(): void - { - $this->assertNull( - $this->provider->getCorrelationId() - ); - } - - public function testGetHeaderReturnsNullWhenNoCorrelationIdHeaderInRequest(): void - { - $this->requestStack->push(new Request()); - - $this->assertNull( - $this->provider->getCorrelationId() - ); - } - - public function testGetHeaderReturnsValueWhenHeaderIsSet(): void - { - $correlationId = 'test-correlation-id'; - $request = new Request(); - $request->headers->set(CorrelationIdListener::HEADER_NAME, $correlationId); - $this->requestStack->push($request); - - $this->assertEquals( - $correlationId, - $this->provider->getCorrelationId() - ); - } -} diff --git a/tests/Unit/Service/CorrelationIdHttpClientDecoratorTest.php b/tests/Unit/Service/CorrelationIdHttpClientDecoratorTest.php index 89afef2..c9ea850 100644 --- a/tests/Unit/Service/CorrelationIdHttpClientDecoratorTest.php +++ b/tests/Unit/Service/CorrelationIdHttpClientDecoratorTest.php @@ -5,9 +5,8 @@ namespace Paysera\LoggingExtraBundle\Tests\Unit\Service; use Paysera\LoggingExtraBundle\Service\CorrelationIdHttpClientDecorator; -use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider; +use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider; use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; @@ -22,8 +21,6 @@ protected function setUp(): void $this->httpClient = $this->createMock(HttpClientInterface::class); - $requestStack = $this->createMock(RequestStack::class); - $correlationIdProvider = $this->createMock(CorrelationIdProvider::class); $correlationIdProvider->method('getCorrelationId')->willReturn('mock-correlation-id'); diff --git a/tests/Unit/Service/CorrelationIdProvider/CorrelationIdProviderTest.php b/tests/Unit/Service/CorrelationIdProvider/CorrelationIdProviderTest.php new file mode 100644 index 0000000..6feab88 --- /dev/null +++ b/tests/Unit/Service/CorrelationIdProvider/CorrelationIdProviderTest.php @@ -0,0 +1,183 @@ +defaultGeneratedId = 'generated-correlation-id'; + + $this->generatedProvider = $this->createMock(GeneratedCorrelationIdProvider::class); + $this->generatedProvider + ->method('getCorrelationId') + ->willReturn($this->defaultGeneratedId); + + $this->requestHeaderProvider = $this->createMock(RequestHeaderCorrelationIdProvider::class); + } + + public function testReturnsCorrelationIdFromRequestWhenAvailable(): void + { + // Arrange + $requestCorrelationId = 'request-correlation-id'; + $this->requestHeaderProvider + ->method('getCorrelationId') + ->willReturn($requestCorrelationId); + + $correlationIdProvider = new CorrelationIdProvider( + $this->generatedProvider, + $this->requestHeaderProvider, + true + ); + + // Act + $actualCorrelationId = $correlationIdProvider->getCorrelationId(); + + // Assert + $this->assertEquals($requestCorrelationId, $actualCorrelationId); + } + + public function testFallsBackToGeneratedIdWhenNoCorrelationIdInRequest(): void + { + // Arrange + $this->requestHeaderProvider + ->method('getCorrelationId') + ->willReturn(null); + + $correlationIdProvider = new CorrelationIdProvider( + $this->generatedProvider, + $this->requestHeaderProvider, + true + ); + + // Act + $actualCorrelationId = $correlationIdProvider->getCorrelationId(); + + // Assert + $this->assertEquals($this->defaultGeneratedId, $actualCorrelationId); + } + + public function testAlwaysUsesGeneratedIdWhenFetchFromRequestIsFalse(): void + { + // Arrange + $requestCorrelationId = 'request-correlation-id'; + $this->requestHeaderProvider + ->method('getCorrelationId') + ->willReturn($requestCorrelationId); + + $correlationIdProvider = new CorrelationIdProvider( + $this->generatedProvider, + $this->requestHeaderProvider, + false + ); + + // Act + $actualCorrelationId = $correlationIdProvider->getCorrelationId(); + + // Assert + $this->assertEquals($this->defaultGeneratedId, $actualCorrelationId); + } + + public function testResetClearsRequestProviderWhenIdWasFetchedFromRequest(): void + { + // Arrange + $requestCorrelationId = 'request-correlation-id'; + $this->requestHeaderProvider + ->method('getCorrelationId') + ->willReturn($requestCorrelationId); + + $this->requestHeaderProvider + ->expects($this->once()) + ->method('reset'); + + $this->generatedProvider + ->expects($this->never()) + ->method('increment'); + + $correlationIdProvider = new CorrelationIdProvider( + $this->generatedProvider, + $this->requestHeaderProvider, + true + ); + + // First get the correlation ID to set isFetchedFromRequest = true + $correlationIdProvider->getCorrelationId(); + + // Act + $correlationIdProvider->reset(); + } + + public function testResetIncrementsGeneratedProviderWhenIdWasNotFetchedFromRequest(): void + { + // Arrange + $this->requestHeaderProvider + ->expects($this->never()) + ->method('reset'); + + $this->generatedProvider + ->expects($this->once()) + ->method('increment'); + + $correlationIdProvider = new CorrelationIdProvider( + $this->generatedProvider, + $this->requestHeaderProvider, + false + ); + + // Act + $correlationIdProvider->reset(); + } + + public function testResetIncrementsGeneratedProviderWhenNoCorrelationIdInRequest(): void + { + // Arrange + $this->requestHeaderProvider + ->method('getCorrelationId') + ->willReturn(null); + + $this->requestHeaderProvider + ->expects($this->never()) + ->method('reset'); + + $this->generatedProvider + ->expects($this->once()) + ->method('increment'); + + $correlationIdProvider = new CorrelationIdProvider( + $this->generatedProvider, + $this->requestHeaderProvider, + true + ); + + // First get the correlation ID, but it will use generated since request returns null + $correlationIdProvider->getCorrelationId(); + + // Act + $correlationIdProvider->reset(); + } +} diff --git a/tests/Unit/Service/CorrelationIdProvider/GeneratedCorrelationIdProviderTest.php b/tests/Unit/Service/CorrelationIdProvider/GeneratedCorrelationIdProviderTest.php new file mode 100644 index 0000000..69af0e9 --- /dev/null +++ b/tests/Unit/Service/CorrelationIdProvider/GeneratedCorrelationIdProviderTest.php @@ -0,0 +1,97 @@ +systemName = 'test-system'; + $this->provider = new GeneratedCorrelationIdProvider($this->systemName); + } + + public function testGetCorrelationId(): void + { + // Act + $correlationId = $this->provider->getCorrelationId(); + + // Assert + $this->assertNotEmpty($correlationId); + $this->assertStringStartsWith($this->systemName, $correlationId); + } + + public function testGetCorrelationIdReturnsSameValueBeforeIncrement(): void + { + // Act + $firstCall = $this->provider->getCorrelationId(); + $secondCall = $this->provider->getCorrelationId(); + + // Assert + $this->assertSame($firstCall, $secondCall); + } + + public function testIncrementChangesCorrelationId(): void + { + // Arrange + $initialCorrelationId = $this->provider->getCorrelationId(); + + // Act + $this->provider->increment(); + $incrementedCorrelationId = $this->provider->getCorrelationId(); + + // Assert + $this->assertNotEquals($initialCorrelationId, $incrementedCorrelationId); + $this->assertStringStartsWith($initialCorrelationId . '_', $incrementedCorrelationId); + $this->assertEquals($initialCorrelationId . '_1', $incrementedCorrelationId); + } + + public function testMultipleIncrementsAppendIncrementCounter(): void + { + // Arrange + $initialCorrelationId = $this->provider->getCorrelationId(); + + // Act - increment multiple times + $this->provider->increment(); + $firstIncrementId = $this->provider->getCorrelationId(); + + $this->provider->increment(); + $secondIncrementId = $this->provider->getCorrelationId(); + + $this->provider->increment(); + $thirdIncrementId = $this->provider->getCorrelationId(); + + // Assert + $this->assertEquals($initialCorrelationId . '_1', $firstIncrementId); + $this->assertEquals($initialCorrelationId . '_2', $secondIncrementId); + $this->assertEquals($initialCorrelationId . '_3', $thirdIncrementId); + } + + public function testCorrelationIdFormatFollowsExpectedPattern(): void + { + // Act + $correlationId = $this->provider->getCorrelationId(); + + // Assert + // The format should be: systemName + uniqid (which includes a timestamp and microseconds) + $this->assertRegExp( + sprintf('/^test\-system[0-9a-f]{14}\.[0-9a-f]+$/', preg_quote($this->systemName, '/')), + $correlationId + ); + } +} diff --git a/tests/Unit/Service/CorrelationIdProvider/RequestHeaderCorrelationIdProviderTest.php b/tests/Unit/Service/CorrelationIdProvider/RequestHeaderCorrelationIdProviderTest.php new file mode 100644 index 0000000..74572b9 --- /dev/null +++ b/tests/Unit/Service/CorrelationIdProvider/RequestHeaderCorrelationIdProviderTest.php @@ -0,0 +1,106 @@ +requestStack = new RequestStack(); + $this->provider = new RequestHeaderCorrelationIdProvider($this->requestStack); + } + + public function testGetCorrelationIdFromRequestHeader(): void + { + // Arrange + $correlationId = 'test-correlation-id'; + $request = new Request(); + $request->headers->set(CorrelationIdListener::HEADER_NAME, $correlationId); + $this->requestStack->push($request); + + // Act + $result = $this->provider->getCorrelationId(); + + // Assert + $this->assertEquals($correlationId, $result); + } + + public function testGetCorrelationIdReturnsCachedValue(): void + { + // Arrange + $correlationId = 'test-correlation-id'; + $request = new Request(); + $request->headers->set(CorrelationIdListener::HEADER_NAME, $correlationId); + $this->requestStack->push($request); + + // First call to cache the value + $this->provider->getCorrelationId(); + + // Modify the request header + $request->headers->set(CorrelationIdListener::HEADER_NAME, 'modified-correlation-id'); + + // Act + $result = $this->provider->getCorrelationId(); + + // Assert - should return the cached value, not the modified one + $this->assertEquals($correlationId, $result); + } + + public function testResetClearsCorrelationIdCache(): void + { + // Arrange + $correlationId = 'test-correlation-id'; + $request = new Request(); + $request->headers->set(CorrelationIdListener::HEADER_NAME, $correlationId); + $this->requestStack->push($request); + + // Cache the initial value + $this->provider->getCorrelationId(); + + // Change the header value + $newCorrelationId = 'new-correlation-id'; + $request->headers->set(CorrelationIdListener::HEADER_NAME, $newCorrelationId); + + // Act + $this->provider->reset(); + $result = $this->provider->getCorrelationId(); + + // Assert + $this->assertEquals($newCorrelationId, $result); + } + + public function testGetCorrelationIdReturnsNullWhenNoRequest(): void + { + // No request in the stack + // Act + $result = $this->provider->getCorrelationId(); + + // Assert + $this->assertNull($result); + } + + public function testGetCorrelationIdReturnsNullWhenNoHeader(): void + { + // Arrange - request without the correlation ID header + $request = new Request(); + $this->requestStack->push($request); + + // Act + $result = $this->provider->getCorrelationId(); + + // Assert + $this->assertNull($result); + } +} diff --git a/tests/Unit/Service/CorrelationIdProviderTest.php b/tests/Unit/Service/CorrelationIdProviderTest.php deleted file mode 100644 index 453e448..0000000 --- a/tests/Unit/Service/CorrelationIdProviderTest.php +++ /dev/null @@ -1,84 +0,0 @@ -correlationIdFromHeaderExtractor = $this->createMock(CorrelationIdFromHeaderExtractor::class); - $this->correlationIdProvider = new CorrelationIdProvider( - 'test-system', - $this->correlationIdFromHeaderExtractor, - false - ); - } - - public function testInitialCorrelationId(): void - { - $uniqueId = $this->correlationIdProvider->getCorrelationId(); - $this->assertStringStartsWith( - 'test-system', - $uniqueId, - 'Initial correlation ID did not start with the system name' - ); - } - - public function testGetCorrelationIdWithoutRequest(): void - { - $this->correlationIdFromHeaderExtractor->method('getCorrelationId')->willReturn(null); - - $correlationId = $this->correlationIdProvider->getCorrelationId(); - $this->assertStringStartsWith('test-system', $correlationId); - } - - public function testGetCorrelationIdFromRequestHeader(): void - { - $correlationIdProvider = new CorrelationIdProvider( - 'test-system', - $this->correlationIdFromHeaderExtractor, - true - ); - - $this->correlationIdFromHeaderExtractor->method('getCorrelationId')->willReturn('test-correlation-id'); - - $correlationId = $correlationIdProvider->getCorrelationId(); - $this->assertEquals( - 'test-correlation-id', - $correlationId, - 'Correlation ID did not match the value from the header' - ); - } - - public function testIncrementedCorrelationId(): void - { - $this->correlationIdFromHeaderExtractor->method('getCorrelationId')->willReturn(null); - - $initialCorrelationId = $this->correlationIdProvider->getCorrelationId(); - - $this->correlationIdProvider->incrementIdentifier(); - $incrementedCorrelationId = $this->correlationIdProvider->getCorrelationId(); - - $this->assertNotEquals($initialCorrelationId, $incrementedCorrelationId); - $this->assertStringEndsWith('_1', $incrementedCorrelationId, 'Incremented correlation ID did not end with _1'); - - $this->correlationIdProvider->incrementIdentifier(); - $secondIncrementedCorrelationId = $this->correlationIdProvider->getCorrelationId(); - - $this->assertStringEndsWith( - '_2', - $secondIncrementedCorrelationId, - 'Correlation ID after second increment did not end with _2' - ); - } -}