Update to PHPUnit 10

This commit is contained in:
Alejandro Celaya 2023-02-09 09:32:38 +01:00
parent ad44a8441a
commit 650a286982
131 changed files with 335 additions and 315 deletions

View File

@ -20,63 +20,62 @@
"akrabat/ip-address-middleware": "^2.1", "akrabat/ip-address-middleware": "^2.1",
"cakephp/chronos": "^2.3", "cakephp/chronos": "^2.3",
"doctrine/migrations": "^3.5", "doctrine/migrations": "^3.5",
"doctrine/orm": "^2.13.3", "doctrine/orm": "^2.14",
"endroid/qr-code": "^4.6", "endroid/qr-code": "^4.7",
"geoip2/geoip2": "^2.13", "geoip2/geoip2": "^2.13",
"guzzlehttp/guzzle": "^7.5", "guzzlehttp/guzzle": "^7.5",
"happyr/doctrine-specification": "^2.0", "happyr/doctrine-specification": "^2.0",
"jaybizzle/crawler-detect": "^1.2.112", "jaybizzle/crawler-detect": "^1.2.112",
"laminas/laminas-config": "^3.7", "laminas/laminas-config": "^3.8",
"laminas/laminas-config-aggregator": "^1.11", "laminas/laminas-config-aggregator": "^1.13",
"laminas/laminas-diactoros": "^2.19", "laminas/laminas-diactoros": "^2.24",
"laminas/laminas-inputfilter": "^2.22", "laminas/laminas-inputfilter": "^2.24",
"laminas/laminas-servicemanager": "^3.19", "laminas/laminas-servicemanager": "^3.20",
"laminas/laminas-stdlib": "^3.15", "laminas/laminas-stdlib": "^3.16",
"lcobucci/jwt": "^4.2", "lcobucci/jwt": "^4.3",
"league/uri": "^6.8", "league/uri": "^6.8",
"lstrojny/functional-php": "^1.17", "lstrojny/functional-php": "^1.17",
"mezzio/mezzio": "^3.13", "mezzio/mezzio": "^3.15",
"mezzio/mezzio-fastroute": "^3.7", "mezzio/mezzio-fastroute": "^3.8",
"mezzio/mezzio-problem-details": "^1.7", "mezzio/mezzio-problem-details": "^1.11",
"mezzio/mezzio-swoole": "^4.5", "mezzio/mezzio-swoole": "^4.6",
"mlocati/ip-lib": "^1.18", "mlocati/ip-lib": "^1.18",
"mobiledetect/mobiledetectlib": "^3.74", "mobiledetect/mobiledetectlib": "^3.74",
"ocramius/proxy-manager": "^2.14", "ocramius/proxy-manager": "^2.14",
"pagerfanta/core": "^3.6", "pagerfanta/core": "^3.7",
"php-middleware/request-id": "^4.1", "php-middleware/request-id": "^4.1",
"pugx/shortid-php": "^1.1", "pugx/shortid-php": "^1.1",
"ramsey/uuid": "^4.5", "ramsey/uuid": "^4.7",
"shlinkio/shlink-common": "^5.3.1", "shlinkio/shlink-common": "^5.3.1",
"shlinkio/shlink-config": "^2.4", "shlinkio/shlink-config": "^2.4",
"shlinkio/shlink-event-dispatcher": "^2.6", "shlinkio/shlink-event-dispatcher": "^2.6",
"shlinkio/shlink-importer": "^5.0", "shlinkio/shlink-importer": "^5.0",
"shlinkio/shlink-installer": "^8.3", "shlinkio/shlink-installer": "^8.3",
"shlinkio/shlink-ip-geolocation": "^3.2", "shlinkio/shlink-ip-geolocation": "^3.2",
"spiral/roadrunner": "^2.11", "spiral/roadrunner": "^2.12",
"spiral/roadrunner-jobs": "^2.5", "spiral/roadrunner-jobs": "^2.7",
"symfony/console": "^6.1", "symfony/console": "^6.2",
"symfony/filesystem": "^6.1", "symfony/filesystem": "^6.2",
"symfony/lock": "^6.1", "symfony/lock": "^6.2",
"symfony/process": "^6.1", "symfony/process": "^6.2",
"symfony/string": "^6.1" "symfony/string": "^6.2"
}, },
"require-dev": { "require-dev": {
"cebe/php-openapi": "^1.7", "cebe/php-openapi": "^1.7",
"devster/ubench": "^2.1", "devster/ubench": "^2.1",
"dms/phpunit-arraysubset-asserts": "^0.4.0", "infection/infection": "^0.26.19",
"infection/infection": "^0.26.15",
"openswoole/ide-helper": "~4.11.5", "openswoole/ide-helper": "~4.11.5",
"phpstan/phpstan": "^1.8", "phpstan/phpstan": "^1.9",
"phpstan/phpstan-doctrine": "^1.3", "phpstan/phpstan-doctrine": "^1.3",
"phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-phpunit": "^1.3",
"phpstan/phpstan-symfony": "^1.2", "phpstan/phpstan-symfony": "^1.2",
"phpunit/php-code-coverage": "^9.2", "phpunit/php-code-coverage": "^10.0",
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^10.0",
"roave/security-advisories": "dev-master", "roave/security-advisories": "dev-master",
"shlinkio/php-coding-standard": "~2.3.0", "shlinkio/php-coding-standard": "~2.3.0",
"shlinkio/shlink-test-utils": "^3.4", "shlinkio/shlink-test-utils": "^3.5",
"symfony/var-dumper": "^6.1", "symfony/var-dumper": "^6.2",
"veewee/composer-run-parallel": "^1.1" "veewee/composer-run-parallel": "^1.2"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -133,7 +132,7 @@
"test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-cli.xml --log-junit=build/coverage-cli/junit.xml", "test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-cli.xml --log-junit=build/coverage-cli/junit.xml",
"test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli", "test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli",
"test:cli:pretty": "GENERATE_COVERAGE=pretty composer test:cli", "test:cli:pretty": "GENERATE_COVERAGE=pretty composer test:cli",
"infect:ci:base": "infection --threads=max --only-covered --only-covering-test-cases --skip-initial-tests", "infect:ci:base": "infection --threads=max --only-covered --skip-initial-tests",
"infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=80", "infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=80",
"infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json5", "infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json5",
"infect:ci:api": "@infect:ci:base --coverage=build/coverage-api --min-msi=80 --configuration=infection-api.json5", "infect:ci:api": "@infect:ci:base --coverage=build/coverage-api --min-msi=80 --configuration=infection-api.json5",

View File

@ -23,7 +23,7 @@ class ListApiKeysTest extends CliTestCase
self::assertEquals(ExitCodes::EXIT_SUCCESS, $exitCode); self::assertEquals(ExitCodes::EXIT_SUCCESS, $exitCode);
} }
public function provideFlags(): iterable public static function provideFlags(): iterable
{ {
$expiredApiKeyDate = Chronos::now()->subDay()->startOfDay()->toAtomString(); $expiredApiKeyDate = Chronos::now()->subDay()->startOfDay()->toAtomString();
$enabledOnlyOutput = <<<OUT $enabledOnlyOutput = <<<OUT

View File

@ -19,7 +19,7 @@ class ListShortUrlsTest extends CliTestCase
self::assertStringContainsString($expectedOutput, $output); self::assertStringContainsString($expectedOutput, $output);
} }
public function provideFlagsAndOutput(): iterable public static function provideFlagsAndOutput(): iterable
{ {
// phpcs:disable Generic.Files.LineLength // phpcs:disable Generic.Files.LineLength
yield 'no flags' => [[], <<<OUTPUT yield 'no flags' => [[], <<<OUTPUT

View File

@ -32,24 +32,25 @@ class RoleResolverTest extends TestCase
* @dataProvider provideRoles * @dataProvider provideRoles
*/ */
public function properRolesAreResolvedBasedOnInput( public function properRolesAreResolvedBasedOnInput(
InputInterface $input, callable $createInput,
array $expectedRoles, array $expectedRoles,
int $expectedDomainCalls, int $expectedDomainCalls,
): void { ): void {
$input = $createInput($this);
$this->domainService->expects($this->exactly($expectedDomainCalls))->method('getOrCreate')->with( $this->domainService->expects($this->exactly($expectedDomainCalls))->method('getOrCreate')->with(
'example.com', 'example.com',
)->willReturn($this->domainWithId(Domain::withAuthority('example.com'))); )->willReturn(self::domainWithId(Domain::withAuthority('example.com')));
$result = $this->resolver->determineRoles($input); $result = $this->resolver->determineRoles($input);
self::assertEquals($expectedRoles, $result); self::assertEquals($expectedRoles, $result);
} }
public function provideRoles(): iterable public static function provideRoles(): iterable
{ {
$domain = $this->domainWithId(Domain::withAuthority('example.com')); $domain = self::domainWithId(Domain::withAuthority('example.com'));
$buildInput = function (array $definition): InputInterface { $buildInput = static fn (array $definition) => function (TestCase $test) use ($definition): InputInterface {
$input = $this->createStub(InputInterface::class); $input = $test->createStub(InputInterface::class);
$input->method('getOption')->willReturnMap( $input->method('getOption')->willReturnMap(
map($definition, static fn (mixed $returnValue, string $param) => [$param, $returnValue]), map($definition, static fn (mixed $returnValue, string $param) => [$param, $returnValue]),
); );
@ -114,7 +115,7 @@ class RoleResolverTest extends TestCase
$this->resolver->determineRoles($input); $this->resolver->determineRoles($input);
} }
private function domainWithId(Domain $domain): Domain private static function domainWithId(Domain $domain): Domain
{ {
$domain->setId('1'); $domain->setId('1');
return $domain; return $domain;

View File

@ -43,7 +43,7 @@ class ListKeysCommandTest extends TestCase
self::assertEquals($expected, $output); self::assertEquals($expected, $output);
} }
public function provideKeysAndOutputs(): iterable public static function provideKeysAndOutputs(): iterable
{ {
$dateInThePast = Chronos::createFromFormat('Y-m-d H:i:s', '2020-01-01 00:00:00'); $dateInThePast = Chronos::createFromFormat('Y-m-d H:i:s', '2020-01-01 00:00:00');
@ -84,14 +84,14 @@ class ListKeysCommandTest extends TestCase
yield 'with roles' => [ yield 'with roles' => [
[ [
$apiKey1 = ApiKey::create(), $apiKey1 = ApiKey::create(),
$apiKey2 = $this->apiKeyWithRoles([RoleDefinition::forAuthoredShortUrls()]), $apiKey2 = self::apiKeyWithRoles([RoleDefinition::forAuthoredShortUrls()]),
$apiKey3 = $this->apiKeyWithRoles( $apiKey3 = self::apiKeyWithRoles(
[RoleDefinition::forDomain($this->domainWithId(Domain::withAuthority('example.com')))], [RoleDefinition::forDomain(self::domainWithId(Domain::withAuthority('example.com')))],
), ),
$apiKey4 = ApiKey::create(), $apiKey4 = ApiKey::create(),
$apiKey5 = $this->apiKeyWithRoles([ $apiKey5 = self::apiKeyWithRoles([
RoleDefinition::forAuthoredShortUrls(), RoleDefinition::forAuthoredShortUrls(),
RoleDefinition::forDomain($this->domainWithId(Domain::withAuthority('example.com'))), RoleDefinition::forDomain(self::domainWithId(Domain::withAuthority('example.com'))),
]), ]),
$apiKey6 = ApiKey::create(), $apiKey6 = ApiKey::create(),
], ],
@ -141,7 +141,7 @@ class ListKeysCommandTest extends TestCase
]; ];
} }
private function apiKeyWithRoles(array $roles): ApiKey private static function apiKeyWithRoles(array $roles): ApiKey
{ {
$apiKey = ApiKey::create(); $apiKey = ApiKey::create();
foreach ($roles as $role) { foreach ($roles as $role) {
@ -151,7 +151,7 @@ class ListKeysCommandTest extends TestCase
return $apiKey; return $apiKey;
} }
private function domainWithId(Domain $domain): Domain private static function domainWithId(Domain $domain): Domain
{ {
$domain->setId('1'); $domain->setId('1');
return $domain; return $domain;

View File

@ -124,7 +124,7 @@ class CreateDatabaseCommandTest extends TestCase
self::assertStringContainsString('Database properly created!', $output); self::assertStringContainsString('Database properly created!', $output);
} }
public function provideEmptyDatabase(): iterable public static function provideEmptyDatabase(): iterable
{ {
yield 'no tables' => [[]]; yield 'no tables' => [[]];
yield 'migrations table' => [[MIGRATIONS_TABLE]]; yield 'migrations table' => [[MIGRATIONS_TABLE]];

View File

@ -60,7 +60,7 @@ class DomainRedirectsCommandTest extends TestCase
self::assertEquals(3, substr_count($output, '(Leave empty for no redirect)')); self::assertEquals(3, substr_count($output, '(Leave empty for no redirect)'));
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'no domain' => [null]; yield 'no domain' => [null];
yield 'domain without redirects' => [Domain::withAuthority('')]; yield 'domain without redirects' => [Domain::withAuthority('')];

View File

@ -57,7 +57,7 @@ class ListDomainsCommandTest extends TestCase
self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode());
} }
public function provideInputsAndOutputs(): iterable public static function provideInputsAndOutputs(): iterable
{ {
$withoutRedirectsOutput = <<<OUTPUT $withoutRedirectsOutput = <<<OUTPUT
+---------+------------+ +---------+------------+

View File

@ -139,7 +139,7 @@ class CreateShortUrlCommandTest extends TestCase
self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode());
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'no domain' => [[], null]; yield 'no domain' => [[], null];
yield 'non-default domain foo' => [['--domain' => 'foo.com'], 'foo.com']; yield 'non-default domain foo' => [['--domain' => 'foo.com'], 'foo.com'];
@ -166,7 +166,7 @@ class CreateShortUrlCommandTest extends TestCase
$this->commandTester->execute($options); $this->commandTester->execute($options);
} }
public function provideFlags(): iterable public static function provideFlags(): iterable
{ {
yield 'no flags' => [[], null]; yield 'no flags' => [[], null];
yield 'validate-url' => [['--validate-url' => true], true]; yield 'validate-url' => [['--validate-url' => true], true];

View File

@ -98,7 +98,7 @@ class DeleteShortUrlCommandTest extends TestCase
self::assertStringContainsString($expectedMessage, $output); self::assertStringContainsString($expectedMessage, $output);
} }
public function provideRetryDeleteAnswers(): iterable public static function provideRetryDeleteAnswers(): iterable
{ {
yield 'answering yes to retry' => [['yes'], 2, 'Short URL with short code "abc123" successfully deleted.']; yield 'answering yes to retry' => [['yes'], 2, 'Short URL with short code "abc123" successfully deleted.'];
yield 'answering no to retry' => [['no'], 1, 'Short URL was not deleted.']; yield 'answering no to retry' => [['no'], 1, 'Short URL was not deleted.'];

View File

@ -137,7 +137,7 @@ class ListShortUrlsCommandTest extends TestCase
} }
} }
public function provideOptionalFlags(): iterable public static function provideOptionalFlags(): iterable
{ {
$apiKey = ApiKey::fromMeta(ApiKeyMeta::withName('my api key')); $apiKey = ApiKey::fromMeta(ApiKeyMeta::withName('my api key'));
$key = $apiKey->toString(); $key = $apiKey->toString();
@ -200,7 +200,7 @@ class ListShortUrlsCommandTest extends TestCase
$this->commandTester->execute($commandArgs); $this->commandTester->execute($commandArgs);
} }
public function provideArgs(): iterable public static function provideArgs(): iterable
{ {
yield [[], 1, null, [], TagsMode::ANY->value]; yield [[], 1, null, [], TagsMode::ANY->value];
yield [['--page' => $page = 3], $page, null, [], TagsMode::ANY->value]; yield [['--page' => $page = 3], $page, null, [], TagsMode::ANY->value];
@ -255,7 +255,7 @@ class ListShortUrlsCommandTest extends TestCase
$this->commandTester->execute($commandArgs); $this->commandTester->execute($commandArgs);
} }
public function provideOrderBy(): iterable public static function provideOrderBy(): iterable
{ {
yield [[], null]; yield [[], null];
yield [['--order-by' => 'visits'], 'visits']; yield [['--order-by' => 'visits'], 'visits'];

View File

@ -61,7 +61,7 @@ class DownloadGeoLiteDbCommandTest extends TestCase
self::assertSame($expectedExitCode, $exitCode); self::assertSame($expectedExitCode, $exitCode);
} }
public function provideFailureParams(): iterable public static function provideFailureParams(): iterable
{ {
yield 'existing db' => [ yield 'existing db' => [
true, true,
@ -93,7 +93,7 @@ class DownloadGeoLiteDbCommandTest extends TestCase
self::assertSame(ExitCodes::EXIT_SUCCESS, $exitCode); self::assertSame(ExitCodes::EXIT_SUCCESS, $exitCode);
} }
public function provideSuccessParams(): iterable public static function provideSuccessParams(): iterable
{ {
yield 'up to date db' => [fn () => GeolocationResult::CHECK_SKIPPED, '[INFO] GeoLite2 db file is up to date.']; yield 'up to date db' => [fn () => GeolocationResult::CHECK_SKIPPED, '[INFO] GeoLite2 db file is up to date.'];
yield 'outdated db' => [function (callable $beforeDownload): GeolocationResult { yield 'outdated db' => [function (callable $beforeDownload): GeolocationResult {

View File

@ -100,7 +100,7 @@ class LocateVisitsCommandTest extends TestCase
} }
} }
public function provideArgs(): iterable public static function provideArgs(): iterable
{ {
yield 'no args' => [1, 0, 0, false, []]; yield 'no args' => [1, 0, 0, false, []];
yield 'retry' => [1, 1, 0, false, ['--retry' => true]]; yield 'retry' => [1, 1, 0, false, ['--retry' => true]];
@ -131,7 +131,7 @@ class LocateVisitsCommandTest extends TestCase
self::assertStringContainsString($message, $output); self::assertStringContainsString($message, $output);
} }
public function provideIgnoredAddresses(): iterable public static function provideIgnoredAddresses(): iterable
{ {
yield 'empty address' => [IpCannotBeLocatedException::forEmptyAddress(), 'Ignored visit with no IP address']; yield 'empty address' => [IpCannotBeLocatedException::forEmptyAddress(), 'Ignored visit with no IP address'];
yield 'localhost address' => [IpCannotBeLocatedException::forLocalhost(), 'Ignored localhost address']; yield 'localhost address' => [IpCannotBeLocatedException::forLocalhost(), 'Ignored localhost address'];
@ -226,7 +226,7 @@ class LocateVisitsCommandTest extends TestCase
$this->commandTester->execute(['--all' => true, '--retry' => true]); $this->commandTester->execute(['--all' => true, '--retry' => true]);
} }
public function provideAbortInputs(): iterable public static function provideAbortInputs(): iterable
{ {
yield 'n' => [['n']]; yield 'n' => [['n']];
yield 'no' => [['no']]; yield 'no' => [['no']];

View File

@ -46,7 +46,7 @@ class GeolocationDbUpdateFailedExceptionTest extends TestCase
self::assertEquals($prev, $e->getPrevious()); self::assertEquals($prev, $e->getPrevious());
} }
public function providePrev(): iterable public static function providePrev(): iterable
{ {
yield 'no prev' => [null]; yield 'no prev' => [null];
yield 'RuntimeException' => [new RuntimeException('prev')]; yield 'RuntimeException' => [new RuntimeException('prev')];

View File

@ -84,7 +84,7 @@ class GeolocationDbUpdaterTest extends TestCase
} }
} }
public function provideBigDays(): iterable public static function provideBigDays(): iterable
{ {
yield [36]; yield [36];
yield [50]; yield [50];
@ -109,7 +109,7 @@ class GeolocationDbUpdaterTest extends TestCase
self::assertEquals(GeolocationResult::DB_IS_UP_TO_DATE, $result); self::assertEquals(GeolocationResult::DB_IS_UP_TO_DATE, $result);
} }
public function provideSmallDays(): iterable public static function provideSmallDays(): iterable
{ {
$generateParamsWithTimestamp = static function (int $days) { $generateParamsWithTimestamp = static function (int $days) {
$timestamp = Chronos::now()->subDays($days)->getTimestamp(); $timestamp = Chronos::now()->subDays($days)->getTimestamp();
@ -164,7 +164,7 @@ class GeolocationDbUpdaterTest extends TestCase
self::assertEquals(GeolocationResult::CHECK_SKIPPED, $result); self::assertEquals(GeolocationResult::CHECK_SKIPPED, $result);
} }
public function provideTrackingOptions(): iterable public static function provideTrackingOptions(): iterable
{ {
yield 'disableTracking' => [new TrackingOptions(disableTracking: true)]; yield 'disableTracking' => [new TrackingOptions(disableTracking: true)];
yield 'disableIpTracking' => [new TrackingOptions(disableIpTracking: true)]; yield 'disableIpTracking' => [new TrackingOptions(disableIpTracking: true)];

View File

@ -22,7 +22,7 @@ class RedirectTest extends ApiTestCase
self::assertEquals($expectedRedirect, $response->getHeaderLine('Location')); self::assertEquals($expectedRedirect, $response->getHeaderLine('Location'));
} }
public function provideUserAgents(): iterable public static function provideUserAgents(): iterable
{ {
yield 'android' => [ANDROID_USER_AGENT, 'https://blog.alejandrocelaya.com/android']; yield 'android' => [ANDROID_USER_AGENT, 'https://blog.alejandrocelaya.com/android'];
yield 'ios' => [IOS_USER_AGENT, 'https://blog.alejandrocelaya.com/ios']; yield 'ios' => [IOS_USER_AGENT, 'https://blog.alejandrocelaya.com/ios'];

View File

@ -52,7 +52,7 @@ class TagsPaginatorAdapterTest extends DatabaseTestCase
self::assertEquals($expectedTotalCount, $adapter->getNbResults()); self::assertEquals($expectedTotalCount, $adapter->getNbResults());
} }
public function provideFilters(): iterable public static function provideFilters(): iterable
{ {
yield [null, null, 0, 10, ['another', 'bar', 'baz', 'foo'], 4]; yield [null, null, 0, 10, ['another', 'bar', 'baz', 'foo'], 4];
yield [null, null, 2, 10, ['baz', 'foo'], 4]; yield [null, null, 2, 10, ['baz', 'foo'], 4];

View File

@ -109,7 +109,7 @@ class TagRepositoryTest extends DatabaseTestCase
} }
} }
public function provideFilters(): iterable public static function provideFilters(): iterable
{ {
$defaultList = [ $defaultList = [
['another', 0, 0, 0], ['another', 0, 0, 0],

View File

@ -56,7 +56,7 @@ class VisitLocationRepositoryTest extends DatabaseTestCase
self::assertCount(6, [...$all]); self::assertCount(6, [...$all]);
} }
public function provideBlockSize(): iterable public static function provideBlockSize(): iterable
{ {
return map(range(1, 10), fn (int $value) => [$value]); return map(range(1, 10), fn (int $value) => [$value]);
} }

View File

@ -87,7 +87,7 @@ class QrCodeActionTest extends TestCase
self::assertEquals($expectedContentType, $resp->getHeaderLine('Content-Type')); self::assertEquals($expectedContentType, $resp->getHeaderLine('Content-Type'));
} }
public function provideQueries(): iterable public static function provideQueries(): iterable
{ {
yield 'no format, png default' => ['png', [], 'image/png']; yield 'no format, png default' => ['png', [], 'image/png'];
yield 'no format, svg default' => ['svg', [], 'image/svg+xml']; yield 'no format, svg default' => ['svg', [], 'image/svg+xml'];
@ -122,7 +122,7 @@ class QrCodeActionTest extends TestCase
self::assertEquals($expectedSize, $size); self::assertEquals($expectedSize, $size);
} }
public function provideRequestsWithSize(): iterable public static function provideRequestsWithSize(): iterable
{ {
yield 'different margin and size defaults' => [ yield 'different margin and size defaults' => [
new QrCodeOptions(size: 660, margin: 40), new QrCodeOptions(size: 660, margin: 40),
@ -215,7 +215,7 @@ class QrCodeActionTest extends TestCase
self::assertEquals($color, $expectedColor); self::assertEquals($color, $expectedColor);
} }
public function provideRoundBlockSize(): iterable public static function provideRoundBlockSize(): iterable
{ {
yield 'no round block param' => [new QrCodeOptions(), null, self::WHITE]; yield 'no round block param' => [new QrCodeOptions(), null, self::WHITE];
yield 'no round block param, but disabled by default' => [ yield 'no round block param, but disabled by default' => [

View File

@ -39,7 +39,7 @@ class RobotsActionTest extends TestCase
self::assertEquals('text/plain', $response->getHeaderLine('Content-Type')); self::assertEquals('text/plain', $response->getHeaderLine('Content-Type'));
} }
public function provideShortCodes(): iterable public static function provideShortCodes(): iterable
{ {
yield 'three short codes' => [['foo', 'bar', 'baz'], <<<ROBOTS yield 'three short codes' => [['foo', 'bar', 'baz'], <<<ROBOTS
# For more information about the robots.txt standard, see: # For more information about the robots.txt standard, see:

View File

@ -32,7 +32,7 @@ class EnvVarsTest extends TestCase
self::assertEquals($exists, $envVar->existsInEnv()); self::assertEquals($exists, $envVar->existsInEnv());
} }
public function provideExistingEnvVars(): iterable public static function provideExistingEnvVars(): iterable
{ {
yield 'DB_NAME' => [EnvVars::DB_NAME, true]; yield 'DB_NAME' => [EnvVars::DB_NAME, true];
yield 'BASE_PATH' => [EnvVars::BASE_PATH, true]; yield 'BASE_PATH' => [EnvVars::BASE_PATH, true];
@ -49,7 +49,7 @@ class EnvVarsTest extends TestCase
self::assertEquals($expected, $envVar->loadFromEnv($default)); self::assertEquals($expected, $envVar->loadFromEnv($default));
} }
public function provideEnvVarsValues(): iterable public static function provideEnvVarsValues(): iterable
{ {
yield 'DB_NAME without default' => [EnvVars::DB_NAME, 'shlink', null]; yield 'DB_NAME without default' => [EnvVars::DB_NAME, 'shlink', null];
yield 'DB_NAME with default' => [EnvVars::DB_NAME, 'shlink', 'foobar']; yield 'DB_NAME with default' => [EnvVars::DB_NAME, 'shlink', 'foobar'];

View File

@ -13,7 +13,6 @@ use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Log\NullLogger; use Psr\Log\NullLogger;
use Shlinkio\Shlink\Core\Action\RedirectAction; use Shlinkio\Shlink\Core\Action\RedirectAction;
use Shlinkio\Shlink\Core\Config\NotFoundRedirectResolver; use Shlinkio\Shlink\Core\Config\NotFoundRedirectResolver;
@ -21,6 +20,8 @@ use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions;
use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface; use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface;
use function Laminas\Stratigility\middleware;
class NotFoundRedirectResolverTest extends TestCase class NotFoundRedirectResolverTest extends TestCase
{ {
private NotFoundRedirectResolver $resolver; private NotFoundRedirectResolver $resolver;
@ -52,47 +53,47 @@ class NotFoundRedirectResolverTest extends TestCase
self::assertSame($expectedResp, $resp); self::assertSame($expectedResp, $resp);
} }
public function provideRedirects(): iterable public static function provideRedirects(): iterable
{ {
yield 'base URL with trailing slash' => [ yield 'base URL with trailing slash' => [
$uri = new Uri('/'), $uri = new Uri('/'),
$this->notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)),
new NotFoundRedirectOptions(baseUrl: 'baseUrl'), new NotFoundRedirectOptions(baseUrl: 'baseUrl'),
'baseUrl', 'baseUrl',
]; ];
yield 'base URL with domain placeholder' => [ yield 'base URL with domain placeholder' => [
$uri = new Uri('https://s.test'), $uri = new Uri('https://s.test'),
$this->notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)),
new NotFoundRedirectOptions(baseUrl: 'https://redirect-here.com/{DOMAIN}'), new NotFoundRedirectOptions(baseUrl: 'https://redirect-here.com/{DOMAIN}'),
'https://redirect-here.com/s.test', 'https://redirect-here.com/s.test',
]; ];
yield 'base URL with domain placeholder in query' => [ yield 'base URL with domain placeholder in query' => [
$uri = new Uri('https://s.test'), $uri = new Uri('https://s.test'),
$this->notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)),
new NotFoundRedirectOptions(baseUrl: 'https://redirect-here.com/?domain={DOMAIN}'), new NotFoundRedirectOptions(baseUrl: 'https://redirect-here.com/?domain={DOMAIN}'),
'https://redirect-here.com/?domain=s.test', 'https://redirect-here.com/?domain=s.test',
]; ];
yield 'base URL without trailing slash' => [ yield 'base URL without trailing slash' => [
$uri = new Uri(''), $uri = new Uri(''),
$this->notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)),
new NotFoundRedirectOptions(baseUrl: 'baseUrl'), new NotFoundRedirectOptions(baseUrl: 'baseUrl'),
'baseUrl', 'baseUrl',
]; ];
yield 'regular 404' => [ yield 'regular 404' => [
$uri = new Uri('/foo/bar'), $uri = new Uri('/foo/bar'),
$this->notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)),
new NotFoundRedirectOptions(regular404: 'regular404'), new NotFoundRedirectOptions(regular404: 'regular404'),
'regular404', 'regular404',
]; ];
yield 'regular 404 with path placeholder in query' => [ yield 'regular 404 with path placeholder in query' => [
$uri = new Uri('/foo/bar'), $uri = new Uri('/foo/bar'),
$this->notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)),
new NotFoundRedirectOptions(regular404: 'https://redirect-here.com/?path={ORIGINAL_PATH}'), new NotFoundRedirectOptions(regular404: 'https://redirect-here.com/?path={ORIGINAL_PATH}'),
'https://redirect-here.com/?path=%2Ffoo%2Fbar', 'https://redirect-here.com/?path=%2Ffoo%2Fbar',
]; ];
yield 'regular 404 with multiple placeholders' => [ yield 'regular 404 with multiple placeholders' => [
$uri = new Uri('https://s.test/foo/bar'), $uri = new Uri('https://s.test/foo/bar'),
$this->notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)), self::notFoundType(ServerRequestFactory::fromGlobals()->withUri($uri)),
new NotFoundRedirectOptions( new NotFoundRedirectOptions(
regular404: 'https://redirect-here.com/{ORIGINAL_PATH}/{DOMAIN}/?d={DOMAIN}&p={ORIGINAL_PATH}', regular404: 'https://redirect-here.com/{ORIGINAL_PATH}/{DOMAIN}/?d={DOMAIN}&p={ORIGINAL_PATH}',
), ),
@ -100,13 +101,13 @@ class NotFoundRedirectResolverTest extends TestCase
]; ];
yield 'invalid short URL' => [ yield 'invalid short URL' => [
new Uri('/foo'), new Uri('/foo'),
$this->notFoundType($this->requestForRoute(RedirectAction::class)), self::notFoundType(self::requestForRoute(RedirectAction::class)),
new NotFoundRedirectOptions(invalidShortUrl: 'invalidShortUrl'), new NotFoundRedirectOptions(invalidShortUrl: 'invalidShortUrl'),
'invalidShortUrl', 'invalidShortUrl',
]; ];
yield 'invalid short URL with path placeholder' => [ yield 'invalid short URL with path placeholder' => [
new Uri('/foo'), new Uri('/foo'),
$this->notFoundType($this->requestForRoute(RedirectAction::class)), self::notFoundType(self::requestForRoute(RedirectAction::class)),
new NotFoundRedirectOptions(invalidShortUrl: 'https://redirect-here.com/{ORIGINAL_PATH}'), new NotFoundRedirectOptions(invalidShortUrl: 'https://redirect-here.com/{ORIGINAL_PATH}'),
'https://redirect-here.com/foo', 'https://redirect-here.com/foo',
]; ];
@ -115,7 +116,7 @@ class NotFoundRedirectResolverTest extends TestCase
/** @test */ /** @test */
public function noResponseIsReturnedIfNoConditionsMatch(): void public function noResponseIsReturnedIfNoConditionsMatch(): void
{ {
$notFoundType = $this->notFoundType($this->requestForRoute('foo')); $notFoundType = self::notFoundType(self::requestForRoute('foo'));
$this->helper->expects($this->never())->method('buildRedirectResponse'); $this->helper->expects($this->never())->method('buildRedirectResponse');
$result = $this->resolver->resolveRedirectResponse($notFoundType, new NotFoundRedirectOptions(), new Uri()); $result = $this->resolver->resolveRedirectResponse($notFoundType, new NotFoundRedirectOptions(), new Uri());
@ -123,12 +124,12 @@ class NotFoundRedirectResolverTest extends TestCase
self::assertNull($result); self::assertNull($result);
} }
private function notFoundType(ServerRequestInterface $req): NotFoundType private static function notFoundType(ServerRequestInterface $req): NotFoundType
{ {
return NotFoundType::fromRequest($req, ''); return NotFoundType::fromRequest($req, '');
} }
private function requestForRoute(string $routeName): ServerRequestInterface private static function requestForRoute(string $routeName): ServerRequestInterface
{ {
return ServerRequestFactory::fromGlobals() return ServerRequestFactory::fromGlobals()
->withAttribute( ->withAttribute(
@ -136,7 +137,8 @@ class NotFoundRedirectResolverTest extends TestCase
RouteResult::fromRoute( RouteResult::fromRoute(
new Route( new Route(
'foo', 'foo',
$this->createMock(MiddlewareInterface::class), middleware(function (): void {
}),
['GET'], ['GET'],
$routeName, $routeName,
), ),

View File

@ -31,7 +31,7 @@ class BasePathPrefixerTest extends TestCase
self::assertEquals($expectedMiddlewares, $middlewares); self::assertEquals($expectedMiddlewares, $middlewares);
} }
public function provideConfig(): iterable public static function provideConfig(): iterable
{ {
yield 'with empty options' => [['routes' => []], [], []]; yield 'with empty options' => [['routes' => []], [], []];
yield 'with non-empty options' => [ yield 'with non-empty options' => [

View File

@ -25,7 +25,7 @@ class MultiSegmentSlugProcessorTest extends TestCase
self::assertEquals($expectedRoutes, ($this->processor)($config)['routes'] ?? []); self::assertEquals($expectedRoutes, ($this->processor)($config)['routes'] ?? []);
} }
public function provideConfigs(): iterable public static function provideConfigs(): iterable
{ {
yield [[], []]; yield [[], []];
yield [['url_shortener' => []], []]; yield [['url_shortener' => []], []];

View File

@ -29,7 +29,7 @@ class ShortUrlMethodsProcessorTest extends TestCase
self::assertEquals($expectedRoutes, ($this->processor)($config)['routes'] ?? null); self::assertEquals($expectedRoutes, ($this->processor)($config)['routes'] ?? null);
} }
public function provideConfigs(): iterable public static function provideConfigs(): iterable
{ {
$buildConfigWithStatus = static fn (int $status, ?array $expectedAllowedMethods) => [[ $buildConfigWithStatus = static fn (int $status, ?array $expectedAllowedMethods) => [[
'routes' => [ 'routes' => [

View File

@ -44,7 +44,7 @@ class DomainServiceTest extends TestCase
self::assertEquals($expectedResult, $result); self::assertEquals($expectedResult, $result);
} }
public function provideExcludedDomains(): iterable public static function provideExcludedDomains(): iterable
{ {
$default = DomainItem::forDefaultDomain('default.com', new EmptyNotFoundRedirectConfig()); $default = DomainItem::forDefaultDomain('default.com', new EmptyNotFoundRedirectConfig());
$adminApiKey = ApiKey::create(); $adminApiKey = ApiKey::create();
@ -190,7 +190,7 @@ class DomainServiceTest extends TestCase
self::assertEquals('baz.com', $result->invalidShortUrlRedirect()); self::assertEquals('baz.com', $result->invalidShortUrlRedirect());
} }
public function provideFoundDomains(): iterable public static function provideFoundDomains(): iterable
{ {
$domain = Domain::withAuthority(''); $domain = Domain::withAuthority('');
$adminApiKey = ApiKey::create(); $adminApiKey = ApiKey::create();

View File

@ -6,6 +6,7 @@ namespace ShlinkioTest\Shlink\Core\ErrorHandler;
use Laminas\Diactoros\Response; use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequestFactory; use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
@ -58,40 +59,39 @@ class NotFoundRedirectHandlerTest extends TestCase
self::assertSame($expectedResp, $result); self::assertSame($expectedResp, $result);
} }
public function provideNonRedirectScenarios(): iterable public static function provideNonRedirectScenarios(): iterable
{ {
yield 'no domain' => [function ( yield 'no domain' => [function (
MockObject&DomainServiceInterface $domainService, MockObject&DomainServiceInterface $domainService,
MockObject&NotFoundRedirectResolverInterface $resolver, MockObject&NotFoundRedirectResolverInterface $resolver,
): void { ): void {
$domainService->expects($this->once())->method('findByAuthority')->withAnyParameters()->willReturn( $domainService->expects(self::once())->method('findByAuthority')->withAnyParameters()->willReturn(
null, null,
); );
$resolver->expects($this->once())->method('resolveRedirectResponse')->with( $resolver->expects(self::once())->method('resolveRedirectResponse')->with(
$this->isInstanceOf(NotFoundType::class), self::isInstanceOf(NotFoundType::class),
$this->isInstanceOf(NotFoundRedirectOptions::class), self::isInstanceOf(NotFoundRedirectOptions::class),
$this->isInstanceOf(UriInterface::class), self::isInstanceOf(UriInterface::class),
)->willReturn(null); )->willReturn(null);
}]; }];
yield 'non-redirecting domain' => [function ( yield 'non-redirecting domain' => [function (
MockObject&DomainServiceInterface $domainService, MockObject&DomainServiceInterface $domainService,
MockObject&NotFoundRedirectResolverInterface $resolver, MockObject&NotFoundRedirectResolverInterface $resolver,
): void { ): void {
$domainService->expects($this->once())->method('findByAuthority')->withAnyParameters()->willReturn( $domainService->expects(self::once())->method('findByAuthority')->withAnyParameters()->willReturn(
Domain::withAuthority(''), Domain::withAuthority(''),
); );
$resolver->expects($this->exactly(2))->method('resolveRedirectResponse')->withConsecutive( $callCount = 0;
[ $resolver->expects(self::exactly(2))->method('resolveRedirectResponse')->willReturnCallback(
$this->isInstanceOf(NotFoundType::class), function (mixed $arg1, mixed $arg2, mixed $arg3) use (&$callCount) {
$this->isInstanceOf(Domain::class), Assert::assertInstanceOf(NotFoundType::class, $arg1);
$this->isInstanceOf(UriInterface::class), Assert::assertInstanceOf($callCount === 0 ? Domain::class : NotFoundRedirectOptions::class, $arg2);
], Assert::assertInstanceOf(UriInterface::class, $arg3);
[
$this->isInstanceOf(NotFoundType::class), $callCount++;
$this->isInstanceOf(NotFoundRedirectOptions::class), return null;
$this->isInstanceOf(UriInterface::class), },
], );
)->willReturn(null);
}]; }];
} }

View File

@ -11,11 +11,12 @@ use Mezzio\Router\Route;
use Mezzio\Router\RouteResult; use Mezzio\Router\RouteResult;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Shlinkio\Shlink\Core\Action\RedirectAction; use Shlinkio\Shlink\Core\Action\RedirectAction;
use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
use Shlinkio\Shlink\Core\ErrorHandler\NotFoundTemplateHandler; use Shlinkio\Shlink\Core\ErrorHandler\NotFoundTemplateHandler;
use function Laminas\Stratigility\middleware;
class NotFoundTemplateHandlerTest extends TestCase class NotFoundTemplateHandlerTest extends TestCase
{ {
private NotFoundTemplateHandler $handler; private NotFoundTemplateHandler $handler;
@ -44,19 +45,20 @@ class NotFoundTemplateHandlerTest extends TestCase
self::assertTrue($this->readFileCalled); self::assertTrue($this->readFileCalled);
} }
public function provideTemplates(): iterable public static function provideTemplates(): iterable
{ {
$request = ServerRequestFactory::fromGlobals()->withUri(new Uri('/foo')); $request = ServerRequestFactory::fromGlobals()->withUri(new Uri('/foo'));
yield 'base url' => [$this->withNotFoundType($request, '/foo'), NotFoundTemplateHandler::NOT_FOUND_TEMPLATE]; yield 'base url' => [self::withNotFoundType($request, '/foo'), NotFoundTemplateHandler::NOT_FOUND_TEMPLATE];
yield 'regular not found' => [$this->withNotFoundType($request), NotFoundTemplateHandler::NOT_FOUND_TEMPLATE]; yield 'regular not found' => [self::withNotFoundType($request), NotFoundTemplateHandler::NOT_FOUND_TEMPLATE];
yield 'invalid short code' => [ yield 'invalid short code' => [
$this->withNotFoundType($request->withAttribute( self::withNotFoundType($request->withAttribute(
RouteResult::class, RouteResult::class,
RouteResult::fromRoute( RouteResult::fromRoute(
new Route( new Route(
'foo', 'foo',
$this->createMock(MiddlewareInterface::class), middleware(function (): void {
}),
['GET'], ['GET'],
RedirectAction::class, RedirectAction::class,
), ),
@ -66,7 +68,7 @@ class NotFoundTemplateHandlerTest extends TestCase
]; ];
} }
private function withNotFoundType(ServerRequestInterface $req, string $baseUrl = ''): ServerRequestInterface private static function withNotFoundType(ServerRequestInterface $req, string $baseUrl = ''): ServerRequestInterface
{ {
$type = NotFoundType::fromRequest($req, $baseUrl); $type = NotFoundType::fromRequest($req, $baseUrl);
return $req->withAttribute(NotFoundType::class, $type); return $req->withAttribute(NotFoundType::class, $type);

View File

@ -46,7 +46,7 @@ class CloseDbConnectionEventListenerTest extends TestCase
self::assertTrue($wrappedWasCalled); self::assertTrue($wrappedWasCalled);
} }
public function provideWrapped(): iterable public static function provideWrapped(): iterable
{ {
yield 'does not throw exception' => (static function (): array { yield 'does not throw exception' => (static function (): array {
$wrappedWasCalled = false; $wrappedWasCalled = false;

View File

@ -146,7 +146,7 @@ class LocateVisitTest extends TestCase
self::assertEquals($visit->getVisitLocation(), VisitLocation::fromGeolocation(Location::emptyInstance())); self::assertEquals($visit->getVisitLocation(), VisitLocation::fromGeolocation(Location::emptyInstance()));
} }
public function provideNonLocatableVisits(): iterable public static function provideNonLocatableVisits(): iterable
{ {
$shortUrl = ShortUrl::createFake(); $shortUrl = ShortUrl::createFake();
@ -180,7 +180,7 @@ class LocateVisitTest extends TestCase
self::assertEquals($visit->getVisitLocation(), VisitLocation::fromGeolocation($location)); self::assertEquals($visit->getVisitLocation(), VisitLocation::fromGeolocation($location));
} }
public function provideIpAddresses(): iterable public static function provideIpAddresses(): iterable
{ {
yield 'no original IP address' => [ yield 'no original IP address' => [
Visit::forValidShortUrl(ShortUrl::createFake(), new Visitor('', '', '1.2.3.4', '')), Visit::forValidShortUrl(ShortUrl::createFake(), new Visitor('', '', '1.2.3.4', '')),

View File

@ -121,7 +121,7 @@ class NotifyVisitToMercureTest extends TestCase
($this->listener)(new VisitLocated($visitId)); ($this->listener)(new VisitLocated($visitId));
} }
public function provideOrphanVisits(): iterable public static function provideOrphanVisits(): iterable
{ {
$visitor = Visitor::emptyInstance(); $visitor = Visitor::emptyInstance();

View File

@ -120,13 +120,13 @@ class NotifyVisitToWebHooksTest extends TestCase
$this->createListener($webhooks)(new VisitLocated('1')); $this->createListener($webhooks)(new VisitLocated('1'));
} }
public function provideVisits(): iterable public static function provideVisits(): iterable
{ {
yield 'regular visit' => [ yield 'regular visit' => [
Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()), Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()),
['shortUrl', 'visit'], ['shortUrl', 'visit'],
]; ];
yield 'orphan visit' => [Visit::forBasePath(Visitor::emptyInstance()), ['visit'],]; yield 'orphan visit' => [Visit::forBasePath(Visitor::emptyInstance()), ['visit']];
} }
private function createListener(array $webhooks, bool $notifyOrphanVisits = true): NotifyVisitToWebHooks private function createListener(array $webhooks, bool $notifyOrphanVisits = true): NotifyVisitToWebHooks

View File

@ -77,7 +77,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
], $update->payload); ], $update->payload);
} }
public function provideMethod(): iterable public static function provideMethod(): iterable
{ {
yield 'newVisitUpdate' => ['newVisitUpdate', 'https://shlink.io/new-visit', 'the cool title']; yield 'newVisitUpdate' => ['newVisitUpdate', 'https://shlink.io/new-visit', 'the cool title'];
yield 'newShortUrlVisitUpdate' => ['newShortUrlVisitUpdate', 'https://shlink.io/new-visit/foo', null]; yield 'newShortUrlVisitUpdate' => ['newShortUrlVisitUpdate', 'https://shlink.io/new-visit/foo', null];
@ -105,7 +105,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
], $update->payload); ], $update->payload);
} }
public function provideOrphanVisits(): iterable public static function provideOrphanVisits(): iterable
{ {
$visitor = Visitor::emptyInstance(); $visitor = Visitor::emptyInstance();

View File

@ -102,7 +102,7 @@ class NotifyNewShortUrlToRabbitMqTest extends TestCase
($this->listener())(new ShortUrlCreated($shortUrlId)); ($this->listener())(new ShortUrlCreated($shortUrlId));
} }
public function provideExceptions(): iterable public static function provideExceptions(): iterable
{ {
yield [new RuntimeException('RuntimeException Error')]; yield [new RuntimeException('RuntimeException Error')];
yield [new Exception('Exception Error')]; yield [new Exception('Exception Error')];

View File

@ -91,7 +91,7 @@ class NotifyVisitToRabbitMqTest extends TestCase
($this->listener())(new VisitLocated($visitId)); ($this->listener())(new VisitLocated($visitId));
} }
public function provideVisits(): iterable public static function provideVisits(): iterable
{ {
$visitor = Visitor::emptyInstance(); $visitor = Visitor::emptyInstance();
@ -130,7 +130,7 @@ class NotifyVisitToRabbitMqTest extends TestCase
($this->listener())(new VisitLocated($visitId)); ($this->listener())(new VisitLocated($visitId));
} }
public function provideExceptions(): iterable public static function provideExceptions(): iterable
{ {
yield [new RuntimeException('RuntimeException Error')]; yield [new RuntimeException('RuntimeException Error')];
yield [new Exception('Exception Error')]; yield [new Exception('Exception Error')];
@ -155,14 +155,14 @@ class NotifyVisitToRabbitMqTest extends TestCase
($this->listener(new RabbitMqOptions(true, $legacy)))(new VisitLocated($visitId)); ($this->listener(new RabbitMqOptions(true, $legacy)))(new VisitLocated($visitId));
} }
public function provideLegacyPayloads(): iterable public static function provideLegacyPayloads(): iterable
{ {
yield 'legacy non-orphan visit' => [ yield 'legacy non-orphan visit' => [
true, true,
$visit = Visit::forValidShortUrl(ShortUrl::withLongUrl('longUrl'), Visitor::emptyInstance()), $visit = Visit::forValidShortUrl(ShortUrl::withLongUrl('longUrl'), Visitor::emptyInstance()),
noop(...), noop(...),
function (MockObject & PublishingHelperInterface $helper) use ($visit): void { function (MockObject & PublishingHelperInterface $helper) use ($visit): void {
$helper->method('publishUpdate')->with($this->callback(function (Update $update) use ($visit): bool { $helper->method('publishUpdate')->with(self::callback(function (Update $update) use ($visit): bool {
$payload = $update->payload; $payload = $update->payload;
Assert::assertEquals($payload, $visit->jsonSerialize()); Assert::assertEquals($payload, $visit->jsonSerialize());
Assert::assertArrayNotHasKey('visitedUrl', $payload); Assert::assertArrayNotHasKey('visitedUrl', $payload);
@ -179,7 +179,7 @@ class NotifyVisitToRabbitMqTest extends TestCase
Visit::forBasePath(Visitor::emptyInstance()), Visit::forBasePath(Visitor::emptyInstance()),
noop(...), noop(...),
function (MockObject & PublishingHelperInterface $helper): void { function (MockObject & PublishingHelperInterface $helper): void {
$helper->method('publishUpdate')->with($this->callback(function (Update $update): bool { $helper->method('publishUpdate')->with(self::callback(function (Update $update): bool {
$payload = $update->payload; $payload = $update->payload;
Assert::assertArrayHasKey('visitedUrl', $payload); Assert::assertArrayHasKey('visitedUrl', $payload);
Assert::assertArrayHasKey('type', $payload); Assert::assertArrayHasKey('type', $payload);
@ -193,14 +193,14 @@ class NotifyVisitToRabbitMqTest extends TestCase
Visit::forValidShortUrl(ShortUrl::withLongUrl('longUrl'), Visitor::emptyInstance()), Visit::forValidShortUrl(ShortUrl::withLongUrl('longUrl'), Visitor::emptyInstance()),
function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator): void { function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator): void {
$update = Update::forTopicAndPayload('', []); $update = Update::forTopicAndPayload('', []);
$updatesGenerator->expects($this->never())->method('newOrphanVisitUpdate'); $updatesGenerator->expects(self::never())->method('newOrphanVisitUpdate');
$updatesGenerator->expects($this->once())->method('newVisitUpdate')->withAnyParameters()->willReturn( $updatesGenerator->expects(self::once())->method('newVisitUpdate')->withAnyParameters()->willReturn(
$update, $update,
); );
$updatesGenerator->expects($this->once())->method('newShortUrlVisitUpdate')->willReturn($update); $updatesGenerator->expects(self::once())->method('newShortUrlVisitUpdate')->willReturn($update);
}, },
function (MockObject & PublishingHelperInterface $helper): void { function (MockObject & PublishingHelperInterface $helper): void {
$helper->expects($this->exactly(2))->method('publishUpdate')->with($this->isInstanceOf(Update::class)); $helper->expects(self::exactly(2))->method('publishUpdate')->with(self::isInstanceOf(Update::class));
}, },
]; ];
yield 'non-legacy orphan visit' => [ yield 'non-legacy orphan visit' => [
@ -208,12 +208,12 @@ class NotifyVisitToRabbitMqTest extends TestCase
Visit::forBasePath(Visitor::emptyInstance()), Visit::forBasePath(Visitor::emptyInstance()),
function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator): void { function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator): void {
$update = Update::forTopicAndPayload('', []); $update = Update::forTopicAndPayload('', []);
$updatesGenerator->expects($this->once())->method('newOrphanVisitUpdate')->willReturn($update); $updatesGenerator->expects(self::once())->method('newOrphanVisitUpdate')->willReturn($update);
$updatesGenerator->expects($this->never())->method('newVisitUpdate'); $updatesGenerator->expects(self::never())->method('newVisitUpdate');
$updatesGenerator->expects($this->never())->method('newShortUrlVisitUpdate'); $updatesGenerator->expects(self::never())->method('newShortUrlVisitUpdate');
}, },
function (MockObject & PublishingHelperInterface $helper): void { function (MockObject & PublishingHelperInterface $helper): void {
$helper->expects($this->once())->method('publishUpdate')->with($this->isInstanceOf(Update::class)); $helper->expects(self::once())->method('publishUpdate')->with(self::isInstanceOf(Update::class));
}, },
]; ];
} }

View File

@ -69,7 +69,7 @@ class NotifyNewShortUrlToRedisTest extends TestCase
$this->createListener()(new ShortUrlCreated($shortUrlId)); $this->createListener()(new ShortUrlCreated($shortUrlId));
} }
public function provideExceptions(): iterable public static function provideExceptions(): iterable
{ {
yield [new RuntimeException('RuntimeException Error')]; yield [new RuntimeException('RuntimeException Error')];
yield [new Exception('Exception Error')]; yield [new Exception('Exception Error')];

View File

@ -68,7 +68,7 @@ class NotifyVisitToRedisTest extends TestCase
$this->createListener()(new VisitLocated($visitId)); $this->createListener()(new VisitLocated($visitId));
} }
public function provideExceptions(): iterable public static function provideExceptions(): iterable
{ {
yield [new RuntimeException('RuntimeException Error')]; yield [new RuntimeException('RuntimeException Error')];
yield [new Exception('Exception Error')]; yield [new Exception('Exception Error')];

View File

@ -67,7 +67,7 @@ class UpdateGeoLiteDbTest extends TestCase
($this->listener)(); ($this->listener)();
} }
public function provideFlags(): iterable public static function provideFlags(): iterable
{ {
yield 'existing old db' => [true, 'Updating GeoLite2 db file...']; yield 'existing old db' => [true, 'Updating GeoLite2 db file...'];
yield 'not existing old db' => [false, 'Downloading GeoLite2 db file...']; yield 'not existing old db' => [false, 'Downloading GeoLite2 db file...'];
@ -101,7 +101,7 @@ class UpdateGeoLiteDbTest extends TestCase
($this->listener)(); ($this->listener)();
} }
public function provideDownloaded(): iterable public static function provideDownloaded(): iterable
{ {
yield [100, 0, true, null]; yield [100, 0, true, null];
yield [100, 0, false, null]; yield [100, 0, false, null];
@ -129,7 +129,7 @@ class UpdateGeoLiteDbTest extends TestCase
($this->listener)(); ($this->listener)();
} }
public function provideGeolocationResults(): iterable public static function provideGeolocationResults(): iterable
{ {
return map(GeolocationResult::cases(), static fn (GeolocationResult $value) => [ return map(GeolocationResult::cases(), static fn (GeolocationResult $value) => [
$value, $value,

View File

@ -41,7 +41,7 @@ class DeleteShortUrlExceptionTest extends TestCase
self::assertEquals(422, $e->getStatus()); self::assertEquals(422, $e->getStatus());
} }
public function provideThresholds(): array public static function provideThresholds(): array
{ {
return map(range(5, 50, 5), function (int $number) { return map(range(5, 50, 5), function (int $number) {
return [$number, $shortCode = generateRandomShortCode(6), sprintf( return [$number, $shortCode = generateRandomShortCode(6), sprintf(

View File

@ -29,7 +29,7 @@ class ForbiddenTagOperationExceptionTest extends TestCase
self::assertEquals(403, $e->getStatus()); self::assertEquals(403, $e->getStatus());
} }
public function provideExceptions(): iterable public static function provideExceptions(): iterable
{ {
yield 'deletion' => [ForbiddenTagOperationException::forDeletion(), 'You are not allowed to delete tags']; yield 'deletion' => [ForbiddenTagOperationException::forDeletion(), 'You are not allowed to delete tags'];
yield 'renaming' => [ForbiddenTagOperationException::forRenaming(), 'You are not allowed to rename tags']; yield 'renaming' => [ForbiddenTagOperationException::forRenaming(), 'You are not allowed to rename tags'];

View File

@ -34,7 +34,7 @@ class InvalidUrlExceptionTest extends TestCase
self::assertEquals($prev, $e->getPrevious()); self::assertEquals($prev, $e->getPrevious());
} }
public function providePrevious(): iterable public static function providePrevious(): iterable
{ {
yield 'null previous' => [null]; yield 'null previous' => [null];
yield 'instance previous' => [new Exception('Previous error', 10)]; yield 'instance previous' => [new Exception('Previous error', 10)];

View File

@ -53,7 +53,7 @@ class IpCannotBeLocatedExceptionTest extends TestCase
self::assertEquals(UnlocatableIpType::ERROR, $e->type); self::assertEquals(UnlocatableIpType::ERROR, $e->type);
} }
public function provideErrors(): iterable public static function provideErrors(): iterable
{ {
yield 'Simple exception with positive code' => [new Exception('Some message', 100)]; yield 'Simple exception with positive code' => [new Exception('Some message', 100)];
yield 'Runtime exception with negative code' => [new RuntimeException('Something went wrong', -50)]; yield 'Runtime exception with negative code' => [new RuntimeException('Something went wrong', -50)];

View File

@ -30,7 +30,7 @@ class NonUniqueSlugExceptionTest extends TestCase
self::assertEquals($expectedAdditional, $e->getAdditionalData()); self::assertEquals($expectedAdditional, $e->getAdditionalData());
} }
public function provideMessages(): iterable public static function provideMessages(): iterable
{ {
yield 'without domain' => [ yield 'without domain' => [
'Provided slug "foo" is already in use.', 'Provided slug "foo" is already in use.',

View File

@ -34,7 +34,7 @@ class ShortUrlNotFoundExceptionTest extends TestCase
self::assertEquals($expectedAdditional, $e->getAdditionalData()); self::assertEquals($expectedAdditional, $e->getAdditionalData());
} }
public function provideMessages(): iterable public static function provideMessages(): iterable
{ {
yield 'without domain' => [ yield 'without domain' => [
'No URL found with short code "abc123"', 'No URL found with short code "abc123"',

View File

@ -46,7 +46,7 @@ class ValidationExceptionTest extends TestCase
self::assertStringContainsString($expectedStringRepresentation, (string) $e); self::assertStringContainsString($expectedStringRepresentation, (string) $e);
} }
public function provideExceptions(): iterable public static function provideExceptions(): iterable
{ {
return [[null], [new RuntimeException()], [new LogicException()]]; return [[null], [new RuntimeException()], [new LogicException()]];
} }

View File

@ -26,7 +26,7 @@ class FunctionsTest extends TestCase
self::assertEquals($expectedValues, enumValues($enum)); self::assertEquals($expectedValues, enumValues($enum));
} }
public function provideEnums(): iterable public static function provideEnums(): iterable
{ {
yield EnvVars::class => [EnvVars::class, map(EnvVars::cases(), static fn (EnvVars $envVar) => $envVar->value)]; yield EnvVars::class => [EnvVars::class, map(EnvVars::cases(), static fn (EnvVars $envVar) => $envVar->value)];
yield VisitType::class => [ yield VisitType::class => [

View File

@ -188,7 +188,7 @@ class ImportedLinksProcessorTest extends TestCase
$this->processor->process($this->io, ImportResult::withShortUrls([$importedUrl]), $this->buildParams()); $this->processor->process($this->io, ImportResult::withShortUrls([$importedUrl]), $this->buildParams());
} }
public function provideUrlsWithVisits(): iterable public static function provideUrlsWithVisits(): iterable
{ {
$now = Chronos::now(); $now = Chronos::now();
$createImportedUrl = static fn (array $visits) => $createImportedUrl = static fn (array $visits) =>
@ -262,7 +262,7 @@ class ImportedLinksProcessorTest extends TestCase
); );
} }
public function provideOrphanVisits(): iterable public static function provideOrphanVisits(): iterable
{ {
yield 'import orphan disable without visits' => [false, [], null, 0]; yield 'import orphan disable without visits' => [false, [], null, 0];
yield 'import orphan enabled without visits' => [true, [], null, 0]; yield 'import orphan enabled without visits' => [true, [], null, 0];

View File

@ -41,7 +41,7 @@ class ShortUrlTest extends TestCase
$shortUrl->regenerateShortCode(ShortUrlMode::STRICT); $shortUrl->regenerateShortCode(ShortUrlMode::STRICT);
} }
public function provideInvalidShortUrls(): iterable public static function provideInvalidShortUrls(): iterable
{ {
yield 'with custom slug' => [ yield 'with custom slug' => [
ShortUrl::create(ShortUrlCreation::fromRawData(['customSlug' => 'custom-slug', 'longUrl' => 'longUrl'])), ShortUrl::create(ShortUrlCreation::fromRawData(['customSlug' => 'custom-slug', 'longUrl' => 'longUrl'])),
@ -68,7 +68,7 @@ class ShortUrlTest extends TestCase
self::assertNotEquals($firstShortCode, $secondShortCode); self::assertNotEquals($firstShortCode, $secondShortCode);
} }
public function provideValidShortUrls(): iterable public static function provideValidShortUrls(): iterable
{ {
yield 'no custom slug' => [ShortUrl::createFake()]; yield 'no custom slug' => [ShortUrl::createFake()];
yield 'imported with custom slug' => [ShortUrl::fromImport( yield 'imported with custom slug' => [ShortUrl::fromImport(
@ -90,7 +90,7 @@ class ShortUrlTest extends TestCase
self::assertEquals($expectedLength, strlen($shortUrl->getShortCode())); self::assertEquals($expectedLength, strlen($shortUrl->getShortCode()));
} }
public function provideLengths(): iterable public static function provideLengths(): iterable
{ {
yield [null, DEFAULT_SHORT_CODES_LENGTH]; yield [null, DEFAULT_SHORT_CODES_LENGTH];
yield from map(range(4, 10), fn (int $value) => [$value, $value]); yield from map(range(4, 10), fn (int $value) => [$value, $value]);

View File

@ -55,7 +55,7 @@ class ShortCodeUniquenessHelperTest extends TestCase
self::assertTrue($result); self::assertTrue($result);
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'no domain' => [null, null]; yield 'no domain' => [null, null];
yield 'domain' => [Domain::withAuthority($authority = 's.test'), $authority]; yield 'domain' => [Domain::withAuthority($authority = 's.test'), $authority];

View File

@ -50,7 +50,7 @@ class ShortUrlRedirectionBuilderTest extends TestCase
self::assertEquals($expectedUrl, $result); self::assertEquals($expectedUrl, $result);
} }
public function provideData(): iterable public static function provideData(): iterable
{ {
$request = static fn (array $query = []) => ServerRequestFactory::fromGlobals()->withQueryParams($query); $request = static fn (array $query = []) => ServerRequestFactory::fromGlobals()->withQueryParams($query);

View File

@ -26,7 +26,7 @@ class ShortUrlStringifierTest extends TestCase
self::assertEquals($expected, $stringifier->stringify($shortUrl)); self::assertEquals($expected, $stringifier->stringify($shortUrl));
} }
public function provideConfigAndShortUrls(): iterable public static function provideConfigAndShortUrls(): iterable
{ {
$shortUrlWithShortCode = fn (string $shortCode, ?string $domain = null) => ShortUrl::create( $shortUrlWithShortCode = fn (string $shortCode, ?string $domain = null) => ShortUrl::create(
ShortUrlCreation::fromRawData([ ShortUrlCreation::fromRawData([

View File

@ -42,7 +42,7 @@ class ShortUrlTitleResolutionHelperTest extends TestCase
); );
} }
public function provideTitles(): iterable public static function provideTitles(): iterable
{ {
yield 'no title' => [null, 1, 0]; yield 'no title' => [null, 1, 0];
yield 'title' => ['link title', 0, 1]; yield 'title' => ['link title', 0, 1];

View File

@ -12,7 +12,6 @@ use Mezzio\Router\RouteResult;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Core\Action\RedirectAction; use Shlinkio\Shlink\Core\Action\RedirectAction;
use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
@ -26,6 +25,7 @@ use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface;
use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface; use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface;
use Shlinkio\Shlink\Core\Visit\RequestTrackerInterface; use Shlinkio\Shlink\Core\Visit\RequestTrackerInterface;
use function Laminas\Stratigility\middleware;
use function str_starts_with; use function str_starts_with;
class ExtraPathRedirectMiddlewareTest extends TestCase class ExtraPathRedirectMiddlewareTest extends TestCase
@ -68,7 +68,7 @@ class ExtraPathRedirectMiddlewareTest extends TestCase
$this->middleware($options)->process($request, $this->handler); $this->middleware($options)->process($request, $this->handler);
} }
public function provideNonRedirectingRequests(): iterable public static function provideNonRedirectingRequests(): iterable
{ {
$baseReq = ServerRequestFactory::fromGlobals(); $baseReq = ServerRequestFactory::fromGlobals();
$buildReq = static fn (?NotFoundType $type): ServerRequestInterface => $buildReq = static fn (?NotFoundType $type): ServerRequestInterface =>
@ -84,7 +84,8 @@ class ExtraPathRedirectMiddlewareTest extends TestCase
RouteResult::class, RouteResult::class,
RouteResult::fromRoute(new Route( RouteResult::fromRoute(new Route(
'/foo', '/foo',
$this->createMock(MiddlewareInterface::class), middleware(function (): void {
}),
['GET'], ['GET'],
RedirectAction::class, RedirectAction::class,
)), )),
@ -170,7 +171,7 @@ class ExtraPathRedirectMiddlewareTest extends TestCase
$this->middleware($options)->process($request, $this->handler); $this->middleware($options)->process($request, $this->handler);
} }
public function provideResolves(): iterable public static function provideResolves(): iterable
{ {
yield [false, 1, '/bar/baz']; yield [false, 1, '/bar/baz'];
yield [true, 3, null]; yield [true, 3, null];

View File

@ -43,7 +43,7 @@ class TrimTrailingSlashMiddlewareTest extends TestCase
$this->middleware($trailingSlashEnabled)->process($inputRequest, $this->requestHandler); $this->middleware($trailingSlashEnabled)->process($inputRequest, $this->requestHandler);
} }
public function provideRequests(): iterable public static function provideRequests(): iterable
{ {
yield 'trailing slash disabled' => [ yield 'trailing slash disabled' => [
false, false,

View File

@ -31,7 +31,7 @@ class ShortUrlCreationTest extends TestCase
ShortUrlCreation::fromRawData($data); ShortUrlCreation::fromRawData($data);
} }
public function provideInvalidData(): iterable public static function provideInvalidData(): iterable
{ {
yield [[]]; yield [[]];
yield [[ yield [[
@ -136,7 +136,7 @@ class ShortUrlCreationTest extends TestCase
self::assertNull($creation->maxVisits); self::assertNull($creation->maxVisits);
} }
public function provideCustomSlugs(): iterable public static function provideCustomSlugs(): iterable
{ {
yield ['🔥', '🔥']; yield ['🔥', '🔥'];
yield ['🦣 🍅', '🦣-🍅']; yield ['🦣 🍅', '🦣-🍅'];
@ -175,7 +175,7 @@ class ShortUrlCreationTest extends TestCase
self::assertEquals($expectedTitle, $creation->title); self::assertEquals($expectedTitle, $creation->title);
} }
public function provideTitles(): iterable public static function provideTitles(): iterable
{ {
yield [null, null]; yield [null, null];
yield ['foo', 'foo']; yield ['foo', 'foo'];
@ -201,7 +201,7 @@ class ShortUrlCreationTest extends TestCase
self::assertSame($expectedDomain, $creation->domain); self::assertSame($expectedDomain, $creation->domain);
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'null domain' => [null, null]; yield 'null domain' => [null, null];
yield 'empty domain' => ['', null]; yield 'empty domain' => ['', null];

View File

@ -27,7 +27,7 @@ class ShortUrlEditionTest extends TestCase
self::assertEquals($expectedDevicesToRemove, $edition->devicesToRemove); self::assertEquals($expectedDevicesToRemove, $edition->devicesToRemove);
} }
public function provideDeviceLongUrls(): iterable public static function provideDeviceLongUrls(): iterable
{ {
yield 'null' => [null, [], []]; yield 'null' => [null, [], []];
yield 'empty' => [[], [], []]; yield 'empty' => [[], [], []];

View File

@ -18,7 +18,7 @@ class ShortUrlModeTest extends TestCase
self::assertSame($expected, ShortUrlMode::tryDeprecated($mode)); self::assertSame($expected, ShortUrlMode::tryDeprecated($mode));
} }
public function provideModes(): iterable public static function provideModes(): iterable
{ {
yield 'invalid' => ['invalid', null]; yield 'invalid' => ['invalid', null];
yield 'foo' => ['foo', null]; yield 'foo' => ['foo', null];

View File

@ -29,7 +29,7 @@ class DeviceLongUrlsValidatorTest extends TestCase
self::assertEquals(['NOT_ARRAY' => 'Provided value is not an array.'], $this->validator->getMessages()); self::assertEquals(['NOT_ARRAY' => 'Provided value is not an array.'], $this->validator->getMessages());
} }
public function provideNonArrayValues(): iterable public static function provideNonArrayValues(): iterable
{ {
yield 'int' => [0]; yield 'int' => [0];
yield 'float' => [100.45]; yield 'float' => [100.45];

View File

@ -79,7 +79,7 @@ class ShortUrlRepositoryAdapterTest extends TestCase
$adapter->getNbResults(); $adapter->getNbResults();
} }
public function provideFilteringArgs(): iterable public static function provideFilteringArgs(): iterable
{ {
yield []; yield [];
yield ['search']; yield ['search'];

View File

@ -55,7 +55,7 @@ class PersistenceShortUrlRelationResolverTest extends TestCase
self::assertEquals($authority, $result->authority); self::assertEquals($authority, $result->authority);
} }
public function provideFoundDomains(): iterable public static function provideFoundDomains(): iterable
{ {
$authority = 's.test'; $authority = 's.test';
@ -89,7 +89,7 @@ class PersistenceShortUrlRelationResolverTest extends TestCase
self::assertEquals($expectedTags, $result->toArray()); self::assertEquals($expectedTags, $result->toArray());
} }
public function provideTags(): iterable public static function provideTags(): iterable
{ {
yield 'no duplicated tags' => [['foo', 'bar', 'baz'], [new Tag('foo'), new Tag('bar'), new Tag('baz')]]; yield 'no duplicated tags' => [['foo', 'bar', 'baz'], [new Tag('foo'), new Tag('bar'), new Tag('baz')]];
yield 'duplicated tags' => [['foo', 'bar', 'bar'], [new Tag('foo'), new Tag('bar')]]; yield 'duplicated tags' => [['foo', 'bar', 'bar'], [new Tag('foo'), new Tag('bar')]];

View File

@ -34,7 +34,7 @@ class SimpleShortUrlRelationResolverTest extends TestCase
} }
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'empty domain' => [null]; yield 'empty domain' => [null];
yield 'non-empty domain' => ['domain.com']; yield 'non-empty domain' => ['domain.com'];

View File

@ -113,7 +113,7 @@ class ShortUrlResolverTest extends TestCase
$this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode)); $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode));
} }
public function provideDisabledShortUrls(): iterable public static function provideDisabledShortUrls(): iterable
{ {
$now = Chronos::now(); $now = Chronos::now();

View File

@ -8,6 +8,7 @@ use Cake\Chronos\Chronos;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder; use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
use PHPUnit\Framework\MockObject\Rule\InvokedCount;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\Model\DeviceType; use Shlinkio\Shlink\Core\Model\DeviceType;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
@ -93,23 +94,23 @@ class ShortUrlServiceTest extends TestCase
self::assertEquals($resolveDeviceLongUrls(), $shortUrl->deviceLongUrls()); self::assertEquals($resolveDeviceLongUrls(), $shortUrl->deviceLongUrls());
} }
public function provideShortUrlEdits(): iterable public static function provideShortUrlEdits(): iterable
{ {
yield 'no long URL' => [$this->never(), ShortUrlEdition::fromRawData([ yield 'no long URL' => [new InvokedCount(0), ShortUrlEdition::fromRawData([
'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(), 'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(),
'validUntil' => Chronos::parse('2017-01-05 00:00:00')->toAtomString(), 'validUntil' => Chronos::parse('2017-01-05 00:00:00')->toAtomString(),
'maxVisits' => 5, 'maxVisits' => 5,
]), null]; ]), null];
yield 'long URL and API key' => [$this->once(), ShortUrlEdition::fromRawData([ yield 'long URL and API key' => [new InvokedCount(1), ShortUrlEdition::fromRawData([
'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(), 'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(),
'maxVisits' => 10, 'maxVisits' => 10,
'longUrl' => 'modifiedLongUrl', 'longUrl' => 'modifiedLongUrl',
]), ApiKey::create()]; ]), ApiKey::create()];
yield 'long URL with validation' => [$this->once(), ShortUrlEdition::fromRawData([ yield 'long URL with validation' => [new InvokedCount(1), ShortUrlEdition::fromRawData([
'longUrl' => 'modifiedLongUrl', 'longUrl' => 'modifiedLongUrl',
'validateUrl' => true, 'validateUrl' => true,
]), null]; ]), null];
yield 'device redirects' => [$this->never(), ShortUrlEdition::fromRawData([ yield 'device redirects' => [new InvokedCount(0), ShortUrlEdition::fromRawData([
'deviceLongUrls' => [ 'deviceLongUrls' => [
DeviceType::IOS->value => 'iosLongUrl', DeviceType::IOS->value => 'iosLongUrl',
DeviceType::ANDROID->value => 'androidLongUrl', DeviceType::ANDROID->value => 'androidLongUrl',

View File

@ -33,7 +33,7 @@ class ShortUrlDataTransformerTest extends TestCase
self::assertEquals($expectedMeta, $meta); self::assertEquals($expectedMeta, $meta);
} }
public function provideShortUrls(): iterable public static function provideShortUrls(): iterable
{ {
$maxVisits = random_int(1, 1000); $maxVisits = random_int(1, 1000);
$now = Chronos::now(); $now = Chronos::now();

View File

@ -95,7 +95,7 @@ class UrlShortenerTest extends TestCase
self::assertSame($expected, $result); self::assertSame($expected, $result);
} }
public function provideExistingShortUrls(): iterable public static function provideExistingShortUrls(): iterable
{ {
$url = 'http://foo.com'; $url = 'http://foo.com';

View File

@ -74,7 +74,7 @@ class TagServiceTest extends TestCase
self::assertEquals($expected, $result->getCurrentPageResults()); self::assertEquals($expected, $result->getCurrentPageResults());
} }
public function provideApiKeysAndSearchTerm(): iterable public static function provideApiKeysAndSearchTerm(): iterable
{ {
yield 'no API key, no filter' => [ yield 'no API key, no filter' => [
null, null,
@ -156,7 +156,7 @@ class TagServiceTest extends TestCase
self::assertEquals($newName, (string) $tag); self::assertEquals($newName, (string) $tag);
} }
public function provideValidRenames(): iterable public static function provideValidRenames(): iterable
{ {
yield 'same names' => ['foo', 'foo', 1]; yield 'same names' => ['foo', 'foo', 1];
yield 'different names names' => ['foo', 'bar', 0]; yield 'different names names' => ['foo', 'bar', 0];

View File

@ -8,7 +8,7 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
trait ApiKeyHelpersTrait trait ApiKeyHelpersTrait
{ {
public function provideAdminApiKeys(): iterable public static function provideAdminApiKeys(): iterable
{ {
yield 'no API key' => [null]; yield 'no API key' => [null];
yield 'admin API key' => [ApiKey::create()]; yield 'admin API key' => [ApiKey::create()];

View File

@ -43,7 +43,7 @@ class DoctrineBatchHelperTest extends TestCase
} }
} }
public function provideIterables(): iterable public static function provideIterables(): iterable
{ {
yield [[], 100, 1]; yield [[], 100, 1];
yield [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3, 4]; yield [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3, 4];

View File

@ -33,7 +33,7 @@ class RedirectResponseHelperTest extends TestCase
self::assertEquals($expectedCacheControl ?? '', $response->getHeaderLine('Cache-Control')); self::assertEquals($expectedCacheControl ?? '', $response->getHeaderLine('Cache-Control'));
} }
public function provideRedirectConfigs(): iterable public static function provideRedirectConfigs(): iterable
{ {
yield 'status 302' => [302, 20, 302, null]; yield 'status 302' => [302, 20, 302, null];
yield 'status 307' => [307, 20, 307, null]; yield 'status 307' => [307, 20, 307, null];

View File

@ -22,7 +22,7 @@ class VisitLocationTest extends TestCase
self::assertEquals($isEmpty, $location->isEmpty()); self::assertEquals($isEmpty, $location->isEmpty());
} }
public function provideArgs(): iterable public static function provideArgs(): iterable
{ {
yield [['', '', '', '', 0.0, 0.0, ''], true]; yield [['', '', '', '', 0.0, 0.0, ''], true];
yield [['', '', '', '', 0.0, 0.0, 'dd'], false]; yield [['', '', '', '', 0.0, 0.0, 'dd'], false];

View File

@ -29,7 +29,7 @@ class VisitTest extends TestCase
], $visit->jsonSerialize()); ], $visit->jsonSerialize());
} }
public function provideUserAgents(): iterable public static function provideUserAgents(): iterable
{ {
yield 'Chrome' => [ yield 'Chrome' => [
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
@ -56,7 +56,7 @@ class VisitTest extends TestCase
self::assertEquals($expectedAddress, $visit->getRemoteAddr()); self::assertEquals($expectedAddress, $visit->getRemoteAddr());
} }
public function provideAddresses(): iterable public static function provideAddresses(): iterable
{ {
yield 'anonymized null address' => [true, null, null]; yield 'anonymized null address' => [true, null, null];
yield 'non-anonymized null address' => [false, null, null]; yield 'non-anonymized null address' => [false, null, null];

View File

@ -72,7 +72,7 @@ class VisitLocatorTest extends TestCase
}); });
} }
public function provideMethodNames(): iterable public static function provideMethodNames(): iterable
{ {
yield 'locateUnlocatedVisits' => ['locateUnlocatedVisits', 'findUnlocatedVisits']; yield 'locateUnlocatedVisits' => ['locateUnlocatedVisits', 'findUnlocatedVisits'];
yield 'locateVisitsWithEmptyLocation' => ['locateVisitsWithEmptyLocation', 'findVisitsWithEmptyLocation']; yield 'locateVisitsWithEmptyLocation' => ['locateVisitsWithEmptyLocation', 'findVisitsWithEmptyLocation'];
@ -120,7 +120,7 @@ class VisitLocatorTest extends TestCase
); );
} }
public function provideIsNonLocatableAddress(): iterable public static function provideIsNonLocatableAddress(): iterable
{ {
yield 'locateUnlocatedVisits - locatable address' => ['locateUnlocatedVisits', 'findUnlocatedVisits', false]; yield 'locateUnlocatedVisits - locatable address' => ['locateUnlocatedVisits', 'findUnlocatedVisits', false];
yield 'locateUnlocatedVisits - non-locatable address' => ['locateUnlocatedVisits', 'findUnlocatedVisits', true]; yield 'locateUnlocatedVisits - non-locatable address' => ['locateUnlocatedVisits', 'findUnlocatedVisits', true];

View File

@ -39,7 +39,7 @@ class VisitToLocationHelperTest extends TestCase
$this->helper->resolveVisitLocation($visit); $this->helper->resolveVisitLocation($visit);
} }
public function provideNonLocatableVisits(): iterable public static function provideNonLocatableVisits(): iterable
{ {
yield [Visit::forBasePath(Visitor::emptyInstance()), IpCannotBeLocatedException::forEmptyAddress()]; yield [Visit::forBasePath(Visitor::emptyInstance()), IpCannotBeLocatedException::forEmptyAddress()];
yield [ yield [

View File

@ -29,7 +29,7 @@ class VisitorTest extends TestCase
self::assertEquals($remoteAddress, $visitor->remoteAddress); self::assertEquals($remoteAddress, $visitor->remoteAddress);
} }
public function provideParams(): iterable public static function provideParams(): iterable
{ {
yield 'all values are bigger' => [ yield 'all values are bigger' => [
[str_repeat('a', 1000), str_repeat('b', 2000), str_repeat('c', 500), ''], [str_repeat('a', 1000), str_repeat('b', 2000), str_repeat('c', 500), ''],
@ -49,8 +49,8 @@ class VisitorTest extends TestCase
]; ];
yield 'random strings' => [ yield 'random strings' => [
[ [
$userAgent = $this->generateRandomString(2000), $userAgent = self::generateRandomString(2000),
$referer = $this->generateRandomString(50), $referer = self::generateRandomString(50),
null, null,
'', '',
], ],
@ -62,7 +62,7 @@ class VisitorTest extends TestCase
]; ];
} }
private function generateRandomString(int $length): string private static function generateRandomString(int $length): string
{ {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters); $charactersLength = strlen($characters);
@ -77,10 +77,10 @@ class VisitorTest extends TestCase
public function newNormalizedInstanceIsCreatedFromTrackingOptions(): void public function newNormalizedInstanceIsCreatedFromTrackingOptions(): void
{ {
$visitor = new Visitor( $visitor = new Visitor(
$this->generateRandomString(2000), self::generateRandomString(2000),
$this->generateRandomString(2000), self::generateRandomString(2000),
$this->generateRandomString(2000), self::generateRandomString(2000),
$this->generateRandomString(2000), self::generateRandomString(2000),
); );
$normalizedVisitor = $visitor->normalizeForTrackingOptions(new TrackingOptions( $normalizedVisitor = $visitor->normalizeForTrackingOptions(new TrackingOptions(
disableIpTracking: true, disableIpTracking: true,

View File

@ -67,7 +67,7 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase
self::assertEquals($list, $result); self::assertEquals($list, $result);
} }
public function provideLimitAndOffset(): iterable public static function provideLimitAndOffset(): iterable
{ {
yield [1, 5]; yield [1, 5];
yield [10, 4]; yield [10, 4];

View File

@ -59,7 +59,7 @@ class OrphanVisitsPaginatorAdapterTest extends TestCase
self::assertEquals($list, $result); self::assertEquals($list, $result);
} }
public function provideLimitAndOffset(): iterable public static function provideLimitAndOffset(): iterable
{ {
yield [1, 5]; yield [1, 5];
yield [10, 4]; yield [10, 4];

View File

@ -57,7 +57,7 @@ class RequestTrackerTest extends TestCase
$this->requestTracker->trackIfApplicable($shortUrl, $request); $this->requestTracker->trackIfApplicable($shortUrl, $request);
} }
public function provideNonTrackingRequests(): iterable public static function provideNonTrackingRequests(): iterable
{ {
yield 'forwarded from head' => [ServerRequestFactory::fromGlobals()->withAttribute( yield 'forwarded from head' => [ServerRequestFactory::fromGlobals()->withAttribute(
ImplicitHeadMiddleware::FORWARDED_HTTP_METHOD_ATTRIBUTE, ImplicitHeadMiddleware::FORWARDED_HTTP_METHOD_ATTRIBUTE,

View File

@ -34,7 +34,7 @@ class OrphanVisitDataTransformerTest extends TestCase
self::assertEquals($expectedResult, $result); self::assertEquals($expectedResult, $result);
} }
public function provideVisits(): iterable public static function provideVisits(): iterable
{ {
yield 'base path visit' => [ yield 'base path visit' => [
$visit = Visit::forBasePath(Visitor::emptyInstance()), $visit = Visit::forBasePath(Visitor::emptyInstance()),

View File

@ -6,6 +6,7 @@ namespace ShlinkioTest\Shlink\Core\Visit;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Laminas\Stdlib\ArrayUtils; use Laminas\Stdlib\ArrayUtils;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Entity\Domain;
@ -53,13 +54,17 @@ class VisitsStatsHelperTest extends TestCase
public function returnsExpectedVisitsStats(int $expectedCount): void public function returnsExpectedVisitsStats(int $expectedCount): void
{ {
$repo = $this->createMock(VisitRepository::class); $repo = $this->createMock(VisitRepository::class);
$repo->expects($this->exactly(2))->method('countNonOrphanVisits')->withConsecutive( $callCount = 0;
[new VisitsCountFiltering()], $repo->expects($this->exactly(2))->method('countNonOrphanVisits')->willReturnCallback(
[new VisitsCountFiltering(excludeBots: true)], function (VisitsCountFiltering $options) use ($expectedCount, &$callCount) {
)->willReturn($expectedCount * 3); Assert::assertEquals($callCount !== 0, $options->excludeBots);
$repo->expects($this->exactly(2))->method('countOrphanVisits')->withConsecutive( $callCount++;
[$this->isInstanceOf(VisitsCountFiltering::class)],
[$this->isInstanceOf(VisitsCountFiltering::class)], return $expectedCount * 3;
},
);
$repo->expects($this->exactly(2))->method('countOrphanVisits')->with(
$this->isInstanceOf(VisitsCountFiltering::class),
)->willReturn($expectedCount); )->willReturn($expectedCount);
$this->em->expects($this->once())->method('getRepository')->with(Visit::class)->willReturn($repo); $this->em->expects($this->once())->method('getRepository')->with(Visit::class)->willReturn($repo);
@ -68,7 +73,7 @@ class VisitsStatsHelperTest extends TestCase
self::assertEquals(new VisitsStats($expectedCount * 3, $expectedCount), $stats); self::assertEquals(new VisitsStats($expectedCount * 3, $expectedCount), $stats);
} }
public function provideCounts(): iterable public static function provideCounts(): iterable
{ {
return map(range(0, 50, 5), fn (int $value) => [$value]); return map(range(0, 50, 5), fn (int $value) => [$value]);
} }

View File

@ -56,7 +56,7 @@ class VisitsTrackerTest extends TestCase
$this->visitsTracker(new TrackingOptions(disableTracking: true))->{$method}(...$args); $this->visitsTracker(new TrackingOptions(disableTracking: true))->{$method}(...$args);
} }
public function provideTrackingMethodNames(): iterable public static function provideTrackingMethodNames(): iterable
{ {
yield 'track' => ['track', [ShortUrl::createFake(), Visitor::emptyInstance()]]; yield 'track' => ['track', [ShortUrl::createFake(), Visitor::emptyInstance()]];
yield 'trackInvalidShortUrlVisit' => ['trackInvalidShortUrlVisit', [Visitor::emptyInstance()]]; yield 'trackInvalidShortUrlVisit' => ['trackInvalidShortUrlVisit', [Visitor::emptyInstance()]];
@ -77,7 +77,7 @@ class VisitsTrackerTest extends TestCase
$this->visitsTracker(new TrackingOptions(trackOrphanVisits: false))->{$method}(Visitor::emptyInstance()); $this->visitsTracker(new TrackingOptions(trackOrphanVisits: false))->{$method}(Visitor::emptyInstance());
} }
public function provideOrphanTrackingMethodNames(): iterable public static function provideOrphanTrackingMethodNames(): iterable
{ {
yield 'trackInvalidShortUrlVisit' => ['trackInvalidShortUrlVisit']; yield 'trackInvalidShortUrlVisit' => ['trackInvalidShortUrlVisit'];
yield 'trackBaseUrlVisit' => ['trackBaseUrlVisit']; yield 'trackBaseUrlVisit' => ['trackBaseUrlVisit'];

View File

@ -72,7 +72,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals($expectedType, $payload['type']); self::assertEquals($expectedType, $payload['type']);
} }
public function provideDuplicatedSlugApiVersions(): iterable public static function provideDuplicatedSlugApiVersions(): iterable
{ {
yield ['1', 'INVALID_SLUG']; yield ['1', 'INVALID_SLUG'];
yield ['2', 'INVALID_SLUG']; yield ['2', 'INVALID_SLUG'];
@ -91,7 +91,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals($expectedTags, $tags); self::assertEquals($expectedTags, $tags);
} }
public function provideTags(): iterable public static function provideTags(): iterable
{ {
yield 'simple tags' => [$simpleTags = ['foo', 'bar', 'baz'], $simpleTags]; yield 'simple tags' => [$simpleTags = ['foo', 'bar', 'baz'], $simpleTags];
yield 'tags with spaces' => [['fo o', ' bar', 'b az'], ['fo-o', 'bar', 'b-az']]; yield 'tags with spaces' => [['fo o', ' bar', 'b az'], ['fo-o', 'bar', 'b-az']];
@ -116,7 +116,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals(self::STATUS_NOT_FOUND, $lastResp->getStatusCode()); self::assertEquals(self::STATUS_NOT_FOUND, $lastResp->getStatusCode());
} }
public function provideMaxVisits(): array public static function provideMaxVisits(): array
{ {
return map(range(10, 15), fn(int $i) => [$i]); return map(range(10, 15), fn(int $i) => [$i]);
} }
@ -165,7 +165,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals($firstShortCode, $secondShortCode); self::assertEquals($firstShortCode, $secondShortCode);
} }
public function provideMatchingBodies(): iterable public static function provideMatchingBodies(): iterable
{ {
$longUrl = 'https://www.alejandrocelaya.com'; $longUrl = 'https://www.alejandrocelaya.com';
@ -202,7 +202,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals(self::STATUS_BAD_REQUEST, $secondStatusCode); self::assertEquals(self::STATUS_BAD_REQUEST, $secondStatusCode);
} }
public function provideConflictingSlugs(): iterable public static function provideConflictingSlugs(): iterable
{ {
yield 'without domain' => ['custom', null]; yield 'without domain' => ['custom', null];
yield 'with domain' => ['custom-with-domain', 'some-domain.com']; yield 'with domain' => ['custom-with-domain', 'some-domain.com'];
@ -236,7 +236,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals($payload['longUrl'], $longUrl); self::assertEquals($payload['longUrl'], $longUrl);
} }
public function provideIdn(): iterable public static function provideIdn(): iterable
{ {
yield ['http://tést.shlink.io']; // Redirects to https://shlink.io yield ['http://tést.shlink.io']; // Redirects to https://shlink.io
yield ['http://test.shlink.io']; // Redirects to http://tést.shlink.io yield ['http://test.shlink.io']; // Redirects to http://tést.shlink.io
@ -261,7 +261,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals($url, $payload['url']); self::assertEquals($url, $payload['url']);
} }
public function provideInvalidUrls(): iterable public static function provideInvalidUrls(): iterable
{ {
yield 'API version 2' => ['https://this-has-to-be-invalid.com', '2', 'INVALID_URL']; yield 'API version 2' => ['https://this-has-to-be-invalid.com', '2', 'INVALID_URL'];
yield 'API version 3' => ['https://this-has-to-be-invalid.com', '3', 'https://shlink.io/api/error/invalid-url']; yield 'API version 3' => ['https://this-has-to-be-invalid.com', '3', 'https://shlink.io/api/error/invalid-url'];
@ -287,7 +287,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals('Invalid data', $payload['title']); self::assertEquals('Invalid data', $payload['title']);
} }
public function provideInvalidArgumentApiVersions(): iterable public static function provideInvalidArgumentApiVersions(): iterable
{ {
yield 'missing long url v2' => [[], '2', 'INVALID_ARGUMENT']; yield 'missing long url v2' => [[], '2', 'INVALID_ARGUMENT'];
yield 'missing long url v3' => [[], '3', 'https://shlink.io/api/error/invalid-data']; yield 'missing long url v3' => [[], '3', 'https://shlink.io/api/error/invalid-data'];
@ -338,7 +338,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals('example.com', $returnedDomain); self::assertEquals('example.com', $returnedDomain);
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'no domain' => [null]; yield 'no domain' => [null];
yield 'invalid domain' => ['this-will-be-overwritten.com']; yield 'invalid domain' => ['this-will-be-overwritten.com'];
@ -355,7 +355,7 @@ class CreateShortUrlTest extends ApiTestCase
self::assertEquals(self::STATUS_OK, $statusCode); self::assertEquals(self::STATUS_OK, $statusCode);
} }
public function provideTwitterUrls(): iterable public static function provideTwitterUrls(): iterable
{ {
yield ['https://twitter.com/shlinkio']; yield ['https://twitter.com/shlinkio'];
yield ['https://mobile.twitter.com/shlinkio']; yield ['https://mobile.twitter.com/shlinkio'];

View File

@ -50,7 +50,7 @@ class DeleteShortUrlTest extends ApiTestCase
self::assertEquals($expectedType, $payload['type']); self::assertEquals($expectedType, $payload['type']);
} }
public function provideApiVersions(): iterable public static function provideApiVersions(): iterable
{ {
yield ['1', 'INVALID_SHORTCODE']; yield ['1', 'INVALID_SHORTCODE'];
yield ['2', 'INVALID_SHORTCODE']; yield ['2', 'INVALID_SHORTCODE'];

View File

@ -29,7 +29,7 @@ class DeleteTagsTest extends ApiTestCase
self::assertEquals('Forbidden tag operation', $payload['title']); self::assertEquals('Forbidden tag operation', $payload['title']);
} }
public function provideNonAdminApiKeys(): iterable public static function provideNonAdminApiKeys(): iterable
{ {
yield 'author' => ['author_api_key', '2', 'FORBIDDEN_OPERATION']; yield 'author' => ['author_api_key', '2', 'FORBIDDEN_OPERATION'];
yield 'domain' => ['domain_api_key', '2', 'FORBIDDEN_OPERATION']; yield 'domain' => ['domain_api_key', '2', 'FORBIDDEN_OPERATION'];

View File

@ -27,7 +27,7 @@ class DomainRedirectsTest extends ApiTestCase
self::assertEquals('Invalid data', $payload['title']); self::assertEquals('Invalid data', $payload['title']);
} }
public function provideInvalidDomains(): iterable public static function provideInvalidDomains(): iterable
{ {
yield 'no domain' => [[]]; yield 'no domain' => [[]];
yield 'empty domain' => [['domain' => '']]; yield 'empty domain' => [['domain' => '']];
@ -50,7 +50,7 @@ class DomainRedirectsTest extends ApiTestCase
self::assertEquals($expectedResponse, $payload); self::assertEquals($expectedResponse, $payload);
} }
public function provideRequests(): iterable public static function provideRequests(): iterable
{ {
yield 'new domain' => [[ yield 'new domain' => [[
'domain' => 'my-new-domain.com', 'domain' => 'my-new-domain.com',

View File

@ -32,7 +32,7 @@ class DomainVisitsTest extends ApiTestCase
self::assertCount($expectedVisitsAmount, $payload['visits']['data']); self::assertCount($expectedVisitsAmount, $payload['visits']['data']);
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'example.com with admin API key' => ['valid_api_key', 'example.com', false, 0]; yield 'example.com with admin API key' => ['valid_api_key', 'example.com', false, 0];
yield 'DEFAULT with admin API key' => ['valid_api_key', 'DEFAULT', false, 7]; yield 'DEFAULT with admin API key' => ['valid_api_key', 'DEFAULT', false, 7];
@ -59,7 +59,7 @@ class DomainVisitsTest extends ApiTestCase
self::assertEquals($domain, $payload['authority']); self::assertEquals($domain, $payload['authority']);
} }
public function provideApiKeysAndTags(): iterable public static function provideApiKeysAndTags(): iterable
{ {
yield 'admin API key with invalid domain' => ['valid_api_key', 'invalid_domain.com']; yield 'admin API key with invalid domain' => ['valid_api_key', 'invalid_domain.com'];
yield 'domain API key with not-owned valid domain' => ['domain_api_key', 'this_domain_is_detached.com']; yield 'domain API key with not-owned valid domain' => ['domain_api_key', 'this_domain_is_detached.com'];
@ -78,7 +78,7 @@ class DomainVisitsTest extends ApiTestCase
self::assertEquals($expectedType, $payload['type']); self::assertEquals($expectedType, $payload['type']);
} }
public function provideApiVersions(): iterable public static function provideApiVersions(): iterable
{ {
yield ['1', 'DOMAIN_NOT_FOUND']; yield ['1', 'DOMAIN_NOT_FOUND'];
yield ['2', 'DOMAIN_NOT_FOUND']; yield ['2', 'DOMAIN_NOT_FOUND'];

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace ShlinkioApiTest\Shlink\Rest\Action; namespace ShlinkioApiTest\Shlink\Rest\Action;
use Cake\Chronos\Chronos; use Cake\Chronos\Chronos;
use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
use GuzzleHttp\Psr7\Query; use GuzzleHttp\Psr7\Query;
use GuzzleHttp\RequestOptions; use GuzzleHttp\RequestOptions;
use Laminas\Diactoros\Uri; use Laminas\Diactoros\Uri;
@ -16,7 +15,6 @@ use function sprintf;
class EditShortUrlTest extends ApiTestCase class EditShortUrlTest extends ApiTestCase
{ {
use ArraySubsetAsserts;
use NotFoundUrlHelpersTrait; use NotFoundUrlHelpersTrait;
/** /**
@ -47,7 +45,14 @@ class EditShortUrlTest extends ApiTestCase
self::assertArraySubset($meta, $metaAfterEditing); self::assertArraySubset($meta, $metaAfterEditing);
} }
public function provideMeta(): iterable private static function assertArraySubset(array $a, array $b): void
{
foreach ($a as $key => $expectedValue) {
self::assertEquals($expectedValue, $b[$key]);
}
}
public static function provideMeta(): iterable
{ {
$now = Chronos::now(); $now = Chronos::now();
@ -92,7 +97,7 @@ class EditShortUrlTest extends ApiTestCase
} }
} }
public function provideLongUrls(): iterable public static function provideLongUrls(): iterable
{ {
yield 'valid URL' => ['https://shlink.io', self::STATUS_OK, null]; yield 'valid URL' => ['https://shlink.io', self::STATUS_OK, null];
yield 'invalid URL' => ['htt:foo', self::STATUS_BAD_REQUEST, 'INVALID_URL']; yield 'invalid URL' => ['htt:foo', self::STATUS_BAD_REQUEST, 'INVALID_URL'];
@ -162,7 +167,7 @@ class EditShortUrlTest extends ApiTestCase
self::assertEquals(100, $editedShortUrl['meta']['maxVisits'] ?? null); self::assertEquals(100, $editedShortUrl['meta']['maxVisits'] ?? null);
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'domain' => [ yield 'domain' => [
'example.com', 'example.com',

View File

@ -24,7 +24,7 @@ class GlobalVisitsTest extends ApiTestCase
self::assertEquals(3, $payload['visits']['orphanVisitsCount']); self::assertEquals(3, $payload['visits']['orphanVisitsCount']);
} }
public function provideApiKeys(): iterable public static function provideApiKeys(): iterable
{ {
yield 'admin API key' => ['valid_api_key', 7]; yield 'admin API key' => ['valid_api_key', 7];
yield 'domain API key' => ['domain_api_key', 0]; yield 'domain API key' => ['domain_api_key', 0];

View File

@ -30,7 +30,7 @@ class ListDomainsTest extends ApiTestCase
], $respPayload); ], $respPayload);
} }
public function provideApiKeysAndDomains(): iterable public static function provideApiKeysAndDomains(): iterable
{ {
yield 'admin API key' => ['valid_api_key', [ yield 'admin API key' => ['valid_api_key', [
[ [

View File

@ -168,7 +168,7 @@ class ListShortUrlsTest extends ApiTestCase
], $respPayload); ], $respPayload);
} }
public function provideFilteredLists(): iterable public static function provideFilteredLists(): iterable
{ {
// FIXME Cannot use enums in constants in PHP 8.1. Change this once support for PHP 8.1 is dropped // FIXME Cannot use enums in constants in PHP 8.1. Change this once support for PHP 8.1 is dropped
$withDeviceLongUrls = static fn (array $shortUrl, ?array $longUrls = null) => [ $withDeviceLongUrls = static fn (array $shortUrl, ?array $longUrls = null) => [
@ -321,7 +321,7 @@ class ListShortUrlsTest extends ApiTestCase
], $respPayload); ], $respPayload);
} }
public function provideInvalidFiltering(): iterable public static function provideInvalidFiltering(): iterable
{ {
yield [['tagsMode' => 'invalid'], ['tagsMode']]; yield [['tagsMode' => 'invalid'], ['tagsMode']];
yield [['orderBy' => 'invalid'], ['orderBy']]; yield [['orderBy' => 'invalid'], ['orderBy']];

View File

@ -21,7 +21,7 @@ class ListTagsTest extends ApiTestCase
self::assertEquals(['tags' => $expectedTags], $payload); self::assertEquals(['tags' => $expectedTags], $payload);
} }
public function provideQueries(): iterable public static function provideQueries(): iterable
{ {
yield 'admin API key' => ['valid_api_key', [], [ yield 'admin API key' => ['valid_api_key', [], [
'data' => ['bar', 'baz', 'foo'], 'data' => ['bar', 'baz', 'foo'],

View File

@ -24,7 +24,7 @@ class NonOrphanVisitsTest extends ApiTestCase
self::assertCount($returnedItems, $payload['visits']['data'] ?? []); self::assertCount($returnedItems, $payload['visits']['data'] ?? []);
} }
public function provideQueries(): iterable public static function provideQueries(): iterable
{ {
yield 'all data' => [[], 7, 7]; yield 'all data' => [[], 7, 7];
yield 'middle page' => [['page' => 2, 'itemsPerPage' => 3], 7, 3]; yield 'middle page' => [['page' => 2, 'itemsPerPage' => 3], 7, 3];

View File

@ -57,7 +57,7 @@ class OrphanVisitsTest extends ApiTestCase
self::assertEquals($expectedVisits, $visits); self::assertEquals($expectedVisits, $visits);
} }
public function provideQueries(): iterable public static function provideQueries(): iterable
{ {
yield 'all data' => [[], 3, 3, [self::INVALID_SHORT_URL, self::REGULAR_NOT_FOUND, self::BASE_URL]]; yield 'all data' => [[], 3, 3, [self::INVALID_SHORT_URL, self::REGULAR_NOT_FOUND, self::BASE_URL]];
yield 'limit items' => [['itemsPerPage' => 2], 3, 2, [self::INVALID_SHORT_URL, self::REGULAR_NOT_FOUND]]; yield 'limit items' => [['itemsPerPage' => 2], 3, 2, [self::INVALID_SHORT_URL, self::REGULAR_NOT_FOUND]];

View File

@ -30,7 +30,7 @@ class RenameTagTest extends ApiTestCase
self::assertEquals('Forbidden tag operation', $payload['title']); self::assertEquals('Forbidden tag operation', $payload['title']);
} }
public function provideNonAdminApiKeys(): iterable public static function provideNonAdminApiKeys(): iterable
{ {
yield 'author' => ['author_api_key']; yield 'author' => ['author_api_key'];
yield 'domain' => ['domain_api_key']; yield 'domain' => ['domain_api_key'];

View File

@ -34,7 +34,7 @@ class ResolveShortUrlTest extends ApiTestCase
self::assertEquals(self::STATUS_OK, $fetchResp->getStatusCode()); self::assertEquals(self::STATUS_OK, $fetchResp->getStatusCode());
} }
public function provideDisabledMeta(): iterable public static function provideDisabledMeta(): iterable
{ {
$now = Chronos::now(); $now = Chronos::now();

View File

@ -66,7 +66,7 @@ class ShortUrlVisitsTest extends ApiTestCase
self::assertCount($expectedAmountOfVisits, $payload['visits']['data'] ?? []); self::assertCount($expectedAmountOfVisits, $payload['visits']['data'] ?? []);
} }
public function provideDomains(): iterable public static function provideDomains(): iterable
{ {
yield 'domain' => ['example.com', 0]; yield 'domain' => ['example.com', 0];
yield 'no domain' => [null, 2]; yield 'no domain' => [null, 2];
@ -95,7 +95,7 @@ class ShortUrlVisitsTest extends ApiTestCase
self::assertCount($expectedAmountOfVisits, $payload['visits']['data'] ?? []); self::assertCount($expectedAmountOfVisits, $payload['visits']['data'] ?? []);
} }
public function provideVisitsForBots(): iterable public static function provideVisitsForBots(): iterable
{ {
yield 'bots excluded' => [true, 1]; yield 'bots excluded' => [true, 1];
yield 'bots not excluded' => [false, 2]; yield 'bots not excluded' => [false, 2];

View File

@ -22,7 +22,7 @@ class SingleStepCreateShortUrlTest extends ApiTestCase
self::assertEquals($expectedContentType, $resp->getHeaderLine('Content-Type')); self::assertEquals($expectedContentType, $resp->getHeaderLine('Content-Type'));
} }
public function provideFormats(): iterable public static function provideFormats(): iterable
{ {
yield 'txt format' => ['txt', 'text/plain']; yield 'txt format' => ['txt', 'text/plain'];
yield 'json format' => ['json', 'application/json']; yield 'json format' => ['json', 'application/json'];

View File

@ -32,7 +32,7 @@ class TagVisitsTest extends ApiTestCase
self::assertCount($expectedVisitsAmount, $payload['visits']['data']); self::assertCount($expectedVisitsAmount, $payload['visits']['data']);
} }
public function provideTags(): iterable public static function provideTags(): iterable
{ {
yield 'foo with admin API key' => ['valid_api_key', 'foo', false, 5]; yield 'foo with admin API key' => ['valid_api_key', 'foo', false, 5];
yield 'foo with admin API key and no bots' => ['valid_api_key', 'foo', true, 4]; yield 'foo with admin API key and no bots' => ['valid_api_key', 'foo', true, 4];
@ -62,7 +62,7 @@ class TagVisitsTest extends ApiTestCase
self::assertEquals('Tag not found', $payload['title']); self::assertEquals('Tag not found', $payload['title']);
} }
public function provideApiKeysAndTags(): iterable public static function provideApiKeysAndTags(): iterable
{ {
yield 'admin API key with invalid tag' => ['valid_api_key', 'invalid_tag']; yield 'admin API key with invalid tag' => ['valid_api_key', 'invalid_tag'];
yield 'domain API key with valid tag not used' => ['domain_api_key', 'bar']; yield 'domain API key with valid tag not used' => ['domain_api_key', 'bar'];

View File

@ -45,7 +45,7 @@ class TagsStatsTest extends ApiTestCase
self::assertArrayHasKey('data', $tags); self::assertArrayHasKey('data', $tags);
} }
public function provideQueries(): iterable public static function provideQueries(): iterable
{ {
yield 'admin API key' => ['valid_api_key', [], [ yield 'admin API key' => ['valid_api_key', [], [
[ [

View File

@ -29,7 +29,7 @@ class UpdateTagTest extends ApiTestCase
self::assertEquals('Invalid data', $payload['title']); self::assertEquals('Invalid data', $payload['title']);
} }
public function provideInvalidBody(): iterable public static function provideInvalidBody(): iterable
{ {
yield [[]]; yield [[]];
yield [['oldName' => 'foo']]; yield [['oldName' => 'foo']];
@ -57,7 +57,7 @@ class UpdateTagTest extends ApiTestCase
self::assertEquals('Tag not found', $payload['title']); self::assertEquals('Tag not found', $payload['title']);
} }
public function provideTagNotFoundApiVersions(): iterable public static function provideTagNotFoundApiVersions(): iterable
{ {
yield 'version 1' => ['1', 'TAG_NOT_FOUND']; yield 'version 1' => ['1', 'TAG_NOT_FOUND'];
yield 'version 2' => ['2', 'TAG_NOT_FOUND']; yield 'version 2' => ['2', 'TAG_NOT_FOUND'];
@ -85,7 +85,7 @@ class UpdateTagTest extends ApiTestCase
self::assertEquals('Tag conflict', $payload['title']); self::assertEquals('Tag conflict', $payload['title']);
} }
public function provideTagConflictsApiVersions(): iterable public static function provideTagConflictsApiVersions(): iterable
{ {
yield 'version 1' => ['1', 'TAG_CONFLICT']; yield 'version 1' => ['1', 'TAG_CONFLICT'];
yield 'version 2' => ['2', 'TAG_CONFLICT']; yield 'version 2' => ['2', 'TAG_CONFLICT'];

Some files were not shown because too many files have changed in this diff Show More