mirror of
https://github.com/google/nomulus.git
synced 2025-07-21 10:16:07 +02:00
Always use Cloud SQL as primary for Reserved and Premium Lists (#1113)
* Always use Cloud SQL as primary for Reserved and Premium Lists * small typos * Add a state check * Add test for bloom filter * fix import
This commit is contained in:
parent
66fc31b7a2
commit
4224e7eef3
13 changed files with 88 additions and 469 deletions
|
@ -14,12 +14,11 @@
|
||||||
|
|
||||||
package google.registry.model.registry.label;
|
package google.registry.model.registry.label;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
import static google.registry.model.DatabaseMigrationUtils.suppressExceptionUnlessInTest;
|
import static google.registry.model.DatabaseMigrationUtils.suppressExceptionUnlessInTest;
|
||||||
|
|
||||||
import com.google.common.collect.Streams;
|
import com.google.common.collect.Streams;
|
||||||
import google.registry.model.DatabaseMigrationUtils;
|
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
|
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||||
import google.registry.schema.tld.PremiumListSqlDao;
|
import google.registry.schema.tld.PremiumListSqlDao;
|
||||||
|
@ -32,27 +31,20 @@ import org.joda.money.Money;
|
||||||
/**
|
/**
|
||||||
* DAO for {@link PremiumList} objects that handles the branching paths for SQL and Datastore.
|
* DAO for {@link PremiumList} objects that handles the branching paths for SQL and Datastore.
|
||||||
*
|
*
|
||||||
* <p>For write actions, this class will perform the action against the primary database then, after
|
* <p>For write actions, this class will perform the action against Cloud SQL then, after that
|
||||||
* that success or failure, against the secondary database. If the secondary database fails, an
|
* success or failure, against Datastore. If Datastore fails, an error is logged (but not thrown).
|
||||||
* error is logged (but not thrown).
|
|
||||||
*
|
*
|
||||||
* <p>For read actions, when retrieving a price, we will log if the primary and secondary databases
|
* <p>For read actions, when retrieving a price, we will log if the primary and secondary databases
|
||||||
* have different values (or if the retrieval from the second database fails).
|
* have different values (or if the retrieval from Datastore fails).
|
||||||
*
|
|
||||||
* <p>TODO (gbrodman): Change the isOfy() calls to the runtime selection of DBs when available
|
|
||||||
*/
|
*/
|
||||||
public class PremiumListDualDao {
|
public class PremiumListDualDao {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves from the appropriate DB and returns the most recent premium list with the given name,
|
* Retrieves from Cloud SQL and returns the most recent premium list with the given name, or
|
||||||
* or absent if no such list exists.
|
* absent if no such list exists.
|
||||||
*/
|
*/
|
||||||
public static Optional<PremiumList> getLatestRevision(String premiumListName) {
|
public static Optional<PremiumList> getLatestRevision(String premiumListName) {
|
||||||
if (DatabaseMigrationUtils.isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
|
||||||
return PremiumListDatastoreDao.getLatestRevision(premiumListName);
|
|
||||||
} else {
|
|
||||||
return PremiumListSqlDao.getLatestRevision(premiumListName);
|
return PremiumListSqlDao.getLatestRevision(premiumListName);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,7 +53,7 @@ public class PremiumListDualDao {
|
||||||
* <p>Returns absent if the label is not premium or there is no premium list for this registry.
|
* <p>Returns absent if the label is not premium or there is no premium list for this registry.
|
||||||
*
|
*
|
||||||
* <p>Retrieves the price from both primary and secondary databases, and logs in the event of a
|
* <p>Retrieves the price from both primary and secondary databases, and logs in the event of a
|
||||||
* failure in the secondary (but does not throw an exception).
|
* failure in Datastore (but does not throw an exception).
|
||||||
*/
|
*/
|
||||||
public static Optional<Money> getPremiumPrice(String label, Registry registry) {
|
public static Optional<Money> getPremiumPrice(String label, Registry registry) {
|
||||||
if (registry.getPremiumList() == null) {
|
if (registry.getPremiumList() == null) {
|
||||||
|
@ -69,98 +61,56 @@ public class PremiumListDualDao {
|
||||||
}
|
}
|
||||||
String premiumListName = registry.getPremiumList().getName();
|
String premiumListName = registry.getPremiumList().getName();
|
||||||
Optional<Money> primaryResult;
|
Optional<Money> primaryResult;
|
||||||
if (DatabaseMigrationUtils.isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
|
||||||
primaryResult =
|
|
||||||
PremiumListDatastoreDao.getPremiumPrice(premiumListName, label, registry.getTldStr());
|
|
||||||
} else {
|
|
||||||
primaryResult = PremiumListSqlDao.getPremiumPrice(premiumListName, label);
|
primaryResult = PremiumListSqlDao.getPremiumPrice(premiumListName, label);
|
||||||
}
|
// Also load the value from Datastore, compare the two results, and log if different.
|
||||||
// Also load the value from the secondary DB, compare the two results, and log if different.
|
suppressExceptionUnlessInTest(
|
||||||
if (DatabaseMigrationUtils.isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
() -> {
|
||||||
suppressExceptionUnlessInTest(
|
Optional<Money> secondaryResult =
|
||||||
() -> {
|
PremiumListDatastoreDao.getPremiumPrice(premiumListName, label, registry.getTldStr());
|
||||||
Optional<Money> secondaryResult =
|
checkState(
|
||||||
PremiumListSqlDao.getPremiumPrice(premiumListName, label);
|
primaryResult.equals(secondaryResult),
|
||||||
if (!primaryResult.equals(secondaryResult)) {
|
"Unequal prices for domain %s.%s from primary SQL DB (%s) and secondary Datastore db"
|
||||||
throw new IllegalStateException(
|
+ " (%s).",
|
||||||
String.format(
|
label,
|
||||||
"Unequal prices for domain %s.%s from primary Datastore DB (%s) and "
|
registry.getTldStr(),
|
||||||
+ "secondary SQL db (%s).",
|
primaryResult,
|
||||||
label, registry.getTldStr(), primaryResult, secondaryResult));
|
secondaryResult);
|
||||||
}
|
},
|
||||||
},
|
String.format(
|
||||||
String.format(
|
"Error loading price of domain %s.%s from Datastore.", label, registry.getTldStr()));
|
||||||
"Error loading price of domain %s.%s from Cloud SQL.", label, registry.getTldStr()));
|
|
||||||
} else {
|
|
||||||
suppressExceptionUnlessInTest(
|
|
||||||
() -> {
|
|
||||||
Optional<Money> secondaryResult =
|
|
||||||
PremiumListDatastoreDao.getPremiumPrice(
|
|
||||||
premiumListName, label, registry.getTldStr());
|
|
||||||
if (!primaryResult.equals(secondaryResult)) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
String.format(
|
|
||||||
"Unequal prices for domain %s.%s from primary SQL DB (%s) and secondary "
|
|
||||||
+ "Datastore db (%s).",
|
|
||||||
label, registry.getTldStr(), primaryResult, secondaryResult));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
String.format(
|
|
||||||
"Error loading price of domain %s.%s from Datastore.", label, registry.getTldStr()));
|
|
||||||
}
|
|
||||||
return primaryResult;
|
return primaryResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the given list data to both primary and secondary databases.
|
* Saves the given list data to both primary and secondary databases.
|
||||||
*
|
*
|
||||||
* <p>Logs but doesn't throw an exception in the event of a failure when writing to the secondary
|
* <p>Logs but doesn't throw an exception in the event of a failure when writing to Datastore.
|
||||||
* database.
|
|
||||||
*/
|
*/
|
||||||
public static PremiumList save(String name, List<String> inputData) {
|
public static PremiumList save(String name, List<String> inputData) {
|
||||||
PremiumList result;
|
PremiumList result = PremiumListSqlDao.save(name, inputData);
|
||||||
if (DatabaseMigrationUtils.isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
|
||||||
result = PremiumListDatastoreDao.save(name, inputData);
|
|
||||||
suppressExceptionUnlessInTest(
|
|
||||||
() -> PremiumListSqlDao.save(name, inputData), "Error when saving premium list to SQL.");
|
|
||||||
} else {
|
|
||||||
result = PremiumListSqlDao.save(name, inputData);
|
|
||||||
suppressExceptionUnlessInTest(
|
suppressExceptionUnlessInTest(
|
||||||
() -> PremiumListDatastoreDao.save(name, inputData),
|
() -> PremiumListDatastoreDao.save(name, inputData),
|
||||||
"Error when saving premium list to Datastore.");
|
"Error when saving premium list to Datastore.");
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the premium list.
|
* Deletes the premium list.
|
||||||
*
|
*
|
||||||
* <p>Logs but doesn't throw an exception in the event of a failure when deleting from the
|
* <p>Logs but doesn't throw an exception in the event of a failure when deleting from Datastore.
|
||||||
* secondary database.
|
|
||||||
*/
|
*/
|
||||||
public static void delete(PremiumList premiumList) {
|
public static void delete(PremiumList premiumList) {
|
||||||
if (DatabaseMigrationUtils.isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
|
||||||
PremiumListDatastoreDao.delete(premiumList);
|
|
||||||
suppressExceptionUnlessInTest(
|
|
||||||
() -> PremiumListSqlDao.delete(premiumList),
|
|
||||||
"Error when deleting premium list from SQL.");
|
|
||||||
} else {
|
|
||||||
PremiumListSqlDao.delete(premiumList);
|
PremiumListSqlDao.delete(premiumList);
|
||||||
suppressExceptionUnlessInTest(
|
suppressExceptionUnlessInTest(
|
||||||
() -> PremiumListDatastoreDao.delete(premiumList),
|
() -> PremiumListDatastoreDao.delete(premiumList),
|
||||||
"Error when deleting premium list from Datastore.");
|
"Error when deleting premium list from Datastore.");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether or not there exists a premium list with the given name. */
|
/** Returns whether or not there exists a premium list with the given name. */
|
||||||
public static boolean exists(String premiumListName) {
|
public static boolean exists(String premiumListName) {
|
||||||
// It may seem like overkill, but loading the list has ways been the way we check existence and
|
// It may seem like overkill, but loading the list has ways been the way we check existence and
|
||||||
// given that we usually load the list around the time we check existence, we'll hit the cache
|
// given that we usually load the list around the time we check existence, we'll hit the cache
|
||||||
if (DatabaseMigrationUtils.isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
return PremiumListSqlDao.getLatestRevision(premiumListName).isPresent();
|
||||||
return PremiumListDatastoreDao.getLatestRevision(premiumListName).isPresent();
|
|
||||||
} else {
|
|
||||||
return PremiumListSqlDao.getLatestRevision(premiumListName).isPresent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -175,9 +125,6 @@ public class PremiumListDualDao {
|
||||||
() ->
|
() ->
|
||||||
new IllegalArgumentException(
|
new IllegalArgumentException(
|
||||||
String.format("No premium list with name %s.", premiumListName)));
|
String.format("No premium list with name %s.", premiumListName)));
|
||||||
if (DatabaseMigrationUtils.isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
|
||||||
return PremiumListDatastoreDao.loadPremiumListEntriesUncached(premiumList);
|
|
||||||
} else {
|
|
||||||
CurrencyUnit currencyUnit = premiumList.getCurrency();
|
CurrencyUnit currencyUnit = premiumList.getCurrency();
|
||||||
return Streams.stream(PremiumListSqlDao.loadPremiumListEntriesUncached(premiumList))
|
return Streams.stream(PremiumListSqlDao.loadPremiumListEntriesUncached(premiumList))
|
||||||
.map(
|
.map(
|
||||||
|
@ -188,7 +135,6 @@ public class PremiumListDualDao {
|
||||||
.build())
|
.build())
|
||||||
.collect(toImmutableList());
|
.collect(toImmutableList());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private PremiumListDualDao() {}
|
private PremiumListDualDao() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,11 @@
|
||||||
package google.registry.model.registry.label;
|
package google.registry.model.registry.label;
|
||||||
|
|
||||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||||
import static google.registry.model.DatabaseMigrationUtils.isDatastore;
|
|
||||||
|
|
||||||
import com.google.common.collect.MapDifference;
|
import com.google.common.collect.MapDifference;
|
||||||
import com.google.common.collect.MapDifference.ValueDifference;
|
import com.google.common.collect.MapDifference.ValueDifference;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import google.registry.model.DatabaseMigrationUtils;
|
import google.registry.model.DatabaseMigrationUtils;
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
|
|
||||||
import google.registry.model.registry.label.ReservedList.ReservedListEntry;
|
import google.registry.model.registry.label.ReservedList.ReservedListEntry;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -38,32 +36,18 @@ public class ReservedListDualDatabaseDao {
|
||||||
|
|
||||||
/** Persist a new reserved list to the database. */
|
/** Persist a new reserved list to the database. */
|
||||||
public static void save(ReservedList reservedList) {
|
public static void save(ReservedList reservedList) {
|
||||||
if (isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
|
||||||
ReservedListDatastoreDao.save(reservedList);
|
|
||||||
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
|
|
||||||
() -> ReservedListSqlDao.save(reservedList),
|
|
||||||
"Error saving the reserved list to Cloud SQL.");
|
|
||||||
} else {
|
|
||||||
ReservedListSqlDao.save(reservedList);
|
ReservedListSqlDao.save(reservedList);
|
||||||
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
|
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
|
||||||
() -> ReservedListDatastoreDao.save(reservedList),
|
() -> ReservedListDatastoreDao.save(reservedList),
|
||||||
"Error saving the reserved list to Datastore.");
|
"Error saving the reserved list to Datastore.");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete a reserved list from both databases. */
|
/** Delete a reserved list from both databases. */
|
||||||
public static void delete(ReservedList reservedList) {
|
public static void delete(ReservedList reservedList) {
|
||||||
if (isDatastore(TransitionId.DOMAIN_LABEL_LISTS)) {
|
|
||||||
ReservedListDatastoreDao.delete(reservedList);
|
|
||||||
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
|
|
||||||
() -> ReservedListSqlDao.delete(reservedList),
|
|
||||||
"Error deleting the reserved list from Cloud SQL.");
|
|
||||||
} else {
|
|
||||||
ReservedListSqlDao.delete(reservedList);
|
ReservedListSqlDao.delete(reservedList);
|
||||||
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
|
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
|
||||||
() -> ReservedListDatastoreDao.delete(reservedList),
|
() -> ReservedListDatastoreDao.delete(reservedList),
|
||||||
"Error deleting the reserved list from Datastore.");
|
"Error deleting the reserved list from Datastore.");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,9 +56,7 @@ public class ReservedListDualDatabaseDao {
|
||||||
*/
|
*/
|
||||||
public static Optional<ReservedList> getLatestRevision(String reservedListName) {
|
public static Optional<ReservedList> getLatestRevision(String reservedListName) {
|
||||||
Optional<ReservedList> maybePrimaryList =
|
Optional<ReservedList> maybePrimaryList =
|
||||||
isDatastore(TransitionId.DOMAIN_LABEL_LISTS)
|
ReservedListSqlDao.getLatestRevision(reservedListName);
|
||||||
? ReservedListDatastoreDao.getLatestRevision(reservedListName)
|
|
||||||
: ReservedListSqlDao.getLatestRevision(reservedListName);
|
|
||||||
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
|
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
|
||||||
() -> maybePrimaryList.ifPresent(primaryList -> loadAndCompare(primaryList)),
|
() -> maybePrimaryList.ifPresent(primaryList -> loadAndCompare(primaryList)),
|
||||||
"Error comparing reserved lists.");
|
"Error comparing reserved lists.");
|
||||||
|
@ -83,14 +65,9 @@ public class ReservedListDualDatabaseDao {
|
||||||
|
|
||||||
private static void loadAndCompare(ReservedList primaryList) {
|
private static void loadAndCompare(ReservedList primaryList) {
|
||||||
Optional<ReservedList> maybeSecondaryList =
|
Optional<ReservedList> maybeSecondaryList =
|
||||||
isDatastore(TransitionId.DOMAIN_LABEL_LISTS)
|
ReservedListDatastoreDao.getLatestRevision(primaryList.getName());
|
||||||
? ReservedListSqlDao.getLatestRevision(primaryList.getName())
|
|
||||||
: ReservedListDatastoreDao.getLatestRevision(primaryList.getName());
|
|
||||||
if (!maybeSecondaryList.isPresent()) {
|
if (!maybeSecondaryList.isPresent()) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException("Reserved list in Datastore is empty.");
|
||||||
String.format(
|
|
||||||
"Reserved list in the secondary database (%s) is empty.",
|
|
||||||
isDatastore(TransitionId.DOMAIN_LABEL_LISTS) ? "Cloud SQL" : "Datastore"));
|
|
||||||
}
|
}
|
||||||
Map<String, ReservedListEntry> labelsToReservations =
|
Map<String, ReservedListEntry> labelsToReservations =
|
||||||
primaryList.reservedListMap.entrySet().parallelStream()
|
primaryList.reservedListMap.entrySet().parallelStream()
|
||||||
|
@ -110,12 +87,10 @@ public class ReservedListDualDatabaseDao {
|
||||||
if (diff.entriesDiffering().size() > 10) {
|
if (diff.entriesDiffering().size() > 10) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
String.format(
|
String.format(
|
||||||
"Unequal reserved lists detected, %s list with revision"
|
"Unequal reserved lists detected, Datastore list with revision"
|
||||||
+ " id %d has %d different records than the current"
|
+ " id %d has %d different records than the current"
|
||||||
+ " primary database list.",
|
+ " Cloud SQL list.",
|
||||||
isDatastore(TransitionId.DOMAIN_LABEL_LISTS) ? "Cloud SQL" : "Datastore",
|
secondaryList.getRevisionId(), diff.entriesDiffering().size()));
|
||||||
secondaryList.getRevisionId(),
|
|
||||||
diff.entriesDiffering().size()));
|
|
||||||
}
|
}
|
||||||
StringBuilder diffMessage = new StringBuilder("Unequal reserved lists detected:\n");
|
StringBuilder diffMessage = new StringBuilder("Unequal reserved lists detected:\n");
|
||||||
diff.entriesDiffering().entrySet().stream()
|
diff.entriesDiffering().entrySet().stream()
|
||||||
|
@ -125,12 +100,9 @@ public class ReservedListDualDatabaseDao {
|
||||||
ValueDifference<ReservedListEntry> valueDiff = entry.getValue();
|
ValueDifference<ReservedListEntry> valueDiff = entry.getValue();
|
||||||
diffMessage.append(
|
diffMessage.append(
|
||||||
String.format(
|
String.format(
|
||||||
"Domain label %s has entry %s in %s and entry"
|
"Domain label %s has entry %s in Cloud SQL and entry"
|
||||||
+ " %s in the secondary database.\n",
|
+ " %s in the Datastore.\n",
|
||||||
label,
|
label, valueDiff.leftValue(), valueDiff.rightValue()));
|
||||||
valueDiff.leftValue(),
|
|
||||||
isDatastore(TransitionId.DOMAIN_LABEL_LISTS) ? "Datastore" : "Cloud SQL",
|
|
||||||
valueDiff.rightValue()));
|
|
||||||
});
|
});
|
||||||
diff.entriesOnlyOnLeft().entrySet().stream()
|
diff.entriesOnlyOnLeft().entrySet().stream()
|
||||||
.forEach(
|
.forEach(
|
||||||
|
@ -138,9 +110,7 @@ public class ReservedListDualDatabaseDao {
|
||||||
String label = entry.getKey();
|
String label = entry.getKey();
|
||||||
diffMessage.append(
|
diffMessage.append(
|
||||||
String.format(
|
String.format(
|
||||||
"Domain label %s has entry in %s, but not in the secondary database.\n",
|
"Domain label %s has entry in Cloud SQL, but not in Datastore.\n", label));
|
||||||
label,
|
|
||||||
isDatastore(TransitionId.DOMAIN_LABEL_LISTS) ? "Datastore" : "Cloud SQL"));
|
|
||||||
});
|
});
|
||||||
diff.entriesOnlyOnRight().entrySet().stream()
|
diff.entriesOnlyOnRight().entrySet().stream()
|
||||||
.forEach(
|
.forEach(
|
||||||
|
@ -148,9 +118,7 @@ public class ReservedListDualDatabaseDao {
|
||||||
String label = entry.getKey();
|
String label = entry.getKey();
|
||||||
diffMessage.append(
|
diffMessage.append(
|
||||||
String.format(
|
String.format(
|
||||||
"Domain label %s has entry in %s, but not in the primary database.\n",
|
"Domain label %s has entry in Datastore, but not in Cloud SQL.\n", label));
|
||||||
label,
|
|
||||||
isDatastore(TransitionId.DOMAIN_LABEL_LISTS) ? "Cloud SQL" : "Datastore"));
|
|
||||||
});
|
});
|
||||||
throw new IllegalStateException(diffMessage.toString());
|
throw new IllegalStateException(diffMessage.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@ import java.nio.file.Files;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
/** Command to create a {@link ReservedList} on Datastore. */
|
/** Command to create a {@link ReservedList}. */
|
||||||
@Parameters(separators = " =", commandDescription = "Create a ReservedList in Datastore.")
|
@Parameters(separators = " =", commandDescription = "Create a ReservedList.")
|
||||||
final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand {
|
final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
|
@ -25,10 +25,10 @@ import google.registry.model.registry.label.PremiumListDualDao;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to delete a {@link PremiumList} in Datastore. This command will fail if the premium list
|
* Command to delete a {@link PremiumList}. This command will fail if the premium list is currently
|
||||||
* is currently in use on a tld.
|
* in use on a tld.
|
||||||
*/
|
*/
|
||||||
@Parameters(separators = " =", commandDescription = "Delete a PremiumList from Datastore.")
|
@Parameters(separators = " =", commandDescription = "Delete a PremiumList.")
|
||||||
final class DeletePremiumListCommand extends ConfirmingCommand implements CommandWithRemoteApi {
|
final class DeletePremiumListCommand extends ConfirmingCommand implements CommandWithRemoteApi {
|
||||||
|
|
||||||
@Nullable PremiumList premiumList;
|
@Nullable PremiumList premiumList;
|
||||||
|
@ -55,7 +55,7 @@ final class DeletePremiumListCommand extends ConfirmingCommand implements Comman
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String prompt() {
|
protected String prompt() {
|
||||||
return "You are about to delete the premium list: \n" + premiumList;
|
return "You are about to delete the premium list: \n" + premiumList.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -22,13 +22,11 @@ import com.google.common.base.Strings;
|
||||||
import com.googlecode.objectify.Key;
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.model.registry.label.ReservedList;
|
import google.registry.model.registry.label.ReservedList;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
import google.registry.util.SystemClock;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.joda.time.DateTime;
|
|
||||||
|
|
||||||
/** Command to safely update {@link ReservedList} on Datastore. */
|
/** Command to safely update {@link ReservedList}. */
|
||||||
@Parameters(separators = " =", commandDescription = "Update a ReservedList in Datastore.")
|
@Parameters(separators = " =", commandDescription = "Update a ReservedList.")
|
||||||
final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand {
|
final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -44,12 +42,10 @@ final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand
|
||||||
boolean shouldPublish =
|
boolean shouldPublish =
|
||||||
this.shouldPublish == null ? existingReservedList.getShouldPublish() : this.shouldPublish;
|
this.shouldPublish == null ? existingReservedList.getShouldPublish() : this.shouldPublish;
|
||||||
List<String> allLines = Files.readAllLines(input, UTF_8);
|
List<String> allLines = Files.readAllLines(input, UTF_8);
|
||||||
DateTime now = new SystemClock().nowUtc();
|
|
||||||
ReservedList.Builder updated =
|
ReservedList.Builder updated =
|
||||||
existingReservedList
|
existingReservedList
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.setReservedListMapFromLines(allLines)
|
.setReservedListMapFromLines(allLines)
|
||||||
.setLastUpdateTime(now)
|
|
||||||
.setShouldPublish(shouldPublish);
|
.setShouldPublish(shouldPublish);
|
||||||
reservedList = updated.build();
|
reservedList = updated.build();
|
||||||
// only call stageEntityChange if there are changes in entries
|
// only call stageEntityChange if there are changes in entries
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
package google.registry.tools.server;
|
package google.registry.tools.server;
|
||||||
|
|
||||||
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
|
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
|
||||||
import static google.registry.request.Action.Method.GET;
|
import static google.registry.request.Action.Method.GET;
|
||||||
import static google.registry.request.Action.Method.POST;
|
import static google.registry.request.Action.Method.POST;
|
||||||
|
|
||||||
|
@ -51,14 +50,15 @@ public final class ListPremiumListsAction extends ListObjectsAction<PremiumList>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableSet<PremiumList> loadObjects() {
|
public ImmutableSet<PremiumList> loadObjects() {
|
||||||
return transactIfJpaTm(
|
return jpaTm()
|
||||||
() ->
|
.transact(
|
||||||
tm().loadAllOf(PremiumList.class).stream()
|
() ->
|
||||||
.map(PremiumList::getName)
|
jpaTm().loadAllOf(PremiumList.class).stream()
|
||||||
.map(PremiumListDualDao::getLatestRevision)
|
.map(PremiumList::getName)
|
||||||
.filter(Optional::isPresent)
|
.map(PremiumListDualDao::getLatestRevision)
|
||||||
.map(Optional::get)
|
.filter(Optional::isPresent)
|
||||||
.peek(list -> Hibernate.initialize(list.getLabelsToPrices()))
|
.map(Optional::get)
|
||||||
.collect(toImmutableSortedSet(Comparator.comparing(PremiumList::getName))));
|
.peek(list -> Hibernate.initialize(list.getLabelsToPrices()))
|
||||||
|
.collect(toImmutableSortedSet(Comparator.comparing(PremiumList::getName))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,40 +15,27 @@
|
||||||
package google.registry.model.registry.label;
|
package google.registry.model.registry.label;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
|
||||||
import static google.registry.testing.DatabaseHelper.createTld;
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
import static google.registry.testing.DatabaseHelper.newRegistry;
|
import static google.registry.testing.DatabaseHelper.newRegistry;
|
||||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
|
||||||
import static org.joda.time.Duration.standardDays;
|
import static org.joda.time.Duration.standardDays;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
|
||||||
import com.google.common.truth.Truth8;
|
import com.google.common.truth.Truth8;
|
||||||
import google.registry.dns.writer.VoidDnsWriter;
|
import google.registry.dns.writer.VoidDnsWriter;
|
||||||
import google.registry.model.EntityTestCase;
|
import google.registry.model.EntityTestCase;
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule;
|
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
|
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabaseTransition;
|
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
|
|
||||||
import google.registry.model.common.TimedTransitionProperty;
|
|
||||||
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
import google.registry.schema.tld.PremiumListSqlDao;
|
|
||||||
import google.registry.testing.DualDatabaseTest;
|
|
||||||
import google.registry.testing.TestCacheExtension;
|
import google.registry.testing.TestCacheExtension;
|
||||||
import google.registry.testing.TestOfyAndSql;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
import org.joda.time.Duration;
|
import org.joda.time.Duration;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
/** Unit tests for {@link PremiumListDualDao}. */
|
/** Unit tests for {@link PremiumListDualDao}. */
|
||||||
@DualDatabaseTest
|
|
||||||
public class PremiumListDualDaoTest extends EntityTestCase {
|
public class PremiumListDualDaoTest extends EntityTestCase {
|
||||||
|
|
||||||
// Set long persist times on caches so they can be tested (cache times default to 0 in tests).
|
// Set long persist times on caches so they can be tested (cache times default to 0 in tests).
|
||||||
|
@ -63,19 +50,6 @@ public class PremiumListDualDaoTest extends EntityTestCase {
|
||||||
void before() {
|
void before() {
|
||||||
createTld("tld");
|
createTld("tld");
|
||||||
fakeClock.setAutoIncrementStep(Duration.millis(1));
|
fakeClock.setAutoIncrementStep(Duration.millis(1));
|
||||||
fakeClock.setTo(DateTime.parse("1984-12-21T00:00:00.000Z"));
|
|
||||||
DatabaseTransitionSchedule schedule =
|
|
||||||
DatabaseTransitionSchedule.create(
|
|
||||||
TransitionId.DOMAIN_LABEL_LISTS,
|
|
||||||
TimedTransitionProperty.fromValueMap(
|
|
||||||
ImmutableSortedMap.of(
|
|
||||||
START_OF_TIME,
|
|
||||||
PrimaryDatabase.DATASTORE,
|
|
||||||
fakeClock.nowUtc().plusDays(1),
|
|
||||||
PrimaryDatabase.CLOUD_SQL),
|
|
||||||
PrimaryDatabaseTransition.class));
|
|
||||||
|
|
||||||
tm().transactNew(() -> ofyTm().putWithoutBackup(schedule));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
|
@ -83,22 +57,8 @@ public class PremiumListDualDaoTest extends EntityTestCase {
|
||||||
fakeClock.setAutoIncrementStep(Duration.ZERO);
|
fakeClock.setAutoIncrementStep(Duration.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testGetPremiumPrice_secondaryLoadMissingSql() {
|
|
||||||
PremiumListSqlDao.delete(PremiumListSqlDao.getLatestRevision("tld").get());
|
|
||||||
assertThat(
|
|
||||||
assertThrows(
|
|
||||||
IllegalStateException.class,
|
|
||||||
() -> PremiumListDualDao.getPremiumPrice("brass", Registry.get("tld"))))
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Unequal prices for domain brass.tld from primary Datastore DB "
|
|
||||||
+ "(Optional[USD 20.00]) and secondary SQL db (Optional.empty).");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testGetPremiumPrice_secondaryLoadMissingOfy() {
|
void testGetPremiumPrice_secondaryLoadMissingOfy() {
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
PremiumList premiumList = PremiumListDatastoreDao.getLatestRevision("tld").get();
|
PremiumList premiumList = PremiumListDatastoreDao.getLatestRevision("tld").get();
|
||||||
PremiumListDatastoreDao.delete(premiumList);
|
PremiumListDatastoreDao.delete(premiumList);
|
||||||
assertThat(
|
assertThat(
|
||||||
|
@ -111,22 +71,8 @@ public class PremiumListDualDaoTest extends EntityTestCase {
|
||||||
+ "and secondary Datastore db (Optional.empty).");
|
+ "and secondary Datastore db (Optional.empty).");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testGetPremiumPrice_secondaryDifferentSql() {
|
|
||||||
PremiumListSqlDao.save("tld", ImmutableList.of("brass,USD 50"));
|
|
||||||
assertThat(
|
|
||||||
assertThrows(
|
|
||||||
IllegalStateException.class,
|
|
||||||
() -> PremiumListDualDao.getPremiumPrice("brass", Registry.get("tld"))))
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Unequal prices for domain brass.tld from primary Datastore DB "
|
|
||||||
+ "(Optional[USD 20.00]) and secondary SQL db (Optional[USD 50.00]).");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testGetPremiumPrice_secondaryDifferentOfy() {
|
void testGetPremiumPrice_secondaryDifferentOfy() {
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
PremiumListDatastoreDao.save("tld", ImmutableList.of("brass,USD 50"));
|
PremiumListDatastoreDao.save("tld", ImmutableList.of("brass,USD 50"));
|
||||||
assertThat(
|
assertThat(
|
||||||
assertThrows(
|
assertThrows(
|
||||||
|
@ -138,7 +84,7 @@ public class PremiumListDualDaoTest extends EntityTestCase {
|
||||||
+ "(Optional[USD 20.00]) and secondary Datastore db (Optional[USD 50.00]).");
|
+ "(Optional[USD 20.00]) and secondary Datastore db (Optional[USD 50.00]).");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testGetPremiumPrice_returnsNoPriceWhenNoPremiumListConfigured() {
|
void testGetPremiumPrice_returnsNoPriceWhenNoPremiumListConfigured() {
|
||||||
createTld("ghost");
|
createTld("ghost");
|
||||||
persistResource(
|
persistResource(
|
||||||
|
@ -151,14 +97,14 @@ public class PremiumListDualDaoTest extends EntityTestCase {
|
||||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("blah", Registry.get("ghost"))).isEmpty();
|
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("blah", Registry.get("ghost"))).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testGetPremiumPrice_emptyWhenPremiumListDeleted() {
|
void testGetPremiumPrice_emptyWhenPremiumListDeleted() {
|
||||||
PremiumList toDelete = PremiumListDualDao.getLatestRevision("tld").get();
|
PremiumList toDelete = PremiumListDualDao.getLatestRevision("tld").get();
|
||||||
PremiumListDualDao.delete(toDelete);
|
PremiumListDualDao.delete(toDelete);
|
||||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("blah", Registry.get("tld"))).isEmpty();
|
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("blah", Registry.get("tld"))).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void getPremiumPrice_returnsNoneWhenNoPremiumListConfigured() {
|
void getPremiumPrice_returnsNoneWhenNoPremiumListConfigured() {
|
||||||
persistResource(newRegistry("foobar", "FOOBAR").asBuilder().setPremiumList(null).build());
|
persistResource(newRegistry("foobar", "FOOBAR").asBuilder().setPremiumList(null).build());
|
||||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("rich", Registry.get("foobar"))).isEmpty();
|
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("rich", Registry.get("foobar"))).isEmpty();
|
||||||
|
|
|
@ -16,7 +16,6 @@ package google.registry.model.registry.label;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
import static com.google.common.truth.Truth.assertWithMessage;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
||||||
import static google.registry.testing.DatabaseHelper.createTld;
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||||
import static google.registry.testing.DatabaseHelper.persistReservedList;
|
import static google.registry.testing.DatabaseHelper.persistReservedList;
|
||||||
|
@ -24,9 +23,9 @@ import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.hash.BloomFilter;
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||||
import google.registry.model.registry.label.PremiumList.PremiumListRevision;
|
|
||||||
import google.registry.testing.AppEngineExtension;
|
import google.registry.testing.AppEngineExtension;
|
||||||
import org.joda.money.Money;
|
import org.joda.money.Money;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -68,13 +67,13 @@ public class PremiumListTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProbablePremiumLabels() {
|
void testBloomFilter() {
|
||||||
PremiumList pl = PremiumListDualDao.getLatestRevision("tld").get();
|
PremiumList pl = PremiumListDualDao.getLatestRevision("tld").get();
|
||||||
PremiumListRevision revision = ofy().load().key(pl.getRevisionKey()).now();
|
BloomFilter<String> bloomFilter = pl.getBloomFilter();
|
||||||
assertThat(revision.getProbablePremiumLabels().mightContain("notpremium")).isFalse();
|
assertThat(bloomFilter.mightContain("notpremium")).isFalse();
|
||||||
for (String label : ImmutableList.of("rich", "lol", "johnny-be-goode", "icann")) {
|
for (String label : ImmutableList.of("rich", "lol", "johnny-be-goode", "icann")) {
|
||||||
assertWithMessage(label + " should be a probable premium")
|
assertWithMessage(label + " should be a probable premium")
|
||||||
.that(revision.getProbablePremiumLabels().mightContain(label))
|
.that(bloomFilter.mightContain(label))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,36 +15,17 @@
|
||||||
package google.registry.model.registry.label;
|
package google.registry.model.registry.label;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
|
||||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
|
||||||
import google.registry.config.RegistryEnvironment;
|
|
||||||
import google.registry.model.EntityTestCase;
|
import google.registry.model.EntityTestCase;
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule;
|
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
|
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabaseTransition;
|
|
||||||
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
|
|
||||||
import google.registry.model.common.TimedTransitionProperty;
|
|
||||||
import google.registry.model.registry.label.ReservedList.ReservedListEntry;
|
import google.registry.model.registry.label.ReservedList.ReservedListEntry;
|
||||||
import google.registry.testing.DualDatabaseTest;
|
|
||||||
import google.registry.testing.SystemPropertyExtension;
|
|
||||||
import google.registry.testing.TestOfyAndSql;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.joda.time.DateTime;
|
|
||||||
import org.joda.time.Duration;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@DualDatabaseTest
|
|
||||||
public class ReservedListDualDatabaseDaoTest extends EntityTestCase {
|
public class ReservedListDualDatabaseDaoTest extends EntityTestCase {
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension();
|
|
||||||
|
|
||||||
private ImmutableMap<String, ReservedListEntry> reservations;
|
private ImmutableMap<String, ReservedListEntry> reservations;
|
||||||
|
|
||||||
private ReservedList reservedList;
|
private ReservedList reservedList;
|
||||||
|
@ -65,41 +46,18 @@ public class ReservedListDualDatabaseDaoTest extends EntityTestCase {
|
||||||
.setShouldPublish(false)
|
.setShouldPublish(false)
|
||||||
.setReservedListMap(reservations)
|
.setReservedListMap(reservations)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
fakeClock.setTo(DateTime.parse("1984-12-21T00:00:00.000Z"));
|
|
||||||
DatabaseTransitionSchedule schedule =
|
|
||||||
DatabaseTransitionSchedule.create(
|
|
||||||
TransitionId.DOMAIN_LABEL_LISTS,
|
|
||||||
TimedTransitionProperty.fromValueMap(
|
|
||||||
ImmutableSortedMap.of(
|
|
||||||
START_OF_TIME,
|
|
||||||
PrimaryDatabase.DATASTORE,
|
|
||||||
fakeClock.nowUtc().plusDays(1),
|
|
||||||
PrimaryDatabase.CLOUD_SQL),
|
|
||||||
PrimaryDatabaseTransition.class));
|
|
||||||
|
|
||||||
tm().transactNew(() -> ofy().saveWithoutBackup().entity(schedule).now());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testSave_datastorePrimary_success() {
|
void testSave_success() {
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
ReservedListDualDatabaseDao.save(reservedList);
|
||||||
Optional<ReservedList> savedList =
|
Optional<ReservedList> savedList =
|
||||||
ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName());
|
ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName());
|
||||||
assertThat(savedList.get()).isEqualTo(reservedList);
|
assertThat(savedList.get()).isEqualTo(reservedList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testSave_cloudSqlPrimary_success() {
|
void testDelete_success() {
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
|
||||||
Optional<ReservedList> savedList =
|
|
||||||
ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName());
|
|
||||||
assertThat(savedList.get()).isEqualTo(reservedList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testDelete_datastorePrimary_success() {
|
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
ReservedListDualDatabaseDao.save(reservedList);
|
||||||
assertThat(ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()).isPresent())
|
assertThat(ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()).isPresent())
|
||||||
.isTrue();
|
.isTrue();
|
||||||
|
@ -108,19 +66,8 @@ public class ReservedListDualDatabaseDaoTest extends EntityTestCase {
|
||||||
.isFalse();
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testDelete_cloudSqlPrimary_success() {
|
void testSaveAndLoad_emptyList() {
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
|
||||||
assertThat(ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()).isPresent())
|
|
||||||
.isTrue();
|
|
||||||
ReservedListDualDatabaseDao.delete(reservedList);
|
|
||||||
assertThat(ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()).isPresent())
|
|
||||||
.isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testSaveAndLoad_datastorePrimary_emptyList() {
|
|
||||||
ReservedList list =
|
ReservedList list =
|
||||||
new ReservedList.Builder()
|
new ReservedList.Builder()
|
||||||
.setName("empty")
|
.setName("empty")
|
||||||
|
@ -132,22 +79,8 @@ public class ReservedListDualDatabaseDaoTest extends EntityTestCase {
|
||||||
assertThat(savedList.get()).isEqualTo(list);
|
assertThat(savedList.get()).isEqualTo(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testSaveAndLoad_cloudSqlPrimary_emptyList() {
|
void testSave_multipleVersions() {
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
ReservedList list =
|
|
||||||
new ReservedList.Builder()
|
|
||||||
.setName("empty")
|
|
||||||
.setLastUpdateTime(fakeClock.nowUtc())
|
|
||||||
.setReservedListMap(ImmutableMap.of())
|
|
||||||
.build();
|
|
||||||
ReservedListDualDatabaseDao.save(list);
|
|
||||||
Optional<ReservedList> savedList = ReservedListDualDatabaseDao.getLatestRevision("empty");
|
|
||||||
assertThat(savedList.get()).isEqualTo(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testSave_datastorePrimary_multipleVersions() {
|
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
ReservedListDualDatabaseDao.save(reservedList);
|
||||||
assertThat(
|
assertThat(
|
||||||
ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName())
|
ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName())
|
||||||
|
@ -173,36 +106,8 @@ public class ReservedListDualDatabaseDaoTest extends EntityTestCase {
|
||||||
.isEqualTo(newReservations);
|
.isEqualTo(newReservations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testSave_cloudSqlPrimary_multipleVersions() {
|
void testLoad_unequalLists() {
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
|
||||||
assertThat(
|
|
||||||
ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName())
|
|
||||||
.get()
|
|
||||||
.getReservedListEntries())
|
|
||||||
.isEqualTo(reservations);
|
|
||||||
ImmutableMap<String, ReservedListEntry> newReservations =
|
|
||||||
ImmutableMap.of(
|
|
||||||
"food",
|
|
||||||
ReservedListEntry.create("food", ReservationType.RESERVED_FOR_SPECIFIC_USE, null));
|
|
||||||
ReservedList secondList =
|
|
||||||
new ReservedList.Builder()
|
|
||||||
.setName("testlist2")
|
|
||||||
.setLastUpdateTime(fakeClock.nowUtc())
|
|
||||||
.setShouldPublish(false)
|
|
||||||
.setReservedListMap(newReservations)
|
|
||||||
.build();
|
|
||||||
ReservedListDualDatabaseDao.save(secondList);
|
|
||||||
assertThat(
|
|
||||||
ReservedListDualDatabaseDao.getLatestRevision(secondList.getName())
|
|
||||||
.get()
|
|
||||||
.getReservedListEntries())
|
|
||||||
.isEqualTo(newReservations);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testLoad_datastorePrimary_unequalLists() {
|
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
ReservedListDualDatabaseDao.save(reservedList);
|
||||||
ReservedList secondList =
|
ReservedList secondList =
|
||||||
new ReservedList.Builder()
|
new ReservedList.Builder()
|
||||||
|
@ -222,78 +127,16 @@ public class ReservedListDualDatabaseDaoTest extends EntityTestCase {
|
||||||
() -> ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()));
|
() -> ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()));
|
||||||
assertThat(thrown)
|
assertThat(thrown)
|
||||||
.hasMessageThat()
|
.hasMessageThat()
|
||||||
.contains("Domain label music has entry in Datastore, but not in the secondary database.");
|
.contains("Domain label music has entry in Datastore, but not in Cloud SQL.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testLoad_cloudSqlPrimary_unequalLists() {
|
void testLoad_noListInDatastore() {
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
|
||||||
ReservedList secondList =
|
|
||||||
new ReservedList.Builder()
|
|
||||||
.setName(reservedList.name)
|
|
||||||
.setLastUpdateTime(fakeClock.nowUtc())
|
|
||||||
.setShouldPublish(false)
|
|
||||||
.setReservedListMap(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"food",
|
|
||||||
ReservedListEntry.create(
|
|
||||||
"food", ReservationType.RESERVED_FOR_SPECIFIC_USE, null)))
|
|
||||||
.build();
|
|
||||||
ReservedListSqlDao.save(secondList);
|
|
||||||
IllegalStateException thrown =
|
|
||||||
assertThrows(
|
|
||||||
IllegalStateException.class,
|
|
||||||
() -> ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()));
|
|
||||||
assertThat(thrown)
|
|
||||||
.hasMessageThat()
|
|
||||||
.contains("Domain label music has entry in Datastore, but not in the primary database.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testLoad_cloudSqlPrimary_unequalLists_succeedsInProduction() {
|
|
||||||
RegistryEnvironment.PRODUCTION.setup(systemPropertyExtension);
|
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
ReservedListDualDatabaseDao.save(reservedList);
|
|
||||||
ReservedList secondList =
|
|
||||||
new ReservedList.Builder()
|
|
||||||
.setName(reservedList.name)
|
|
||||||
.setLastUpdateTime(fakeClock.nowUtc())
|
|
||||||
.setShouldPublish(false)
|
|
||||||
.setReservedListMap(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"food",
|
|
||||||
ReservedListEntry.create(
|
|
||||||
"food", ReservationType.RESERVED_FOR_SPECIFIC_USE, null)))
|
|
||||||
.build();
|
|
||||||
ReservedListSqlDao.save(secondList);
|
|
||||||
Optional<ReservedList> savedList =
|
|
||||||
ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName());
|
|
||||||
assertThat(savedList.get()).isEqualTo(secondList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testLoad_DatastorePrimary_noListInCloudSql() {
|
|
||||||
ReservedListDatastoreDao.save(reservedList);
|
|
||||||
IllegalStateException thrown =
|
|
||||||
assertThrows(
|
|
||||||
IllegalStateException.class,
|
|
||||||
() -> ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()));
|
|
||||||
assertThat(thrown)
|
|
||||||
.hasMessageThat()
|
|
||||||
.contains("Reserved list in the secondary database (Cloud SQL) is empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testLoad_cloudSqlPrimary_noListInDatastore() {
|
|
||||||
fakeClock.advanceBy(Duration.standardDays(5));
|
|
||||||
ReservedListSqlDao.save(reservedList);
|
ReservedListSqlDao.save(reservedList);
|
||||||
IllegalStateException thrown =
|
IllegalStateException thrown =
|
||||||
assertThrows(
|
assertThrows(
|
||||||
IllegalStateException.class,
|
IllegalStateException.class,
|
||||||
() -> ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()));
|
() -> ReservedListDualDatabaseDao.getLatestRevision(reservedList.getName()));
|
||||||
assertThat(thrown)
|
assertThat(thrown).hasMessageThat().contains("Reserved list in Datastore is empty.");
|
||||||
.hasMessageThat()
|
|
||||||
.contains("Reserved list in the secondary database (Datastore) is empty.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,15 +21,10 @@ import static google.registry.testing.DatabaseHelper.createTlds;
|
||||||
import static google.registry.testing.DatabaseHelper.persistReservedList;
|
import static google.registry.testing.DatabaseHelper.persistReservedList;
|
||||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
import static google.registry.tools.CreateReservedListCommand.INVALID_FORMAT_ERROR_MESSAGE;
|
import static google.registry.tools.CreateReservedListCommand.INVALID_FORMAT_ERROR_MESSAGE;
|
||||||
import static org.joda.time.DateTimeZone.UTC;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
import google.registry.model.registry.label.ReservedList;
|
import google.registry.model.registry.label.ReservedList;
|
||||||
import google.registry.model.registry.label.ReservedList.ReservedListEntry;
|
|
||||||
import google.registry.model.registry.label.ReservedListSqlDao;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -58,16 +53,6 @@ class CreateReservedListCommandTest
|
||||||
assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isPresent();
|
assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSuccess_timestampsSetCorrectly() throws Exception {
|
|
||||||
DateTime before = DateTime.now(UTC);
|
|
||||||
runCommandForced("--input=" + reservedTermsPath);
|
|
||||||
assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isPresent();
|
|
||||||
ReservedList rl = ReservedList.get("xn--q9jyb4c_common-reserved").get();
|
|
||||||
assertThat(rl.getCreationTime()).isAtLeast(before);
|
|
||||||
assertThat(rl.getLastUpdateTime()).isEqualTo(rl.getCreationTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_shouldPublishDefaultsToTrue() throws Exception {
|
void testSuccess_shouldPublishDefaultsToTrue() throws Exception {
|
||||||
runCommandForced("--input=" + reservedTermsPath);
|
runCommandForced("--input=" + reservedTermsPath);
|
||||||
|
@ -187,21 +172,6 @@ class CreateReservedListCommandTest
|
||||||
verifyXnq9jyb4cInCloudSql();
|
verifyXnq9jyb4cInCloudSql();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
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.
|
|
||||||
ReservedListSqlDao.save(
|
|
||||||
createCloudSqlReservedList(
|
|
||||||
"xn--q9jyb4c_common-reserved",
|
|
||||||
fakeClock.nowUtc(),
|
|
||||||
true,
|
|
||||||
ImmutableMap.of(
|
|
||||||
"testdomain", ReservedListEntry.create("testdomain", FULLY_BLOCKED, ""))));
|
|
||||||
runCommandForced("--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath);
|
|
||||||
verifyXnq9jyb4cInDatastore();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runNameTestExpectedFailure(String name, String expectedErrorMsg) {
|
private void runNameTestExpectedFailure(String name, String expectedErrorMsg) {
|
||||||
IllegalArgumentException thrown =
|
IllegalArgumentException thrown =
|
||||||
assertThrows(
|
assertThrows(
|
||||||
|
|
|
@ -16,7 +16,6 @@ package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.truth.Truth8.assertThat;
|
import static com.google.common.truth.Truth8.assertThat;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
||||||
import static google.registry.testing.DatabaseHelper.createTld;
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
import static google.registry.testing.DatabaseHelper.loadPremiumListEntries;
|
import static google.registry.testing.DatabaseHelper.loadPremiumListEntries;
|
||||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||||
|
@ -25,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
import google.registry.model.registry.label.PremiumList;
|
import google.registry.model.registry.label.PremiumList;
|
||||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
|
||||||
import google.registry.model.registry.label.PremiumListDualDao;
|
import google.registry.model.registry.label.PremiumListDualDao;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -38,13 +36,6 @@ class DeletePremiumListCommandTest extends CommandTestCase<DeletePremiumListComm
|
||||||
assertThat(loadPremiumListEntries(premiumList)).hasSize(1);
|
assertThat(loadPremiumListEntries(premiumList)).hasSize(1);
|
||||||
runCommand("--force", "--name=xn--q9jyb4c");
|
runCommand("--force", "--name=xn--q9jyb4c");
|
||||||
assertThat(PremiumListDualDao.getLatestRevision("xn--q9jyb4c")).isEmpty();
|
assertThat(PremiumListDualDao.getLatestRevision("xn--q9jyb4c")).isEmpty();
|
||||||
|
|
||||||
// Ensure that the Datastore premium list entry entities were deleted correctly.
|
|
||||||
assertThat(ofy().load()
|
|
||||||
.type(PremiumListEntry.class)
|
|
||||||
.ancestor(premiumList.getRevisionKey())
|
|
||||||
.keys())
|
|
||||||
.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -74,16 +74,6 @@ class UpdateReservedListCommandTest
|
||||||
runSuccessfulUpdateTest("--input=" + reservedTermsPath);
|
runSuccessfulUpdateTest("--input=" + reservedTermsPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSuccess_lastUpdateTime_updatedCorrectly() throws Exception {
|
|
||||||
ReservedList original = ReservedList.get("xn--q9jyb4c_common-reserved").get();
|
|
||||||
runCommandForced("--input=" + reservedTermsPath);
|
|
||||||
ReservedList updated = ReservedList.get("xn--q9jyb4c_common-reserved").get();
|
|
||||||
assertThat(updated.getLastUpdateTime()).isGreaterThan(original.getLastUpdateTime());
|
|
||||||
assertThat(updated.getCreationTime()).isEqualTo(original.getCreationTime());
|
|
||||||
assertThat(updated.getLastUpdateTime()).isGreaterThan(updated.getCreationTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_shouldPublish_setToFalseCorrectly() throws Exception {
|
void testSuccess_shouldPublish_setToFalseCorrectly() throws Exception {
|
||||||
runSuccessfulUpdateTest("--input=" + reservedTermsPath, "--should_publish=false");
|
runSuccessfulUpdateTest("--input=" + reservedTermsPath, "--should_publish=false");
|
||||||
|
|
|
@ -16,15 +16,11 @@ package google.registry.tools.server;
|
||||||
|
|
||||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||||
|
|
||||||
import google.registry.testing.DualDatabaseTest;
|
|
||||||
import google.registry.testing.TestOfyAndSql;
|
|
||||||
import google.registry.testing.TestOfyOnly;
|
|
||||||
import google.registry.testing.TestSqlOnly;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/** Unit tests for {@link ListPremiumListsAction}. */
|
/** Unit tests for {@link ListPremiumListsAction}. */
|
||||||
@DualDatabaseTest
|
|
||||||
class ListPremiumListsActionTest extends ListActionTestCase {
|
class ListPremiumListsActionTest extends ListActionTestCase {
|
||||||
|
|
||||||
private ListPremiumListsAction action;
|
private ListPremiumListsAction action;
|
||||||
|
@ -36,7 +32,7 @@ class ListPremiumListsActionTest extends ListActionTestCase {
|
||||||
action = new ListPremiumListsAction();
|
action = new ListPremiumListsAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyAndSql
|
@Test
|
||||||
void testRun_noParameters() {
|
void testRun_noParameters() {
|
||||||
testRunSuccess(
|
testRunSuccess(
|
||||||
action,
|
action,
|
||||||
|
@ -47,20 +43,7 @@ class ListPremiumListsActionTest extends ListActionTestCase {
|
||||||
"^xn--q9jyb4c$");
|
"^xn--q9jyb4c$");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyOnly // only ofy has revisionKey
|
@Test
|
||||||
void testRun_withParameters() {
|
|
||||||
testRunSuccess(
|
|
||||||
action,
|
|
||||||
Optional.of("revisionKey"),
|
|
||||||
Optional.empty(),
|
|
||||||
Optional.empty(),
|
|
||||||
"^name\\s+revisionKey\\s*$",
|
|
||||||
"^-+\\s+-+\\s*$",
|
|
||||||
"^how\\s+.*PremiumList.*$",
|
|
||||||
"^xn--q9jyb4c\\s+.*PremiumList.*$");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestSqlOnly
|
|
||||||
void testRun_withLabelsToPrices() {
|
void testRun_withLabelsToPrices() {
|
||||||
testRunSuccess(
|
testRunSuccess(
|
||||||
action,
|
action,
|
||||||
|
@ -69,24 +52,11 @@ class ListPremiumListsActionTest extends ListActionTestCase {
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
"^name\\s+labelsToPrices\\s*$",
|
"^name\\s+labelsToPrices\\s*$",
|
||||||
"^-+\\s+-+\\s*$",
|
"^-+\\s+-+\\s*$",
|
||||||
"^how\\s+\\{richer=5000\\}\\s+$",
|
"^how\\s+\\{richer=5000.00\\}$",
|
||||||
"^xn--q9jyb4c\\s+\\{rich=100\\.00\\}\\s+$");
|
"^xn--q9jyb4c\\s+\\{rich=100\\.00\\}\\s+$");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOfyOnly
|
@Test
|
||||||
void testRun_withWildcard() {
|
|
||||||
testRunSuccess(
|
|
||||||
action,
|
|
||||||
Optional.of("*"),
|
|
||||||
Optional.empty(),
|
|
||||||
Optional.empty(),
|
|
||||||
"^name\\s+.*revisionKey",
|
|
||||||
"^-+\\s+-+.*",
|
|
||||||
"^how\\s+.*PremiumList",
|
|
||||||
"^xn--q9jyb4c\\s+.*PremiumList");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestOfyAndSql
|
|
||||||
void testRun_withBadField_returnsError() {
|
void testRun_withBadField_returnsError() {
|
||||||
testRunError(
|
testRunError(
|
||||||
action,
|
action,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue