From ee1be690daa3b44882aa3ee08299a411e8a83cc9 Mon Sep 17 00:00:00 2001 From: Yoan-Alexander Grigorov Date: Mon, 29 Jan 2024 19:36:41 +0100 Subject: [PATCH] :sparkles: Added support for publishing failure messages to SNS --- src/Sns/EmptyPayloadException.php | 12 +++++ src/Sns/FailureCode.php | 12 +++++ src/Sns/PublishJob.php | 10 ++++- src/Sns/Publisher.php | 21 ++++++--- tests/Http/SetupResponseTest.php | 4 ++ tests/Sns/PublishJobTest.php | 8 +++- tests/Sns/PublisherTest.php | 73 +++++++++++++++++++++++++++++-- 7 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 src/Sns/EmptyPayloadException.php create mode 100644 src/Sns/FailureCode.php diff --git a/src/Sns/EmptyPayloadException.php b/src/Sns/EmptyPayloadException.php new file mode 100644 index 0000000..83a9f23 --- /dev/null +++ b/src/Sns/EmptyPayloadException.php @@ -0,0 +1,12 @@ +publish($this->topicArn, $this->myparcelcomPaymentId, $this->paidAt) + ->publish( + $this->topicArn, + $this->myparcelcomPaymentId, + $this->paidAt, + $this->failureCode, + $this->failureMessage, + ) ->wait(); } } diff --git a/src/Sns/Publisher.php b/src/Sns/Publisher.php index fad34df..a2bac28 100644 --- a/src/Sns/Publisher.php +++ b/src/Sns/Publisher.php @@ -18,19 +18,30 @@ public function __construct( ) { } - public function publish(string $topicArn, string $myparcelcomPaymentId, ?DateTimeInterface $paidAt = null): PromiseInterface - { - $payload = [ + public function publish( + string $topicArn, + string $myparcelcomPaymentId, + ?DateTimeInterface $paidAt = null, + ?FailureCode $failureCode = null, + ?string $failureMessage = null, + ): PromiseInterface { + if ($paidAt === null && $failureCode === null) { + throw new EmptyPayloadException(); + } + + $payload = array_filter([ 'myparcelcom_payment_id' => $myparcelcomPaymentId, 'paid_at' => $paidAt?->format(DateTimeInterface::ATOM), - ]; + 'failure_code' => $failureCode?->value, + 'failure_message' => $failureMessage, + ], static fn ($value) => $value !== null); if (Env::get('APP_ENV') === 'local') { return $this->localClient->publish($payload); } return $this->snsClient->publishAsync([ - 'Message' => Utils::jsonEncode(array_filter($payload, static fn ($value) => $value !== null)), + 'Message' => Utils::jsonEncode($payload), 'TopicArn' => $topicArn, ]); } diff --git a/tests/Http/SetupResponseTest.php b/tests/Http/SetupResponseTest.php index f621ef6..bebb1c7 100644 --- a/tests/Http/SetupResponseTest.php +++ b/tests/Http/SetupResponseTest.php @@ -6,6 +6,7 @@ use Faker\Factory; use Illuminate\Http\Request; +use JsonException; use Mockery; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; use MyParcelCom\Payments\Providers\Http\SetupResponse; @@ -28,6 +29,9 @@ public function test_it_returns_no_content_response_when_no_authorization_url_is assertEmpty($response->getContent()); } + /** + * @throws JsonException + */ public function test_it_returns_json_response_when_authorization_url_is_set(): void { $faker = Factory::create(); diff --git a/tests/Sns/PublishJobTest.php b/tests/Sns/PublishJobTest.php index caf44c7..126dbae 100644 --- a/tests/Sns/PublishJobTest.php +++ b/tests/Sns/PublishJobTest.php @@ -35,7 +35,13 @@ public function test_it_handles_publish_job(): void $paidAt ) { $mock->expects('publish') - ->with($topicArn, $myparcelcomPaymentId, $paidAt) + ->with( + $topicArn, + $myparcelcomPaymentId, + $paidAt, + null, + null, + ) ->andReturns($snsPromise); }); diff --git a/tests/Sns/PublisherTest.php b/tests/Sns/PublisherTest.php index 112fcb1..b6b9d99 100644 --- a/tests/Sns/PublisherTest.php +++ b/tests/Sns/PublisherTest.php @@ -12,6 +12,8 @@ use Mockery; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; use Mockery\MockInterface; +use MyParcelCom\Payments\Providers\Sns\EmptyPayloadException; +use MyParcelCom\Payments\Providers\Sns\FailureCode; use MyParcelCom\Payments\Providers\Sns\LocalClient; use MyParcelCom\Payments\Providers\Sns\Publisher; use PHPUnit\Framework\TestCase; @@ -20,7 +22,7 @@ class PublisherTest extends TestCase { use MockeryPHPUnitIntegration; - public function test_it_publishes_a_message_to_sns(): void + public function test_it_publishes_a_success_message_to_sns(): void { $faker = Factory::create(); @@ -49,10 +51,44 @@ public function test_it_publishes_a_message_to_sns(): void $publisher->publish($topicArn, $myparcelcomPaymentId, $paidAt); } - public function test_it_publishes_message_to_sns_without_paid_at(): void + public function test_it_publishes_a_failure_message_to_sns(): void { $faker = Factory::create(); + $topicArn = "arn:aws:sns:eu-west-1:{$faker->randomNumber()}:{$faker->word}"; + $myparcelcomPaymentId = $faker->uuid; + $failureCode = $faker->randomElement(FailureCode::cases()); + $failureMessage = $faker->sentence; + + $snsClient = Mockery::mock(SnsClient::class, function (MockInterface & SnsClient $mock) use ( + $topicArn, + $myparcelcomPaymentId, + $failureCode, + $failureMessage + ) { + $mock + ->expects('publishAsync') + ->with([ + 'Message' => json_encode([ + 'myparcelcom_payment_id' => $myparcelcomPaymentId, + 'failure_code' => $failureCode, + 'failure_message' => $failureMessage, + ], JSON_THROW_ON_ERROR), + 'TopicArn' => $topicArn, + ]) + ->andReturns(Mockery::mock(Promise::class)); + }); + + $publisher = new Publisher($snsClient, Mockery::mock(LocalClient::class)); + $publisher->publish($topicArn, $myparcelcomPaymentId, null, $failureCode, $failureMessage); + } + + public function test_it_does_not_publish_message_to_sns_without_paid_at_or_failures(): void + { + $this->expectException(EmptyPayloadException::class); + + $faker = Factory::create(); + $topicArn = "arn:aws:sns:eu-west-1:{$faker->randomNumber()}:{$faker->word}"; $myparcelcomPaymentId = $faker->uuid; @@ -62,6 +98,7 @@ public function test_it_publishes_message_to_sns_without_paid_at(): void ) { $mock ->expects('publishAsync') + ->never() ->with([ 'Message' => json_encode([ 'myparcelcom_payment_id' => $myparcelcomPaymentId, @@ -75,7 +112,7 @@ public function test_it_publishes_message_to_sns_without_paid_at(): void $publisher->publish($topicArn, $myparcelcomPaymentId); } - public function test_it_publishes_message_to_local_client(): void + public function test_it_publishes_success_message_to_local_client(): void { putenv('APP_ENV=local'); $faker = Factory::create(); @@ -101,4 +138,34 @@ public function test_it_publishes_message_to_local_client(): void putenv('APP_ENV='); } + + public function test_it_publishes_fail_message_to_local_client(): void + { + putenv('APP_ENV=local'); + $faker = Factory::create(); + + $topicArn = "arn:aws:sns:eu-west-1:{$faker->randomNumber()}:{$faker->word}"; + $myparcelcomPaymentId = $faker->uuid; + $failureCode = $faker->randomElement(FailureCode::cases()); + $failureMessage = $faker->sentence; + + $localClient = Mockery::mock(LocalClient::class, function (MockInterface & LocalClient $mock) use ( + $myparcelcomPaymentId, + $failureCode, + $failureMessage + ) { + $mock + ->expects('publish') + ->with([ + 'myparcelcom_payment_id' => $myparcelcomPaymentId, + 'failure_code' => $failureCode->value, + 'failure_message' => $failureMessage, + ]); + }); + + $publisher = new Publisher(Mockery::mock(SnsClient::class), $localClient); + $publisher->publish($topicArn, $myparcelcomPaymentId, null, $failureCode, $failureMessage); + + putenv('APP_ENV='); + } }