create(); $organisation = Organisation::factory()->create(['owner_id' => $user->id]); $sourceProvider = SourceProvider::query()->create([ 'organisation_id' => $organisation->id, 'name' => 'GitHub', 'type' => SourceProviderType::GITHUB, 'url' => 'https://github.com', ]); $application = Application::factory()->for($organisation)->create(); $this->actingAs($user) ->get(route('applications.edit', [$organisation, $application])) ->assertOk() ->assertInertia(fn (AssertableInertia $page) => $page->component('applications/Edit', false)); $this->actingAs($user)->put(route('applications.update', [$organisation, $application]), [ 'name' => 'Renamed API', 'source_provider_id' => $sourceProvider->id, 'repository_type' => RepositoryType::GIT->value, 'repository_url' => 'git@example.com:org/renamed.git', 'default_branch' => 'release', ])->assertRedirect(route('applications.show', [$organisation, $application])); expect($application->refresh()->name)->toBe('Renamed API') ->and($application->source_provider_id)->toBe($sourceProvider->id) ->and($application->repository_type)->toBe(RepositoryType::GIT); $this->actingAs($user) ->delete(route('applications.destroy', [$organisation, $application])) ->assertRedirect(route('applications.index', $organisation)); expect(Application::query()->whereKey($application->id)->exists())->toBeFalse(); }); it('creates updates and deletes environments through UI routes', function () { $user = User::factory()->create(); $organisation = Organisation::factory()->create(['owner_id' => $user->id]); $application = Application::factory()->for($organisation)->create(['default_branch' => 'main']); $this->actingAs($user) ->post(route('environments.store', [$organisation, $application]), [ 'name' => 'staging', 'branch' => 'develop', 'php_version' => '8.4', ]) ->assertRedirect(); $environment = $application->environments()->where('name', 'staging')->firstOrFail(); $service = $environment->services()->firstOrFail(); $this->actingAs($user) ->get(route('environments.edit', [$organisation, $application, $environment])) ->assertOk() ->assertInertia(fn (AssertableInertia $page) => $page->component('environments/Edit', false)); $this->actingAs($user) ->put(route('environments.update', [$organisation, $application, $environment]), [ 'name' => 'preview', 'branch' => 'preview', 'status' => 'active', 'scheduler_enabled' => true, 'scheduler_target_service_id' => $service->id, 'scheduler_mode' => SchedulerMode::EVERY_REPLICA->value, 'build_strategy' => BuildStrategy::DEDICATED_BUILDER->value, 'php_version' => '8.4', 'document_root' => 'public', 'health_path' => '/health', 'js_package_manager' => 'bun', 'js_build_command' => 'bun run build', ]) ->assertRedirect(route('environments.show', [$organisation, $application, $environment])); expect($environment->refresh()->name)->toBe('preview') ->and($environment->scheduler_mode)->toBe(SchedulerMode::EVERY_REPLICA) ->and($environment->build_config['build_strategy'])->toBe(BuildStrategy::DEDICATED_BUILDER->value); $this->actingAs($user) ->delete(route('environments.destroy', [$organisation, $application, $environment])) ->assertRedirect(route('applications.show', [$organisation, $application])); expect(Environment::query()->whereKey($environment->id)->exists())->toBeFalse(); }); it('updates and deletes registries and source providers', function () { $user = User::factory()->create(); $organisation = Organisation::factory()->create(['owner_id' => $user->id]); $registry = $organisation->registries()->create([ 'name' => 'Old Registry', 'type' => RegistryType::GENERIC, 'url' => 'registry.example.com', 'credentials' => ['username' => 'old', 'password' => 'secret'], ]); $sourceProvider = $organisation->sourceProviders()->create([ 'name' => 'Old Git', 'type' => SourceProviderType::GENERIC_GIT, 'url' => 'https://git.example.com', 'config' => [], ]); $this->actingAs($user) ->get(route('source-providers.index', $organisation)) ->assertOk() ->assertInertia(fn (AssertableInertia $page) => $page ->component('source-providers/Index', false) ->has('sourceProviders', 1) ->where('sourceProviders.0.name', 'Old Git')); $this->actingAs($user) ->put(route('registries.update', [$organisation, $registry]), [ 'name' => 'Registry', 'type' => RegistryType::GHCR->value, 'url' => 'ghcr.io/example', 'username' => 'new', 'password' => '', ]) ->assertRedirect(route('organisations.show', $organisation)); $this->actingAs($user) ->put(route('source-providers.update', [ 'organisation' => $organisation, 'source_provider' => $sourceProvider, ]), [ 'name' => 'GitHub', 'type' => SourceProviderType::GITHUB->value, 'url' => 'https://github.com', ]) ->assertRedirect(route('organisations.show', $organisation)); expect($registry->refresh()->name)->toBe('Registry') ->and($sourceProvider->refresh()->name)->toBe('GitHub'); $this->actingAs($user)->delete(route('registries.destroy', [$organisation, $registry])) ->assertRedirect(route('organisations.show', $organisation)); $this->actingAs($user)->delete(route('source-providers.destroy', [ 'organisation' => $organisation, 'source_provider' => $sourceProvider, ]))->assertRedirect(route('organisations.show', $organisation)); expect($organisation->registries()->exists())->toBeFalse() ->and($organisation->sourceProviders()->exists())->toBeFalse(); }); it('updates service deployment and migration settings', function () { $user = User::factory()->create(); $organisation = Organisation::factory()->create(['owner_id' => $user->id]); $provider = Provider::factory()->forOrganisation($organisation)->create(); $network = $organisation->networks()->create([ 'provider_id' => $provider->id, 'external_id' => 'network-1', 'network_zone' => 'global', 'name' => 'keystone-global', 'ip_range' => '10.0.0.0/16', ]); $server = \App\Models\Server::factory() ->forOrganisation($organisation->id) ->forProvider((string) $provider->id) ->forNetwork((string) $network->id) ->create(); $service = Service::factory()->for($organisation)->for($server)->create(); $this->actingAs($user) ->put(route('services.update', [$organisation, $server, $service]), [ 'name' => 'postgres', 'desired_replicas' => 2, 'default_cpu_limit' => 1, 'default_memory_limit_mb' => 512, 'deploy_policy' => DeployPolicy::MANUAL->value, 'version_track' => '18', 'available_image_digest' => 'sha256:test', 'process_roles' => 'database, scheduler', 'migration_mode' => 'manual', 'migration_timing' => 'post_switch', 'migration_command' => 'php artisan migrate --force', 'health_path' => '/up', ]) ->assertRedirect(route('services.show', [$organisation, $server, $service])); expect($service->refresh()->deploy_policy)->toBe(DeployPolicy::MANUAL) ->and($service->process_roles)->toBe(['database', 'scheduler']) ->and($service->config['health_path'])->toBe('/up'); });