|
|
@@ -0,0 +1,134 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace Tests\Unit;
|
|
|
+
|
|
|
+use App\Services\SportClientService;
|
|
|
+use Illuminate\Container\Container;
|
|
|
+use Illuminate\Filesystem\Filesystem;
|
|
|
+use Illuminate\Foundation\Application;
|
|
|
+use Illuminate\Foundation\Bootstrap\LoadConfiguration;
|
|
|
+use Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables;
|
|
|
+use Illuminate\Http\Client\Factory;
|
|
|
+use Illuminate\Http\Client\Request;
|
|
|
+use Illuminate\Support\Env;
|
|
|
+use Illuminate\Support\Facades\Facade;
|
|
|
+use Illuminate\Support\Facades\Http;
|
|
|
+use PHPUnit\Framework\Attributes\DataProvider;
|
|
|
+use PHPUnit\Framework\Attributes\PreserveGlobalState;
|
|
|
+use PHPUnit\Framework\Attributes\RunInSeparateProcess;
|
|
|
+use PHPUnit\Framework\TestCase;
|
|
|
+
|
|
|
+class RuntimeConfigurationCacheTest extends TestCase
|
|
|
+{
|
|
|
+ private string $temporaryRoot;
|
|
|
+
|
|
|
+ private const ENVIRONMENT = [
|
|
|
+ 'APP_ENV' => 'local',
|
|
|
+ 'APP_URL' => 'https://app.example.test',
|
|
|
+ 'FOOTBALL_APP_URL' => 'https://football-app.example.test',
|
|
|
+ 'API_FOOTBALL_HOST' => 'https://football.example.test',
|
|
|
+ 'API_FOOTBALL_KEY' => 'football-test-key',
|
|
|
+ 'API_BASKETBALL_HOST' => 'https://basketball.example.test',
|
|
|
+ 'API_BASKETBALL_KEY' => 'basketball-test-key',
|
|
|
+ 'SEND_TELEGRAM' => 'true',
|
|
|
+ 'unique_identification' => 'custom-test-salt',
|
|
|
+ ];
|
|
|
+
|
|
|
+ protected function tearDown(): void
|
|
|
+ {
|
|
|
+ Facade::clearResolvedInstances();
|
|
|
+ Facade::setFacadeApplication(null);
|
|
|
+ Container::setInstance(null);
|
|
|
+ if (isset($this->temporaryRoot)) {
|
|
|
+ (new Filesystem())->deleteDirectory($this->temporaryRoot);
|
|
|
+ }
|
|
|
+ parent::tearDown();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function configurationModes(): array
|
|
|
+ {
|
|
|
+ return [
|
|
|
+ 'uncached, telegram enabled' => [false, true],
|
|
|
+ 'cached, telegram enabled' => [true, true],
|
|
|
+ 'cached, telegram disabled' => [true, false],
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ #[DataProvider('configurationModes')]
|
|
|
+ #[RunInSeparateProcess]
|
|
|
+ #[PreserveGlobalState(false)]
|
|
|
+ public function test_runtime_configuration_survives_caching(bool $cached, bool $sendTelegram): void
|
|
|
+ {
|
|
|
+ // Bootstrap only configuration in a temporary application, without project providers or .env.
|
|
|
+ $this->temporaryRoot = sys_get_temp_dir() . '/runtime-config-test-' . bin2hex(random_bytes(8));
|
|
|
+ mkdir($this->temporaryRoot . '/config', 0700, true);
|
|
|
+ mkdir($this->temporaryRoot . '/bootstrap/cache', 0700, true);
|
|
|
+ foreach (['app', 'services'] as $file) {
|
|
|
+ copy(dirname(__DIR__, 2) . '/config/' . $file . '.php', $this->temporaryRoot . '/config/' . $file . '.php');
|
|
|
+ }
|
|
|
+ $environment = array_replace(self::ENVIRONMENT, ['SEND_TELEGRAM' => $sendTelegram ? 'true' : 'false']);
|
|
|
+ $contents = '';
|
|
|
+ foreach ($environment as $key => $value) {
|
|
|
+ $contents .= $key . '=' . $value . PHP_EOL;
|
|
|
+ }
|
|
|
+ file_put_contents($this->temporaryRoot . '/.env', $contents);
|
|
|
+ $this->clearEnvironment();
|
|
|
+ $app = $this->bootConfiguration();
|
|
|
+
|
|
|
+ if ($cached) {
|
|
|
+ // Use the same serialized config format as config:cache, then start with a fresh environment.
|
|
|
+ file_put_contents($app->getCachedConfigPath(), '<?php return ' . var_export($app['config']->all(), true) . ';');
|
|
|
+ $this->clearEnvironment();
|
|
|
+ $app = $this->bootConfiguration();
|
|
|
+ foreach (array_keys(self::ENVIRONMENT) as $key) {
|
|
|
+ $this->assertNull(env($key), 'Cached bootstrap must skip loading ' . $key . ' from .env');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->assertSame($cached, $app['config_loaded_from_cache']);
|
|
|
+ $this->assertSame('local', config('app.env'));
|
|
|
+ $this->assertSame('https://app.example.test', config('app.url'));
|
|
|
+ $this->assertSame('https://football-app.example.test', config('app.football_url'));
|
|
|
+ $this->assertSame('custom-test-salt', config('app.unique_identification'));
|
|
|
+ $this->assertSame($sendTelegram, config('services.telegram.send_enabled'));
|
|
|
+
|
|
|
+ $app->instance(Factory::class, new Factory());
|
|
|
+ Facade::clearResolvedInstances();
|
|
|
+ Facade::setFacadeApplication($app);
|
|
|
+ Http::preventStrayRequests();
|
|
|
+ Http::fake(['*' => Http::response(['response' => []])]);
|
|
|
+
|
|
|
+ foreach ([1 => 'football', 2 => 'basketball'] as $type => $sport) {
|
|
|
+ $endpoint = $type === 1 ? 'fixtures' : 'games';
|
|
|
+ foreach (['get', 'post'] as $method) {
|
|
|
+ $params = ['live' => 'all'];
|
|
|
+ $this->assertSame(['response' => []], SportClientService::$method($type, $endpoint, $params));
|
|
|
+ Http::assertSent(fn (Request $request) =>
|
|
|
+ $request->method() === strtoupper($method)
|
|
|
+ && strtok($request->url(), '?') === 'https://' . $sport . '.example.test/' . $endpoint
|
|
|
+ && $request->hasHeader('x-apisports-key', $sport . '-test-key')
|
|
|
+ && $request->data() === $params
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Http::assertSentCount(4);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function bootConfiguration(): Application
|
|
|
+ {
|
|
|
+ $app = new Application($this->temporaryRoot);
|
|
|
+ (new LoadEnvironmentVariables())->bootstrap($app);
|
|
|
+ (new LoadConfiguration())->bootstrap($app);
|
|
|
+
|
|
|
+ return $app;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function clearEnvironment(): void
|
|
|
+ {
|
|
|
+ foreach (array_merge(array_keys(self::ENVIRONMENT), ['APP_CONFIG_CACHE']) as $key) {
|
|
|
+ unset($_ENV[$key], $_SERVER[$key]);
|
|
|
+ putenv($key);
|
|
|
+ }
|
|
|
+ Env::enablePutenv();
|
|
|
+ }
|
|
|
+}
|