em = self::getContainer()->get(EntityManagerInterface::class); $this->admin = new User(); $this->admin->setUsername('mcp-prest-admin-'.uniqid()); $this->admin->setPassword('x'); $this->admin->setRoles(['ROLE_ADMIN']); $this->em->persist($this->admin); $this->em->flush(); } public function testCreatePersistsAllFields(): void { $json = ($this->createTool(admin: true))('ACME Cleaning', 'contact@acme.example', '+33100000000', 'https://acme.example'); $data = json_decode($json, true); self::assertIsInt($data['id']); self::assertSame('ACME Cleaning', $data['name']); self::assertSame('contact@acme.example', $data['email']); self::assertSame('+33100000000', $data['phone']); self::assertSame('https://acme.example', $data['website']); } public function testCreateRequiresAdmin(): void { $this->expectException(AccessDeniedException::class); ($this->createTool(admin: false))('Should not pass'); } public function testGetReturnsEmptyCollectionsWhenNoChildren(): void { $created = json_decode(($this->createTool(admin: true))('Lonely Prest'), true); $json = ($this->getTool(admin: true))((int) $created['id']); $data = json_decode($json, true); self::assertSame($created['id'], $data['id']); self::assertSame([], $data['contacts']); self::assertSame([], $data['addresses']); self::assertSame([], $data['reports']); } public function testGetUnknownIdThrows(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Prestataire with ID 999999 not found.'); ($this->getTool(admin: true))(999999); } public function testUpdateOnlyTouchesProvidedFields(): void { $created = json_decode(($this->createTool(admin: true))('Before', 'before@x.test', '+33000000000', 'https://before.test'), true); $json = ($this->updateTool(admin: true))((int) $created['id'], null, 'after@x.test', null, null); $data = json_decode($json, true); self::assertSame('Before', $data['name']); // unchanged self::assertSame('after@x.test', $data['email']); // changed self::assertSame('+33000000000', $data['phone']); // unchanged self::assertSame('https://before.test', $data['website']); // unchanged } public function testListReturnsAllPrestatairesOrderedByName(): void { // Unique prefix isolates this test from data leaked by prior PHPUnit // runs (DAMA rollback is not active in this project). $prefix = 'list-test-'.uniqid().'-'; ($this->createTool(admin: true))($prefix.'Zeta'); ($this->createTool(admin: true))($prefix.'Alpha'); ($this->createTool(admin: true))($prefix.'Mu'); $data = json_decode(($this->listTool(admin: true))(), true); $names = array_values(array_filter( array_column($data, 'name'), fn ($n) => str_starts_with((string) $n, $prefix), )); self::assertSame([$prefix.'Alpha', $prefix.'Mu', $prefix.'Zeta'], $names); } public function testDeleteRemovesPrestataire(): void { $created = json_decode(($this->createTool(admin: true))('To be removed'), true); $id = (int) $created['id']; $json = ($this->deleteTool(admin: true))($id); $data = json_decode($json, true); self::assertTrue($data['success']); self::assertStringContainsString('"To be removed"', $data['message']); $this->em->clear(); self::assertNull(self::getContainer()->get(PrestataireRepositoryInterface::class)->findById($id)); } private function securityFor(bool $admin): Security { $security = $this->createMock(Security::class); $security->method('isGranted')->willReturn($admin); $security->method('getUser')->willReturn($admin ? $this->admin : null); return $security; } private function createTool(bool $admin): CreatePrestataireTool { return new CreatePrestataireTool( $this->em, $this->securityFor($admin), ); } private function getTool(bool $admin): GetPrestataireTool { $c = self::getContainer(); return new GetPrestataireTool( $c->get(PrestataireRepositoryInterface::class), $c->get(ContactRepositoryInterface::class), $c->get(AddressRepositoryInterface::class), $c->get(CommercialReportRepositoryInterface::class), $this->securityFor($admin), ); } private function updateTool(bool $admin): UpdatePrestataireTool { return new UpdatePrestataireTool( self::getContainer()->get(PrestataireRepositoryInterface::class), $this->em, $this->securityFor($admin), ); } private function listTool(bool $admin): ListPrestatairesTool { return new ListPrestatairesTool( self::getContainer()->get(PrestataireRepositoryInterface::class), $this->securityFor($admin), ); } private function deleteTool(bool $admin): DeletePrestataireTool { return new DeletePrestataireTool( self::getContainer()->get(PrestataireRepositoryInterface::class), $this->em, $this->securityFor($admin), ); } }