em = $this->createMock(EntityManagerInterface::class); $this->mailer = $this->createMock(MailerService::class); } private function makePrestataire(string $raisonSociale, ?string $siret = null): Prestataire { $p = new Prestataire($raisonSociale); if (null !== $siret) { $p->setSiret($siret); } return $p; } private function makeFacturePrestataire(int $year, int $month): FacturePrestataire { $f = $this->createStub(FacturePrestataire::class); $f->method('getYear')->willReturn($year); $f->method('getMonth')->willReturn($month); return $f; } private function stubRepository(array $prestataires): void { $repo = $this->createStub(EntityRepository::class); $repo->method('findBy')->willReturn($prestataires); $this->em->method('getRepository') ->with(Prestataire::class) ->willReturn($repo); } public function testNoActivePrestatairesReturnsSuccess(): void { $this->stubRepository([]); $this->mailer->expects($this->never())->method('sendEmail'); $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer); $tester = new CommandTester($command); $tester->execute([]); $this->assertSame(0, $tester->getStatusCode()); $this->assertStringContainsString('Toutes les factures prestataires', $tester->getDisplay()); } public function testAllPrestatairesHaveFactureForPreviousMonth(): void { $now = new \DateTimeImmutable(); $prev = $now->modify('first day of last month'); $year = (int) $prev->format('Y'); $month = (int) $prev->format('n'); $presta = $this->makePrestataire('ACME Corp', '12345678900011'); $facture = $this->makeFacturePrestataire($year, $month); $presta->getFactures()->add($facture); $this->stubRepository([$presta]); $this->mailer->expects($this->never())->method('sendEmail'); $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer); $tester = new CommandTester($command); $tester->execute([]); $this->assertSame(0, $tester->getStatusCode()); $this->assertStringContainsString('Toutes les factures prestataires', $tester->getDisplay()); } public function testMissingFacturesSendsReminderEmail(): void { $presta1 = $this->makePrestataire('ACME Corp', '12345678900011'); $presta2 = $this->makePrestataire('Beta SARL'); // presta1 has no factures for the previous month, presta2 has none at all $this->stubRepository([$presta1, $presta2]); $this->mailer->expects($this->once()) ->method('getAdminEmail') ->willReturn('admin@e-cosplay.fr'); $this->mailer->expects($this->once()) ->method('sendEmail') ->with( 'admin@e-cosplay.fr', $this->stringContains('Rappel : factures prestataires'), $this->stringContains('ACME Corp'), null, null, false, ); $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer); $tester = new CommandTester($command); $tester->execute([]); $this->assertSame(0, $tester->getStatusCode()); $display = $tester->getDisplay(); $this->assertStringContainsString('2 facture(s) prestataire(s) manquante(s)', $display); $this->assertStringContainsString('rappel envoye', $display); } public function testPrestataireWithFactureForDifferentMonthStillMissing(): void { $now = new \DateTimeImmutable(); $prev = $now->modify('first day of last month'); $year = (int) $prev->format('Y'); $month = (int) $prev->format('n'); // Facture for a different month (two months ago) $wrongMonth = $month === 1 ? 12 : $month - 1; $wrongYear = $month === 1 ? $year - 1 : $year; $presta = $this->makePrestataire('Old Corp', '99999999900011'); $oldFacture = $this->makeFacturePrestataire($wrongYear, $wrongMonth); $presta->getFactures()->add($oldFacture); $this->stubRepository([$presta]); $this->mailer->method('getAdminEmail')->willReturn('admin@e-cosplay.fr'); $this->mailer->expects($this->once())->method('sendEmail'); $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer); $tester = new CommandTester($command); $tester->execute([]); $this->assertSame(0, $tester->getStatusCode()); $this->assertStringContainsString('1 facture(s) prestataire(s) manquante(s)', $tester->getDisplay()); } public function testEmailContainsPrestataireDetails(): void { $presta = $this->makePrestataire('Dupont & Cie', '11122233300011'); $this->stubRepository([$presta]); $this->mailer->method('getAdminEmail')->willReturn('admin@e-cosplay.fr'); $capturedHtml = null; $this->mailer->method('sendEmail') ->willReturnCallback(function (string $to, string $subject, string $html) use (&$capturedHtml) { $capturedHtml = $html; }); $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer); $tester = new CommandTester($command); $tester->execute([]); $this->assertNotNull($capturedHtml); $this->assertStringContainsString('Dupont & Cie', $capturedHtml); $this->assertStringContainsString('11122233300011', $capturedHtml); $this->assertStringContainsString('crm.e-cosplay.fr/admin/prestataires', $capturedHtml); } public function testPrestataireWithoutSiretSendsEmailWithoutSiret(): void { $presta = $this->makePrestataire('Anonymous Corp'); $this->stubRepository([$presta]); $this->mailer->method('getAdminEmail')->willReturn('admin@e-cosplay.fr'); $capturedHtml = null; $this->mailer->method('sendEmail') ->willReturnCallback(function (string $to, string $subject, string $html) use (&$capturedHtml) { $capturedHtml = $html; }); $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer); $tester = new CommandTester($command); $tester->execute([]); $this->assertSame(0, $tester->getStatusCode()); $this->assertNotNull($capturedHtml); $this->assertStringContainsString('Anonymous Corp', $capturedHtml); } }