diff --git a/java/google/registry/flows/domain/DomainUpdateFlow.java b/java/google/registry/flows/domain/DomainUpdateFlow.java
index 8f6b623c5..6517b12a0 100644
--- a/java/google/registry/flows/domain/DomainUpdateFlow.java
+++ b/java/google/registry/flows/domain/DomainUpdateFlow.java
@@ -135,7 +135,11 @@ import org.joda.time.DateTime;
public final class DomainUpdateFlow implements TransactionalFlow {
/**
- * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it
+ * A list of {@link StatusValue}s that prohibit updates.
+ *
+ *
Superusers can override these statuses and perform updates anyway.
+ *
+ *
Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it
* requires special checking, since you must be able to clear the status off the object with an
* update.
*/
@@ -208,12 +212,12 @@ public final class DomainUpdateFlow implements TransactionalFlow {
/** Fail if the object doesn't exist or was deleted. */
private void verifyUpdateAllowed(Update command, DomainResource existingDomain, DateTime now)
throws EppException {
- verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES);
verifyOptionalAuthInfo(authInfo, existingDomain);
AddRemove add = command.getInnerAdd();
AddRemove remove = command.getInnerRemove();
String tld = existingDomain.getTld();
if (!isSuperuser) {
+ verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES);
verifyResourceOwnership(clientId, existingDomain);
verifyClientUpdateNotProhibited(command, existingDomain);
verifyAllStatusesAreClientSettable(union(add.getStatusValues(), remove.getStatusValues()));
diff --git a/java/google/registry/model/eppcommon/StatusValue.java b/java/google/registry/model/eppcommon/StatusValue.java
index 540a19b26..0902ff0b9 100644
--- a/java/google/registry/model/eppcommon/StatusValue.java
+++ b/java/google/registry/model/eppcommon/StatusValue.java
@@ -43,11 +43,9 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
* @see RFC 5731 (Domain) Section 2.3
* @see RFC 5732 (Host) Section 2.3
* @see RFC 5733 (Contact) Section 2.2
-
*/
@XmlJavaTypeAdapter(StatusValueAdapter.class)
public enum StatusValue implements EppEnum {
-
CLIENT_DELETE_PROHIBITED(AllowedOn.ALL),
CLIENT_HOLD(AllowedOn.ALL),
CLIENT_RENEW_PROHIBITED(AllowedOn.ALL),
@@ -114,10 +112,17 @@ public enum StatusValue implements EppEnum {
*/
PENDING_UPDATE(AllowedOn.NONE),
+
+ /** A non-client-settable status that prevents deletes of EPP resources. */
SERVER_DELETE_PROHIBITED(AllowedOn.ALL),
+
SERVER_HOLD(AllowedOn.ALL),
SERVER_RENEW_PROHIBITED(AllowedOn.ALL),
+
+ /** A non-client-settable status that prevents transfers of EPP resources. */
SERVER_TRANSFER_PROHIBITED(AllowedOn.ALL),
+
+ /** A non-client-settable status that prevents updates of EPP resources, except by superusers. */
SERVER_UPDATE_PROHIBITED(AllowedOn.ALL);
private final String xmlName = UPPER_UNDERSCORE.to(LOWER_CAMEL, name());
diff --git a/java/google/registry/tools/LockDomainCommand.java b/java/google/registry/tools/LockDomainCommand.java
new file mode 100644
index 000000000..49d0fdcff
--- /dev/null
+++ b/java/google/registry/tools/LockDomainCommand.java
@@ -0,0 +1,76 @@
+// Copyright 2017 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.tools;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static google.registry.model.EppResourceUtils.loadByForeignKey;
+import static google.registry.util.FormattingLogger.getLoggerForCallerClass;
+import static java.util.stream.Collectors.toList;
+import static org.joda.time.DateTimeZone.UTC;
+
+import com.beust.jcommander.Parameters;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.template.soy.data.SoyMapData;
+import google.registry.model.domain.DomainResource;
+import google.registry.model.eppcommon.StatusValue;
+import google.registry.tools.soy.DomainUpdateSoyInfo;
+import google.registry.util.FormattingLogger;
+import org.joda.time.DateTime;
+
+/**
+ * A command to registry lock domain names via EPP.
+ *
+ *
A registry lock consists of server-side statuses preventing deletes, updates, and transfers.
+ */
+@Parameters(separators = " =", commandDescription = "Registry lock a domain via EPP.")
+public class LockDomainCommand extends LockOrUnlockDomainCommand {
+
+ private static final FormattingLogger logger = getLoggerForCallerClass();
+
+ @Override
+ protected void initMutatingEppToolCommand() throws Exception {
+ // Project all domains as of the same time so that argument order doesn't affect behavior.
+ DateTime now = DateTime.now(UTC);
+ for (String domain : getDomains()) {
+ DomainResource domainResource = loadByForeignKey(DomainResource.class, domain, now);
+ checkArgument(domainResource != null, "Domain '%s' does not exist", domain);
+ ImmutableSet statusesToAdd =
+ Sets.difference(REGISTRY_LOCK_STATUSES, domainResource.getStatusValues()).immutableCopy();
+ if (statusesToAdd.isEmpty()) {
+ logger.infofmt("Domain '%s' is already locked and needs no updates.", domain);
+ continue;
+ }
+
+ setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE);
+ addSoyRecord(
+ clientId,
+ new SoyMapData(
+ "domain", domain,
+ "add", true,
+ "addNameservers", ImmutableList.of(),
+ "addAdmins", ImmutableList.of(),
+ "addTechs", ImmutableList.of(),
+ "addStatuses", statusesToAdd.stream().map(StatusValue::getXmlName).collect(toList()),
+ "remove", false,
+ "removeNameservers", ImmutableList.of(),
+ "removeAdmins", ImmutableList.of(),
+ "removeTechs", ImmutableList.of(),
+ "removeStatuses", ImmutableList.of(),
+ "change", false));
+ }
+ }
+}
diff --git a/java/google/registry/tools/LockOrUnlockDomainCommand.java b/java/google/registry/tools/LockOrUnlockDomainCommand.java
new file mode 100644
index 000000000..a5a2e3f65
--- /dev/null
+++ b/java/google/registry/tools/LockOrUnlockDomainCommand.java
@@ -0,0 +1,60 @@
+// Copyright 2017 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.tools;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static google.registry.model.eppcommon.StatusValue.SERVER_DELETE_PROHIBITED;
+import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED;
+import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
+import static google.registry.util.CollectionUtils.findDuplicates;
+
+import com.beust.jcommander.Parameter;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import google.registry.model.eppcommon.StatusValue;
+import java.util.List;
+
+/** Shared base class for commands to registry lock or unlock a domain via EPP. */
+public abstract class LockOrUnlockDomainCommand extends MutatingEppToolCommand {
+
+ protected static final ImmutableSet REGISTRY_LOCK_STATUSES =
+ ImmutableSet.of(
+ SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED);
+
+ @Parameter(
+ names = {"-c", "--client"},
+ description = "Client identifier of the registrar to execute the command as",
+ required = true
+ )
+ String clientId;
+
+ @Parameter(description = "Names of the domains", required = true)
+ private List mainParameters;
+
+ protected ImmutableSet getDomains() {
+ return ImmutableSet.copyOf(mainParameters);
+ }
+
+ @Override
+ protected void initEppToolCommand() throws Exception {
+ // Superuser status is required to update registry lock statuses.
+ superuser = true;
+ String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters));
+ checkArgument(duplicates.isEmpty(), "Duplicate domain arguments found: '%s'", duplicates);
+ initMutatingEppToolCommand();
+ System.out.println(
+ "== ENSURE THAT YOU HAVE AUTHENTICATED THE REGISTRAR BEFORE RUNNING THIS COMMAND ==");
+ }
+}
diff --git a/java/google/registry/tools/RegistryTool.java b/java/google/registry/tools/RegistryTool.java
index 4682a5f3c..36ca98165 100644
--- a/java/google/registry/tools/RegistryTool.java
+++ b/java/google/registry/tools/RegistryTool.java
@@ -95,6 +95,7 @@ public final class RegistryTool {
.put("list_tlds", ListTldsCommand.class)
.put("load_snapshot", LoadSnapshotCommand.class)
.put("load_test", LoadTestCommand.class)
+ .put("lock_domain", LockDomainCommand.class)
.put("login", LoginCommand.class)
.put("logout", LogoutCommand.class)
.put("make_billing_tables", MakeBillingTablesCommand.class)
diff --git a/java/google/registry/tools/RegistryToolComponent.java b/java/google/registry/tools/RegistryToolComponent.java
index 83e0d1f21..8d5d3cc15 100644
--- a/java/google/registry/tools/RegistryToolComponent.java
+++ b/java/google/registry/tools/RegistryToolComponent.java
@@ -94,9 +94,7 @@ interface RegistryToolComponent {
void inject(SendEscrowReportToIcannCommand command);
void inject(SetupOteCommand command);
void inject(UpdateCursorsCommand command);
-
void inject(UpdateDomainCommand command);
-
void inject(UpdateKmsKeyringCommand command);
void inject(UpdateTldCommand command);
void inject(ValidateEscrowDepositCommand command);
diff --git a/java/google/registry/tools/UpdateDomainCommand.java b/java/google/registry/tools/UpdateDomainCommand.java
index 8661c033a..4daec5114 100644
--- a/java/google/registry/tools/UpdateDomainCommand.java
+++ b/java/google/registry/tools/UpdateDomainCommand.java
@@ -17,6 +17,7 @@ package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
+import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static org.joda.time.DateTimeZone.UTC;
@@ -127,6 +128,11 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
DateTime now = DateTime.now(UTC);
DomainResource domainResource = loadByForeignKey(DomainResource.class, domain, now);
checkArgument(domainResource != null, "Domain '%s' does not exist", domain);
+ checkArgument(
+ !domainResource.getStatusValues().contains(SERVER_UPDATE_PROHIBITED),
+ "The domain '%s' has status SERVER_UPDATE_PROHIBITED. Verify that you are allowed "
+ + "to make updates, and if so, use the domain_unlock command to enable updates.",
+ domain);
if (!nameservers.isEmpty()) {
ImmutableSortedSet existingNameservers =
domainResource.loadNameserverFullyQualifiedHostNames();
diff --git a/javatests/google/registry/flows/domain/DomainUpdateFlowTest.java b/javatests/google/registry/flows/domain/DomainUpdateFlowTest.java
index d48c2ed52..d20bb08e3 100644
--- a/javatests/google/registry/flows/domain/DomainUpdateFlowTest.java
+++ b/javatests/google/registry/flows/domain/DomainUpdateFlowTest.java
@@ -18,6 +18,7 @@ import static com.google.common.collect.Sets.union;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
+import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.assertBillingEvents;
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
@@ -34,6 +35,7 @@ import static google.registry.testing.DatastoreHelper.persistReservedList;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DomainResourceSubject.assertAboutDomains;
import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
+import static google.registry.testing.JUnitBackports.expectThrows;
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static org.joda.money.CurrencyUnit.USD;
@@ -892,9 +894,46 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase
+
+
+
+ example.tld
+
+
+ ns2.example.foo
+
+ Add registry lock for domain.
+
+
+
+ ns1.example.foo
+
+
+
+ sh8013
+
+ 2BARfoo
+
+
+
+
+ ABC-12345
+
+
diff --git a/javatests/google/registry/testing/TestDataHelper.java b/javatests/google/registry/testing/TestDataHelper.java
index 83ce3f273..fef7f391e 100644
--- a/javatests/google/registry/testing/TestDataHelper.java
+++ b/javatests/google/registry/testing/TestDataHelper.java
@@ -20,9 +20,7 @@ import static google.registry.util.ResourceUtils.readResourceUtf8;
import java.util.Map;
import java.util.Map.Entry;
-/**
- * Contains helper methods for dealing with test data.
- */
+/** Contains helper methods for dealing with test data. */
public final class TestDataHelper {
/**
@@ -31,7 +29,11 @@ public final class TestDataHelper {
*/
public static String loadFileWithSubstitutions(
Class> context, String filename, Map substitutions) {
- String fileContents = readResourceUtf8(context, "testdata/" + filename);
+ return applySubstitutions(readResourceUtf8(context, "testdata/" + filename), substitutions);
+ }
+
+ /** Applies the given substitutions to the given string and returns the result. */
+ public static String applySubstitutions(String fileContents, Map substitutions) {
for (Entry entry : nullToEmpty(substitutions).entrySet()) {
fileContents = fileContents.replaceAll("%" + entry.getKey() + "%", entry.getValue());
}
diff --git a/javatests/google/registry/tools/CommandTestCase.java b/javatests/google/registry/tools/CommandTestCase.java
index 8858e8768..b33acc0b1 100644
--- a/javatests/google/registry/tools/CommandTestCase.java
+++ b/javatests/google/registry/tools/CommandTestCase.java
@@ -14,6 +14,7 @@
package google.registry.tools;
+import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.toArray;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy;
@@ -22,6 +23,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.beust.jcommander.JCommander;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.ObjectArrays;
import com.google.common.io.Files;
import com.google.common.reflect.TypeToken;
@@ -95,11 +98,21 @@ public abstract class CommandTestCase {
runCommandInEnvironment(RegistryToolEnvironment.UNITTEST, args);
}
+ protected void runCommand(Iterable args) throws Exception {
+ runCommandInEnvironment(
+ RegistryToolEnvironment.UNITTEST, Iterables.toArray(args, String.class));
+ }
+
/** Adds "--force" as the first parameter, then runs the command. */
protected void runCommandForced(String... args) throws Exception {
runCommand(ObjectArrays.concat("--force", args));
}
+ /** Adds "--force" as the first parameter, then runs the command. */
+ protected void runCommandForced(Iterable args) throws Exception {
+ runCommand(concat(ImmutableList.of("--force"), args));
+ }
+
/** Writes the data to a named temporary file and then returns a path to the file. */
String writeToNamedTmpFile(String filename, byte[] data) throws IOException {
File tmpFile = tmpDir.newFile(filename);
diff --git a/javatests/google/registry/tools/EppToolVerifier.java b/javatests/google/registry/tools/EppToolVerifier.java
index bd17335ed..6750fa693 100644
--- a/javatests/google/registry/tools/EppToolVerifier.java
+++ b/javatests/google/registry/tools/EppToolVerifier.java
@@ -14,12 +14,14 @@
package google.registry.tools;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.xml.XmlTestUtils.assertXmlEquals;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
@@ -27,6 +29,7 @@ import com.google.common.net.MediaType;
import google.registry.tools.ServerSideCommand.Connection;
import google.registry.tools.server.ToolsTestData;
import java.net.URLDecoder;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.mockito.ArgumentCaptor;
@@ -67,29 +70,34 @@ public class EppToolVerifier {
return new EppToolVerifier(connection, clientId, superuser, true);
}
- void verifySent(String... xmlToMatch) throws Exception {
+ void verifySent(String... expectedXmlFiles) throws Exception {
+ verifySentContents(
+ Arrays.stream(expectedXmlFiles).map(ToolsTestData::loadUtf8).collect(toImmutableList()));
+ }
+
+ void verifySentContents(List expectedXmlContents) throws Exception {
ArgumentCaptor params = ArgumentCaptor.forClass(byte[].class);
- verify(connection, times(xmlToMatch.length)).send(
+ verify(connection, times(expectedXmlContents.size())).send(
eq("/_dr/epptool"),
eq(ImmutableMap.of()),
eq(MediaType.FORM_DATA),
params.capture());
List capturedParams = params.getAllValues();
- assertThat(capturedParams).hasSize(xmlToMatch.length);
- for (int i = 0; i < xmlToMatch.length; i++) {
- String xml = xmlToMatch[i];
+ assertThat(capturedParams).hasSize(expectedXmlContents.size());
+ for (int i = 0; i < expectedXmlContents.size(); i++) {
byte[] capturedParam = capturedParams.get(i);
Map map =
- Splitter.on('&')
- .withKeyValueSeparator('=')
- .split(new String(capturedParam, UTF_8));
+ Splitter.on('&').withKeyValueSeparator('=').split(new String(capturedParam, UTF_8));
assertThat(map).hasSize(4);
assertXmlEquals(
- ToolsTestData.loadUtf8(xml),
- URLDecoder.decode(map.get("xml"), UTF_8.toString()));
+ expectedXmlContents.get(i), URLDecoder.decode(map.get("xml"), UTF_8.toString()));
assertThat(map).containsEntry("dryRun", Boolean.toString(dryRun));
assertThat(map).containsEntry("clientId", clientId);
assertThat(map).containsEntry("superuser", Boolean.toString(superuser));
}
}
+
+ void verifyNothingSent() {
+ verifyZeroInteractions(connection);
+ }
}
diff --git a/javatests/google/registry/tools/LockDomainCommandTest.java b/javatests/google/registry/tools/LockDomainCommandTest.java
new file mode 100644
index 000000000..b6714c659
--- /dev/null
+++ b/javatests/google/registry/tools/LockDomainCommandTest.java
@@ -0,0 +1,112 @@
+// Copyright 2017 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.tools;
+
+import static com.google.common.truth.Truth.assertThat;
+import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED;
+import static google.registry.testing.DatastoreHelper.newDomainResource;
+import static google.registry.testing.DatastoreHelper.persistActiveDomain;
+import static google.registry.testing.DatastoreHelper.persistResource;
+import static google.registry.testing.JUnitBackports.expectThrows;
+import static google.registry.testing.TestDataHelper.applySubstitutions;
+import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
+import static google.registry.tools.server.ToolsTestData.loadUtf8;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+
+/** Unit tests for {@link LockDomainCommand}. */
+public class LockDomainCommandTest extends EppToolCommandTestCase {
+
+ /** Gets an overridden eppVerifier that has superuser set to true on it. */
+ @Override
+ EppToolVerifier eppVerifier() {
+ return new EppToolVerifier()
+ .withConnection(connection)
+ .withClientId("NewRegistrar")
+ .asSuperuser();
+ }
+
+ @Test
+ public void testSuccess_sendsCorrectEppXml() throws Exception {
+ persistActiveDomain("example.tld");
+ runCommandForced("--client=NewRegistrar", "example.tld");
+ eppVerifier()
+ .verifySentContents(
+ ImmutableList.of(
+ loadUtf8("domain_lock.xml", ImmutableMap.of("DOMAIN", "example.tld"))));
+ }
+
+ @Test
+ public void testSuccess_partiallyUpdatesStatuses() throws Exception {
+ persistResource(
+ newDomainResource("example.tld")
+ .asBuilder()
+ .addStatusValue(SERVER_TRANSFER_PROHIBITED)
+ .build());
+ runCommandForced("--client=NewRegistrar", "example.tld");
+ eppVerifier().verifySent("domain_lock_partial_statuses.xml");
+ }
+
+ @Test
+ public void testSuccess_manyDomains() throws Exception {
+ List params = new ArrayList<>();
+ List expectedXmls = new ArrayList<>();
+ params.add("--client=NewRegistrar");
+ String updateDomainXml = loadUtf8("domain_lock.xml");
+ // Create 26 domains -- one more than the number of entity groups allowed in a transaction (in
+ // case that was going to be the failure point).
+ for (int n = 0; n < 26; n++) {
+ String domain = String.format("domain%d.tld", n);
+ persistActiveDomain(domain);
+ params.add(domain);
+ expectedXmls.add(applySubstitutions(updateDomainXml, ImmutableMap.of("DOMAIN", domain)));
+ }
+ runCommandForced(params);
+ eppVerifier().verifySentContents(expectedXmls);
+ }
+
+ @Test
+ public void testFailure_domainDoesntExist() throws Exception {
+ IllegalArgumentException e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> runCommandForced("--client=NewRegistrar", "missing.tld"));
+ assertThat(e).hasMessageThat().isEqualTo("Domain 'missing.tld' does not exist");
+ }
+
+ @Test
+ public void testSuccess_alreadyLockedDomain_performsNoAction() throws Exception {
+ persistResource(
+ newDomainResource("example.tld")
+ .asBuilder()
+ .addStatusValues(REGISTRY_LOCK_STATUSES)
+ .build());
+ runCommandForced("--client=NewRegistrar", "example.tld");
+ eppVerifier().verifyNothingSent();
+ }
+
+ @Test
+ public void testFailure_duplicateDomainsAreSpecified() throws Exception {
+ IllegalArgumentException e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> runCommandForced("--client=NewRegistrar", "dupe.tld", "dupe.tld"));
+ assertThat(e).hasMessageThat().isEqualTo("Duplicate domain arguments found: 'dupe.tld'");
+ }
+}
diff --git a/javatests/google/registry/tools/UpdateDomainCommandTest.java b/javatests/google/registry/tools/UpdateDomainCommandTest.java
index bee5e7b07..43c68f104 100644
--- a/javatests/google/registry/tools/UpdateDomainCommandTest.java
+++ b/javatests/google/registry/tools/UpdateDomainCommandTest.java
@@ -14,10 +14,13 @@
package google.registry.tools;
+import static com.google.common.truth.Truth.assertThat;
+import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
import static google.registry.testing.DatastoreHelper.newContactResource;
import static google.registry.testing.DatastoreHelper.newDomainResource;
import static google.registry.testing.DatastoreHelper.persistActiveHost;
import static google.registry.testing.DatastoreHelper.persistResource;
+import static google.registry.testing.JUnitBackports.expectThrows;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableSet;
@@ -151,7 +154,7 @@ public class UpdateDomainCommandTest extends EppToolCommandTestCase> nameservers = ImmutableSet.of(Key.create(host));
+ persistResource(
+ newDomainResource("example.tld")
+ .asBuilder()
+ .setStatusValues(ImmutableSet.of(SERVER_UPDATE_PROHIBITED))
+ .setNameservers(nameservers)
+ .build());
+
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ runCommandForced(
+ "--client=NewRegistrar",
+ "--statuses=clientRenewProhibited,serverHold",
+ "--superuser",
+ "example.tld"));
+ assertThat(e)
+ .hasMessageThat()
+ .containsMatch("The domain 'example.tld' has status SERVER_UPDATE_PROHIBITED");
+ eppVerifier().verifyNothingSent();
+ }
+
@Test
public void testFailure_duplicateDomains() throws Exception {
thrown.expect(IllegalArgumentException.class, "Duplicate arguments found");
diff --git a/javatests/google/registry/tools/server/ToolsTestData.java b/javatests/google/registry/tools/server/ToolsTestData.java
index 82116f2f6..a946e5c0b 100644
--- a/javatests/google/registry/tools/server/ToolsTestData.java
+++ b/javatests/google/registry/tools/server/ToolsTestData.java
@@ -14,12 +14,12 @@
package google.registry.tools.server;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
+import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteSource;
import com.google.common.io.Resources;
-import java.io.IOException;
+import google.registry.testing.TestDataHelper;
import java.net.URL;
+import java.util.Map;
/** Utility class providing easy access to contents of the {@code testdata/} directory. */
public final class ToolsTestData {
@@ -30,12 +30,17 @@ public final class ToolsTestData {
}
/**
- * Loads data from file in {@code tools/server/testdata/} as a String (assuming file is UTF-8).
- *
- * @throws IOException if the file couldn't be loaded from the jar.
+ * Loads data from file in {@code tools/server/testdata/} as a UTF-8 String.
*/
- public static String loadUtf8(String filename) throws IOException {
- return Resources.asCharSource(getUrl(filename), UTF_8).read();
+ public static String loadUtf8(String filename) {
+ return loadUtf8(filename, ImmutableMap.of());
+ }
+
+ /**
+ * Loads data from file in {@code tools/server/testdata/} as a UTF-8 String, with substitutions.
+ */
+ public static String loadUtf8(String filename, Map substitutions) {
+ return TestDataHelper.loadFileWithSubstitutions(ToolsTestData.class, filename, substitutions);
}
private static URL getUrl(String filename) {
diff --git a/javatests/google/registry/tools/server/testdata/domain_lock.xml b/javatests/google/registry/tools/server/testdata/domain_lock.xml
new file mode 100644
index 000000000..aa1329a20
--- /dev/null
+++ b/javatests/google/registry/tools/server/testdata/domain_lock.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %DOMAIN%
+
+
+
+
+
+
+
+ RegistryTool
+
+
diff --git a/javatests/google/registry/tools/server/testdata/domain_lock_partial_statuses.xml b/javatests/google/registry/tools/server/testdata/domain_lock_partial_statuses.xml
new file mode 100644
index 000000000..0a4843905
--- /dev/null
+++ b/javatests/google/registry/tools/server/testdata/domain_lock_partial_statuses.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ example.tld
+
+
+
+
+
+
+ RegistryTool
+
+
diff --git a/javatests/google/registry/tools/server/testdata/domain_update_set_statuses.xml b/javatests/google/registry/tools/server/testdata/domain_update_set_statuses.xml
index 707274e3d..6bdb3352d 100644
--- a/javatests/google/registry/tools/server/testdata/domain_update_set_statuses.xml
+++ b/javatests/google/registry/tools/server/testdata/domain_update_set_statuses.xml
@@ -9,7 +9,7 @@
-
+