Skip to content

Commit

Permalink
Merge pull request #38 from renoki-co/feature/testing-trait
Browse files Browse the repository at this point in the history
[7.x] Simplifying the testing trait
  • Loading branch information
rennokki authored Nov 13, 2021
2 parents 7b4c506 + 60d5acf commit c93a441
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 103 deletions.
65 changes: 34 additions & 31 deletions src/Concerns/GeneratesSnsMessages.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@
trait GeneratesSnsMessages
{
/**
* Get the private key to sign the request.
* Get the private key to sign the request for SNS.
*
* @var string
*/
protected static $privateKey;
protected static $snsPrivateKey;

/**
* The certificate to sign the request.
* The certificate to sign the request for SNS.
*
* @var string
*/
protected static $certificate;
protected static $snsCertificate;

/**
* An valid certificate URL for test.
* An valid certificate URL to test SNS.
*
* @var string
*/
public static $validCertUrl = 'https://sns.us-west-2.amazonaws.com/bar.pem';
public static $snsValidCertUrl = 'https://sns.us-west-2.amazonaws.com/bar.pem';

/**
* Initialize the SSL keys and private keys.
Expand All @@ -35,27 +35,13 @@ trait GeneratesSnsMessages
*/
protected static function initializeSsl(): void
{
self::$privateKey = openssl_pkey_new();
static::$snsPrivateKey = openssl_pkey_new();

$csr = openssl_csr_new([], self::$privateKey);
$csr = openssl_csr_new([], static::$snsPrivateKey);

$x509 = openssl_csr_sign($csr, null, self::$privateKey, 1);
$x509 = openssl_csr_sign($csr, null, static::$snsPrivateKey, 1);

openssl_x509_export($x509, self::$certificate);

// Deprecated in PHP >= 8.0
// openssl_x509_free($x509);
}

/**
* Deinitialize the SSL keys.
*
* @return void
*/
protected static function tearDownSsl(): void
{
// Deprecated in PHP >= 8.0
// openssl_pkey_free(self::$privateKey);
openssl_x509_export($x509, static::$snsCertificate);
}

/**
Expand All @@ -66,7 +52,9 @@ protected static function tearDownSsl(): void
*/
protected function getSignature($stringToSign)
{
openssl_sign($stringToSign, $signature, self::$privateKey);
static::initializeSsl();

openssl_sign($stringToSign, $signature, static::$snsPrivateKey);

return base64_encode($signature);
}
Expand All @@ -91,7 +79,7 @@ protected function getSubscriptionConfirmationPayload(array $custom = []): array
'Timestamp' => now()->toDateTimeString(),
'SignatureVersion' => '1',
'Signature' => true,
'SigningCertURL' => static::$validCertUrl,
'SigningCertURL' => static::$snsValidCertUrl,
], $custom);

$message['Signature'] = $this->getSignature(
Expand Down Expand Up @@ -124,7 +112,7 @@ protected function getNotificationPayload(array $payload = [], array $custom = [
'SignatureVersion' => '1',
'Token' => '2336412f37...',
'Signature' => true,
'SigningCertURL' => static::$validCertUrl,
'SigningCertURL' => static::$snsValidCertUrl,
'UnsubscribeURL' => 'https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96',
], $custom);

Expand All @@ -135,6 +123,21 @@ protected function getNotificationPayload(array $payload = [], array $custom = [
return $message;
}

/**
* Send the SNS-signed message to the given URL.
*
* @param string $url
* @param array $snsPayload
* @return \Illuminate\Testing\TestResponse
*/
protected function sendSnsMessage($url, array $snsPayload = [])
{
/** @var \Illuminate\Foundation\Testing\Concerns\MakesHttpRequests&\Rennokki\LaravelSnsEvents\Concerns\GeneratesSnsMessages $this */
return $this->withHeaders($this->getHeadersForMessage($snsPayload))
->withHeaders(['X-Sns-Testing-Certificate' => static::$snsCertificate])
->json('POST', $url, $snsPayload);
}

/**
* Get the right headers for a SNS message.
*
Expand All @@ -144,10 +147,10 @@ protected function getNotificationPayload(array $payload = [], array $custom = [
protected function getHeadersForMessage(array $message): array
{
return [
'X-AMZ-SNS-MESSAGE-TYPE' => $message['Type'],
'X-AMZ-SNS-MESSAGE-ID' => $message['MessageId'],
'X-AMZ-SNS-TOPIC-ARN' => $message['TopicArn'],
'X-AMZ-SNS-SUBSCRIPTION-ARN' => "{$message['TopicArn']}:c9135db0-26c4-47ec-8998-413945fb5a96",
'X-AMZ-SNS-MESSAGE-TYPE' => $message['Type'] ?? null,
'X-AMZ-SNS-MESSAGE-ID' => $message['MessageId'] ?? null,
'X-AMZ-SNS-TOPIC-ARN' => $message['TopicArn'] ?? null,
'X-AMZ-SNS-SUBSCRIPTION-ARN' => ($message['TopicArn'] ?? null).':c9135db0-26c4-47ec-8998-413945fb5a96',
];
}
}
15 changes: 15 additions & 0 deletions src/Concerns/HandlesSns.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Aws\Sns\MessageValidator;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;

trait HandlesSns
{
Expand Down Expand Up @@ -51,6 +52,20 @@ public function snsMessageIsValid(Request $request): bool
*/
protected function getMessageValidator(Request $request)
{
if (App::environment(['testing', 'local'])) {
return new MessageValidator(function ($url) use ($request) {
if ($certificate = $request->sns_certificate) {
return $certificate;
}

if ($certificate = $request->header('X-Sns-Testing-Certificate')) {
return $certificate;
}

return $url;
});
}

return new MessageValidator;
}
}
14 changes: 0 additions & 14 deletions tests/Controllers/CustomSnsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Rennokki\LaravelSnsEvents\Tests\Controllers;

use Aws\Sns\MessageValidator;
use Illuminate\Http\Request;
use Rennokki\LaravelSnsEvents\Http\Controllers\SnsController;
use Rennokki\LaravelSnsEvents\Tests\Events\CustomSnsEvent;
Expand Down Expand Up @@ -87,17 +86,4 @@ protected function onSubscriptionConfirmation(array $snsMessage, Request $reques
{
mt_rand(0, 10000);
}

/**
* Get the message validator instance.
*
* @param \Illuminate\Http\Request $request
* @return \Aws\Sns\MessageValidator
*/
protected function getMessageValidator(Request $request)
{
return new MessageValidator(function ($url) use ($request) {
return $request->certificate ?: $url;
});
}
}
15 changes: 1 addition & 14 deletions tests/Controllers/SnsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,9 @@

namespace Rennokki\LaravelSnsEvents\Tests\Controllers;

use Aws\Sns\MessageValidator;
use Illuminate\Http\Request;
use Rennokki\LaravelSnsEvents\Http\Controllers\SnsController as BaseSnsController;

class SnsController extends BaseSnsController
{
/**
* Get the message validator instance.
*
* @param \Illuminate\Http\Request $request
* @return \Aws\Sns\MessageValidator
*/
protected function getMessageValidator(Request $request)
{
return new MessageValidator(function ($url) use ($request) {
return $request->certificate ?: $url;
});
}
//
}
42 changes: 15 additions & 27 deletions tests/EventTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,19 @@ public function test_no_event_triggering_on_bad_request()
{
Event::fake();

$this->json('GET', route('sns'))
->assertSee('OK');
$this->sendSnsMessage(route('sns'))->assertSee('OK');

Event::assertNotDispatched(SnsNotification::class);
Event::assertNotDispatched(SnsSubscriptionConfirmation::class);

$this->json('GET', route('sns', ['certificate' => static::$certificate]))
->assertSee('OK');
$this->sendSnsMessage(route('sns'))->assertSee('OK');

Event::assertNotDispatched(SnsNotification::class);
Event::assertNotDispatched(SnsSubscriptionConfirmation::class);

$payload = $this->getSubscriptionConfirmationPayload();

$this->withHeaders($this->getHeadersForMessage($payload))
->json('GET', route('sns', ['certificate' => static::$certificate]))
->assertSee('OK');
$this->sendSnsMessage(route('sns'))->assertSee('OK');

Event::assertNotDispatched(SnsNotification::class);
Event::assertNotDispatched(SnsSubscriptionConfirmation::class);
Expand All @@ -42,11 +38,9 @@ public function test_subscription_confirmation()

$payload = $this->getSubscriptionConfirmationPayload();

$this->withHeaders(array_merge($this->getHeadersForMessage($payload), [
'x-test-header' => 1,
]))
->json('POST', route('sns', ['certificate' => static::$certificate]), $payload)
->assertSee('OK');
$this->withHeaders(['x-test-header' => 1])
->sendSnsMessage(route('sns'), $payload)
->assertSee('OK');

Event::assertNotDispatched(SnsNotification::class);

Expand All @@ -68,11 +62,9 @@ public function test_notification_confirmation()
'sns' => true,
]);

$this->withHeaders(array_merge($this->getHeadersForMessage($payload), [
'x-test-header' => 1,
]))
->json('POST', route('sns', ['certificate' => static::$certificate]), $payload)
->assertSee('OK');
$this->withHeaders(['x-test-header' => 1])
->sendSnsMessage(route('sns'), $payload)
->assertSee('OK');

Event::assertNotDispatched(SnsSubscriptionConfirmation::class);

Expand All @@ -98,11 +90,9 @@ public function test_custom_controller_confirmation()

$payload = $this->getSubscriptionConfirmationPayload();

$this->withHeaders(array_merge($this->getHeadersForMessage($payload), [
'x-test-header' => 1,
]))
->json('POST', route('custom-sns', ['test' => 'some-string', 'certificate' => static::$certificate]), $payload)
->assertSee('OK');
$this->withHeaders(['x-test-header' => 1])
->sendSnsMessage(route('custom-sns', ['test' => 'some-string']), $payload)
->assertSee('OK');

Event::assertNotDispatched(CustomSnsEvent::class);

Expand All @@ -128,11 +118,9 @@ public function test_custom_controller_notification()
'sns' => true,
]);

$this->withHeaders(array_merge($this->getHeadersForMessage($payload), [
'x-test-header' => 1,
]))
->json('POST', route('custom-sns', ['test' => 'some-string', 'certificate' => static::$certificate]), $payload)
->assertSee('OK');
$this->withHeaders(['x-test-header' => 1])
->sendSnsMessage(route('custom-sns', ['test' => 'some-string']), $payload)
->assertSee('OK');

Event::assertNotDispatched(CustomSubscriptionConfirmation::class);

Expand Down
17 changes: 0 additions & 17 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,10 @@ class TestCase extends Orchestra
{
use GeneratesSnsMessages;

/**
* {@inheritdoc}
*/
public static function setUpBeforeClass(): void
{
static::initializeSsl();
}

/**
* {@inheritdoc}
*/
public static function tearDownAfterClass(): void
{
static::tearDownSsl();
}

/**
* Get package providers.
*
* @param \Illuminate\Foundation\Application $app
*
* @return array
*/
protected function getPackageProviders($app)
Expand Down

0 comments on commit c93a441

Please sign in to comment.