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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ data/infra/matomo
docs/mercure.html
.phpunit.result.cache
docs/swagger/openapi-inlined.json
config/test/dynamic_test_env.php
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
"shlinkio/php-coding-standard": "~2.5.0",
"shlinkio/shlink-test-utils": "^4.3.1",
"symfony/var-dumper": "^7.3",
"veewee/composer-run-parallel": "^1.4"
"veewee/composer-run-parallel": "^1.4",
"webimpress/safe-writer": "^2.2"
},
"conflict": {
"symfony/var-exporter": ">=6.3.9,<=6.4.0"
Expand Down
2 changes: 2 additions & 0 deletions config/container.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

require 'vendor/autoload.php';

// Promote env vars from dynamic test config
loadEnvVarsFromConfig('config/test/dynamic_test_env.php', enumValues(EnvVars::class));
// Promote env vars from installer, dev config or test config
loadEnvVarsFromConfig(
EnvVars::isTestEnv() ? 'config/test/shlink_test_env.php' : 'config/params/*.php',
Expand Down
2 changes: 2 additions & 0 deletions config/test/constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@
. 'Version/18.4 Safari/605.1.15';
const CHROMEOS_USER_AGENT = 'Mozilla/5.0 (X11; CrOS x86_64 16181.61.0) AppleWebKit/537.36 (KHTML, like Gecko) '
. 'Chrome/134.0.6998.198 Safari/537.36';

const DYNAMIC_ENV_VARS_FILE = __DIR__ . '/../../config/test/dynamic_test_env.php';
34 changes: 34 additions & 0 deletions module/Rest/test-api/Action/MercureInfoTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace ShlinkioApiTest\Shlink\Rest\Action;

use PHPUnit\Framework\Attributes\Test;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Shlinkio\Shlink\TestUtils\ApiTest\ApiTestCase;
use ShlinkioApiTest\Shlink\Rest\Utils\WithEnvVars;

class MercureInfoTest extends ApiTestCase
{
#[Test]
public function mercureServerInfoIsReturnedIfConfigured(): void
{
$resp = $this->callApiWithKey('GET', '/mercure-info');
self::assertEquals(501, $resp->getStatusCode());
}

#[Test, WithEnvVars([
EnvVars::MERCURE_ENABLED->value => true,
EnvVars::MERCURE_PUBLIC_HUB_URL->value => 'https://mercure.example.com',
EnvVars::MERCURE_JWT_SECRET->value => 'mercure_jwt_key_long_enough_to_avoid_error',
])]
public function errorIsReturnedIfMercureServerIsNotConfigured(): void
{
$resp = $this->callApiWithKey('GET', '/mercure-info');
$payload = $this->getJsonResponsePayload($resp);

self::assertEquals(200, $resp->getStatusCode());
self::assertEquals('https://mercure.example.com/.well-known/mercure', $payload['mercureHubUrl']);
}
}
22 changes: 20 additions & 2 deletions module/Rest/test-api/Middleware/CorsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
use GuzzleHttp\RequestOptions;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Shlinkio\Shlink\TestUtils\ApiTest\ApiTestCase;
use ShlinkioApiTest\Shlink\Rest\Utils\WithEnvVars;

class CorsTest extends ApiTestCase
{
Expand Down Expand Up @@ -61,8 +63,8 @@ public function preflightRequestsIncludeExtraCorsHeaders(string $endpoint, strin
]);

self::assertEquals(204, $resp->getStatusCode());
self::assertTrue($resp->hasHeader('Access-Control-Allow-Origin'));
self::assertTrue($resp->hasHeader('Access-Control-Max-Age'));
self::assertEquals('*', $resp->getHeaderLine('Access-Control-Allow-Origin'));
self::assertEquals('3600', $resp->getHeaderLine('Access-Control-Max-Age'));
self::assertEquals($expectedAllowedMethods, $resp->getHeaderLine('Access-Control-Allow-Methods'));
self::assertEquals($allowedHeaders, $resp->getHeaderLine('Access-Control-Allow-Headers'));
}
Expand All @@ -74,4 +76,20 @@ public static function providePreflightEndpoints(): iterable
yield 'tags route' => ['/tags', 'GET,DELETE,PUT'];
yield 'health route' => ['/health', 'GET'];
}

#[Test, WithEnvVars([
EnvVars::CORS_MAX_AGE->value => 5000,
EnvVars::CORS_ALLOW_CREDENTIALS->value => true,
EnvVars::CORS_ALLOW_ORIGIN->value => 'example.com,localhost:8000',
])]
public function parametersCanBeCustomized(): void
{
$resp = $this->callApiWithKey(self::METHOD_OPTIONS, '/short-urls', [
RequestOptions::HEADERS => ['Origin' => 'example.com'],
]);

self::assertEquals('5000', $resp->getHeaderLine('Access-Control-Max-Age'));
self::assertEquals('example.com', $resp->getHeaderLine('Access-Control-Allow-Origin'));
self::assertEquals('true', $resp->getHeaderLine('Access-Control-Allow-Credentials'));
}
}
25 changes: 25 additions & 0 deletions module/Rest/test-api/Utils/ApiTestsExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace ShlinkioApiTest\Shlink\Rest\Utils;

use PHPUnit\Runner\Extension\Extension;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
use PHPUnit\TextUI\Configuration\Configuration;
use Symfony\Component\Process\Process;

class ApiTestsExtension implements Extension
{
public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void
{
$facade->registerSubscriber(new EnvSpecificTestListener());
$facade->registerSubscriber(new CleanDynamicEnvVarsTestListener());
}

public static function restartRRServer(): void
{
(new Process(['bin/rr', 'reset', '-c=config/roadrunner/.rr.test.yml']))->mustRun();
}
}
28 changes: 28 additions & 0 deletions module/Rest/test-api/Utils/CleanDynamicEnvVarsTestListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace ShlinkioApiTest\Shlink\Rest\Utils;

use PHPUnit\Event\Test\Finished;
use PHPUnit\Event\Test\FinishedSubscriber;

use function file_exists;
use function unlink;

use const ShlinkioTest\Shlink\DYNAMIC_ENV_VARS_FILE;

/**
* Tries to delete the dynamic env vars after a test has finished, if any, so that it does not affect subsequent tests
*/
class CleanDynamicEnvVarsTestListener implements FinishedSubscriber
{
public function notify(Finished $event): void
{
if (file_exists(DYNAMIC_ENV_VARS_FILE)) {
unlink(DYNAMIC_ENV_VARS_FILE);
// Restart server again so that it removes the env vars from the file that has just been deleted
ApiTestsExtension::restartRRServer();
}
}
}
60 changes: 60 additions & 0 deletions module/Rest/test-api/Utils/EnvSpecificTestListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace ShlinkioApiTest\Shlink\Rest\Utils;

use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\Test\PreparationStarted;
use PHPUnit\Event\Test\PreparationStartedSubscriber;
use ReflectionMethod;
use Webimpress\SafeWriter\FileWriter;

use function sprintf;
use function var_export;

use const ShlinkioTest\Shlink\DYNAMIC_ENV_VARS_FILE;

/**
* Checks if a test to be executed has the WithEnvVars attribute, and if so, creates a file with the env vars it defines
* and restarts RoadRunner test server so that those env vars are applied.
*/
class EnvSpecificTestListener implements PreparationStartedSubscriber
{
public function notify(PreparationStarted $event): void
{
$test = $event->test();
if (! ($test instanceof TestMethod)) {
return;
}

$className = $test->className();
$methodName = $test->methodName();

$reflection = new ReflectionMethod($className, $methodName);
$attributes = $reflection->getAttributes(WithEnvVars::class);

// If the method has the attribute, generate a temporary env file, and restart the RoadRunner server
if (! empty($attributes)) {
/** @var WithEnvVars $withEnvVars */
$withEnvVars = $attributes[0]->newInstance();
$this->createDynamicEnvVarsFile($withEnvVars->envVars);
ApiTestsExtension::restartRRServer();
}
}

private function createDynamicEnvVarsFile(array $envVars): void
{
$template = <<<TEMPLATE
<?php

/* Shlink config generated by %s */

return %s;

TEMPLATE;
$content = sprintf($template, self::class, var_export($envVars, return: true));

FileWriter::writeFile(DYNAMIC_ENV_VARS_FILE, $content);
}
}
15 changes: 15 additions & 0 deletions module/Rest/test-api/Utils/WithEnvVars.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace ShlinkioApiTest\Shlink\Rest\Utils;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
final readonly class WithEnvVars
{
public function __construct(public array $envVars)
{
}
}
4 changes: 4 additions & 0 deletions phpunit-api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@
<directory>./module/Rest/src</directory>
</include>
</source>

<extensions>
<bootstrap class="ShlinkioApiTest\Shlink\Rest\Utils\ApiTestsExtension" />
</extensions>
</phpunit>
Loading