diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 94f8ba0..e6b184a 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -68,3 +68,27 @@ jobs: - name: Run code analysis run: | docker compose exec -T phpfpm vendor/bin/phpstan analyse --no-progress + + code-analysis-rector: + runs-on: ubuntu-latest + name: Rector + steps: + - uses: actions/checkout@v7 + + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- + + - name: Start docker compose setup and install site + run: | + docker network create frontend + docker compose pull --quiet + docker compose up --detach --wait + docker compose exec -T phpfpm composer install --no-interaction + + - name: Check for Rector suggestions + run: | + docker compose exec -T phpfpm vendor/bin/rector process --dry-run --no-progress-bar diff --git a/CHANGELOG.md b/CHANGELOG.md index 73b7891..2138a30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ See [keep a changelog] for information about writing changes to this log. ## [Unreleased] +- [PR-54](https://github.com/itk-dev/event-database-api/pull/54) + Add Rector (relevant sets aligned with PHPStan), apply it across the codebase, and run it in CI - [PR-53](https://github.com/itk-dev/event-database-api/pull/53) Compare effective schemas in the oasdiff gate (flatten-allof) so allOf/$ref restructures are not miscounted - [PR-52](https://github.com/itk-dev/event-database-api/pull/52) diff --git a/Taskfile.yml b/Taskfile.yml index 2cd8f36..b63cd08 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -165,6 +165,7 @@ tasks: desc: "Run code analysis" cmds: - task: code-analysis:phpstan + - task: code-analysis:rector silent: true code-analysis:phpstan: @@ -175,6 +176,22 @@ tasks: COMPOSE_ARGS: exec phpfpm vendor/bin/phpstan silent: true + code-analysis:rector: + desc: "Check for Rector suggestions (dry-run; no changes applied)" + cmds: + - task: compose + vars: + COMPOSE_ARGS: exec phpfpm vendor/bin/rector process --dry-run + silent: true + + code-analysis:rector:apply: + desc: "Apply Rector changes" + cmds: + - task: compose + vars: + COMPOSE_ARGS: exec phpfpm vendor/bin/rector process + silent: true + api:test: desc: Run API tests cmds: diff --git a/composer.json b/composer.json index 98a600d..c6eaeed 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,7 @@ "phpstan/phpstan-strict-rules": "^2.0", "phpstan/phpstan-symfony": "^2.0", "phpunit/phpunit": "^13", + "rector/rector": "^2.5", "symfony/browser-kit": "~7.4.0", "symfony/css-selector": "~7.4.0", "symfony/maker-bundle": "^1.52", diff --git a/composer.lock b/composer.lock index 2b423af..dbd433c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "48d72e29a5aeb508c05569314faf8904", + "content-hash": "0de130ed061bef0747c1862bd4a9072a", "packages": [ { "name": "api-platform/core", @@ -9155,6 +9155,66 @@ ], "time": "2024-06-11T12:45:25+00:00" }, + { + "name": "rector/rector", + "version": "2.5.4", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "adaa18d7cd6b3c960967cfbc98c03efb3116ac0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/adaa18d7cd6b3c960967cfbc98c03efb3116ac0e", + "reference": "adaa18d7cd6b3c960967cfbc98c03efb3116ac0e", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.2.2" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.5.4" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2026-07-06T12:41:46+00:00" + }, { "name": "sebastian/cli-parser", "version": "5.0.0", diff --git a/public/spec.yaml b/public/spec.yaml index 24d71eb..f3707c6 100644 --- a/public/spec.yaml +++ b/public/spec.yaml @@ -1780,10 +1780,10 @@ components: - type: object properties: - slug: - type: string name: type: string + slug: + type: string Vocabulary.jsonld: allOf: - @@ -1791,10 +1791,10 @@ components: - type: object properties: - slug: - type: string name: type: string + slug: + type: string description: type: string tags: diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..37ea484 --- /dev/null +++ b/rector.php @@ -0,0 +1,34 @@ +withPaths([ + __DIR__.'/src', + __DIR__.'/tests', + ]) + ->withCache(__DIR__.'/var/cache/rector') + // Coding style and import (`use`) management are owned by php-cs-fixer + // (PostToolUse hook / coding standards), so Rector does not touch them. + // + // Modernize to the language level declared in composer.json (PHP >= 8.3). + ->withPhpSets() + // Version-based upgrade + quality rules resolved from the installed package + // versions (Symfony 7.4, PHPUnit 13). No domain Doctrine here — the entities + // and migrations live in event-database-imports — so the Doctrine sets are + // intentionally omitted. + ->withComposerBased(symfony: true, phpunit: true) + // Convert any remaining annotations to native PHP attributes. + ->withAttributesSets(symfony: true, phpunit: true) + ->withPreparedSets( + deadCode: true, + codeQuality: true, + typeDeclarations: true, + privatization: true, + instanceOf: true, + earlyReturn: true, + symfonyCodeQuality: true, + phpunitCodeQuality: true, + ); diff --git a/src/Api/Dto/DailyOccurrence.php b/src/Api/Dto/DailyOccurrence.php index 0f3074a..5a61d15 100644 --- a/src/Api/Dto/DailyOccurrence.php +++ b/src/Api/Dto/DailyOccurrence.php @@ -1,5 +1,7 @@ name = $name; - $this->slug = $slug; + public function __construct( + public string $name, + #[ApiProperty(identifier: true)] + public string $slug, + ) { } } diff --git a/src/Api/Dto/Vocabulary.php b/src/Api/Dto/Vocabulary.php index b766b4b..8f88f82 100644 --- a/src/Api/Dto/Vocabulary.php +++ b/src/Api/Dto/Vocabulary.php @@ -1,5 +1,7 @@ name = $name; - $this->slug = $slug; - $this->description = $description; - $this->tags = $tags; + public function __construct( + public string $name, + #[ApiProperty( + identifier: true, + )] + public string $slug, + public string $description, + public array $tags, + ) { } } diff --git a/src/Api/Filter/ElasticSearch/BooleanFilter.php b/src/Api/Filter/ElasticSearch/BooleanFilter.php index 20e3fdc..e7fbe2a 100644 --- a/src/Api/Filter/ElasticSearch/BooleanFilter.php +++ b/src/Api/Filter/ElasticSearch/BooleanFilter.php @@ -1,5 +1,7 @@ $terms + ['boost' => 1.0]]; @@ -33,7 +43,7 @@ public function getDescription(string $resourceClass): array } $description = []; - foreach ($this->properties as $filterParameterName => $value) { + foreach (array_keys($this->properties) as $filterParameterName) { $description[$filterParameterName] = [ 'property' => $filterParameterName, 'type' => TypeIdentifier::BOOL->value, diff --git a/src/Api/Filter/ElasticSearch/DateRangeFilter.php b/src/Api/Filter/ElasticSearch/DateRangeFilter.php index 2dde5b4..9ba4657 100644 --- a/src/Api/Filter/ElasticSearch/DateRangeFilter.php +++ b/src/Api/Filter/ElasticSearch/DateRangeFilter.php @@ -52,7 +52,7 @@ public function apply(array $clauseBody, string $resourceClass, ?Operation $oper return $ranges; } - foreach ($this->properties as $property => $value) { + foreach (array_keys($this->properties) as $property) { if (isset($context['filters'][$property]) && '' !== $context['filters'][$property] && [] !== $context['filters'][$property]) { $ranges[] = $this->getElasticSearchQueryRanges($property, $context['filters'][$property]); } @@ -94,7 +94,7 @@ private function getElasticSearchQueryRanges(string $property, string|array $fil $value = $filter; } else { $operator = $this->resolveOperator((string) array_key_first($filter), $throwOnInvalid); - if (null === $operator) { + if (!$operator instanceof DateLimit) { return []; } $value = array_shift($filter); @@ -102,7 +102,7 @@ private function getElasticSearchQueryRanges(string $property, string|array $fil switch ($operator) { case DateLimit::between: - $values = explode('..', $value); + $values = explode('..', (string) $value); if (2 !== count($values)) { if ($throwOnInvalid) { diff --git a/src/Api/Filter/ElasticSearch/IdFilter.php b/src/Api/Filter/ElasticSearch/IdFilter.php index abf5fed..02858a2 100644 --- a/src/Api/Filter/ElasticSearch/IdFilter.php +++ b/src/Api/Filter/ElasticSearch/IdFilter.php @@ -1,5 +1,7 @@ properties as $filterParameterName => $value) { + foreach (array_keys($this->properties) as $filterParameterName) { $description[$filterParameterName] = [ 'property' => $filterParameterName, 'type' => TypeIdentifier::ARRAY->value, diff --git a/src/Api/Filter/ElasticSearch/MatchFilter.php b/src/Api/Filter/ElasticSearch/MatchFilter.php index 0cedc10..b6a4d02 100644 --- a/src/Api/Filter/ElasticSearch/MatchFilter.php +++ b/src/Api/Filter/ElasticSearch/MatchFilter.php @@ -1,5 +1,7 @@ properties as $filterParameterName => $value) { + foreach (array_keys($this->properties) as $filterParameterName) { $description[$filterParameterName] = [ 'property' => $filterParameterName, 'type' => TypeIdentifier::STRING->value, diff --git a/src/Api/Filter/ElasticSearch/TagFilter.php b/src/Api/Filter/ElasticSearch/TagFilter.php index d67a6d6..b31f4d2 100644 --- a/src/Api/Filter/ElasticSearch/TagFilter.php +++ b/src/Api/Filter/ElasticSearch/TagFilter.php @@ -1,5 +1,7 @@ $terms + ['boost' => 1.0]]; @@ -33,7 +43,7 @@ public function getDescription(string $resourceClass): array } $description = []; - foreach ($this->properties as $filterParameterName => $value) { + foreach (array_keys($this->properties) as $filterParameterName) { $description[$filterParameterName] = [ 'property' => $filterParameterName, 'type' => TypeIdentifier::ARRAY->value, diff --git a/src/Api/State/TagRepresentationProvider.php b/src/Api/State/TagRepresentationProvider.php index 02a23ab..54beb67 100644 --- a/src/Api/State/TagRepresentationProvider.php +++ b/src/Api/State/TagRepresentationProvider.php @@ -20,7 +20,7 @@ final class TagRepresentationProvider extends AbstractProvider implements Provid * @throws NotFoundExceptionInterface * @throws IndexException */ - public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object { if ($operation instanceof CollectionOperationInterface) { $filters = $this->getFilters($operation, $context); diff --git a/src/Api/State/VocabularyRepresentationProvider.php b/src/Api/State/VocabularyRepresentationProvider.php index 04c7e09..953f4a4 100644 --- a/src/Api/State/VocabularyRepresentationProvider.php +++ b/src/Api/State/VocabularyRepresentationProvider.php @@ -20,7 +20,7 @@ final class VocabularyRepresentationProvider extends AbstractProvider implements * @throws NotFoundExceptionInterface * @throws IndexException */ - public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object { if ($operation instanceof CollectionOperationInterface) { $filters = $this->getFilters($operation, $context); diff --git a/src/Command/FixturesLoadCommand.php b/src/Command/FixturesLoadCommand.php index 6bf1aef..34f84a9 100644 --- a/src/Command/FixturesLoadCommand.php +++ b/src/Command/FixturesLoadCommand.php @@ -40,9 +40,7 @@ protected function configure(): void InputArgument::REQUIRED, sprintf('Index to populate with fixture data (one of %s)', implode(', ', IndexName::values())), null, - function (CompletionInput $input): array { - return array_filter(IndexName::values(), fn ($item) => str_starts_with($item, $input->getCompletionValue())); - } + fn (CompletionInput $input): array => array_filter(IndexName::values(), fn ($item): bool => str_starts_with((string) $item, $input->getCompletionValue())) ) ->addOption('url', null, InputOption::VALUE_OPTIONAL, 'Remote url to read fixture data from', 'https://raw.githubusercontent.com/itk-dev/event-database-imports/develop/src/DataFixtures/indexes/[index].json'); } diff --git a/src/Controller/RootController.php b/src/Controller/RootController.php index 7642cfc..a6e8daf 100644 --- a/src/Controller/RootController.php +++ b/src/Controller/RootController.php @@ -1,14 +1,15 @@ redirectToRoute('api_doc'); diff --git a/src/Exception/AppException.php b/src/Exception/AppException.php index d4fa913..760351d 100644 --- a/src/Exception/AppException.php +++ b/src/Exception/AppException.php @@ -1,5 +1,7 @@ title() ->toString(); - $reason = explode(': ', $message->error->root_cause[0]->reason)[0]; + $reason = explode(': ', (string) $message->error->root_cause[0]->reason)[0]; return $type.': '.$reason; } diff --git a/src/Service/ElasticSearch/ElasticSearchPaginator.php b/src/Service/ElasticSearch/ElasticSearchPaginator.php index 91d8a81..f23ccc5 100644 --- a/src/Service/ElasticSearch/ElasticSearchPaginator.php +++ b/src/Service/ElasticSearch/ElasticSearchPaginator.php @@ -1,5 +1,7 @@ toArray(); - self::assertArrayHasKey('hydra:member', $data, $message); + $this->assertArrayHasKey('hydra:member', $data, $message); $actual = array_map( static fn (array $member) => $member[$field] ?? null, @@ -73,13 +75,13 @@ protected function assertMemberIds(array $expected, ResponseInterface $response, ); if ($ordered) { - self::assertSame($expected, $actual, $message); + $this->assertSame($expected, $actual, $message); return; } sort($expected); sort($actual); - self::assertSame($expected, $actual, $message); + $this->assertSame($expected, $actual, $message); } } diff --git a/tests/ApiPlatform/ApiTest.php b/tests/ApiPlatform/ApiTest.php index dccfa9e..42aac29 100644 --- a/tests/ApiPlatform/ApiTest.php +++ b/tests/ApiPlatform/ApiTest.php @@ -1,12 +1,14 @@ request('GET', '/'); + self::createClient()->request('GET', '/'); $this->assertResponseRedirects('/api/v2/docs'); } diff --git a/tests/ApiPlatform/AuthenticationTest.php b/tests/ApiPlatform/AuthenticationTest.php index fe773cf..17c391e 100644 --- a/tests/ApiPlatform/AuthenticationTest.php +++ b/tests/ApiPlatform/AuthenticationTest.php @@ -1,5 +1,7 @@ get([ 'event.tags' => 'ITKDev', 'event.publicAccess' => 'true', - 'start' => static::formatDateTime('2024-01-01'), - 'end' => static::formatDateTime('2024-12-31'), + 'start' => self::formatDateTime('2024-01-01'), + 'end' => self::formatDateTime('2024-12-31'), 'page' => 1, 'itemsPerPage' => 6, ]); diff --git a/tests/ApiPlatform/Consumer/Os2displayContractTest.php b/tests/ApiPlatform/Consumer/Os2displayContractTest.php index 62df9a6..5811f6b 100644 --- a/tests/ApiPlatform/Consumer/Os2displayContractTest.php +++ b/tests/ApiPlatform/Consumer/Os2displayContractTest.php @@ -1,5 +1,7 @@ get([ 'event.tags' => 'Koncert', 'event.location.entityId' => 4, - 'end' => ['gt' => static::formatDateTime('2024-12-01')], + 'end' => ['gt' => self::formatDateTime('2024-12-01')], 'itemsPerPage' => 20, 'page' => 1, ]); @@ -79,7 +81,7 @@ public function testEventSearchQuery(): void $response = $this->get([ 'title' => 'ITKDev', 'location.entityId' => 4, - 'occurrences.end' => ['gt' => static::formatDateTime('2024-01-01')], + 'occurrences.end' => ['gt' => self::formatDateTime('2024-01-01')], 'itemsPerPage' => 10, ], '/api/v2/events'); diff --git a/tests/ApiPlatform/Contract/CollectionContractTest.php b/tests/ApiPlatform/Contract/CollectionContractTest.php index 2263944..7c65a30 100644 --- a/tests/ApiPlatform/Contract/CollectionContractTest.php +++ b/tests/ApiPlatform/Contract/CollectionContractTest.php @@ -1,5 +1,7 @@ get([], $path)->toArray(); - self::assertNotEmpty($data['hydra:member'], $path.' needs at least one fixture member'); + $this->assertNotEmpty($data['hydra:member'], $path.' needs at least one fixture member'); foreach ($data['hydra:member'] as $i => $member) { $this->assertMatchesDefinition($member, $definition, $path.' member #'.$i); @@ -45,8 +47,8 @@ public function testItemMatchesSchema(string $path, string $definition, bool $co $data = $this->get([], $path)->toArray(); if ($collectionWrapped) { - self::assertArrayHasKey('hydra:member', $data, $path.' should return a collection wrapper (D6)'); - self::assertNotEmpty($data['hydra:member']); + $this->assertArrayHasKey('hydra:member', $data, $path.' should return a collection wrapper (D6)'); + $this->assertNotEmpty($data['hydra:member']); foreach ($data['hydra:member'] as $member) { $this->assertMatchesDefinition($member, $definition, $path.' item member'); } @@ -93,19 +95,16 @@ private function assertMatchesDefinition(array $payload, string $definition, str $validator->validate($data, (object) ['$ref' => self::SCHEMA_URI.'#/definitions/'.$definition]); - self::assertTrue( - $validator->isValid(), - $message.' failed contract schema "'.$definition.'": '.self::formatErrors($validator->getErrors()) - ); + $this->assertTrue($validator->isValid(), $message.' failed contract schema "'.$definition.'": '.$this->formatErrors($validator->getErrors())); } /** * @param array $errors */ - private static function formatErrors(array $errors): string + private function formatErrors(array $errors): string { return implode('; ', array_map( - static fn (array $e) => trim(($e['property'] ?? '').' '.($e['message'] ?? '')), + static fn (array $e): string => trim(($e['property'] ?? '').' '.($e['message'] ?? '')), $errors )); } diff --git a/tests/ApiPlatform/Contract/DateFormatContractTest.php b/tests/ApiPlatform/Contract/DateFormatContractTest.php index 5b25fef..eb49f13 100644 --- a/tests/ApiPlatform/Contract/DateFormatContractTest.php +++ b/tests/ApiPlatform/Contract/DateFormatContractTest.php @@ -1,5 +1,7 @@ [ - ['start[between]' => static::formatDateTime('2024-12-01').'..'.static::formatDateTime('2024-12-31')], + ['start[between]' => self::formatDateTime('2024-12-01').'..'.self::formatDateTime('2024-12-31')], [10, 12], ]; diff --git a/tests/ApiPlatform/DailyOccurrencesTest.php b/tests/ApiPlatform/DailyOccurrencesTest.php index 14c3163..c7a7883 100644 --- a/tests/ApiPlatform/DailyOccurrencesTest.php +++ b/tests/ApiPlatform/DailyOccurrencesTest.php @@ -1,12 +1,14 @@ [ - ['occurrences.start[between]' => static::formatDateTime('2001-01-01').'..'.static::formatDateTime('2100-01-01')], + ['occurrences.start[between]' => self::formatDateTime('2001-01-01').'..'.self::formatDateTime('2100-01-01')], [7, 8, 9], 'Every fixture event has an occurrence in the 21st century', ]; yield 'occurrences in 2026' => [ - ['occurrences.start[between]' => static::formatDateTime('2026-01-01').'..'.static::formatDateTime('2026-12-31')], + ['occurrences.start[between]' => self::formatDateTime('2026-01-01').'..'.self::formatDateTime('2026-12-31')], [9], 'Only event 9 has an occurrence in 2026', ]; // DateRangeFilter on updated (default operator: gte). yield 'updated on or after 2024' => [ - ['updated' => static::formatDateTime('2024-01-01')], + ['updated' => self::formatDateTime('2024-01-01')], [7, 8, 9], 'All events were updated on or after 2024-01-01', ]; yield 'updated after 2100' => [ - ['updated[gte]' => static::formatDateTime('2100-01-01')], + ['updated[gte]' => self::formatDateTime('2100-01-01')], [], 'No events updated after 2100', ]; @@ -64,8 +66,8 @@ public static function getEventsProvider(): iterable // a conscious, BC-visible decision. yield 'F7: start/end satisfied by different occurrences' => [ [ - 'occurrences.start[gte]' => static::formatDateTime('2024-12-01'), - 'occurrences.end[lte]' => static::formatDateTime('2024-11-30'), + 'occurrences.start[gte]' => self::formatDateTime('2024-12-01'), + 'occurrences.end[lte]' => self::formatDateTime('2024-11-30'), ], [8], 'Non-nested occurrences: event 8 matches although no single occurrence satisfies both bounds', @@ -91,7 +93,7 @@ public static function getEventsProvider(): iterable // Combined filters. yield 'occurrences in 2026 AND tagged aros' => [ [ - 'occurrences.start[between]' => static::formatDateTime('2026-01-01').'..'.static::formatDateTime('2026-12-31'), + 'occurrences.start[between]' => self::formatDateTime('2026-01-01').'..'.self::formatDateTime('2026-12-31'), 'tags' => 'aros', ], [9], diff --git a/tests/ApiPlatform/EventsTest.php b/tests/ApiPlatform/EventsTest.php index 9929897..6b0f0d4 100644 --- a/tests/ApiPlatform/EventsTest.php +++ b/tests/ApiPlatform/EventsTest.php @@ -1,5 +1,7 @@ [ - ['start[between]' => static::formatDateTime('2024-12-01').'..'.static::formatDateTime('2024-12-31')], + ['start[between]' => self::formatDateTime('2024-12-01').'..'.self::formatDateTime('2024-12-31')], [10, 12], 'Occurrences 10 and 12 start in December 2024', ]; yield 'start in November 2024' => [ - ['start[between]' => static::formatDateTime('2024-11-01').'..'.static::formatDateTime('2024-11-30')], + ['start[between]' => self::formatDateTime('2024-11-01').'..'.self::formatDateTime('2024-11-30')], [11], 'Only occurrence 11 starts in November 2024', ]; // DateRangeFilter on end. yield 'end around 2024-12-08' => [ - ['end[between]' => static::formatDateTime('2024-12-07').'..'.static::formatDateTime('2024-12-09')], + ['end[between]' => self::formatDateTime('2024-12-07').'..'.self::formatDateTime('2024-12-09')], [10, 12], 'Occurrences 10 and 12 end within 2024-12-07..2024-12-09', ]; diff --git a/tests/ApiPlatform/OccurrencesTest.php b/tests/ApiPlatform/OccurrencesTest.php index af7f48e..b516b84 100644 --- a/tests/ApiPlatform/OccurrencesTest.php +++ b/tests/ApiPlatform/OccurrencesTest.php @@ -1,12 +1,14 @@ assertSame($sorted, $titles, 'Events must be ordered by title.keyword ascending'); } public function testOccurrencesAreSortedByStartAscending(): void @@ -37,7 +39,7 @@ public function testOccurrencesAreSortedByStartAscending(): void $starts = array_column($response->toArray()['hydra:member'], 'start'); $sorted = $starts; sort($sorted, SORT_STRING); - self::assertSame($sorted, $starts, 'start values must be ascending'); + $this->assertSame($sorted, $starts, 'start values must be ascending'); } public function testDailyOccurrencesAreSortedByStartAscending(): void diff --git a/tests/ApiPlatform/TagsFilterTest.php b/tests/ApiPlatform/TagsFilterTest.php index cae92a1..f1a6d74 100644 --- a/tests/ApiPlatform/TagsFilterTest.php +++ b/tests/ApiPlatform/TagsFilterTest.php @@ -1,5 +1,7 @@ filterDependencies(); $filter = new BooleanFilter($names, $meta, $resolver, null, ['publicAccess' => null]); - self::assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); + $this->assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); } public static function applyProvider(): iterable @@ -46,8 +48,8 @@ public function testGetDescriptionShape(): void $description = $filter->getDescription(self::RESOURCE); - self::assertArrayHasKey('publicAccess', $description); - self::assertSame('bool', $description['publicAccess']['type']); - self::assertFalse($description['publicAccess']['required']); + $this->assertArrayHasKey('publicAccess', $description); + $this->assertSame('bool', $description['publicAccess']['type']); + $this->assertFalse($description['publicAccess']['required']); } } diff --git a/tests/Unit/Api/Filter/ElasticSearch/DateRangeFilterTest.php b/tests/Unit/Api/Filter/ElasticSearch/DateRangeFilterTest.php index 78cc677..3ea4dc9 100644 --- a/tests/Unit/Api/Filter/ElasticSearch/DateRangeFilterTest.php +++ b/tests/Unit/Api/Filter/ElasticSearch/DateRangeFilterTest.php @@ -1,5 +1,7 @@ ['limit' => DateLimit::gte, 'throwOnInvalid' => true]], ); - self::assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); + $this->assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); } public static function applyProvider(): iterable @@ -109,7 +111,7 @@ public function testThrowOnInvalidFalseSkipsInvalidInput(array $filters): void ['gte' => ['limit' => DateLimit::gte, 'throwOnInvalid' => false]], ); - self::assertSame([], $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); + $this->assertSame([], $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); } public static function invalidInputProvider(): iterable @@ -130,7 +132,7 @@ public function testGetDescriptionExposesEveryOperatorVariant(): void $description = $filter->getDescription(self::RESOURCE); foreach (['updated', 'updated[between]', 'updated[gt]', 'updated[gte]', 'updated[lt]', 'updated[lte]'] as $key) { - self::assertArrayHasKey($key, $description, $key.' should be described'); + $this->assertArrayHasKey($key, $description, $key.' should be described'); } } } diff --git a/tests/Unit/Api/Filter/ElasticSearch/IdFilterTest.php b/tests/Unit/Api/Filter/ElasticSearch/IdFilterTest.php index 0664ffa..29ca841 100644 --- a/tests/Unit/Api/Filter/ElasticSearch/IdFilterTest.php +++ b/tests/Unit/Api/Filter/ElasticSearch/IdFilterTest.php @@ -1,5 +1,7 @@ filterDependencies(); $filter = new IdFilter($names, $meta, $resolver, null, $properties); - self::assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); + $this->assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); } public static function applyProvider(): iterable @@ -62,7 +64,7 @@ public function testGetDescriptionShape(): void $description = $filter->getDescription(self::RESOURCE); - self::assertArrayHasKey('organizer.entityId', $description); - self::assertTrue($description['organizer.entityId']['is_collection']); + $this->assertArrayHasKey('organizer.entityId', $description); + $this->assertTrue($description['organizer.entityId']['is_collection']); } } diff --git a/tests/Unit/Api/Filter/ElasticSearch/MatchFilterTest.php b/tests/Unit/Api/Filter/ElasticSearch/MatchFilterTest.php index ce5afd7..2fe70ef 100644 --- a/tests/Unit/Api/Filter/ElasticSearch/MatchFilterTest.php +++ b/tests/Unit/Api/Filter/ElasticSearch/MatchFilterTest.php @@ -1,5 +1,7 @@ filterDependencies(); $filter = new MatchFilter($names, $meta, $resolver, null, $properties); - self::assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); + $this->assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); } public static function applyProvider(): iterable @@ -66,9 +68,9 @@ public function testGetDescriptionShape(): void $description = $filter->getDescription(self::RESOURCE); - self::assertArrayHasKey('title', $description); - self::assertSame('title', $description['title']['property']); - self::assertSame('string', $description['title']['type']); - self::assertFalse($description['title']['required']); + $this->assertArrayHasKey('title', $description); + $this->assertSame('title', $description['title']['property']); + $this->assertSame('string', $description['title']['type']); + $this->assertFalse($description['title']['required']); } } diff --git a/tests/Unit/Api/Filter/ElasticSearch/TagFilterTest.php b/tests/Unit/Api/Filter/ElasticSearch/TagFilterTest.php index 6db4173..73b685b 100644 --- a/tests/Unit/Api/Filter/ElasticSearch/TagFilterTest.php +++ b/tests/Unit/Api/Filter/ElasticSearch/TagFilterTest.php @@ -1,5 +1,7 @@ filterDependencies(); $filter = new TagFilter($names, $meta, $resolver, null, ['tags' => null]); - self::assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); + $this->assertSame($expected, $filter->apply([], self::RESOURCE, null, ['filters' => $filters])); } public static function applyProvider(): iterable @@ -46,7 +48,7 @@ public function testGetDescriptionShape(): void $description = $filter->getDescription(self::RESOURCE); - self::assertArrayHasKey('tags', $description); - self::assertTrue($description['tags']['is_collection']); + $this->assertArrayHasKey('tags', $description); + $this->assertTrue($description['tags']['is_collection']); } } diff --git a/tests/Unit/Service/ElasticSearch/ElasticIndexExceptionTest.php b/tests/Unit/Service/ElasticSearch/ElasticIndexExceptionTest.php index 271df2e..82d7300 100644 --- a/tests/Unit/Service/ElasticSearch/ElasticIndexExceptionTest.php +++ b/tests/Unit/Service/ElasticSearch/ElasticIndexExceptionTest.php @@ -1,5 +1,7 @@ ". public function test400ParsesElasticErrorMessage(): void @@ -20,11 +22,8 @@ public function test400ParsesElasticErrorMessage(): void $exception = new ElasticIndexException($raw, 400); - self::assertSame( - 'Parse exception: failed to parse date field [2004-02-12T15:19:21+0000]', - $exception->getMessage(), - ); - self::assertSame(400, $exception->getCode()); + $this->assertSame('Parse exception: failed to parse date field [2004-02-12T15:19:21+0000]', $exception->getMessage()); + $this->assertSame(400, $exception->getCode()); } // Goal: non-400 codes collapse to a generic message (no ES JSON to parse). @@ -32,6 +31,6 @@ public function testNon400CollapsesToBadRequest(): void { $exception = new ElasticIndexException('500 Internal Server Error: something', 500); - self::assertSame('Bad Request', $exception->getMessage()); + $this->assertSame('Bad Request', $exception->getMessage()); } } diff --git a/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php b/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php index c9eb3da..7ba3c20 100644 --- a/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php +++ b/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php @@ -1,5 +1,7 @@ buildParams(IndexName::Events->value, $this->noFilters(), 20, 5); - self::assertSame(IndexName::Events->value, $params['index']); - self::assertEquals(['match_all' => (object) []], $params['body']['query']); - self::assertSame(5, $params['body']['size']); - self::assertSame(20, $params['body']['from']); - self::assertArrayHasKey('sort', $params['body']); + $this->assertSame(IndexName::Events->value, $params['index']); + $this->assertEquals(['match_all' => (object) []], $params['body']['query']); + $this->assertSame(5, $params['body']['size']); + $this->assertSame(20, $params['body']['from']); + $this->assertArrayHasKey('sort', $params['body']); } // Goal: a single associative clause (e.g. a MatchFilter hit) is wrapped in bool/must. @@ -50,7 +52,7 @@ public function testSingleClauseWrappedInBoolMust(): void 10, ); - self::assertSame(['bool' => ['must' => [['match' => ['title' => 'x']]]]], $params['body']['query']); + $this->assertSame(['bool' => ['must' => [['match' => ['title' => 'x']]]]], $params['body']['query']); } // Goal: a list-shaped clause (e.g. IdFilter output) is flattened into must, not @@ -64,10 +66,7 @@ public function testListClauseIsFlattenedIntoMust(): void 10, ); - self::assertSame( - ['bool' => ['must' => [['terms' => ['organizer.entityId' => ['9'], 'boost' => 1.0]]]]], - $params['body']['query'], - ); + $this->assertSame(['bool' => ['must' => [['terms' => ['organizer.entityId' => ['9'], 'boost' => 1.0]]]]], $params['body']['query']); } // Goal: multiple clauses accumulate under a single bool/must. @@ -83,7 +82,7 @@ public function testMultipleClausesCombine(): void 10, ); - self::assertSame([ + $this->assertSame([ ['match' => ['title' => 'x']], ['terms' => ['tags' => ['aros'], 'boost' => 1.0]], ], $params['body']['query']['bool']['must']); @@ -95,7 +94,7 @@ public function testSortPerIndex(string $index, array $expectedSort): void { $params = (new SearchParamsBuilder())->buildParams($index, $this->noFilters(), 0, 10); - self::assertSame($expectedSort, $params['body']['sort']); + $this->assertSame($expectedSort, $params['body']['sort']); } public static function sortProvider(): iterable diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 47a5855..8e0530d 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,5 +1,7 @@