diff --git a/tests/Feature/ZzPageRenderCrawlTest.php b/tests/Feature/ZzPageRenderCrawlTest.php new file mode 100644 index 0000000000..8a67c4a883 --- /dev/null +++ b/tests/Feature/ZzPageRenderCrawlTest.php @@ -0,0 +1,166 @@ +set('constants.coolify.dev_seed_ip', '127.0.0.1'); + config()->set('app.maintenance.store', 'array'); + Process::fake(); + $this->seed(DatabaseSeeder::class); + + $user = User::find(0); + $team = $user->teams()->first(); + $team->forceFill(['show_boarding' => false])->save(); + Cache::flush(); + + $server = Server::find(0); + $environment = Environment::query()->first(); + $project = $environment->project; + $application = Application::query()->first(); + $database = StandalonePostgresql::query()->where('id', '!=', 0)->first() ?? StandalonePostgresql::query()->first(); + $destination = StandaloneDocker::query()->first(); + + $service = Service::factory()->create([ + 'environment_id' => $environment->id, + 'destination_id' => $destination->id, + 'server_id' => $server->id, + 'docker_compose_raw' => "services:\n app:\n image: nginx\n db:\n image: postgres:16\n", + ]); + $serviceApp = ServiceApplication::forceCreate(['uuid' => (string) Str::uuid(), 'name' => 'app', 'service_id' => $service->id, 'image' => 'nginx']); + $serviceDb = ServiceDatabase::forceCreate(['uuid' => (string) Str::uuid(), 'name' => 'db', 'service_id' => $service->id, 'image' => 'postgres:16']); + + $tag = Tag::forceCreate(['name' => 'crawl', 'team_id' => $team->id]); + $application->tags()->attach($tag->id); + + $s3 = S3Storage::query()->first(); + $backup = ScheduledDatabaseBackup::forceCreate([ + 'uuid' => (string) Str::uuid(), 'team_id' => $team->id, 'frequency' => '0 0 * * *', + 'database_id' => $database->id, 'database_type' => $database->getMorphClass(), + 's3_storage_id' => $s3?->id, + ]); + $deployment = ApplicationDeploymentQueue::forceCreate([ + 'deployment_uuid' => (string) Str::uuid(), 'application_id' => $application->id, + 'server_id' => $server->id, 'status' => 'finished', 'logs' => '[]', + 'application_name' => $application->name, 'server_name' => $server->name, + ]); + $task = ScheduledTask::factory()->create(['application_id' => $application->id, 'team_id' => $team->id]); + + $cloudToken = \App\Models\CloudProviderToken::factory()->create(['team_id' => $team->id]); + $cloudInit = \App\Models\CloudInitScript::forceCreate(['uuid' => (string) Str::uuid(), 'team_id' => $team->id, 'name' => 'init', 'script' => '#!/bin/sh']); + $gitea = \App\Models\GiteaApp::forceCreate(['uuid' => (string) Str::uuid(), 'name' => 'gitea', 'api_url' => 'https://gitea.example.com/api/v1', 'html_url' => 'https://gitea.example.com', 'team_id' => $team->id, 'private_key_id' => PrivateKey::query()->value('id')]); + + $params = [ + 'token_uuid' => $cloudToken->uuid, + 'cloud_token_uuid' => $cloudToken->uuid, + 'cloud_init_script_uuid' => $cloudInit->uuid, + 'gitea_app_uuid' => $gitea->uuid, + 'project_uuid' => $project->uuid, + 'environment_uuid' => $environment->uuid, + 'application_uuid' => $application->uuid, + 'database_uuid' => $database->uuid, + 'service_uuid' => $service->uuid, + 'stack_service_uuid' => $serviceApp->uuid, + 'server_uuid' => $server->uuid, + 'destination_uuid' => $destination->uuid, + 'backup_uuid' => $backup->uuid, + 'deployment_uuid' => $deployment->deployment_uuid, + 'task_uuid' => $task->uuid, + 'github_app_uuid' => GithubApp::query()->value('uuid'), + 'gitlab_app_uuid' => GitlabApp::query()->value('uuid'), + 'private_key_uuid' => PrivateKey::query()->value('uuid'), + 'storage_uuid' => $s3?->uuid, + 'tagName' => $tag->name, + 'user_id' => $user->id, + 'team_id' => $team->id, + 'type' => 'hetzner', + 'provider' => 'github', + ]; + + $originalRedirector = app('redirect'); + $renderedViews = []; + \Illuminate\Support\Facades\View::composer('*', function ($view) use (&$renderedViews) { + $renderedViews[$view->getPath()] = true; + }); + $failures = []; + $skipped = []; + $redirects = []; + $count = 0; + foreach (Route::getRoutes() as $route) { + if (! in_array('GET', $route->methods())) { + continue; + } + $uri = $route->uri(); + if (Str::startsWith($uri, ['api', 'canvas/api', '_', 'horizon', 'telescope', 'livewire', 'webhooks', 'auth/', 'email/', 'download/', 'files/', 'sanctum', 'up', 'storage', 'mcp', 'oauth', 'two-factor', 'user/', 'reset-password', 'login', 'register', 'forgot-password', 'logout', 'verify', 'invitations', 'force-password-reset', 'onboarding', 'select-team', 'subscription', 'terminal/']) + || $uri === '{any}') { + continue; + } + $variants = [$uri]; + if (str_contains($uri, '?}')) { + $variants[] = rtrim(preg_replace('/\/?\{\w+\?\}/', '', $uri), '/'); + } + foreach ($variants as $uri) { + $missing = false; + $path = preg_replace_callback('/\{(\w+)\??\}/', function ($m) use ($params, &$missing) { + if (empty($params[$m[1]]) && ($params[$m[1]] ?? null) !== 0) { + $missing = $m[1]; + + return ''; + } + + return $params[$m[1]]; + }, $uri); + if ($missing) { + $skipped[] = "$uri (no $missing)"; + + continue; + } + if ($uri === 'project/{project_uuid}/environment/{environment_uuid}/service/{service_uuid}/{stack_service_uuid}/backups' || str_contains($uri, '{stack_service_uuid}/backups') || str_contains($uri, '{stack_service_uuid}/import')) { + $path = str_replace($serviceApp->uuid, $serviceDb->uuid, $path); + } + $count++; + app()->instance('redirect', $originalRedirector); + \Livewire\Features\SupportRedirects\SupportRedirects::$redirectorCacheStack = []; + session(['currentTeam' => $team]); + $response = $this->actingAs($user)->get('/'.ltrim($path, '/')); + if ($response->isRedirect()) { + $redirects[] = '/'.$path.' -> '.$response->headers->get('Location'); + } + if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) { + fwrite(STDERR, "4XX /$path => ".$response->getStatusCode().' '.Str::limit((string) $response->exception?->getMessage(), 100)."\n"); + } + if ($response->getStatusCode() >= 500) { + $e = $response->exception; + $failures[] = "/".$path." => ".$response->getStatusCode()." ".($e ? get_class($e).": ".Str::limit($e->getMessage(), 120)."\n ".collect($e->getTrace())->filter(fn ($f) => isset($f["file"]))->take(0)->map(fn ($f) => str_replace(base_path()."/", "", $f["file"]).":".$f["line"])->implode("\n ") : ""); + } + } + } + + file_put_contents('/tmp/rendered-views.txt', implode("\n", array_keys($renderedViews))); + fwrite(STDERR, "\nCrawled $count pages\nSKIPPED:\n".implode("\n", $skipped)."\nREDIRECTS:\n".implode("\n", $redirects)."\nFAILURES:\n".implode("\n", $failures)."\n"); + expect($failures)->toBe([]); +});