14-04-2026
This commit is contained in:
parent
f58c709945
commit
0f82fea88a
72 changed files with 7414 additions and 148 deletions
298
tests/Feature/PaymentDashboard/PaymentIncidentCrudTest.php
Normal file
298
tests/Feature/PaymentDashboard/PaymentIncidentCrudTest.php
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Admin\PaymentDashboardController;
|
||||
use App\Http\Requests\PaymentIncident\AddIncidentActivityRequest;
|
||||
use App\Http\Requests\PaymentIncident\StorePaymentIncidentRequest;
|
||||
use App\Http\Requests\PaymentIncident\UpdateIncidentStatusRequest;
|
||||
use App\Models\IncidentActivity;
|
||||
use App\Models\PaymentIncident;
|
||||
use App\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
function makeAdmin(): User
|
||||
{
|
||||
return User::forceCreate([
|
||||
'email' => 'admin-crud-'.uniqid().'@test.com',
|
||||
'password' => Hash::make('secret'),
|
||||
'admin' => 2,
|
||||
'lang' => 'de',
|
||||
]);
|
||||
}
|
||||
|
||||
// ─── Model / Scopes ───────────────────────────────────────────────────────────
|
||||
|
||||
it('scopeOpen gibt nur offene, laufende und wartende Incidents zurück', function () {
|
||||
PaymentIncident::create(['title' => 'Offen', 'provider' => 'payone', 'type' => 'other', 'severity' => 'low', 'status' => 'open', 'detected_at' => now()]);
|
||||
PaymentIncident::create(['title' => 'In Bearbeitung', 'provider' => 'payone', 'type' => 'other', 'severity' => 'low', 'status' => 'in_progress', 'detected_at' => now()]);
|
||||
PaymentIncident::create(['title' => 'Wartet', 'provider' => 'payone', 'type' => 'other', 'severity' => 'low', 'status' => 'waiting_provider', 'detected_at' => now()]);
|
||||
PaymentIncident::create(['title' => 'Gelöst', 'provider' => 'payone', 'type' => 'other', 'severity' => 'low', 'status' => 'resolved', 'detected_at' => now()]);
|
||||
|
||||
$open = PaymentIncident::open()->get();
|
||||
|
||||
expect($open)->toHaveCount(3);
|
||||
expect($open->pluck('title')->toArray())->not->toContain('Gelöst');
|
||||
});
|
||||
|
||||
it('scopePayone filtert nach PAYONE', function () {
|
||||
PaymentIncident::create(['title' => 'PAYONE Incident', 'provider' => 'payone', 'type' => 'other', 'severity' => 'low', 'detected_at' => now()]);
|
||||
PaymentIncident::create(['title' => 'Stripe Incident', 'provider' => 'stripe', 'type' => 'other', 'severity' => 'low', 'detected_at' => now()]);
|
||||
|
||||
expect(PaymentIncident::payone()->count())->toBe(1);
|
||||
expect(PaymentIncident::payone()->first()->title)->toBe('PAYONE Incident');
|
||||
});
|
||||
|
||||
it('scopeLastDays filtert nach Zeitraum', function () {
|
||||
PaymentIncident::create(['title' => 'Alt', 'provider' => 'payone', 'type' => 'other', 'severity' => 'low', 'detected_at' => now()->subDays(10)]);
|
||||
PaymentIncident::create(['title' => 'Neu', 'provider' => 'payone', 'type' => 'other', 'severity' => 'low', 'detected_at' => now()]);
|
||||
|
||||
expect(PaymentIncident::lastDays(7)->count())->toBe(1);
|
||||
expect(PaymentIncident::lastDays(7)->first()->title)->toBe('Neu');
|
||||
});
|
||||
|
||||
it('Accessor severity_color gibt Bootstrap-Klasse zurück', function () {
|
||||
$incident = new PaymentIncident(['severity' => 'critical']);
|
||||
expect($incident->severity_color)->toBe('danger');
|
||||
|
||||
$incident = new PaymentIncident(['severity' => 'high']);
|
||||
expect($incident->severity_color)->toBe('warning');
|
||||
|
||||
$incident = new PaymentIncident(['severity' => 'low']);
|
||||
expect($incident->severity_color)->toBe('success');
|
||||
});
|
||||
|
||||
it('Accessor duration berechnet Dauer korrekt', function () {
|
||||
$incident = new PaymentIncident([
|
||||
'detected_at' => now()->subMinutes(90),
|
||||
'resolved_at' => null,
|
||||
]);
|
||||
|
||||
expect($incident->duration)->toContain('h');
|
||||
});
|
||||
|
||||
// ─── Controller: Incident anlegen ─────────────────────────────────────────────
|
||||
|
||||
it('legt einen neuen Incident an und erstellt automatisch eine erste Aktivität', function () {
|
||||
$admin = makeAdmin();
|
||||
$this->actingAs($admin);
|
||||
|
||||
$request = StorePaymentIncidentRequest::create('/admin/payment-dashboard', 'POST', [
|
||||
'title' => 'PAYONE IPN ausgefallen',
|
||||
'provider' => 'payone',
|
||||
'type' => 'ipn_error',
|
||||
'severity' => 'high',
|
||||
'detected_at' => now()->format('Y-m-d H:i:s'),
|
||||
'affected_orders' => 5,
|
||||
'affected_revenue' => '450.00',
|
||||
]);
|
||||
$request->setContainer(app());
|
||||
$request->validateResolved();
|
||||
|
||||
try {
|
||||
(new PaymentDashboardController)->store($request);
|
||||
} catch (\Symfony\Component\Routing\Exception\RouteNotFoundException $e) {
|
||||
// Domain-spezifische Routen sind im Test-Kontext nicht registriert – erwartet.
|
||||
}
|
||||
|
||||
$incident = PaymentIncident::where('title', 'PAYONE IPN ausgefallen')->first();
|
||||
expect($incident)->not->toBeNull();
|
||||
expect($incident->provider)->toBe('payone');
|
||||
expect($incident->status)->toBe('open');
|
||||
expect($incident->affected_orders)->toBe(5);
|
||||
|
||||
expect(IncidentActivity::where('incident_id', $incident->id)->count())->toBe(1);
|
||||
expect(IncidentActivity::where('incident_id', $incident->id)->first()->type)->toBe('note');
|
||||
});
|
||||
|
||||
// ─── FormRequest Validierung ──────────────────────────────────────────────────
|
||||
|
||||
it('validiert Pflichtfelder beim Anlegen eines Incidents', function () {
|
||||
$rules = (new StorePaymentIncidentRequest)->rules();
|
||||
$validator = Validator::make([], $rules);
|
||||
|
||||
expect($validator->fails())->toBeTrue();
|
||||
expect($validator->errors()->has('title'))->toBeTrue();
|
||||
expect($validator->errors()->has('provider'))->toBeTrue();
|
||||
expect($validator->errors()->has('type'))->toBeTrue();
|
||||
expect($validator->errors()->has('severity'))->toBeTrue();
|
||||
expect($validator->errors()->has('detected_at'))->toBeTrue();
|
||||
});
|
||||
|
||||
it('validiert ungültige Enum-Werte in StorePaymentIncidentRequest', function () {
|
||||
$rules = (new StorePaymentIncidentRequest)->rules();
|
||||
$validator = Validator::make([
|
||||
'title' => 'Test',
|
||||
'provider' => 'bitcoin',
|
||||
'type' => 'unknown',
|
||||
'severity' => 'extreme',
|
||||
'detected_at' => now()->toDateTimeString(),
|
||||
], $rules);
|
||||
|
||||
expect($validator->fails())->toBeTrue();
|
||||
expect($validator->errors()->has('provider'))->toBeTrue();
|
||||
expect($validator->errors()->has('type'))->toBeTrue();
|
||||
expect($validator->errors()->has('severity'))->toBeTrue();
|
||||
});
|
||||
|
||||
it('validiert Pflichtfelder in AddIncidentActivityRequest', function () {
|
||||
$rules = (new AddIncidentActivityRequest)->rules();
|
||||
$validator = Validator::make([], $rules);
|
||||
|
||||
expect($validator->fails())->toBeTrue();
|
||||
expect($validator->errors()->has('type'))->toBeTrue();
|
||||
expect($validator->errors()->has('title'))->toBeTrue();
|
||||
});
|
||||
|
||||
// ─── Controller: Status ändern ────────────────────────────────────────────────
|
||||
|
||||
it('ändert den Status eines Incidents und erstellt eine Aktivität', function () {
|
||||
$admin = makeAdmin();
|
||||
$this->actingAs($admin);
|
||||
|
||||
$incident = PaymentIncident::create([
|
||||
'title' => 'Status-Test',
|
||||
'provider' => 'payone',
|
||||
'type' => 'outage',
|
||||
'severity' => 'critical',
|
||||
'detected_at' => now(),
|
||||
]);
|
||||
|
||||
$request = UpdateIncidentStatusRequest::create('/admin/payment-dashboard/'.$incident->id.'/status', 'PATCH', [
|
||||
'status' => 'resolved',
|
||||
]);
|
||||
$request->setContainer(app());
|
||||
$request->validateResolved();
|
||||
|
||||
$controller = new PaymentDashboardController;
|
||||
$controller->updateStatus($request, $incident);
|
||||
|
||||
$incident->refresh();
|
||||
expect($incident->status)->toBe('resolved');
|
||||
expect($incident->resolved_at)->not->toBeNull();
|
||||
|
||||
$statusActivity = IncidentActivity::where('incident_id', $incident->id)
|
||||
->where('type', 'status_change')
|
||||
->first();
|
||||
expect($statusActivity)->not->toBeNull();
|
||||
expect($statusActivity->title)->toContain('Gelöst');
|
||||
});
|
||||
|
||||
it('setzt resolved_at nicht wenn Status in_progress ist', function () {
|
||||
$admin = makeAdmin();
|
||||
$this->actingAs($admin);
|
||||
|
||||
$incident = PaymentIncident::create([
|
||||
'title' => 'Resolved At Test',
|
||||
'provider' => 'payone',
|
||||
'type' => 'other',
|
||||
'severity' => 'low',
|
||||
'detected_at' => now(),
|
||||
]);
|
||||
|
||||
$request = UpdateIncidentStatusRequest::create('', 'PATCH', ['status' => 'in_progress']);
|
||||
$request->setContainer(app());
|
||||
$request->validateResolved();
|
||||
(new PaymentDashboardController)->updateStatus($request, $incident);
|
||||
|
||||
expect($incident->fresh()->resolved_at)->toBeNull();
|
||||
});
|
||||
|
||||
// ─── Controller: Aktivität hinzufügen ────────────────────────────────────────
|
||||
|
||||
it('fügt eine Aktivität zu einem Incident hinzu', function () {
|
||||
$admin = makeAdmin();
|
||||
$this->actingAs($admin);
|
||||
|
||||
$incident = PaymentIncident::create([
|
||||
'title' => 'Aktivitäts-Test',
|
||||
'provider' => 'payone',
|
||||
'type' => 'payment_failure',
|
||||
'severity' => 'medium',
|
||||
'detected_at' => now(),
|
||||
]);
|
||||
|
||||
$request = AddIncidentActivityRequest::create('', 'POST', [
|
||||
'type' => 'email',
|
||||
'title' => 'Mail an PAYONE gesendet',
|
||||
'content' => 'Ticket eröffnet, Antwort abwarten.',
|
||||
]);
|
||||
$request->setContainer(app());
|
||||
$request->validateResolved();
|
||||
|
||||
(new PaymentDashboardController)->addActivity($request, $incident);
|
||||
|
||||
$activity = IncidentActivity::where('incident_id', $incident->id)
|
||||
->where('type', 'email')
|
||||
->first();
|
||||
expect($activity)->not->toBeNull();
|
||||
expect($activity->title)->toBe('Mail an PAYONE gesendet');
|
||||
expect($activity->author)->toBe($admin->name ?? 'System');
|
||||
});
|
||||
|
||||
it('setzt Incident automatisch auf in_progress wenn Aktivität bei offenem Incident hinzugefügt wird', function () {
|
||||
$admin = makeAdmin();
|
||||
$this->actingAs($admin);
|
||||
|
||||
$incident = PaymentIncident::create([
|
||||
'title' => 'Auto-Status-Test',
|
||||
'provider' => 'payone',
|
||||
'type' => 'other',
|
||||
'severity' => 'low',
|
||||
'status' => 'open',
|
||||
'detected_at' => now(),
|
||||
]);
|
||||
|
||||
$request = AddIncidentActivityRequest::create('', 'POST', ['type' => 'note', 'title' => 'Erste Notiz']);
|
||||
$request->setContainer(app());
|
||||
$request->validateResolved();
|
||||
|
||||
(new PaymentDashboardController)->addActivity($request, $incident);
|
||||
|
||||
expect($incident->fresh()->status)->toBe('in_progress');
|
||||
});
|
||||
|
||||
it('validiert Pflichtfelder in UpdateIncidentStatusRequest', function () {
|
||||
$rules = (new UpdateIncidentStatusRequest)->rules();
|
||||
$validator = Validator::make([], $rules);
|
||||
|
||||
expect($validator->fails())->toBeTrue();
|
||||
expect($validator->errors()->has('status'))->toBeTrue();
|
||||
});
|
||||
|
||||
it('validiert ungültigen Status in UpdateIncidentStatusRequest', function () {
|
||||
$rules = (new UpdateIncidentStatusRequest)->rules();
|
||||
$validator = Validator::make(['status' => 'deleted'], $rules);
|
||||
|
||||
expect($validator->fails())->toBeTrue();
|
||||
expect($validator->errors()->has('status'))->toBeTrue();
|
||||
});
|
||||
|
||||
// ─── Cache Invalidierung ──────────────────────────────────────────────────────
|
||||
|
||||
it('invalidiert den Cache nach Incident-Anlage', function () {
|
||||
$admin = makeAdmin();
|
||||
$this->actingAs($admin);
|
||||
Cache::put('open_incident_count', 99, 60);
|
||||
|
||||
$request = StorePaymentIncidentRequest::create('', 'POST', [
|
||||
'title' => 'Cache-Test',
|
||||
'provider' => 'payone',
|
||||
'type' => 'other',
|
||||
'severity' => 'low',
|
||||
'detected_at' => now()->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
$request->setContainer(app());
|
||||
$request->validateResolved();
|
||||
|
||||
try {
|
||||
(new PaymentDashboardController)->store($request);
|
||||
} catch (\Symfony\Component\Routing\Exception\RouteNotFoundException $e) {
|
||||
// Domain-spezifische Routen sind im Test-Kontext nicht registriert – erwartet.
|
||||
}
|
||||
|
||||
expect(Cache::has('open_incident_count'))->toBeFalse();
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue