db = new Database(new Config()); try { $this->db->fetch('SELECT 1'); } catch (Throwable $exception) { self::markTestSkipped('Base injoignable: ' . $exception->getMessage()); } $this->code = 'it-role-' . bin2hex(random_bytes(4)); $this->permA = (int) ($this->db->fetch("SELECT id FROM permission WHERE code = 'stats.read'")['id'] ?? 0); $this->permB = (int) ($this->db->fetch("SELECT id FROM permission WHERE code = 'user.read'")['id'] ?? 0); } protected function tearDown(): void { if ($this->code !== '') { $this->db->execute('DELETE FROM role WHERE code = :c', ['c' => $this->code]); // CASCADE perms + sources } } private function makeRole(RoleRepository $repo): int { return $repo->createRole([ 'code' => $this->code, 'label' => 'IT Role', 'description' => 'jetable', 'default_route' => '/admin/dashboard', 'order_source' => null, ]); } public function testCreateRoleAndCodeUnique(): void { $repo = new RoleRepository($this->db); $id = $this->makeRole($repo); self::assertGreaterThan(0, $id); $found = $repo->findRole($id); self::assertNotNull($found); self::assertSame($this->code, (string) $found['code']); self::assertTrue($repo->codeExists($this->code)); self::assertFalse($repo->codeExists($this->code, $id)); // s'exclut lui-meme $violated = false; try { $repo->createRole(['code' => $this->code, 'label' => 'Dup', 'description' => null, 'default_route' => null, 'order_source' => null]); } catch (PDOException $exception) { $violated = (string) $exception->getCode() === '23000'; } self::assertTrue($violated, 'uk_role_code doit rejeter un doublon.'); } public function testSetPermissionsReplacesAndExposesCodes(): void { $repo = new RoleRepository($this->db); $id = $this->makeRole($repo); $repo->setPermissions($id, [$this->permA, $this->permB]); $ids = $repo->permissionIdsFor($id); sort($ids); $expected = [$this->permA, $this->permB]; sort($expected); self::assertSame($expected, $ids); $codes = $repo->permissionCodesFor($id); self::assertContains('stats.read', $codes); self::assertContains('user.read', $codes); // Delete-and-reinsert : la nouvelle selection REMPLACE l'ancienne. $repo->setPermissions($id, [$this->permA]); self::assertSame([$this->permA], $repo->permissionIdsFor($id)); // 23 permissions au catalogue (fige au seed). self::assertCount(23, $repo->allPermissions()); } public function testSetVisibleSourcesReplaces(): void { $repo = new RoleRepository($this->db); $id = $this->makeRole($repo); $repo->setVisibleSources($id, ['counter', 'drive']); $sources = $repo->visibleSources($id); sort($sources); self::assertSame(['counter', 'drive'], $sources); $repo->setVisibleSources($id, ['kiosk']); self::assertSame(['kiosk'], $repo->visibleSources($id)); } public function testUpdateRoleKeepsCodeImmutable(): void { $repo = new RoleRepository($this->db); $id = $this->makeRole($repo); $repo->updateRole($id, [ 'label' => 'Relabelled', 'description' => 'maj', 'default_route' => '/admin/stats', 'order_source' => 'counter', 'is_active' => 1, ]); $updated = $repo->findRole($id); self::assertNotNull($updated); self::assertSame('Relabelled', (string) $updated['label']); self::assertSame('/admin/stats', (string) $updated['default_route']); self::assertSame('counter', (string) $updated['order_source']); self::assertSame($this->code, (string) $updated['code']); // code inchange (immuable) } }