From 098dbde1377e9a2d30b89802739c1a055206a4ee Mon Sep 17 00:00:00 2001 From: Tim Ward Date: Thu, 29 Jul 2021 12:02:29 -0400 Subject: [PATCH 1/8] Add checks for Laravel version and exceptions if an acceptable version is not detected. --- .../SnsConfirmationRequestListener.php | 16 +++++++++- tests/SnsHttpTestHelperTrait.php | 32 +++++++++++++++++++ .../SnsConfirmationRequestListenerTest.php | 8 +++-- 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 tests/SnsHttpTestHelperTrait.php diff --git a/src/Listeners/SnsConfirmationRequestListener.php b/src/Listeners/SnsConfirmationRequestListener.php index 9ddc317..1d9c992 100644 --- a/src/Listeners/SnsConfirmationRequestListener.php +++ b/src/Listeners/SnsConfirmationRequestListener.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Log; use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived; use Nipwaayoni\SnsHandler\SnsConfirmSubscriptionException; +use Nipwaayoni\SnsHandler\SnsException; class SnsConfirmationRequestListener { @@ -14,7 +15,7 @@ public function handle(SnsConfirmationRequestReceived $event) { $message = $event->message(); //TODO Make this work with Laravel 6, as the Http facade was introduced in Laravel 7 - $response = Http::get($message->subscribeUrl()); + $response = $this->getResponse($message); if ($response->successful()) { $info = sprintf('Subscription confirmation for %s succeeded with status %s', $message->topicArn(), $response->status()); Log::info($info); @@ -24,4 +25,17 @@ public function handle(SnsConfirmationRequestReceived $event) Log::error($error); throw new SnsConfirmSubscriptionException($error); } + + /** + * @param \Nipwaayoni\SnsHandler\SnsMessage $message + * @return \Illuminate\Http\Client\Response + * @throws \Nipwaayoni\SnsHandler\SnsException + */ + private function getResponse(\Nipwaayoni\SnsHandler\SnsMessage $message): \Illuminate\Http\Client\Response + { + if (class_exists(Http::class)) { + return Http::get($message->subscribeUrl()); + } + throw new SnsException("Unable to determine HTTP method"); + } } diff --git a/tests/SnsHttpTestHelperTrait.php b/tests/SnsHttpTestHelperTrait.php new file mode 100644 index 0000000..c0d44b3 --- /dev/null +++ b/tests/SnsHttpTestHelperTrait.php @@ -0,0 +1,32 @@ + 'https://aws.amazon.com/subscribe/123', ])); - Http::fake([ + $this->httpExpects([ 'https://aws.amazon.com/subscribe/123' => Http::response([], 404, []) ]); @@ -53,7 +55,7 @@ public function testThrowsExceptionIfConfirmSubscriptionFails(): void public function testConfirmsSubscriptionUsingSubscribeUrl(): void { - Http::fake(['https://aws.amazon.com/subscribe/123' => Http::response([], 200, [])]); + $this->httpExpects(['https://aws.amazon.com/subscribe/123' => Http::response([], 200, [])]); $message = Message::fromJsonString($this->makeSnsMessageJson([ 'Type' => SnsMessage::SUBSCRIBE_TYPE, @@ -66,7 +68,7 @@ public function testConfirmsSubscriptionUsingSubscribeUrl(): void $this->listener->handle($event); - Http::assertSent(function (Request $request) { + $this->httpAssertSent(function (Request $request) { return $request->url() === 'https://aws.amazon.com/subscribe/123'; }); } From 325b803d9dd958666038bfa067849f1c60b5d4ac Mon Sep 17 00:00:00 2001 From: Tim Ward Date: Thu, 29 Jul 2021 14:44:44 -0400 Subject: [PATCH 2/8] Style fixes. --- tests/SnsHttpTestHelperTrait.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/SnsHttpTestHelperTrait.php b/tests/SnsHttpTestHelperTrait.php index c0d44b3..5777b39 100644 --- a/tests/SnsHttpTestHelperTrait.php +++ b/tests/SnsHttpTestHelperTrait.php @@ -3,14 +3,12 @@ namespace Nipwaayoni\Tests\SnsHandler; - use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Http; use Nipwaayoni\SnsHandler\SnsException; trait SnsHttpTestHelperTrait { - public function httpExpects(array $content = null): void { if (class_exists(Http::class)) { @@ -20,7 +18,7 @@ public function httpExpects(array $content = null): void throw new SnsException("Unable to determine HTTP method"); } - public function httpAssertSent(Callable $function): void + public function httpAssertSent(callable $function): void { if (class_exists(Http::class)) { Http::assertSent($function); @@ -28,5 +26,4 @@ public function httpAssertSent(Callable $function): void } throw new SnsException("Unable to determine HTTP method"); } - -} \ No newline at end of file +} From 53be1c3f4ed0fde1674108ed32c83dde5f875859 Mon Sep 17 00:00:00 2001 From: Dirk Tepe Date: Tue, 10 Aug 2021 17:52:37 -0400 Subject: [PATCH 3/8] Switch to generic HTTP client Using the Laravel Http facade limits us to Laravel 7+. Since we want to support Laravel 6, we'll use the generic Http client solution for now since it should work in all releases. Add Laravel 6 to GitHub workflow matrix Also update documentation to include developer notes. --- .github/workflows/run-tests.yml | 4 +- .gitignore | 1 + README.md | 13 +++- composer.json | 10 ++- .../SnsConfirmationRequestListener.php | 62 ++++++++++++++----- tests/HttpTransaction.php | 30 +++++++++ tests/HttpTransactionContainer.php | 45 ++++++++++++++ tests/SnsHttpTestHelperTrait.php | 42 ++++++++----- .../SnsConfirmationRequestListenerTest.php | 24 +++---- 9 files changed, 184 insertions(+), 47 deletions(-) create mode 100644 tests/HttpTransaction.php create mode 100644 tests/HttpTransactionContainer.php diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c020e78..1e816fa 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,8 +9,10 @@ jobs: strategy: matrix: php: [7.4, 7.3] - laravel: [8.*] + laravel: [6.*, 8.*] include: + - laravel: 6.* + testbench: 4.* - laravel: 8.* testbench: 6.* diff --git a/.gitignore b/.gitignore index 21a92ba..4d8c462 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .idea .php_cs.cache .phpunit.result.cache +composer.lock diff --git a/README.md b/README.md index 494f28e..56f42b7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Introduction +# Laravel SNS Handler This package provides an easy way of adding AWS SNS message handling to your Laravel application as a REST endpoint. The package can automatically confirm subscription requests and dispatches events when a message is received. ## How to create an SNS topic @@ -75,3 +75,14 @@ This package adds a route to your application for incoming SNS requests. Note th **Note: You will only be able to subscribe your endpoint if it can be reached from the AWS SNS service** +## Development +This package is expected to work with supported versions of Laravel, including LTS releases. During development, you should be sure to run tests and validate expected behaviors under different releases. Since we use the `orchestra/testbench` package, you can easily switch between installed Laravel framework releases using `composer`: + +```bash +# Laravel 6 +composer require --dev orchestra/testbench:^4.0 -W +# Laravel 8 +composer require --dev orchestra/testbench:^6.0 -W +``` + +New releases of Laravel should be added to the GitHub workflow matrix in `.github/workflows/run-tests.yml`. diff --git a/composer.json b/composer.json index 02aebac..1351584 100644 --- a/composer.json +++ b/composer.json @@ -16,12 +16,16 @@ "require": { "php": ">=7.3", "ext-json": "*", - "aws/aws-php-sns-message-validator": "^1.6" + "aws/aws-php-sns-message-validator": "^1.6", + "php-http/discovery": "^1.14", + "psr/http-factory": "^1.0", + "psr/http-client": "^1.0" }, "require-dev" : { "roave/security-advisories": "dev-latest", - "guzzlehttp/guzzle": "^7.2", - "orchestra/testbench": "^6.0" + "orchestra/testbench": "^6.0", + "http-interop/http-factory-guzzle": "^1.2", + "php-http/guzzle6-adapter": "^2.0" }, "autoload": { "psr-4": { diff --git a/src/Listeners/SnsConfirmationRequestListener.php b/src/Listeners/SnsConfirmationRequestListener.php index 1d9c992..f717af5 100644 --- a/src/Listeners/SnsConfirmationRequestListener.php +++ b/src/Listeners/SnsConfirmationRequestListener.php @@ -3,39 +3,67 @@ namespace Nipwaayoni\SnsHandler\Listeners; -use Illuminate\Support\Facades\Http; +use Http\Discovery\HttpClientDiscovery; +use Http\Discovery\Psr17FactoryDiscovery; use Illuminate\Support\Facades\Log; use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived; use Nipwaayoni\SnsHandler\SnsConfirmSubscriptionException; use Nipwaayoni\SnsHandler\SnsException; +use Nipwaayoni\SnsHandler\SnsMessage; +use Psr\Http\Client\ClientExceptionInterface; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; +use Psr\Http\Message\ResponseInterface; class SnsConfirmationRequestListener { + /** + * @var ClientInterface + */ + private $client; + + /** + * @var RequestFactoryInterface + */ + private $requestFactory; + + /** + * @param ClientInterface|null $client + * @param RequestFactoryInterface|null $requestFactory + */ + public function __construct( + ClientInterface $client = null, + RequestFactoryInterface $requestFactory = null + ) { + $this->client = $client ?? HttpClientDiscovery::find(); + $this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory(); + } + public function handle(SnsConfirmationRequestReceived $event) { $message = $event->message(); - //TODO Make this work with Laravel 6, as the Http facade was introduced in Laravel 7 + $response = $this->getResponse($message); - if ($response->successful()) { - $info = sprintf('Subscription confirmation for %s succeeded with status %s', $message->topicArn(), $response->status()); - Log::info($info); - return; - } - $error = sprintf('Subscription confirmation for %s failed with status %s', $message->topicArn(), $response->status()); - Log::error($error); - throw new SnsConfirmSubscriptionException($error); + + Log::info(sprintf('Subscription confirmation for %s succeeded with status %s', $message->topicArn(), $response->getStatusCode())); } /** - * @param \Nipwaayoni\SnsHandler\SnsMessage $message - * @return \Illuminate\Http\Client\Response - * @throws \Nipwaayoni\SnsHandler\SnsException + * @param SnsMessage $message + * @return ResponseInterface + * @throws SnsConfirmSubscriptionException + * @throws SnsException */ - private function getResponse(\Nipwaayoni\SnsHandler\SnsMessage $message): \Illuminate\Http\Client\Response + private function getResponse(SnsMessage $message): ResponseInterface { - if (class_exists(Http::class)) { - return Http::get($message->subscribeUrl()); + try { + return $this->client->sendRequest( + $this->requestFactory->createRequest('GET', $message->subscribeUrl()) + ); + } catch (ClientExceptionInterface $e) { + throw new SnsConfirmSubscriptionException( + sprintf('Subscription confirmation for %s failed with status %s', $message->topicArn(), $e->getCode()) + ); } - throw new SnsException("Unable to determine HTTP method"); } } diff --git a/tests/HttpTransaction.php b/tests/HttpTransaction.php new file mode 100644 index 0000000..daf998c --- /dev/null +++ b/tests/HttpTransaction.php @@ -0,0 +1,30 @@ +transaction = $transaction; + } + + public function request(): RequestInterface + { + return $this->transaction['request']; + } + + public function response(): ResponseInterface + { + return $this->transaction['response']; + } +} diff --git a/tests/HttpTransactionContainer.php b/tests/HttpTransactionContainer.php new file mode 100644 index 0000000..b743b39 --- /dev/null +++ b/tests/HttpTransactionContainer.php @@ -0,0 +1,45 @@ +container[$offset]); + } + + public function offsetGet($offset): HttpTransaction + { + if (!isset($this->container[$offset])) { + throw new \Exception('Undefined transaction offset'); + } + + return new HttpTransaction($this->container[$offset]); + } + + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + public function count(): int + { + return count($this->container); + } +} diff --git a/tests/SnsHttpTestHelperTrait.php b/tests/SnsHttpTestHelperTrait.php index 5777b39..24ba7b8 100644 --- a/tests/SnsHttpTestHelperTrait.php +++ b/tests/SnsHttpTestHelperTrait.php @@ -3,27 +3,41 @@ namespace Nipwaayoni\Tests\SnsHandler; -use Illuminate\Http\Client\Request; -use Illuminate\Support\Facades\Http; -use Nipwaayoni\SnsHandler\SnsException; +use GuzzleHttp\Handler\MockHandler; +use GuzzleHttp\HandlerStack; +use GuzzleHttp\Middleware; +use GuzzleHttp\Psr7\Response; +use Http\Client\HttpClient; trait SnsHttpTestHelperTrait { - public function httpExpects(array $content = null): void + /** @var HttpClient */ + private $client; + + /** @var HttpTransactionContainer */ + private $container; + + public function httpExpects(Response ...$responses): void { - if (class_exists(Http::class)) { - Http::fake($content); - return; - } - throw new SnsException("Unable to determine HTTP method"); + $this->container = new HttpTransactionContainer(); + + $history = Middleware::history($this->container); + + $mock = new MockHandler($responses); + + $handlerStack = HandlerStack::create($mock); + $handlerStack->push($history); + + $client = new \GuzzleHttp\Client(['handler' => $handlerStack]); + $this->client = new \Http\Adapter\Guzzle6\Client($client); } public function httpAssertSent(callable $function): void { - if (class_exists(Http::class)) { - Http::assertSent($function); - return; - } - throw new SnsException("Unable to determine HTTP method"); + $this->assertCount(1, $this->container); + + $request = $this->container[0]->request(); + + $function($request); } } diff --git a/tests/Unit/SnsConfirmationRequestListenerTest.php b/tests/Unit/SnsConfirmationRequestListenerTest.php index 77874c6..2e0bde2 100644 --- a/tests/Unit/SnsConfirmationRequestListenerTest.php +++ b/tests/Unit/SnsConfirmationRequestListenerTest.php @@ -4,9 +4,8 @@ namespace Nipwaayoni\Tests\SnsHandler\Unit; use Aws\Sns\Message; -use Illuminate\Http\Client\Request; +use GuzzleHttp\Psr7\Response; use Illuminate\Support\Facades\Event; -use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived; use Nipwaayoni\SnsHandler\Listeners\SnsConfirmationRequestListener; @@ -29,8 +28,6 @@ public function setUp(): void parent::setUp(); Event::fake(); - - $this->listener = new SnsConfirmationRequestListener(); } public function testThrowsExceptionIfConfirmSubscriptionFails(): void @@ -40,36 +37,41 @@ public function testThrowsExceptionIfConfirmSubscriptionFails(): void 'SubscribeURL' => 'https://aws.amazon.com/subscribe/123', ])); - $this->httpExpects([ - 'https://aws.amazon.com/subscribe/123' => Http::response([], 404, []) - ]); + $this->httpExpects( + new Response(404) + ); + + $this->listener = new SnsConfirmationRequestListener($this->client); $event = new SnsConfirmationRequestReceived(new SnsMessage($message)); $this->expectException(SnsConfirmSubscriptionException::class); $this->expectExceptionMessage('Subscription confirmation for arn:aws:sns:us-west-2:123456789012:MyTopic failed with status 404'); - Log::shouldReceive('error')->once(); $this->listener->handle($event); } public function testConfirmsSubscriptionUsingSubscribeUrl(): void { - $this->httpExpects(['https://aws.amazon.com/subscribe/123' => Http::response([], 200, [])]); + $this->httpExpects( + new Response(200) + ); $message = Message::fromJsonString($this->makeSnsMessageJson([ 'Type' => SnsMessage::SUBSCRIBE_TYPE, 'SubscribeURL' => 'https://aws.amazon.com/subscribe/123', ])); + $this->listener = new SnsConfirmationRequestListener($this->client); + $event = new SnsConfirmationRequestReceived(new SnsMessage($message)); Log::shouldReceive('info')->once(); $this->listener->handle($event); - $this->httpAssertSent(function (Request $request) { - return $request->url() === 'https://aws.amazon.com/subscribe/123'; + $this->httpAssertSent(function (\Psr\Http\Message\RequestInterface $request) { + $this->assertEquals('https://aws.amazon.com/subscribe/123', $request->getUri()); }); } } From 47de3ec55e89e5d3bc44a47ffab3a493da1227a6 Mon Sep 17 00:00:00 2001 From: Dirk Tepe Date: Sun, 15 Aug 2021 10:18:14 -0400 Subject: [PATCH 4/8] Add tests to cover basic Topic to Event mapping --- src/SnsBroker.php | 8 +++++- tests/Events/SnsMessageAlphaReceived.php | 25 +++++++++++++++++++ tests/Events/SnsMessageBetaReceived.php | 25 +++++++++++++++++++ tests/Unit/SnsBrokerTest.php | 31 ++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 tests/Events/SnsMessageAlphaReceived.php create mode 100644 tests/Events/SnsMessageBetaReceived.php diff --git a/src/SnsBroker.php b/src/SnsBroker.php index 279ee65..70e353f 100644 --- a/src/SnsBroker.php +++ b/src/SnsBroker.php @@ -8,6 +8,8 @@ use Illuminate\Support\Facades\Log; use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived; use Nipwaayoni\SnsHandler\Events\SnsMessageReceived; +use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageAlphaReceived; +use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageBetaReceived; class SnsBroker { @@ -74,7 +76,11 @@ private function getSubscriptionEvent(string $arn) private function getNotificationEvent(string $arn) { - $map = [SnsMessageReceived::class => ['*']]; + $map = [ + SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsMessageBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsMessageReceived::class => ['*'], + ]; return $this->arnMap($arn, $map); } diff --git a/tests/Events/SnsMessageAlphaReceived.php b/tests/Events/SnsMessageAlphaReceived.php new file mode 100644 index 0000000..143c739 --- /dev/null +++ b/tests/Events/SnsMessageAlphaReceived.php @@ -0,0 +1,25 @@ +message = $message; + } + + public function message(): SnsMessage + { + return $this->message; + } +} diff --git a/tests/Events/SnsMessageBetaReceived.php b/tests/Events/SnsMessageBetaReceived.php new file mode 100644 index 0000000..d7927c6 --- /dev/null +++ b/tests/Events/SnsMessageBetaReceived.php @@ -0,0 +1,25 @@ +message = $message; + } + + public function message(): SnsMessage + { + return $this->message; + } +} diff --git a/tests/Unit/SnsBrokerTest.php b/tests/Unit/SnsBrokerTest.php index a84a94f..ab8ef9d 100644 --- a/tests/Unit/SnsBrokerTest.php +++ b/tests/Unit/SnsBrokerTest.php @@ -11,6 +11,8 @@ use Nipwaayoni\SnsHandler\SnsHttpRequest; use Nipwaayoni\SnsHandler\SnsMessage; use Nipwaayoni\SnsHandler\SnsUnknownTopicArnException; +use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageAlphaReceived; +use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageBetaReceived; use PHPUnit\Framework\MockObject\MockObject; use Nipwaayoni\Tests\SnsHandler\MakesSnsTests; use Nipwaayoni\Tests\SnsHandler\TestCase; @@ -112,4 +114,33 @@ public function testValidatesSnsMessage(): void $this->broker->handleRequest($request); } + + public function testDispatchMappedNotificationMessage(): void + { + $request = $this->createMock(SnsHttpRequest::class); + $request->expects($this->once())->method('jsonContent') + ->willReturn($this->makeSnsMessageJson([ + 'MessageId' => 'abc123', + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic' + ])); + + $this->broker->handleRequest($request); + Event::assertDispatched(SnsMessageAlphaReceived::class); + Event::assertNotDispatched(SnsMessageReceived::class); + } + + public function testDispatchFirstMappedNotificationMessage(): void + { + $request = $this->createMock(SnsHttpRequest::class); + $request->expects($this->once())->method('jsonContent') + ->willReturn($this->makeSnsMessageJson([ + 'MessageId' => 'abc123', + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic' + ])); + + $this->broker->handleRequest($request); + Event::assertDispatched(SnsMessageAlphaReceived::class); + Event::assertNotDispatched(SnsMessageBetaReceived::class); + Event::assertNotDispatched(SnsMessageReceived::class); + } } From 9fac4f929214b45092219bc50797131923024dd0 Mon Sep 17 00:00:00 2001 From: Dirk Tepe Date: Sun, 15 Aug 2021 21:09:40 -0400 Subject: [PATCH 5/8] Move ARN mapping to configuration --- config/sns-handler.php | 4 ++- src/SnsBroker.php | 42 ++++++++++++++++++------ src/SnsUnknownTopicArnException.php | 6 ---- tests/TestCase.php | 25 +++++++++++++++ tests/Unit/SnsBrokerTest.php | 50 ++++++++++++++++++++--------- 5 files changed, 95 insertions(+), 32 deletions(-) diff --git a/config/sns-handler.php b/config/sns-handler.php index 8545b00..d47e6a5 100644 --- a/config/sns-handler.php +++ b/config/sns-handler.php @@ -2,5 +2,7 @@ return [ 'validate-sns-messages' => env('VALIDATE_SNS_MESSAGES', true), - 'sns-class-map' => [] + 'message-events' => [ + Nipwaayoni\SnsHandler\Events\SnsMessageReceived::class => ['*'] + ], ]; \ No newline at end of file diff --git a/src/SnsBroker.php b/src/SnsBroker.php index 70e353f..f6ba087 100644 --- a/src/SnsBroker.php +++ b/src/SnsBroker.php @@ -5,6 +5,7 @@ use Aws\Sns\Message; use Aws\Sns\MessageValidator; +use Illuminate\Config\Repository as Config; use Illuminate\Support\Facades\Log; use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived; use Nipwaayoni\SnsHandler\Events\SnsMessageReceived; @@ -21,10 +22,15 @@ class SnsBroker * @var Log */ private $log; + /** + * @var Config + */ + private $config; - public function __construct(MessageValidator $validator) + public function __construct(MessageValidator $validator, Config $config) { $this->validator = $validator; + $this->config = $config; } /** @@ -68,23 +74,35 @@ public function handleRequest(SnsHttpRequest $request): void throw new SnsException(sprintf('Unknown message type: %s', $message->type())); } - private function getSubscriptionEvent(string $arn) + /** + * @param string $arn + * @return string + * @throws SnsUnknownTopicArnException + */ + private function getSubscriptionEvent(string $arn): string { $map = [SnsConfirmationRequestReceived::class => ['*']]; return $this->arnMap($arn, $map); } - private function getNotificationEvent(string $arn) + /** + * @param string $arn + * @return string + * @throws SnsUnknownTopicArnException + */ + private function getNotificationEvent(string $arn): string { - $map = [ - SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], - SnsMessageBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], - SnsMessageReceived::class => ['*'], - ]; + $map = $this->config->get('sns-handler.message-events', []); return $this->arnMap($arn, $map); } - private function arnMap(string $arn, array $map) + /** + * @param string $arn + * @param array $map + * @return string + * @throws SnsUnknownTopicArnException + */ + private function arnMap(string $arn, array $map): string { $default = null; foreach ($map as $className => $arnList) { @@ -96,6 +114,12 @@ private function arnMap(string $arn, array $map) } } + if (null === $default) { + throw new SnsUnknownTopicArnException(sprintf('Unmappable TopicArn: %s', $arn)); + } + + // TODO ensure class is dispatchable + return $default; } } diff --git a/src/SnsUnknownTopicArnException.php b/src/SnsUnknownTopicArnException.php index 4539c39..84f1780 100644 --- a/src/SnsUnknownTopicArnException.php +++ b/src/SnsUnknownTopicArnException.php @@ -3,12 +3,6 @@ namespace Nipwaayoni\SnsHandler; -use Throwable; - class SnsUnknownTopicArnException extends SnsException { - public function __construct(string $topicArn = '', $code = 0, Throwable $previous = null) - { - parent::__construct(sprintf('No handler registered for TopicArn %s', $topicArn), $code, $previous); - } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 937e99a..8f936a4 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,6 +3,31 @@ namespace Nipwaayoni\Tests\SnsHandler; +use Illuminate\Config\Repository; +use Nipwaayoni\SnsHandler\Events\SnsMessageReceived; +use PHPUnit\Framework\MockObject\MockObject; + class TestCase extends \Orchestra\Testbench\TestCase { + /** @var Repository|mixed|MockObject */ + protected $config; + + protected $configValues = [ + 'validate-sns-messages' => true, + 'message-events' => [ + SnsMessageReceived::class => ['*'] + ], + ]; + + public function setUp(): void + { + parent::setUp(); + + $this->config = $this->createMock(Repository::class); + $this->config->method('get') + ->willReturnCallback(function (string $key) { + $parts = explode('.', $key); + return $this->configValues[$parts[1]] ?? null; + }); + } } diff --git a/tests/Unit/SnsBrokerTest.php b/tests/Unit/SnsBrokerTest.php index ab8ef9d..4f47d9c 100644 --- a/tests/Unit/SnsBrokerTest.php +++ b/tests/Unit/SnsBrokerTest.php @@ -27,17 +27,15 @@ class SnsBrokerTest extends TestCase /** @var MessageValidator|MockObject */ private $validator; - public function setUp(): void { parent::setUp(); Event::fake(); - $this->validator = $this->createMock(MessageValidator::class); - $this->broker = new SnsBroker($this->validator); + $this->broker = new SnsBroker($this->validator, $this->config); } public function testMakesSnsMessageFromHttpRequest(): void @@ -51,19 +49,6 @@ public function testMakesSnsMessageFromHttpRequest(): void $this->assertEquals(SnsMessage::NOTIFICATION_TYPE, $message->type()); } - public function testRejectsMessageWithUnknownTopicArn(): void - { - $this->markTestSkipped("Doesn't work with event faking and we think this test will go away after refactoring."); - $request = $this->createMock(SnsHttpRequest::class); - $request->expects($this->once())->method('jsonContent') - ->willReturn($this->makeSnsMessageJson(['TopicArn' => 'arn:aws:sns:us-west-2:123456789012:Unknown'])); - - $this->expectException(SnsUnknownTopicArnException::class); - $this->expectExceptionMessage('No handler registered for TopicArn arn:aws:sns:us-west-2:123456789012:Unknown'); - - $this->broker->handleRequest($request); - } - public function testThrowsExceptionForUnknownMessageType(): void { $request = $this->createMock(SnsHttpRequest::class); @@ -117,6 +102,12 @@ public function testValidatesSnsMessage(): void public function testDispatchMappedNotificationMessage(): void { + $this->configValues['message-events'] = [ + SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsMessageBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsMessageReceived::class => ['*'], + ]; + $request = $this->createMock(SnsHttpRequest::class); $request->expects($this->once())->method('jsonContent') ->willReturn($this->makeSnsMessageJson([ @@ -131,6 +122,12 @@ public function testDispatchMappedNotificationMessage(): void public function testDispatchFirstMappedNotificationMessage(): void { + $this->configValues['message-events'] = [ + SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsMessageBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsMessageReceived::class => ['*'], + ]; + $request = $this->createMock(SnsHttpRequest::class); $request->expects($this->once())->method('jsonContent') ->willReturn($this->makeSnsMessageJson([ @@ -143,4 +140,25 @@ public function testDispatchFirstMappedNotificationMessage(): void Event::assertNotDispatched(SnsMessageBetaReceived::class); Event::assertNotDispatched(SnsMessageReceived::class); } + + public function testRejectsMessageWithUnhandledTopicArn(): void + { + $this->configValues['message-events'] = [ + SnsMessageBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:BetaTopic'], + ]; + + $request = $this->createMock(SnsHttpRequest::class); + $request->expects($this->once())->method('jsonContent') + ->willReturn($this->makeSnsMessageJson([ + 'MessageId' => 'abc123', + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic' + ])); + + $this->expectException(SnsUnknownTopicArnException::class); + $this->expectExceptionMessage('Unmappable TopicArn: arn:aws:sns:us-west-2:123456789012:AlphaTopic'); + + $this->broker->handleRequest($request); + + Event::assertNotDispatched(SnsMessageReceived::class); + } } From 46fae44f47d502019c7665fb5655e38d9dd7aa74 Mon Sep 17 00:00:00 2001 From: Dirk Tepe Date: Mon, 16 Aug 2021 20:58:24 -0400 Subject: [PATCH 6/8] Add mapping of confirmation request messages --- config/sns-handler.php | 3 + src/SnsBroker.php | 2 +- .../SnsConfirmationRequestAlphaReceived.php | 25 ++++++ .../SnsConfirmationRequestBetaReceived.php | 25 ++++++ tests/TestCase.php | 4 + tests/Unit/SnsBrokerTest.php | 87 ++++++++++++++++--- 6 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 tests/Events/SnsConfirmationRequestAlphaReceived.php create mode 100644 tests/Events/SnsConfirmationRequestBetaReceived.php diff --git a/config/sns-handler.php b/config/sns-handler.php index d47e6a5..ca7cbb6 100644 --- a/config/sns-handler.php +++ b/config/sns-handler.php @@ -2,6 +2,9 @@ return [ 'validate-sns-messages' => env('VALIDATE_SNS_MESSAGES', true), + 'confirmation-events' => [ + Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived::class => ['*'] + ], 'message-events' => [ Nipwaayoni\SnsHandler\Events\SnsMessageReceived::class => ['*'] ], diff --git a/src/SnsBroker.php b/src/SnsBroker.php index f6ba087..f81ea8c 100644 --- a/src/SnsBroker.php +++ b/src/SnsBroker.php @@ -81,7 +81,7 @@ public function handleRequest(SnsHttpRequest $request): void */ private function getSubscriptionEvent(string $arn): string { - $map = [SnsConfirmationRequestReceived::class => ['*']]; + $map = $this->config->get('sns-handler.confirmation-events', []); return $this->arnMap($arn, $map); } diff --git a/tests/Events/SnsConfirmationRequestAlphaReceived.php b/tests/Events/SnsConfirmationRequestAlphaReceived.php new file mode 100644 index 0000000..9c2fbff --- /dev/null +++ b/tests/Events/SnsConfirmationRequestAlphaReceived.php @@ -0,0 +1,25 @@ +message = $message; + } + + public function message(): SnsMessage + { + return $this->message; + } +} diff --git a/tests/Events/SnsConfirmationRequestBetaReceived.php b/tests/Events/SnsConfirmationRequestBetaReceived.php new file mode 100644 index 0000000..a8bb0cc --- /dev/null +++ b/tests/Events/SnsConfirmationRequestBetaReceived.php @@ -0,0 +1,25 @@ +message = $message; + } + + public function message(): SnsMessage + { + return $this->message; + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 8f936a4..33f839f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,6 +4,7 @@ namespace Nipwaayoni\Tests\SnsHandler; use Illuminate\Config\Repository; +use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived; use Nipwaayoni\SnsHandler\Events\SnsMessageReceived; use PHPUnit\Framework\MockObject\MockObject; @@ -14,6 +15,9 @@ class TestCase extends \Orchestra\Testbench\TestCase protected $configValues = [ 'validate-sns-messages' => true, + 'confirmation-events' => [ + SnsConfirmationRequestReceived::class => ['*'] + ], 'message-events' => [ SnsMessageReceived::class => ['*'] ], diff --git a/tests/Unit/SnsBrokerTest.php b/tests/Unit/SnsBrokerTest.php index 4f47d9c..83b82a6 100644 --- a/tests/Unit/SnsBrokerTest.php +++ b/tests/Unit/SnsBrokerTest.php @@ -11,6 +11,8 @@ use Nipwaayoni\SnsHandler\SnsHttpRequest; use Nipwaayoni\SnsHandler\SnsMessage; use Nipwaayoni\SnsHandler\SnsUnknownTopicArnException; +use Nipwaayoni\Tests\SnsHandler\Events\SnsConfirmationRequestAlphaReceived; +use Nipwaayoni\Tests\SnsHandler\Events\SnsConfirmationRequestBetaReceived; use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageAlphaReceived; use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageBetaReceived; use PHPUnit\Framework\MockObject\MockObject; @@ -62,7 +64,20 @@ public function testThrowsExceptionForUnknownMessageType(): void Event::assertNotDispatched(SnsMessageReceived::class); } - public function testDispatchesSnsConfirmationRequestEvent(): void + public function testValidatesSnsMessage(): void + { + $request = $this->createMock(SnsHttpRequest::class); + $request->expects($this->once())->method('jsonContent') + ->willReturn($this->makeSnsMessageJson([ + 'MessageId' => 'abc123', + ])); + + $this->validator->expects($this->once())->method('validate'); + + $this->broker->handleRequest($request); + } + + public function testDispatchesDefaultConfirmationRequestEvent(): void { $request = $this->createMock(SnsHttpRequest::class); $request->expects($this->once())->method('jsonContent') @@ -75,32 +90,84 @@ public function testDispatchesSnsConfirmationRequestEvent(): void Event::assertDispatched(SnsConfirmationRequestReceived::class); } - public function testDispatchesDefaultNotificationMessage(): void + public function testDispatchesMappedConfirmationRequestEvent(): void { + $this->configValues['confirmation-events'] = [ + SnsConfirmationRequestAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsConfirmationRequestBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsConfirmationRequestReceived::class => ['*'], + ]; + $request = $this->createMock(SnsHttpRequest::class); $request->expects($this->once())->method('jsonContent') ->willReturn($this->makeSnsMessageJson([ - 'MessageId' => 'abc123', + 'Type' => SnsMessage::SUBSCRIBE_TYPE, + 'SubscribeURL' => 'https://aws.amazon.com/subscribe/123', + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic' ])); $this->broker->handleRequest($request); - Event::assertDispatched(SnsMessageReceived::class); + Event::assertDispatched(SnsConfirmationRequestAlphaReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestReceived::class); } - public function testValidatesSnsMessage(): void + public function testDispatchFirstMappedConfirmationEvent(): void { + $this->configValues['confirmation-events'] = [ + SnsConfirmationRequestAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsConfirmationRequestBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsConfirmationRequestReceived::class => ['*'], + ]; + $request = $this->createMock(SnsHttpRequest::class); $request->expects($this->once())->method('jsonContent') ->willReturn($this->makeSnsMessageJson([ - 'MessageId' => 'abc123', + 'Type' => SnsMessage::SUBSCRIBE_TYPE, + 'SubscribeURL' => 'https://aws.amazon.com/subscribe/123', + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic' ])); - $this->validator->expects($this->once())->method('validate'); + $this->broker->handleRequest($request); + Event::assertDispatched(SnsConfirmationRequestAlphaReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestBetaReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestReceived::class); + } + + public function testRejectsWithUnhandledTopicArnOnConfirmation(): void + { + $this->configValues['confirmation-events'] = [ + SnsConfirmationRequestBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:BetaTopic'], + ]; + + $request = $this->createMock(SnsHttpRequest::class); + $request->expects($this->once())->method('jsonContent') + ->willReturn($this->makeSnsMessageJson([ + 'Type' => SnsMessage::SUBSCRIBE_TYPE, + 'SubscribeURL' => 'https://aws.amazon.com/subscribe/123', + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic' + ])); + + $this->expectException(SnsUnknownTopicArnException::class); + $this->expectExceptionMessage('Unmappable TopicArn: arn:aws:sns:us-west-2:123456789012:AlphaTopic'); + + $this->broker->handleRequest($request); + + Event::assertNotDispatched(SnsConfirmationRequestReceived::class); + } + + public function testDispatchesDefaultNotificationEvent(): void + { + $request = $this->createMock(SnsHttpRequest::class); + $request->expects($this->once())->method('jsonContent') + ->willReturn($this->makeSnsMessageJson([ + 'MessageId' => 'abc123', + ])); $this->broker->handleRequest($request); + Event::assertDispatched(SnsMessageReceived::class); } - public function testDispatchMappedNotificationMessage(): void + public function testDispatchMappedNotificationEvent(): void { $this->configValues['message-events'] = [ SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], @@ -120,7 +187,7 @@ public function testDispatchMappedNotificationMessage(): void Event::assertNotDispatched(SnsMessageReceived::class); } - public function testDispatchFirstMappedNotificationMessage(): void + public function testDispatchFirstMappedNotificationEvent(): void { $this->configValues['message-events'] = [ SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], @@ -141,7 +208,7 @@ public function testDispatchFirstMappedNotificationMessage(): void Event::assertNotDispatched(SnsMessageReceived::class); } - public function testRejectsMessageWithUnhandledTopicArn(): void + public function testRejectsWithUnhandledTopicArnOnMessage(): void { $this->configValues['message-events'] = [ SnsMessageBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:BetaTopic'], From 72f40538bf091ebfa0e4ac87c25167367ab3280c Mon Sep 17 00:00:00 2001 From: Dirk Tepe Date: Sat, 21 Aug 2021 14:18:32 -0400 Subject: [PATCH 7/8] Add feature tests for mapped events --- src/SnsBroker.php | 4 -- tests/Feature/SnsHandlerTest.php | 73 +++++++++++++++++++++++--------- tests/Unit/SnsBrokerTest.php | 7 +++ 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/src/SnsBroker.php b/src/SnsBroker.php index f81ea8c..55f8f74 100644 --- a/src/SnsBroker.php +++ b/src/SnsBroker.php @@ -7,10 +7,6 @@ use Aws\Sns\MessageValidator; use Illuminate\Config\Repository as Config; use Illuminate\Support\Facades\Log; -use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived; -use Nipwaayoni\SnsHandler\Events\SnsMessageReceived; -use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageAlphaReceived; -use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageBetaReceived; class SnsBroker { diff --git a/tests/Feature/SnsHandlerTest.php b/tests/Feature/SnsHandlerTest.php index a24e43b..ce3e9b6 100644 --- a/tests/Feature/SnsHandlerTest.php +++ b/tests/Feature/SnsHandlerTest.php @@ -4,15 +4,16 @@ namespace Nipwaayoni\Tests\SnsHandler\Feature; use Aws\Sns\MessageValidator; -use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Event; -use Illuminate\Support\Facades\Http; use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived; use Nipwaayoni\SnsHandler\Events\SnsMessageReceived; -use Nipwaayoni\SnsHandler\Listeners\SnsConfirmationRequestListener; use Nipwaayoni\SnsHandler\NullMessageValidator; use Nipwaayoni\SnsHandler\ServiceProvider; use Nipwaayoni\SnsHandler\SnsMessage; +use Nipwaayoni\Tests\SnsHandler\Events\SnsConfirmationRequestAlphaReceived; +use Nipwaayoni\Tests\SnsHandler\Events\SnsConfirmationRequestBetaReceived; +use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageAlphaReceived; +use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageBetaReceived; use Nipwaayoni\Tests\SnsHandler\MakesSnsTests; class SnsHandlerTest extends \Nipwaayoni\Tests\SnsHandler\TestCase @@ -22,14 +23,10 @@ class SnsHandlerTest extends \Nipwaayoni\Tests\SnsHandler\TestCase public function setUp(): void { parent::setUp(); - $this->app->bind(MessageValidator::class, NullMessageValidator::class); - } - protected function getEnvironmentSetUp($app) - { -// $app['config']->set('sns-handler.sns-class-map', [ -// 'arn:aws:sns:us-west-2:123456789012:MyTopic' => SnsMessageHandlerStub::class, -// ]); + Event::fake(); + + $this->app->bind(MessageValidator::class, NullMessageValidator::class); } protected function getPackageProviders($app) @@ -37,22 +34,21 @@ protected function getPackageProviders($app) return [ServiceProvider::class]; } - public function testReturnsNotFoundForUnknownTopicArn(): void + protected function getEnvironmentSetUp($app) { - $this->markTestSkipped("Skipping until we implement enhanced mapping functionality for events."); - $data = $this->makeSnsMessageData([ - 'Type' => SnsMessage::NOTIFICATION_TYPE, - 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:Unknown', + $app['config']->set('sns-handler.confirmation-events', [ + SnsConfirmationRequestAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsConfirmationRequestReceived::class => ['*'], ]); - $response = $this->postJson('/api/sns/message', $data); - $this->assertEquals(404, $response->status()); + $app['config']->set('sns-handler.message-events', [ + SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], + SnsMessageReceived::class => ['*'], + ]); } public function testDispatchesDefaultConfirmationEvent(): void { - Event::fake(); - $data = $this->makeSnsMessageData([ 'Type' => SnsMessage::SUBSCRIBE_TYPE, 'Message' => json_encode(['id' => 123, 'color' => 'red'], true), @@ -60,13 +56,31 @@ public function testDispatchesDefaultConfirmationEvent(): void ]); $response = $this->postJson('/api/sns/message', $data); + + $this->assertEquals(200, $response->status()); Event::assertDispatched(SnsConfirmationRequestReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestAlphaReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestBetaReceived::class); + } + + public function testDispatchesMappedConfirmationEvent(): void + { + $data = $this->makeSnsMessageData([ + 'Type' => SnsMessage::SUBSCRIBE_TYPE, + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic', + 'SubscribeURL' => 'https://aws.amazon.com/sns/register/abc123' + ]); + + $response = $this->postJson('/api/sns/message', $data); + $this->assertEquals(200, $response->status()); + Event::assertDispatched(SnsConfirmationRequestAlphaReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestBetaReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestReceived::class); } public function testDispatchesDefaultMessageEvent(): void { - Event::fake(); $data = $this->makeSnsMessageData([ 'Type' => SnsMessage::NOTIFICATION_TYPE, 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:MyTopic', @@ -76,7 +90,24 @@ public function testDispatchesDefaultMessageEvent(): void $response = $this->postJson('/api/sns/message', $data); $this->assertEquals(200, $response->status()); - Event::assertDispatched(SnsMessageReceived::class); + Event::assertNotDispatched(SnsMessageAlphaReceived::class); + Event::assertNotDispatched(SnsMessageBetaReceived::class); + } + + public function testDispatchesMappedMessageEvent(): void + { + $data = $this->makeSnsMessageData([ + 'Type' => SnsMessage::NOTIFICATION_TYPE, + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic', + 'Message' => 'Test message', + ]); + + $response = $this->postJson('/api/sns/message', $data); + + $this->assertEquals(200, $response->status()); + Event::assertDispatched(SnsMessageAlphaReceived::class); + Event::assertNotDispatched(SnsMessageReceived::class); + Event::assertNotDispatched(SnsMessageBetaReceived::class); } } diff --git a/tests/Unit/SnsBrokerTest.php b/tests/Unit/SnsBrokerTest.php index 83b82a6..b50644d 100644 --- a/tests/Unit/SnsBrokerTest.php +++ b/tests/Unit/SnsBrokerTest.php @@ -61,6 +61,7 @@ public function testThrowsExceptionForUnknownMessageType(): void $this->expectExceptionMessage('Unknown message type: Unknown'); $this->broker->handleRequest($request); + Event::assertNotDispatched(SnsMessageReceived::class); } @@ -87,6 +88,7 @@ public function testDispatchesDefaultConfirmationRequestEvent(): void ])); $this->broker->handleRequest($request); + Event::assertDispatched(SnsConfirmationRequestReceived::class); } @@ -107,6 +109,7 @@ public function testDispatchesMappedConfirmationRequestEvent(): void ])); $this->broker->handleRequest($request); + Event::assertDispatched(SnsConfirmationRequestAlphaReceived::class); Event::assertNotDispatched(SnsConfirmationRequestReceived::class); } @@ -128,6 +131,7 @@ public function testDispatchFirstMappedConfirmationEvent(): void ])); $this->broker->handleRequest($request); + Event::assertDispatched(SnsConfirmationRequestAlphaReceived::class); Event::assertNotDispatched(SnsConfirmationRequestBetaReceived::class); Event::assertNotDispatched(SnsConfirmationRequestReceived::class); @@ -164,6 +168,7 @@ public function testDispatchesDefaultNotificationEvent(): void ])); $this->broker->handleRequest($request); + Event::assertDispatched(SnsMessageReceived::class); } @@ -183,6 +188,7 @@ public function testDispatchMappedNotificationEvent(): void ])); $this->broker->handleRequest($request); + Event::assertDispatched(SnsMessageAlphaReceived::class); Event::assertNotDispatched(SnsMessageReceived::class); } @@ -203,6 +209,7 @@ public function testDispatchFirstMappedNotificationEvent(): void ])); $this->broker->handleRequest($request); + Event::assertDispatched(SnsMessageAlphaReceived::class); Event::assertNotDispatched(SnsMessageBetaReceived::class); Event::assertNotDispatched(SnsMessageReceived::class); From 26f54ff17ee190603a7922e9e8eb2a766d55d0d6 Mon Sep 17 00:00:00 2001 From: Dirk Tepe Date: Sat, 21 Aug 2021 20:46:50 -0400 Subject: [PATCH 8/8] Split and expand feature tests --- tests/Feature/SnsHandlerDefaultEventsTest.php | 79 +++++++++++++++++++ ...est.php => SnsHandlerMappedEventsTest.php} | 36 +++------ 2 files changed, 88 insertions(+), 27 deletions(-) create mode 100644 tests/Feature/SnsHandlerDefaultEventsTest.php rename tests/Feature/{SnsHandlerTest.php => SnsHandlerMappedEventsTest.php} (73%) diff --git a/tests/Feature/SnsHandlerDefaultEventsTest.php b/tests/Feature/SnsHandlerDefaultEventsTest.php new file mode 100644 index 0000000..a563f19 --- /dev/null +++ b/tests/Feature/SnsHandlerDefaultEventsTest.php @@ -0,0 +1,79 @@ +app->bind(MessageValidator::class, NullMessageValidator::class); + } + + protected function getPackageProviders($app) + { + return [ServiceProvider::class]; + } + + protected function getEnvironmentSetUp($app) + { + $app['config']->set('sns-handler.confirmation-events', [ + SnsConfirmationRequestReceived::class => ['*'], + ]); + + $app['config']->set('sns-handler.message-events', [ + SnsMessageReceived::class => ['*'], + ]); + } + + public function testDispatchesDefaultConfirmationEvent(): void + { + $data = $this->makeSnsMessageData([ + 'Type' => SnsMessage::SUBSCRIBE_TYPE, + 'Message' => json_encode(['id' => 123, 'color' => 'red'], true), + 'SubscribeURL' => 'https://aws.amazon.com/sns/register/abc123' + ]); + + $response = $this->postJson('/api/sns/message', $data); + + $this->assertEquals(200, $response->status()); + Event::assertDispatched(SnsConfirmationRequestReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestAlphaReceived::class); + Event::assertNotDispatched(SnsConfirmationRequestBetaReceived::class); + } + + public function testDispatchesDefaultMessageEvent(): void + { + $data = $this->makeSnsMessageData([ + 'Type' => SnsMessage::NOTIFICATION_TYPE, + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:MyTopic', + 'Message' => 'Test message', + ]); + + $response = $this->postJson('/api/sns/message', $data); + + $this->assertEquals(200, $response->status()); + Event::assertDispatched(SnsMessageReceived::class); + Event::assertNotDispatched(SnsMessageAlphaReceived::class); + Event::assertNotDispatched(SnsMessageBetaReceived::class); + } +} diff --git a/tests/Feature/SnsHandlerTest.php b/tests/Feature/SnsHandlerMappedEventsTest.php similarity index 73% rename from tests/Feature/SnsHandlerTest.php rename to tests/Feature/SnsHandlerMappedEventsTest.php index ce3e9b6..4b1c982 100644 --- a/tests/Feature/SnsHandlerTest.php +++ b/tests/Feature/SnsHandlerMappedEventsTest.php @@ -16,7 +16,7 @@ use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageBetaReceived; use Nipwaayoni\Tests\SnsHandler\MakesSnsTests; -class SnsHandlerTest extends \Nipwaayoni\Tests\SnsHandler\TestCase +class SnsHandlerMappedEventsTest extends \Nipwaayoni\Tests\SnsHandler\TestCase { use MakesSnsTests; @@ -38,31 +38,13 @@ protected function getEnvironmentSetUp($app) { $app['config']->set('sns-handler.confirmation-events', [ SnsConfirmationRequestAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], - SnsConfirmationRequestReceived::class => ['*'], ]); $app['config']->set('sns-handler.message-events', [ SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'], - SnsMessageReceived::class => ['*'], ]); } - public function testDispatchesDefaultConfirmationEvent(): void - { - $data = $this->makeSnsMessageData([ - 'Type' => SnsMessage::SUBSCRIBE_TYPE, - 'Message' => json_encode(['id' => 123, 'color' => 'red'], true), - 'SubscribeURL' => 'https://aws.amazon.com/sns/register/abc123' - ]); - - $response = $this->postJson('/api/sns/message', $data); - - $this->assertEquals(200, $response->status()); - Event::assertDispatched(SnsConfirmationRequestReceived::class); - Event::assertNotDispatched(SnsConfirmationRequestAlphaReceived::class); - Event::assertNotDispatched(SnsConfirmationRequestBetaReceived::class); - } - public function testDispatchesMappedConfirmationEvent(): void { $data = $this->makeSnsMessageData([ @@ -79,34 +61,34 @@ public function testDispatchesMappedConfirmationEvent(): void Event::assertNotDispatched(SnsConfirmationRequestReceived::class); } - public function testDispatchesDefaultMessageEvent(): void + public function testDispatchesMappedMessageEvent(): void { $data = $this->makeSnsMessageData([ 'Type' => SnsMessage::NOTIFICATION_TYPE, - 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:MyTopic', + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic', 'Message' => 'Test message', ]); $response = $this->postJson('/api/sns/message', $data); $this->assertEquals(200, $response->status()); - Event::assertDispatched(SnsMessageReceived::class); - Event::assertNotDispatched(SnsMessageAlphaReceived::class); + Event::assertDispatched(SnsMessageAlphaReceived::class); + Event::assertNotDispatched(SnsMessageReceived::class); Event::assertNotDispatched(SnsMessageBetaReceived::class); } - public function testDispatchesMappedMessageEvent(): void + public function testReturnsNotFoundForUnmappedMessageEvent(): void { $data = $this->makeSnsMessageData([ 'Type' => SnsMessage::NOTIFICATION_TYPE, - 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic', + 'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:BetaTopic', 'Message' => 'Test message', ]); $response = $this->postJson('/api/sns/message', $data); - $this->assertEquals(200, $response->status()); - Event::assertDispatched(SnsMessageAlphaReceived::class); + $this->assertEquals(404, $response->status()); + Event::assertNotDispatched(SnsMessageAlphaReceived::class); Event::assertNotDispatched(SnsMessageReceived::class); Event::assertNotDispatched(SnsMessageBetaReceived::class); }