Files

78 lines
3.6 KiB
PHP
Raw Permalink Normal View History

<?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");