92 lines
3.3 KiB
PHP
92 lines
3.3 KiB
PHP
<?php
|
||
|
||
use App\Livewire\Admin\ConfirmDeleteIncident;
|
||
use App\Livewire\Admin\Incidents as IncidentConsole;
|
||
use App\Models\Incident;
|
||
use Livewire\Livewire;
|
||
|
||
/**
|
||
* Taking a whole incident back.
|
||
*
|
||
* Deliberately a different act from editing an update, which there is still no
|
||
* way to do: an update is a statement made at a time, and rewriting one
|
||
* afterwards is what the record exists to prevent. Removing the WHOLE incident
|
||
* is how a test entry, or one published against the wrong service, is undone —
|
||
* and it leaves nothing half-standing behind.
|
||
*/
|
||
function anIncident(array $attributes = []): Incident
|
||
{
|
||
$incident = Incident::query()->create([
|
||
'title' => 'Testeintrag',
|
||
'impact' => 'degraded',
|
||
'components' => ['portal'],
|
||
'started_at' => now()->subHour(),
|
||
'published_at' => now()->subHour(),
|
||
...$attributes,
|
||
]);
|
||
|
||
$incident->updates()->create(['status' => 'investigating', 'body' => 'Wird untersucht.']);
|
||
|
||
return $incident;
|
||
}
|
||
|
||
it('removes the incident and every entry under it', function () {
|
||
$incident = anIncident();
|
||
|
||
Livewire::actingAs(operator('Owner'), 'operator')
|
||
->test(IncidentConsole::class)
|
||
->call('delete', $incident->uuid);
|
||
|
||
expect(Incident::query()->count())->toBe(0)
|
||
// The updates go with it. A timeline whose incident is gone is rows
|
||
// nobody can reach and nobody will remember to clean up.
|
||
->and(DB::table('incident_updates')->count())->toBe(0);
|
||
});
|
||
|
||
it('takes it off the public page at once', function () {
|
||
$incident = anIncident(['resolved_at' => now()]);
|
||
|
||
$this->get('/status')->assertSee('Testeintrag');
|
||
|
||
Livewire::actingAs(operator('Owner'), 'operator')
|
||
->test(IncidentConsole::class)
|
||
->call('delete', $incident->uuid);
|
||
|
||
$this->get('/status')->assertDontSee('Testeintrag');
|
||
});
|
||
|
||
it('asks first, in the product’s own dialog', function () {
|
||
// R23: not window.confirm. The modal mutates nothing itself — it dispatches
|
||
// to the page component, which is where the capability check already is.
|
||
$incident = anIncident();
|
||
|
||
Livewire::actingAs(operator('Owner'), 'operator')
|
||
->test(ConfirmDeleteIncident::class, ['uuid' => $incident->uuid])
|
||
->assertSee($incident->title)
|
||
->call('confirm')
|
||
->assertDispatched('incident-delete-confirmed');
|
||
|
||
// The modal alone does not delete anything.
|
||
expect(Incident::query()->count())->toBe(1);
|
||
});
|
||
|
||
it('needs the capability, not merely an operator account', function () {
|
||
// Asserted on the page, not on the action: render() authorizes too, so an
|
||
// operator without the capability never reaches a button — and a
|
||
// Livewire::test() against a component that refuses to render fails with a
|
||
// snapshot error that says nothing about permissions.
|
||
$incident = anIncident();
|
||
|
||
$this->actingAs(operator('Support'), 'operator')
|
||
->get(route('admin.incidents'))
|
||
->assertForbidden();
|
||
|
||
expect(Incident::query()->count())->toBe(1);
|
||
});
|
||
|
||
it('still offers no way to rewrite a single entry', function () {
|
||
// The rule the delete button must not quietly become an exception to.
|
||
expect(method_exists(IncidentConsole::class, 'editUpdate'))->toBeFalse()
|
||
->and(method_exists(IncidentConsole::class, 'deleteUpdate'))->toBeFalse();
|
||
});
|