diff --git a/api/pom.xml b/api/pom.xml
index e18ba55d2..95d6e5290 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -137,7 +137,7 @@
org.mockito
- mockito-junit-jupiter
+ mockito-core
test
diff --git a/api/src/main/java/com/google/appengine/api/mail/EnvironmentProvider.java b/api/src/main/java/com/google/appengine/api/mail/EnvironmentProvider.java
new file mode 100644
index 000000000..74be3c2d1
--- /dev/null
+++ b/api/src/main/java/com/google/appengine/api/mail/EnvironmentProvider.java
@@ -0,0 +1,22 @@
+package com.google.appengine.api.mail;
+
+/**
+ * An interface for providing environment variables.
+ */
+interface EnvironmentProvider {
+ /**
+ * Gets the value of the specified environment variable.
+ * @param name the name of the environment variable
+ * @return the string value of the variable, or {@code null} if the variable is not defined
+ */
+ String getenv(String name);
+
+ /**
+ * Gets the value of the specified environment variable, returning a default value if the
+ * variable is not defined.
+ * @param name the name of the environment variable
+ * @param defaultValue the default value to return
+ * @return the string value of the variable, or the default value if the variable is not defined
+ */
+ String getenv(String name, String defaultValue);
+}
diff --git a/api/src/main/java/com/google/appengine/api/mail/MailServiceImpl.java b/api/src/main/java/com/google/appengine/api/mail/LegacyMailServiceImpl.java
similarity index 86%
rename from api/src/main/java/com/google/appengine/api/mail/MailServiceImpl.java
rename to api/src/main/java/com/google/appengine/api/mail/LegacyMailServiceImpl.java
index 55700f61b..f484eb858 100644
--- a/api/src/main/java/com/google/appengine/api/mail/MailServiceImpl.java
+++ b/api/src/main/java/com/google/appengine/api/mail/LegacyMailServiceImpl.java
@@ -23,6 +23,26 @@
import com.google.apphosting.api.ApiProxy;
import com.google.protobuf.ByteString;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.mail.Address;
+import javax.mail.BodyPart;
+import javax.mail.Message.RecipientType;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.util.ByteArrayDataSource;
/**
* This class implements raw access to the mail service.
@@ -31,9 +51,12 @@
* convenience methods that JavaMail provides.
*
*/
-class MailServiceImpl implements MailService {
+class LegacyMailServiceImpl implements MailService {
static final String PACKAGE = "mail";
+ /** Default constructor. */
+ LegacyMailServiceImpl() {}
+
/** {@inheritDoc} */
@Override
public void sendToAdmins(Message message)
@@ -47,7 +70,7 @@ public void send(Message message)
throws IllegalArgumentException, IOException {
doSend(message, false);
}
-
+
/**
* Does the actual sending of the message.
* @param message The message to be sent.
diff --git a/api/src/main/java/com/google/appengine/api/mail/MailServiceFactoryImpl.java b/api/src/main/java/com/google/appengine/api/mail/MailServiceFactoryImpl.java
index a3d05702b..bbf2862df 100644
--- a/api/src/main/java/com/google/appengine/api/mail/MailServiceFactoryImpl.java
+++ b/api/src/main/java/com/google/appengine/api/mail/MailServiceFactoryImpl.java
@@ -21,8 +21,22 @@
*/
final class MailServiceFactoryImpl implements IMailServiceFactory {
+ private final EnvironmentProvider envProvider;
+
+ MailServiceFactoryImpl() {
+ this.envProvider = new SystemEnvironmentProvider();
+ }
+
+ // For testing
+ MailServiceFactoryImpl(EnvironmentProvider envProvider) {
+ this.envProvider = envProvider;
+ }
+
@Override
public MailService getMailService() {
- return new MailServiceImpl();
+ if ("true".equals(envProvider.getenv("APPENGINE_USE_SMTP_MAIL_SERVICE"))) {
+ return new SmtpMailServiceImpl(envProvider);
+ }
+ return new LegacyMailServiceImpl();
}
}
diff --git a/api/src/main/java/com/google/appengine/api/mail/SmtpMailServiceImpl.java b/api/src/main/java/com/google/appengine/api/mail/SmtpMailServiceImpl.java
new file mode 100644
index 000000000..d3a0cd71d
--- /dev/null
+++ b/api/src/main/java/com/google/appengine/api/mail/SmtpMailServiceImpl.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.appengine.api.mail;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.mail.Address;
+import javax.mail.Message.RecipientType;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.util.ByteArrayDataSource;
+
+/**
+ * This class implements the MailService interface using an external SMTP server.
+ */
+class SmtpMailServiceImpl implements MailService {
+ private final EnvironmentProvider envProvider;
+
+ /**
+ * Constructor.
+ * @param envProvider The provider for environment variables.
+ */
+ SmtpMailServiceImpl(EnvironmentProvider envProvider) {
+ this.envProvider = envProvider;
+ }
+
+ @Override
+ public void send(Message message) throws IOException {
+ sendSmtp(message, false);
+ }
+
+ @Override
+ public void sendToAdmins(Message message) throws IOException {
+ sendSmtp(message, true);
+ }
+
+ private void sendSmtp(Message message, boolean toAdmin)
+ throws IllegalArgumentException, IOException {
+ String smtpHost = envProvider.getenv("APPENGINE_SMTP_HOST");
+ if (smtpHost == null || smtpHost.isEmpty()) {
+ throw new IllegalArgumentException("SMTP_HOST environment variable is not set.");
+ }
+ Properties props = new Properties();
+ props.put("mail.smtp.host", smtpHost);
+ props.put("mail.smtp.port", envProvider.getenv("APPENGINE_SMTP_PORT"));
+ props.put("mail.smtp.auth", "true");
+ if (Boolean.parseBoolean(envProvider.getenv("APPENGINE_SMTP_USE_TLS"))) {
+ props.put("mail.smtp.starttls.enable", "true");
+ }
+
+ Session session = Session.getInstance(props, new javax.mail.Authenticator() {
+ protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
+ return new javax.mail.PasswordAuthentication(
+ envProvider.getenv("APPENGINE_SMTP_USER"), envProvider.getenv("APPENGINE_SMTP_PASSWORD"));
+ }
+ });
+
+ try {
+ MimeMessage mimeMessage = new MimeMessage(session);
+ mimeMessage.setFrom(new InternetAddress(message.getSender()));
+
+ List toRecipients = new ArrayList<>();
+ List ccRecipients = new ArrayList<>();
+ List bccRecipients = new ArrayList<>();
+
+ if (toAdmin) {
+ String adminRecipients = envProvider.getenv("APPENGINE_ADMIN_EMAIL_RECIPIENTS");
+ if (adminRecipients == null || adminRecipients.isEmpty()) {
+ throw new IllegalArgumentException("Admin recipients not configured.");
+ }
+ toRecipients.addAll(Arrays.asList(InternetAddress.parse(adminRecipients)));
+ } else {
+ if (message.getTo() != null) {
+ toRecipients.addAll(toInternetAddressList(message.getTo()));
+ }
+ if (message.getCc() != null) {
+ ccRecipients.addAll(toInternetAddressList(message.getCc()));
+ }
+ if (message.getBcc() != null) {
+ bccRecipients.addAll(toInternetAddressList(message.getBcc()));
+ }
+ }
+
+ List allTransportRecipients = new ArrayList<>();
+ allTransportRecipients.addAll(toRecipients);
+ allTransportRecipients.addAll(ccRecipients);
+ allTransportRecipients.addAll(bccRecipients);
+
+ if (allTransportRecipients.isEmpty()) {
+ throw new IllegalArgumentException("No recipients specified.");
+ }
+
+ if (!toRecipients.isEmpty()) {
+ mimeMessage.setRecipients(RecipientType.TO, toRecipients.toArray(new Address[0]));
+ }
+ if (!ccRecipients.isEmpty()) {
+ mimeMessage.setRecipients(RecipientType.CC, ccRecipients.toArray(new Address[0]));
+ }
+
+ if (message.getReplyTo() != null) {
+ mimeMessage.setReplyTo(new Address[] {new InternetAddress(message.getReplyTo())});
+ }
+
+ mimeMessage.setSubject(message.getSubject());
+
+ final boolean hasAttachments = message.getAttachments() != null && !message.getAttachments().isEmpty();
+ final boolean hasHtmlBody = message.getHtmlBody() != null;
+ final boolean hasAmpHtmlBody = message.getAmpHtmlBody() != null;
+ final boolean hasTextBody = message.getTextBody() != null;
+
+ if (hasTextBody && !hasHtmlBody && !hasAmpHtmlBody && !hasAttachments) {
+ mimeMessage.setText(message.getTextBody());
+ } else {
+ MimeMultipart topLevelMultipart = new MimeMultipart("mixed");
+
+ if (hasTextBody || hasHtmlBody || hasAmpHtmlBody) {
+ MimeMultipart alternativeMultipart = new MimeMultipart("alternative");
+ MimeBodyPart alternativeBodyPart = new MimeBodyPart();
+ alternativeBodyPart.setContent(alternativeMultipart);
+
+ if (hasTextBody) {
+ MimeBodyPart textPart = new MimeBodyPart();
+ textPart.setText(message.getTextBody());
+ alternativeMultipart.addBodyPart(textPart);
+ } else if (hasHtmlBody) {
+ MimeBodyPart textPart = new MimeBodyPart();
+ textPart.setText("");
+ alternativeMultipart.addBodyPart(textPart);
+ }
+
+ if (hasHtmlBody) {
+ MimeBodyPart htmlPart = new MimeBodyPart();
+ htmlPart.setContent(message.getHtmlBody(), "text/html");
+ alternativeMultipart.addBodyPart(htmlPart);
+ }
+ if (hasAmpHtmlBody) {
+ MimeBodyPart ampPart = new MimeBodyPart();
+ ampPart.setContent(message.getAmpHtmlBody(), "text/x-amp-html");
+ alternativeMultipart.addBodyPart(ampPart);
+ }
+ topLevelMultipart.addBodyPart(alternativeBodyPart);
+ }
+
+ if (hasAttachments) {
+ for (Attachment attachment : message.getAttachments()) {
+ MimeBodyPart attachmentBodyPart = new MimeBodyPart();
+ DataSource source =
+ new ByteArrayDataSource(attachment.getData(), "application/octet-stream");
+ attachmentBodyPart.setDataHandler(new DataHandler(source));
+ attachmentBodyPart.setFileName(attachment.getFileName());
+ if (attachment.getContentID() != null) {
+ attachmentBodyPart.setContentID(attachment.getContentID());
+ }
+ topLevelMultipart.addBodyPart(attachmentBodyPart);
+ }
+ }
+ mimeMessage.setContent(topLevelMultipart);
+ }
+
+ if (message.getHeaders() != null) {
+ for (Header header : message.getHeaders()) {
+ mimeMessage.addHeader(header.getName(), header.getValue());
+ }
+ }
+
+ mimeMessage.saveChanges();
+
+ Transport transport = session.getTransport("smtp");
+ try {
+ transport.connect();
+ transport.sendMessage(mimeMessage, allTransportRecipients.toArray(new Address[0]));
+ } finally {
+ if (transport != null) {
+ transport.close();
+ }
+ }
+
+ } catch (MessagingException e) {
+ if (e instanceof javax.mail.AuthenticationFailedException) {
+ throw new IllegalArgumentException("SMTP authentication failed: " + e.getMessage(), e);
+ }
+ throw new IOException("Error sending email via SMTP: " + e.getMessage(), e);
+ }
+ }
+
+ private List toInternetAddressList(Collection addresses)
+ throws AddressException {
+ List list = new ArrayList<>();
+ for (String address : addresses) {
+ list.add(new InternetAddress(address));
+ }
+ return list;
+ }
+}
diff --git a/api/src/main/java/com/google/appengine/api/mail/SystemEnvironmentProvider.java b/api/src/main/java/com/google/appengine/api/mail/SystemEnvironmentProvider.java
new file mode 100644
index 000000000..a5807bfa5
--- /dev/null
+++ b/api/src/main/java/com/google/appengine/api/mail/SystemEnvironmentProvider.java
@@ -0,0 +1,29 @@
+package com.google.appengine.api.mail;
+
+/**
+ * A simple wrapper around {@link System} to allow for easier testing.
+ */
+class SystemEnvironmentProvider implements EnvironmentProvider {
+ /**
+ * Gets the value of the specified environment variable.
+ * @param name the name of the environment variable
+ * @return the string value of the variable, or {@code null} if the variable is not defined
+ */
+ @Override
+ public String getenv(String name) {
+ return System.getenv(name);
+ }
+
+ /**
+ * Gets the value of the specified environment variable, returning a default value if the
+ * variable is not defined.
+ * @param name the name of the environment variable
+ * @param defaultValue the default value to return
+ * @return the string value of the variable, or the default value if the variable is not defined
+ */
+ @Override
+ public String getenv(String name, String defaultValue) {
+ String value = System.getenv(name);
+ return value != null ? value : defaultValue;
+ }
+}
diff --git a/api/src/test/java/com/google/appengine/api/mail/MailServiceFactoryImplTest.java b/api/src/test/java/com/google/appengine/api/mail/MailServiceFactoryImplTest.java
new file mode 100644
index 000000000..d0ae0bb73
--- /dev/null
+++ b/api/src/test/java/com/google/appengine/api/mail/MailServiceFactoryImplTest.java
@@ -0,0 +1,36 @@
+package com.google.appengine.api.mail;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MailServiceFactoryImplTest {
+
+ @Mock private EnvironmentProvider envProvider;
+
+ @Test
+ public void testGetMailService_smtp() {
+ when(envProvider.getenv("APPENGINE_USE_SMTP_MAIL_SERVICE")).thenReturn("true");
+ MailServiceFactoryImpl factory = new MailServiceFactoryImpl(envProvider);
+ assertTrue(factory.getMailService() instanceof SmtpMailServiceImpl);
+ }
+
+ @Test
+ public void testGetMailService_legacy() {
+ when(envProvider.getenv("APPENGINE_USE_SMTP_MAIL_SERVICE")).thenReturn("false");
+ MailServiceFactoryImpl factory = new MailServiceFactoryImpl(envProvider);
+ assertTrue(factory.getMailService() instanceof LegacyMailServiceImpl);
+ }
+
+ @Test
+ public void testGetMailService_legacy_null() {
+ when(envProvider.getenv("APPENGINE_USE_SMTP_MAIL_SERVICE")).thenReturn(null);
+ MailServiceFactoryImpl factory = new MailServiceFactoryImpl(envProvider);
+ assertTrue(factory.getMailService() instanceof LegacyMailServiceImpl);
+ }
+}
diff --git a/api/src/test/java/com/google/appengine/api/mail/SmtpMailServiceImplTest.java b/api/src/test/java/com/google/appengine/api/mail/SmtpMailServiceImplTest.java
new file mode 100644
index 000000000..aa78bce6e
--- /dev/null
+++ b/api/src/test/java/com/google/appengine/api/mail/SmtpMailServiceImplTest.java
@@ -0,0 +1,641 @@
+package com.google.appengine.api.mail;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.doThrow;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Properties;
+import javax.mail.Address;
+import javax.mail.Message.RecipientType;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.MockitoJUnitRunner;
+
+// TODO: Remove Silent runner and use default runner once all tests are implemented and unnecessary stubs are removed.
+@RunWith(MockitoJUnitRunner.Silent.class)
+public class SmtpMailServiceImplTest {
+
+ @Mock private Transport transport;
+ @Mock private Session session;
+ @Mock private EnvironmentProvider envProvider;
+
+ private SmtpMailServiceImpl mailService;
+
+ @Before
+ public void setUp() {
+ mailService = new SmtpMailServiceImpl(envProvider);
+ // Mock environment variables
+ when(envProvider.getenv("APPENGINE_SMTP_HOST")).thenReturn("smtp.example.com");
+ when(envProvider.getenv("APPENGINE_SMTP_PORT")).thenReturn("587");
+ when(envProvider.getenv("APPENGINE_SMTP_USER")).thenReturn("user");
+ when(envProvider.getenv("APPENGINE_SMTP_PASSWORD")).thenReturn("password");
+ when(envProvider.getenv("APPENGINE_SMTP_USE_TLS")).thenReturn("true");
+ }
+
+ @Test
+ public void testSendSmtp_basic() throws IOException, MessagingException {
+ // Setup
+ // Use try-with-resources for MockedStatic for Session
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message to send
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo(Collections.singletonList("to@example.com"));
+ message.setCc(Collections.singletonList("cc@example.com"));
+ message.setBcc(Collections.singletonList("bcc@example.com"));
+ message.setSubject("Test Subject");
+ message.setTextBody("Test Body");
+
+ // Act
+ // Call the method under test
+ mailService.send(message);
+
+ // Assert
+ // Capture the arguments passed to transport.sendMessage
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ ArgumentCaptor recipientsCaptor = ArgumentCaptor.forClass(Address[].class);
+ verify(transport, times(1))
+ .sendMessage(messageCaptor.capture(), recipientsCaptor.capture());
+
+ // Assertions for the MimeMessage
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertEquals("Test Subject", sentMessage.getSubject());
+ assertEquals("sender@example.com", sentMessage.getFrom()[0].toString());
+ assertEquals("to@example.com", sentMessage.getRecipients(RecipientType.TO)[0].toString());
+ assertEquals("cc@example.com", sentMessage.getRecipients(RecipientType.CC)[0].toString());
+
+ Address[] bccRecipients = sentMessage.getRecipients(RecipientType.BCC);
+ assertTrue(
+ "BCC recipients should not be in the message headers",
+ bccRecipients == null || bccRecipients.length == 0);
+
+ // Assertions for the recipient list passed to the transport layer
+ Address[] allRecipients = recipientsCaptor.getValue();
+ assertEquals(3, allRecipients.length);
+ assertTrue(
+ "Recipient list should contain TO address",
+ Arrays.stream(allRecipients).anyMatch(a -> a.toString().equals("to@example.com")));
+ assertTrue(
+ "Recipient list should contain CC address",
+ Arrays.stream(allRecipients).anyMatch(a -> a.toString().equals("cc@example.com")));
+ assertTrue(
+ "Recipient list should contain BCC address",
+ Arrays.stream(allRecipients).anyMatch(a -> a.toString().equals("bcc@example.com")));
+ }
+ }
+
+ @Test
+ public void testSendSmtp_multipleRecipients() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with multiple recipients
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo(Arrays.asList("to1@example.com", "to2@example.com"));
+ message.setCc(Arrays.asList("cc1@example.com", "cc2@example.com"));
+ message.setBcc(Arrays.asList("bcc1@example.com", "bcc2@example.com"));
+ message.setSubject("Multiple Recipients Test");
+ message.setTextBody("Test Body");
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ ArgumentCaptor recipientsCaptor = ArgumentCaptor.forClass(Address[].class);
+ verify(transport, times(1))
+ .sendMessage(messageCaptor.capture(), recipientsCaptor.capture());
+
+ // Assertions for the MimeMessage headers
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertEquals("to1@example.com, to2@example.com", sentMessage.getHeader("To", ", "));
+ assertEquals("cc1@example.com, cc2@example.com", sentMessage.getHeader("Cc", ", "));
+
+ // Assertions for the recipient list passed to the transport layer
+ Address[] allRecipients = recipientsCaptor.getValue();
+ assertEquals(6, allRecipients.length);
+ assertTrue(
+ "Recipient list should contain all TO addresses",
+ Arrays.stream(allRecipients)
+ .map(Address::toString)
+ .anyMatch(s -> s.equals("to1@example.com") || s.equals("to2@example.com")));
+ assertTrue(
+ "Recipient list should contain all CC addresses",
+ Arrays.stream(allRecipients)
+ .map(Address::toString)
+ .anyMatch(s -> s.equals("cc1@example.com") || s.equals("cc2@example.com")));
+ assertTrue(
+ "Recipient list should contain all BCC addresses",
+ Arrays.stream(allRecipients)
+ .map(Address::toString)
+ .anyMatch(s -> s.equals("bcc1@example.com") || s.equals("bcc2@example.com")));
+ }
+ }
+
+ @Test
+ public void testSendSmtp_htmlAndPlainTextBodies() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with HTML and plain text bodies
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("HTML and Plain Text Test");
+ message.setTextBody("This is the plain text body.");
+ message.setHtmlBody("This is the HTML body.
");
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ verify(transport).sendMessage(messageCaptor.capture(), any());
+
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertTrue(sentMessage.getHeader("Content-Type")[0].startsWith("multipart/mixed"));
+
+ // Further inspection of the multipart content can be added here
+ // For example, checking the content of each part of the multipart message
+ MimeMultipart mixedMultipart = (MimeMultipart) sentMessage.getContent();
+ assertEquals(1, mixedMultipart.getCount());
+
+ // Check the nested multipart/alternative part
+ MimeBodyPart alternativePart = (MimeBodyPart) mixedMultipart.getBodyPart(0);
+ assertTrue(alternativePart.getContentType().startsWith("multipart/alternative"));
+ MimeMultipart alternativeMultipart = (MimeMultipart) alternativePart.getContent();
+ assertEquals(2, alternativeMultipart.getCount());
+
+ // Check the plain text part
+ MimeBodyPart textPart = (MimeBodyPart) alternativeMultipart.getBodyPart(0);
+ assertTrue(textPart.isMimeType("text/plain"));
+ assertEquals("This is the plain text body.", textPart.getContent());
+
+ // Check the HTML part
+ MimeBodyPart htmlPart = (MimeBodyPart) alternativeMultipart.getBodyPart(1);
+ assertTrue(htmlPart.isMimeType("text/html"));
+ assertEquals("This is the HTML body.
", htmlPart.getContent());
+ }
+ }
+
+ @Test
+ public void testSendSmtp_htmlBodyOnly() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with only an HTML body
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("HTML Body Only Test");
+ message.setHtmlBody("This is the HTML body.
");
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ verify(transport).sendMessage(messageCaptor.capture(), any());
+
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertTrue(sentMessage.getHeader("Content-Type")[0].startsWith("multipart/mixed"));
+
+ // Check the multipart content
+ MimeMultipart mixedMultipart = (MimeMultipart) sentMessage.getContent();
+ assertEquals(1, mixedMultipart.getCount());
+
+ // Check the nested multipart/alternative part
+ MimeBodyPart alternativePart = (MimeBodyPart) mixedMultipart.getBodyPart(0);
+ assertTrue(alternativePart.getContentType().startsWith("multipart/alternative"));
+ MimeMultipart alternativeMultipart = (MimeMultipart) alternativePart.getContent();
+ assertEquals(2, alternativeMultipart.getCount());
+
+ // Check that the plain text part is empty
+ MimeBodyPart textPart = (MimeBodyPart) alternativeMultipart.getBodyPart(0);
+ assertTrue(textPart.isMimeType("text/plain"));
+ assertEquals("", textPart.getContent());
+
+ // Check the HTML part
+ MimeBodyPart htmlPart = (MimeBodyPart) alternativeMultipart.getBodyPart(1);
+ assertTrue(htmlPart.isMimeType("text/html"));
+ assertEquals("This is the HTML body.
", htmlPart.getContent());
+ }
+ }
+
+ @Test
+ public void testSendSmtp_singleAttachment() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with an attachment
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Single Attachment Test");
+ message.setTextBody("This is the body.");
+
+ byte[] attachmentData = "This is an attachment.".getBytes();
+ MailService.Attachment attachment = new MailService.Attachment("attachment.txt", attachmentData);
+ message.setAttachments(Collections.singletonList(attachment));
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ verify(transport).sendMessage(messageCaptor.capture(), any());
+
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertTrue(sentMessage.getContentType().startsWith("multipart/mixed"));
+
+ MimeMultipart mixedMultipart = (MimeMultipart) sentMessage.getContent();
+ assertEquals(2, mixedMultipart.getCount());
+
+ // Check the body part, which should be a multipart/alternative
+ MimeBodyPart bodyPart = (MimeBodyPart) mixedMultipart.getBodyPart(0);
+ assertTrue(bodyPart.getContentType().startsWith("multipart/alternative"));
+ MimeMultipart alternativeMultipart = (MimeMultipart) bodyPart.getContent();
+ assertEquals(1, alternativeMultipart.getCount());
+ MimeBodyPart textPart = (MimeBodyPart) alternativeMultipart.getBodyPart(0);
+ assertTrue(textPart.isMimeType("text/plain"));
+ assertEquals("This is the body.", textPart.getContent());
+
+
+ // Check the attachment part
+ MimeBodyPart attachmentPart = (MimeBodyPart) mixedMultipart.getBodyPart(1);
+ assertEquals("attachment.txt", attachmentPart.getFileName());
+
+ // Verify the content of the attachment
+ byte[] actualAttachmentData = new byte[attachmentData.length];
+ attachmentPart.getDataHandler().getInputStream().read(actualAttachmentData);
+ assertTrue(Arrays.equals(attachmentData, actualAttachmentData));
+ }
+ }
+
+ @Test
+ public void testSendSmtp_multipleAttachments() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with multiple attachments
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Multiple Attachments Test");
+ message.setTextBody("This is the body.");
+
+ byte[] attachmentData1 = "This is attachment 1.".getBytes();
+ MailService.Attachment attachment1 = new MailService.Attachment("attachment1.txt", attachmentData1);
+ byte[] attachmentData2 = "This is attachment 2.".getBytes();
+ MailService.Attachment attachment2 = new MailService.Attachment("attachment2.txt", attachmentData2);
+ message.setAttachments(Arrays.asList(attachment1, attachment2));
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ verify(transport).sendMessage(messageCaptor.capture(), any());
+
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertTrue(sentMessage.getContentType().startsWith("multipart/mixed"));
+
+ MimeMultipart mixedMultipart = (MimeMultipart) sentMessage.getContent();
+ assertEquals(3, mixedMultipart.getCount());
+
+ // Check the body part
+ MimeBodyPart bodyPart = (MimeBodyPart) mixedMultipart.getBodyPart(0);
+ assertTrue(bodyPart.getContentType().startsWith("multipart/alternative"));
+
+ // Check the first attachment
+ MimeBodyPart attachmentPart1 = (MimeBodyPart) mixedMultipart.getBodyPart(1);
+ assertEquals("attachment1.txt", attachmentPart1.getFileName());
+ byte[] actualAttachmentData1 = new byte[attachmentData1.length];
+ attachmentPart1.getDataHandler().getInputStream().read(actualAttachmentData1);
+ assertTrue(Arrays.equals(attachmentData1, actualAttachmentData1));
+
+ // Check the second attachment
+ MimeBodyPart attachmentPart2 = (MimeBodyPart) mixedMultipart.getBodyPart(2);
+ assertEquals("attachment2.txt", attachmentPart2.getFileName());
+ byte[] actualAttachmentData2 = new byte[attachmentData2.length];
+ attachmentPart2.getDataHandler().getInputStream().read(actualAttachmentData2);
+ assertTrue(Arrays.equals(attachmentData2, actualAttachmentData2));
+ }
+ }
+
+ @Test
+ public void testSendSmtp_htmlBodyAndAttachments() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with HTML body and an attachment
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("HTML Body and Attachments Test");
+ message.setHtmlBody("This is the HTML body.
");
+
+ byte[] attachmentData = "This is an attachment.".getBytes();
+ MailService.Attachment attachment = new MailService.Attachment("attachment.txt", attachmentData);
+ message.setAttachments(Collections.singletonList(attachment));
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ verify(transport).sendMessage(messageCaptor.capture(), any());
+
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertTrue(sentMessage.getContentType().startsWith("multipart/mixed"));
+
+ MimeMultipart mixedMultipart = (MimeMultipart) sentMessage.getContent();
+ assertEquals(2, mixedMultipart.getCount());
+
+ // Check the body part (multipart/alternative)
+ MimeBodyPart bodyPart = (MimeBodyPart) mixedMultipart.getBodyPart(0);
+ assertTrue(bodyPart.getContentType().startsWith("multipart/alternative"));
+ MimeMultipart alternativeMultipart = (MimeMultipart) bodyPart.getContent();
+ assertEquals(2, alternativeMultipart.getCount());
+
+ // Check the plain text part (should be empty)
+ MimeBodyPart textPart = (MimeBodyPart) alternativeMultipart.getBodyPart(0);
+ assertTrue(textPart.isMimeType("text/plain"));
+ assertEquals("", textPart.getContent());
+
+ // Check the HTML part
+ MimeBodyPart htmlPart = (MimeBodyPart) alternativeMultipart.getBodyPart(1);
+ assertTrue(htmlPart.isMimeType("text/html"));
+ assertEquals("This is the HTML body.
", htmlPart.getContent());
+
+ // Check the attachment part
+ MimeBodyPart attachmentPart = (MimeBodyPart) mixedMultipart.getBodyPart(1);
+ assertEquals("attachment.txt", attachmentPart.getFileName());
+ byte[] actualAttachmentData = new byte[attachmentData.length];
+ attachmentPart.getDataHandler().getInputStream().read(actualAttachmentData);
+ assertTrue(Arrays.equals(attachmentData, actualAttachmentData));
+ }
+ }
+
+ @Test
+ public void testSendSmtp_attachmentWithContentId() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with an attachment with a Content-ID
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Attachment with Content-ID Test");
+ message.setTextBody("This is the body.");
+
+ byte[] attachmentData = "This is an attachment.".getBytes();
+ MailService.Attachment attachment =
+ new MailService.Attachment("attachment.txt", attachmentData, "");
+ message.setAttachments(Collections.singletonList(attachment));
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ verify(transport).sendMessage(messageCaptor.capture(), any());
+
+ MimeMessage sentMessage = messageCaptor.getValue();
+ MimeMultipart mixedMultipart = (MimeMultipart) sentMessage.getContent();
+
+ // Check the attachment part
+ MimeBodyPart attachmentPart = (MimeBodyPart) mixedMultipart.getBodyPart(1);
+ assertEquals("", attachmentPart.getContentID());
+ }
+ }
+
+ @Test
+ public void testSendSmtp_replyToHeader() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with a Reply-To header
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Reply-To Test");
+ message.setTextBody("This is the body.");
+ message.setReplyTo("reply-to@example.com");
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ verify(transport).sendMessage(messageCaptor.capture(), any());
+
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertEquals(1, sentMessage.getReplyTo().length);
+ assertEquals("reply-to@example.com", sentMessage.getReplyTo()[0].toString());
+ }
+ }
+
+ @Test
+ public void testSendSmtp_customHeaders() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create the message with custom headers
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Custom Headers Test");
+ message.setTextBody("This is the body.");
+ message.setHeaders(
+ Collections.singletonList(new MailService.Header("X-Custom-Header", "my-value")));
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(MimeMessage.class);
+ verify(transport).sendMessage(messageCaptor.capture(), any());
+
+ MimeMessage sentMessage = messageCaptor.getValue();
+ assertEquals("my-value", sentMessage.getHeader("X-Custom-Header")[0]);
+ }
+ }
+
+ @Test
+ public void testSendSmtp_disabledTls() throws IOException, MessagingException {
+ // Setup
+ when(envProvider.getenv("APPENGINE_SMTP_USE_TLS")).thenReturn("false");
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ ArgumentCaptor propsCaptor = ArgumentCaptor.forClass(Properties.class);
+ mockedSession
+ .when(() -> Session.getInstance(propsCaptor.capture(), any()))
+ .thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create a simple message
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Disabled TLS Test");
+ message.setTextBody("This is the body.");
+
+ // Act
+ mailService.send(message);
+
+ // Assert
+ Properties props = propsCaptor.getValue();
+ assertEquals(null, props.getProperty("mail.smtp.starttls.enable"));
+ }
+ }
+
+ @Test
+ public void testSendSmtp_adminEmail() throws IOException, MessagingException {
+ // Setup
+ when(envProvider.getenv("APPENGINE_ADMIN_EMAIL_RECIPIENTS"))
+ .thenReturn("admin1@example.com,admin2@example.com");
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+
+ // Create a message with some recipients that should be ignored
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setCc("cc@example.com");
+ message.setBcc("bcc@example.com");
+ message.setSubject("Admin Email Test");
+ message.setTextBody("This is the body.");
+
+ // Act
+ mailService.sendToAdmins(message);
+
+ // Assert
+ ArgumentCaptor recipientsCaptor = ArgumentCaptor.forClass(Address[].class);
+ verify(transport).sendMessage(any(MimeMessage.class), recipientsCaptor.capture());
+
+ Address[] recipients = recipientsCaptor.getValue();
+ assertEquals(2, recipients.length);
+ assertTrue(
+ "Recipient list should contain admin1@example.com",
+ Arrays.stream(recipients).anyMatch(a -> a.toString().equals("admin1@example.com")));
+ assertTrue(
+ "Recipient list should contain admin2@example.com",
+ Arrays.stream(recipients).anyMatch(a -> a.toString().equals("admin2@example.com")));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSendSmtp_adminEmailNoRecipients() throws IOException, MessagingException {
+ // Setup
+ when(envProvider.getenv("APPENGINE_ADMIN_EMAIL_RECIPIENTS")).thenReturn(null);
+
+ // Create a simple message
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setSubject("Admin Email No Recipients Test");
+ message.setTextBody("This is the body.");
+
+ // Act
+ mailService.sendToAdmins(message);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSendSmtp_authenticationFailure() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+ doThrow(new javax.mail.AuthenticationFailedException("Authentication failed"))
+ .when(transport)
+ .connect();
+
+ // Create a simple message
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Authentication Failure Test");
+ message.setTextBody("This is the body.");
+
+ // Act
+ mailService.send(message);
+ }
+ }
+
+ @Test(expected = IOException.class)
+ public void testSendSmtp_connectionFailure() throws IOException, MessagingException {
+ // Setup
+ try (MockedStatic mockedSession = mockStatic(Session.class)) {
+ mockedSession.when(() -> Session.getInstance(any(Properties.class), any())).thenReturn(session);
+ when(session.getTransport("smtp")).thenReturn(transport);
+ doThrow(new MessagingException("Connection failed")).when(transport).connect();
+
+ // Create a simple message
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Connection Failure Test");
+ message.setTextBody("This is the body.");
+
+ // Act
+ mailService.send(message);
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSendSmtp_missingSmtpHost() throws IOException, MessagingException {
+ // Setup
+ when(envProvider.getenv("APPENGINE_SMTP_HOST")).thenReturn(null);
+
+ // Create a simple message
+ MailService.Message message = new MailService.Message();
+ message.setSender("sender@example.com");
+ message.setTo("to@example.com");
+ message.setSubject("Missing SMTP Host Test");
+ message.setTextBody("This is the body.");
+
+ // Act
+ mailService.send(message);
+ }
+}
diff --git a/api_dev/src/test/java/com/google/appengine/api/mail/MailServiceImplTest.java b/api_dev/src/test/java/com/google/appengine/api/mail/LegacyMailServiceImplTest.java
similarity index 92%
rename from api_dev/src/test/java/com/google/appengine/api/mail/MailServiceImplTest.java
rename to api_dev/src/test/java/com/google/appengine/api/mail/LegacyMailServiceImplTest.java
index fe6215c03..29cc93a3d 100644
--- a/api_dev/src/test/java/com/google/appengine/api/mail/MailServiceImplTest.java
+++ b/api_dev/src/test/java/com/google/appengine/api/mail/LegacyMailServiceImplTest.java
@@ -42,11 +42,11 @@
import org.mockito.junit.MockitoRule;
/**
- * Unit tests for the MailServiceImpl class. Cloned from URLFetchService.
+ * Unit tests for the LegacyMailServiceImpl class. Cloned from URLFetchService.
*
*/
@RunWith(JUnit4.class)
-public class MailServiceImplTest {
+public class LegacyMailServiceImplTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private ApiProxy.Delegate delegate;
@@ -57,7 +57,7 @@ public class MailServiceImplTest {
public void setUp() throws Exception {
ApiProxy.setDelegate(delegate);
ApiProxy.setEnvironmentForCurrentThread(environment);
- service = new MailServiceImpl();
+ service = new LegacyMailServiceImpl();
}
@After
@@ -119,10 +119,10 @@ public void testSendAllNull() throws Exception {
when(delegate.makeSyncCall(any(), any(), any(), any())).thenReturn(new byte[0]);
- new MailServiceImpl().send(msg);
+ new LegacyMailServiceImpl().send(msg);
verify(delegate)
.makeSyncCall(
- same(environment), eq(MailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray()));
+ same(environment), eq(LegacyMailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray()));
}
/** Tests that a message with an attachment works correctly. */
@@ -145,7 +145,7 @@ public void testDoSend_withAttachment() throws Exception {
verify(delegate)
.makeSyncCall(
same(environment),
- eq(MailServiceImpl.PACKAGE),
+ eq(LegacyMailServiceImpl.PACKAGE),
eq("Send"),
eq(msgProto.build().toByteArray()));
}
@@ -175,7 +175,7 @@ public void testDoSend_withContentIDAttachment() throws Exception {
verify(delegate)
.makeSyncCall(
- same(environment), eq(MailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray()));
+ same(environment), eq(LegacyMailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray()));
}
/** Tests that sending a AMP Email message works correctly. */
@@ -199,7 +199,7 @@ public void testDoSend_ampEmail() throws Exception {
service.send(msg);
verify(delegate)
.makeSyncCall(
- same(environment), eq(MailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray()));
+ same(environment), eq(LegacyMailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray()));
}
/** Tests that a message with a header works correctly. */
@@ -220,7 +220,7 @@ public void testDoSend_withHeader() throws Exception {
verify(delegate)
.makeSyncCall(
same(environment),
- eq(MailServiceImpl.PACKAGE),
+ eq(LegacyMailServiceImpl.PACKAGE),
eq("Send"),
eq(msgProto.build().toByteArray()));
}
@@ -237,7 +237,7 @@ public void testDoSend_replyToAddress() throws Exception {
verify(delegate)
.makeSyncCall(
- same(environment), eq(MailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray()));
+ same(environment), eq(LegacyMailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray()));
}
@Test
@@ -306,7 +306,7 @@ private void runRecipientTest(String to, String cc, String bcc) throws Exception
verify(delegate)
.makeSyncCall(
same(environment),
- eq(MailServiceImpl.PACKAGE),
+ eq(LegacyMailServiceImpl.PACKAGE),
eq("Send"),
eq(msgProto.build().toByteArray()));
}
@@ -324,7 +324,7 @@ private MailService.Message setupSendCallWithApplicationException(ErrorCode code
MailMessage msgProto = newMailMessage(msg);
when(delegate.makeSyncCall(
- same(environment), eq(MailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray())))
+ same(environment), eq(LegacyMailServiceImpl.PACKAGE), eq("Send"), eq(msgProto.toByteArray())))
.thenThrow(new ApiProxy.ApplicationException(code.getNumber(), "detail"));
return msg;
@@ -365,7 +365,7 @@ public void testSendToAdmins() throws Exception {
verify(delegate)
.makeSyncCall(
same(environment),
- eq(MailServiceImpl.PACKAGE),
+ eq(LegacyMailServiceImpl.PACKAGE),
eq("SendToAdmins"),
eq(msgProto.toByteArray()));
}
@@ -383,8 +383,8 @@ public void testSendToAdmins_multiArgConstructor() throws Exception {
verify(delegate)
.makeSyncCall(
same(environment),
- eq(MailServiceImpl.PACKAGE),
+ eq(LegacyMailServiceImpl.PACKAGE),
eq("SendToAdmins"),
eq(msgProto.toByteArray()));
}
-}
+}
\ No newline at end of file