diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..cd3d475
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,44 @@
+name: PHPUnit
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ]
+
+ name: PHP ${{ matrix.php }}
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ coverage: xdebug
+
+ - name: Install dependencies
+ run: composer install --no-interaction --prefer-dist --no-progress
+
+ - name: Run PHPUnit
+ run: vendor/bin/phpunit --coverage-text=coverage.txt
+
+ - name: Check coverage
+ run: |
+ cat coverage.txt
+ COVERAGE=$(grep -A3 'Summary:' coverage.txt | grep 'Lines:' | grep -oP '\d+\.\d+(?=%)')
+ THRESHOLD=100
+ if (( $(echo "$COVERAGE < $THRESHOLD" | bc -l) )); then
+ echo "Coverage is $COVERAGE%, below ${THRESHOLD}% threshold"
+ exit 1
+ fi
+ echo "Coverage is $COVERAGE% (threshold: ${THRESHOLD}%)"
diff --git a/composer.json b/composer.json
index 6ac775d..8b9f87e 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
"packaged/helpers": "~1.0||~2.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.5"
+ "phpunit/phpunit": "~9"
},
"autoload": {
"psr-4": {
diff --git a/phpunit.xml.dist b/phpunit.xml
similarity index 62%
rename from phpunit.xml.dist
rename to phpunit.xml
index 55fd283..8958c0a 100755
--- a/phpunit.xml.dist
+++ b/phpunit.xml
@@ -1,27 +1,23 @@
-
./tests
-
-
+
+
src
-
-
- vendor
- tests
-
-
+
+
diff --git a/src/Payloads/FortifiWebhookPayload.php b/src/Payloads/FortifiWebhookPayload.php
index 140a365..f1e1601 100644
--- a/src/Payloads/FortifiWebhookPayload.php
+++ b/src/Payloads/FortifiWebhookPayload.php
@@ -29,6 +29,7 @@ final public function __construct($event)
* @return mixed data which can be serialized by json_encode,
* which is a value of any type other than a resource.
*/
+ #[\ReturnTypeWillChange]
public function jsonSerialize()
{
return [
diff --git a/src/Webhooks.php b/src/Webhooks.php
index ff99e23..86202f4 100644
--- a/src/Webhooks.php
+++ b/src/Webhooks.php
@@ -62,7 +62,7 @@ public static function getPayload($json)
$decoded = json_decode($json);
$events = static::all();
$event = Objects::property($decoded, 'event');
- if(isset($events[$event]))
+ if($event !== null && isset($events[$event]))
{
return call_user_func([$events[$event], 'hydrateFromJson'], $json);
}
diff --git a/tests/Payloads/FortifiWebhookPayloadTest.php b/tests/Payloads/FortifiWebhookPayloadTest.php
index ad1d75c..0ba71ae 100644
--- a/tests/Payloads/FortifiWebhookPayloadTest.php
+++ b/tests/Payloads/FortifiWebhookPayloadTest.php
@@ -2,8 +2,10 @@
namespace Fortifi\Tests\Webhooks\Payloads;
use Fortifi\Webhooks\Payloads\FortifiWebhookPayload;
+use Fortifi\Webhooks\Payloads\Test\TestWHP;
+use PHPUnit\Framework\TestCase;
-class FortifiWebhookPayloadTest extends \PHPUnit_Framework_TestCase
+class FortifiWebhookPayloadTest extends TestCase
{
public function testWebookPayload()
{
@@ -25,4 +27,35 @@ public function testWebookPayload()
$this->assertEquals(456, $object->getRequestId());
}
}
+
+ /**
+ * Test that jsonSerialize works without deprecation in PHP 8+
+ * Requires #[\ReturnTypeWillChange] attribute on jsonSerialize()
+ */
+ public function testJsonSerializeCompatibility()
+ {
+ $deprecation = null;
+ set_error_handler(
+ function($errno, $errstr) use (&$deprecation) {
+ if(strpos($errstr, 'jsonSerialize') !== false)
+ {
+ $deprecation = $errstr;
+ }
+ return true;
+ },
+ E_DEPRECATED
+ );
+
+ try
+ {
+ $payload = new TestWHP('test.event');
+ json_encode($payload);
+ }
+ finally
+ {
+ restore_error_handler();
+ }
+
+ $this->assertNull($deprecation, $deprecation ?? '');
+ }
}
diff --git a/tests/Payloads/Test/TestWHPTest.php b/tests/Payloads/Test/TestWHPTest.php
index f81c442..51e97ac 100644
--- a/tests/Payloads/Test/TestWHPTest.php
+++ b/tests/Payloads/Test/TestWHPTest.php
@@ -2,8 +2,9 @@
namespace Fortifi\Tests\Webhooks\Payloads\Test;
use Fortifi\Webhooks\Payloads\Test\TestWHP;
+use PHPUnit\Framework\TestCase;
-class TestWHPTest extends \PHPUnit_Framework_TestCase
+class TestWHPTest extends TestCase
{
public function testTransport()
{
diff --git a/tests/WebhooksTest.php b/tests/WebhooksTest.php
new file mode 100644
index 0000000..e337635
--- /dev/null
+++ b/tests/WebhooksTest.php
@@ -0,0 +1,103 @@
+assertIsArray($all);
+ $this->assertNotEmpty($all);
+ $this->assertArrayHasKey(CustomerWHE::CREATED, $all);
+ $this->assertEquals(CustomerCreatedWHP::class, $all[CustomerWHE::CREATED]);
+ }
+
+ public function testGetPayloadWithValidEvent()
+ {
+ $json = json_encode([
+ 'event' => CustomerWHE::CREATED,
+ 'sig' => 'test-sig',
+ 'uuid' => 'test-uuid',
+ 'rqid' => 'test-rqid',
+ 'data' => ['customerFid' => 'cust-123'],
+ ]);
+
+ $payload = Webhooks::getPayload($json);
+
+ $this->assertInstanceOf(CustomerCreatedWHP::class, $payload);
+ $this->assertEquals(CustomerWHE::CREATED, $payload->getEventType());
+ $this->assertEquals('test-uuid', $payload->getPayloadId());
+ $this->assertEquals('test-rqid', $payload->getRequestId());
+ }
+
+ public function testGetPayloadWithUnknownEvent()
+ {
+ $json = json_encode([
+ 'event' => 'unknown.event',
+ 'sig' => 'test-sig',
+ 'uuid' => 'test-uuid',
+ 'rqid' => 'test-rqid',
+ 'data' => [],
+ ]);
+
+ $payload = Webhooks::getPayload($json);
+
+ $this->assertNull($payload);
+ }
+
+ public function testGetPayloadWithMissingEvent()
+ {
+ $json = json_encode([
+ 'sig' => 'test-sig',
+ 'uuid' => 'test-uuid',
+ 'data' => [],
+ ]);
+
+ $payload = Webhooks::getPayload($json);
+
+ $this->assertNull($payload);
+ }
+
+ public function testAllWithDisplayNames()
+ {
+ $displayNames = Webhooks::allWithDisplayNames();
+
+ $this->assertIsArray($displayNames);
+ $this->assertNotEmpty($displayNames);
+ $this->assertCount(count(Webhooks::all()), $displayNames);
+
+ foreach($displayNames as $name)
+ {
+ $this->assertIsString($name);
+ $this->assertNotEmpty($name);
+ }
+ }
+
+ public function testGetDisplayName()
+ {
+ $this->assertEquals('Customer Created', Webhooks::getDisplayName('customer.created'));
+ $this->assertEquals('Customer Email Unsubscribed', Webhooks::getDisplayName('customer.email.unsubscribed'));
+ $this->assertEquals('Invoice Add Payment', Webhooks::getDisplayName('invoice.add.payment'));
+ }
+
+ public function testGetDisplayNamePreservesCase()
+ {
+ $this->assertEquals('Test Event', Webhooks::getDisplayName('test.event'));
+ }
+
+ public function testAllEventsMapToValidClasses()
+ {
+ foreach(Webhooks::all() as $event => $class)
+ {
+ $this->assertTrue(class_exists($class), "Class $class for event $event does not exist");
+ }
+ }
+}