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); $this->db->exec('PRAGMA foreign_keys=ON'); 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); } }; $sanitizer = new class () implements HtmlSanitizerInterface { public function sanitize(string $html): string { return $html; } }; $transactionManager = new class () implements TransactionManagerInterface { public function run(callable $operation): mixed { return $operation(); } }; $referenceExtractor = new class () implements PostMediaReferenceExtractorInterface { public function extractMediaIds(string $html): array { return []; } }; $usageRepository = new class () implements PostMediaUsageRepositoryInterface { public function countUsages(int $mediaId): int { return 0; } public function countUsagesByMediaIds(array $mediaIds): array { return []; } public function findUsages(int $mediaId, int $limit = 5): array { return []; } public function findUsagesByMediaIds(array $mediaIds, int $limit = 5): array { return []; } public function syncPostMedia(int $postId, array $mediaIds): void {} }; $slugGenerator = new PostSlugGenerator(); $service = new PostApplicationService( $repo, new CreatePost($repo, $sanitizer, $slugGenerator, $transactionManager, $referenceExtractor, $usageRepository), new UpdatePost($repo, $sanitizer, $slugGenerator, $transactionManager, $referenceExtractor, $usageRepository), new DeletePost($repo), ); $this->expectException(NotFoundException::class); $service->update(1, 'Titre modifié', '

Contenu modifié

'); } }