db = new PDO('sqlite::memory:', options: [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); $this->db->sqliteCreateFunction('strip_tags', 'strip_tags', 1); Migrator::run($this->db); $this->db->exec("INSERT INTO users (id, username, email, password_hash, role, created_at) VALUES (1, 'alice', 'alice@example.com', 'hash', 'user', '2024-01-01 00:00:00')"); $this->db->exec("INSERT INTO posts (id, title, content, slug, author_id, created_at, updated_at) VALUES (1, 'Titre', '

Contenu

', 'titre', 1, '2024-01-01 00:00:00', '2024-01-01 00:00:00')"); } public function testUpdatePostThrowsWhenRowDisappearsBetweenReadAndWrite(): void { $realRepo = new PdoPostRepository($this->db); $repo = new class($realRepo) implements PostRepositoryInterface { private bool $deleted = false; public function __construct(private readonly PdoPostRepository $inner) {} public function findAll(?int $categoryId = null): array { return $this->inner->findAll($categoryId); } public function findPage(int $limit, int $offset, ?int $categoryId = null): array { return $this->inner->findPage($limit, $offset, $categoryId); } public function countAll(?int $categoryId = null): int { return $this->inner->countAll($categoryId); } public function findRecent(int $limit): array { return $this->inner->findRecent($limit); } public function findByUserId(int $userId, ?int $categoryId = null): array { return $this->inner->findByUserId($userId, $categoryId); } public function findByUserPage(int $userId, int $limit, int $offset, ?int $categoryId = null): array { return $this->inner->findByUserPage($userId, $limit, $offset, $categoryId); } public function countByUserId(int $userId, ?int $categoryId = null): int { return $this->inner->countByUserId($userId, $categoryId); } public function findBySlug(string $slug): ?Post { return $this->inner->findBySlug($slug); } public function findById(int $id): ?Post { return $this->inner->findById($id); } public function create(Post $post, string $slug, int $authorId, ?int $categoryId): int { return $this->inner->create($post, $slug, $authorId, $categoryId); } public function update(int $id, Post $post, string $slug, ?int $categoryId): int { if (!$this->deleted) { $this->deleted = true; $this->inner->delete($id); } return $this->inner->update($id, $post, $slug, $categoryId); } public function delete(int $id): int { return $this->inner->delete($id); } public function search(string $query, ?int $categoryId = null, ?int $authorId = null): array { return $this->inner->search($query, $categoryId, $authorId); } public function searchPage(string $query, int $limit, int $offset, ?int $categoryId = null, ?int $authorId = null): array { return $this->inner->searchPage($query, $limit, $offset, $categoryId, $authorId); } public function countSearch(string $query, ?int $categoryId = null, ?int $authorId = null): int { return $this->inner->countSearch($query, $categoryId, $authorId); } public function slugExists(string $slug, ?int $excludeId = null): bool { return $this->inner->slugExists($slug, $excludeId); } public function countByEmbeddedMediaUrl(string $url): int { return $this->inner->countByEmbeddedMediaUrl($url); } public function findByEmbeddedMediaUrl(string $url, int $limit = 5): array { return $this->inner->findByEmbeddedMediaUrl($url, $limit); } }; $sanitizer = new class implements HtmlSanitizerInterface { public function sanitize(string $html): string { return $html; } }; $service = new PostApplicationService($repo, $sanitizer); $this->expectException(NotFoundException::class); $service->updatePost(1, 'Titre modifié', '

Contenu modifié

'); } }