mailManager = $this->createMock(IManager::class); $this->mailMergeService = new MailMergeService($this->mailManager); } private function makeMessage(): MailMergeMessage { return (new MailMergeMessage()) ->setFrom('sender@example.com') ->setTo('recipient@example.com') ->setSubject('Hello') ->setBodyPlain('Plain text') ->setBodyHtml('
HTML
'); } private function stubProviderMessage(): MailProviderIMessage&MockObject { $message = $this->createMock(MailProviderIMessage::class); $message->method('setFrom')->willReturnSelf(); $message->method('setTo')->willReturnSelf(); $message->method('setSubject')->willReturnSelf(); $message->method('setBodyHtml')->willReturnSelf(); $message->method('setBodyPlain')->willReturnSelf(); return $message; } /** * Throws when the mail manager cannot find a service for the given user and address. */ public function testSendThrowsWhenServiceNotFound(): void { $this->mailManager->method('findServiceByAddress')->willReturn(null); $this->expectException(Exception::class); $this->mailMergeService->send('user1', $this->makeMessage()); } /** * Throws when the found service does not have the MessageSend capability. */ public function testSendThrowsWhenServiceNotCapable(): void { $service = $this->createStub(IService::class); $service->method('capable')->willReturn(false); $this->mailManager->method('findServiceByAddress')->willReturn($service); $this->expectException(Exception::class); $this->mailMergeService->send('user1', $this->makeMessage()); } /** * Does not call setAttachments when the message carries no attachment. */ public function testSendHtmlEmailDoesNotSetAttachments(): void { $providerMessage = $this->stubProviderMessage(); $providerMessage->expects($this->never())->method('setAttachments'); $service = $this->createStub(IServiceWithSend::class); $service->method('capable')->willReturn(true); $service->method('initiateMessage')->willReturn($providerMessage); $this->mailManager->method('findServiceByAddress')->willReturn($service); $this->mailMergeService->send('user1', $this->makeMessage()); } /** * Calls setAttachments exactly once when the message carries an attachment. */ public function testSendWithAttachmentCallsSetAttachments(): void { $providerMessage = $this->stubProviderMessage(); $providerMessage->expects($this->once())->method('setAttachments'); $service = $this->createStub(IServiceWithSend::class); $service->method('capable')->willReturn(true); $service->method('initiateMessage')->willReturn($providerMessage); $this->mailManager->method('findServiceByAddress')->willReturn($service); $attachment = (new MailMergeAttachment()) ->setName('report.pdf') ->setExtension('pdf') ->setContent('%PDF-content'); $this->mailMergeService->send('user1', $this->makeMessage()->setAttachment($attachment)); } /** * Calls sendMessage exactly once on the mail service. */ public function testSendCallsSendMessage(): void { $providerMessage = $this->stubProviderMessage(); $service = $this->createMock(IServiceWithSend::class); $service->method('capable')->willReturn(true); $service->method('initiateMessage')->willReturn($providerMessage); $service->expects($this->once())->method('sendMessage'); $this->mailManager->method('findServiceByAddress')->willReturn($service); $this->mailMergeService->send('user1', $this->makeMessage()); } /** * Propagates exceptions thrown by sendMessage without wrapping them. */ public function testSendPropagatesExceptionFromSendMessage(): void { $providerMessage = $this->stubProviderMessage(); $service = $this->createStub(IServiceWithSend::class); $service->method('capable')->willReturn(true); $service->method('initiateMessage')->willReturn($providerMessage); $service->method('sendMessage')->willThrowException(new Exception('SMTP error')); $this->mailManager->method('findServiceByAddress')->willReturn($service); $this->expectException(Exception::class); $this->expectExceptionMessage('SMTP error'); $this->mailMergeService->send('user1', $this->makeMessage()); } }