markConfirmed(new \DateTimeImmutable('2026-04-29T10:00:00+00:00')); $repository = $this->createMock(NewsletterConsentRepository::class); $repository ->expects(self::once()) ->method('findOneByEmailAndListId') ->with('customer@example.com', 123) ->willReturn($consent); $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->expects(self::never())->method('persist'); $entityManager->expects(self::once())->method('flush'); $handler = new MailjetNewsletterEventHandler($repository, $entityManager); $handler(new MailjetNewsletterEventMessage( email: 'customer@example.com', mailjetListId: 123, event: MailjetNewsletterEventMessage::EVENT_UNSUBSCRIBE, eventAt: new \DateTimeImmutable('2026-04-29T11:00:00+00:00'), )); self::assertTrue($consent->isRevoked()); self::assertSame('2026-04-29T11:00:00+00:00', $consent->getRevokedAt()?->format(DATE_ATOM)); } public function testUnsubscribeCreatesRevokedTombstoneForUnknownConsent(): void { $persistedConsent = null; $repository = $this->createMock(NewsletterConsentRepository::class); $repository ->expects(self::once()) ->method('findOneByEmailAndListId') ->with('customer@example.com', 123) ->willReturn(null); $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager ->expects(self::once()) ->method('persist') ->with(self::callback(static function (NewsletterConsent $consent) use (&$persistedConsent): bool { $persistedConsent = $consent; return true; })); $entityManager->expects(self::once())->method('flush'); $handler = new MailjetNewsletterEventHandler($repository, $entityManager); $handler(new MailjetNewsletterEventMessage( email: 'customer@example.com', mailjetListId: 123, event: MailjetNewsletterEventMessage::EVENT_UNSUBSCRIBE, eventAt: new \DateTimeImmutable('2026-04-29T11:00:00+00:00'), )); self::assertInstanceOf(NewsletterConsent::class, $persistedConsent); self::assertSame('customer@example.com', $persistedConsent->getEmail()); self::assertSame(123, $persistedConsent->getMailjetListId()); self::assertTrue($persistedConsent->isRevoked()); } public function testDuplicateOlderUnsubscribeDoesNotRewriteConsent(): void { $consent = new NewsletterConsent('customer@example.com', 123); $consent->markRevoked(new \DateTimeImmutable('2026-04-29T11:00:00+00:00')); $repository = $this->createStub(NewsletterConsentRepository::class); $repository->method('findOneByEmailAndListId')->willReturn($consent); $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->expects(self::never())->method('flush'); $handler = new MailjetNewsletterEventHandler($repository, $entityManager); $handler(new MailjetNewsletterEventMessage( email: 'customer@example.com', mailjetListId: 123, event: MailjetNewsletterEventMessage::EVENT_UNSUBSCRIBE, eventAt: new \DateTimeImmutable('2026-04-29T10:00:00+00:00'), )); self::assertSame('2026-04-29T11:00:00+00:00', $consent->getRevokedAt()?->format(DATE_ATOM)); } public function testOlderUnsubscribeDoesNotRevokeNewerConfirmation(): void { $consent = new NewsletterConsent('customer@example.com', 123); $consent->markConfirmed(new \DateTimeImmutable('2026-04-29T11:00:00+00:00')); $repository = $this->createStub(NewsletterConsentRepository::class); $repository->method('findOneByEmailAndListId')->willReturn($consent); $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->expects(self::never())->method('flush'); $handler = new MailjetNewsletterEventHandler($repository, $entityManager); $handler(new MailjetNewsletterEventMessage( email: 'customer@example.com', mailjetListId: 123, event: MailjetNewsletterEventMessage::EVENT_UNSUBSCRIBE, eventAt: new \DateTimeImmutable('2026-04-29T10:00:00+00:00'), )); self::assertTrue($consent->isConfirmed()); self::assertNull($consent->getRevokedAt()); } public function testDuplicateUnsubscribeWithoutTimestampDoesNotRewriteConsent(): void { $consent = new NewsletterConsent('customer@example.com', 123); $consent->markRevoked(new \DateTimeImmutable('2026-04-29T11:00:00+00:00')); $repository = $this->createStub(NewsletterConsentRepository::class); $repository->method('findOneByEmailAndListId')->willReturn($consent); $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->expects(self::never())->method('flush'); $handler = new MailjetNewsletterEventHandler($repository, $entityManager); $handler(new MailjetNewsletterEventMessage( email: 'customer@example.com', mailjetListId: 123, event: MailjetNewsletterEventMessage::EVENT_UNSUBSCRIBE, )); self::assertSame('2026-04-29T11:00:00+00:00', $consent->getRevokedAt()?->format(DATE_ATOM)); } }