diff --git a/composer.json b/composer.json index 57b55cb..939e732 100644 --- a/composer.json +++ b/composer.json @@ -11,11 +11,11 @@ } ], "require": { - "php": "^8.1", - "illuminate/http": "^9.0|^10.0", - "illuminate/support": "^9.0|^10.0", - "illuminate/contracts": "^9.0|^10.0", - "jms/serializer": "^3.27" + "php": "^8.1|^8.2", + "illuminate/http": "^9.0|^10.0|^11.0", + "illuminate/support": "^9.0|^10.0|^11.0", + "illuminate/contracts": "^9.0|^10.0|^11.0", + "jms/serializer": "^3.30" }, "autoload": { "psr-4": { @@ -37,17 +37,17 @@ }, "require-dev": { "roave/security-advisories": "dev-latest", - "friendsofphp/php-cs-fixer": "^3.23", - "phpunit/phpunit": "^10.3", - "nunomaduro/larastan": "^2.6", - "orchestra/testbench": "^8.9", + "friendsofphp/php-cs-fixer": "^3.51", + "phpunit/phpunit": "^10.0", + "larastan/larastan": "^2.9", + "orchestra/testbench": "^8.9|^9.0", "phpstan/phpstan-phpunit": "^1.3", "php-parallel-lint/php-parallel-lint": "^1.3", - "symfony/cache": "^6.3", - "vimeo/psalm": "^5.15", - "psalm/plugin-laravel": "^2.8", - "psalm/plugin-phpunit": "^0.18.4", - "infection/infection": "^0.27.6" + "symfony/cache": "^6.3|^7.0", + "vimeo/psalm": "^5.23", + "psalm/plugin-laravel": "^2.10", + "psalm/plugin-phpunit": "^0.19", + "infection/infection": "^0.27.10" }, "scripts": { "lint": "parallel-lint --exclude .git --exclude vendor .", diff --git a/tests/Http/Responses/ResponseFactoryTest.php b/tests/Http/Responses/ResponseFactoryTest.php index 139d386..17cd7a0 100644 --- a/tests/Http/Responses/ResponseFactoryTest.php +++ b/tests/Http/Responses/ResponseFactoryTest.php @@ -10,8 +10,13 @@ use Dropelikeit\LaravelJmsSerializer\Serializer\Factory; use Dropelikeit\LaravelJmsSerializer\Tests\ResponseFactory\Dummy; use Dropelikeit\LaravelJmsSerializer\Tests\ResponseFactory\Response; +use Dropelikeit\LaravelJmsSerializer\Tests\ResponseFactory\XmlDummy; use Illuminate\Http\Response as LaravelResponse; +use InvalidArgumentException; use JMS\Serializer\SerializationContext; +use JMS\Serializer\SerializerInterface; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -20,10 +25,8 @@ */ final class ResponseFactoryTest extends TestCase { - /** - * @psalm-var MockObject&Contracts\Config - */ - private MockObject $config; + private readonly MockObject&Contracts\Config $config; + private readonly MockObject&SerializerInterface $serializer; public function setUp(): void { @@ -33,11 +36,13 @@ public function setUp(): void ->getMockBuilder(Contracts\Config::class) ->disableOriginalConstructor() ->getMock(); + + $this->serializer = $this + ->getMockBuilder(SerializerInterface::class) + ->getMock(); } - /** - * @test - */ + #[Test] public function canCreateResponse(): void { $this->config @@ -63,9 +68,7 @@ public function canCreateResponse(): void self::assertEquals('{"amount":12,"text":"Hello World!"}', $response->getContent()); } - /** - * @test - */ + #[Test] public function canCreateFromArrayIterator(): void { $this->config @@ -91,9 +94,7 @@ public function canCreateFromArrayIterator(): void self::assertEquals('[{"key":"magic_number","value":12}]', $response->getContent()); } - /** - * @test - */ + #[Test] public function canCreateJsonResponseFromArray(): void { $this->config @@ -122,9 +123,7 @@ public function canCreateJsonResponseFromArray(): void ); } - /** - * @test - */ + #[Test] public function canCreateXmlResponseFromArray(): void { $this->config @@ -140,7 +139,7 @@ public function canCreateXmlResponseFromArray(): void $this->config ->expects(self::once()) ->method('getSerializeType') - ->willReturn(Contracts\Config::SERIALIZE_TYPE_XML); + ->willReturn('xml'); $responseFactory = new ResponseFactory((new Factory())->getSerializer($this->config), $this->config); @@ -165,9 +164,7 @@ public function canCreateXmlResponseFromArray(): void ); } - /** - * @test - */ + #[Test] public function canChangeStatusCode(): void { $this->config @@ -195,9 +192,7 @@ public function canChangeStatusCode(): void self::assertEquals('{"amount":12,"text":"Hello World!"}', $response->getContent()); } - /** - * @test - */ + #[Test] public function canUseGivenContext(): void { $this->config @@ -225,11 +220,9 @@ public function canUseGivenContext(): void /** * @psalm-param Contracts\Config::SERIALIZE_TYPE_* $changeSerializeTypeTo - * @param string $expectedResult - * - * @test - * @dataProvider dataProviderCanSerializeWithSerializeType */ + #[Test] + #[DataProvider(methodName: 'dataProviderCanSerializeWithSerializeType')] public function canSerializeWithSerializeType(string $changeSerializeTypeTo, string $expectedResult): void { $this->config @@ -282,9 +275,7 @@ public static function dataProviderCanSerializeWithSerializeType(): array ]; } - /** - * @test - */ + #[Test] public function canNotCreateWithUnknownSerializeType(): void { $this->expectException(SerializeType::class); @@ -310,9 +301,7 @@ public function canNotCreateWithUnknownSerializeType(): void $responseFactory->withSerializeType('array'); } - /** - * @test - */ + #[Test] public function canCreateQuietResponse(): void { $responseFactory = new ResponseFactory((new Factory())->getSerializer($this->config), $this->config); @@ -321,4 +310,96 @@ public function canCreateQuietResponse(): void $this->assertEquals(new LaravelResponse(status: 204), $response); } + + #[Test] + public function throwInvalidArgumentExceptionIfContentOnCreateMethodIsEmpty(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Expected a different value than "".'); + + $object = new Dummy(); + + $this->config + ->expects(self::never()) + ->method('getCacheDir'); + + $this->config + ->expects(self::never()) + ->method('debug'); + + $this->config + ->expects(self::once()) + ->method('getSerializeType') + ->willReturn(Contracts\Config::SERIALIZE_TYPE_JSON); + + $this->serializer + ->expects(self::once()) + ->method('serialize') + ->with($object, 'json', null, null) + ->wilLReturn(''); + + (new ResponseFactory($this->serializer, $this->config))->create($object); + } + + #[Test] + public function throwInvalidArgumentExceptionIfContentOnCreateFromArrayMethodIsEmpty(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Expected a different value than "".'); + + $object = new Dummy(); + + $this->config + ->expects(self::never()) + ->method('getCacheDir'); + + $this->config + ->expects(self::never()) + ->method('debug'); + + $this->config + ->expects(self::once()) + ->method('getSerializeType') + ->willReturn(Contracts\Config::SERIALIZE_TYPE_JSON); + + $this->serializer + ->expects(self::once()) + ->method('serialize') + ->with([$object], 'json', null) + ->wilLReturn(''); + + (new ResponseFactory($this->serializer, $this->config))->createFromArray([$object]); + } + + #[Test] + public function canDetectIfSerializeTypeIsXmlResultResponseHasXml(): void + { + $this->config + ->expects(self::once()) + ->method('getCacheDir') + ->willReturn(__DIR__); + + $this->config + ->expects(self::once()) + ->method('debug') + ->willReturn(true); + + $this->config + ->expects(self::once()) + ->method('getSerializeType') + ->willReturn('xml'); + + $responseFactory = new ResponseFactory((new Factory())->getSerializer($this->config), $this->config); + + $response = $responseFactory->create(new XmlDummy()); + + $this->assertEquals( + ' + +', + $response->getContent(), + ); + } } diff --git a/tests/ResponseFactory/XmlDummy.php b/tests/ResponseFactory/XmlDummy.php new file mode 100644 index 0000000..8f80c47 --- /dev/null +++ b/tests/ResponseFactory/XmlDummy.php @@ -0,0 +1,19 @@ +title = 'My test'; + } +} diff --git a/tests/Serializer/FactoryTest.php b/tests/Serializer/FactoryTest.php index eea904f..4dbe513 100644 --- a/tests/Serializer/FactoryTest.php +++ b/tests/Serializer/FactoryTest.php @@ -10,9 +10,16 @@ use Dropelikeit\LaravelJmsSerializer\Tests\Serializer\data\CustomHandler; use InvalidArgumentException; use JMS\Serializer\Context; +use JMS\Serializer\Handler\HandlerRegistry; use JMS\Serializer\JsonSerializationVisitor; +use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy; +use JMS\Serializer\Naming\SerializedNameAnnotationStrategy; +use JMS\Serializer\SerializationContext; use JMS\Serializer\Serializer; +use JMS\Serializer\SerializerBuilder; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Webmozart\Assert\Assert; /** * @author Marcel Strahl @@ -138,4 +145,91 @@ public function canNotCreateSerializerWithInvalidCustomHandler(): void ], ])); } + + #[Test] + public function detectIfSerializerHasDefaultListeners(): void + { + $expectedSerializer = $this->getSerializer(); + + /** @var array{serialize_null: bool, cache_dir: string, serialize_type: string, debug: bool, add_default_handlers: bool, custom_handlers: array} $config */ + $config = [ + 'serialize_null' => true, + 'serialize_type' => 'json', + 'cache_dir' => 'tmp', + 'debug' => false, + 'add_default_handlers' => true, + 'custom_handlers' => [ + CustomHandler::class, + ], + ]; + + $serializer = (new Factory())->getSerializer(Config::fromConfig($config)); + + $this->assertEquals($expectedSerializer, $serializer); + } + + private function getSerializer(): Serializer + { + $config = Config::fromConfig([ + 'serialize_null' => true, + 'serialize_type' => 'json', + 'cache_dir' => 'tmp', + 'debug' => false, + 'add_default_handlers' => true, + 'custom_handlers' => [ + CustomHandler::class, + ], + ]); + + $builder = SerializerBuilder::create() + ->setPropertyNamingStrategy( + new SerializedNameAnnotationStrategy( + new IdenticalPropertyNamingStrategy() + ) + ) + ->addDefaultListeners() + ->setSerializationContextFactory(static function () use ($config): SerializationContext { + return SerializationContext::create()->setSerializeNull($config->shouldSerializeNull()); + }); + + if ($config->shouldAddDefaultHeaders()) { + $builder->addDefaultHandlers(); + } + + $customHandlers = $config->getCustomHandlers(); + if ($customHandlers !== []) { + $builder->configureHandlers(function (HandlerRegistry $registry) use ($customHandlers): void { + foreach ($customHandlers as $customHandler) { + if (is_string($customHandler) && class_exists($customHandler)) { + $customHandler = new $customHandler(); + } + + Assert::implementsInterface( + $customHandler, + CustomHandlerConfiguration::class, + sprintf( + 'Its required to implement the "%s" interface', + CustomHandlerConfiguration::class + ) + ); + /** @phpstan-ignore-next-line */ + assert($customHandler instanceof CustomHandlerConfiguration); + + $registry->registerHandler( + $customHandler->getDirection(), + $customHandler->getTypeName(), + $customHandler->getFormat(), + $customHandler->getCallable(), + ); + } + }); + } + + $cacheDir = $config->getCacheDir(); + if ($cacheDir !== '') { + $builder->setCacheDir($cacheDir); + } + + return $builder->setDebug($config->debug())->build(); + } }