98 lines
6.3 KiB
PHP
98 lines
6.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Prüft zwei Dinge rund um „SLA überschritten“: (1) ein wieder geöffnetes (zuvor gelöstes) Ticket bekommt eine neue Frist und löst nicht sofort
|
|
* eine Überschreitung aus, (2) die Benachrichtigung geht standardmäßig nur per E-Mail an die Administratoren (Einstellung „Bearbeiter bzw. Abteilung“ bleibt möglich).
|
|
*/
|
|
|
|
putenv('DESKHUB_TESTING=1');
|
|
putenv('DESKHUB_SQLITE_PATH=:memory:');
|
|
require_once dirname(__DIR__) . '/includes/bootstrap.php';
|
|
if (!defined('DESKHUB_CRON_RUNNER')) define('DESKHUB_CRON_RUNNER', true);
|
|
require_once dirname(__DIR__) . '/cron/check_sla.php';
|
|
|
|
$failures = [];
|
|
$total = 0;
|
|
$check = function (bool $ok, string $what) use (&$failures, &$total): void {
|
|
$total++;
|
|
if (!$ok) $failures[] = $what;
|
|
};
|
|
$pdo = get_db();
|
|
$now = date('Y-m-d H:i:s');
|
|
$ago = fn(int $h) => date('Y-m-d H:i:s', time() - $h * 3600);
|
|
$dept = (int) $pdo->query('SELECT id FROM departments ORDER BY id LIMIT 1')->fetchColumn();
|
|
$pdo->exec('UPDATE priorities SET response_hours = 4, resolution_hours = 48');
|
|
$prio = (int) $pdo->query('SELECT id FROM priorities ORDER BY id LIMIT 1')->fetchColumn();
|
|
$pdo->exec('DELETE FROM staff');
|
|
$ins = $pdo->prepare('INSERT INTO staff (name, email, password_hash, role, department_id, is_active, created_at) VALUES (?,?,?,?,?,1,?)');
|
|
$ins->execute(['Chef', 'chef@test.de', 'x', 'admin', $dept, $now]);
|
|
$ins->execute(['Agent', 'agent@test.de', 'x', 'agent', $dept, $now]);
|
|
$agent = (int) $pdo->lastInsertId();
|
|
$ins->execute(['Kollege', 'kollege@test.de', 'x', 'agent', $dept, $now]);
|
|
|
|
$mk = function (string $n, string $status, array $o = []) use ($pdo, $dept, $prio, $now, $agent): int {
|
|
$pdo->prepare('INSERT INTO tickets (ticket_number, subject, requester_name, requester_email, department_id, priority_id, status, source, assigned_staff_id, resolution_due_at, first_response_due_at, first_responded_at, resolved_at, is_overdue, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)')
|
|
->execute([$n, 'S ' . $n, 'K', 'k@example.org', $dept, $prio, $status, 'web', $agent, $o['due'] ?? null, $o['rdue'] ?? null, $o['first'] ?? null, $o['resolved'] ?? null, $o['overdue'] ?? 0, $o['created'] ?? $now, $now]);
|
|
return (int) $pdo->lastInsertId();
|
|
};
|
|
$row = fn(int $id) => $pdo->query("SELECT * FROM tickets WHERE id = {$id}")->fetch(PDO::FETCH_ASSOC);
|
|
|
|
// ── Wieder geöffnet: neue Frist ──────────────────────────────────────────────
|
|
$old = $ago(100);
|
|
$t = $mk('R-1', 'open', ['due' => $ago(50), 'rdue' => $ago(90), 'first' => $ago(95), 'resolved' => $ago(60), 'overdue' => 1, 'created' => $old]);
|
|
ticket_sla_status_transition($pdo, $t, 'resolved', 'open');
|
|
$r = $row($t);
|
|
$check($r['resolution_due_at'] > $now && $r['is_overdue'] == 0, 'gelöst → offen: neue Lösungsfrist in der Zukunft, nicht mehr überfällig: ' . json_encode([$r['resolution_due_at'], $r['is_overdue']]));
|
|
$expected = strtotime($r['resolution_due_at']) - time();
|
|
$check($expected > 47 * 3600 && $expected < 49 * 3600, 'neue Frist = 48 Stunden der Priorität ab jetzt');
|
|
$h = $pdo->query("SELECT COUNT(*) FROM ticket_sla_history WHERE ticket_id = {$t} AND event_type = 'restarted'")->fetchColumn();
|
|
$check((int) $h === 1, 'im SLA-Verlauf steht „neu gestartet“');
|
|
$res = run_check_sla($pdo, false);
|
|
$check($res['newlyOverdue'] === 0, 'der SLA-Job meldet für das wieder geöffnete Ticket nichts: ' . json_encode($res['newlyOverdue']));
|
|
$check((int) $row($t)['is_overdue'] === 0, 'Ticket bleibt nicht überfällig');
|
|
|
|
$t2 = $mk('R-2', 'open', ['due' => $ago(50), 'overdue' => 1, 'created' => $old]);
|
|
ticket_sla_status_transition($pdo, $t2, 'closed', 'open');
|
|
$check($row($t2)['resolution_due_at'] > $now, 'geschlossen → offen: ebenfalls neue Frist');
|
|
|
|
$t3 = $mk('R-3', 'pending', ['due' => $ago(50), 'overdue' => 1, 'created' => $old]);
|
|
ticket_sla_status_transition($pdo, $t3, 'resolved', 'pending');
|
|
$r3 = $row($t3);
|
|
$check($r3['resolution_due_at'] > $now && $r3['sla_paused_at'] !== null, 'gelöst → wartet auf Kunde: neue Frist, die Uhr ist pausiert');
|
|
|
|
// Keine Änderung bei anderen Übergängen
|
|
$due = $ago(30);
|
|
$t4 = $mk('R-4', 'answered', ['due' => $due, 'created' => $old]);
|
|
ticket_sla_status_transition($pdo, $t4, 'open', 'answered');
|
|
$check($row($t4)['resolution_due_at'] === $due, 'offen → beantwortet: Frist bleibt');
|
|
$t5 = $mk('R-5', 'closed', ['due' => $due, 'created' => $old]);
|
|
ticket_sla_status_transition($pdo, $t5, 'resolved', 'closed');
|
|
$check($row($t5)['resolution_due_at'] === $due, 'gelöst → geschlossen: Frist bleibt');
|
|
$t6 = $mk('R-6', 'open', ['due' => $due, 'created' => $old]);
|
|
ticket_sla_status_transition($pdo, $t6, 'open', 'open');
|
|
$check($row($t6)['resolution_due_at'] === $due, 'gleicher Status: nichts passiert');
|
|
$t7 = $mk('R-7', 'open', ['due' => $due, 'overdue' => 1, 'created' => $old]);
|
|
$check(ticket_sla_restart($pdo, 999999) === null, 'unbekanntes Ticket: kein Fehler');
|
|
|
|
// ── Wer benachrichtigt wird ──────────────────────────────────────────────────
|
|
$tk = ['assigned_staff_id' => $agent, 'department_id' => $dept];
|
|
$check(sla_notify_mode() === 'admins', 'Standard: nur Administratoren');
|
|
$rec = array_map('strtolower', sla_overdue_recipients($pdo, $tk));
|
|
$check($rec === ['chef@test.de'], 'Empfänger: nur der Administrator, nicht Bearbeiter oder Abteilung: ' . json_encode($rec));
|
|
set_setting('sla_notify_mode', 'assignee');
|
|
$rec = array_map('strtolower', sla_overdue_recipients($pdo, $tk));
|
|
$check($rec === ['agent@test.de'], 'Einstellung „Bearbeiter bzw. Abteilung“: der Bearbeiter: ' . json_encode($rec));
|
|
set_setting('sla_notify_mode', 'unsinn');
|
|
$check(sla_notify_mode() === 'admins', 'ungültige Einstellung fällt auf Administratoren zurück');
|
|
set_setting('sla_notify_mode', 'admins');
|
|
$check(sla_overdue_recipients($pdo, ['assigned_staff_id' => null, 'department_id' => $dept]) === ['chef@test.de'], 'auch ohne Bearbeiter: nur der Administrator');
|
|
fcm_notify_overdue(['ticket_number' => 'X', 'department_id' => $dept, 'assigned_staff_id' => $agent]);
|
|
$check(true, 'Push im Administrator-Modus ohne Fehler übersprungen');
|
|
|
|
if ($failures) {
|
|
fwrite(STDERR, "Fehlgeschlagen:\n - " . implode("\n - ", $failures) . "\n");
|
|
exit(1);
|
|
}
|
|
echo "SLA reopen and notify checks passed ({$total} Prüfungen).\n";
|