1
0
mirror of https://github.com/google/nomulus synced 2026-07-09 09:37:04 +00:00

Compare commits

...

6 Commits

Author SHA1 Message Date
Weimin Yu c6f62dcffd Fix the alert recipient address for Spec11 (#2103)
During email migration, alerts should be sent to the address
annotated with `newAlertRecipientEmailAddress`.
2023-08-10 15:38:13 -04:00
sarahcaseybot ee66805d2e Modify getTldCommand to return TLD in YAML (#2102) 2023-08-10 14:20:56 -04:00
Weimin Yu d7a3c0c439 Send Spec11 emails using Gmail (#2101)
First of a series of migrations to Gmail.

This can only be verified in production.
2023-08-10 10:26:28 -04:00
Weimin Yu 45666773ee Enable/disable email sending by environments (#2099) 2023-08-09 10:46:48 -04:00
Lai Jiang b8b5152336 Bump PUBAPI instance number to 24 (#2098) 2023-08-08 11:23:50 -04:00
Weimin Yu 0f6302e92b Add tests to GmailClient (#2097)
Also make GmailClient do retries on transit errors.
2023-08-07 16:05:15 -04:00
11 changed files with 368 additions and 42 deletions
@@ -158,6 +158,12 @@ public final class RegistryConfig {
return config.registryPolicy.contactAndHostRoidSuffix;
}
@Provides
@Config("isEmailSendingEnabled")
public static boolean provideIsEmailSendingEnabled(RegistryConfigSettings config) {
return config.misc.isEmailSendingEnabled;
}
/**
* The e-mail address for questions about integrating with the registry. Used in the
* "contact-us" section of the registrar console.
@@ -205,6 +205,7 @@ public class RegistryConfigSettings {
/** Miscellaneous configuration that doesn't quite fit in anywhere else. */
public static class Misc {
public String sheetExportId;
public boolean isEmailSendingEnabled;
public String alertRecipientEmailAddress;
// TODO(b/279671974): remove below field after migration
public String newAlertRecipientEmailAddress;
@@ -432,6 +432,9 @@ misc:
# to. Leave this null to disable syncing.
sheetExportId: null
# Whether emails may be sent. For Prod and Sandbox this should be true.
isEmailSendingEnabled: false
# Address we send alert summary emails to.
alertRecipientEmailAddress: email@example.com
@@ -7,7 +7,7 @@
<sessions-enabled>true</sessions-enabled>
<instance-class>B4_1G</instance-class>
<manual-scaling>
<instances>12</instances>
<instances>24</instances>
</manual-scaling>
<system-properties>
@@ -14,19 +14,24 @@
package google.registry.groups;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Iterables.toArray;
import com.google.api.client.http.HttpResponseException;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.model.Message;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.EmailMessage;
import google.registry.util.EmailMessage.Attachment;
import google.registry.util.Retrier;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Properties;
import java.util.function.Predicate;
import javax.inject.Inject;
import javax.mail.Address;
import javax.mail.BodyPart;
@@ -38,23 +43,30 @@ import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.apache.commons.codec.binary.Base64;
/** Sends {@link EmailMessage EmailMessages} through Google Workspace using {@link Gmail}. */
public final class GmailClient {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final Gmail gmail;
private final Retrier retrier;
private final boolean isEmailSendingEnabled;
private final InternetAddress outgoingEmailAddressWithUsername;
private final InternetAddress replyToEmailAddress;
@Inject
GmailClient(
Gmail gmail,
Retrier retrier,
@Config("isEmailSendingEnabled") boolean isEmailSendingEnabled,
@Config("gSuiteNewOutgoingEmailAddress") String gSuiteOutgoingEmailAddress,
@Config("gSuiteOutgoingEmailDisplayName") String gSuiteOutgoingEmailDisplayName,
@Config("replyToEmailAddress") InternetAddress replyToEmailAddress) {
this.gmail = gmail;
this.retrier = retrier;
this.isEmailSendingEnabled = isEmailSendingEnabled;
this.replyToEmailAddress = replyToEmailAddress;
try {
this.outgoingEmailAddressWithUsername =
@@ -70,15 +82,25 @@ public final class GmailClient {
* <p>If the sender as specified by {@link EmailMessage#from} differs from the caller's identity,
* the caller must have delegated `send` authority to the sender.
*/
@CanIgnoreReturnValue
public Message sendEmail(EmailMessage emailMessage) {
Message message = toGmailMessage(toMimeMessage(emailMessage));
try {
// "me" is reserved word for the authorized user of the Gmail API.
return gmail.users().messages().send("me", message).execute();
} catch (IOException e) {
throw new EmailException(e);
public void sendEmail(EmailMessage emailMessage) {
if (!isEmailSendingEnabled) {
logger.atInfo().log(
String.format(
"Email with subject %s would have been sent to recipients %s",
emailMessage.subject().substring(0, Math.min(emailMessage.subject().length(), 15)),
String.join(
" , ",
emailMessage.recipients().stream()
.map(ia -> ia.toString())
.collect(toImmutableSet()))));
return;
}
Message message = toGmailMessage(toMimeMessage(emailMessage));
// Unlike other Cloud APIs such as GCS and SecretManager, Gmail does not retry on errors.
retrier.callWithRetry(
// "me" is reserved word for the authorized user of the Gmail API.
() -> this.gmail.users().messages().send("me", message).execute(),
RetriableGmailExceptionPredicate.INSTANCE);
}
static Message toGmailMessage(MimeMessage message) {
@@ -86,10 +108,7 @@ public final class GmailClient {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
message.writeTo(buffer);
byte[] rawMessageBytes = buffer.toByteArray();
String encodedEmail = Base64.encodeBase64URLSafeString(rawMessageBytes);
Message gmailMessage = new Message();
gmailMessage.setRaw(encodedEmail);
return gmailMessage;
return new Message().encodeRaw(rawMessageBytes);
} catch (MessagingException | IOException e) {
throw new EmailException(e);
}
@@ -135,4 +154,41 @@ public final class GmailClient {
super(cause);
}
}
/**
* Determines if a Gmail API exception may be retried.
*
* <p>See <a href="https://developers.google.com/gmail/api/guides/handle-errors">online doc</a>
* for details.
*/
static class RetriableGmailExceptionPredicate implements Predicate<Throwable> {
static RetriableGmailExceptionPredicate INSTANCE = new RetriableGmailExceptionPredicate();
private static final int USAGE_LIMIT_EXCEEDED = 403;
private static final int TOO_MANY_REQUESTS = 429;
private static final int BACKEND_ERROR = 500;
private static final ImmutableSet<String> TRANSIENT_OVERAGE_REASONS =
ImmutableSet.of("userRateLimitExceeded", "rateLimitExceeded");
@Override
public boolean test(Throwable e) {
if (e instanceof HttpResponseException) {
return testHttpResponse((HttpResponseException) e);
}
return true;
}
private boolean testHttpResponse(HttpResponseException e) {
int statusCode = e.getStatusCode();
if (statusCode == TOO_MANY_REQUESTS || statusCode == BACKEND_ERROR) {
return true;
}
if (statusCode == USAGE_LIMIT_EXCEEDED) {
return TRANSIENT_OVERAGE_REASONS.contains(e.getStatusMessage());
}
return false;
}
}
}
@@ -31,12 +31,12 @@ import com.google.template.soy.tofu.SoyTofu;
import com.google.template.soy.tofu.SoyTofu.Renderer;
import google.registry.beam.spec11.ThreatMatch;
import google.registry.config.RegistryConfig.Config;
import google.registry.groups.GmailClient;
import google.registry.model.domain.Domain;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
@@ -56,8 +56,7 @@ public class Spec11EmailUtils {
Spec11EmailSoyInfo.getInstance().getFileName()))
.build()
.compileToTofu();
private final SendEmailService emailService;
private final GmailClient gmailClient;
private final InternetAddress outgoingEmailAddress;
private final ImmutableList<InternetAddress> spec11BccEmailAddresses;
private final InternetAddress alertRecipientAddress;
@@ -66,13 +65,13 @@ public class Spec11EmailUtils {
@Inject
Spec11EmailUtils(
SendEmailService emailService,
@Config("alertRecipientEmailAddress") InternetAddress alertRecipientAddress,
GmailClient gmailClient,
@Config("newAlertRecipientEmailAddress") InternetAddress alertRecipientAddress,
@Config("spec11OutgoingEmailAddress") InternetAddress spec11OutgoingEmailAddress,
@Config("spec11BccEmailAddresses") ImmutableList<InternetAddress> spec11BccEmailAddresses,
@Config("spec11WebResources") ImmutableList<String> spec11WebResources,
@Config("registryName") String registryName) {
this.emailService = emailService;
this.gmailClient = gmailClient;
this.outgoingEmailAddress = spec11OutgoingEmailAddress;
this.spec11BccEmailAddresses = spec11BccEmailAddresses;
this.alertRecipientAddress = alertRecipientAddress;
@@ -151,7 +150,7 @@ public class Spec11EmailUtils {
String subject,
RegistrarThreatMatches registrarThreatMatches)
throws MessagingException {
emailService.sendEmail(
gmailClient.sendEmail(
EmailMessage.newBuilder()
.setSubject(subject)
.setBody(getContent(date, soyTemplateInfo, registrarThreatMatches))
@@ -191,7 +190,7 @@ public class Spec11EmailUtils {
/** Sends an e-mail indicating the state of the spec11 pipeline, with a given subject and body. */
void sendAlertEmail(String subject, String body) {
try {
emailService.sendEmail(
gmailClient.sendEmail(
EmailMessage.newBuilder()
.setFrom(outgoingEmailAddress)
.addRecipient(alertRecipientAddress)
@@ -14,10 +14,13 @@
package google.registry.tools;
import static google.registry.model.tld.TldYamlUtils.getObjectMapper;
import static google.registry.model.tld.Tlds.assertTldsExist;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import google.registry.model.tld.Tld;
import java.util.List;
@@ -31,9 +34,10 @@ final class GetTldCommand implements Command {
private List<String> mainParameters;
@Override
public void run() {
public void run() throws JsonProcessingException {
ObjectMapper mapper = getObjectMapper();
for (String tld : assertTldsExist(mainParameters)) {
System.out.println(Tld.get(tld));
System.out.println(mapper.writeValueAsString(Tld.get(tld)));
}
}
}
@@ -0,0 +1,202 @@
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
//
// 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
//
// http://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 google.registry.groups;
import static com.google.common.net.MediaType.CSV_UTF_8;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import com.google.api.client.http.HttpResponseException;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.Gmail.Users;
import com.google.api.services.gmail.Gmail.Users.Messages;
import com.google.api.services.gmail.Gmail.Users.Messages.Send;
import com.google.api.services.gmail.model.Message;
import google.registry.groups.GmailClient.RetriableGmailExceptionPredicate;
import google.registry.util.EmailMessage;
import google.registry.util.EmailMessage.Attachment;
import google.registry.util.Retrier;
import google.registry.util.SystemSleeper;
import java.io.OutputStream;
import javax.mail.Message.RecipientType;
import javax.mail.Part;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
/** Unit tests for {@link GmailClient}. */
@ExtendWith(MockitoExtension.class)
public class GmailClientTest {
@Mock private Gmail gmail;
@Mock private HttpResponseException httpResponseException;
private GmailClient getGmailClient(boolean isExternalEmailAllowed) throws Exception {
return new GmailClient(
gmail,
new Retrier(new SystemSleeper(), 3),
isExternalEmailAllowed,
"from@example.com",
"My sender",
new InternetAddress("replyTo@example.com"));
}
@Test
public void sendEmail_sentWhenAllowed() throws Exception {
EmailMessage message =
EmailMessage.create(
"subject",
"body",
new InternetAddress("from@example.com"),
new InternetAddress("to@example.com"));
Send gSend = mock(Send.class);
Messages gMessages = mock(Messages.class);
Users gUsers = mock(Users.class);
when(gmail.users()).thenReturn(gUsers);
when(gUsers.messages()).thenReturn(gMessages);
when(gMessages.send(anyString(), any())).thenReturn(gSend);
getGmailClient(true).sendEmail(message);
verify(gmail, times(1)).users();
}
@Test
public void sendEmail_notSentWhenNotAllowed() throws Exception {
EmailMessage message =
EmailMessage.create(
"subject",
"body",
new InternetAddress("from@example.com"),
new InternetAddress("to@example.com"));
getGmailClient(false).sendEmail(message);
verifyNoInteractions(gmail);
}
@Test
public void toMimeMessage_fullMessage() throws Exception {
InternetAddress fromAddr = new InternetAddress("from@example.com", "My sender");
InternetAddress toAddr = new InternetAddress("to@example.com");
InternetAddress ccAddr = new InternetAddress("cc@example.com");
InternetAddress bccAddr = new InternetAddress("bcc@example.com");
EmailMessage emailMessage =
EmailMessage.newBuilder()
.setFrom(fromAddr)
.setRecipients(ImmutableList.of(toAddr))
.setSubject("My subject")
.setBody("My body")
.addCc(ccAddr)
.addBcc(bccAddr)
.setAttachment(
Attachment.newBuilder()
.setFilename("filename")
.setContent("foo,bar\nbaz,qux")
.setContentType(CSV_UTF_8)
.build())
.build();
MimeMessage mimeMessage = getGmailClient(true).toMimeMessage(emailMessage);
assertThat(mimeMessage.getFrom()).asList().containsExactly(fromAddr);
assertThat(mimeMessage.getRecipients(RecipientType.TO)).asList().containsExactly(toAddr);
assertThat(mimeMessage.getRecipients(RecipientType.CC)).asList().containsExactly(ccAddr);
assertThat(mimeMessage.getRecipients(RecipientType.BCC)).asList().containsExactly(bccAddr);
assertThat(mimeMessage.getSubject()).isEqualTo("My subject");
assertThat(mimeMessage.getContent()).isInstanceOf(MimeMultipart.class);
MimeMultipart parts = (MimeMultipart) mimeMessage.getContent();
Part body = parts.getBodyPart(0);
assertThat(body.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8.toString());
assertThat(body.getContent()).isEqualTo("My body");
Part attachment = parts.getBodyPart(1);
assertThat(attachment.getContentType()).startsWith(CSV_UTF_8.toString());
assertThat(attachment.getContentType()).endsWith("name=filename");
assertThat(attachment.getContent()).isEqualTo("foo,bar\nbaz,qux");
}
@Test
public void toGmailMessage() throws Exception {
MimeMessage mimeMessage = mock(MimeMessage.class);
byte[] data = "My content".getBytes(UTF_8);
doAnswer(
new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
OutputStream os = invocation.getArgument(0);
os.write(data);
return null;
}
})
.when(mimeMessage)
.writeTo(any(OutputStream.class));
Message gmailMessage = GmailClient.toGmailMessage(mimeMessage);
assertThat(gmailMessage.decodeRaw()).isEqualTo(data);
}
@Test
public void isRetriable_trueIfNotHttpResponseException() {
assertThat(RetriableGmailExceptionPredicate.INSTANCE.test(new Exception())).isTrue();
}
@Test
public void isHttpResponseExceptionRetriable_trueIf500() {
when(httpResponseException.getStatusCode()).thenReturn(500);
assertThat(RetriableGmailExceptionPredicate.INSTANCE.test(httpResponseException)).isTrue();
}
@Test
public void isHttpResponseExceptionRetriable_trueIf429() {
when(httpResponseException.getStatusCode()).thenReturn(429);
assertThat(RetriableGmailExceptionPredicate.INSTANCE.test(httpResponseException)).isTrue();
}
@Test
public void isHttpResponseExceptionRetriable_trueIf403WithRateLimitOverage() {
when(httpResponseException.getStatusCode()).thenReturn(403);
when(httpResponseException.getStatusMessage()).thenReturn("rateLimitExceeded");
assertThat(RetriableGmailExceptionPredicate.INSTANCE.test(httpResponseException)).isTrue();
}
@Test
public void isHttpResponseExceptionRetriable_trueIf403WithUserRateLimitOverage() {
when(httpResponseException.getStatusCode()).thenReturn(403);
when(httpResponseException.getStatusMessage()).thenReturn("userRateLimitExceeded");
assertThat(RetriableGmailExceptionPredicate.INSTANCE.test(httpResponseException)).isTrue();
}
@Test
public void isHttpResponseExceptionRetriable_falseIf403WithLongLastingOverage() {
when(httpResponseException.getStatusCode()).thenReturn(403);
when(httpResponseException.getStatusMessage()).thenReturn("dailyLimitExceeded");
assertThat(RetriableGmailExceptionPredicate.INSTANCE.test(httpResponseException)).isFalse();
}
@Test
public void isHttpResponseExceptionRetriable_falseIfBadRequest() {
when(httpResponseException.getStatusCode()).thenReturn(400);
assertThat(RetriableGmailExceptionPredicate.INSTANCE.test(httpResponseException)).isFalse();
}
}
@@ -27,14 +27,13 @@ import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistResource;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.MediaType;
import google.registry.groups.GmailClient;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.persistence.transaction.JpaTestExtensions;
@@ -42,7 +41,6 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
import google.registry.testing.DatabaseHelper;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
@@ -51,10 +49,14 @@ import javax.mail.internet.InternetAddress;
import org.joda.time.LocalDate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
/** Unit tests for {@link Spec11EmailUtils}. */
@ExtendWith(MockitoExtension.class)
class Spec11EmailUtilsTest {
private static final ImmutableList<String> FAKE_RESOURCES = ImmutableList.of("foo");
@@ -98,9 +100,8 @@ class Spec11EmailUtilsTest {
final JpaIntegrationTestExtension jpa =
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
private SendEmailService emailService;
@Mock private GmailClient gmailClient;
private Spec11EmailUtils emailUtils;
private Spec11RegistrarThreatMatchesParser parser;
private ArgumentCaptor<EmailMessage> contentCaptor;
private final LocalDate date = new LocalDate(2018, 7, 15);
@@ -109,13 +110,10 @@ class Spec11EmailUtilsTest {
@BeforeEach
void beforeEach() throws Exception {
emailService = mock(SendEmailService.class);
parser = mock(Spec11RegistrarThreatMatchesParser.class);
when(parser.getRegistrarThreatMatches(date)).thenReturn(sampleThreatMatches());
contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
emailUtils =
new Spec11EmailUtils(
emailService,
gmailClient,
new InternetAddress("my-receiver@test.com"),
new InternetAddress("abuse@test.com"),
ImmutableList.of(
@@ -138,7 +136,7 @@ class Spec11EmailUtilsTest {
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
sampleThreatMatches());
// We inspect individual parameters because Message doesn't implement equals().
verify(emailService, times(3)).sendEmail(contentCaptor.capture());
verify(gmailClient, times(3)).sendEmail(contentCaptor.capture());
List<EmailMessage> capturedContents = contentCaptor.getAllValues();
validateMessage(
capturedContents.get(0),
@@ -176,7 +174,7 @@ class Spec11EmailUtilsTest {
"Super Cool Registry Daily Threat Detector [2018-07-15]",
sampleThreatMatches());
// We inspect individual parameters because Message doesn't implement equals().
verify(emailService, times(3)).sendEmail(contentCaptor.capture());
verify(gmailClient, times(3)).sendEmail(contentCaptor.capture());
List<EmailMessage> capturedMessages = contentCaptor.getAllValues();
validateMessage(
capturedMessages.get(0),
@@ -217,7 +215,7 @@ class Spec11EmailUtilsTest {
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
sampleThreatMatches());
// We inspect individual parameters because Message doesn't implement equals().
verify(emailService, times(2)).sendEmail(contentCaptor.capture());
verify(gmailClient, times(2)).sendEmail(contentCaptor.capture());
List<EmailMessage> capturedContents = contentCaptor.getAllValues();
validateMessage(
capturedContents.get(0),
@@ -250,7 +248,7 @@ class Spec11EmailUtilsTest {
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
sampleThreatMatches());
// We inspect individual parameters because Message doesn't implement equals().
verify(emailService, times(3)).sendEmail(contentCaptor.capture());
verify(gmailClient, times(3)).sendEmail(contentCaptor.capture());
List<EmailMessage> capturedContents = contentCaptor.getAllValues();
validateMessage(
capturedContents.get(0),
@@ -289,7 +287,7 @@ class Spec11EmailUtilsTest {
doThrow(new RuntimeException(new MessagingException("expected")))
.doNothing()
.doNothing()
.when(emailService)
.when(gmailClient)
.sendEmail(contentCaptor.capture());
RuntimeException thrown =
assertThrows(
@@ -305,7 +303,7 @@ class Spec11EmailUtilsTest {
.isEqualTo("Emailing Spec11 reports failed, first exception:");
assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("expected");
// Verify we sent an e-mail alert
verify(emailService, times(3)).sendEmail(contentCaptor.capture());
verify(gmailClient, times(3)).sendEmail(contentCaptor.capture());
List<EmailMessage> capturedMessages = contentCaptor.getAllValues();
validateMessage(
capturedMessages.get(0),
@@ -338,7 +336,7 @@ class Spec11EmailUtilsTest {
@Test
void testSuccess_sendAlertEmail() throws Exception {
emailUtils.sendAlertEmail("Spec11 Pipeline Alert: 2018-07", "Alert!");
verify(emailService).sendEmail(contentCaptor.capture());
verify(gmailClient).sendEmail(contentCaptor.capture());
validateMessage(
contentCaptor.getValue(),
"abuse@test.com",
@@ -363,7 +361,7 @@ class Spec11EmailUtilsTest {
Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL,
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
sampleThreatMatches());
verify(emailService, times(3)).sendEmail(contentCaptor.capture());
verify(gmailClient, times(3)).sendEmail(contentCaptor.capture());
assertThat(contentCaptor.getAllValues().get(0).recipients())
.containsExactly(new InternetAddress("johndoe@theregistrar.com"));
}
@@ -16,6 +16,7 @@ package google.registry.tools;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.createTlds;
import static google.registry.testing.TestDataHelper.loadFile;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
@@ -28,6 +29,7 @@ class GetTldCommandTest extends CommandTestCase<GetTldCommand> {
void testSuccess() throws Exception {
createTld("xn--q9jyb4c");
runCommand("xn--q9jyb4c");
assertInStdout(loadFile(getClass(), "tld.yaml"));
}
@Test
@@ -0,0 +1,55 @@
tldStr: "xn--q9jyb4c"
roidSuffix: "Q9JYB4C"
pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
dnsWriters:
- "VoidDnsWriter"
numDnsPublishLocks: 1
dnsAPlusAaaaTtl: null
dnsNsTtl: null
dnsDsTtl: null
tldUnicode: "みんな"
driveFolderId: null
tldType: "REAL"
invoicingEnabled: false
tldStateTransitions:
"1970-01-01T00:00:00.000Z": "GENERAL_AVAILABILITY"
creationTime: "2022-09-01T00:00:00.000Z"
reservedListNames: []
premiumListName: "xn--q9jyb4c"
escrowEnabled: false
dnsPaused: false
addGracePeriodLength: 432000000
anchorTenantAddGracePeriodLength: 2592000000
autoRenewGracePeriodLength: 3888000000
redemptionGracePeriodLength: 2592000000
renewGracePeriodLength: 432000000
transferGracePeriodLength: 432000000
automaticTransferLength: 432000000
pendingDeleteLength: 432000000
currency: "USD"
createBillingCost:
currency: "USD"
amount: 13.00
restoreBillingCost:
currency: "USD"
amount: 17.00
serverStatusChangeBillingCost:
currency: "USD"
amount: 19.00
registryLockOrUnlockBillingCost:
currency: "USD"
amount: 0.00
renewBillingCostTransitions:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 11.00
lordnUsername: null
claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
allowedRegistrantContactIds: []
allowedFullyQualifiedHostNames: []
defaultPromoTokens: []
idnTables: []
eapFeeSchedule:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 0.00