feat: complete jobcard management workflows and UI
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../app/Domain/Notification/NotificationRecord.php';
|
||||
require_once __DIR__ . '/../app/Domain/Notification/NotificationQueue.php';
|
||||
|
||||
use App\Domain\Notification\NotificationQueue;
|
||||
|
||||
final class NotificationFakeStatement extends PDOStatement
|
||||
{
|
||||
private array $params = [];
|
||||
public function __construct(private readonly NotificationFakePdo $pdo, private readonly string $sql) {}
|
||||
public function execute(?array $params = null): bool
|
||||
{
|
||||
$this->params = $params ?? [];
|
||||
if (str_starts_with($this->sql, 'SELECT')) {
|
||||
$this->pdo->selectedEmail = (string)($this->params['email'] ?? '');
|
||||
return true;
|
||||
}
|
||||
if (str_starts_with($this->sql, 'INSERT') && $this->pdo->failOnInsert === $this->params['user']) {
|
||||
throw new RuntimeException('simulated partial failure');
|
||||
}
|
||||
if (str_starts_with($this->sql, 'UPDATE')) {
|
||||
$this->pdo->updateParams = $this->params;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public function fetchColumn(int $column = 0): mixed
|
||||
{
|
||||
return $this->pdo->users[$this->pdo->selectedEmail] ?? false;
|
||||
}
|
||||
public function rowCount(): int { return $this->pdo->updateRowCount; }
|
||||
}
|
||||
|
||||
final class NotificationFakePdo extends PDO
|
||||
{
|
||||
public array $users = ['one@example.com' => 1, 'two@example.com' => 2];
|
||||
public ?string $selectedEmail = null;
|
||||
public mixed $failOnInsert = null;
|
||||
public int $updateRowCount = 1;
|
||||
public array $updateParams = [];
|
||||
public bool $rolledBack = false;
|
||||
public bool $inTxn = false;
|
||||
public function __construct() {}
|
||||
public function beginTransaction(): bool { $this->inTxn = true; return true; }
|
||||
public function inTransaction(): bool { return $this->inTxn; }
|
||||
public function rollBack(): bool { $this->rolledBack = true; $this->inTxn = false; return true; }
|
||||
public function commit(): bool { $this->inTxn = false; return true; }
|
||||
public function prepare(string $query, array $options = []): PDOStatement|false { return new NotificationFakeStatement($this, $query); }
|
||||
public function lastInsertId(?string $name = null): string { return '1'; }
|
||||
}
|
||||
|
||||
function notification_persistence_assert(bool $condition, string $message): void
|
||||
{
|
||||
if (!$condition) throw new RuntimeException($message);
|
||||
}
|
||||
|
||||
$pdo = new NotificationFakePdo();
|
||||
$pdo->failOnInsert = 2;
|
||||
$queue = new NotificationQueue();
|
||||
try {
|
||||
$queue->enqueue($pdo, [
|
||||
'type' => 'assignment_created', 'recipients' => ['one@example.com', 'two@example.com'],
|
||||
'title' => 'Assigned', 'body' => 'Jobcard assigned', 'deduplication_key' => 'assignment:42',
|
||||
]);
|
||||
throw new RuntimeException('Expected the simulated second-recipient failure.');
|
||||
} catch (RuntimeException $error) {
|
||||
notification_persistence_assert($error->getMessage() === 'simulated partial failure', 'The simulated partial failure should be surfaced.');
|
||||
}
|
||||
notification_persistence_assert($pdo->rolledBack, 'A partial recipient failure must roll back the whole queue transaction.');
|
||||
|
||||
notification_persistence_assert($queue->markRead($pdo, 7, ['notification_id' => '9']), 'Mark-read should update a user-owned row.');
|
||||
notification_persistence_assert($pdo->updateParams === ['id' => 9, 'user' => 7], 'Mark-read must use schema-aligned id and user_id predicates.');
|
||||
notification_persistence_assert($queue->markUnread($pdo, '7', ['id' => '9']), 'Mark-unread should update a user-owned row.');
|
||||
notification_persistence_assert($pdo->updateParams === ['id' => 9, 'user' => 7], 'Mark-unread must use schema-aligned id and user_id predicates.');
|
||||
|
||||
printf("Notification persistence tests: 6 passed\n");
|
||||
Reference in New Issue
Block a user