em = self::getContainer()->get(EntityManagerInterface::class); $this->admin = new User(); $this->admin->setUsername('mcp-contact-admin-'.uniqid()); $this->admin->setPassword('x'); $this->admin->setRoles(['ROLE_ADMIN']); $this->em->persist($this->admin); $this->client = new Client(); $this->client->setName('Test Client '.uniqid()); $this->em->persist($this->client); $this->prospect = new Prospect(); $this->prospect->setCompany('Test Prospect '.uniqid()); $this->em->persist($this->prospect); $this->prestataire = new Prestataire(); $this->prestataire->setName('Test Prestataire '.uniqid()); $this->em->persist($this->prestataire); $this->em->flush(); } public function testCreateRequiresExactlyOneParent(): void { try { ($this->createTool())(null, null, null, 'Anon'); self::fail('Expected error when no parent provided.'); } catch (InvalidArgumentException $e) { self::assertStringContainsString('Exactly one of clientId, prospectId or prestataireId', $e->getMessage()); } try { ($this->createTool())($this->client->getId(), $this->prospect->getId(), null, 'Dup'); self::fail('Expected error when two parents provided.'); } catch (InvalidArgumentException $e) { self::assertStringContainsString('Exactly one of clientId, prospectId or prestataireId', $e->getMessage()); } } public function testCreateWithUnknownClientThrows(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Client with ID 999999 not found.'); ($this->createTool())(999999, null, null, 'Anon'); } public function testCreateOnEachParentWorks(): void { foreach ( [ ['clientId', $this->client->getId()], ['prospectId', $this->prospect->getId()], ['prestataireId', $this->prestataire->getId()], ] as [$field, $id] ) { $args = [null, null, null, 'John', 'Doe-'.$field, 'CTO', 'john@x.test']; $idx = ['clientId' => 0, 'prospectId' => 1, 'prestataireId' => 2][$field]; $args[$idx] = $id; $data = json_decode(($this->createTool())(...$args), true); self::assertSame('Doe-'.$field, $data['lastName']); self::assertSame($id, $data[$field]); } } public function testGetReturnsContact(): void { $created = json_decode(($this->createTool())($this->client->getId(), null, null, 'Jane', 'Smith'), true); $data = json_decode(($this->getTool())((int) $created['id']), true); self::assertSame('Jane', $data['firstName']); self::assertSame('Smith', $data['lastName']); self::assertSame($this->client->getId(), $data['clientId']); } public function testListFilteredByPrestataire(): void { ($this->createTool())(null, null, $this->prestataire->getId(), 'A', 'A-Last'); ($this->createTool())(null, null, $this->prestataire->getId(), 'B', 'B-Last'); ($this->createTool())($this->client->getId(), null, null, 'Z', 'Z-Last'); $data = json_decode(($this->listTool())(null, null, $this->prestataire->getId()), true); self::assertCount(2, $data); self::assertSame('A-Last', $data[0]['lastName']); self::assertSame('B-Last', $data[1]['lastName']); } public function testListRejectsMultipleFilters(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('At most one of clientId, prospectId or prestataireId'); ($this->listTool())($this->client->getId(), $this->prospect->getId(), null); } public function testUpdateOnlyTouchesProvidedFields(): void { $created = json_decode(($this->createTool())(null, null, $this->prestataire->getId(), 'Old', 'Last', 'CTO', 'old@x.test'), true); $data = json_decode(($this->updateTool())((int) $created['id'], 'New', null, null, 'new@x.test'), true); self::assertSame('New', $data['firstName']); // changed self::assertSame('Last', $data['lastName']); // unchanged self::assertSame('CTO', $data['jobTitle']); // unchanged self::assertSame('new@x.test', $data['email']); // changed } public function testDeleteRemovesContact(): void { $created = json_decode(($this->createTool())($this->client->getId(), null, null, 'Bye'), true); $id = (int) $created['id']; $data = json_decode(($this->deleteTool())($id), true); self::assertTrue($data['success']); $this->em->clear(); self::assertNull(self::getContainer()->get(ContactRepositoryInterface::class)->findById($id)); } private function securityFor(bool $admin = true): Security { $security = $this->createMock(Security::class); $security->method('isGranted')->willReturn($admin); $security->method('getUser')->willReturn($admin ? $this->admin : null); return $security; } private function createTool(): CreateContactTool { $c = self::getContainer(); return new CreateContactTool( $this->em, $c->get(ClientRepositoryInterface::class), $c->get(ProspectRepositoryInterface::class), $c->get(PrestataireRepositoryInterface::class), $this->securityFor(), ); } private function getTool(): GetContactTool { return new GetContactTool( self::getContainer()->get(ContactRepositoryInterface::class), $this->securityFor(), ); } private function listTool(): ListContactsTool { return new ListContactsTool( self::getContainer()->get(ContactRepositoryInterface::class), $this->securityFor(), ); } private function updateTool(): UpdateContactTool { return new UpdateContactTool( self::getContainer()->get(ContactRepositoryInterface::class), $this->em, $this->securityFor(), ); } private function deleteTool(): DeleteContactTool { return new DeleteContactTool( self::getContainer()->get(ContactRepositoryInterface::class), $this->em, $this->securityFor(), ); } }