mirror of
https://github.com/google/nomulus.git
synced 2025-08-12 12:39:39 +02:00
Change premium list command to be based off of mutating command (#1123)
* Change premium list command to be based off of mutating command * Modify test cases and add comments for better readability * Fix typo
This commit is contained in:
parent
7b03d28ab1
commit
0f4bf7ac38
6 changed files with 401 additions and 259 deletions
|
@ -14,39 +14,28 @@
|
||||||
|
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
|
||||||
import static google.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
|
|
||||||
import static google.registry.tools.server.CreateOrUpdatePremiumListAction.INPUT_PARAM;
|
|
||||||
import static google.registry.tools.server.CreateOrUpdatePremiumListAction.NAME_PARAM;
|
|
||||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
|
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.flogger.FluentLogger;
|
||||||
import com.google.common.base.Verify;
|
import google.registry.schema.tld.PremiumListSqlDao;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.net.MediaType;
|
|
||||||
import google.registry.model.registry.label.PremiumList;
|
|
||||||
import google.registry.tools.params.PathParameter;
|
import google.registry.tools.params.PathParameter;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.json.simple.JSONValue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for specification of command line parameters common to creating and updating premium
|
* Base class for specification of command line parameters common to creating and updating premium
|
||||||
* lists.
|
* lists.
|
||||||
*/
|
*/
|
||||||
abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand
|
abstract class CreateOrUpdatePremiumListCommand extends MutatingCommand {
|
||||||
implements CommandWithConnection, CommandWithRemoteApi {
|
|
||||||
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
protected List<String> inputData;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Parameter(
|
@Parameter(
|
||||||
names = {"-n", "--name"},
|
names = {"-n", "--name"},
|
||||||
description = "The name of this premium list (defaults to filename if not specified). "
|
description =
|
||||||
|
"The name of this premium list (defaults to filename if not specified). "
|
||||||
+ "This is almost always the name of the TLD this premium list will be used on.")
|
+ "This is almost always the name of the TLD this premium list will be used on.")
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
|
@ -57,78 +46,17 @@ abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand
|
||||||
required = true)
|
required = true)
|
||||||
Path inputFile;
|
Path inputFile;
|
||||||
|
|
||||||
protected AppEngineConnection connection;
|
|
||||||
protected int inputLineCount;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setConnection(AppEngineConnection connection) {
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract String getCommandPath();
|
|
||||||
|
|
||||||
ImmutableMap<String, String> getParameterMap() {
|
|
||||||
return ImmutableMap.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init() throws Exception {
|
|
||||||
name = isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
|
||||||
List<String> lines = Files.readAllLines(inputFile, UTF_8);
|
|
||||||
// Try constructing and parsing the premium list locally to check up front for validation errors
|
|
||||||
new PremiumList.Builder().setName(name).build().parse(lines);
|
|
||||||
inputLineCount = lines.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String prompt() {
|
|
||||||
return String.format(
|
|
||||||
"You are about to save the premium list %s with %d items: ", name, inputLineCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute() throws Exception {
|
public String execute() throws Exception {
|
||||||
ImmutableMap.Builder<String, String> params = new ImmutableMap.Builder<>();
|
String message = String.format("Saved premium list %s with %d entries", name, inputData.size());
|
||||||
params.put(NAME_PARAM, name);
|
try {
|
||||||
String inputFileContents = new String(Files.readAllBytes(inputFile), UTF_8);
|
logger.atInfo().log("Saving premium list for TLD %s", name);
|
||||||
String requestBody =
|
PremiumListSqlDao.save(name, inputData);
|
||||||
Joiner.on('&').withKeyValueSeparator("=").join(
|
logger.atInfo().log(message);
|
||||||
ImmutableMap.of(INPUT_PARAM, URLEncoder.encode(inputFileContents, UTF_8.toString())));
|
} catch (Throwable e) {
|
||||||
|
message = "Unexpected error saving premium list from nomulus tool command";
|
||||||
ImmutableMap<String, String> extraParams = getParameterMap();
|
logger.atSevere().withCause(e).log(message);
|
||||||
if (extraParams != null) {
|
|
||||||
params.putAll(extraParams);
|
|
||||||
}
|
}
|
||||||
|
return message;
|
||||||
// Call the server and get the response data
|
|
||||||
String response =
|
|
||||||
connection.sendPostRequest(
|
|
||||||
getCommandPath(), params.build(), MediaType.FORM_DATA, requestBody.getBytes(UTF_8));
|
|
||||||
|
|
||||||
return extractServerResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(user): refactor this behavior into a better general-purpose
|
|
||||||
// response validation that can be re-used across the new client/server commands.
|
|
||||||
private String extractServerResponse(String response) {
|
|
||||||
Map<String, Object> responseMap = toMap(JSONValue.parse(stripJsonPrefix(response)));
|
|
||||||
|
|
||||||
// TODO(user): consider using jart's FormField Framework.
|
|
||||||
// See: j/c/g/d/r/ui/server/RegistrarFormFields.java
|
|
||||||
String status = (String) responseMap.get("status");
|
|
||||||
Verify.verify(!status.equals("error"), "Server error: %s", responseMap.get("error"));
|
|
||||||
return String.format("Successfully saved premium list %s\n", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
static Map<String, Object> toMap(Object obj) {
|
|
||||||
Verify.verify(obj instanceof Map<?, ?>, "JSON object is not a Map: %s", obj);
|
|
||||||
return (Map<String, Object>) obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(user): figure out better place to put this method to make it re-usable
|
|
||||||
private static String stripJsonPrefix(String json) {
|
|
||||||
Verify.verify(json.startsWith(JSON_SAFETY_PREFIX));
|
|
||||||
return json.substring(JSON_SAFETY_PREFIX.length());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,23 @@
|
||||||
|
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static google.registry.model.registry.Registries.assertTldExists;
|
||||||
|
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.beust.jcommander.Parameters;
|
import com.beust.jcommander.Parameters;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.base.Strings;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.model.registry.label.PremiumList;
|
import google.registry.model.registry.label.PremiumList;
|
||||||
import google.registry.tools.server.CreatePremiumListAction;
|
import google.registry.persistence.VKey;
|
||||||
|
import google.registry.schema.tld.PremiumListSqlDao;
|
||||||
|
import google.registry.schema.tld.PremiumListUtils;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
/** Command to create a {@link PremiumList} on Datastore. */
|
/** Command to create a {@link PremiumList} on Database. */
|
||||||
@Parameters(separators = " =", commandDescription = "Create a PremiumList in Datastore.")
|
@Parameters(separators = " =", commandDescription = "Create a PremiumList in Database.")
|
||||||
public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||||
|
|
||||||
@Parameter(
|
@Parameter(
|
||||||
|
@ -29,18 +38,24 @@ public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||||
description = "Override restrictions on premium list naming")
|
description = "Override restrictions on premium list naming")
|
||||||
boolean override;
|
boolean override;
|
||||||
|
|
||||||
/** Returns the path to the servlet task. */
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandPath() {
|
// Using CreatePremiumListAction.java as reference;
|
||||||
return CreatePremiumListAction.PATH;
|
protected void init() throws Exception {
|
||||||
}
|
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||||
|
checkArgument(
|
||||||
@Override
|
!PremiumListSqlDao.getLatestRevision(name).isPresent(),
|
||||||
ImmutableMap<String, String> getParameterMap() {
|
"A premium list already exists by this name");
|
||||||
if (override) {
|
if (!override) {
|
||||||
return ImmutableMap.of("override", "true");
|
// refer to CreatePremiumListAction.java
|
||||||
} else {
|
assertTldExists(
|
||||||
return ImmutableMap.of();
|
name,
|
||||||
|
"Premium names must match the name of the TLD they are intended to be used on"
|
||||||
|
+ " (unless --override is specified), yet TLD %s does not exist");
|
||||||
}
|
}
|
||||||
|
inputData = Files.readAllLines(inputFile, UTF_8);
|
||||||
|
// create a premium list with only input data and store as the first version of the entity
|
||||||
|
PremiumList newPremiumList = PremiumListUtils.parseToPremiumList(name, inputData);
|
||||||
|
stageEntityChange(
|
||||||
|
null, newPremiumList, VKey.createOfy(PremiumList.class, Key.create(newPremiumList)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,18 +14,86 @@
|
||||||
|
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
import com.beust.jcommander.Parameters;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import google.registry.model.registry.label.PremiumList;
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||||
import google.registry.tools.server.UpdatePremiumListAction;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
|
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
/** Command to safely update {@link PremiumList} in Datastore for a given TLD. */
|
import com.beust.jcommander.Parameters;
|
||||||
@Parameters(separators = " =", commandDescription = "Update a PremiumList in Datastore.")
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Streams;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
|
import google.registry.model.registry.label.PremiumList;
|
||||||
|
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||||
|
import google.registry.persistence.VKey;
|
||||||
|
import google.registry.schema.tld.PremiumEntry;
|
||||||
|
import google.registry.schema.tld.PremiumListSqlDao;
|
||||||
|
import google.registry.schema.tld.PremiumListUtils;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.joda.money.BigMoney;
|
||||||
|
|
||||||
|
/** Command to safely update {@link PremiumList} in Database for a given TLD. */
|
||||||
|
@Parameters(separators = " =", commandDescription = "Update a PremiumList in Database.")
|
||||||
class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||||
|
|
||||||
/** Returns the path to the servlet task. */
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandPath() {
|
// Using UpdatePremiumListAction.java as reference;
|
||||||
return UpdatePremiumListAction.PATH;
|
protected void init() throws Exception {
|
||||||
|
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||||
|
List<String> existingEntry = getExistingPremiumListEntry(name).asList();
|
||||||
|
inputData = Files.readAllLines(inputFile, UTF_8);
|
||||||
|
|
||||||
|
// reconstructing existing premium list to bypass Hibernate lazy initialization exception
|
||||||
|
PremiumList existingPremiumList = PremiumListUtils.parseToPremiumList(name, existingEntry);
|
||||||
|
PremiumList updatedPremiumList = PremiumListUtils.parseToPremiumList(name, inputData);
|
||||||
|
|
||||||
|
// use LabelsToPrices() for comparison between old and new premium lists since they have
|
||||||
|
// different creation date, updated date even if they have same content;
|
||||||
|
if (!existingPremiumList.getLabelsToPrices().equals(updatedPremiumList.getLabelsToPrices())) {
|
||||||
|
stageEntityChange(
|
||||||
|
existingPremiumList,
|
||||||
|
updatedPremiumList,
|
||||||
|
VKey.createOfy(PremiumList.class, Key.create(existingPremiumList)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
To get premium list content as a set of string. This is a workaround to avoid dealing with
|
||||||
|
Hibernate.LazyInitizationException error. It occurs when trying to access data of the
|
||||||
|
latest revision of an existing premium list.
|
||||||
|
"Cannot evaluate google.registry.model.registry.label.PremiumList.toString()'".
|
||||||
|
Ideally, the following should be the way to verify info in latest revision of a premium list:
|
||||||
|
|
||||||
|
PremiumList existingPremiumList =
|
||||||
|
PremiumListSqlDao.getLatestRevision(name)
|
||||||
|
.orElseThrow(
|
||||||
|
() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Could not update premium list %s because it doesn't exist.", name)));
|
||||||
|
assertThat(persistedList.getLabelsToPrices()).containsEntry("foo", new BigDecimal("9000.00"));
|
||||||
|
assertThat(persistedList.size()).isEqualTo(1);
|
||||||
|
*/
|
||||||
|
protected ImmutableSet<String> getExistingPremiumListEntry(String name) {
|
||||||
|
Optional<PremiumList> list = PremiumListSqlDao.getLatestRevision(name);
|
||||||
|
checkArgument(
|
||||||
|
list.isPresent(),
|
||||||
|
String.format("Could not update premium list %s because it doesn't exist.", name));
|
||||||
|
Iterable<PremiumEntry> sqlListEntries =
|
||||||
|
jpaTm().transact(() -> PremiumListSqlDao.loadPremiumListEntriesUncached(list.get()));
|
||||||
|
return Streams.stream(sqlListEntries)
|
||||||
|
.map(
|
||||||
|
premiumEntry ->
|
||||||
|
new PremiumListEntry.Builder()
|
||||||
|
.setPrice(
|
||||||
|
BigMoney.of(list.get().getCurrency(), premiumEntry.getPrice()).toMoney())
|
||||||
|
.setLabel(premiumEntry.getDomainLabel())
|
||||||
|
.build()
|
||||||
|
.toString())
|
||||||
|
.collect(toImmutableSet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,47 +14,64 @@
|
||||||
|
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
import static google.registry.testing.DatabaseHelper.newRegistry;
|
||||||
|
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||||
|
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
|
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.base.Ascii;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.ImmutableSortedMap;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.google.common.net.MediaType;
|
import google.registry.dns.writer.VoidDnsWriter;
|
||||||
import google.registry.testing.UriParameters;
|
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||||
|
import google.registry.model.registry.Registry;
|
||||||
|
import google.registry.model.registry.Registry.TldType;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.io.IOException;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.mockito.Captor;
|
|
||||||
|
|
||||||
/** Base class for common testing setup for create and update commands for Premium Lists. */
|
/** Base class for common testing setup for create and update commands for Premium Lists. */
|
||||||
abstract class CreateOrUpdatePremiumListCommandTestCase<T extends CreateOrUpdatePremiumListCommand>
|
abstract class CreateOrUpdatePremiumListCommandTestCase<T extends CreateOrUpdatePremiumListCommand>
|
||||||
extends CommandTestCase<T> {
|
extends CommandTestCase<T> {
|
||||||
|
|
||||||
@Captor
|
protected static final String TLD_TEST = "prime";
|
||||||
ArgumentCaptor<ImmutableMap<String, String>> urlParamCaptor;
|
protected String premiumTermsPath;
|
||||||
|
protected String initialPremiumListData;
|
||||||
|
|
||||||
@Captor
|
@BeforeEach
|
||||||
ArgumentCaptor<byte[]> requestBodyCaptor;
|
void beforeEachCreateOrUpdatePremiumListCommandTestCase() throws IOException {
|
||||||
|
// initial set up for both CreatePremiumListCommand and UpdatePremiumListCommand test cases;
|
||||||
static String generateInputData(String premiumTermsPath) throws Exception {
|
initialPremiumListData = "doge,USD 9090";
|
||||||
return Files.asCharSource(new File(premiumTermsPath), StandardCharsets.UTF_8).read();
|
File premiumTermsFile = tmpDir.resolve(TLD_TEST + ".txt").toFile();
|
||||||
|
Files.asCharSink(premiumTermsFile, UTF_8).write(initialPremiumListData);
|
||||||
|
premiumTermsPath = premiumTermsFile.getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
void verifySentParams(
|
Registry createRegistry(String tldStr, String premiumListInput) {
|
||||||
AppEngineConnection connection, String path, ImmutableMap<String, String> parameterMap)
|
Registry registry;
|
||||||
throws Exception {
|
if (premiumListInput != null) {
|
||||||
verify(connection)
|
registry =
|
||||||
.sendPostRequest(
|
newRegistry(
|
||||||
eq(path),
|
tldStr,
|
||||||
urlParamCaptor.capture(),
|
Ascii.toUpperCase(tldStr),
|
||||||
eq(MediaType.FORM_DATA),
|
ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY),
|
||||||
requestBodyCaptor.capture());
|
TldType.TEST);
|
||||||
assertThat(new ImmutableMap.Builder<String, String>()
|
persistPremiumList(tldStr, premiumListInput);
|
||||||
.putAll(urlParamCaptor.getValue())
|
persistResource(registry);
|
||||||
.putAll(UriParameters.parse(new String(requestBodyCaptor.getValue(), UTF_8)).entries())
|
} else {
|
||||||
.build())
|
registry =
|
||||||
.containsExactlyEntriesIn(parameterMap);
|
new Registry.Builder()
|
||||||
|
.setTldStr(tldStr)
|
||||||
|
.setPremiumPricingEngine(StaticPremiumListPricingEngine.NAME)
|
||||||
|
.setDnsWriters(ImmutableSet.of(VoidDnsWriter.NAME))
|
||||||
|
.setPremiumList(null)
|
||||||
|
.build();
|
||||||
|
tm().transact(() -> tm().put(registry));
|
||||||
|
}
|
||||||
|
return registry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,105 +15,125 @@
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.request.JsonResponse.JSON_SAFETY_PREFIX;
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
import static google.registry.testing.TestDataHelper.loadFile;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyMap;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.reset;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.beust.jcommander.ParameterException;
|
import com.google.common.io.Files;
|
||||||
import com.google.common.base.VerifyException;
|
import google.registry.model.registry.Registry;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import google.registry.schema.tld.PremiumListSqlDao;
|
||||||
import com.google.common.net.MediaType;
|
import java.nio.file.Path;
|
||||||
import google.registry.tools.server.CreatePremiumListAction;
|
import java.nio.file.Paths;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.ArgumentMatchers;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.jupiter.MockitoSettings;
|
|
||||||
import org.mockito.quality.Strictness;
|
|
||||||
|
|
||||||
/** Unit tests for {@link CreatePremiumListCommand}. */
|
/** Unit tests for {@link CreatePremiumListCommand}. */
|
||||||
class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
||||||
extends CreateOrUpdatePremiumListCommandTestCase<C> {
|
extends CreateOrUpdatePremiumListCommandTestCase<C> {
|
||||||
|
Registry registry;
|
||||||
@Mock AppEngineConnection connection;
|
|
||||||
|
|
||||||
private String premiumTermsPath;
|
|
||||||
String premiumTermsCsv;
|
|
||||||
private String servletPath;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() throws Exception {
|
void beforeEach() {
|
||||||
command.setConnection(connection);
|
registry = createRegistry(TLD_TEST, null);
|
||||||
premiumTermsPath =
|
|
||||||
writeToNamedTmpFile(
|
|
||||||
"example_premium_terms.csv",
|
|
||||||
loadFile(CreatePremiumListCommandTest.class, "example_premium_terms.csv"));
|
|
||||||
servletPath = "/_dr/admin/createPremiumList";
|
|
||||||
when(connection.sendPostRequest(
|
|
||||||
eq(CreatePremiumListAction.PATH),
|
|
||||||
ArgumentMatchers.<String, String>anyMap(),
|
|
||||||
any(MediaType.class),
|
|
||||||
any(byte[].class)))
|
|
||||||
.thenReturn(JSON_SAFETY_PREFIX + "{\"status\":\"success\",\"lines\":[]}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRun() throws Exception {
|
void verify_registryIsSetUpCorrectly() {
|
||||||
runCommandForced("-i=" + premiumTermsPath, "-n=foo");
|
// ensure that no premium list is created before running the command
|
||||||
assertInStdout("Successfully");
|
// this check also implicitly verifies the TLD is successfully created;
|
||||||
verifySentParams(
|
assertThat(PremiumListSqlDao.getLatestRevision(TLD_TEST).isPresent()).isFalse();
|
||||||
connection,
|
|
||||||
servletPath,
|
|
||||||
ImmutableMap.of("name", "foo", "inputData", generateInputData(premiumTermsPath)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRun_noProvidedName_usesBasenameOfInputFile() throws Exception {
|
void commandRun_successCreateList() throws Exception {
|
||||||
runCommandForced("-i=" + premiumTermsPath);
|
runCommandForced("--name=" + TLD_TEST, "--input=" + premiumTermsPath);
|
||||||
assertInStdout("Successfully");
|
assertThat(registry.getTld().toString()).isEqualTo(TLD_TEST);
|
||||||
verifySentParams(
|
assertThat(PremiumListSqlDao.getLatestRevision(TLD_TEST).isPresent()).isTrue();
|
||||||
connection,
|
|
||||||
servletPath,
|
|
||||||
ImmutableMap.of(
|
|
||||||
"name", "example_premium_terms", "inputData", generateInputData(premiumTermsPath)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRun_errorResponse() throws Exception {
|
// since the old entity is always null and file cannot be empty, the prompt will NOT be "No entity
|
||||||
reset(connection);
|
// changes to apply."
|
||||||
command.setConnection(connection);
|
void commandInit_successStageNewEntity() throws Exception {
|
||||||
when(connection.sendPostRequest(
|
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||||
eq(CreatePremiumListAction.PATH), anyMap(), any(MediaType.class), any(byte[].class)))
|
command.inputFile = Paths.get(premiumTermsPath);
|
||||||
.thenReturn(JSON_SAFETY_PREFIX + "{\"status\":\"error\",\"error\":\"foo already exists\"}");
|
command.init();
|
||||||
VerifyException thrown =
|
assertThat(command.prompt()).contains("Create PremiumList@");
|
||||||
assertThrows(
|
assertThat(command.prompt()).contains(String.format("name=%s", TLD_TEST));
|
||||||
VerifyException.class, () -> runCommandForced("-i=" + premiumTermsPath, "-n=foo"));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Server error:");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
void commandInit_successStageNewEntityWithOverride() throws Exception {
|
||||||
void testRun_noInputFileSpecified_throwsException() {
|
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||||
ParameterException thrown = assertThrows(ParameterException.class, this::runCommand);
|
String alterTld = "override";
|
||||||
assertThat(thrown).hasMessageThat().contains("The following option is required");
|
command.inputFile = Paths.get(premiumTermsPath);
|
||||||
|
command.override = true;
|
||||||
|
command.name = alterTld;
|
||||||
|
command.init();
|
||||||
|
assertThat(command.prompt()).contains("Create PremiumList@");
|
||||||
|
assertThat(command.prompt()).contains(String.format("name=%s", alterTld));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
void commandInit_failureNoInputFile() {
|
||||||
void testRun_invalidInputData() throws Exception {
|
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||||
premiumTermsPath =
|
assertThrows(NullPointerException.class, command::init);
|
||||||
writeToNamedTmpFile(
|
}
|
||||||
"tmp_file2",
|
|
||||||
loadFile(CreatePremiumListCommandTest.class, "example_invalid_premium_terms.csv"));
|
@Test
|
||||||
IllegalArgumentException thrown =
|
void commandInit_failurePremiumListAlreadyExists() {
|
||||||
assertThrows(
|
String randomStr = "random";
|
||||||
IllegalArgumentException.class,
|
createTld(randomStr);
|
||||||
() -> runCommandForced("-i=" + premiumTermsPath, "-n=foo"));
|
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||||
assertThat(thrown).hasMessageThat().contains("Could not parse line in premium list");
|
command.name = randomStr;
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::init);
|
||||||
|
assertThat(thrown).hasMessageThat().isEqualTo("A premium list already exists by this name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandInit_failureMismatchedTldFileName_noOverride() throws Exception {
|
||||||
|
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||||
|
String fileName = "random";
|
||||||
|
Path tmpPath = tmpDir.resolve(String.format("%s.txt", fileName));
|
||||||
|
Files.write(new byte[0], tmpPath.toFile());
|
||||||
|
command.inputFile = tmpPath;
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::init);
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.contains(
|
||||||
|
String.format(
|
||||||
|
"Premium names must match the name of the TLD they are "
|
||||||
|
+ "intended to be used on (unless --override is specified), "
|
||||||
|
+ "yet TLD %s does not exist",
|
||||||
|
fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandInit_failureMismatchedTldName_noOverride() {
|
||||||
|
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||||
|
String fileName = "random";
|
||||||
|
command.name = fileName;
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::init);
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.contains(
|
||||||
|
String.format(
|
||||||
|
"Premium names must match the name of the TLD they are "
|
||||||
|
+ "intended to be used on (unless --override is specified), "
|
||||||
|
+ "yet TLD %s does not exist",
|
||||||
|
fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandInit_failureUseEmptyFile() throws Exception {
|
||||||
|
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||||
|
String fileName = "empty";
|
||||||
|
Path tmpPath = tmpDir.resolve(String.format("%s.txt", fileName));
|
||||||
|
Files.write(new byte[0], tmpPath.toFile());
|
||||||
|
command.inputFile = tmpPath;
|
||||||
|
command.name = TLD_TEST;
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::init);
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.contains("The Cloud SQL schema requires exactly one currency");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,60 +14,154 @@
|
||||||
|
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
import static google.registry.request.JsonResponse.JSON_SAFETY_PREFIX;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.testing.TestDataHelper.loadFile;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.mockito.ArgumentMatchers.anyMap;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.net.MediaType;
|
import com.google.common.io.Files;
|
||||||
import google.registry.tools.server.UpdatePremiumListAction;
|
import google.registry.model.registry.Registry;
|
||||||
|
import google.registry.schema.tld.PremiumListSqlDao;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mock;
|
|
||||||
|
|
||||||
/** Unit tests for {@link UpdatePremiumListCommand}. */
|
/** Unit tests for {@link UpdatePremiumListCommand}. */
|
||||||
class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||||
extends CreateOrUpdatePremiumListCommandTestCase<C> {
|
extends CreateOrUpdatePremiumListCommandTestCase<C> {
|
||||||
|
Registry registry;
|
||||||
@Mock AppEngineConnection connection;
|
|
||||||
|
|
||||||
private String premiumTermsPath;
|
|
||||||
String premiumTermsCsv;
|
|
||||||
private String servletPath;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() throws Exception {
|
void beforeEach() {
|
||||||
command.setConnection(connection);
|
registry = createRegistry(TLD_TEST, initialPremiumListData);
|
||||||
servletPath = "/_dr/admin/updatePremiumList";
|
|
||||||
premiumTermsPath =
|
|
||||||
writeToNamedTmpFile(
|
|
||||||
"example_premium_terms.csv",
|
|
||||||
loadFile(UpdatePremiumListCommandTest.class, "example_premium_terms.csv"));
|
|
||||||
when(connection.sendPostRequest(
|
|
||||||
eq(UpdatePremiumListAction.PATH), anyMap(), any(MediaType.class), any(byte[].class)))
|
|
||||||
.thenReturn(JSON_SAFETY_PREFIX + "{\"status\":\"success\",\"lines\":[]}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRun() throws Exception {
|
void verify_registryIsSetUpCorrectly() {
|
||||||
runCommandForced("-i=" + premiumTermsPath, "-n=foo");
|
// ensure that no premium list is created before running the command
|
||||||
verifySentParams(
|
assertThat(PremiumListSqlDao.getLatestRevision(TLD_TEST).isPresent()).isTrue();
|
||||||
connection,
|
// ensure that there's value in existing premium list;
|
||||||
servletPath,
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
ImmutableMap.of("name", "foo", "inputData", generateInputData(premiumTermsPath)));
|
ImmutableSet<String> entries = command.getExistingPremiumListEntry(TLD_TEST);
|
||||||
|
assertThat(entries.size()).isEqualTo(1);
|
||||||
|
// data from @beforeEach of CreateOrUpdatePremiumListCommandTestCase.java
|
||||||
|
assertThat(entries.contains("doge,USD 9090.00")).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRun_noProvidedName_usesBasenameOfInputFile() throws Exception {
|
void commandInit_successStageNoEntityChange() throws Exception {
|
||||||
runCommandForced("-i=" + premiumTermsPath);
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
assertInStdout("Successfully");
|
command.inputFile = Paths.get(premiumTermsPath);
|
||||||
verifySentParams(
|
command.name = TLD_TEST;
|
||||||
connection,
|
command.init();
|
||||||
servletPath,
|
assertThat(command.prompt()).contains("No entity changes to apply.");
|
||||||
ImmutableMap.of(
|
}
|
||||||
"name", "example_premium_terms", "inputData", generateInputData(premiumTermsPath)));
|
|
||||||
|
@Test
|
||||||
|
void commandInit_successStageEntityChange() throws Exception {
|
||||||
|
File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
|
||||||
|
String newPremiumListData = "omg,JPY 1234";
|
||||||
|
Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
|
||||||
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
|
command.inputFile = Paths.get(tmpFile.getPath());
|
||||||
|
command.name = TLD_TEST;
|
||||||
|
command.init();
|
||||||
|
assertThat(command.prompt()).contains("Update PremiumList@");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandRun_successUpdateList() throws Exception {
|
||||||
|
File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
|
||||||
|
String newPremiumListData = "eth,USD 9999";
|
||||||
|
Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
|
||||||
|
|
||||||
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
|
// data come from @beforeEach of CreateOrUpdatePremiumListCommandTestCase.java
|
||||||
|
command.inputFile = Paths.get(tmpFile.getPath());
|
||||||
|
runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile);
|
||||||
|
|
||||||
|
ImmutableSet<String> entries = command.getExistingPremiumListEntry(TLD_TEST);
|
||||||
|
assertThat(entries.size()).isEqualTo(1);
|
||||||
|
// verify that list is updated; cannot use only string since price is formatted;
|
||||||
|
assertThat(entries.contains("eth,USD 9999.00")).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandRun_successUpdateMultiLineList() throws Exception {
|
||||||
|
File tmpFile = tmpDir.resolve(TLD_TEST + ".txt").toFile();
|
||||||
|
String premiumTerms = "foo,USD 9000\ndoge,USD 100\nelon,USD 2021";
|
||||||
|
Files.asCharSink(tmpFile, UTF_8).write(premiumTerms);
|
||||||
|
|
||||||
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
|
command.inputFile = Paths.get(tmpFile.getPath());
|
||||||
|
runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile);
|
||||||
|
|
||||||
|
// assert all three lines from premiumTerms are added
|
||||||
|
ImmutableSet<String> entries = command.getExistingPremiumListEntry(TLD_TEST);
|
||||||
|
assertThat(entries.size()).isEqualTo(3);
|
||||||
|
assertThat(entries.contains("foo,USD 9000.00")).isTrue();
|
||||||
|
assertThat(entries.contains("doge,USD 100.00")).isTrue();
|
||||||
|
assertThat(entries.contains("elon,USD 2021.00")).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandInit_failureUpdateEmptyList() throws Exception {
|
||||||
|
Path tmpPath = tmpDir.resolve(String.format("%s.txt", TLD_TEST));
|
||||||
|
Files.write(new byte[0], tmpPath.toFile());
|
||||||
|
|
||||||
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
|
command.inputFile = tmpPath;
|
||||||
|
command.name = TLD_TEST;
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::init);
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.contains("The Cloud SQL schema requires exactly one currency");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandInit_failureNoPreviousVersion() {
|
||||||
|
String fileName = "random";
|
||||||
|
registry = createRegistry(fileName, null);
|
||||||
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
|
command.name = fileName;
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::init);
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo(
|
||||||
|
String.format("Could not update premium list %s because it doesn't exist.", fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandInit_failureNoInputFile() {
|
||||||
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
|
assertThrows(NullPointerException.class, command::init);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandInit_failureTldFromNameDoesNotExist() {
|
||||||
|
String fileName = "random";
|
||||||
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
|
command.name = fileName;
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::init);
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo(
|
||||||
|
String.format("Could not update premium list %s because it doesn't exist.", fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commandInit_failureTldFromInputFileDoesNotExist() {
|
||||||
|
String fileName = "random";
|
||||||
|
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||||
|
// using tld extracted from file name but this tld is not part of the registry
|
||||||
|
command.inputFile =
|
||||||
|
Paths.get(tmpDir.resolve(String.format("%s.txt", fileName)).toFile().getPath());
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::init);
|
||||||
|
assertThat(thrown)
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo(
|
||||||
|
String.format("Could not update premium list %s because it doesn't exist.", fileName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue