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"); + } + } +}