CluPilotCloud/tests/Feature/Admin/ConsoleReportsRealDataTest.php

181 lines
7.7 KiB
PHP

<?php
use App\Models\Customer;
use App\Models\Host;
use App\Models\Instance;
use App\Models\ProvisioningRun;
use App\Models\Subscription;
use App\Livewire\Admin\Instances as AdminInstances;
use App\Livewire\Admin\Overview;
use App\Livewire\Admin\Revenue;
use Illuminate\Support\Facades\DB;
use Livewire\Livewire;
/**
* The console reports what is in the database, or nothing.
*
* Its front page, its instance list and its revenue page were hard-coded
* fiction: forty-two customers, €7,842 a month, hosts named pve-fsn-1..3, a
* twelve-month growth curve. It read like a running business and measured
* nothing. These tests exist so that cannot come back quietly.
*/
it('reports an empty estate as empty, not as a going concern', function () {
// The strongest statement of the old bug: with nothing in the database the
// console still claimed 42 customers, 39 instances, 4 hosts and €7,842 a
// month. The figures are asserted directly rather than searched for in the
// markup — "42" also occurs inside Livewire's own component ids.
//
// The mailer is pinned to a delivering one: phpunit.xml forces
// MAIL_MAILER=array so a test suite cannot send mail, and the console
// rightly warns that nothing is being delivered. That warning is about the
// ENVIRONMENT, not about the estate, and this test is about an empty estate.
config()->set('mail.default', 'smtp');
Livewire::actingAs(operator('Owner'), 'operator')
->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 () {
// See above: the test suite deliberately delivers no mail, and the console
// says so. Pinned here so "clean" means an estate with nothing wrong in it.
config()->set('mail.default', 'smtp');
$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');
});