customerRepository = $this->createStub(CustomerRepository::class); $this->em = $this->createStub(EntityManagerInterface::class); $this->meilisearch = $this->createStub(MeilisearchService::class); $this->logger = $this->createStub(LoggerInterface::class); } private function makeCommand(string $stripeKey = ''): CleanPendingDeleteCommand { return new CleanPendingDeleteCommand( $this->customerRepository, $this->em, $this->meilisearch, $this->logger, $stripeKey, ); } private function execute(string $stripeKey = ''): CommandTester { $tester = new CommandTester($this->makeCommand($stripeKey)); $tester->execute([]); return $tester; } private function makeCustomer(string $name = 'John Doe', string $email = 'john@example.com', ?string $stripeId = null): Customer { $user = $this->createStub(User::class); $customer = $this->createStub(Customer::class); $customer->method('getFullName')->willReturn($name); $customer->method('getEmail')->willReturn($email); $customer->method('getUser')->willReturn($user); $customer->method('getId')->willReturn(42); $customer->method('getStripeCustomerId')->willReturn($stripeId); return $customer; } // ------------------------------------------------------------------ // No pending_delete customers → SUCCESS immediately // ------------------------------------------------------------------ public function testNoPendingDeleteCustomers(): void { $this->customerRepository->method('findBy')->willReturn([]); $tester = $this->execute(); $this->assertSame(0, $tester->getStatusCode()); $this->assertStringContainsString('Aucun client en attente de suppression', $tester->getDisplay()); } // ------------------------------------------------------------------ // One customer deleted with no Stripe key and no Meilisearch error // ------------------------------------------------------------------ public function testOneCustomerDeletedSuccessfully(): void { $customer = $this->makeCustomer(); $this->customerRepository->method('findBy')->willReturn([$customer]); $em = $this->createMock(EntityManagerInterface::class); $em->expects($this->exactly(2))->method('remove'); $em->expects($this->once())->method('flush'); $this->em = $em; $tester = $this->execute(); $this->assertSame(0, $tester->getStatusCode()); $this->assertStringContainsString('1 client(s) supprime(s)', $tester->getDisplay()); } // ------------------------------------------------------------------ // Multiple customers deleted // ------------------------------------------------------------------ public function testMultipleCustomersDeleted(): void { $c1 = $this->makeCustomer('Alice', 'alice@example.com'); $c2 = $this->makeCustomer('Bob', 'bob@example.com'); $this->customerRepository->method('findBy')->willReturn([$c1, $c2]); $tester = $this->execute(); $this->assertSame(0, $tester->getStatusCode()); $this->assertStringContainsString('2 client(s) supprime(s)', $tester->getDisplay()); } // ------------------------------------------------------------------ // Meilisearch error during delete → logged as warning, continues // ------------------------------------------------------------------ public function testMeilisearchErrorIsLoggedAndContinues(): void { $customer = $this->makeCustomer(); $this->customerRepository->method('findBy')->willReturn([$customer]); $meilisearch = $this->createStub(MeilisearchService::class); $meilisearch->method('removeCustomer')->willThrowException(new \RuntimeException('Meili down')); $this->meilisearch = $meilisearch; $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->atLeastOnce()) ->method('warning') ->with($this->stringContains('Meilisearch')); $this->logger = $logger; $tester = $this->execute(); // Command must still succeed and delete the customer $this->assertSame(0, $tester->getStatusCode()); $this->assertStringContainsString('1 client(s) supprime(s)', $tester->getDisplay()); } // ------------------------------------------------------------------ // deleteFromStripe: empty SK → skipped (no Stripe call, returns early) // ------------------------------------------------------------------ public function testDeleteFromStripeSkipsOnEmptyKey(): void { $customer = $this->makeCustomer('Alice', 'alice@example.com', 'cus_123'); $this->customerRepository->method('findBy')->willReturn([$customer]); // Empty string key — guard must skip before reaching Stripe API $tester = $this->execute(''); $this->assertSame(0, $tester->getStatusCode()); } // ------------------------------------------------------------------ // deleteFromStripe: test placeholder SK → skipped // ------------------------------------------------------------------ public function testDeleteFromStripeSkipsOnTestKey(): void { $customer = $this->makeCustomer('Alice', 'alice@example.com', 'cus_123'); $this->customerRepository->method('findBy')->willReturn([$customer]); $tester = $this->execute('sk_test_***'); $this->assertSame(0, $tester->getStatusCode()); } // ------------------------------------------------------------------ // deleteFromStripe: null stripeCustomerId → skipped // ------------------------------------------------------------------ public function testDeleteFromStripeSkipsOnNullStripeCustomerId(): void { // stripeCustomerId is null → guard returns early even with a real-looking key $customer = $this->makeCustomer('Alice', 'alice@example.com', null); $this->customerRepository->method('findBy')->willReturn([$customer]); // Use a key that passes the first guard but hits the second guard (null id) $tester = $this->execute('sk_live_real_but_null_id'); $this->assertSame(0, $tester->getStatusCode()); } // ------------------------------------------------------------------ // logger->info is called for each deleted customer // ------------------------------------------------------------------ public function testLoggerInfoCalledForDeletedCustomer(): void { $customer = $this->makeCustomer('Alice', 'alice@example.com'); $this->customerRepository->method('findBy')->willReturn([$customer]); $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->once()) ->method('info') ->with($this->stringContains('alice@example.com')); $this->logger = $logger; $tester = $this->execute(); $this->assertSame(0, $tester->getStatusCode()); } }