Allow test TLDs in drop list export for non-production environments (#3231)

In non-production environments (e.g. sandbox), allow TEST TLDs to be
included in the domain drop list export so XAP can be verified with
test TLDs.

Also remove the isInvoicingEnabled filter globally, as the
tld.getExpiryAccessPeriodModeAt(now) check is sufficient.

TAG=agy
CONV=67020217-639f-40a2-9d7c-54e592c91a53
This commit is contained in:
Ben McIlwain
2026-10-01 18:42:01 +00:00
committed by GitHub
parent 7fc61974a3
commit 7f85d6a7de
4 changed files with 85 additions and 16 deletions
@@ -16,6 +16,7 @@ package google.registry.export;
import static com.google.common.base.Verify.verifyNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.tld.Tlds.getTldEntities;
import static google.registry.model.tld.Tlds.getTldEntitiesOfType;
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
@@ -35,6 +36,7 @@ import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.storage.drive.DriveConnection;
import google.registry.util.Clock;
import google.registry.util.RegistryEnvironment;
import jakarta.inject.Inject;
import java.io.IOException;
import java.io.StringWriter;
@@ -45,7 +47,10 @@ import java.util.Optional;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
/** An action that exports the upcoming domain drop list across all open TLDs to Google Drive. */
/**
* An action that exports the upcoming domain drop list across TLDs with XAP enabled to Google
* Drive.
*/
@Action(
service = Action.Service.BACKEND,
path = "/_dr/task/exportDropList",
@@ -89,15 +94,18 @@ public class ExportDropListAction implements Runnable {
Instant now = clock.now();
ImmutableSet<String> xapTlds =
getTldEntitiesOfType(TldType.REAL).stream()
.filter(Tld::isInvoicingEnabled)
.filter(tld -> tld.getExpiryAccessPeriodModeAt(now) == ExpiryAccessPeriodMode.ENABLED)
.map(Tld::getTldStr)
.collect(toImmutableSet());
logger.atInfo().log("Exporting domain drop list for open TLDs with XAP enabled: %s", xapTlds);
(RegistryEnvironment.get() == RegistryEnvironment.PRODUCTION
? getTldEntitiesOfType(TldType.REAL)
: getTldEntities())
.stream()
.filter(
tld -> tld.getExpiryAccessPeriodModeAt(now) == ExpiryAccessPeriodMode.ENABLED)
.map(Tld::getTldStr)
.collect(toImmutableSet());
logger.atInfo().log("Exporting domain drop list for TLDs with XAP enabled: %s", xapTlds);
if (xapTlds.isEmpty()) {
logger.atInfo().log("No open TLDs found with XAP enabled.");
logger.atInfo().log("No TLDs found with XAP enabled.");
exportToDrive(createCsv(ImmutableList.of()));
return;
}
@@ -86,6 +86,11 @@ public final class Tlds {
return ImmutableSet.copyOf(filterValues(cache.get(), equalTo(type)).keySet());
}
/** Returns all TLD entities loaded fresh from the database. */
public static ImmutableSet<Tld> getTldEntities() {
return Tld.get(cache.get().keySet());
}
/** Returns the TLD entities themselves of the given type loaded fresh from the database. */
public static ImmutableSet<Tld> getTldEntitiesOfType(TldType type) {
return Tld.get(filterValues(cache.get(), equalTo(type)).keySet());
@@ -39,10 +39,13 @@ import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.storage.drive.DriveConnection;
import google.registry.testing.FakeClock;
import google.registry.testing.SystemPropertyExtension;
import google.registry.util.RegistryEnvironment;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.ArgumentCaptor;
@@ -59,6 +62,10 @@ class ExportDropListActionTest {
final JpaIntegrationTestExtension jpa =
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
@RegisterExtension
@Order(Integer.MAX_VALUE)
final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension();
@BeforeEach
void beforeEach() {
createTld("open1");
@@ -80,11 +87,23 @@ class ExportDropListActionTest {
.build());
createTld("closed");
persistResource(Tld.get("closed").asBuilder().setInvoicingEnabled(false).build());
persistResource(
Tld.get("closed")
.asBuilder()
.setInvoicingEnabled(false)
.setExpiryAccessPeriodTransitions(
ImmutableSortedMap.of(START_INSTANT, ExpiryAccessPeriodMode.ENABLED))
.build());
createTld("testtld");
persistResource(
Tld.get("testtld").asBuilder().setTldType(TldType.TEST).setInvoicingEnabled(true).build());
Tld.get("testtld")
.asBuilder()
.setTldType(TldType.TEST)
.setInvoicingEnabled(false)
.setExpiryAccessPeriodTransitions(
ImmutableSortedMap.of(START_INSTANT, ExpiryAccessPeriodMode.ENABLED))
.build());
action = new ExportDropListAction();
action.clock = clock;
@@ -104,7 +123,7 @@ class ExportDropListActionTest {
}
@Test
void test_exportsDropListAcrossOpenTlds_sortedByDomainName() throws Exception {
void test_exportsDropListAcrossTlds_sortedByDomainName() throws Exception {
// Active domain with no drop date (END_INSTANT) on open TLD -> excluded
persistActiveDomain("active.open1");
@@ -112,9 +131,12 @@ class ExportDropListActionTest {
persistDeletedDomain("zebra.open1", Instant.parse("2020-02-07T02:02:02Z"));
persistDeletedDomain("alpha.open2", Instant.parse("2020-02-04T02:02:02Z"));
// Pending delete domain on non-invoicing (closed) TLD -> excluded
// Pending delete domain on non-invoicing (closed) TLD with XAP enabled -> included
persistDeletedDomain("closed.closed", Instant.parse("2020-02-05T02:02:02Z"));
// Pending delete domain on test TLD with XAP enabled in non-PROD -> included
persistDeletedDomain("test.testtld", Instant.parse("2020-02-06T02:02:02Z"));
// Pending delete domain on open TLD with XAP disabled -> excluded
createTld("noxap");
persistResource(
@@ -126,9 +148,6 @@ class ExportDropListActionTest {
.build());
persistDeletedDomain("noxap.noxap", Instant.parse("2020-02-05T02:02:02Z"));
// Pending delete domain on test TLD -> excluded
persistDeletedDomain("test.testtld", Instant.parse("2020-02-06T02:02:02Z"));
// Already deleted domain on open TLD -> excluded
persistDeletedDomain("deleted.open1", Instant.parse("2020-02-01T02:02:02Z"));
@@ -138,10 +157,33 @@ class ExportDropListActionTest {
"""
domain_name,tld,deletion_time
alpha.open2,open2,2020-02-04T02:02:02Z
closed.closed,closed,2020-02-05T02:02:02Z
test.testtld,testtld,2020-02-06T02:02:02Z
zebra.open1,open1,2020-02-07T02:02:02Z
""");
}
@Test
void test_exportsDropList_excludesTestTldsInProduction() throws Exception {
RegistryEnvironment.PRODUCTION.setup(systemPropertyExtension);
// Pending delete domains on open TLD and non-invoicing TLD with XAP enabled -> included
persistDeletedDomain("alpha.open2", Instant.parse("2020-02-04T02:02:02Z"));
persistDeletedDomain("closed.closed", Instant.parse("2020-02-05T02:02:02Z"));
// Pending delete domain on test TLD with XAP enabled -> excluded in PROD
persistDeletedDomain("test.testtld", Instant.parse("2020-02-06T02:02:02Z"));
action.run();
verifyExportedToDrive(
"""
domain_name,tld,deletion_time
alpha.open2,open2,2020-02-04T02:02:02Z
closed.closed,closed,2020-02-05T02:02:02Z
""");
}
@Test
void test_skipsDriveExport_whenDriveFolderIdIsEmpty() {
action.driveFolderId = Optional.empty();
@@ -164,7 +206,7 @@ class ExportDropListActionTest {
}
@Test
void test_noOpenTldsWithXap_outputsHeaderOnly() throws Exception {
void test_noTldsWithXap_outputsHeaderOnly() throws Exception {
persistResource(
Tld.get("open1")
.asBuilder()
@@ -177,6 +219,18 @@ class ExportDropListActionTest {
.setExpiryAccessPeriodTransitions(
ImmutableSortedMap.of(START_INSTANT, ExpiryAccessPeriodMode.DISABLED))
.build());
persistResource(
Tld.get("closed")
.asBuilder()
.setExpiryAccessPeriodTransitions(
ImmutableSortedMap.of(START_INSTANT, ExpiryAccessPeriodMode.DISABLED))
.build());
persistResource(
Tld.get("testtld")
.asBuilder()
.setExpiryAccessPeriodTransitions(
ImmutableSortedMap.of(START_INSTANT, ExpiryAccessPeriodMode.DISABLED))
.build());
persistDeletedDomain("zebra.open1", Instant.parse("2020-02-07T02:02:02Z"));
@@ -54,6 +54,8 @@ class TldsTest {
void test_getTldEntities() {
initTestTlds();
persistResource(newTld("testtld", "TESTTLD").asBuilder().setTldType(TldType.TEST).build());
assertThat(Tlds.getTldEntities())
.containsExactly(Tld.get("foo"), Tld.get("a.b.c"), Tld.get("testtld"));
assertThat(Tlds.getTldEntitiesOfType(TldType.REAL))
.containsExactly(Tld.get("foo"), Tld.get("a.b.c"));
assertThat(Tlds.getTldEntitiesOfType(TldType.TEST)).containsExactly(Tld.get("testtld"));