test(Overview::class) ->assertViewHas('kpis', function (array $kpis) { [$customers, $instances, $hosts, $mrr] = $kpis; return $customers['value'] === '0' && $instances['value'] === '0' && $hosts['value'] === '0' && str_contains($mrr['value'], '0,00'); }) ->assertViewHas('hostLoad', []) ->assertViewHas('runs', []) ->assertViewHas('notices', []) ->assertSee(__('admin.no_open_runs')) ->assertSee(__('admin.no_notices')); }); it('counts what is actually there', function () { $host = Host::factory()->create(['status' => 'active', 'total_gb' => 1000, 'last_seen_at' => now()]); $customer = Customer::factory()->create(['status' => 'active']); Instance::factory()->create([ 'customer_id' => $customer->id, 'host_id' => $host->id, 'status' => 'active', 'quota_gb' => 250, ]); Livewire::actingAs(operator('Owner'), 'operator') ->test(Overview::class) ->assertSee('1') ->assertSee($host->name); }); it('reports host capacity the way placement counts it', function () { // The figure on the dashboard has to be the figure placement acts on. // Placement counts the VM disk allocation — not the smaller Nextcloud // quota — against capacity AFTER the host's reserve. A dashboard doing its // own arithmetic would call a host comfortable while orders were already // being refused on it. $host = Host::factory()->create([ 'status' => 'active', 'total_gb' => 1000, 'reserve_pct' => 20, 'last_seen_at' => now(), ]); $customer = Customer::factory()->create(); Instance::factory()->create([ 'customer_id' => $customer->id, 'host_id' => $host->id, 'status' => 'active', 'quota_gb' => 250, 'disk_gb' => 600, ]); expect($host->committedGb())->toBe(600) ->and($host->freeGb())->toBe(800); Livewire::actingAs(operator('Owner'), 'operator') ->test(Overview::class) // 600 of the 800 GB placement may actually use, not 250 of 1000. ->assertSee('600 / 800 GB') ->assertDontSee('250 / 1000 GB'); }); it('states monthly revenue off the contracts, with a yearly term divided', function () { $customer = Customer::factory()->create(); Subscription::factory()->create([ 'customer_id' => $customer->id, 'status' => 'active', 'term' => 'monthly', 'price_cents' => 17900, 'currency' => 'EUR', ]); // A year paid up front is not twelve times the monthly revenue. Subscription::factory()->create([ 'customer_id' => $customer->id, 'status' => 'active', 'term' => 'yearly', 'price_cents' => 120000, 'currency' => 'EUR', ]); // A cancelled contract bills nothing. Subscription::factory()->create([ 'customer_id' => $customer->id, 'status' => 'cancelled', 'term' => 'monthly', 'price_cents' => 99900, 'currency' => 'EUR', ]); // 179,00 monthly + 1.200,00 a year ÷ 12 = 179,00 + 100,00 = 279,00 Livewire::actingAs(operator('Owner'), 'operator') ->test(Revenue::class) ->assertSee('279,00') ->assertDontSee('999,00'); }); it('raises a notice only when something is actually wrong', function () { $clean = Livewire::actingAs(operator('Owner'), 'operator')->test(Overview::class); $clean->assertSee(__('admin.systems_ok')); ProvisioningRun::factory()->create(['status' => ProvisioningRun::STATUS_FAILED]); Livewire::actingAs(operator('Owner'), 'operator') ->test(Overview::class) ->assertSee(__('admin.notice.failed_runs', ['n' => 1])) ->assertDontSee(__('admin.systems_ok')); }); it('gets the same committed storage whether or not the sum was preloaded', function () { // Two code paths now answer one question. If they ever disagree, the // dashboard and placement disagree — which is the bug this replaced. $host = Host::factory()->create(['total_gb' => 1000, 'reserve_pct' => 10]); $customer = Customer::factory()->create(); Instance::factory()->create(['customer_id' => $customer->id, 'host_id' => $host->id, 'status' => 'active', 'disk_gb' => 120]); Instance::factory()->create(['customer_id' => $customer->id, 'host_id' => $host->id, 'status' => 'failed', 'vmid' => 900, 'disk_gb' => 80]); // Failed before a VM existed — it holds nothing. Instance::factory()->create(['customer_id' => $customer->id, 'host_id' => $host->id, 'status' => 'failed', 'vmid' => null, 'disk_gb' => 500]); $onDemand = Host::query()->find($host->id)->committedGb(); $preloaded = Host::query() ->withSum(['instances as committed_disk_gb' => fn ($q) => $q->occupyingHost()], 'disk_gb') ->find($host->id) ->committedGb(); expect($onDemand)->toBe(200)->and($preloaded)->toBe($onDemand); // A host with nothing on it arrives preloaded-but-null (SUM over no rows). // It must read as zero without going back to the database — that is the // case the preload exists for. $bare = Host::factory()->create(['total_gb' => 500]); $preloadedBare = Host::query() ->withSum(['instances as committed_disk_gb' => fn ($q) => $q->occupyingHost()], 'disk_gb') ->find($bare->id); DB::enableQueryLog(); expect($preloadedBare->committedGb())->toBe(0); expect(DB::getQueryLog())->toBeEmpty(); DB::disableQueryLog(); }); it('lists real instances and claims no version it does not record', function () { $host = Host::factory()->create(['name' => 'pve-test-9']); $customer = Customer::factory()->create(['name' => 'Beispielkunde GmbH']); Instance::factory()->create([ 'customer_id' => $customer->id, 'host_id' => $host->id, 'subdomain' => 'beispielkunde', 'vmid' => 4711, 'quota_gb' => 500, 'status' => 'active', ]); Livewire::actingAs(operator('Owner'), 'operator') ->test(AdminInstances::class) ->assertSee('beispielkunde') ->assertSee('Beispielkunde GmbH') ->assertSee('pve-test-9') ->assertSee('4711') // The Nextcloud version is not recorded anywhere, so it is not shown. ->assertDontSee('NC 31'); });