Add initial support to write/update reserved lists to Cloud SQL (#388)

* Add initial support to write reserved list to Cloud SQL

* Add support to update reserved list in Cloud SQL

* Fix wrong check when override is enabled in create command

* Add sql dependent tests to the suite

* Address comment

* Make invocation of super.execute more readable

* Add test to check upper case and non-puny code label

* Move ReservedListDao related classes to schema package

* Simplify a function name
This commit is contained in:
Shicong Huang 2019-12-19 12:51:48 -05:00 committed by GitHub
parent f301314d97
commit f76030f7f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 573 additions and 12 deletions

View file

@ -121,7 +121,7 @@ public abstract class BaseDomainLabelList<T extends Comparable<?>, R extends Dom
* (sans comment) and the comment (in that order). If the line was blank or empty, then this
* method returns an empty list.
*/
protected static List<String> splitOnComment(String line) {
public static List<String> splitOnComment(String line) {
String comment = "";
int index = line.indexOf('#');
if (index != -1) {

View file

@ -14,13 +14,20 @@
package google.registry.schema.tld;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.sortedCopyOf;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
import google.registry.model.registry.label.ReservationType;
import java.util.Map;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
@ -114,6 +121,22 @@ public class ReservedList extends ImmutableObject {
/** Constructs a {@link ReservedList} object. */
public static ReservedList create(
String name, Boolean shouldPublish, Map<String, ReservedEntry> labelsToReservations) {
ImmutableList<String> invalidLabels =
labelsToReservations.entrySet().parallelStream()
.flatMap(
entry -> {
String label = entry.getKey();
if (label.equals(canonicalizeDomainName(label))) {
return Stream.empty();
} else {
return Stream.of(label);
}
})
.collect(toImmutableList());
checkArgument(
invalidLabels.isEmpty(),
"Label(s) [%s] must be in puny-coded, lower-case form",
Joiner.on(",").join(sortedCopyOf(invalidLabels)));
return new ReservedList(name, shouldPublish, labelsToReservations);
}

View file

@ -0,0 +1,47 @@
// Copyright 2019 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.schema.tld;
import static google.registry.model.transaction.TransactionManagerFactory.jpaTm;
/** Data access object class for {@link ReservedList} */
public class ReservedListDao {
/** Persist a new reserved list to Cloud SQL. */
public static void save(ReservedList reservedList) {
jpaTm().transact(() -> jpaTm().getEntityManager().persist(reservedList));
}
/**
* Returns whether the reserved list of the given name exists.
*
* <p>This means that at least one reserved list revision must exist for the given name.
*/
public static boolean checkExists(String reservedListName) {
return jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createQuery("SELECT 1 FROM ReservedList WHERE name = :name", Integer.class)
.setParameter("name", reservedListName)
.setMaxResults(1)
.getResultList()
.size()
> 0);
}
private ReservedListDao() {}
}

View file

@ -14,9 +14,23 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.registry.label.BaseDomainLabelList.splitOnComment;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import com.beust.jcommander.Parameter;
import com.google.common.base.Splitter;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multiset;
import com.google.common.flogger.FluentLogger;
import google.registry.model.registry.label.ReservationType;
import google.registry.schema.tld.ReservedList.ReservedEntry;
import google.registry.tools.params.PathParameter;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
@ -25,6 +39,8 @@ import javax.annotation.Nullable;
*/
public abstract class CreateOrUpdateReservedListCommand extends MutatingCommand {
static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Nullable
@Parameter(
names = {"-n", "--name"},
@ -45,4 +61,81 @@ public abstract class CreateOrUpdateReservedListCommand extends MutatingCommand
"Whether the list is published to the concatenated list on Drive (defaults to true).",
arity = 1)
Boolean shouldPublish;
@Parameter(
names = {"--also_cloud_sql"},
description =
"Persist reserved list to Cloud SQL in addition to Datastore; defaults to false.")
boolean alsoCloudSql;
google.registry.schema.tld.ReservedList cloudSqlReservedList;
abstract void saveToCloudSql();
@Override
protected String execute() throws Exception {
// Save the list to Datastore and output its response.
String output = super.execute();
logger.atInfo().log(output);
String cloudSqlMessage;
if (alsoCloudSql) {
cloudSqlMessage =
String.format(
"Saved reserved list %s with %d entries",
name, cloudSqlReservedList.getLabelsToReservations().size());
try {
logger.atInfo().log("Saving reserved list to Cloud SQL for TLD %s", name);
saveToCloudSql();
logger.atInfo().log(cloudSqlMessage);
} catch (Throwable e) {
cloudSqlMessage =
"Unexpected error saving reserved list to Cloud SQL from nomulus tool command";
logger.atSevere().withCause(e).log(cloudSqlMessage);
}
} else {
cloudSqlMessage = "Persisting reserved list to Cloud SQL is not enabled";
}
return cloudSqlMessage;
}
/** Turns the list CSV data into a map of labels to {@link ReservedEntry}. */
static ImmutableMap<String, ReservedEntry> parseToReservationsByLabels(Iterable<String> lines) {
Map<String, ReservedEntry> labelsToEntries = Maps.newHashMap();
Multiset<String> duplicateLabels = HashMultiset.create();
for (String originalLine : lines) {
List<String> lineAndComment = splitOnComment(originalLine);
if (lineAndComment.isEmpty()) {
continue;
}
String line = lineAndComment.get(0);
String comment = lineAndComment.get(1);
List<String> parts = Splitter.on(',').trimResults().splitToList(line);
checkArgument(
parts.size() == 2 || parts.size() == 3,
"Could not parse line in reserved list: %s",
originalLine);
String label = parts.get(0);
checkArgument(
label.equals(canonicalizeDomainName(label)),
"Label '%s' must be in puny-coded, lower-case form",
label);
ReservationType reservationType = ReservationType.valueOf(parts.get(1));
ReservedEntry reservedEntry = ReservedEntry.create(reservationType, comment);
// Check if the label was already processed for this list (which is an error), and if so,
// accumulate it so that a list of all duplicates can be thrown.
if (labelsToEntries.containsKey(label)) {
duplicateLabels.add(label, duplicateLabels.contains(label) ? 1 : 2);
} else {
labelsToEntries.put(label, reservedEntry);
}
}
if (!duplicateLabels.isEmpty()) {
throw new IllegalStateException(
String.format(
"Reserved list cannot contain duplicate labels. Dupes (with counts) were: %s",
duplicateLabels));
}
return ImmutableMap.copyOf(labelsToEntries);
}
}

View file

@ -16,6 +16,7 @@ package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.registry.Registries.assertTldExists;
import static google.registry.model.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.ListNamingUtils.convertFilePathToName;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.joda.time.DateTimeZone.UTC;
@ -26,6 +27,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import google.registry.model.registry.label.ReservedList;
import google.registry.schema.tld.ReservedListDao;
import java.nio.file.Files;
import java.util.List;
import org.joda.time.DateTime;
@ -54,15 +56,35 @@ final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand
validateListName(name);
}
DateTime now = DateTime.now(UTC);
List<String> allLines = Files.readAllLines(input, UTF_8);
boolean shouldPublish = this.shouldPublish == null || this.shouldPublish;
ReservedList reservedList =
new ReservedList.Builder()
.setName(name)
.setReservedListMapFromLines(Files.readAllLines(input, UTF_8))
.setShouldPublish(shouldPublish == null || shouldPublish)
.setReservedListMapFromLines(allLines)
.setShouldPublish(shouldPublish)
.setCreationTime(now)
.setLastUpdateTime(now)
.build();
stageEntityChange(null, reservedList);
if (alsoCloudSql) {
cloudSqlReservedList =
google.registry.schema.tld.ReservedList.create(
name, shouldPublish, parseToReservationsByLabels(allLines));
}
}
@Override
void saveToCloudSql() {
jpaTm()
.transact(
() -> {
checkArgument(
!ReservedListDao.checkExists(cloudSqlReservedList.getName()),
"A reserved list of this name already exists: %s.",
cloudSqlReservedList.getName());
ReservedListDao.save(cloudSqlReservedList);
});
}
private static void validateListName(String name) {

View file

@ -15,14 +15,17 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.ListNamingUtils.convertFilePathToName;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.beust.jcommander.Parameters;
import com.google.common.base.Strings;
import google.registry.model.registry.label.ReservedList;
import google.registry.schema.tld.ReservedListDao;
import google.registry.util.SystemClock;
import java.nio.file.Files;
import java.util.List;
import java.util.Optional;
/** Command to safely update {@link ReservedList} on Datastore. */
@ -32,18 +35,38 @@ final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand
@Override
protected void init() throws Exception {
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(input) : name;
// TODO(shicong): Read existing entry from Cloud SQL
Optional<ReservedList> existing = ReservedList.get(name);
checkArgument(
existing.isPresent(), "Could not update reserved list %s because it doesn't exist.", name);
boolean shouldPublish =
this.shouldPublish == null ? existing.get().getShouldPublish() : this.shouldPublish;
List<String> allLines = Files.readAllLines(input, UTF_8);
ReservedList.Builder updated =
existing
.get()
.asBuilder()
.setReservedListMapFromLines(Files.readAllLines(input, UTF_8))
.setLastUpdateTime(new SystemClock().nowUtc());
if (shouldPublish != null) {
updated.setShouldPublish(shouldPublish);
}
.setReservedListMapFromLines(allLines)
.setLastUpdateTime(new SystemClock().nowUtc())
.setShouldPublish(shouldPublish);
stageEntityChange(existing.get(), updated.build());
if (alsoCloudSql) {
cloudSqlReservedList =
google.registry.schema.tld.ReservedList.create(
name, shouldPublish, parseToReservationsByLabels(allLines));
}
}
@Override
void saveToCloudSql() {
jpaTm()
.transact(
() -> {
checkArgument(
ReservedListDao.checkExists(cloudSqlReservedList.getName()),
"A reserved list of this name doesn't exist: %s.",
cloudSqlReservedList.getName());
ReservedListDao.save(cloudSqlReservedList);
});
}
}

View file

@ -26,7 +26,10 @@ import google.registry.persistence.UpdateAutoTimestampConverterTest;
import google.registry.persistence.ZonedDateTimeConverterTest;
import google.registry.schema.cursor.CursorDaoTest;
import google.registry.schema.tld.PremiumListDaoTest;
import google.registry.schema.tld.ReservedListDaoTest;
import google.registry.schema.tmch.ClaimsListDaoTest;
import google.registry.tools.CreateReservedListCommandTest;
import google.registry.tools.UpdateReservedListCommandTest;
import google.registry.ui.server.registrar.RegistryLockGetActionTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@ -47,6 +50,7 @@ import org.junit.runners.Suite.SuiteClasses;
BloomFilterConverterTest.class,
ClaimsListDaoTest.class,
CreateAutoTimestampConverterTest.class,
CreateReservedListCommandTest.class,
CurrencyUnitConverterTest.class,
CursorDaoTest.class,
DateTimeConverterTest.class,
@ -56,7 +60,9 @@ import org.junit.runners.Suite.SuiteClasses;
PremiumListDaoTest.class,
RegistryLockDaoTest.class,
RegistryLockGetActionTest.class,
ReservedListDaoTest.class,
UpdateAutoTimestampConverterTest.class,
UpdateReservedListCommandTest.class,
ZonedDateTimeConverterTest.class
})
public class SqlIntegrationTestSuite {}

View file

@ -0,0 +1,69 @@
// Copyright 2019 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.schema.tld;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.collect.ImmutableMap;
import google.registry.model.registry.label.ReservationType;
import google.registry.model.transaction.JpaTransactionManagerRule;
import google.registry.schema.tld.ReservedList.ReservedEntry;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link ReservedListDao}. */
@RunWith(JUnit4.class)
public class ReservedListDaoTest {
@Rule
public final JpaTransactionManagerRule jpaTmRule =
new JpaTransactionManagerRule.Builder().build();
private static final ImmutableMap<String, ReservedEntry> TEST_RESERVATIONS =
ImmutableMap.of(
"food",
ReservedEntry.create(ReservationType.RESERVED_FOR_SPECIFIC_USE, null),
"music",
ReservedEntry.create(ReservationType.FULLY_BLOCKED, "fully blocked"));
@Test
public void save_worksSuccessfully() {
ReservedList reservedList = ReservedList.create("testname", false, TEST_RESERVATIONS);
ReservedListDao.save(reservedList);
jpaTm()
.transact(
() -> {
ReservedList persistedList =
jpaTm()
.getEntityManager()
.createQuery("FROM ReservedList WHERE name = :name", ReservedList.class)
.setParameter("name", "testname")
.getSingleResult();
assertThat(persistedList.getLabelsToReservations())
.containsExactlyEntriesIn(TEST_RESERVATIONS);
assertThat(persistedList.getCreationTimestamp())
.isEqualTo(jpaTmRule.getTxnClock().nowUtc());
});
}
@Test
public void checkExists_worksSuccessfully() {
assertThat(ReservedListDao.checkExists("testlist")).isFalse();
ReservedListDao.save(ReservedList.create("testlist", false, TEST_RESERVATIONS));
assertThat(ReservedListDao.checkExists("testlist")).isTrue();
}
}

View file

@ -15,6 +15,8 @@
package google.registry.schema.tld;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
import static google.registry.testing.JUnitBackports.assertThrows;
import com.google.common.collect.ImmutableMap;
import google.registry.model.registry.label.ReservationType;
@ -50,4 +52,34 @@ public class ReservedListTest {
ReservedEntry.create(
ReservationType.RESERVED_FOR_ANCHOR_TENANT, "reserved for anchor tenant"));
}
@Test
public void create_throwsExceptionWhenLabelIsNotLowercase() {
Exception e =
assertThrows(
IllegalArgumentException.class,
() ->
ReservedList.create(
"UPPER.tld",
true,
ImmutableMap.of("UPPER", ReservedEntry.create(FULLY_BLOCKED, ""))));
assertThat(e)
.hasMessageThat()
.contains("Label(s) [UPPER] must be in puny-coded, lower-case form");
}
@Test
public void create_labelMustBePunyCoded() {
Exception e =
assertThrows(
IllegalArgumentException.class,
() ->
ReservedList.create(
"lower.みんな",
true,
ImmutableMap.of("みんな", ReservedEntry.create(FULLY_BLOCKED, ""))));
assertThat(e)
.hasMessageThat()
.contains("Label(s) [みんな] must be in puny-coded, lower-case form");
}
}

View file

@ -0,0 +1,126 @@
// Copyright 2019 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.tools;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.registry.label.ReservationType.ALLOWED_IN_SUNRISE;
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.tools.CreateOrUpdateReservedListCommand.parseToReservationsByLabels;
import com.google.common.collect.ImmutableList;
import google.registry.model.registry.label.ReservationType;
import google.registry.schema.tld.ReservedList.ReservedEntry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link CreateOrUpdateReservedListCommand}. */
@RunWith(JUnit4.class)
public class CreateOrUpdateReservedListCommandTest {
@Test
public void parseToReservationsByLabels_worksCorrectly() {
assertThat(
parseToReservationsByLabels(
ImmutableList.of(
"reserveddomain,FULLY_BLOCKED",
"availableinga,ALLOWED_IN_SUNRISE#allowed_in_sunrise",
"fourletterword,FULLY_BLOCKED")))
.containsExactly(
"reserveddomain",
ReservedEntry.create(ReservationType.FULLY_BLOCKED, ""),
"availableinga",
ReservedEntry.create(ALLOWED_IN_SUNRISE, "allowed_in_sunrise"),
"fourletterword",
ReservedEntry.create(FULLY_BLOCKED, ""));
}
@Test
public void parseToReservationsByLabels_throwsExceptionForInvalidLabel() {
Throwable thrown =
assertThrows(
IllegalArgumentException.class,
() ->
parseToReservationsByLabels(
ImmutableList.of("reserveddomain,FULLY_BLOCKED", "UPPER,FULLY_BLOCKED")));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Label 'UPPER' must be in puny-coded, lower-case form");
}
@Test
public void parseToReservationsByLabels_throwsExceptionForNonPunyCodedLabel() {
Throwable thrown =
assertThrows(
IllegalArgumentException.class,
() ->
parseToReservationsByLabels(
ImmutableList.of("reserveddomain,FULLY_BLOCKED", "みんな,FULLY_BLOCKED")));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Label 'みんな' must be in puny-coded, lower-case form");
}
@Test
public void parseToReservationsByLabels_throwsExceptionForInvalidReservationType() {
Throwable thrown =
assertThrows(
IllegalArgumentException.class,
() ->
parseToReservationsByLabels(
ImmutableList.of(
"reserveddomain,FULLY_BLOCKED", "invalidtype,INVALID_RESERVATION_TYPE")));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"No enum constant"
+ " google.registry.model.registry.label.ReservationType.INVALID_RESERVATION_TYPE");
}
@Test
public void parseToReservationsByLabels_throwsExceptionForInvalidLines() {
Throwable thrown =
assertThrows(
IllegalArgumentException.class,
() ->
parseToReservationsByLabels(
ImmutableList.of(
"reserveddomain,FULLY_BLOCKED,too,many,parts",
"fourletterword,FULLY_BLOCKED")));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"Could not parse line in reserved list: reserveddomain,FULLY_BLOCKED,too,many,parts");
}
@Test
public void parseToReservationsByLabels_throwsExceptionForDuplicateEntries() {
Throwable thrown =
assertThrows(
IllegalStateException.class,
() ->
parseToReservationsByLabels(
ImmutableList.of(
"reserveddomain,FULLY_BLOCKED",
"fourletterword,FULLY_BLOCKED",
"fourletterword,FULLY_BLOCKED")));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"Reserved list cannot contain duplicate labels. Dupes (with counts) were:"
+ " [fourletterword x 2]");
}
}

View file

@ -14,15 +14,26 @@
package google.registry.tools;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
import static google.registry.model.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.testing.TestDataHelper.loadFile;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.beust.jcommander.ParameterException;
import com.google.common.io.Files;
import com.google.common.truth.Truth8;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.transaction.JpaTransactionManagerRule;
import google.registry.schema.tld.ReservedList.ReservedEntry;
import google.registry.schema.tld.ReservedListDao;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import javax.persistence.EntityManager;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
/**
@ -33,6 +44,10 @@ import org.junit.Test;
public abstract class CreateOrUpdateReservedListCommandTestCase
<T extends CreateOrUpdateReservedListCommand> extends CommandTestCase<T> {
@Rule
public final JpaTransactionManagerRule jpaTmRule =
new JpaTransactionManagerRule.Builder().build();
String reservedTermsPath;
String invalidReservedTermsPath;
@ -64,4 +79,52 @@ public abstract class CreateOrUpdateReservedListCommandTestCase
IllegalArgumentException.class,
() -> runCommandForced("--name=xn--q9jyb4c-blork", "--input=" + invalidReservedTermsPath));
}
google.registry.schema.tld.ReservedList createCloudSqlReservedList(
String name, boolean shouldPublish, Map<String, ReservedEntry> labelsToEntries) {
return google.registry.schema.tld.ReservedList.create(name, shouldPublish, labelsToEntries);
}
google.registry.schema.tld.ReservedList getCloudSqlReservedList(String name) {
return jpaTm()
.transact(
() -> {
EntityManager em = jpaTm().getEntityManager();
long revisionId =
em.createQuery(
"SELECT MAX(rl.revisionId) FROM ReservedList rl WHERE name = :name",
Long.class)
.setParameter("name", name)
.getSingleResult();
return em.createQuery(
"FROM ReservedList rl LEFT JOIN FETCH rl.labelsToReservations WHERE"
+ " rl.revisionId = :revisionId",
google.registry.schema.tld.ReservedList.class)
.setParameter("revisionId", revisionId)
.getSingleResult();
});
}
void verifyXnq9jyb4cInCloudSql() {
assertThat(ReservedListDao.checkExists("xn--q9jyb4c_common-reserved")).isTrue();
google.registry.schema.tld.ReservedList persistedList =
getCloudSqlReservedList("xn--q9jyb4c_common-reserved");
assertThat(persistedList.getName()).isEqualTo("xn--q9jyb4c_common-reserved");
assertThat(persistedList.getShouldPublish()).isTrue();
assertThat(persistedList.getCreationTimestamp()).isEqualTo(jpaTmRule.getTxnClock().nowUtc());
assertThat(persistedList.getLabelsToReservations())
.containsExactly(
"baddies",
ReservedEntry.create(FULLY_BLOCKED, ""),
"ford",
ReservedEntry.create(FULLY_BLOCKED, "random comment"));
}
void verifyXnq9jyb4cInDatastore() {
Truth8.assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isPresent();
ReservedList reservedList = ReservedList.get("xn--q9jyb4c_common-reserved").get();
assertThat(reservedList.getReservedListEntries()).hasSize(2);
Truth8.assertThat(reservedList.getReservationInList("baddies")).hasValue(FULLY_BLOCKED);
Truth8.assertThat(reservedList.getReservationInList("ford")).hasValue(FULLY_BLOCKED);
}
}

View file

@ -24,8 +24,11 @@ import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.tools.CreateReservedListCommand.INVALID_FORMAT_ERROR_MESSAGE;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.collect.ImmutableMap;
import google.registry.model.registry.Registry;
import google.registry.model.registry.label.ReservedList;
import google.registry.schema.tld.ReservedList.ReservedEntry;
import google.registry.schema.tld.ReservedListDao;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
@ -173,6 +176,28 @@ public class CreateReservedListCommandTest extends
runNameTestExpectedFailure("soy_$oy", INVALID_FORMAT_ERROR_MESSAGE);
}
@Test
public void testSaveToCloudSql_succeeds() throws Exception {
runCommandForced(
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--also_cloud_sql");
verifyXnq9jyb4cInDatastore();
verifyXnq9jyb4cInCloudSql();
}
@Test
public void testSaveToCloudSql_noExceptionThrownWhenSaveFail() throws Exception {
// Note that, during the dual-write phase, we want to make sure that no exception will be
// thrown if saving reserved list to Cloud SQL fails.
ReservedListDao.save(
createCloudSqlReservedList(
"xn--q9jyb4c_common-reserved",
true,
ImmutableMap.of("testdomain", ReservedEntry.create(FULLY_BLOCKED, ""))));
runCommandForced(
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--also_cloud_sql");
verifyXnq9jyb4cInDatastore();
}
private void runNameTestExpectedFailure(String name, String expectedErrorMsg) {
IllegalArgumentException thrown =
assertThrows(

View file

@ -22,14 +22,17 @@ import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import google.registry.model.registry.label.ReservedList;
import google.registry.schema.tld.ReservedList.ReservedEntry;
import google.registry.schema.tld.ReservedListDao;
import org.junit.Test;
/** Unit tests for {@link UpdateReservedListCommand}. */
public class UpdateReservedListCommandTest extends
CreateOrUpdateReservedListCommandTestCase<UpdateReservedListCommand> {
private void populateInitialReservedList(boolean shouldPublish) {
private void populateInitialReservedListInDatastore(boolean shouldPublish) {
persistResource(
new ReservedList.Builder()
.setName("xn--q9jyb4c_common-reserved")
@ -40,6 +43,14 @@ public class UpdateReservedListCommandTest extends
.build());
}
private void populateInitialReservedListInCloudSql(boolean shouldPublish) {
ReservedListDao.save(
createCloudSqlReservedList(
"xn--q9jyb4c_common-reserved",
shouldPublish,
ImmutableMap.of("helicopter", ReservedEntry.create(FULLY_BLOCKED, ""))));
}
@Test
public void testSuccess() throws Exception {
runSuccessfulUpdateTest("--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath);
@ -52,7 +63,7 @@ public class UpdateReservedListCommandTest extends
@Test
public void testSuccess_lastUpdateTime_updatedCorrectly() throws Exception {
populateInitialReservedList(true);
populateInitialReservedListInDatastore(true);
ReservedList original = ReservedList.get("xn--q9jyb4c_common-reserved").get();
runCommandForced("--input=" + reservedTermsPath);
ReservedList updated = ReservedList.get("xn--q9jyb4c_common-reserved").get();
@ -71,7 +82,7 @@ public class UpdateReservedListCommandTest extends
@Test
public void testSuccess_shouldPublish_doesntOverrideFalseIfNotSpecified() throws Exception {
populateInitialReservedList(false);
populateInitialReservedListInDatastore(false);
runCommandForced("--input=" + reservedTermsPath);
assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isPresent();
ReservedList reservedList = ReservedList.get("xn--q9jyb4c_common-reserved").get();
@ -79,7 +90,7 @@ public class UpdateReservedListCommandTest extends
}
private void runSuccessfulUpdateTest(String... args) throws Exception {
populateInitialReservedList(true);
populateInitialReservedListInDatastore(true);
runCommandForced(args);
assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isPresent();
ReservedList reservedList = ReservedList.get("xn--q9jyb4c_common-reserved").get();
@ -100,4 +111,25 @@ public class UpdateReservedListCommandTest extends
runCommand("--force", "--name=xn--q9jyb4c_poobah", "--input=" + reservedTermsPath));
assertThat(thrown).hasMessageThat().contains(errorMessage);
}
@Test
public void testSaveToCloudSql_succeeds() throws Exception {
populateInitialReservedListInDatastore(true);
populateInitialReservedListInCloudSql(true);
runCommandForced(
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--also_cloud_sql");
verifyXnq9jyb4cInDatastore();
verifyXnq9jyb4cInCloudSql();
}
@Test
public void testSaveToCloudSql_noExceptionThrownWhenSaveFail() throws Exception {
// Note that, during the dual-write phase, we want to make sure that no exception will be
// thrown if saving reserved list to Cloud SQL fails.
populateInitialReservedListInDatastore(true);
runCommandForced(
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--also_cloud_sql");
verifyXnq9jyb4cInDatastore();
assertThat(ReservedListDao.checkExists("xn--q9jyb4c_common-reserved")).isFalse();
}
}