Restructure UX and seed a fully simulated organisation
Some checks failed
CI / Tests (push) Failing after 56s
CI / Lint (push) Failing after 1m35s

Rework the dashboard, environment topology view, header navigation, and
status rendering, and standardise selects on a shadcn-vue component.

Replace the thin database seeder with a SimulatedEnvironmentSeeder that
builds a fully wired, mostly-running organisation (ACTIVE server fleet,
managed + GHCR registries, Gitea source provider, ClipBin app with
production/staging environments, services, slices, endpoints, managed
variables, build artifacts, and a completed/in-progress/failed operations
history) so the new UI renders against realistic data.
This commit is contained in:
2026-06-08 22:09:57 +01:00
parent 3a851db08f
commit 85c44296ac
58 changed files with 2292 additions and 847 deletions

View File

@@ -0,0 +1,100 @@
<?php
use App\Enums\BuildArtifactStatus;
use App\Enums\EnvironmentVariableSource;
use App\Enums\OperationStatus;
use App\Enums\RegistryType;
use App\Enums\ServiceStatus;
use App\Enums\ServiceType;
use App\Models\Application;
use App\Models\Operation;
use App\Models\Organisation;
use App\Models\ServiceReplica;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Support\Facades\Bus;
beforeEach(function (): void {
Bus::fake();
$this->seed(DatabaseSeeder::class);
});
it('does not dispatch any deployment jobs while seeding', function (): void {
Bus::assertNothingDispatched();
});
it('seeds an organisation with registries including a ready managed registry', function (): void {
$organisation = Organisation::where('name', 'Stratbucket')->firstOrFail();
expect($organisation->registries()->count())->toBeGreaterThanOrEqual(2);
$managed = $organisation->registries()->where('type', RegistryType::MANAGED)->firstOrFail();
expect($managed->isReady())->toBeTrue()
->and($managed->health_status)->toBe('healthy')
->and($managed->control_server_id)->not->toBeNull();
});
it('seeds a fleet of active servers with a control node', function (): void {
$organisation = Organisation::where('name', 'Stratbucket')->firstOrFail();
expect($organisation->servers()->where('is_control_node', true)->where('build_enabled', true)->count())->toBe(1)
->and($organisation->servers()->count())->toBeGreaterThanOrEqual(4);
});
it('seeds an application with production and staging environments', function (): void {
$application = Application::where('name', 'ClipBin')->firstOrFail();
expect($application->source_provider_id)->not->toBeNull()
->and($application->environments()->pluck('name')->sort()->values()->all())
->toBe(['production', 'staging']);
});
it('wires each environment with web, postgres, valkey and caddy services', function (): void {
$application = Application::where('name', 'ClipBin')->firstOrFail();
foreach ($application->environments as $environment) {
$types = $environment->services()->pluck('type')->map->value->sort()->values()->all();
expect($types)->toBe(['caddy', 'laravel', 'postgres', 'valkey'])
->and($environment->status)->toBe('active');
$postgres = $environment->services()->where('type', ServiceType::POSTGRES)->firstOrFail();
expect($postgres->status)->toBe(ServiceStatus::RUNNING)
->and($postgres->slices()->where('type', 'database_user')->where('status', 'active')->exists())->toBeTrue()
->and($postgres->endpoints()->exists())->toBeTrue();
}
});
it('syncs managed database variables that are not overridable', function (): void {
$environment = Application::where('name', 'ClipBin')->firstOrFail()
->environments()->where('name', 'production')->firstOrFail();
$password = $environment->variables()->where('key', 'DB_PASSWORD')->firstOrFail();
expect($password->source)->toBe(EnvironmentVariableSource::MANAGED_ATTACHMENT)
->and($password->overridable)->toBeFalse()
->and($environment->variables()->where('key', 'APP_NAME')->where('source', EnvironmentVariableSource::USER)->exists())->toBeTrue();
});
it('seeds build artifacts in available and building states', function (): void {
$statuses = Application::where('name', 'ClipBin')->firstOrFail()
->environments()
->with('buildArtifacts')
->get()
->flatMap->buildArtifacts
->pluck('status');
expect($statuses)->toContain(BuildArtifactStatus::AVAILABLE)
->and($statuses)->toContain(BuildArtifactStatus::BUILDING);
});
it('seeds an operations history with completed, in-progress and failed operations', function (): void {
expect(Operation::where('status', OperationStatus::COMPLETED)->exists())->toBeTrue()
->and(Operation::where('status', OperationStatus::IN_PROGRESS)->exists())->toBeTrue()
->and(Operation::where('status', OperationStatus::FAILED)->exists())->toBeTrue();
});
it('seeds at least one unhealthy replica for state variety', function (): void {
expect(ServiceReplica::where('health_status', 'unhealthy')->exists())->toBeTrue();
});