Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,57 @@ jobs:
- name: Run PHPUnit tests
run: vendor/bin/phpunit

phpunit-symfony-64:
name: PHPUnit (PHP ${{ matrix.php }}) (Symfony 6.4)
runs-on: ubuntu-latest
timeout-minutes: 20
continue-on-error: true
strategy:
matrix:
php:
- '8.2'
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- name: Setup PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2
with:
php-version: ${{ matrix.php }}
tools: pecl, composer:2.9.8
extensions: intl, bcmath, curl, openssl, mbstring
coverage: none
ini-values: memory_limit=-1
- name: Get composer cache directory
id: composercache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
# Every package below only exists from Symfony 7.x (or requires it), no other job ever resolves 6.4 without dropping them
- name: Remove dependencies that require Symfony 7+
run: composer remove --dev --no-update --no-interaction symfony/mcp-bundle mcp/sdk laravel/framework illuminate/config illuminate/contracts illuminate/database illuminate/http illuminate/pagination illuminate/routing illuminate/support orchestra/testbench symfony/json-streamer symfony/object-mapper symfony/var-exporter
# symfony/type-info has no 6.x release, 7.4 is the highest line compatible with the 6.4 components
- name: Force Symfony 6.4
run: |
jq '(.["require"], .["require-dev"]) |= with_entries(if (.key | startswith("symfony/")) and (.value | contains("^6.4")) then .value = "6.4.*" elif .key == "symfony/type-info" then .value = "7.4.*" else . end)' composer.json > composer.json.tmp
mv composer.json.tmp composer.json
- name: Cache dependencies
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-symfony-64-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-symfony-64-
- name: Remove cache
run: rm -Rf tests/Fixtures/app/var/cache/*
- name: Update project dependencies
run: |
composer global require "soyuka/pmu:$PMU_VERSION"
composer global config allow-plugins.soyuka/pmu true --no-interaction
composer global link .
composer update
- name: Clear test app cache
run: tests/Fixtures/app/console cache:clear --ansi
- name: Run PHPUnit tests
run: vendor/bin/phpunit

phpunit-symfony-lowest:
name: PHPUnit (PHP ${{ matrix.php }}) (Symfony lowest)
runs-on: ubuntu-latest
Expand Down
14 changes: 14 additions & 0 deletions src/Metadata/Tests/Util/PropertyInfoToTypeInfoHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ public function testConvertLegacyTypesToType(): void
}
}

public function testConvertLegacyCollectionWithoutKeyTypeToType(): void
{
if (!class_exists(LegacyType::class)) {
$this->markTestSkipped();
}

$legacyTypes = [new LegacyType('array', false, null, true, null, new LegacyType('int'))];

$this->assertEquals(
Type::collection(Type::builtin(TypeIdentifier::ARRAY), Type::int(), Type::union(Type::int(), Type::string())), // @phpstan-ignore-line
PropertyInfoToTypeInfoHelper::convertLegacyTypesToType($legacyTypes)
);
}

public function testConvertTypeToLegacyTypes(): void
{
if (!class_exists(LegacyType::class)) {
Expand Down
2 changes: 1 addition & 1 deletion src/Metadata/Util/PropertyInfoToTypeInfoHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public static function createTypeFromLegacyValues(string $builtinType, bool $nul

if ($collectionValueTypes) {
if (!$collectionKeyTypes) {
$variableTypes[] = \is_array($collectionKeyTypes) ? Type::mixed() : Type::union(Type::int(), Type::string()); // @phpstan-ignore-line
$variableTypes[] = Type::union(Type::int(), Type::string());
}

$collectionValueTypes = array_unique(array_map(self::convertLegacyTypeToType(...), $collectionValueTypes));
Expand Down
4 changes: 4 additions & 0 deletions tests/Fixtures/app/AppKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load

$loader->load(__DIR__."/config/config_{$this->getEnvironment()}.yml");

if (class_exists(McpBundle::class)) {
$loader->load(__DIR__.'/config/config_mcp.yml');
}

$messengerConfig = [
'default_bus' => 'messenger.bus.default',
'buses' => [
Expand Down
11 changes: 0 additions & 11 deletions tests/Fixtures/app/config/config_common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,6 @@ api_platform:
mercure:
include_type: true

mcp:
servers:
default:
http:
path: '/mcp'
session:
store: 'file'
directory: '%kernel.cache_dir%/mcp'
ttl: 3600
registry: '*'

services:
test.client:
class: ApiPlatform\Tests\Fixtures\TestBundle\BrowserKit\Client
Expand Down
10 changes: 10 additions & 0 deletions tests/Fixtures/app/config/config_mcp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
mcp:
servers:
default:
http:
path: '/mcp'
session:
store: 'file'
directory: '%kernel.cache_dir%/mcp'
ttl: 3600
registry: '*'
5 changes: 4 additions & 1 deletion tests/Fixtures/app/config/routing_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@

declare(strict_types=1);

use Symfony\AI\McpBundle\McpBundle;
use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return static function (RoutingConfigurator $routes) {
$routes->import('routing_common.yml');
$routes->import('@TestBundle/Controller/Orm', 'attribute');

$routes->import('.', 'mcp');
if (class_exists(McpBundle::class)) {
$routes->import('.', 'mcp');
}

if (class_exists(WebProfilerBundle::class)) {
$reflection = new ReflectionClass(WebProfilerBundle::class);
Expand Down
4 changes: 0 additions & 4 deletions tests/Functional/Doctrine/PartialPaginationMongoDbTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
use ApiPlatform\Tests\Fixtures\TestBundle\Document\PartialPaginationMongo\PartialPaginationMongoDummy;
use ApiPlatform\Tests\RecreateSchemaTrait;
use ApiPlatform\Tests\SetupClassResourcesTrait;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Orchestra\Testbench\Concerns\WithWorkbench;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
Expand All @@ -28,9 +26,7 @@
class PartialPaginationMongoDbTest extends ApiTestCase
{
use RecreateSchemaTrait;
use RefreshDatabase;
use SetupClassResourcesTrait;
use WithWorkbench;

/**
* @return class-string[]
Expand Down
5 changes: 5 additions & 0 deletions tests/Functional/Parameters/ValidationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\WithParameter;
use ApiPlatform\Tests\SetupClassResourcesTrait;
use PHPUnit\Framework\Attributes\DataProvider;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;

final class ValidationTest extends ApiTestCase
{
Expand Down Expand Up @@ -190,6 +191,10 @@ public function testValidatePattern(): void

public function testTranslationValidation(): void
{
if (class_exists(AnnotationLoader::class)) {
$this->markTestSkipped('Parameter validation messages are not translated on Symfony 6.4.');
}

$res = self::createClient()->request('GET', 'translate_validation_error', ['headers' => ['accept-language' => 'es']]);
$this->assertSame([
['propertyPath' => 'name', 'message' => 'Este valor no debería estar vacío.', 'code' => 'c1051bb4-d103-4f74-8988-acbcafc7fdc3'],
Expand Down
7 changes: 7 additions & 0 deletions tests/State/Provider/ObjectMapperProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@

class ObjectMapperProviderTest extends TestCase
{
protected function setUp(): void
{
if (!interface_exists(ObjectMapperInterface::class)) {
$this->markTestSkipped('ObjectMapper not installed');
}
}

public function testProvideBypassesWhenNoObjectMapper(): void
{
$data = new SourceEntity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
use Symfony\Component\Serializer\Mapping\Loader\LoaderChain;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
Expand All @@ -32,8 +33,17 @@
*/
final class ErrorResourceAttributeLoaderPassTest extends TestCase
{
private function skipOnSymfony64(): void
{
if (class_exists(AnnotationLoader::class)) {
$this->markTestSkipped('Symfony 6.4 AttributeLoader has a different constructor.');
}
}

public function testRegistersAttributeLoaderForErrorClassesInChainLoader(): void
{
$this->skipOnSymfony64();

$container = new ContainerBuilder();
$container->setDefinition('serializer.mapping.chain_loader', new Definition(LoaderChain::class, [[]]));
$container->setDefinition('serializer.mapping.cache_warmer', new Definition(\stdClass::class, [[]]));
Expand All @@ -58,6 +68,8 @@ public function testRegistersAttributeLoaderForErrorClassesInChainLoader(): void

public function testAlsoUpdatesCacheWarmer(): void
{
$this->skipOnSymfony64();

$container = new ContainerBuilder();
$container->setDefinition('serializer.mapping.chain_loader', new Definition(LoaderChain::class, [[]]));
$container->setDefinition('serializer.mapping.cache_warmer', new Definition(\stdClass::class, [[]]));
Expand Down Expand Up @@ -99,7 +111,7 @@ public function testDoesNothingWhenChainAlreadyContainsAnAttributeLoader(): void
*/
public function testSkipsOnSymfony64SerializerSignature(): void
{
if (!class_exists(\Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader::class)) {
if (!class_exists(AnnotationLoader::class)) {
$this->markTestSkipped('Only relevant when running against symfony/serializer 6.4 (AnnotationLoader still present).');
}

Expand All @@ -122,6 +134,8 @@ public function testSkipsOnSymfony64SerializerSignature(): void
*/
public function testErrorNormalizationStaysPopulatedWhenAttributesAreDisabled(): void
{
$this->skipOnSymfony64();

// Simulates `enable_attributes: false`: Symfony's attribute loader rejects every class.
$disabledAttributeLoader = new AttributeLoader(allowAnyClass: false, mappedClasses: []);

Expand Down Expand Up @@ -169,6 +183,8 @@ public function testErrorNormalizationStaysPopulatedWhenAttributesAreDisabled():

public function testTheCompilerPassDefinitionMatchesTheRuntimeExpectation(): void
{
$this->skipOnSymfony64();

$container = new ContainerBuilder();
$container->setDefinition('serializer.mapping.chain_loader', new Definition(LoaderChain::class, [[]]));
$container->setDefinition('serializer.mapping.cache_warmer', new Definition(\stdClass::class, [[]]));
Expand Down
5 changes: 5 additions & 0 deletions tests/Symfony/Bundle/Test/ApiTestCaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,11 @@ public function testExplicitContentTypeIsPreserved(): void

public function testBrowserKitAssertionsStayVerboseByDefault(): void
{
// @phpstan-ignore function.alreadyNarrowedType (the setter only exists since Symfony 7.3)
if (!method_exists(self::class, 'setBrowserKitAssertionsAsVerbose')) {
$this->markTestSkipped('BrowserKit assertions verbosity requires Symfony 7.3+.');
}

// The trait's static property is flattened into ApiTestCase (which directly uses the assertions trait)
// and shared with subclasses, so read it there rather than on the trait or this subclass.
$verboseMode = new \ReflectionProperty(ApiTestCase::class, 'defaultVerboseMode');
Expand Down
Loading