93 lines
3.9 KiB
PHP
93 lines
3.9 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Admin\Maintenance;
|
|
use App\Mail\MaintenanceAnnouncementMail;
|
|
use App\Models\Customer;
|
|
use App\Models\Host;
|
|
use App\Models\Instance;
|
|
use App\Models\MaintenanceNotification;
|
|
use App\Models\MaintenanceWindow;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Livewire\Livewire;
|
|
|
|
// operator() helper defined in RbacTest.
|
|
|
|
function affectedCustomerOn(Host $host): Customer
|
|
{
|
|
$customer = Customer::factory()->create();
|
|
Instance::factory()->create(['customer_id' => $customer->id, 'host_id' => $host->id, 'status' => 'active']);
|
|
|
|
return $customer;
|
|
}
|
|
|
|
it('publishes a window and emails affected customers once (idempotent)', function () {
|
|
Mail::fake();
|
|
$host = Host::factory()->active()->create();
|
|
$customer = affectedCustomerOn($host);
|
|
|
|
Livewire::actingAs(operator('Owner'))->test(Maintenance::class)
|
|
->set('title', 'Netz-Wartung')
|
|
->set('startsAt', now()->addDay()->format('Y-m-d\TH:i'))
|
|
->set('endsAt', now()->addDay()->addHours(2)->format('Y-m-d\TH:i'))
|
|
->set('hostIds', [$host->id])
|
|
->call('publish')->assertHasNoErrors();
|
|
|
|
$window = MaintenanceWindow::query()->where('title', 'Netz-Wartung')->first();
|
|
expect($window)->not->toBeNull()->and($window->state)->toBe('scheduled')
|
|
->and($window->hosts()->count())->toBe(1);
|
|
Mail::assertQueued(MaintenanceAnnouncementMail::class, 1);
|
|
expect(MaintenanceNotification::query()->where('customer_id', $customer->id)->count())->toBe(1);
|
|
|
|
// The ledger's unique (window, customer, event) makes re-announcing a no-op.
|
|
expect(fn () => MaintenanceNotification::query()->create([
|
|
'maintenance_window_id' => $window->id, 'customer_id' => $customer->id, 'event' => 'announcement', 'email' => $customer->email,
|
|
]))->toThrow(Illuminate\Database\UniqueConstraintViolationException::class);
|
|
});
|
|
|
|
it('emails a cancellation to customers who were announced', function () {
|
|
Mail::fake();
|
|
$host = Host::factory()->active()->create();
|
|
$customer = affectedCustomerOn($host);
|
|
$window = MaintenanceWindow::factory()->scheduled()->create();
|
|
$window->hosts()->attach($host->id);
|
|
// Simulate the customer having received the announcement.
|
|
\App\Models\MaintenanceNotification::create([
|
|
'maintenance_window_id' => $window->id, 'customer_id' => $customer->id, 'event' => 'announcement', 'email' => $customer->email,
|
|
]);
|
|
|
|
Livewire::actingAs(operator('Owner'))->test(Maintenance::class)->call('cancel', $window->uuid);
|
|
|
|
expect($window->fresh()->state)->toBe('cancelled');
|
|
Mail::assertQueued(\App\Mail\MaintenanceCancelledMail::class, 1);
|
|
});
|
|
|
|
it('requires a host to publish', function () {
|
|
Livewire::actingAs(operator('Owner'))->test(Maintenance::class)
|
|
->set('title', 'X')
|
|
->set('startsAt', now()->addDay()->format('Y-m-d\TH:i'))
|
|
->set('endsAt', now()->addDay()->addHours(2)->format('Y-m-d\TH:i'))
|
|
->set('hostIds', [])
|
|
->call('publish')->assertHasErrors(['hostIds']);
|
|
});
|
|
|
|
it('forbids non-privileged roles from managing maintenance', function () {
|
|
Livewire::actingAs(operator('Support'))->test(Maintenance::class)
|
|
->set('title', 'X')->set('startsAt', now()->addDay()->format('Y-m-d\TH:i'))
|
|
->set('endsAt', now()->addDay()->addHours(2)->format('Y-m-d\TH:i'))
|
|
->call('saveDraft')->assertForbidden();
|
|
});
|
|
|
|
it('shows the banner to an affected customer within the horizon', function () {
|
|
$host = Host::factory()->active()->create();
|
|
$customer = affectedCustomerOn($host);
|
|
$window = MaintenanceWindow::factory()->scheduled()->create(['starts_at' => now()->addDay(), 'ends_at' => now()->addDay()->addHour()]);
|
|
$window->hosts()->attach($host->id);
|
|
|
|
$banner = MaintenanceWindow::bannerFor($customer);
|
|
expect($banner)->toHaveCount(1)->and($banner->first()->id)->toBe($window->id);
|
|
|
|
// A customer on another host sees nothing.
|
|
$other = affectedCustomerOn(Host::factory()->active()->create());
|
|
expect(MaintenanceWindow::bannerFor($other))->toHaveCount(0);
|
|
});
|