mv com/google/domain/registry google/registry

This change renames directories in preparation for the great package
rename. The repository is now in a broken state because the code
itself hasn't been updated. However this should ensure that git
correctly preserves history for each file.
This commit is contained in:
Justine Tunney 2016-05-13 18:55:08 -04:00
parent a41677aea1
commit 5012893c1d
2396 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,73 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
# Needed for the documentation tests
filegroup(
name = "flows_files",
srcs = glob([
"*.java",
"**/*.java",
]),
)
java_library(
name = "flows",
srcs = glob([
"*.java",
"**/*.java",
]),
resources = glob(["**/testdata/*.xml"]),
deps = [
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/io",
"//java/com/google/common/net",
"//java/com/google/domain/registry/config",
"//java/com/google/domain/registry/dns",
"//java/com/google/domain/registry/flows",
"//java/com/google/domain/registry/mapreduce",
"//java/com/google/domain/registry/model",
"//java/com/google/domain/registry/monitoring/whitebox",
"//java/com/google/domain/registry/request",
"//java/com/google/domain/registry/security",
"//java/com/google/domain/registry/security:servlets",
"//java/com/google/domain/registry/tmch",
"//java/com/google/domain/registry/util",
"//java/com/google/domain/registry/xml",
"//javatests/com/google/domain/registry/model",
"//javatests/com/google/domain/registry/testing",
"//javatests/com/google/domain/registry/testing/mapreduce",
"//javatests/com/google/domain/registry/xml",
"//third_party/java/appengine:appengine-api-testonly",
"//third_party/java/appengine:appengine-testing",
"//third_party/java/joda_money",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//third_party/java/truth",
],
)
# If the flows tests should grow again to the point that they last longer than
# sixty seconds, then shard_count should be tuned. You can binary search for a
# good value that balances time reduction with environmental impact. However,
# any unit test that contains fewer @Test methods than the shard count will
# If you grep for testNothing you can find the existing dummy methods.
GenTestRules(
name = "GeneratedTestRules",
default_test_size = "medium",
jvm_flags = ["-XX:MaxPermSize=256m"],
shard_count = 4,
test_files = glob([
"*Test.java",
"*/*Test.java",
]),
deps = [":flows"],
)

View file

@ -0,0 +1,65 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.UserInfo;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
/** Tests for {@link EppConsoleServlet} running in admin mode. */
@RunWith(MockitoJUnitRunner.class)
public class EppConsoleAsAdminServletTest extends EppServletXmlLoginTestCase<EppConsoleServlet> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.withUserService(UserInfo.createAdmin(GAE_USER_EMAIL, GAE_USER_ID))
.build();
private static final String GAE_USER_ID = "12345";
private static final String GAE_USER_EMAIL = "someone@example.com";
// Note that the setup done in EppConsoleServletTest, of allowing
// the test user to login as the Registrar, is not done here.
@Before
public void initTest() throws Exception {
persistResource(
Registrar.loadByClientId("NewRegistrar").asBuilder().setPassword("PwAdminDNKnow").build());
}
@Test
public void testNonAuthedLogin() throws Exception {
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
}
@Test
public void testMultiLogin() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
}
}

View file

@ -0,0 +1,71 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registrar.RegistrarContact;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.UserInfo;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
/** Tests for {@link EppConsoleServlet}. */
@RunWith(MockitoJUnitRunner.class)
public class EppConsoleServletTest extends EppServletXmlLoginTestCase<EppConsoleServlet> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.withUserService(UserInfo.create(GAE_USER_EMAIL, GAE_USER_ID))
.build();
private static final String GAE_USER_ID = "12345";
private static final String GAE_USER_EMAIL = "person@example.com";
@Before
public void initTest() throws Exception {
Registrar registrar = Registrar.loadByClientId("NewRegistrar");
RegistrarContact contact = new RegistrarContact.Builder()
.setParent(registrar)
.setEmailAddress(GAE_USER_EMAIL)
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
.setGaeUserId(GAE_USER_ID)
.build();
persistResource(contact);
}
@Test
public void testNonAuthedLogin() throws Exception {
assertCommandAndResponse("login2_valid.xml", "login_response_unauthorized_role.xml");
}
@Test
public void testMultiLogin() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login2_valid.xml", "login_response_unauthorized_role.xml");
}
}

View file

@ -0,0 +1,50 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.domain.registry.flows.EppController.getErrorResponse;
import static com.google.domain.registry.flows.EppXmlTransformer.marshal;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.eppoutput.Result;
import com.google.domain.registry.model.eppoutput.Result.Code;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.xml.ValidationMode;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link EppController}. */
@RunWith(JUnit4.class)
public class EppControllerTest {
@Rule
public AppEngineRule appEngineRule = new AppEngineRule.Builder().build();
@Test
public void testMarshallingUnknownError() throws Exception {
marshal(
getErrorResponse(Result.create(Code.CommandFailed), Trid.create(null)),
ValidationMode.STRICT);
}
// Extra methods so the test runner doesn't produce empty shards.
@Test public void testNothing1() {}
@Test public void testNothing2() {}
@Test public void testNothing3() {}
}

View file

@ -0,0 +1,308 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
import static com.google.domain.registry.security.XsrfTokenManager.generateToken;
import static com.google.domain.registry.testing.DatastoreHelper.createTlds;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TestDataHelper.loadFileWithSubstitutions;
import static com.google.domain.registry.xml.XmlTestUtils.assertXmlEqualsWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.joda.time.DateTimeZone.UTC;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.appengine.api.modules.ModulesService;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
import com.google.domain.registry.monitoring.whitebox.Metrics;
import com.google.domain.registry.security.XsrfProtectedServlet;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.FakeServletInputStream;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.util.BasicHttpSession;
import com.google.domain.registry.util.TypeUtils.TypeInstantiator;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.ByteArrayOutputStream;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Test setup for all EppServletTest subclasses.
*
* @param <S> The EppXXXServlet class to test.
*/
public abstract class EppServletTestCase<S extends HttpServlet> {
@Rule
public final InjectRule inject = new InjectRule();
@Mock
HttpServletRequest req;
@Mock
HttpServletResponse rsp;
@Mock
ModulesService modulesService;
HttpSession session;
FakeClock clock = new FakeClock();
private String currentTld = null;
private Optional<Boolean> isSuperuser = Optional.<Boolean> absent();
private Optional<String> clientIdentifier = Optional.<String> absent();
void setSuperuser(boolean isSuperuser) {
this.isSuperuser = Optional.of(isSuperuser);
}
void setClientIdentifier(String clientIdentifier) {
this.clientIdentifier = Optional.of(clientIdentifier);
}
static final DateTime START_OF_GA = DateTime.parse("2014-03-01T00:00:00Z");
@Before
public final void init() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock); // For transactional flows.
inject.setStaticField(FlowRunner.class, "clock", clock); // For non-transactional flows.
inject.setStaticField(Metrics.class, "modulesService", modulesService);
when(modulesService.getVersionHostname("backend", null)).thenReturn("backend.hostname");
// Create RegistryData for all TLDs used in these tests.
// We want to create all of these even for tests that don't use them to make sure that
// tld-selection works correctly.
createTlds("net", "xn--q9jyb4c", "example");
ofy().saveWithoutBackup().entity(new ClaimsListSingleton()).now();
session = new BasicHttpSession();
persistResource(
Registrar.loadByClientId("NewRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.of("net", "example", "xn--q9jyb4c"))
.build());
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.of("net", "example", "xn--q9jyb4c"))
.build());
}
void assertCommandAndResponse(
String inputFilename,
Map<String, String> inputSubstitutions,
String outputFilename,
Map<String, String> outputSubstitutions) throws Exception {
assertCommandAndResponse(
inputFilename,
inputSubstitutions,
outputFilename,
outputSubstitutions,
DateTime.now(UTC));
}
String assertCommandAndResponse(String inputFilename, String outputFilename) throws Exception {
return assertCommandAndResponse(inputFilename, outputFilename, DateTime.now(UTC));
}
String assertCommandAndResponse(
String inputFilename,
Map<String, String> inputSubstitutions,
String outputFilename,
Map<String, String> outputSubstitutions,
String nowString) throws Exception {
return assertCommandAndResponse(
inputFilename,
inputSubstitutions,
outputFilename,
outputSubstitutions,
DateTime.parse(nowString));
}
String assertCommandAndResponse(String inputFilename, String outputFilename, String nowString)
throws Exception {
return assertCommandAndResponse(inputFilename, outputFilename, DateTime.parse(nowString));
}
String assertCommandAndResponse(String inputFilename, String outputFilename, DateTime now)
throws Exception {
return assertCommandAndResponse(inputFilename, null, outputFilename, null, now);
}
String assertCommandAndResponse(
String inputFilename,
Map<String, String> inputSubstitutions,
String outputFilename,
Map<String, String> outputSubstitutions,
DateTime now) throws Exception {
String outputFile =
loadFileWithSubstitutions(EppServletTestCase.class, outputFilename, outputSubstitutions);
String actualOutput = expectXmlCommand(loadFileWithSubstitutions(
EppServletTestCase.class, inputFilename, inputSubstitutions), now);
assertXmlEqualsWithMessage(
outputFile,
actualOutput,
"Running " + inputFilename + " => " + outputFilename,
"epp.response.resData.infData.roid",
"epp.response.trID.svTRID");
ofy().clearSessionCache(); // Clear the cache like OfyFilter would.
return actualOutput;
}
HttpSession getOrRenewSession() {
// Try an idempotent op on the session to see if it's valid.
try {
session.getAttribute(null);
return session;
} catch (IllegalStateException e) {
// Session is invalid.
session = new BasicHttpSession();
return session;
}
}
@SuppressWarnings("resource")
String expectXmlCommand(String inputFile, DateTime now) throws Exception {
clock.setTo(now); // Makes Ofy use 'now' as its time
reset(req, rsp);
HttpServlet servlet = new TypeInstantiator<S>(getClass()){}.instantiate();
if (servlet instanceof XsrfProtectedServlet) {
when(req.getHeader(X_CSRF_TOKEN))
.thenReturn(generateToken(((XsrfProtectedServlet) servlet).getScope()));
}
when(req.getInputStream()).thenReturn(new FakeServletInputStream(inputFile.getBytes(UTF_8)));
when(req.getParameter("xml")).thenReturn(inputFile);
if (isSuperuser.isPresent()) {
when(req.getParameter("superuser")).thenReturn(isSuperuser.get().toString());
}
if (clientIdentifier.isPresent()) {
when(req.getParameter("clientIdentifier")).thenReturn(clientIdentifier.get());
}
when(req.getParameter("tld")).thenReturn(currentTld);
when(req.getServletPath()).thenReturn("");
when(req.getMethod()).thenReturn("POST");
when(req.getHeader("X-Requested-With")).thenReturn("XMLHttpRequest");
when(req.getSession(true)).thenAnswer(new Answer<HttpSession>() {
@Override
public HttpSession answer(InvocationOnMock invocation) {
return getOrRenewSession();
}});
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
when(rsp.getOutputStream()).thenReturn(new ServletOutputStream() {
@Override
public void write(int b) {
byteArrayOutputStream.write(b);
}});
extendedSessionConfig(inputFile);
servlet.init(mock(ServletConfig.class));
servlet.service(req, rsp);
verify(rsp).setStatus(HttpServletResponse.SC_OK);
String result = new String(byteArrayOutputStream.toByteArray(), UTF_8);
// Run the resulting xml through the unmarshaller to verify that it was valid.
EppXmlTransformer.validateOutput(result);
return result;
}
/** Create the two administrative contacts and two hosts that are used by a lot of our tests. */
protected void createContactsAndHosts() throws Exception {
DateTime startTime = DateTime.parse("2000-06-01T00:00:00Z");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
startTime);
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
startTime.plusMinutes(1));
assertCommandAndResponse(
"host_create.xml",
"host_create_response.xml",
startTime.plusMinutes(2));
assertCommandAndResponse(
"host_create2.xml",
"host_create2_response.xml",
startTime.plusMinutes(3));
}
/**
* Creates the domain fakesite.example with two nameservers on it.
*/
protected void createFakesite() throws Exception {
createContactsAndHosts();
assertCommandAndResponse(
"domain_create_fakesite.xml",
"domain_create_response_fakesite.xml",
"2000-06-01T00:04:00Z");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_ok.xml",
"2000-06-06T00:00:00Z");
}
// Adds ns3.fakesite.example as a host, then adds it to fakesite.
protected void createSubordinateHost() throws Exception {
// Add the fakesite nameserver (requires that domain is already created).
assertCommandAndResponse(
"host_create_fakesite.xml",
"host_create_response_fakesite.xml",
"2000-06-06T00:01:00Z");
// Add new nameserver to domain.
assertCommandAndResponse(
"domain_update_add_nameserver_fakesite.xml",
"domain_update_add_nameserver_response_fakesite.xml",
"2000-06-08T00:00:00Z");
// Verify new nameserver was added.
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_3_nameservers.xml",
"2000-06-08T00:01:00Z");
// Verify that nameserver's data was set correctly.
assertCommandAndResponse(
"host_info_fakesite.xml",
"host_info_response_fakesite.xml",
"2000-06-08T00:02:00Z");
}
/** For subclasses to further setup the session. */
protected void extendedSessionConfig(
@SuppressWarnings("unused") String inputFile) throws Exception {}
}

View file

@ -0,0 +1,355 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static com.google.domain.registry.util.ResourceUtils.readResourceUtf8;
import static com.google.domain.registry.xml.XmlTestUtils.assertXmlEquals;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.util.DateTimeUtils;
import org.joda.time.DateTime;
import org.junit.Test;
import javax.servlet.http.HttpServlet;
/**
* Test setup for EppServletTest subclasses which use XML-based authentication.
*
* @param <S> The EppXXXServlet class to test.
*/
public abstract class EppServletXmlLoginTestCase<S extends HttpServlet> extends
EppServletTestCase<S> {
@Test
public void testHello() throws Exception {
assertXmlEquals(
readResourceUtf8(getClass(), "testdata/greeting_crr.xml"),
expectXmlCommand(readResourceUtf8(getClass(), "testdata/hello.xml"), DateTime.now(UTC)),
"epp.greeting.svDate");
}
@Test
public void testLoginLogout() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testPdtLogin() throws Exception {
assertCommandAndResponse("pdt_login.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testSyntaxError() throws Exception {
assertCommandAndResponse("syntax_error.xml", "syntax_error_response.xml");
}
@Test
public void testContactLifecycle() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse(
"contact_info.xml",
"contact_info_from_create_response.xml",
"2000-06-01T00:01:00Z");
assertCommandAndResponse("contact_delete_sh8013.xml", "contact_delete_response_sh8013.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainDeleteRestore() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create contacts sh8013 and jd1234.
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
"2000-06-01T00:01:00Z");
// Create domain example.tld.
assertCommandAndResponse(
"domain_create_no_hosts_or_dsdata.xml",
"domain_create_response.xml",
"2000-06-01T00:02:00Z");
// Delete domain example.com after its add grace period has expired.
assertCommandAndResponse(
"domain_delete.xml",
"generic_success_action_pending_response.xml",
"2000-07-01T00:02:00Z");
// Restore the domain.
assertCommandAndResponse(
"domain_update_restore_request.xml",
"domain_update_restore_request_response.xml",
"2000-07-01T00:03:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainDeletion_withinAddGracePeriod() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create contacts sh8013 and jd1234.
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
"2000-06-01T00:01:00Z");
// Create domain example.tld.
assertCommandAndResponse(
"domain_create_no_hosts_or_dsdata.xml",
"domain_create_response.xml",
"2000-06-01T00:02:00Z");
// Delete domain example.tld after its add grace period has expired.
assertCommandAndResponse(
"domain_delete.xml",
"generic_success_action_pending_response.xml",
"2000-07-01T00:02:00Z");
// Poke the domain a little at various times to see its status
assertCommandAndResponse(
"domain_info.xml",
"domain_info_response_pendingdelete.xml",
"2000-08-01T00:02:00Z"); // 1 day out.
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainDeletionWithSubordinateHost_fails() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
createSubordinateHost();
assertCommandAndResponse(
"domain_delete_fakesite.xml",
"domain_delete_response_prohibited.xml",
"2002-05-30T01:01:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDeletionOfDomain_afterRenameOfSubordinateHost_succeeds() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
createSubordinateHost();
// Update the ns3 host to no longer be on fakesite.example domain.
assertCommandAndResponse(
"host_update_fakesite.xml",
"generic_success_response.xml",
"2002-05-30T01:01:00Z");
// Delete the fakesite.example domain (which should succeed since it no longer has subords).
assertCommandAndResponse(
"domain_delete_fakesite.xml",
"generic_success_action_pending_response.xml",
"2002-05-30T01:02:00Z");
// Check info on the renamed host and verify that it's still around and wasn't deleted.
assertCommandAndResponse(
"host_info_ns9000_example.xml",
"host_info_response_ns9000_example.xml",
"2002-06-30T01:03:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDeletionOfDomain_afterUpdateThatCreatesSubordinateHost_fails() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
// Update the ns1 host to be on the fakesite.example domain.
assertCommandAndResponse(
"host_update_ns1_to_fakesite.xml",
"generic_success_response.xml",
"2002-05-30T01:01:00Z");
// Attempt to delete the fakesite.example domain (which should fail since it now has a
// subordinate host).
assertCommandAndResponse(
"domain_delete_fakesite.xml",
"domain_delete_response_prohibited.xml",
"2002-05-30T01:02:00Z");
// Check info on the renamed host and verify that it's still around and wasn't deleted.
assertCommandAndResponse(
"host_info_fakesite.xml",
"host_info_response_fakesite_post_update.xml",
"2002-06-30T01:03:00Z");
// Verify that fakesite.example domain is still around and wasn't deleted.
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_ok_post_host_update.xml",
"2002-05-30T01:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testRenamingHostToExistingHost_fails() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create the two hosts.
assertCommandAndResponse(
"host_create.xml",
"host_create_response.xml",
"2000-06-01T00:02:00Z");
assertCommandAndResponse(
"host_create2.xml",
"host_create2_response.xml",
"2000-06-01T00:03:00Z");
// Verify that host1 and host2 were created as we expect them.
assertCommandAndResponse(
"host_info_ns1.xml",
"host_info_response_ns1.xml",
"2000-06-01T00:04:00Z");
assertCommandAndResponse(
"host_info_ns2.xml",
"host_info_response_ns2.xml",
"2000-06-01T00:05:00Z");
// Attempt overwriting of host1 on top of host2 (and verify that it fails).
assertCommandAndResponse(
"host_update_ns1_to_ns2.xml",
"host_update_failed_response.xml",
"2000-06-01T00:06:00Z");
// Verify that host1 and host2 still exist in their unmodified states.
assertCommandAndResponse(
"host_info_ns1.xml",
"host_info_response_ns1.xml",
"2000-06-01T00:07:00Z");
assertCommandAndResponse(
"host_info_ns2.xml",
"host_info_response_ns2.xml",
"2000-06-01T00:08:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testApplicationDuringSunrise_doesntCreateDomainWithoutAllocation() throws Exception {
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
DateTimeUtils.START_OF_TIME, TldState.SUNRISE,
START_OF_GA, TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createContactsAndHosts();
// Note that the trademark is valid from 2013-08-09 to 2017-07-23, hence the domain creation
// in 2014.
assertCommandAndResponse(
"domain_create_sunrise_encoded_mark.xml",
"domain_create_sunrise_encoded_signed_mark_response.xml",
"2014-01-01T00:00:00Z");
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_doesnt_exist.xml",
"2014-01-01T00:01:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainCreation_failsBeforeSunrise() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
DateTime sunriseDate = DateTime.parse("2000-05-30T00:00:00Z");
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
sunriseDate, TldState.SUNRISE,
sunriseDate.plusMonths(2), TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
setClientIdentifier("NewRegistrar");
createContactsAndHosts();
assertCommandAndResponse(
"domain_create_sunrise_encoded_mark.xml",
"domain_create_testvalidate_invalid_phase.xml",
sunriseDate.minusDays(1));
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_doesnt_exist.xml",
sunriseDate.plusDays(1));
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainCheckFee_succeeds() throws Exception {
assertCommandAndResponse("login_valid_fee_extension.xml", "login_response.xml");
DateTime gaDate = DateTime.parse("2000-05-30T00:00:00Z");
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
gaDate, TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
assertCommandAndResponse(
"domain_check_fee_premium.xml",
"domain_check_fee_premium_response.xml",
gaDate.plusDays(1));
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testRemoteXmlExternalEntity() throws Exception {
// Check go/XXE
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_remote_xxe.xml",
"contact_create_remote_response_xxe.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testLocalXmlExternalEntity() throws Exception {
// Check go/XXE
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_local_xxe.xml",
"contact_create_local_response_xxe.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testBillionLaughsAttack() throws Exception {
// Check go/XXE
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_billion_laughs.xml",
"contact_create_response_billion_laughs.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
}

View file

@ -0,0 +1,510 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.CertificateSamples;
import com.google.domain.registry.testing.FakeServletInputStream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** Test setup for EppServletTest subclasses. */
@RunWith(MockitoJUnitRunner.class)
public class EppTlsServletTest extends EppServletXmlLoginTestCase<EppTlsServlet> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
String ipAddressAndPort = "192.168.1.100:54321";
String clientCert = CertificateSamples.SAMPLE_CERT_HASH;
String clientCert2 = CertificateSamples.SAMPLE_CERT2_HASH;
String requestedServername = "test.example";
private String gfeRequestClientCertificateHashField;
@Before
public void initTest() throws Exception {
persistResource(Registrar.loadByClientId("NewRegistrar")
.asBuilder()
.setClientCertificateHash(clientCert)
.build());
persistResource(Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setClientCertificateHash(clientCert2)
.build());
}
@Test
public void testSetTldViaSni() throws Exception {
requestedServername = "epp.nic.xn--q9jyb4c";
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse(
"domain_create_minna.xml",
"domain_create_response_minna.xml",
"2000-06-01T01:02:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
/** This test requires multiple registrars, which EppConsoleServlet doesn't allow. */
@Test
public void testContactTransferPollMessage() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Initiate a transfer of the newly created contact.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_transfer_request.xml",
"contact_transfer_request_response_alternate.xml",
"2000-06-08T22:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in with the losing registrar, read the poll message, and then ack it.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"poll.xml",
"poll_response_contact_transfer.xml",
"2000-06-08T22:01:00Z");
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "2-4-ROID-6-7"),
"poll_ack_response_empty.xml",
null,
"2000-06-08T22:02:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
/** This test requires multiple registrars, which EppConsoleServlet doesn't allow. */
@Test
public void testDomainTransferPollMessage_serverApproved() throws Exception {
// As the losing registrar, create the domain.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
assertCommandAndResponse("logout.xml", "logout_response.xml");
// As the winning registrar, request a transfer. Capture the server trid; we'll need it later.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
String response = assertCommandAndResponse(
"domain_transfer_request_1_year.xml",
"domain_transfer_response_1_year.xml",
"2001-01-01T00:00:00Z");
Matcher matcher = Pattern.compile("<svTRID>(.*)</svTRID>").matcher(response);
matcher.find();
String transferRequestTrid = matcher.group(1);
assertCommandAndResponse("logout.xml", "logout_response.xml");
// As the losing registrar, read the request poll message, and then ack it.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"poll.xml",
"poll_response_domain_transfer_request.xml",
"2001-01-01T00:01:00Z");
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-C-EXAMPLE-18-22"),
"poll_ack_response_empty.xml",
null,
"2001-01-01T00:01:00Z");
// Five days in the future, expect a server approval poll message to the loser, and ack it.
assertCommandAndResponse(
"poll.xml",
"poll_response_domain_transfer_server_approve_loser.xml",
"2001-01-06T00:01:00Z");
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-C-EXAMPLE-18-24"),
"poll_ack_response_empty.xml",
null,
"2001-01-06T00:01:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Also expect a server approval poll message to the winner, with the transfer request trid.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"poll.xml",
null,
"poll_response_domain_transfer_server_approve_winner.xml",
ImmutableMap.of("SERVER_TRID", transferRequestTrid),
"2001-01-06T00:02:00Z");
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-C-EXAMPLE-18-23"),
"poll_ack_response_empty.xml",
null,
"2001-01-06T00:02:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Override
protected void extendedSessionConfig(String inputFile) throws Exception {
when(req.getHeader(EppTlsServlet.REQUESTED_SERVERNAME_VIA_SNI_FIELD))
.thenReturn(requestedServername);
when(req.getHeader(EppTlsServlet.FORWARDED_FOR_FIELD))
.thenReturn(ipAddressAndPort);
if (gfeRequestClientCertificateHashField != null) {
when(req.getHeader(EppTlsServlet.SSL_CLIENT_CERTIFICATE_HASH_FIELD))
.thenReturn(gfeRequestClientCertificateHashField);
} else {
when(req.getHeader(EppTlsServlet.SSL_CLIENT_CERTIFICATE_HASH_FIELD))
.thenReturn(inputFile.contains("TheRegistrar") ? clientCert2 : clientCert);
}
when(req.getInputStream()).thenReturn(new FakeServletInputStream(inputFile.getBytes(UTF_8)));
}
@Test
public void testIgnoredTransferDuringAutoRenewPeriod_succeeds() throws Exception {
// Register the domain as the first registrar.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_2_years.xml",
"domain_transfer_response_2_years.xml",
"2002-05-30T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as the first registrar and verify things.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_pending_transfer.xml",
"2002-05-30T01:00:00Z");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_pending_transfer_autorenew.xml",
"2002-06-02T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as the second registrar and verify transfer details.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
// Verify that domain is in the transfer period now with expiration date two years out.
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_transfer_period.xml",
"2002-06-06T00:00:00Z");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_transfer_complete.xml",
"2002-06-12T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testBadCertificate_failsBadCertificate2200() throws Exception {
gfeRequestClientCertificateHashField = "laffo";
assertCommandAndResponse("login_valid.xml", "login_response_bad_certificate.xml");
}
@Test
public void testGfeDidntProvideClientCertificate_failsMissingCertificate2200() throws Exception {
gfeRequestClientCertificateHashField = "";
assertCommandAndResponse("login_valid.xml", "login_response_missing_certificate.xml");
}
@Test
public void testGoodPrimaryCertificate() throws Exception {
gfeRequestClientCertificateHashField = CertificateSamples.SAMPLE_CERT_HASH;
persistResource(Registrar.loadByClientId("NewRegistrar").asBuilder()
.setClientCertificate(CertificateSamples.SAMPLE_CERT, clock.nowUtc())
.setFailoverClientCertificate(CertificateSamples.SAMPLE_CERT2, clock.nowUtc())
.build());
assertCommandAndResponse("login_valid.xml", "login_response.xml");
}
@Test
public void testGoodFailoverCertificate() throws Exception {
gfeRequestClientCertificateHashField = CertificateSamples.SAMPLE_CERT2_HASH;
persistResource(Registrar.loadByClientId("NewRegistrar").asBuilder()
.setClientCertificate(CertificateSamples.SAMPLE_CERT, clock.nowUtc())
.setFailoverClientCertificate(CertificateSamples.SAMPLE_CERT2, clock.nowUtc())
.build());
assertCommandAndResponse("login_valid.xml", "login_response.xml");
}
@Test
public void testMissingPrimaryCertificateButHasFailover_usesFailover() throws Exception {
gfeRequestClientCertificateHashField = CertificateSamples.SAMPLE_CERT2_HASH;
persistResource(Registrar.loadByClientId("NewRegistrar").asBuilder()
.setClientCertificate(null, clock.nowUtc())
.setFailoverClientCertificate(CertificateSamples.SAMPLE_CERT2, clock.nowUtc())
.build());
assertCommandAndResponse("login_valid.xml", "login_response.xml");
}
@Test
public void testRegistrarHasNoCertificatesOnFile_disablesCertChecking() throws Exception {
gfeRequestClientCertificateHashField = "laffo";
persistResource(Registrar.loadByClientId("NewRegistrar").asBuilder()
.setClientCertificate(null, clock.nowUtc())
.setFailoverClientCertificate(null, clock.nowUtc())
.build());
assertCommandAndResponse("login_valid.xml", "login_response.xml");
}
@Test
public void testNameserversTransferWithDomain_successfully() throws Exception {
// Log in as the first registrar and set up domains with hosts.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
createSubordinateHost();
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_2_years.xml",
"domain_transfer_response_2_years.xml",
"2002-05-30T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as the first registrar and verify domain is pending transfer.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_3_nameservers_pending_transfer.xml",
"2002-05-30T01:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as second registrar and verify transfer was successful.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
// Expect transfer complete with all three nameservers on it.
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_3_nameservers_transfer_successful.xml",
"2002-06-09T00:00:00Z");
// Verify that host's client ID was set to the new registrar and has the transfer date set.
assertCommandAndResponse(
"host_info_fakesite.xml",
null,
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("trDate", "2002-06-04T00:00:00Z"),
"2002-06-09T00:01:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainDeletionCancelsPendingTransfer() throws Exception {
// Register the domain as the first registrar.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_2_years.xml",
"domain_transfer_response_2_years.xml",
"2002-05-30T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as the first registrar and delete then restore the domain while the transfer
// is still pending.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_pending_transfer.xml",
"2002-05-30T01:00:00Z");
assertCommandAndResponse(
"domain_delete_fakesite.xml",
"generic_success_action_pending_response.xml",
"2002-05-30T01:01:00Z");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_pending_delete.xml",
"2002-05-30T01:02:00Z");
assertCommandAndResponse(
"domain_update_restore_fakesite.xml",
"domain_update_restore_request_response.xml",
"2002-05-30T01:03:00Z");
// Expect domain is ok now, not pending delete or transfer, and has been extended by a year from
// the date of the restore. (Not from the original expiration date.)
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_restored_ok.xml",
"2002-05-30T01:04:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainTransfer_subordinateHost_showsChangeInTransferQuery() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
createSubordinateHost();
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_no_transfer_history.xml",
"2000-09-02T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_1_year.xml",
"domain_transfer_response_1_year.xml",
"2001-01-01T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Verify that reID is set correctly.
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_fakesite.xml",
"2001-01-02T00:00:00Z");
// Verify that status went from 'pending' to 'serverApproved'.
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_completed_fakesite.xml",
"2001-01-08T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
/**
* Tests that when a superordinate domain of a host is transferred, and then the host is updated
* to be subordinate to a different domain, that the host retains the transfer time of the first
* superordinate domain, not whatever the transfer time from the second domain is.
*/
@Test
public void testSuccess_lastTransferTime_superordinateDomainTransferFollowedByHostUpdate()
throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create fakesite.example with subordinate host ns3.fakesite.example
createFakesite();
createSubordinateHost();
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_no_transfer_history.xml",
"2000-09-02T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_1_year.xml",
"domain_transfer_response_1_year.xml",
"2001-01-01T00:00:00Z");
// Verify that the lastTransferTime now reflects the superordinate domain's transfer.
assertCommandAndResponse(
"host_info.xml",
ImmutableMap.of("hostname", "ns3.fakesite.example"),
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("trDate", "2001-01-06T00:00:00.000Z"),
"2001-01-07T00:00:00Z");
assertCommandAndResponse(
"domain_create_secondsite.xml",
"domain_create_response_secondsite.xml",
"2001-01-08T00:00:00Z");
// Update the host to be subordinate to a different domain by renaming it to
// ns3.secondsite.example
assertCommandAndResponse(
"host_update_rename_only.xml",
ImmutableMap.of("oldName", "ns3.fakesite.example", "newName", "ns3.secondsite.example"),
"generic_success_response.xml",
null,
"2002-05-30T01:01:00Z");
// The last transfer time on the host should still be what it was from the transfer.
assertCommandAndResponse(
"host_info.xml",
ImmutableMap.of("hostname", "ns3.secondsite.example"),
"host_info_response_fakesite_post_transfer_and_update.xml",
ImmutableMap.of(
"hostname", "ns3.secondsite.example",
"trDate", "2001-01-06T00:00:00.000Z"),
"2003-01-07T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
/**
* Tests that when a superordinate domain of a host is transferred, and then the host is updated
* to be external, that the host retains the transfer time of the first superordinate domain.
*/
@Test
public void testSuccess_lastTransferTime_superordinateDomainTransferThenHostUpdateToExternal()
throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create fakesite.example with subordinate host ns3.fakesite.example
createFakesite();
createSubordinateHost();
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_no_transfer_history.xml",
"2000-09-02T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_1_year.xml",
"domain_transfer_response_1_year.xml",
"2001-01-01T00:00:00Z");
// Verify that the lastTransferTime now reflects the superordinate domain's transfer.
assertCommandAndResponse(
"host_info_fakesite.xml",
null,
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("trDate", "2001-01-06T00:00:00.000Z"),
"2001-01-07T00:00:00Z");
// Update the host to be external by renaming it to ns3.notarealsite.external
assertCommandAndResponse(
"host_update_rename_and_remove_addresses.xml",
ImmutableMap.of(
"oldName", "ns3.fakesite.example",
"newName", "ns3.notarealsite.external"),
"generic_success_response.xml",
null,
"2002-05-30T01:01:00Z");
// The last transfer time on the host should still be what it was from the transfer.
assertCommandAndResponse(
"host_info.xml",
ImmutableMap.of("hostname", "ns3.notarealsite.external"),
"host_info_response_fakesite_post_transfer_and_update_no_addresses.xml",
ImmutableMap.of(
"hostname", "ns3.notarealsite.external",
"trDate", "2001-01-06T00:00:00.000Z"),
"2001-01-07T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
}

View file

@ -0,0 +1,136 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registrar.RegistrarContact;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.UserInfo;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
/** Tests for {@link EppToolServlet}. */
@RunWith(MockitoJUnitRunner.class)
public class EppToolServletTest extends EppServletTestCase<EppToolServlet> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.withUserService(UserInfo.createAdmin(GAE_USER_EMAIL, GAE_USER_ID))
.build();
private static final String GAE_USER_ID = "12345";
private static final String GAE_USER_EMAIL = "person@example.com";
@Before
public void initTest() throws Exception {
Registrar registrar = Registrar.loadByClientId("NewRegistrar");
persistResource(
new RegistrarContact.Builder()
.setParent(registrar)
.setEmailAddress(GAE_USER_EMAIL)
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
.setGaeUserId(GAE_USER_ID)
.build());
}
@Test
public void testDomainAllocation_succeedsOnlyAsSuperuser() throws Exception {
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.SUNRISE,
START_OF_GA, TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
setClientIdentifier("NewRegistrar");
setSuperuser(false);
createContactsAndHosts();
// Note that the trademark is valid from 20130809 to 20170723, hence the domain creation
// in 2014.
assertCommandAndResponse(
"domain_create_sunrise_encoded_mark.xml",
"domain_create_sunrise_encoded_signed_mark_response.xml",
"2014-01-01T00:00:00Z");
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_doesnt_exist.xml",
"2014-01-01T00:01:00Z");
assertCommandAndResponse(
"domain_allocate_testvalidate.xml",
"domain_allocate_response_testvalidate_only_superuser.xml",
START_OF_GA.plusDays(1));
setSuperuser(true);
assertCommandAndResponse(
"domain_allocate_testvalidate.xml",
"domain_allocate_response_testvalidate.xml",
START_OF_GA.plusDays(1).plusMinutes(1));
setSuperuser(false);
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_ok.xml",
START_OF_GA.plusDays(1).plusMinutes(2));
}
@Test
public void testDomainCreation_failsBeforeSunrise() throws Exception {
DateTime sunriseDate = DateTime.parse("2000-05-30T00:00:00Z");
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
sunriseDate, TldState.SUNRISE,
sunriseDate.plusMonths(2), TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
setClientIdentifier("NewRegistrar");
createContactsAndHosts();
assertCommandAndResponse(
"domain_create_sunrise_encoded_mark.xml",
"domain_create_testvalidate_invalid_phase.xml",
sunriseDate.minusDays(1));
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_doesnt_exist.xml",
sunriseDate.plusDays(1));
}
@Test
public void testDomainCheckFee_succeeds() throws Exception {
DateTime gaDate = DateTime.parse("2000-05-30T00:00:00Z");
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
gaDate, TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
setClientIdentifier("NewRegistrar");
assertCommandAndResponse(
"domain_check_fee_premium.xml",
"domain_check_fee_premium_response.xml",
gaDate.plusDays(1));
}
// Extra method so the test runner doesn't produce empty shards.
@Test public void testNothing1() {}
}

View file

@ -0,0 +1,317 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.Sets.difference;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.flows.EppXmlTransformer.marshal;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.testing.DatastoreHelper.BILLING_EVENT_ID_STRIPPER;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.util.ResourceUtils.readResourceUtf8;
import static com.google.domain.registry.xml.XmlTestUtils.assertXmlEquals;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.SessionMetadata.SessionSource;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.eppcommon.ProtocolDefinition;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.eppinput.EppInput;
import com.google.domain.registry.model.eppoutput.EppOutput;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.EppLoader;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.TestSessionMetadata;
import com.google.domain.registry.util.TypeUtils.TypeInstantiator;
import com.google.domain.registry.xml.ValidationMode;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.List;
import java.util.Map;
/**
* Base class for resource flow unit tests.
*
* @param <F> the flow type
*/
@RunWith(MockitoJUnitRunner.class)
public abstract class FlowTestCase<F extends Flow> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final InjectRule inject = new InjectRule();
private FlowRunner flowRunner;
protected EppLoader eppLoader;
protected Class<? extends Flow> flowClass;
protected SessionMetadata sessionMetadata;
protected FakeClock clock = new FakeClock(DateTime.now(UTC));
@Before
public void init() throws Exception {
flowRunner = null;
sessionMetadata = new TestSessionMetadata();
sessionMetadata.setClientId("TheRegistrar");
sessionMetadata.setServiceExtensionUris(ProtocolDefinition.getVisibleServiceExtensionUris());
sessionMetadata.setSessionSource(SessionSource.NONE);
ofy().saveWithoutBackup().entity(new ClaimsListSingleton()).now();
inject.setStaticField(Ofy.class, "clock", clock); // For transactional flows.
inject.setStaticField(FlowRunner.class, "clock", clock); // For non-transactional flows.
}
protected void removeServiceExtensionUri(String uri) {
sessionMetadata.setServiceExtensionUris(
difference(sessionMetadata.getServiceExtensionUris(), ImmutableSet.of(uri)));
}
protected void setEppInput(String inputFilename) {
eppLoader = new EppLoader(this, inputFilename, ImmutableMap.<String, String>of());
}
protected void setEppInput(String inputFilename, Map<String, String> substitutions) {
eppLoader = new EppLoader(this, inputFilename, substitutions);
}
protected String readFile(String filename) {
return readResourceUtf8(getClass(), "testdata/" + filename);
}
/** Lazily load the flow, since it may fail to initialize if the environment isn't set up yet. */
public FlowRunner getFlowRunner() throws Exception {
if (flowRunner == null) {
flowRunner = createFlowRunner();
}
return flowRunner;
}
/** Load a flow from an epp object. */
private FlowRunner createFlowRunner() throws Exception {
EppInput eppInput = eppLoader.getEpp();
flowClass = firstNonNull(flowClass, FlowRegistry.getFlowClass(eppInput));
Class<?> expectedFlowClass = new TypeInstantiator<F>(getClass()){}.getExactType();
assertThat(flowClass).isEqualTo(expectedFlowClass);
return new FlowRunner(
flowClass,
eppInput,
getTrid(),
sessionMetadata,
"<xml></xml>".getBytes(),
null);
}
protected Trid getTrid() throws Exception {
return Trid.create(eppLoader.getEpp().getCommandWrapper().getClTrid(), "server-trid");
}
/** Gets the client ID that the flow will run as. */
protected String getClientIdForFlow() {
return sessionMetadata.getClientId();
}
/** Sets the client ID that the flow will run as. */
protected void setClientIdForFlow(String clientId) {
sessionMetadata.setClientId(clientId);
}
public void assertTransactionalFlow(boolean isTransactional) throws Exception {
assertThat(getFlowRunner().isTransactional()).isEqualTo(isTransactional);
}
public void assertNoHistory() throws Exception {
assertThat(ofy().load().type(HistoryEntry.class)).isEmpty();
}
public <T> T getOnlyGlobalResource(Class<T> clazz) throws Exception {
return Iterables.getOnlyElement(ofy().load().type(clazz));
}
/** Helper to remove the grace period's billing event key to facilitate comparison. */
private static final Function<GracePeriod, GracePeriod> GRACE_PERIOD_KEY_STRIPPER =
new Function<GracePeriod, GracePeriod>() {
@Override
public GracePeriod apply(GracePeriod gracePeriod) {
return GracePeriod.create(
gracePeriod.isSunrushAddGracePeriod()
? GracePeriodStatus.SUNRUSH_ADD
: gracePeriod.getType(),
gracePeriod.getExpirationTime(),
gracePeriod.getClientId(),
null);
}};
/** A helper class that sets the billing event parent history entry to facilitate comparison. */
public static class BillingEventParentSetter implements Function<BillingEvent, BillingEvent> {
private HistoryEntry historyEntry;
public static BillingEventParentSetter withParent(HistoryEntry historyEntry) {
BillingEventParentSetter instance = new BillingEventParentSetter();
instance.historyEntry = historyEntry;
return instance;
}
@Override
public BillingEvent apply(BillingEvent billingEvent) {
return billingEvent.asBuilder().setParent(historyEntry).build();
}
private BillingEventParentSetter() {}
}
/**
* Helper to facilitate comparison of maps of GracePeriods to BillingEvents. This takes a map of
* GracePeriods to BillingEvents and returns a map of the same entries that ignores the keys
* on the grace periods and the IDs on the billing events (by setting them all to the same dummy
* values), since they will vary between instantiations even when the other data is the same.
*/
private ImmutableMap<GracePeriod, BillingEvent>
canonicalizeGracePeriods(ImmutableMap<GracePeriod, ? extends BillingEvent> gracePeriods) {
ImmutableMap.Builder<GracePeriod, BillingEvent> builder = new ImmutableMap.Builder<>();
for (Map.Entry<GracePeriod, ? extends BillingEvent> entry : gracePeriods.entrySet()) {
builder.put(
GRACE_PERIOD_KEY_STRIPPER.apply(entry.getKey()),
BILLING_EVENT_ID_STRIPPER.apply(entry.getValue()));
}
return builder.build();
}
/**
* Assert that the actual grace periods and the corresponding billing events referenced from
* their keys match the expected map of grace periods to billing events. For the expected map,
* the keys on the grace periods and IDs on the billing events are ignored.
*/
public void assertGracePeriods(
Iterable<GracePeriod> actual,
ImmutableMap<GracePeriod, ? extends BillingEvent> expected) {
Function<GracePeriod, BillingEvent> gracePeriodExpander =
new Function<GracePeriod, BillingEvent>() {
@Override
public BillingEvent apply(GracePeriod gracePeriod) {
assertThat(gracePeriod.hasBillingEvent())
.named("Billing event is present for grace period: " + gracePeriod)
.isTrue();
return firstNonNull(
gracePeriod.getOneTimeBillingEvent(),
gracePeriod.getRecurringBillingEvent())
.get();
}};
assertThat(canonicalizeGracePeriods(Maps.toMap(actual, gracePeriodExpander)))
.isEqualTo(canonicalizeGracePeriods(expected));
}
public void assertPollMessages(
String clientId,
PollMessage... expected) throws Exception {
assertPollMessagesHelper(getPollMessages(clientId), expected);
}
public void assertPollMessages(
String clientId,
DateTime now,
PollMessage... expected) throws Exception {
assertPollMessagesHelper(getPollMessages(clientId, now), expected);
}
public void assertPollMessages(PollMessage... expected) throws Exception {
assertPollMessagesHelper(getPollMessages(), expected);
}
/** Assert that the list matches all the poll messages in the fake datastore. */
public void assertPollMessagesHelper(Iterable<PollMessage> pollMessages, PollMessage... expected)
throws Exception {
// To facilitate comparison, remove the ids.
Function<PollMessage, PollMessage> idStripper =
new Function<PollMessage, PollMessage>() {
@Override
public PollMessage apply(PollMessage pollMessage) {
return pollMessage.asBuilder().setId(1L).build();
}};
// Ordering is irrelevant but duplicates should be considered independently.
assertThat(FluentIterable.from(pollMessages).transform(idStripper))
.containsExactlyElementsIn(FluentIterable.from(asList(expected)).transform(idStripper));
}
/** Run a flow, and attempt to marshal the result to EPP or throw if it doesn't validate. */
public EppOutput runFlow(CommitMode commitMode, UserPrivileges userPrivileges) throws Exception {
EppOutput output = getFlowRunner().run(commitMode, userPrivileges);
marshal(output, ValidationMode.STRICT);
return output;
}
public EppOutput runFlow() throws Exception {
return runFlow(CommitMode.LIVE, UserPrivileges.NORMAL);
}
public void runFlowAssertResponse(
CommitMode commitMode, UserPrivileges userPrivileges, String xml, String... ignoredPaths)
throws Exception {
EppOutput eppOutput = getFlowRunner().run(commitMode, userPrivileges);
assertThat(eppOutput.isSuccess()).isTrue();
try {
assertXmlEquals(
xml, new String(marshal(eppOutput, ValidationMode.STRICT), UTF_8), ignoredPaths);
} catch (Throwable e) {
assertXmlEquals(
xml, new String(marshal(eppOutput, ValidationMode.LENIENT), UTF_8), ignoredPaths);
// If it was a marshaling error, augment the output.
throw new Exception(
String.format(
"Invalid xml.\nExpected:\n%s\n\nActual:\n%s\n",
xml,
marshal(eppOutput, ValidationMode.LENIENT)),
e);
}
// Clear the cache so that we don't see stale results in tests.
ofy().clearSessionCache();
}
public void dryRunFlowAssertResponse(String xml, String... ignoredPaths) throws Exception {
List<Object> beforeEntities = ofy().load().list();
runFlowAssertResponse(CommitMode.DRY_RUN, UserPrivileges.NORMAL, xml, ignoredPaths);
assertThat(ofy().load()).containsExactlyElementsIn(beforeEntities);
}
public void runFlowAssertResponse(String xml, String... ignoredPaths) throws Exception {
runFlowAssertResponse(CommitMode.LIVE, UserPrivileges.NORMAL, xml, ignoredPaths);
}
}

View file

@ -0,0 +1,39 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.eppoutput.CheckData;
/**
* Base class for resource check flow unit tests.
*
* @param <F> the flow type
* @param <R> the resource type
*/
public class ResourceCheckFlowTestCase<F extends Flow, R extends EppResource>
extends ResourceFlowTestCase<F, R> {
protected void doCheckTest(CheckData.Check... expected) throws Exception {
assertTransactionalFlow(false);
assertThat(expected).asList().containsExactlyElementsIn(
((CheckData) runFlow().getResponse().getResponseData().get(0)).getChecks());
assertNoHistory(); // Checks don't create a history event.
assertNoBillingEvents(); // Checks are always free.
}
}

View file

@ -0,0 +1,140 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.model.tmch.ClaimsListShardTest.createTestClaimsListShard;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.domain.registry.flows.EppException.CommandUseErrorException;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.domain.launch.ApplicationIdTargetExtension;
import com.google.domain.registry.model.eppinput.EppInput.ResourceCommandWrapper;
import com.google.domain.registry.model.eppinput.ResourceCommand;
import com.google.domain.registry.model.index.EppResourceIndex;
import com.google.domain.registry.model.index.EppResourceIndexBucket;
import com.google.domain.registry.model.tmch.ClaimsListShard.ClaimsListRevision;
import com.google.domain.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.util.TypeUtils.TypeInstantiator;
import com.googlecode.objectify.Key;
import org.joda.time.DateTime;
import org.junit.Rule;
import org.junit.Test;
/**
* Base class for resource flow unit tests.
*
* @param <F> the flow type
* @param <R> the resource type
*/
public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource>
extends FlowTestCase<F> {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
private R reloadResourceByUniqueId(DateTime now) throws Exception {
// Force the session to be cleared so that when we read it back, we read from the datastore and
// not from the transaction cache or memcache.
ofy().clearSessionCache();
return loadByUniqueId(getResourceClass(), getUniqueIdFromCommand(), now);
}
protected R reloadResourceByUniqueId() throws Exception {
return reloadResourceByUniqueId(clock.nowUtc());
}
protected R reloadResourceByUniqueIdYesterday() throws Exception {
return reloadResourceByUniqueId(clock.nowUtc().minusDays(1));
}
protected <T extends EppResource> T reloadResourceAndCloneAtTime(T resource, DateTime now) {
// Force the session to be cleared.
ofy().clearSessionCache();
@SuppressWarnings("unchecked")
T refreshedResource = (T) ofy().load().entity(resource).now().cloneProjectedAtTime(now);
return refreshedResource;
}
protected ResourceCommand.SingleResourceCommand getResourceCommand() throws Exception {
return (ResourceCommand.SingleResourceCommand)
((ResourceCommandWrapper) eppLoader.getEpp().getCommandWrapper().getCommand())
.getResourceCommand();
}
/**
* We have to duplicate the logic from SingleResourceFlow.getTargetId() here. However, given the
* choice between making that method public, and duplicating its logic, it seems better to muddy
* the test code rather than the production code.
*/
protected String getUniqueIdFromCommand() throws Exception {
ApplicationIdTargetExtension extension =
eppLoader.getEpp().getSingleExtension(ApplicationIdTargetExtension.class);
return extension == null ? getResourceCommand().getTargetId() : extension.getApplicationId();
}
protected Class<R> getResourceClass() {
return new TypeInstantiator<R>(getClass()){}.getExactType();
}
/**
* Persists a testing claims list to Datastore that contains a single shard.
*/
protected void persistClaimsList(ImmutableMap<String, String> labelsToKeys) {
ClaimsListSingleton singleton = new ClaimsListSingleton();
Key<ClaimsListRevision> revision = ClaimsListRevision.createKey(singleton);
singleton.setActiveRevision(revision);
ofy().saveWithoutBackup().entity(singleton).now();
if (!labelsToKeys.isEmpty()) {
ofy().saveWithoutBackup()
.entity(createTestClaimsListShard(clock.nowUtc(), labelsToKeys, revision))
.now();
}
}
@Test
public void testRequiresLogin() throws Exception {
thrown.expect(CommandUseErrorException.class);
sessionMetadata.setClientId(null);
runFlow();
}
/**
* Confirms that an EppResourceIndex entity exists in datastore for a given resource.
*/
protected static <T extends EppResource> void assertEppResourceIndexEntityFor(final T resource) {
ImmutableList<EppResourceIndex> indices = FluentIterable
.from(ofy().load()
.type(EppResourceIndex.class)
.filter("kind", Key.getKind(resource.getClass())))
.filter(new Predicate<EppResourceIndex>() {
@Override
public boolean apply(EppResourceIndex index) {
return index.getReference().get().equals(resource);
}})
.toList();
assertThat(indices).hasSize(1);
assertThat(indices.get(0).getBucket())
.isEqualTo(EppResourceIndexBucket.getBucketKey(Key.create(resource)));
}
}

View file

@ -0,0 +1,293 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.async;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistContactWithPendingTransfer;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.domain.registry.model.contact.ContactAddress;
import com.google.domain.registry.model.contact.ContactPhoneNumber;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.contact.PostalInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DeleteContactResourceAction}. */
@RunWith(JUnit4.class)
public class DeleteContactResourceActionTest
extends DeleteEppResourceActionTestCase<DeleteContactResourceAction> {
ContactResource contactUnused;
@Before
public void setup() throws Exception {
setupDeleteEppResourceAction(new DeleteContactResourceAction());
contactUnused = persistActiveContact("blah1235");
}
@Test
public void testSuccess_contact_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
contactUsed = persistResource(
contactUsed.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
runMapreduceWithKeyParam(Key.create(contactUsed).getString());
contactUsed = loadByUniqueId(ContactResource.class, "blah1234", now);
assertAboutContacts().that(contactUsed).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
.and().hasDeletionTime(END_OF_TIME);
domain = loadByUniqueId(DomainResource.class, "example.tld", now);
assertThat(domain.getReferencedContacts())
.contains(ReferenceUnion.<ContactResource>create(Ref.create(contactUsed)));
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactUsed, HistoryEntry.Type.CONTACT_DELETE_FAILURE);
assertPollMessageFor(
historyEntry,
"TheRegistrar",
"Can't delete contact blah1234 because it is referenced by a domain.");
}
@Test
public void testSuccess_contact_notReferenced_getsDeleted() throws Exception {
contactUnused = persistResource(
contactUnused.asBuilder()
.setLocalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.LOCALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Grand Ave"))
.build())
.build())
.setInternationalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.INTERNATIONALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Avenida Grande"))
.build())
.build())
.setEmailAddress("bob@bob.com")
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
.setFaxNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
assertAboutContacts().that(contactUnused).hasNonNullLocalizedPostalInfo()
.and().hasNonNullInternationalizedPostalInfo()
.and().hasNonNullEmailAddress()
.and().hasNonNullVoiceNumber()
.and().hasNonNullFaxNumber();
Key<ContactResource> key = Key.create(contactUnused);
runMapreduceWithKeyParam(key.getString());
assertThat(loadByUniqueId(ContactResource.class, "blah1235", now)).isNull();
ContactResource contactAfterDeletion = ofy().load().key(key).now();
assertAboutContacts().that(contactAfterDeletion).hasDeletionTime(now)
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
assertAboutContacts().that(contactAfterDeletion).hasNullLocalizedPostalInfo()
.and().hasNullInternationalizedPostalInfo()
.and().hasNullEmailAddress()
.and().hasNullVoiceNumber()
.and().hasNullFaxNumber();
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactAfterDeletion, HistoryEntry.Type.CONTACT_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact blah1235.");
}
@Test
public void testSuccess_contactWithPendingTransfer_getsDeleted() throws Exception {
ContactResource contact = persistContactWithPendingTransfer(
persistActiveContact("sh8013").asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build(),
transferRequestTime,
transferExpirationTime,
clock.nowUtc());
runMapreduceWithKeyParam(Key.create(contact).getString());
// Check that the contact is deleted as of now.
assertThat(loadByUniqueId(ContactResource.class, "sh8013", now)).isNull();
// Check that it's still there (it wasn't deleted yesterday) and that it has history.
assertAboutContacts()
.that(loadByUniqueId(ContactResource.class, "sh8013", now.minusDays(1)))
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
HistoryEntry.Type.CONTACT_DELETE);
assertNoBillingEvents();
PollMessage deletePollMessage = Iterables.getOnlyElement(
getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)));
assertThat(deletePollMessage.getMsg()).isEqualTo("Deleted contact sh8013.");
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar.
PollMessage gainingPollMessage = Iterables.getOnlyElement(
getPollMessages("NewRegistrar", clock.nowUtc()));
System.out.println(gainingPollMessage);
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable.from(gainingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.SERVER_CANCELLED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isFalse();
}
@Test
public void testSuccess_contact_referencedByDeleteDomain_getsDeleted() throws Exception {
contactUsed = persistResource(
contactUsed.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
domain = persistResource(
domain.asBuilder()
.setDeletionTime(now.minusDays(3))
.build());
runMapreduceWithKeyParam(Key.create(contactUsed).getString());
assertThat(loadByUniqueId(ContactResource.class, "blah1234", now)).isNull();
ContactResource contactBeforeDeletion =
loadByUniqueId(ContactResource.class, "blah1234", now.minusDays(1));
assertAboutContacts().that(contactBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactBeforeDeletion, HistoryEntry.Type.CONTACT_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact blah1234.");
}
@Test
public void testFailure_notPendingDelete() throws Exception {
thrown.expect(IllegalStateException.class, "Resource blah1235 is not set as PENDING_DELETE");
runMapreduceWithKeyParam(Key.create(contactUnused).getString());
assertThat(
loadByUniqueId(ContactResource.class, "blah1235", now)).isEqualTo(contactUnused);
}
@Test
public void testSuccess_notRequestedByOwner_doesNotGetDeleted() throws Exception {
contactUnused = persistResource(
contactUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
Key<ContactResource> key = Key.create(contactUnused);
runMapreduceWithParams(key.getString(), "OtherRegistrar", false);
contactUnused = loadByUniqueId(ContactResource.class, "blah1235", now);
assertAboutContacts().that(contactUnused).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
.and().hasDeletionTime(END_OF_TIME);
domain = loadByUniqueId(DomainResource.class, "example.tld", now);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactUnused, HistoryEntry.Type.CONTACT_DELETE_FAILURE);
assertPollMessageFor(
historyEntry,
"OtherRegistrar",
"Can't delete contact blah1235 because it was transferred prior to deletion.");
}
@Test
public void testSuccess_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
contactUnused = persistResource(
contactUnused.asBuilder()
.setLocalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.LOCALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Grand Ave"))
.build())
.build())
.setInternationalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.INTERNATIONALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Avenida Grande"))
.build())
.build())
.setEmailAddress("bob@bob.com")
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
.setFaxNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
Key<ContactResource> key = Key.create(contactUnused);
runMapreduceWithParams(key.getString(), "OtherRegistrar", true);
assertThat(loadByUniqueId(ContactResource.class, "blah1235", now)).isNull();
ContactResource contactAfterDeletion = ofy().load().key(key).now();
assertAboutContacts().that(contactAfterDeletion).hasDeletionTime(now)
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
assertAboutContacts().that(contactAfterDeletion).hasNullLocalizedPostalInfo()
.and().hasNullInternationalizedPostalInfo()
.and().hasNullEmailAddress()
.and().hasNullVoiceNumber()
.and().hasNullFaxNumber();
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactAfterDeletion, HistoryEntry.Type.CONTACT_DELETE);
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted contact blah1235.");
}
@Test
public void testFailure_targetResourceDoesntExist() throws Exception {
createTld("tld");
ContactResource notPersisted = newContactResource("somecontact");
thrown.expect(
BadRequestException.class,
"Could not load resource for key: Key<?>(ContactResource(\"7-ROID\"))");
runMapreduceWithKeyParam(Key.create(notPersisted).getString());
}
@Test
public void testFailure_contactAlreadyDeleted() throws Exception {
ContactResource contactDeleted = persistResource(
newContactResource("blah1236").asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusDays(2))
.setDeletionTime(clock.nowUtc().minusDays(1))
.build());
thrown.expect(
IllegalStateException.class,
"Resource blah1236 is already deleted.");
runMapreduceWithKeyParam(Key.create(contactDeleted).getString());
}
}

View file

@ -0,0 +1,142 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.async;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessageForHistoryEntry;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.mapreduce.MapreduceRunner;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.poll.PollMessage.OneTime;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.FakeResponse;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.mapreduce.MapreduceTestCase;
import com.googlecode.objectify.Key;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Rule;
import org.junit.Test;
/** Unit tests for {@link DeleteEppResourceAction}. */
public abstract class DeleteEppResourceActionTestCase<T extends DeleteEppResourceAction<?>>
extends MapreduceTestCase<T> {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final InjectRule inject = new InjectRule();
DateTime now = DateTime.now(DateTimeZone.UTC);
FakeClock clock = new FakeClock(now);
final DateTime transferRequestTime = now.minusDays(3);
final DateTime transferExpirationTime =
transferRequestTime.plus(Registry.DEFAULT_TRANSFER_GRACE_PERIOD);
ContactResource contactUsed;
HostResource hostUsed;
DomainResource domain;
public void setupDeleteEppResourceAction(T deleteEppResourceAction) throws Exception {
action = deleteEppResourceAction;
action.mrRunner = new MapreduceRunner(Optional.<Integer>absent(), Optional.<Integer>absent());
action.response = new FakeResponse();
inject.setStaticField(Ofy.class, "clock", clock);
inject.setStaticField(DeleteEppResourceAction.class, "clock", clock);
createTld("tld");
contactUsed = persistActiveContact("blah1234");
hostUsed = persistActiveHost("ns1.example.tld");
domain = persistResource(
newDomainResource("example.tld", contactUsed).asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(hostUsed)))
.build());
}
void runMapreduce() throws Exception {
clock.advanceOneMilli();
action.run();
executeTasksUntilEmpty("mapreduce");
ofy().clearSessionCache();
now = clock.nowUtc();
}
void runMapreduceWithParams(
String resourceKeyString,
String requestingClientId,
boolean isSuperuser) throws Exception {
action.resourceKeyString = resourceKeyString;
action.requestingClientId = requestingClientId;
action.isSuperuser = isSuperuser;
runMapreduce();
}
void runMapreduceWithKeyParam(String resourceKeyString) throws Exception {
runMapreduceWithParams(resourceKeyString, "TheRegistrar", false);
}
/**
* Helper method to check that one poll message exists with a given history entry, resource,
* client id, and message.
*/
void assertPollMessageFor(
HistoryEntry historyEntry,
String clientId,
String msg) {
PollMessage.OneTime pollMessage = (OneTime) getOnlyPollMessageForHistoryEntry(historyEntry);
assertThat(msg).isEqualTo(pollMessage.getMsg());
assertThat(now).isEqualTo(pollMessage.getEventTime());
assertThat(clientId).isEqualTo(pollMessage.getClientId());
assertThat(pollMessage.getClientId()).isEqualTo(clientId);
}
@Test
public void testFailure_domainKeyPassed() throws Exception {
DomainResource domain = persistActiveDomain("fail.tld");
thrown.expect(
IllegalArgumentException.class, "Cannot delete a DomainResource via this action.");
runMapreduceWithKeyParam(Key.create(domain).getString());
assertThat(loadByUniqueId(DomainResource.class, "fail.tld", now)).isEqualTo(domain);
}
@Test
public void testFailure_badKeyPassed() throws Exception {
createTld("tld");
thrown.expect(BadRequestException.class, "Could not parse key string: a bad key");
runMapreduceWithKeyParam("a bad key");
}
}

View file

@ -0,0 +1,226 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.async;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.newHostResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.HostResourceSubject.assertAboutHosts;
import static com.google.domain.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DeleteHostResourceAction}. */
@RunWith(JUnit4.class)
public class DeleteHostResourceActionTest
extends DeleteEppResourceActionTestCase<DeleteHostResourceAction> {
HostResource hostUnused;
@Before
public void setup() throws Exception {
setupDeleteEppResourceAction(new DeleteHostResourceAction());
hostUnused = persistActiveHost("ns2.example.tld");
}
@Test
public void testSuccess_host_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
hostUsed = persistResource(
hostUsed.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
runMapreduceWithKeyParam(Key.create(hostUsed).getString());
hostUsed = loadByUniqueId(HostResource.class, "ns1.example.tld", now);
assertAboutHosts().that(hostUsed).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
.and().hasDeletionTime(END_OF_TIME);
domain = loadByUniqueId(DomainResource.class, "example.tld", now);
assertThat(domain.getNameservers())
.contains(ReferenceUnion.<HostResource>create(Ref.create(hostUsed)));
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostUsed, HistoryEntry.Type.HOST_DELETE_FAILURE);
assertPollMessageFor(
historyEntry,
"TheRegistrar",
"Can't delete host ns1.example.tld because it is referenced by a domain.");
}
@Test
public void testSuccess_host_notReferenced_getsDeleted() throws Exception {
hostUnused = persistResource(
hostUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
assertThat(loadByUniqueId(HostResource.class, "ns2.example.tld", now)).isNull();
HostResource hostBeforeDeletion =
loadByUniqueId(HostResource.class, "ns2.example.tld", now.minusDays(1));
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.");
}
@Test
public void testSuccess_host_referencedByDeletedDomain_getsDeleted() throws Exception {
hostUsed = persistResource(
hostUsed.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
domain = persistResource(
domain.asBuilder()
.setDeletionTime(now.minusDays(3))
.build());
runMapreduceWithKeyParam(Key.create(hostUsed).getString());
assertThat(loadByUniqueId(HostResource.class, "ns1.example.tld", now)).isNull();
HostResource hostBeforeDeletion =
loadByUniqueId(HostResource.class, "ns1.example.tld", now.minusDays(1));
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns1.example.tld.");
}
@Test
public void testSuccess_subordinateHost_getsDeleted() throws Exception {
domain = persistResource(
newDomainResource("example.tld").asBuilder()
.setSubordinateHosts(ImmutableSet.of("ns2.example.tld"))
.build());
persistResource(
hostUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.setSuperordinateDomain(Ref.create(domain))
.build());
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
// Check that the host is deleted as of now.
assertThat(loadByUniqueId(HostResource.class, "ns2.example.tld", clock.nowUtc()))
.isNull();
assertNoBillingEvents();
assertThat(loadByUniqueId(DomainResource.class, "example.tld", clock.nowUtc())
.getSubordinateHosts())
.isEmpty();
assertDnsTasksEnqueued("ns2.example.tld");
HostResource hostBeforeDeletion =
loadByUniqueId(HostResource.class, "ns2.example.tld", now.minusDays(1));
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.");
}
@Test
public void testFailure_notPendingDelete() throws Exception {
thrown.expect(
IllegalStateException.class, "Resource ns2.example.tld is not set as PENDING_DELETE");
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
assertThat(
loadByUniqueId(HostResource.class, "ns2.example.tld", now)).isEqualTo(hostUnused);
}
@Test
public void testSuccess_notRequestedByOwner_doesNotGetDeleted() throws Exception {
hostUnused = persistResource(
hostUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
Key<HostResource> key = Key.create(hostUnused);
runMapreduceWithParams(key.getString(), "OtherRegistrar", false);
hostUnused = loadByUniqueId(HostResource.class, "ns2.example.tld", now);
assertAboutHosts().that(hostUnused).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
.and().hasDeletionTime(END_OF_TIME);
domain = loadByUniqueId(DomainResource.class, "example.tld", now);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostUnused, HistoryEntry.Type.HOST_DELETE_FAILURE);
assertPollMessageFor(
historyEntry,
"OtherRegistrar",
"Can't delete host ns2.example.tld because it was transferred prior to deletion.");
}
@Test
public void testSuccess_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
hostUnused = persistResource(
hostUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
Key<HostResource> key = Key.create(hostUnused);
runMapreduceWithParams(key.getString(), "OtherRegistrar", true);
assertThat(loadByUniqueId(HostResource.class, "ns2.example.tld", now)).isNull();
HostResource hostBeforeDeletion =
loadByUniqueId(HostResource.class, "ns2.example.tld", now.minusDays(1));
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted host ns2.example.tld.");
}
@Test
public void testFailure_targetResourceDoesntExist() throws Exception {
createTld("tld");
HostResource notPersisted = newHostResource("ns1.example.tld");
thrown.expect(
BadRequestException.class,
"Could not load resource for key: Key<?>(HostResource(\"7-ROID\"))");
runMapreduceWithKeyParam(Key.create(notPersisted).getString());
}
@Test
public void testFailure_hostAlreadyDeleted() throws Exception {
HostResource hostDeleted = persistResource(
newHostResource("ns3.example.tld").asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusDays(2))
.setDeletionTime(clock.nowUtc().minusDays(1))
.build());
thrown.expect(
IllegalStateException.class,
"Resource ns3.example.tld is already deleted.");
runMapreduceWithKeyParam(Key.create(hostDeleted).getString());
}
}

View file

@ -0,0 +1,127 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.async;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainApplication;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.newHostResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.dns.DnsQueue;
import com.google.domain.registry.mapreduce.MapreduceRunner;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeResponse;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.mapreduce.MapreduceTestCase;
import com.googlecode.objectify.Key;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DnsRefreshForHostRenameAction}. */
@RunWith(JUnit4.class)
public class DnsRefreshForHostRenameActionTest
extends MapreduceTestCase<DnsRefreshForHostRenameAction> {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public InjectRule inject = new InjectRule();
private final DnsQueue dnsQueue = mock(DnsQueue.class);
@Before
public void setup() throws Exception {
inject.setStaticField(DnsRefreshForHostRenameAction.class, "dnsQueue", dnsQueue);
}
private void runMapreduce(String hostKeyString) throws Exception {
action = new DnsRefreshForHostRenameAction();
action.hostKeyString = hostKeyString;
action.mrRunner = new MapreduceRunner(Optional.<Integer>absent(), Optional.<Integer>absent());
action.response = new FakeResponse();
action.run();
executeTasksUntilEmpty("mapreduce");
}
@Test
public void testSuccess_dnsUpdateEnqueued() throws Exception {
createTld("tld");
HostResource renamedHost = persistActiveHost("ns1.example.tld");
HostResource otherHost = persistActiveHost("ns2.example.tld");
persistResource(newDomainApplication("notadomain.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(renamedHost)))
.build());
persistResource(newDomainResource("example.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(renamedHost)))
.build());
persistResource(newDomainResource("otherexample.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(renamedHost)))
.build());
persistResource(newDomainResource("untouched.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(otherHost)))
.build());
runMapreduce(Key.create(renamedHost).getString());
verify(dnsQueue).addDomainRefreshTask("example.tld");
verify(dnsQueue).addDomainRefreshTask("otherexample.tld");
verifyNoMoreInteractions(dnsQueue);
}
@Test
public void testSuccess_noDnsTasksForDeletedDomain() throws Exception {
createTld("tld");
HostResource renamedHost = persistActiveHost("ns1.example.tld");
persistResource(newDomainResource("example.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(renamedHost)))
.setDeletionTime(START_OF_TIME)
.build());
runMapreduce(Key.create(renamedHost).getString());
verifyZeroInteractions(dnsQueue);
}
@Test
public void testFailure_badKeyPassed() throws Exception {
createTld("tld");
thrown.expect(BadRequestException.class, "Could not parse key string: a bad key");
runMapreduce("a bad key");
}
@Test
public void testFailure_hostDoesntExist() throws Exception {
createTld("tld");
HostResource notPersistedHost = newHostResource("ns1.example.tld");
thrown.expect(
BadRequestException.class,
"Could not load resource for key: Key<?>(HostResource(\"2-ROID\"))");
runMapreduce(Key.create(notPersistedHost).getString());
}
}

View file

@ -0,0 +1,83 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.model.eppoutput.CheckData.ContactCheck.create;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedContact;
import com.google.domain.registry.flows.ResourceCheckFlow.TooManyResourceChecksException;
import com.google.domain.registry.flows.ResourceCheckFlowTestCase;
import com.google.domain.registry.model.contact.ContactResource;
import org.junit.Test;
/** Unit tests for {@link ContactCheckFlow}. */
public class ContactCheckFlowTest
extends ResourceCheckFlowTestCase<ContactCheckFlow, ContactResource> {
public ContactCheckFlowTest() {
setEppInput("contact_check.xml");
}
@Test
public void testNothingExists() throws Exception {
// These ids come from the check xml.
doCheckTest(
create(true, "sh8013", null),
create(true, "sah8013", null),
create(true, "8013sah", null));
}
@Test
public void testOneExists() throws Exception {
persistActiveContact("sh8013");
// These ids come from the check xml.
doCheckTest(
create(false, "sh8013", "In use"),
create(true, "sah8013", null),
create(true, "8013sah", null));
}
@Test
public void testOneExistsButWasDeleted() throws Exception {
persistDeletedContact("sh8013", clock.nowUtc());
// These ids come from the check xml.
doCheckTest(
create(true, "sh8013", null),
create(true, "sah8013", null),
create(true, "8013sah", null));
}
@Test
public void testXmlMatches() throws Exception {
persistActiveContact("sah8013");
runFlowAssertResponse(readFile("contact_check_response.xml"));
}
@Test
public void test50IdsAllowed() throws Exception {
// Make sure we don't have a regression that reduces the number of allowed checks.
setEppInput("contact_check_50.xml");
runFlow();
}
@Test
public void testTooManyIds() throws Exception {
setEppInput("contact_check_51.xml");
thrown.expect(TooManyResourceChecksException.class);
runFlow();
}
}

View file

@ -0,0 +1,95 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedContact;
import com.google.domain.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
import com.google.domain.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException;
import com.google.domain.registry.model.contact.ContactResource;
import org.joda.time.DateTime;
import org.junit.Test;
/** Unit tests for {@link ContactCreateFlow}. */
public class ContactCreateFlowTest
extends ResourceFlowTestCase<ContactCreateFlow, ContactResource> {
public ContactCreateFlowTest() {
setEppInput("contact_create.xml");
clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z"));
}
private void doSuccessfulTest() throws Exception {
assertTransactionalFlow(true);
runFlowAssertResponse(readFile("contact_create_response.xml"));
// Check that the contact was created and persisted with a history entry.
assertAboutContacts().that(reloadResourceByUniqueId())
.hasOnlyOneHistoryEntryWhich().hasNoXml();
assertNoBillingEvents();
assertEppResourceIndexEntityFor(reloadResourceByUniqueId());
}
@Test
public void testDryRun() throws Exception {
dryRunFlowAssertResponse(readFile("contact_create_response.xml"));
}
@Test
public void testSuccess_neverExisted() throws Exception {
doSuccessfulTest();
}
@Test
public void testSuccess_existedButWasDeleted() throws Exception {
persistDeletedContact(getUniqueIdFromCommand(), clock.nowUtc());
clock.advanceOneMilli();
doSuccessfulTest();
}
@Test
public void testFailure_alreadyExists() throws Exception {
thrown.expect(
ResourceAlreadyExistsException.class,
String.format("Object with given ID (%s) already exists", getUniqueIdFromCommand()));
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testSuccess_nonAsciiInLocAddress() throws Exception {
setEppInput("contact_create_hebrew_loc.xml");
doSuccessfulTest();
}
@Test
public void testFailure_nonAsciiInIntAddress() throws Exception {
thrown.expect(BadInternationalizedPostalInfoException.class);
setEppInput("contact_create_hebrew_int.xml");
runFlow();
}
@Test
public void testFailure_declineDisclosure() throws Exception {
thrown.expect(DeclineContactDisclosureFieldDisallowedPolicyException.class);
setEppInput("contact_create_decline_disclosure.xml");
runFlow();
}
}

View file

@ -0,0 +1,189 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.flows.async.AsyncFlowUtils.ASYNC_FLOW_QUEUE_NAME;
import static com.google.domain.registry.request.Actions.getPathForAction;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceAsyncDeleteFlow.ResourceToDeleteIsReferencedException;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
import com.google.domain.registry.flows.async.DeleteContactResourceAction;
import com.google.domain.registry.flows.async.DeleteEppResourceAction;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import com.googlecode.objectify.Key;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactDeleteFlow}. */
public class ContactDeleteFlowTest
extends ResourceFlowTestCase<ContactDeleteFlow, ContactResource> {
@Before
public void initFlowTest() {
setEppInput("contact_delete.xml");
}
private void doFailingStatusTest(StatusValue statusValue, Class<? extends Exception> exception)
throws Exception {
thrown.expect(exception);
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(statusValue))
.build());
runFlow();
}
@Test
public void testDryRun() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
dryRunFlowAssertResponse(readFile("contact_delete_response.xml"));
}
@Test
public void testSuccess() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
assertTransactionalFlow(true);
runFlowAssertResponse(readFile("contact_delete_response.xml"));
ContactResource deletedContact = reloadResourceByUniqueId();
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
.url(getPathForAction(DeleteContactResourceAction.class))
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.param(
DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID,
"TheRegistrar")
.param(
DeleteEppResourceAction.PARAM_IS_SUPERUSER,
Boolean.toString(false))
.param(
DeleteEppResourceAction.PARAM_RESOURCE_KEY,
Key.create(deletedContact).getString()));
assertAboutContacts().that(deletedContact)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
assertNoBillingEvents();
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistDeletedContact(getUniqueIdFromCommand(), clock.nowUtc());
runFlow();
}
@Test
public void testFailure_existedButWasClientDeleteProhibited() throws Exception {
doFailingStatusTest(
StatusValue.CLIENT_DELETE_PROHIBITED, ResourceStatusProhibitsOperationException.class);
}
@Test
public void testFailure_existedButWasServerDeleteProhibited() throws Exception {
doFailingStatusTest(
StatusValue.SERVER_DELETE_PROHIBITED, ResourceStatusProhibitsOperationException.class);
}
@Test
public void testFailure_existedButWasPendingDelete() throws Exception {
doFailingStatusTest(
StatusValue.PENDING_DELETE, ResourceStatusProhibitsOperationException.class);
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("contact_delete_response.xml"));
ContactResource deletedContact = reloadResourceByUniqueId();
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
.url(getPathForAction(DeleteContactResourceAction.class))
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.param(
DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID,
"NewRegistrar")
.param(
DeleteEppResourceAction.PARAM_IS_SUPERUSER,
Boolean.toString(true))
.param(
DeleteEppResourceAction.PARAM_RESOURCE_KEY,
Key.create(deletedContact).getString()));
assertAboutContacts().that(deletedContact)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
assertNoBillingEvents();
}
@Test
public void testFailure_failfastWhenLinkedToDomain() throws Exception {
createTld("tld");
persistResource(
newDomainResource("example.tld", persistActiveContact(getUniqueIdFromCommand())));
thrown.expect(ResourceToDeleteIsReferencedException.class);
runFlow();
}
@Test
public void testFailure_failfastWhenLinkedToApplication() throws Exception {
createTld("tld");
persistResource(
newDomainResource("example.tld", persistActiveContact(getUniqueIdFromCommand())));
thrown.expect(ResourceToDeleteIsReferencedException.class);
runFlow();
}
}

View file

@ -0,0 +1,141 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.isDeleted;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import com.google.domain.registry.model.contact.ContactAddress;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactPhoneNumber;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.contact.Disclose;
import com.google.domain.registry.model.contact.PostalInfo;
import com.google.domain.registry.model.contact.PostalInfo.Type;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.PresenceMarker;
import com.google.domain.registry.model.eppcommon.StatusValue;
import org.joda.time.DateTime;
import org.junit.Test;
/** Unit tests for {@link ContactInfoFlow}. */
public class ContactInfoFlowTest extends ResourceFlowTestCase<ContactInfoFlow, ContactResource> {
public ContactInfoFlowTest() {
setEppInput("contact_info.xml");
}
private ContactResource persistContactResource(boolean active) {
ContactResource contact = persistResource(
new ContactResource.Builder()
.setContactId("sh8013")
.setRepoId("2FF-ROID")
.setDeletionTime(active ? null : DateTime.now().minusDays(1))
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_DELETE_PROHIBITED))
.setInternationalizedPostalInfo(new PostalInfo.Builder()
.setType(Type.INTERNATIONALIZED)
.setName("John Doe")
.setOrg("Example Inc.")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Example Dr.", "Suite 100"))
.setCity("Dulles")
.setState("VA")
.setZip("20166-6503")
.setCountryCode("US")
.build())
.build())
.setVoiceNumber(
new ContactPhoneNumber.Builder()
.setPhoneNumber("+1.7035555555")
.setExtension("1234")
.build())
.setFaxNumber(
new ContactPhoneNumber.Builder()
.setPhoneNumber("+1.7035555556")
.build())
.setEmailAddress("jdoe@example.com")
.setCurrentSponsorClientId("TheRegistrar")
.setCreationClientId("NewRegistrar")
.setLastEppUpdateClientId("NewRegistrar")
.setCreationTimeForTest(DateTime.parse("1999-04-03T22:00:00.0Z"))
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("2fooBAR")))
.setDisclose(new Disclose.Builder()
.setFlag(true)
.setVoice(new PresenceMarker())
.setEmail(new PresenceMarker())
.build())
.build());
assertThat(isDeleted(contact, DateTime.now())).isNotEqualTo(active);
return contact;
}
@Test
public void testSuccess() throws Exception {
persistContactResource(true);
// Check that the persisted contact info was returned.
assertTransactionalFlow(false);
runFlowAssertResponse(
readFile("contact_info_response.xml"),
// We use a different roid scheme than the samples so ignore it.
"epp.response.resData.infData.roid");
assertNoHistory();
assertNoBillingEvents();
}
@Test
public void testSuccess_linked() throws Exception {
createTld("foobar");
persistResource(newDomainResource("example.foobar", persistContactResource(true)));
// Check that the persisted contact info was returned.
assertTransactionalFlow(false);
runFlowAssertResponse(
readFile("contact_info_response_linked.xml"),
// We use a different roid scheme than the samples so ignore it.
"epp.response.resData.infData.roid");
assertNoHistory();
assertNoBillingEvents();
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistContactResource(false);
runFlow();
}
// Extra methods so the test runner doesn't produce empty shards.
@Test public void testNothing1() {}
}

View file

@ -0,0 +1,213 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferApproveFlow}. */
public class ContactTransferApproveFlowTest
extends ContactTransferFlowTestCase<ContactTransferApproveFlow, ContactResource> {
@Before
public void setUp() throws Exception {
setEppInput("contact_transfer_approve.xml");
setClientIdForFlow("TheRegistrar");
setupContactWithPendingTransfer();
clock.advanceOneMilli();
createTld("foobar");
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have succeeded. Verify correct fields were set.
contact = reloadResourceByUniqueId();
assertAboutContacts().that(contact)
.hasCurrentSponsorClientId("NewRegistrar").and()
.hasLastTransferTime(clock.nowUtc()).and()
.hasTransferStatus(TransferStatus.CLIENT_APPROVED).and()
.hasPendingTransferExpirationTime(clock.nowUtc()).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
HistoryEntry.Type.CONTACT_TRANSFER_APPROVE);
assertNoBillingEvents();
// The poll message (in the future) to the losing registrar for implicit ack should be gone.
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1))).isEmpty();
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar.
PollMessage gainingPollMessage = getOnlyPollMessage("NewRegistrar");
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.CLIENT_APPROVED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isTrue();
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("contact_transfer_approve.xml");
dryRunFlowAssertResponse(readFile("contact_transfer_approve_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_approve.xml", "contact_transfer_approve_response.xml");
}
@Test
public void testSuccess_withAuthinfo() throws Exception {
doSuccessfulTest("contact_transfer_approve_with_authinfo.xml",
"contact_transfer_approve_response.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_approve_with_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_gainingClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("NewRegistrar");
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("ClientZ");
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_approve.xml");
}
}

View file

@ -0,0 +1,199 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.flows.ResourceTransferCancelFlow.NotTransferInitiatorException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferCancelFlow}. */
public class ContactTransferCancelFlowTest
extends ContactTransferFlowTestCase<ContactTransferCancelFlow, ContactResource> {
@Before
public void setUp() throws Exception {
this.setEppInput("contact_transfer_cancel.xml");
setClientIdForFlow("NewRegistrar");
setupContactWithPendingTransfer();
clock.advanceOneMilli();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
this.setEppInput(commandFilename);
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1);
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have been cancelled. Verify correct fields were set.
contact = reloadResourceByUniqueId();
assertAboutContacts().that(contact)
.hasCurrentSponsorClientId("TheRegistrar").and()
.hasLastTransferTimeNotEqualTo(clock.nowUtc()).and()
.hasTransferStatus(TransferStatus.CLIENT_CANCELLED).and()
.hasPendingTransferExpirationTime(clock.nowUtc()).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
HistoryEntry.Type.CONTACT_TRANSFER_CANCEL);
assertNoBillingEvents();
// The poll message (in the future) to the gaining registrar for implicit ack should be gone.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1))).isEmpty();
// The poll message in the future to the losing registrar should be gone too, but there
// should be one at the current time to the losing registrar.
PollMessage losingPollMessage = getOnlyPollMessage("TheRegistrar");
assertThat(losingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(losingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.CLIENT_CANCELLED);
}
private void doFailingTest(String commandFilename) throws Exception {
this.setEppInput(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("contact_transfer_cancel.xml");
dryRunFlowAssertResponse(readFile("contact_transfer_cancel_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_cancel.xml", "contact_transfer_cancel_response.xml");
}
@Test
public void testSuccess_withAuthinfo() throws Exception {
doSuccessfulTest("contact_transfer_cancel_with_authinfo.xml",
"contact_transfer_cancel_response.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_cancel_with_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_sponsoringClient() throws Exception {
thrown.expect(NotTransferInitiatorException.class);
setClientIdForFlow("TheRegistrar");
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(NotTransferInitiatorException.class);
setClientIdForFlow("ClientZ");
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
doFailingTest("contact_transfer_cancel.xml");
}
}

View file

@ -0,0 +1,90 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.common.base.Preconditions.checkState;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistContactWithPendingTransfer;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.flows.Flow;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.transfer.TransferStatus;
import com.google.domain.registry.testing.AppEngineRule;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
/**
* Base class for contact transfer flow unit tests.
*
* @param <F> the flow type
* @param <R> the resource type
*/
public class ContactTransferFlowTestCase<F extends Flow, R extends EppResource>
extends ResourceFlowTestCase<F, R>{
// Transfer is requested on the 6th and expires on the 11th.
// The "now" of this flow is on the 9th, 3 days in.
private static final DateTime TRANSFER_REQUEST_TIME = DateTime.parse("2000-06-06T22:00:00.0Z");
private static final DateTime TRANSFER_EXPIRATION_TIME =
TRANSFER_REQUEST_TIME.plus(Registry.DEFAULT_TRANSFER_GRACE_PERIOD);
private static final Duration TIME_SINCE_REQUEST = Duration.standardDays(3);
protected ContactResource contact;
public ContactTransferFlowTestCase() {
checkState(!Registry.DEFAULT_TRANSFER_GRACE_PERIOD.isShorterThan(TIME_SINCE_REQUEST));
clock.setTo(TRANSFER_REQUEST_TIME.plus(TIME_SINCE_REQUEST));
}
@Before
public void initContactTest() {
// Registrar ClientZ is used in tests that need another registrar that definitely doesn't own
// the resources in question.
persistResource(
AppEngineRule.makeRegistrar1().asBuilder().setClientIdentifier("ClientZ").build());
}
/** Adds a contact that has a pending transfer on it from TheRegistrar to NewRegistrar. */
protected void setupContactWithPendingTransfer() throws Exception {
contact = persistContactWithPendingTransfer(
newContactResource("sh8013"),
TRANSFER_REQUEST_TIME,
TRANSFER_EXPIRATION_TIME,
TRANSFER_REQUEST_TIME);
}
/** Changes the transfer status on the persisted contact. */
protected void changeTransferStatus(TransferStatus transferStatus) {
contact = persistResource(
contact.asBuilder()
.setTransferData(
contact.getTransferData().asBuilder().setTransferStatus(transferStatus).build())
.build());
clock.advanceOneMilli();
}
/** Changes the client ID that the flow will run as. */
@Override
protected void setClientIdForFlow(String clientId) {
sessionMetadata.setClientId(clientId);
}
}

View file

@ -0,0 +1,187 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import com.google.domain.registry.flows.ResourceTransferQueryFlow.NoTransferHistoryToQueryException;
import com.google.domain.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferQueryFlow}. */
public class ContactTransferQueryFlowTest
extends ContactTransferFlowTestCase<ContactTransferQueryFlow, ContactResource> {
@Before
public void setUp() throws Exception {
setEppInput("contact_transfer_query.xml");
clock.setTo(DateTime.parse("2000-06-10T22:00:00.0Z"));
setClientIdForFlow("NewRegistrar");
setupContactWithPendingTransfer();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(false);
runFlowAssertResponse(readFile(expectedXmlFilename));
assertAboutContacts().that(reloadResourceByUniqueIdYesterday())
.hasOneHistoryEntryEachOfTypes(HistoryEntry.Type.CONTACT_TRANSFER_REQUEST);
assertNoBillingEvents();
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(false);
runFlow();
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_query.xml", "contact_transfer_query_response.xml");
}
@Test
public void testSuccess_withContactRoid() throws Exception {
doSuccessfulTest("contact_transfer_query_with_roid.xml", "contact_transfer_query_response.xml");
}
@Test
public void testSuccess_sponsoringClient() throws Exception {
setClientIdForFlow("TheRegistrar");
doSuccessfulTest("contact_transfer_query.xml", "contact_transfer_query_response.xml");
}
@Test
public void testSuccess_withAuthinfo() throws Exception {
setClientIdForFlow("ClientZ");
doSuccessfulTest("contact_transfer_query_with_authinfo.xml",
"contact_transfer_query_response.xml");
}
@Test
public void testSuccess_clientApproved() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_client_approved.xml");
}
@Test
public void testSuccess_clientRejected() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_client_rejected.xml");
}
@Test
public void testSuccess_clientCancelled() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_client_cancelled.xml");
}
@Test
public void testSuccess_serverApproved() throws Exception {
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_server_approved.xml");
}
@Test
public void testSuccess_serverCancelled() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_server_cancelled.xml");
}
@Test
public void testFailure_pendingDeleteContact() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().plusDays(1)).build());
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_server_cancelled.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_query_with_authinfo.xml");
}
@Test
public void testFailure_badContactRoid() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Set the contact to a different ROID, but don't persist it; this is just so the substitution
// code above will write the wrong ROID into the file.
contact = contact.asBuilder().setRepoId("DEADBEEF_TLD-ROID").build();
doFailingTest("contact_transfer_query_with_roid.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NoTransferHistoryToQueryException.class);
changeTransferStatus(null);
doFailingTest("contact_transfer_query.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(NotAuthorizedToViewTransferException.class);
setClientIdForFlow("ClientZ");
doFailingTest("contact_transfer_query.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_query.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
doFailingTest("contact_transfer_query.xml");
}
}

View file

@ -0,0 +1,209 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferRejectFlow}. */
public class ContactTransferRejectFlowTest
extends ContactTransferFlowTestCase<ContactTransferRejectFlow, ContactResource> {
@Before
public void setUp() throws Exception {
setEppInput("contact_transfer_reject.xml");
setClientIdForFlow("TheRegistrar");
setupContactWithPendingTransfer();
clock.advanceOneMilli();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have failed. Verify correct fields were set.
contact = reloadResourceByUniqueId();
assertAboutContacts().that(contact)
.hasCurrentSponsorClientId("TheRegistrar").and()
.hasLastTransferTimeNotEqualTo(clock.nowUtc()).and()
.hasTransferStatus(TransferStatus.CLIENT_REJECTED).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
HistoryEntry.Type.CONTACT_TRANSFER_REJECT);
// The poll message (in the future) to the losing registrar for implicit ack should be gone.
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.isEmpty();
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar.
PollMessage gainingPollMessage = getOnlyPollMessage("NewRegistrar");
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.CLIENT_REJECTED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isFalse();
assertNoBillingEvents();
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("contact_transfer_reject.xml");
dryRunFlowAssertResponse(readFile("contact_transfer_reject_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_reject.xml", "contact_transfer_reject_response.xml");
}
@Test
public void testSuccess_domainAuthInfo() throws Exception {
doSuccessfulTest("contact_transfer_reject_with_authinfo.xml",
"contact_transfer_reject_response.xml");
}
@Test
public void testFailure_badPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_reject_with_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_gainingClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("NewRegistrar");
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("ClientZ");
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
doFailingTest("contact_transfer_reject.xml");
}
}

View file

@ -0,0 +1,192 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.config.RegistryEnvironment;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.AlreadyPendingTransferException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.MissingTransferRequestAuthInfoException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.ObjectAlreadySponsoredException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferRequestFlow}. */
public class ContactTransferRequestFlowTest
extends ContactTransferFlowTestCase<ContactTransferRequestFlow, ContactResource> {
public ContactTransferRequestFlowTest() {
// We need the transfer to happen at exactly this time in order for the response to match up.
clock.setTo(DateTime.parse("2000-06-08T22:00:00.0Z"));
}
@Before
public void setUp() throws Exception {
setEppInput("contact_transfer_request.xml");
setClientIdForFlow("NewRegistrar");
contact = persistActiveContact("sh8013");
clock.advanceOneMilli();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
DateTime afterTransfer =
clock.nowUtc().plus(RegistryEnvironment.get().config().getContactAutomaticTransferLength());
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have been requested. Verify correct fields were set.
contact = reloadResourceByUniqueId();
assertAboutContacts().that(contact)
.hasTransferStatus(TransferStatus.PENDING).and()
.hasTransferGainingClientId("NewRegistrar").and()
.hasTransferLosingClientId("TheRegistrar").and()
.hasTransferRequestTrid(getTrid()).and()
.hasCurrentSponsorClientId("TheRegistrar").and()
.hasPendingTransferExpirationTime(afterTransfer).and()
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.CONTACT_TRANSFER_REQUEST);
assertNoBillingEvents();
assertThat(getPollMessages("TheRegistrar", clock.nowUtc())).hasSize(1);
// If we fast forward AUTOMATIC_TRANSFER_DAYS the transfer should have happened.
assertAboutContacts().that(contact.cloneProjectedAtTime(afterTransfer))
.hasCurrentSponsorClientId("NewRegistrar");
assertThat(getPollMessages("NewRegistrar", afterTransfer)).hasSize(1);
assertThat(getPollMessages("TheRegistrar", afterTransfer)).hasSize(2);
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("contact_transfer_request.xml");
dryRunFlowAssertResponse(readFile("contact_transfer_request_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testFailure_noAuthInfo() throws Exception {
thrown.expect(MissingTransferRequestAuthInfoException.class);
doFailingTest("contact_transfer_request_no_authinfo.xml");
}
@Test
public void testFailure_badPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_request.xml");
}
@Test
public void testSuccess_clientApproved() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testSuccess_clientRejected() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testSuccess_clientCancelled() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testSuccess_serverApproved() throws Exception {
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testSuccess_serverCancelled() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testFailure_pending() throws Exception {
thrown.expect(AlreadyPendingTransferException.class);
contact = persistResource(
contact.asBuilder()
.setTransferData(contact.getTransferData().asBuilder()
.setTransferStatus(TransferStatus.PENDING)
.setPendingTransferExpirationTime(clock.nowUtc().plusDays(1))
.build())
.build());
doFailingTest("contact_transfer_request.xml");
}
@Test
public void testFailure_sponsoringClient() throws Exception {
thrown.expect(ObjectAlreadySponsoredException.class);
setClientIdForFlow("TheRegistrar");
doFailingTest("contact_transfer_request.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_request.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
doFailingTest("contact_transfer_request.xml");
}
}

View file

@ -0,0 +1,265 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException;
import com.google.domain.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException;
import com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
import com.google.domain.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
import com.google.domain.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException;
import com.google.domain.registry.model.contact.ContactAddress;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.contact.PostalInfo;
import com.google.domain.registry.model.contact.PostalInfo.Type;
import com.google.domain.registry.model.eppcommon.StatusValue;
import org.junit.Test;
/** Unit tests for {@link ContactUpdateFlow}. */
public class ContactUpdateFlowTest
extends ResourceFlowTestCase<ContactUpdateFlow, ContactResource> {
public ContactUpdateFlowTest() {
setEppInput("contact_update.xml");
}
private void doSuccessfulTest() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
assertTransactionalFlow(true);
runFlowAssertResponse(readFile("contact_update_response.xml"));
// Check that the contact was updated. This value came from the xml.
assertAboutContacts().that(reloadResourceByUniqueId())
.hasAuthInfoPwd("2fooBAR").and()
.hasOnlyOneHistoryEntryWhich()
.hasNoXml();
assertNoBillingEvents();
}
@Test
public void testDryRun() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
dryRunFlowAssertResponse(readFile("contact_update_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest();
}
@Test
public void testSuccess_removeClientUpdateProhibited() throws Exception {
setEppInput("contact_update_remove_client_update_prohibited.xml");
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
.build());
doSuccessfulTest();
assertAboutContacts().that(reloadResourceByUniqueId())
.doesNotHaveStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED);
}
@Test
public void testSuccess_updatingOnePostalInfoDeletesTheOther() throws Exception {
ContactResource contact =
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setLocalizedPostalInfo(new PostalInfo.Builder()
.setType(Type.LOCALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
.setCity("New York")
.setState("NY")
.setZip("10011")
.setCountryCode("US")
.build())
.build())
.build());
clock.advanceOneMilli();
// The test xml updates the internationalized postal info and should therefore implicitly delete
// the localized one since they are treated as a pair for update purposes.
assertAboutContacts().that(contact)
.hasNonNullLocalizedPostalInfo().and()
.hasInternationalizedPostalInfo(null);
runFlowAssertResponse(readFile("contact_update_response.xml"));
assertAboutContacts().that(reloadResourceByUniqueId())
.hasLocalizedPostalInfo(null).and()
.hasNonNullInternationalizedPostalInfo();
}
@Test
public void testSuccess_partialPostalInfoUpdate() throws Exception {
setEppInput("contact_update_partial_postalinfo.xml");
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setLocalizedPostalInfo(new PostalInfo.Builder()
.setType(Type.LOCALIZED)
.setName("A. Person")
.setOrg("Company Inc.")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 4th st", "5th Floor"))
.setCity("City")
.setState("AB")
.setZip("12345")
.setCountryCode("US")
.build())
.build())
.build());
clock.advanceOneMilli();
// The test xml updates the address of the postal info and should leave the name untouched.
runFlowAssertResponse(readFile("contact_update_response.xml"));
assertAboutContacts().that(reloadResourceByUniqueId()).hasLocalizedPostalInfo(
new PostalInfo.Builder()
.setType(Type.LOCALIZED)
.setName("A. Person")
.setOrg("Company Inc.")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("456 5th st"))
.setCity("Place")
.setState("CD")
.setZip("54321")
.setCountryCode("US")
.build())
.build());
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistDeletedContact(getUniqueIdFromCommand(), clock.nowUtc());
runFlow();
}
@Test
public void testFailure_clientProhibitedStatusValue() throws Exception {
thrown.expect(StatusNotClientSettableException.class);
setEppInput("contact_update_prohibited_status.xml");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testSuccess_superuserClientProhibitedStatusValue() throws Exception {
setEppInput("contact_update_prohibited_status.xml");
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
readFile("contact_update_response.xml"));
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("contact_update_response.xml"));
}
@Test
public void testSuccess_superuserClientUpdateProhibited() throws Exception {
setEppInput("contact_update_prohibited_status.xml");
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
.build());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
readFile("contact_update_response.xml"));
assertAboutContacts().that(reloadResourceByUniqueId())
.hasStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED).and()
.hasStatusValue(StatusValue.SERVER_DELETE_PROHIBITED);
}
@Test
public void testFailure_clientUpdateProhibited() throws Exception {
thrown.expect(ResourceHasClientUpdateProhibitedException.class);
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
.build());
runFlow();
}
@Test
public void testFailure_serverUpdateProhibited() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.SERVER_UPDATE_PROHIBITED))
.build());
runFlow();
}
@Test
public void testSuccess_nonAsciiInLocAddress() throws Exception {
setEppInput("contact_update_hebrew_loc.xml");
doSuccessfulTest();
}
@Test
public void testFailure_nonAsciiInIntAddress() throws Exception {
thrown.expect(BadInternationalizedPostalInfoException.class);
setEppInput("contact_update_hebrew_int.xml");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testFailure_declineDisclosure() throws Exception {
thrown.expect(DeclineContactDisclosureFieldDisallowedPolicyException.class);
setEppInput("contact_update_decline_disclosure.xml");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
}

View file

@ -0,0 +1,13 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<contact:check
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:id>sah8013</contact:id>
<contact:id>8013sah</contact:id>
</contact:check>
</check>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,60 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<contact:check
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>contact1</contact:id>
<contact:id>contact2</contact:id>
<contact:id>contact3</contact:id>
<contact:id>contact4</contact:id>
<contact:id>contact5</contact:id>
<contact:id>contact6</contact:id>
<contact:id>contact7</contact:id>
<contact:id>contact8</contact:id>
<contact:id>contact9</contact:id>
<contact:id>contact10</contact:id>
<contact:id>contact11</contact:id>
<contact:id>contact12</contact:id>
<contact:id>contact13</contact:id>
<contact:id>contact14</contact:id>
<contact:id>contact15</contact:id>
<contact:id>contact16</contact:id>
<contact:id>contact17</contact:id>
<contact:id>contact18</contact:id>
<contact:id>contact19</contact:id>
<contact:id>contact20</contact:id>
<contact:id>contact21</contact:id>
<contact:id>contact22</contact:id>
<contact:id>contact23</contact:id>
<contact:id>contact24</contact:id>
<contact:id>contact25</contact:id>
<contact:id>contact26</contact:id>
<contact:id>contact27</contact:id>
<contact:id>contact28</contact:id>
<contact:id>contact29</contact:id>
<contact:id>contact30</contact:id>
<contact:id>contact31</contact:id>
<contact:id>contact32</contact:id>
<contact:id>contact33</contact:id>
<contact:id>contact34</contact:id>
<contact:id>contact35</contact:id>
<contact:id>contact36</contact:id>
<contact:id>contact37</contact:id>
<contact:id>contact38</contact:id>
<contact:id>contact39</contact:id>
<contact:id>contact40</contact:id>
<contact:id>contact41</contact:id>
<contact:id>contact42</contact:id>
<contact:id>contact43</contact:id>
<contact:id>contact44</contact:id>
<contact:id>contact45</contact:id>
<contact:id>contact46</contact:id>
<contact:id>contact47</contact:id>
<contact:id>contact48</contact:id>
<contact:id>contact49</contact:id>
<contact:id>contact50</contact:id>
</contact:check>
</check>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,61 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<contact:check
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>contact1</contact:id>
<contact:id>contact2</contact:id>
<contact:id>contact3</contact:id>
<contact:id>contact4</contact:id>
<contact:id>contact5</contact:id>
<contact:id>contact6</contact:id>
<contact:id>contact7</contact:id>
<contact:id>contact8</contact:id>
<contact:id>contact9</contact:id>
<contact:id>contact10</contact:id>
<contact:id>contact11</contact:id>
<contact:id>contact12</contact:id>
<contact:id>contact13</contact:id>
<contact:id>contact14</contact:id>
<contact:id>contact15</contact:id>
<contact:id>contact16</contact:id>
<contact:id>contact17</contact:id>
<contact:id>contact18</contact:id>
<contact:id>contact19</contact:id>
<contact:id>contact20</contact:id>
<contact:id>contact21</contact:id>
<contact:id>contact22</contact:id>
<contact:id>contact23</contact:id>
<contact:id>contact24</contact:id>
<contact:id>contact25</contact:id>
<contact:id>contact26</contact:id>
<contact:id>contact27</contact:id>
<contact:id>contact28</contact:id>
<contact:id>contact29</contact:id>
<contact:id>contact30</contact:id>
<contact:id>contact31</contact:id>
<contact:id>contact32</contact:id>
<contact:id>contact33</contact:id>
<contact:id>contact34</contact:id>
<contact:id>contact35</contact:id>
<contact:id>contact36</contact:id>
<contact:id>contact37</contact:id>
<contact:id>contact38</contact:id>
<contact:id>contact39</contact:id>
<contact:id>contact40</contact:id>
<contact:id>contact41</contact:id>
<contact:id>contact42</contact:id>
<contact:id>contact43</contact:id>
<contact:id>contact44</contact:id>
<contact:id>contact45</contact:id>
<contact:id>contact46</contact:id>
<contact:id>contact47</contact:id>
<contact:id>contact48</contact:id>
<contact:id>contact49</contact:id>
<contact:id>contact50</contact:id>
<contact:id>contact51</contact:id>
</contact:check>
</check>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,26 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:chkData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:cd>
<contact:id avail="1">sh8013</contact:id>
</contact:cd>
<contact:cd>
<contact:id avail="0">sah8013</contact:id>
<contact:reason>In use</contact:reason>
</contact:cd>
<contact:cd>
<contact:id avail="1">8013sah</contact:id>
</contact:cd>
</contact:chkData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,33 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,33 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="0">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,45 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>חנוך גולדפדר</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:postalInfo type="loc">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,45 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:postalInfo type="loc">
<contact:name>חנוך גולדפדר</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,18 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:creData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:crDate>1999-04-03T22:00:00.0Z</contact:crDate>
</contact:creData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<delete>
<contact:delete
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:delete>
</delete>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1001">
<msg>Command completed successfully; action pending</msg>
</result>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<info>
<contact:info
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:info>
</info>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,47 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:infData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:roid>SH8013-REP</contact:roid>
<contact:status s="clientDeleteProhibited"/>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:clID>TheRegistrar</contact:clID>
<contact:crID>NewRegistrar</contact:crID>
<contact:crDate>1999-04-03T22:00:00.0Z</contact:crDate>
<contact:upID>NewRegistrar</contact:upID>
<contact:upDate>1999-12-03T09:00:00.0Z</contact:upDate>
<contact:trDate>2000-04-08T09:00:00.0Z</contact:trDate>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:infData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,48 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:infData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:roid>SH8013-REP</contact:roid>
<contact:status s="linked"/>
<contact:status s="clientDeleteProhibited"/>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:clID>TheRegistrar</contact:clID>
<contact:crID>NewRegistrar</contact:crID>
<contact:crDate>1999-04-03T22:00:00.0Z</contact:crDate>
<contact:upID>NewRegistrar</contact:upID>
<contact:upDate>1999-12-03T09:00:00.0Z</contact:upDate>
<contact:trDate>2000-04-08T09:00:00.0Z</contact:trDate>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:infData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="approve">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientApproved</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-09T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="approve">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="cancel">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientCancelled</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-09T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="cancel">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="query">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>pending</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientApproved</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientCancelled</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientRejected</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>serverApproved</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>serverCancelled</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="query">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="query">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw roid="JD1234-REP">2fooBAR</contact:pw>
</contact:authInfo>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="reject">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientRejected</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-09T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="reject">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="request">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="request">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1001">
<msg>Command completed successfully; action pending</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>pending</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-08T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-13T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1001">
<msg>Command completed successfully; action pending</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>pending</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-08T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-08T22:15:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,36 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<contact:update
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:add>
<contact:status s="clientDeleteProhibited"/>
</contact:add>
<contact:chg>
<contact:postalInfo type="int">
<contact:org/>
<contact:addr>
<contact:street>124 Example Dr.</contact:street>
<contact:street>Suite 200</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice>+1.7034444444</contact:voice>
<contact:fax/>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:chg>
</contact:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,36 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<contact:update
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:add>
<contact:status s="clientDeleteProhibited"/>
</contact:add>
<contact:chg>
<contact:postalInfo type="int">
<contact:org/>
<contact:addr>
<contact:street>124 Example Dr.</contact:street>
<contact:street>Suite 200</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice>+1.7034444444</contact:voice>
<contact:fax/>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="0">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:chg>
</contact:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,47 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<contact:update
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:add>
<contact:status s="clientDeleteProhibited"/>
</contact:add>
<contact:chg>
<contact:postalInfo type="int">
<contact:org/>
<contact:addr>
<contact:street>123 רחוב סומסום</contact:street>
<contact:street>Suite 200</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:postalInfo type="loc">
<contact:org/>
<contact:addr>
<contact:street>124 Example Dr.</contact:street>
<contact:street>Suite 200</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice>+1.7034444444</contact:voice>
<contact:fax/>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:chg>
</contact:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,47 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<contact:update
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:add>
<contact:status s="clientDeleteProhibited"/>
</contact:add>
<contact:chg>
<contact:postalInfo type="int">
<contact:org/>
<contact:addr>
<contact:street>124 Example Dr.</contact:street>
<contact:street>Suite 200</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:postalInfo type="loc">
<contact:org/>
<contact:addr>
<contact:street>123 רחוב סומסום</contact:street>
<contact:street>Suite 200</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice>+1.7034444444</contact:voice>
<contact:fax/>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:chg>
</contact:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<contact:update
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:chg>
<contact:postalInfo type="loc">
<contact:addr>
<contact:street>456 5th st</contact:street>
<contact:city>Place</contact:city>
<contact:sp>CD</contact:sp>
<contact:pc>54321</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
</contact:chg>
</contact:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,36 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<contact:update
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:add>
<contact:status s="serverDeleteProhibited"/>
</contact:add>
<contact:chg>
<contact:postalInfo type="int">
<contact:org/>
<contact:addr>
<contact:street>124 Example Dr.</contact:street>
<contact:street>Suite 200</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice>+1.7034444444</contact:voice>
<contact:fax/>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:chg>
</contact:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<contact:update
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:rem>
<contact:status s="clientUpdateProhibited"/>
</contact:rem>
</contact:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,155 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.ResourceCheckFlow.TooManyResourceChecksException;
import com.google.domain.registry.flows.ResourceFlow.BadCommandForRegistryPhaseException;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.TldDoesNotExistException;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.testing.DatastoreHelper;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ClaimsCheckFlow}. */
public class ClaimsCheckFlowTest extends ResourceFlowTestCase<ClaimsCheckFlow, DomainResource> {
public ClaimsCheckFlowTest() {
setEppInput("domain_check_claims.xml");
}
@Before
public void initCheckTest() {
createTld("tld");
persistResource(Registry.get("tld").asBuilder().build());
}
protected void doSuccessfulTest(String expectedXmlFilename) throws Exception {
assertTransactionalFlow(false);
assertNoHistory(); // Checks don't create a history event.
assertNoBillingEvents(); // Checks are always free.
runFlowAssertResponse(readFile(expectedXmlFilename));
}
@Test
public void testSuccess_noClaims() throws Exception {
doSuccessfulTest("domain_check_claims_response_none.xml");
}
@Test
public void testSuccess_sunrush() throws Exception {
createTld("tld", TldState.SUNRUSH);
persistResource(Registry.get("tld").asBuilder().build());
doSuccessfulTest("domain_check_claims_response_none.xml");
}
@Test
public void testSuccess_quietPeriod() throws Exception {
createTld("tld", TldState.QUIET_PERIOD);
persistResource(Registry.get("tld").asBuilder().build());
doSuccessfulTest("domain_check_claims_response_none.xml");
}
@Test
public void testSuccess_oneClaim() throws Exception {
persistClaimsList(
ImmutableMap.of("example2", "2013041500/2/6/9/rJ1NrDO92vDsAzf7EQzgjX4R0000000001"));
doSuccessfulTest("domain_check_claims_response.xml");
}
@Test
public void testSuccess_multipleTlds() throws Exception {
setEppInput("domain_check_claims_multiple_tlds.xml");
createTld("tld1");
createTld("tld2");
persistClaimsList(
ImmutableMap.of("example", "2013041500/2/6/9/rJ1NrDO92vDsAzf7EQzgjX4R0000000001"));
doSuccessfulTest("domain_check_claims_response_multiple_tlds.xml");
}
@Test
public void testSuccess_50IdsAllowed() throws Exception {
// Make sure we don't have a regression that reduces the number of allowed checks.
setEppInput("domain_check_claims_50.xml");
runFlow();
}
@Test
public void testFailure_TooManyIds() throws Exception {
setEppInput("domain_check_claims_51.xml");
thrown.expect(TooManyResourceChecksException.class);
runFlow();
}
@Test
public void testFailure_tldDoesntExist() throws Exception {
setEppInput("domain_check_claims_bad_tld.xml");
thrown.expect(TldDoesNotExistException.class);
runFlow();
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
DatastoreHelper.persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
runFlow();
}
@Test
public void testFailure_predelgation() throws Exception {
createTld("tld", TldState.PREDELEGATION);
persistResource(Registry.get("tld").asBuilder().build());
setEppInput("domain_check_claims.xml");
thrown.expect(BadCommandForRegistryPhaseException.class);
runFlow();
}
@Test
public void testFailure_sunrise() throws Exception {
createTld("tld", TldState.SUNRISE);
persistResource(Registry.get("tld").asBuilder().build());
setEppInput("domain_check_claims.xml");
thrown.expect(BadCommandForRegistryPhaseException.class);
runFlow();
}
@Test
public void testFailure_multipleTlds_oneHasEndedClaims() throws Exception {
createTld("tld1");
createTld("tld2");
persistResource(Registry.get("tld2").asBuilder()
.setClaimsPeriodEnd(clock.nowUtc().minusMillis(1))
.build());
setEppInput("domain_check_claims_multiple_tlds.xml");
thrown.expect(BadCommandForRegistryPhaseException.class);
runFlow();
}
}

View file

@ -0,0 +1,533 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainApplication;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistReservedList;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainApplicationSubject.assertAboutApplications;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import static com.google.domain.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertNoTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.EppException.UnimplementedExtensionException;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.domain.DomainAllocateFlow.HasFinalStatusException;
import com.google.domain.registry.flows.domain.DomainAllocateFlow.MissingAllocateCreateExtensionException;
import com.google.domain.registry.flows.domain.DomainAllocateFlow.MissingApplicationException;
import com.google.domain.registry.flows.domain.DomainAllocateFlow.OnlySuperuserCanAllocateException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.billing.BillingEvent.Flag;
import com.google.domain.registry.model.billing.BillingEvent.Reason;
import com.google.domain.registry.model.domain.DomainApplication;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
import com.google.domain.registry.model.domain.launch.LaunchInfoResponseExtension;
import com.google.domain.registry.model.domain.launch.LaunchNotice;
import com.google.domain.registry.model.domain.launch.LaunchPhase;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.ofy.ObjectifyService;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.smd.EncodedSignedMark;
import com.google.domain.registry.testing.DatastoreHelper;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import com.googlecode.objectify.Ref;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainAllocateFlow}. */
public class DomainAllocateFlowTest
extends ResourceFlowTestCase<DomainAllocateFlow, DomainResource> {
// These constants come from "domain_allocate.xml" and its variants.
private static final DateTime APPLICATION_TIME = DateTime.parse("2010-08-16T10:00:00.0Z");
private static final String SMD_ID = "1-1";
private static final String CLIENT_ID = "TheRegistrar";
private static final Trid TRID = Trid.create("ABC-123");
/** The applicationId, expressed as a base 10 String. */
private String applicationId = "2-TLD";
private DomainApplication application;
private HistoryEntry historyEntry;
@Before
public void initAllocateTest() throws Exception {
setEppInput("domain_allocate.xml", ImmutableMap.of("APPLICATIONID", "2-TLD"));
clock.setTo(APPLICATION_TIME);
// We must manually set the flow class since this flow is not registered in the flow registry.
flowClass = DomainAllocateFlow.class;
}
private void setupDomainApplication(String tld, TldState tldState) throws Exception {
createTld(tld, tldState);
persistResource(Registry.get(tld).asBuilder().setReservedLists(persistReservedList(
tld + "-reserved",
"reserved-label,FULLY_BLOCKED",
"collision-label,NAME_COLLISION")).build());
String domainName = getUniqueIdFromCommand();
application = persistResource(newDomainApplication(domainName).asBuilder()
.setEncodedSignedMarks(ImmutableList.of(EncodedSignedMark.create("base64", "abcdef")))
.build());
for (int i = 1; i <= 14; ++i) {
persistActiveHost(String.format("ns%d.example.net", i));
}
persistActiveContact("jd1234");
persistActiveContact("sh8013");
// Add a history entry under this application that corresponds to its creation.
persistResource(
new HistoryEntry.Builder()
.setParent(application)
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_CREATE)
.setModificationTime(APPLICATION_TIME)
.setTrid(TRID)
.build());
clock.setTo(DateTime.parse("2010-09-16T10:00:00.0Z"));
}
private void doSuccessfulTest(int nameservers) throws Exception {
assertTransactionalFlow(true);
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
readFile("domain_allocate_response.xml"));
// Check that the domain was created and persisted with a history entry.
DomainResource domain = reloadResourceByUniqueId();
assertAboutDomains().that(domain)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.DOMAIN_ALLOCATE);
historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_ALLOCATE);
// The domain gets the sunrush add grace period if no nameservers were set during allocation.
boolean sunrushAddGracePeriod = (nameservers == 0);
// The application should be marked as allocated, with a new history entry.
DomainApplication application =
loadByUniqueId(DomainApplication.class, applicationId, clock.nowUtc());
assertAboutApplications().that(application)
.hasApplicationStatus(ApplicationStatus.ALLOCATED).and()
.hasHistoryEntryAtIndex(1)
.which().hasType(HistoryEntry.Type.DOMAIN_APPLICATION_STATUS_UPDATE);
String domainName = getUniqueIdFromCommand();
// There should be a poll message for the allocated application (and one for generic autorenew).
assertPollMessages(
new PollMessage.OneTime.Builder()
.setClientId(CLIENT_ID)
.setEventTime(clock.nowUtc())
.setMsg("Domain was allocated")
.setResponseData(ImmutableList.of(DomainPendingActionNotificationResponse.create(
domainName, true, TRID, clock.nowUtc())))
.setResponseExtensions(ImmutableList.of(new LaunchInfoResponseExtension.Builder()
.setApplicationId(applicationId)
.setPhase(LaunchPhase.SUNRISE) // This comes from newDomainApplication()
.setApplicationStatus(ApplicationStatus.ALLOCATED)
.build()))
.setParent(historyEntry)
.build(),
new PollMessage.Autorenew.Builder()
.setTargetId(domainName)
.setClientId(CLIENT_ID)
.setEventTime(clock.nowUtc().plusYears(2))
.setMsg("Domain was auto-renewed.")
.setParent(historyEntry)
.build());
// There should be a bill for the create and a recurring autorenew event.
BillingEvent.OneTime createBillingEvent = new BillingEvent.OneTime.Builder()
.setReason(Reason.CREATE)
.setFlags(ImmutableSet.of(Flag.ALLOCATION, Flag.SUNRISE))
.setTargetId(domainName)
.setClientId(CLIENT_ID)
.setCost(Money.of(USD, 26))
.setPeriodYears(2)
.setEventTime(clock.nowUtc())
.setBillingTime(clock.nowUtc().plus(sunrushAddGracePeriod
? Registry.get("tld").getSunrushAddGracePeriodLength()
: Registry.get("tld").getAddGracePeriodLength()))
.setParent(historyEntry)
.build();
assertBillingEvents(
createBillingEvent,
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(domainName)
.setClientId(CLIENT_ID)
.setEventTime(domain.getRegistrationExpirationTime())
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntry)
.build());
assertGracePeriods(
domain.getGracePeriods(),
ImmutableMap.of(
GracePeriod.create(
sunrushAddGracePeriod ? GracePeriodStatus.SUNRUSH_ADD : GracePeriodStatus.ADD,
clock.nowUtc().plus(sunrushAddGracePeriod
? Registry.get("tld").getSunrushAddGracePeriodLength()
: Registry.get("tld").getAddGracePeriodLength()),
CLIENT_ID,
null),
createBillingEvent));
assertThat(domain.getAutorenewBillingEvent().get().getEventTime())
.isEqualTo(domain.getRegistrationExpirationTime());
assertThat(domain.getApplicationTime()).isEqualTo(APPLICATION_TIME);
assertThat(domain.getApplication()).isEqualTo(Ref.create(application));
if (nameservers == 0) {
assertNoDnsTasksEnqueued();
} else {
assertDnsTasksEnqueued(domainName);
}
}
private void runFlowAsSuperuser() throws Exception {
assertTransactionalFlow(true);
runFlow(CommitMode.LIVE, UserPrivileges.SUPERUSER);
}
@Test
public void testSuccess() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
doSuccessfulTest(2);
}
@Test
public void testSuccess_sunrushAddGracePeriod() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_no_nameservers.xml");
doSuccessfulTest(0);
}
@Test
public void testSuccess_nonDefaultAddGracePeriod() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
persistResource(Registry.get("tld").asBuilder()
.setAddGracePeriodLength(Duration.standardMinutes(6))
.build());
doSuccessfulTest(2);
}
@Test
public void testSuccess_nonDefaultSunrushAddGracePeriod() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
persistResource(Registry.get("tld").asBuilder()
.setSunrushAddGracePeriodLength(Duration.standardMinutes(9))
.build());
doSuccessfulTest(2);
}
@Test
public void testSuccess_existedButWasDeleted() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
persistDeletedDomain(getUniqueIdFromCommand(), clock.nowUtc());
clock.advanceOneMilli();
doSuccessfulTest(2);
}
@Test
public void testSuccess_maxNumberOfNameservers() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_13_nameservers.xml");
doSuccessfulTest(13);
}
@Test
@Override
public void testRequiresLogin() throws Exception {
createTld("tld");
super.testRequiresLogin();
}
@Test
public void testSuccess_secDns() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_dsdata.xml");
doSuccessfulTest(2);
assertAboutDomains().that(reloadResourceByUniqueId())
.hasExactlyDsData(DelegationSignerData.create(
12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC")));
}
@Test
public void testSuccess_secDnsMaxRecords() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_dsdata_8_records.xml");
doSuccessfulTest(2);
assertThat(getOnlyGlobalResource(DomainResource.class)).isNotNull();
assertThat(reloadResourceByUniqueId().getDsData()).hasSize(8);
}
@Test
public void testSuccess_idn() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_idn.xml");
clock.advanceOneMilli();
runFlowAsSuperuser();
assertThat(getOnlyGlobalResource(DomainResource.class)).isNotNull();
assertDnsTasksEnqueued("xn--abc-873b2e7eb1k8a4lpjvv.tld");
}
private void doSuccessfulClaimsNoticeTest() throws Exception {
setEppInput("domain_allocate_claims_notice.xml");
runFlowAsSuperuser();
assertAboutDomains().that(getOnlyGlobalResource(DomainResource.class))
.hasLaunchNotice(LaunchNotice.create(
"370d0b7c9223372036854775807",
"tmch",
DateTime.parse("2011-08-16T09:00:00.0Z"),
DateTime.parse("2010-07-16T09:00:00.0Z")));
}
@Test
public void testSuccess_claimsNotice() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
doSuccessfulClaimsNoticeTest();
String expectedCsv = String.format(
"%s,example-one.tld,370d0b7c9223372036854775807,1,"
+ "2010-09-16T10:00:00.000Z,2010-07-16T09:00:00.000Z,2010-08-16T10:00:00.000Z",
reloadResourceByUniqueId().getRepoId());
assertTasksEnqueued(
"lordn-claims", new TaskMatcher().payload(expectedCsv).tag("tld"));
}
@Test
public void testSuccess_expiredClaim() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
clock.setTo(DateTime.parse("2011-08-17T09:00:00.0Z"));
doSuccessfulClaimsNoticeTest();
String expectedCsv = String.format(
"%s,example-one.tld,370d0b7c9223372036854775807,1,"
+ "2011-08-17T09:00:00.000Z,2010-07-16T09:00:00.000Z,2010-08-16T10:00:00.000Z",
reloadResourceByUniqueId().getRepoId());
assertTasksEnqueued("lordn-claims", new TaskMatcher().payload(expectedCsv).tag("tld"));
}
@Test
public void testSuccess_smdId() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_smd_id.xml");
doSuccessfulTest(2);
DomainResource domain = getOnlyGlobalResource(DomainResource.class);
assertThat(domain.getSmdId()).isEqualTo(SMD_ID);
String expectedCsv = String.format(
"%s,example-one.tld,1-1,1,2010-09-16T10:00:00.000Z,2010-08-16T10:00:00.000Z",
domain.getRepoId());
assertTasksEnqueued(
"lordn-sunrise", new TaskMatcher().payload(expectedCsv).tag("tld"));
}
@Test
public void testSuccess_collision() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_collision.xml");
assertNoDnsTasksEnqueued();
runFlowAsSuperuser();
assertAboutDomains()
.that(getOnlyGlobalResource(DomainResource.class))
.hasStatusValue(StatusValue.SERVER_HOLD);
assertNoDnsTasksEnqueued();
}
@Test
public void testSuccess_reserved() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_reserved.xml");
runFlowAsSuperuser();
assertThat(getOnlyGlobalResource(DomainResource.class)).isNotNull();
}
@Test
public void testSuccess_premiumName() throws Exception {
setEppInput("domain_allocate_premium.xml");
setupDomainApplication("example", TldState.QUIET_PERIOD);
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(true).build());
clock.advanceOneMilli();
runFlowAsSuperuser();
}
@Test
public void testSuccess_hexApplicationId() throws Exception {
setEppInput("domain_allocate.xml", ImmutableMap.of("APPLICATIONID", "A-TLD"));
applicationId = "A-TLD";
// Grab the next 8 ids so that when the application is created it gets dec 10, or hex A.
// (one additional ID goes to the reserved list created before the application).
for (int i = 1; i <= 8; i++) {
ObjectifyService.allocateId();
}
setupDomainApplication("tld", TldState.QUIET_PERIOD);
doSuccessfulTest(2);
}
@Test
public void testFailure_expiredAcceptance() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
doSuccessfulClaimsNoticeTest();
assertNoTasksEnqueued("lordn-sunrise");
}
@Test
public void testSuccess_missingClaimsNotice() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
persistClaimsList(
ImmutableMap.of("example-one", "2013041500/2/6/9/rJ1NrDO92vDsAzf7EQzgjX4R0000000001"));
doSuccessfulTest(2);
}
@Test
public void testFailure_alreadyExists() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
persistActiveDomain(getUniqueIdFromCommand());
thrown.expect(ResourceAlreadyExistsException.class);
runFlowAsSuperuser();
}
@Test
public void testSuccess_predelegation() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
createTld("tld", TldState.PREDELEGATION);
doSuccessfulTest(2);
}
@Test
public void testSuccess_sunrise() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
createTld("tld", TldState.SUNRISE);
doSuccessfulTest(2);
}
@Test
public void testSuccess_sunrush() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
createTld("tld", TldState.SUNRUSH);
doSuccessfulTest(2);
}
@Test
public void testSucess_quietPeriod() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
createTld("tld", TldState.QUIET_PERIOD);
doSuccessfulTest(2);
}
@Test
public void testFailure_applicationDeleted() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
persistResource(application.asBuilder().setDeletionTime(clock.nowUtc()).build());
thrown.expect(MissingApplicationException.class);
runFlowAsSuperuser();
}
@Test
public void testFailure_applicationRejected() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
persistResource(application.asBuilder()
.setApplicationStatus(ApplicationStatus.REJECTED)
.build());
thrown.expect(HasFinalStatusException.class);
runFlowAsSuperuser();
}
@Test
public void testFailure_applicationAllocated() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
persistResource(application.asBuilder()
.setApplicationStatus(ApplicationStatus.ALLOCATED)
.build());
thrown.expect(HasFinalStatusException.class);
runFlowAsSuperuser();
}
@Test
public void testFailure_launchExtension() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_create_claim_notice.xml");
thrown.expect(UnimplementedExtensionException.class);
runFlowAsSuperuser();
}
@Test
public void testFailure_applicationDoesNotExist() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_allocate_bad_application_roid.xml");
thrown.expect(MissingApplicationException.class);
runFlowAsSuperuser();
}
@Test
public void testFailure_missingAllocateCreateExtension() throws Exception {
setupDomainApplication("tld", TldState.QUIET_PERIOD);
setEppInput("domain_create.xml");
thrown.expect(MissingAllocateCreateExtensionException.class);
runFlowAsSuperuser();
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
setupDomainApplication("tld", TldState.QUIET_PERIOD);
DatastoreHelper.persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
runFlow();
}
@Test
public void testFailure_onlySuperuserCanAllocate() throws Exception {
setupDomainApplication("tld", TldState.GENERAL_AVAILABILITY);
clock.advanceOneMilli();
setEppInput("domain_allocate_no_nameservers.xml");
thrown.expect(OnlySuperuserCanAllocateException.class);
assertTransactionalFlow(true);
runFlow(CommitMode.LIVE, UserPrivileges.NORMAL);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,278 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainApplication;
import static com.google.domain.registry.testing.DatastoreHelper.newHostResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomainApplication;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.GenericEppResourceSubject.assertAboutEppResources;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.EppException.UnimplementedExtensionException;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceFlow.BadCommandForRegistryPhaseException;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.domain.DomainApplicationDeleteFlow.SunriseApplicationCannotBeDeletedInLandrushException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.ApplicationDomainNameMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.LaunchPhaseMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.domain.DomainApplication;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.domain.launch.LaunchPhase;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry.TldState;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainApplicationDeleteFlow}. */
public class DomainApplicationDeleteFlowTest
extends ResourceFlowTestCase<DomainApplicationDeleteFlow, DomainApplication> {
public DomainApplicationDeleteFlowTest() {
setEppInput("domain_delete_application.xml");
}
@Before
public void setUp() {
createTld("tld", TldState.SUNRUSH);
}
public void doSuccessfulTest() throws Exception {
assertTransactionalFlow(true);
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response.xml"));
// Check that the domain is fully deleted.
assertThat(reloadResourceByUniqueId()).isNull();
assertNoBillingEvents();
}
@Test
public void testDryRun() throws Exception {
persistResource(
newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD").build());
dryRunFlowAssertResponse(readFile("domain_delete_response.xml"));
}
@Test
public void testSuccess_contactsAndHostUnlinked() throws Exception {
// Persist a linked contact and host
persistActiveContact("sh8013");
persistResource(newHostResource("ns1.example.net"));
// Create the DomainApplication to test.
persistResource(newDomainApplication("example.tld").asBuilder()
.setRepoId("1-TLD")
.setRegistrant(
ReferenceUnion.create(
loadByUniqueId(ContactResource.class, "sh8013", clock.nowUtc())))
.setNameservers(ImmutableSet.of(
ReferenceUnion.create(
loadByUniqueId(HostResource.class, "ns1.example.net", clock.nowUtc()))))
.build());
doSuccessfulTest();
for (EppResource resource : new EppResource[]{
loadByUniqueId(ContactResource.class, "sh8013", clock.nowUtc()),
loadByUniqueId(HostResource.class, "ns1.example.net", clock.nowUtc()) }) {
assertAboutEppResources().that(resource).doesNotHaveStatusValue(StatusValue.LINKED);
}
}
@Test
public void testSuccess_clientDeleteProhibited() throws Exception {
persistResource(newDomainApplication("example.tld").asBuilder()
.setRepoId("1-TLD")
.addStatusValue(StatusValue.CLIENT_DELETE_PROHIBITED)
.build());
doSuccessfulTest();
}
@Test
public void testSuccess_serverDeleteProhibited() throws Exception {
persistResource(newDomainApplication("example.tld").asBuilder()
.setRepoId("1-TLD")
.addStatusValue(StatusValue.SERVER_DELETE_PROHIBITED)
.build());
doSuccessfulTest();
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistResource(newDomainApplication("example.tld").asBuilder()
.setRepoId("1-TLD")
.setDeletionTime(clock.nowUtc().minusSeconds(1))
.build());
runFlow();
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistResource(
newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD").build());
runFlow();
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
persistResource(
newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD").build());
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
persistResource(
newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD").build());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("domain_delete_response.xml"));
}
@Test
public void testFailure_sunriseDuringLandrush() throws Exception {
createTld("tld", TldState.LANDRUSH);
setEppInput("domain_delete_application_landrush.xml");
thrown.expect(SunriseApplicationCannotBeDeletedInLandrushException.class);
persistResource(newDomainApplication("example.tld")
.asBuilder()
.setRepoId("1-TLD")
.setPhase(LaunchPhase.SUNRISE)
.build());
runFlow();
}
@Test
public void testSuccess_superuserSunriseDuringLandrush() throws Exception {
createTld("tld", TldState.LANDRUSH);
setEppInput("domain_delete_application_landrush.xml");
persistResource(newDomainApplication("example.tld")
.asBuilder()
.setRepoId("1-TLD")
.setPhase(LaunchPhase.SUNRISE)
.build());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("domain_delete_response.xml"));
}
@Test
public void testSuccess_sunrushDuringLandrush() throws Exception {
createTld("tld", TldState.LANDRUSH);
setEppInput("domain_delete_application_landrush.xml");
persistResource(newDomainApplication("example.tld")
.asBuilder()
.setRepoId("1-TLD")
.setPhase(LaunchPhase.SUNRUSH)
.build());
doSuccessfulTest();
}
@Test
public void testSuccess_sunriseDuringSunrush() throws Exception {
createTld("tld", TldState.SUNRUSH);
setEppInput("domain_delete_application_sunrush.xml");
persistResource(newDomainApplication("example.tld")
.asBuilder()
.setRepoId("1-TLD")
.setPhase(LaunchPhase.SUNRISE)
.build());
doSuccessfulTest();
}
@Test
public void testFailure_mismatchedPhase() throws Exception {
thrown.expect(LaunchPhaseMismatchException.class);
setEppInput("domain_delete_application_landrush.xml");
persistResource(
newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD").build());
runFlow();
}
@Test
public void testFailure_wrongExtension() throws Exception {
thrown.expect(UnimplementedExtensionException.class);
setEppInput("domain_delete_application_wrong_extension.xml");
persistActiveDomainApplication("example.tld");
runFlow();
}
@Test
public void testFailure_predelegation() throws Exception {
thrown.expect(BadCommandForRegistryPhaseException.class);
createTld("tld", TldState.PREDELEGATION);
persistResource(
newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD").build());
runFlow();
}
@Test
public void testFailure_quietPeriod() throws Exception {
thrown.expect(BadCommandForRegistryPhaseException.class);
createTld("tld", TldState.QUIET_PERIOD);
persistResource(
newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD").build());
runFlow();
}
@Test
public void testFailure_generalAvailability() throws Exception {
thrown.expect(BadCommandForRegistryPhaseException.class);
createTld("tld", TldState.GENERAL_AVAILABILITY);
persistResource(
newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD").build());
runFlow();
}
@Test
public void testFailure_applicationIdForDifferentDomain() throws Exception {
thrown.expect(ApplicationDomainNameMismatchException.class);
persistResource(
newDomainApplication("invalid.tld").asBuilder().setRepoId("1-TLD").build());
runFlow();
}
}

View file

@ -0,0 +1,320 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TestDataHelper.loadFileWithSubstitutions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import com.google.domain.registry.flows.domain.DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException;
import com.google.domain.registry.flows.domain.DomainApplicationInfoFlow.MissingApplicationIdException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.ApplicationDomainNameMismatchException;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.domain.DesignatedContact;
import com.google.domain.registry.model.domain.DesignatedContact.Type;
import com.google.domain.registry.model.domain.DomainApplication;
import com.google.domain.registry.model.domain.DomainAuthInfo;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
import com.google.domain.registry.model.domain.launch.LaunchCreateExtension;
import com.google.domain.registry.model.domain.launch.LaunchPhase;
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.model.smd.EncodedSignedMark;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.EppLoader;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainApplicationInfoFlow}. */
public class DomainApplicationInfoFlowTest
extends ResourceFlowTestCase<DomainApplicationInfoFlow, DomainApplication> {
private ContactResource registrant;
private ContactResource contact;
private HostResource host1;
private HostResource host2;
private DomainApplication application;
private enum MarksState { MARKS_EXIST, NO_MARKS_EXIST }
private enum HostsState { HOSTS_EXIST, NO_HOSTS_EXIST }
@Before
public void resetClientId() {
setEppInput("domain_info_sunrise.xml");
sessionMetadata.setClientId("NewRegistrar");
createTld("tld", TldState.SUNRUSH);
}
private void persistTestEntities(HostsState hostsState, MarksState marksState) throws Exception {
registrant = persistActiveContact("jd1234");
contact = persistActiveContact("sh8013");
host1 = persistActiveHost("ns1.example.net");
host2 = persistActiveHost("ns1.example.tld");
application = persistResource(new DomainApplication.Builder()
.setRepoId("123-TLD")
.setFullyQualifiedDomainName("example.tld")
.setPhase(LaunchPhase.SUNRUSH)
.setCurrentSponsorClientId("NewRegistrar")
.setCreationClientId("TheRegistrar")
.setLastEppUpdateClientId("NewRegistrar")
.setCreationTimeForTest(DateTime.parse("1999-04-03T22:00:00.0Z"))
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
.setRegistrant(ReferenceUnion.create(registrant))
.setContacts(ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, ReferenceUnion.create(contact)),
DesignatedContact.create(Type.TECH, ReferenceUnion.create(contact))))
.setNameservers(hostsState.equals(HostsState.HOSTS_EXIST) ? ImmutableSet.of(
ReferenceUnion.create(host1), ReferenceUnion.create(host2)) : null)
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR")))
.addStatusValue(StatusValue.PENDING_CREATE)
.setApplicationStatus(ApplicationStatus.PENDING_VALIDATION)
.setEncodedSignedMarks(marksState.equals(MarksState.MARKS_EXIST)
// If we need to include an encoded signed mark, pull it out of the create xml.
? ImmutableList.of((EncodedSignedMark)
new EppLoader(this, "domain_create_sunrise_encoded_signed_mark.xml")
.getEpp()
.getSingleExtension(LaunchCreateExtension.class)
.getSignedMarks().get(0))
: null)
.build());
}
private void doSuccessfulTest(String expectedXmlFilename, HostsState hostsState)
throws Exception {
assertTransactionalFlow(false);
String expected = loadFileWithSubstitutions(
getClass(), expectedXmlFilename, ImmutableMap.of("ROID", "123-TLD"));
if (hostsState.equals(HostsState.NO_HOSTS_EXIST)) {
expected = expected.replaceAll("\"ok\"", "\"inactive\"");
}
runFlowAssertResponse(expected);
assertNoHistory();
assertNoBillingEvents();
}
private void doSuccessfulTest(String expectedXmlFilename) throws Exception {
persistTestEntities(HostsState.HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
doSuccessfulTest(expectedXmlFilename, HostsState.HOSTS_EXIST);
}
private void doSuccessfulTestNoNameservers(String expectedXmlFilename) throws Exception {
persistTestEntities(HostsState.NO_HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
doSuccessfulTest(expectedXmlFilename, HostsState.NO_HOSTS_EXIST);
}
@Test
public void testSuccess_quietPeriod() throws Exception {
createTld("tld", TldState.QUIET_PERIOD);
doSuccessfulTest("domain_info_sunrise_response.xml");
}
@Test
public void testSuccess_generalAvailability() throws Exception {
createTld("tld", TldState.GENERAL_AVAILABILITY);
doSuccessfulTest("domain_info_sunrise_response.xml");
}
@Test
public void testSuccess_requestedDefaultHosts_nameserversExist() throws Exception {
// Default is "all", which means nameservers since there can't be subordinates.
doSuccessfulTest("domain_info_sunrise_response.xml");
}
@Test
public void testSuccess_requestedDefaultHosts_noNameserversExist() throws Exception {
// Default is "all", which means nameservers since there can't be subordinates.
doSuccessfulTestNoNameservers("domain_info_sunrise_response_no_nameservers.xml");
}
@Test
public void testSuccess_requestedAllHosts_nameserversExist() throws Exception {
// "All" means nameservers since there can't be subordinates (same as "delegated").
setEppInput("domain_info_sunrise_all_hosts.xml");
doSuccessfulTest("domain_info_sunrise_response.xml");
}
@Test
public void testSuccess_requestedAllHosts_noNameserversExist() throws Exception {
// "All" means nameservers since there can't be subordinates (same as "delegated").
setEppInput("domain_info_sunrise_all_hosts.xml");
doSuccessfulTestNoNameservers("domain_info_sunrise_response_no_nameservers.xml");
}
@Test
public void testSuccess_requestedDelegatedHosts_nameserversExist() throws Exception {
// "Delegated" means nameservers since there can't be subordinates (same as "all").
setEppInput("domain_info_sunrise_delegated_hosts.xml");
doSuccessfulTest("domain_info_sunrise_response.xml");
}
@Test
public void testSuccess_requestedDelegatedHosts_noNameserversExist() throws Exception {
// "Delegated" means nameservers since there can't be subordinates (same as "all").
setEppInput("domain_info_sunrise_delegated_hosts.xml");
doSuccessfulTestNoNameservers("domain_info_sunrise_response_no_nameservers.xml");
}
@Test
public void testSuccess_requestedNoneHosts_nameserversExist() throws Exception {
setEppInput("domain_info_sunrise_none_hosts.xml");
doSuccessfulTestNoNameservers("domain_info_sunrise_response_no_nameservers.xml");
}
@Test
public void testSuccess_requestedNoneHosts_noNameserversExist() throws Exception {
setEppInput("domain_info_sunrise_none_hosts.xml");
doSuccessfulTestNoNameservers("domain_info_sunrise_response_no_nameservers.xml");
}
@Test
public void testSuccess_requestedDefaultMarks_noMarksExist() throws Exception {
persistTestEntities(HostsState.HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
doSuccessfulTest("domain_info_sunrise_response.xml", HostsState.HOSTS_EXIST);
}
@Test
public void testSuccess_requestedDefaultMarks_marksExist() throws Exception {
persistTestEntities(HostsState.HOSTS_EXIST, MarksState.MARKS_EXIST);
doSuccessfulTest("domain_info_sunrise_response.xml", HostsState.HOSTS_EXIST);
}
@Test
public void testSuccess_requestedNoMarks_marksExist() throws Exception {
setEppInput("domain_info_sunrise_no_marks.xml");
persistTestEntities(HostsState.HOSTS_EXIST, MarksState.MARKS_EXIST);
doSuccessfulTest("domain_info_sunrise_response.xml", HostsState.HOSTS_EXIST);
}
@Test
public void testSuccess_requestedNoMarks_noMarksExist() throws Exception {
setEppInput("domain_info_sunrise_no_marks.xml");
persistTestEntities(HostsState.HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
doSuccessfulTest("domain_info_sunrise_response.xml", HostsState.HOSTS_EXIST);
}
@Test
public void testSuccess_requestedIncludeMarks_marksExist() throws Exception {
setEppInput("domain_info_sunrise_include_marks.xml");
persistTestEntities(HostsState.HOSTS_EXIST, MarksState.MARKS_EXIST);
doSuccessfulTest("domain_info_sunrise_response_with_mark.xml", HostsState.HOSTS_EXIST);
}
@Test
public void testSuccess_requestedIncludeMarks_noMarksExist() throws Exception {
setEppInput("domain_info_sunrise_include_marks.xml");
persistTestEntities(HostsState.HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
doSuccessfulTest("domain_info_sunrise_response.xml", HostsState.HOSTS_EXIST);
}
@Test
public void testSuccess_secDns() throws Exception {
persistTestEntities(HostsState.NO_HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
// Add the dsData to the saved resource and change the nameservers to match the sample xml.
persistResource(application.asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(
12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
.setNameservers(ImmutableSet.of(
ReferenceUnion.create(host1), ReferenceUnion.create(host2)))
.build());
doSuccessfulTest("domain_info_sunrise_response_dsdata.xml", HostsState.NO_HOSTS_EXIST);
}
@Test
public void testSuccess_allocated() throws Exception {
persistTestEntities(HostsState.HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
// Update the application status of the saved resource.
persistResource(application.asBuilder()
.removeStatusValue(StatusValue.PENDING_CREATE)
.setApplicationStatus(ApplicationStatus.ALLOCATED)
.build());
doSuccessfulTest("domain_info_sunrise_allocated.xml", HostsState.HOSTS_EXIST);
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistResource(new DomainApplication.Builder()
.setRepoId("123-COM")
.setFullyQualifiedDomainName("timber.com")
.setDeletionTime(DateTime.now().minusDays(1))
.build());
runFlow();
}
@Test
public void testFailure_unauthorized() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
persistResource(
AppEngineRule.makeRegistrar1().asBuilder().setClientIdentifier("ClientZ").build());
sessionMetadata.setClientId("ClientZ");
persistTestEntities(HostsState.NO_HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
runFlow();
}
@Test
public void testFailure_applicationIdForDifferentDomain() throws Exception {
thrown.expect(ApplicationDomainNameMismatchException.class);
persistResource(new DomainApplication.Builder()
.setRepoId("123-TLD")
.setFullyQualifiedDomainName("invalid.tld")
.setPhase(LaunchPhase.SUNRUSH)
.build());
runFlow();
}
@Test
public void testFailure_noApplicationId() throws Exception {
thrown.expect(MissingApplicationIdException.class);
setEppInput("domain_info_sunrise_no_application_id.xml");
persistTestEntities(HostsState.NO_HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
runFlow();
}
@Test
public void testFailure_mismatchedLaunchPhase() throws Exception {
thrown.expect(ApplicationLaunchPhaseMismatchException.class);
persistTestEntities(HostsState.NO_HOSTS_EXIST, MarksState.NO_MARKS_EXIST);
application = persistResource(
application.asBuilder().setPhase(LaunchPhase.SUNRISE).build());
runFlow();
}
}

View file

@ -0,0 +1,667 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
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 com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.generateNewDomainRoid;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainApplication;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainApplicationSubject.assertAboutApplications;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.EppException.UnimplementedExtensionException;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException;
import com.google.domain.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException;
import com.google.domain.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException;
import com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
import com.google.domain.registry.flows.domain.BaseDomainUpdateFlow.EmptySecDnsUpdateException;
import com.google.domain.registry.flows.domain.BaseDomainUpdateFlow.MaxSigLifeChangeNotSupportedException;
import com.google.domain.registry.flows.domain.BaseDomainUpdateFlow.SecDnsAllUsageException;
import com.google.domain.registry.flows.domain.BaseDomainUpdateFlow.UrgentAttributeNotSupportedException;
import com.google.domain.registry.flows.domain.DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.LinkedResourceDoesNotExistException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.MissingAdminContactException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NameserverNotAllowedException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.domain.DesignatedContact;
import com.google.domain.registry.model.domain.DesignatedContact.Type;
import com.google.domain.registry.model.domain.DomainApplication;
import com.google.domain.registry.model.domain.DomainApplication.Builder;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.model.reporting.HistoryEntry;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainApplicationUpdateFlow}. */
public class DomainApplicationUpdateFlowTest
extends ResourceFlowTestCase<DomainApplicationUpdateFlow, DomainApplication> {
private static final DelegationSignerData SOME_DSDATA =
DelegationSignerData.create(1, 2, 3, new byte[]{0, 1, 2});
ContactResource sh8013Contact;
ContactResource mak21Contact;
ContactResource unusedContact;
public DomainApplicationUpdateFlowTest() {
// Note that "domain_update_sunrise.xml" tests adding and removing the same contact type.
setEppInput("domain_update_sunrise.xml");
}
@Before
public void setUp() {
createTld("tld", TldState.SUNRUSH);
}
private void persistReferencedEntities() {
// Grab the 1 id for use with the DomainApplication.
generateNewDomainRoid("tld");
for (int i = 1; i <= 14; ++i) {
persistActiveHost(String.format("ns%d.example.tld", i));
}
sh8013Contact = persistActiveContact("sh8013");
mak21Contact = persistActiveContact("mak21");
unusedContact = persistActiveContact("unused");
}
private DomainApplication persistApplication() throws Exception {
return persistResource(newApplicationBuilder()
.setContacts(ImmutableSet.of(
DesignatedContact.create(Type.TECH, ReferenceUnion.create(sh8013Contact)),
DesignatedContact.create(Type.ADMIN, ReferenceUnion.create(unusedContact))))
.setNameservers(ImmutableSet.of(ReferenceUnion.create(
loadByUniqueId(HostResource.class, "ns1.example.tld", clock.nowUtc()))))
.build());
}
private Builder newApplicationBuilder() throws Exception {
return newDomainApplication("example.tld").asBuilder().setRepoId("1-TLD");
}
private DomainApplication persistNewApplication() throws Exception {
return persistResource(newApplicationBuilder().build());
}
private void doSuccessfulTest() throws Exception {
assertTransactionalFlow(true);
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_update_response.xml"));
// Check that the application was updated. These values came from the xml.
DomainApplication application = reloadResourceByUniqueId();
assertAboutApplications().that(application)
.hasStatusValue(StatusValue.CLIENT_HOLD).and()
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE);
assertThat(application.getAuthInfo().getPw().getValue()).isEqualTo("2BARfoo");
// Check that the hosts and contacts have correct linked status
assertNoBillingEvents();
}
@Test
public void testDryRun() throws Exception {
persistReferencedEntities();
persistApplication();
dryRunFlowAssertResponse(readFile("domain_update_response.xml"));
}
@Test
public void testSuccess() throws Exception {
persistReferencedEntities();
persistApplication();
doSuccessfulTest();
}
@Test
public void testSuccess_maxNumberOfNameservers() throws Exception {
persistReferencedEntities();
persistApplication();
modifyApplicationToHave13Nameservers();
doSuccessfulTest();
}
@Test
public void testSuccess_removeContact() throws Exception {
setEppInput("domain_update_sunrise_remove_contact.xml");
persistReferencedEntities();
persistApplication();
doSuccessfulTest();
}
@Test
public void testSuccess_registrantMovedToTechContact() throws Exception {
setEppInput("domain_update_sunrise_registrant_to_tech.xml");
persistReferencedEntities();
ContactResource sh8013 = loadByUniqueId(ContactResource.class, "sh8013", clock.nowUtc());
persistResource(
newApplicationBuilder().setRegistrant(ReferenceUnion.create(sh8013)).build());
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_update_response.xml"));
}
@Test
public void testSuccess_multipleReferencesToSameContactRemoved() throws Exception {
setEppInput("domain_update_sunrise_remove_multiple_contacts.xml");
persistReferencedEntities();
ContactResource sh8013 = loadByUniqueId(ContactResource.class, "sh8013", clock.nowUtc());
ReferenceUnion<ContactResource> sh8013ReferenceUnion = ReferenceUnion.create(sh8013);
persistResource(newApplicationBuilder()
.setRegistrant(sh8013ReferenceUnion)
.setContacts(ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, sh8013ReferenceUnion),
DesignatedContact.create(Type.BILLING, sh8013ReferenceUnion),
DesignatedContact.create(Type.TECH, sh8013ReferenceUnion)))
.build());
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_update_response.xml"));
}
@Test
public void testSuccess_removeClientUpdateProhibited() throws Exception {
persistReferencedEntities();
persistResource(persistApplication().asBuilder().setStatusValues(
ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED)).build());
clock.advanceOneMilli();
runFlow();
assertAboutApplications().that(reloadResourceByUniqueId())
.doesNotHaveStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED);
}
private void doSecDnsSuccessfulTest(
String xmlFilename,
ImmutableSet<DelegationSignerData> originalDsData,
ImmutableSet<DelegationSignerData> expectedDsData)
throws Exception {
setEppInput(xmlFilename);
persistResource(newApplicationBuilder().setDsData(originalDsData).build());
assertTransactionalFlow(true);
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_update_response.xml"));
assertAboutApplications().that(reloadResourceByUniqueId())
.hasExactlyDsData(expectedDsData).and()
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE);
}
@Test
public void testSuccess_secDnsAdd() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_add.xml",
null,
ImmutableSet.of(DelegationSignerData.create(
12346, 3, 1, base16().decode("38EC35D5B3A34B44C39B"))));
}
@Test
public void testSuccess_secDnsAddPreservesExisting() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_add.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA, DelegationSignerData.create(
12346, 3, 1, base16().decode("38EC35D5B3A34B44C39B"))));
}
@Test
public void testSuccess_secDnsAddToMaxRecords() throws Exception {
ImmutableSet.Builder<DelegationSignerData> builder = new ImmutableSet.Builder<>();
for (int i = 0; i < 7; ++i) {
builder.add(DelegationSignerData.create(i, 2, 3, new byte[]{0, 1, 2}));
}
ImmutableSet<DelegationSignerData> commonDsData = builder.build();
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_add.xml",
commonDsData,
ImmutableSet.copyOf(
union(commonDsData, ImmutableSet.of(DelegationSignerData.create(
12346, 3, 1, base16().decode("38EC35D5B3A34B44C39B"))))));
}
@Test
public void testSuccess_secDnsRemove() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_rem.xml",
ImmutableSet.of(SOME_DSDATA, DelegationSignerData.create(
12346, 3, 1, base16().decode("38EC35D5B3A34B44C39B"))),
ImmutableSet.of(SOME_DSDATA));
}
@Test
public void testSuccess_secDnsRemoveAll() throws Exception {
// As an aside, this test also validates that it's ok to set the 'urgent' attribute to false.
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_rem_all.xml",
ImmutableSet.of(SOME_DSDATA, DelegationSignerData.create(
12346, 3, 1, base16().decode("38EC35D5B3A34B44C39B"))),
ImmutableSet.<DelegationSignerData>of());
}
@Test
public void testSuccess_secDnsAddRemove() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_add_rem.xml",
ImmutableSet.of(SOME_DSDATA, DelegationSignerData.create(
12345, 3, 1, base16().decode("38EC35D5B3A34B33C99B"))),
ImmutableSet.of(SOME_DSDATA, DelegationSignerData.create(
12346, 3, 1, base16().decode("38EC35D5B3A34B44C39B"))));
}
@Test
public void testSuccess_secDnsAddRemoveToMaxRecords() throws Exception {
ImmutableSet.Builder<DelegationSignerData> builder = new ImmutableSet.Builder<>();
for (int i = 0; i < 7; ++i) {
builder.add(DelegationSignerData.create(i, 2, 3, new byte[]{0, 1, 2}));
}
ImmutableSet<DelegationSignerData> commonDsData = builder.build();
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_add_rem.xml",
ImmutableSet.copyOf(
union(commonDsData, ImmutableSet.of(DelegationSignerData.create(
12345, 3, 1, base16().decode("38EC35D5B3A34B33C99B"))))),
ImmutableSet.copyOf(
union(commonDsData, ImmutableSet.of(DelegationSignerData.create(
12346, 3, 1, base16().decode("38EC35D5B3A34B44C39B"))))));
}
@Test
public void testSuccess_secDnsAddRemoveSame() throws Exception {
// Adding and removing the same dsData is a no-op because removes are processed first.
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_add_rem_same.xml",
ImmutableSet.of(SOME_DSDATA, DelegationSignerData.create(
12345, 3, 1, base16().decode("38EC35D5B3A34B33C99B"))),
ImmutableSet.of(SOME_DSDATA, DelegationSignerData.create(
12345, 3, 1, base16().decode("38EC35D5B3A34B33C99B"))));
}
@Test
public void testSuccess_secDnsRemoveAlreadyNotThere() throws Exception {
// Removing a dsData that isn't there is a no-op.
doSecDnsSuccessfulTest(
"domain_update_sunrise_dsdata_rem.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA));
}
private void doSecDnsFailingTest(Class<? extends Exception> expectedException, String xmlFilename)
throws Exception {
thrown.expect(expectedException);
setEppInput(xmlFilename);
persistReferencedEntities();
persistNewApplication();
runFlow();
}
@Test
public void testFailure_secDnsAllCannotBeFalse() throws Exception {
doSecDnsFailingTest(
SecDnsAllUsageException.class, "domain_update_sunrise_dsdata_rem_all_false.xml");
}
@Test
public void testFailure_secDnsEmptyNotAllowed() throws Exception {
doSecDnsFailingTest(EmptySecDnsUpdateException.class, "domain_update_sunrise_dsdata_empty.xml");
}
@Test
public void testFailure_secDnsUrgentNotSupported() throws Exception {
doSecDnsFailingTest(
UrgentAttributeNotSupportedException.class,
"domain_update_sunrise_dsdata_urgent.xml");
}
@Test
public void testFailure_secDnsChangeNotSupported() throws Exception {
doSecDnsFailingTest(
MaxSigLifeChangeNotSupportedException.class,
"domain_update_sunrise_maxsiglife.xml");
}
@Test
public void testFailure_secDnsTooManyDsRecords() throws Exception {
thrown.expect(TooManyDsRecordsException.class);
ImmutableSet.Builder<DelegationSignerData> builder = new ImmutableSet.Builder<>();
for (int i = 0; i < 8; ++i) {
builder.add(DelegationSignerData.create(i, 2, 3, new byte[]{0, 1, 2}));
}
setEppInput("domain_update_sunrise_dsdata_add.xml");
persistResource(newApplicationBuilder().setDsData(builder.build()).build());
runFlow();
}
private void modifyApplicationToHave13Nameservers() throws Exception {
ImmutableSet.Builder<ReferenceUnion<HostResource>> nameservers = new ImmutableSet.Builder<>();
for (int i = 1; i < 15; i++) {
if (i != 2) { // Skip 2 since that's the one that the tests will add.
nameservers.add(ReferenceUnion.create(loadByUniqueId(
HostResource.class, String.format("ns%d.example.tld", i), clock.nowUtc())));
}
}
persistResource(reloadResourceByUniqueId().asBuilder()
.setNameservers(nameservers.build())
.build());
}
@Test
public void testFailure_tooManyNameservers() throws Exception {
thrown.expect(TooManyNameserversException.class);
setEppInput("domain_update_sunrise_add_nameserver.xml");
persistReferencedEntities();
persistApplication();
// Modify application to have 13 nameservers. We will then remove one and add one in the test.
modifyApplicationToHave13Nameservers();
runFlow();
}
@Test
public void testFailure_wrongExtension() throws Exception {
thrown.expect(UnimplementedExtensionException.class);
setEppInput("domain_update_sunrise_wrong_extension.xml");
runFlow();
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistReferencedEntities();
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistReferencedEntities();
persistResource(newApplicationBuilder().setDeletionTime(START_OF_TIME).build());
runFlow();
}
@Test
public void testFailure_clientUpdateProhibited() throws Exception {
thrown.expect(ResourceHasClientUpdateProhibitedException.class);
setEppInput("domain_update_sunrise_authinfo.xml");
persistReferencedEntities();
persistResource(newApplicationBuilder().setStatusValues(
ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED)).build());
runFlow();
}
@Test
public void testFailure_serverUpdateProhibited() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistReferencedEntities();
persistResource(newApplicationBuilder().setStatusValues(
ImmutableSet.of(StatusValue.SERVER_UPDATE_PROHIBITED)).build());
runFlow();
}
private void doIllegalApplicationStatusTest(ApplicationStatus status) throws Exception {
thrown.expect(ApplicationStatusProhibitsUpdateException.class);
persistReferencedEntities();
persistResource(newApplicationBuilder().setApplicationStatus(status).build());
runFlow();
}
@Test
public void testFailure_allocatedApplicationStatus() throws Exception {
doIllegalApplicationStatusTest(ApplicationStatus.ALLOCATED);
}
@Test
public void testFailure_invalidApplicationStatus() throws Exception {
doIllegalApplicationStatusTest(ApplicationStatus.INVALID);
}
@Test
public void testFailure_rejectedApplicationStatus() throws Exception {
doIllegalApplicationStatusTest(ApplicationStatus.REJECTED);
}
@Test
public void testFailure_missingHost() throws Exception {
thrown.expect(
LinkedResourceDoesNotExistException.class,
"(ns2.example.tld)");
persistActiveHost("ns1.example.tld");
persistActiveContact("sh8013");
persistActiveContact("mak21");
persistNewApplication();
runFlow();
}
@Test
public void testFailure_missingContact() throws Exception {
thrown.expect(
LinkedResourceDoesNotExistException.class,
"(sh8013)");
persistActiveHost("ns1.example.tld");
persistActiveHost("ns2.example.tld");
persistActiveContact("mak21");
persistNewApplication();
runFlow();
}
@Test
public void testFailure_addingDuplicateContact() throws Exception {
thrown.expect(DuplicateContactForRoleException.class);
persistReferencedEntities();
persistActiveContact("foo");
persistNewApplication();
// Add a tech contact to the persisted entity, which should cause the flow to fail when it tries
// to add "mak21" as a second tech contact.
persistResource(reloadResourceByUniqueId().asBuilder().setContacts(ImmutableSet.of(
DesignatedContact.create(Type.TECH, ReferenceUnion.create(
loadByUniqueId(ContactResource.class, "foo", clock.nowUtc()))))).build());
runFlow();
}
@Test
public void testFailure_clientProhibitedStatusValue() throws Exception {
thrown.expect(StatusNotClientSettableException.class);
setEppInput("domain_update_sunrise_prohibited_status.xml");
persistReferencedEntities();
persistNewApplication();
runFlow();
}
@Test
public void testSuccess_superuserProhibitedStatusValue() throws Exception {
setEppInput("domain_update_sunrise_prohibited_status.xml");
persistReferencedEntities();
persistNewApplication();
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
readFile("domain_update_response.xml"));
}
@Test
public void testFailure_duplicateContactInCommand() throws Exception {
thrown.expect(DuplicateContactForRoleException.class);
setEppInput("domain_update_sunrise_duplicate_contact.xml");
persistReferencedEntities();
persistNewApplication();
runFlow();
}
@Test
public void testFailure_missingContactType() throws Exception {
// We need to test for missing type, but not for invalid - the schema enforces that for us.
thrown.expect(MissingContactTypeException.class);
setEppInput("domain_update_sunrise_missing_contact_type.xml");
persistReferencedEntities();
persistNewApplication();
runFlow();
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistReferencedEntities();
persistApplication();
runFlow();
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
persistReferencedEntities();
persistApplication();
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
persistReferencedEntities();
persistApplication();
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("domain_update_response.xml"));
}
@Test
public void testFailure_sameNameserverAddedAndRemoved() throws Exception {
thrown.expect(AddRemoveSameValueEppException.class);
setEppInput("domain_update_sunrise_add_remove_same_host.xml");
persistReferencedEntities();
persistResource(newApplicationBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(
loadByUniqueId(HostResource.class, "ns1.example.tld", clock.nowUtc()))))
.build());
runFlow();
}
@Test
public void testFailure_sameContactAddedAndRemoved() throws Exception {
thrown.expect(AddRemoveSameValueEppException.class);
setEppInput("domain_update_sunrise_add_remove_same_contact.xml");
persistReferencedEntities();
persistResource(newApplicationBuilder()
.setContacts(ImmutableSet.of(DesignatedContact.create(
Type.TECH,
ReferenceUnion.create(
loadByUniqueId(ContactResource.class, "sh8013", clock.nowUtc())))))
.build());
runFlow();
}
@Test
public void testFailure_removeAdmin() throws Exception {
thrown.expect(MissingAdminContactException.class);
setEppInput("domain_update_sunrise_remove_admin.xml");
persistReferencedEntities();
persistResource(newApplicationBuilder()
.setContacts(ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, ReferenceUnion.create(sh8013Contact)),
DesignatedContact.create(Type.TECH, ReferenceUnion.create(sh8013Contact))))
.build());
runFlow();
}
@Test
public void testFailure_removeTech() throws Exception {
thrown.expect(MissingTechnicalContactException.class);
setEppInput("domain_update_sunrise_remove_tech.xml");
persistReferencedEntities();
persistResource(newApplicationBuilder()
.setContacts(ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, ReferenceUnion.create(sh8013Contact)),
DesignatedContact.create(Type.TECH, ReferenceUnion.create(sh8013Contact))))
.build());
runFlow();
}
@Test
public void testFailure_newRegistrantNotWhitelisted() throws Exception {
setEppInput("domain_update_sunrise_registrant_to_tech.xml");
persistReferencedEntities();
persistApplication();
persistResource(
Registry.get("tld").asBuilder()
.setAllowedRegistrantContactIds(ImmutableSet.of("sha8013"))
.build());
clock.advanceOneMilli();
thrown.expect(RegistrantNotAllowedException.class);
runFlow();
}
@Test
public void testFailure_newNameserverNotWhitelisted() throws Exception {
persistReferencedEntities();
persistApplication();
persistResource(
Registry.get("tld").asBuilder()
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo"))
.build());
clock.advanceOneMilli();
thrown.expect(NameserverNotAllowedException.class);
runFlow();
}
@Test
public void testSuccess_nameserverAndRegistrantWhitelisted() throws Exception {
persistResource(
Registry.get("tld").asBuilder()
.setAllowedRegistrantContactIds(ImmutableSet.of("sh8013"))
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns2.example.tld"))
.build());
persistReferencedEntities();
persistApplication();
doSuccessfulTest();
}
}

View file

@ -0,0 +1,410 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.domain.registry.model.eppoutput.CheckData.DomainCheck.create;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistPremiumList;
import static com.google.domain.registry.testing.DatastoreHelper.persistReservedList;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.ResourceCheckFlow.TooManyResourceChecksException;
import com.google.domain.registry.flows.ResourceCheckFlowTestCase;
import com.google.domain.registry.flows.domain.DomainCheckFlow.OnlyCheckedNamesCanBeFeeCheckedException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.BadDomainNameCharacterException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.BadDomainNamePartsCountException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.DashesInThirdAndFourthException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.DomainLabelTooLongException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.EmptyDomainNamePartException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.InvalidPunycodeException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.LeadingDashException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.RestoresAreAlwaysForOneYearException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.TldDoesNotExistException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.TrailingDashException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.UnknownFeeCommandException;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.model.registry.label.ReservedList;
import com.google.domain.registry.testing.DatastoreHelper;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainCheckFlow}. */
public class DomainCheckFlowTest
extends ResourceCheckFlowTestCase<DomainCheckFlow, DomainResource> {
public DomainCheckFlowTest() {
setEppInput("domain_check_one_tld.xml");
}
private ReservedList createReservedList() throws Exception {
return persistReservedList(
"tld-reserved",
"reserved,FULLY_BLOCKED",
"anchor,RESERVED_FOR_ANCHOR_TENANT,foo2BAR",
"allowedinsunrise,ALLOWED_IN_SUNRISE",
"collision,NAME_COLLISION",
"premiumcollision,NAME_COLLISION");
}
@Before
public void initCheckTest() throws Exception {
createTld("tld", TldState.QUIET_PERIOD);
persistResource(Registry.get("tld").asBuilder().setReservedLists(createReservedList()).build());
}
@Test
public void testNothingExists() throws Exception {
doCheckTest(
create(true, "example1.tld", null),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
@Test
public void testOneExists() throws Exception {
persistActiveDomain("example1.tld");
doCheckTest(
create(false, "example1.tld", "In use"),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
@Test
public void testOneReserved() throws Exception {
setEppInput("domain_check_one_tld_reserved.xml");
doCheckTest(
create(false, "reserved.tld", "Reserved"),
create(false, "allowedinsunrise.tld", "Reserved for non-sunrise"),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
@Test
public void testAnchorTenantReserved() throws Exception {
setEppInput("domain_check_anchor.xml");
doCheckTest(create(false, "anchor.tld", "Reserved"));
}
@Test
public void testOneReserved_multipartTld() throws Exception {
createTld("tld.foo");
persistResource(
Registry.get("tld.foo")
.asBuilder()
.setReservedLists(persistReservedList(
"tld.foo", "reserved,FULLY_BLOCKED", "allowedinsunrise,ALLOWED_IN_SUNRISE"))
.build());
setEppInput("domain_check_one_multipart_tld_reserved.xml");
doCheckTest(
create(false, "reserved.tld.foo", "Reserved"),
create(false, "allowedinsunrise.tld.foo", "Reserved for non-sunrise"),
create(true, "example2.tld.foo", null),
create(true, "example3.tld.foo", null));
}
@Test
public void testOneExistsButWasDeleted() throws Exception {
persistDeletedDomain("example1.tld", clock.nowUtc());
doCheckTest(
create(true, "example1.tld", null),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
@Test
public void testDuplicatesAllowed() throws Exception {
setEppInput("domain_check_duplicates.xml");
doCheckTest(
create(true, "example1.tld", null),
create(true, "example1.tld", null));
}
@Test
public void testXmlMatches() throws Exception {
persistActiveDomain("example2.tld");
runFlowAssertResponse(readFile("domain_check_one_tld_response.xml"));
}
@Test
public void test50IdsAllowed() throws Exception {
// Make sure we don't have a regression that reduces the number of allowed checks.
setEppInput("domain_check_50.xml");
runFlow();
}
@Test
public void testTooManyIds() throws Exception {
setEppInput("domain_check_51.xml");
thrown.expect(TooManyResourceChecksException.class);
runFlow();
}
@Test
public void testWrongTld() throws Exception {
setEppInput("domain_check.xml");
thrown.expect(TldDoesNotExistException.class);
runFlow();
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
DatastoreHelper.persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
runFlow();
}
private void doFailingBadLabelTest(String label, Class<? extends Exception> expectedException)
throws Exception {
setEppInput("domain_check_template.xml", ImmutableMap.of("LABEL", label));
thrown.expect(expectedException);
runFlow();
}
@Test
public void testFailure_uppercase() throws Exception {
doFailingBadLabelTest("FOO.tld", BadDomainNameCharacterException.class);
}
@Test
public void testFailure_badCharacter() throws Exception {
doFailingBadLabelTest("test_example.tld", BadDomainNameCharacterException.class);
}
@Test
public void testFailure_leadingDash() throws Exception {
doFailingBadLabelTest("-example.tld", LeadingDashException.class);
}
@Test
public void testFailure_trailingDash() throws Exception {
doFailingBadLabelTest("example-.tld", TrailingDashException.class);
}
@Test
public void testFailure_tooLong() throws Exception {
doFailingBadLabelTest(Strings.repeat("a", 64) + ".tld", DomainLabelTooLongException.class);
}
@Test
public void testFailure_leadingDot() throws Exception {
doFailingBadLabelTest(".example.tld", EmptyDomainNamePartException.class);
}
@Test
public void testFailure_leadingDotTld() throws Exception {
doFailingBadLabelTest("foo..tld", EmptyDomainNamePartException.class);
}
@Test
public void testFailure_tooManyParts() throws Exception {
doFailingBadLabelTest("foo.example.tld", BadDomainNamePartsCountException.class);
}
@Test
public void testFailure_tooFewParts() throws Exception {
doFailingBadLabelTest("tld", BadDomainNamePartsCountException.class);
}
@Test
public void testFailure_invalidPunycode() throws Exception {
doFailingBadLabelTest("xn--abcdefg.tld", InvalidPunycodeException.class);
}
@Test
public void testFailure_dashesInThirdAndFourthPosition() throws Exception {
doFailingBadLabelTest("ab--cdefg.tld", DashesInThirdAndFourthException.class);
}
@Test
public void testFailure_tldDoesNotExist() throws Exception {
doFailingBadLabelTest("foo.nosuchtld", TldDoesNotExistException.class);
}
@Test
public void testFailure_invalidIdnCodePoints() throws Exception {
// .tld
doFailingBadLabelTest("xn--k3hel9n7bxlu1e.tld", InvalidIdnDomainLabelException.class);
}
@Test
public void testAvailExtension() throws Exception {
persistActiveDomain("example1.tld");
setEppInput("domain_check_avail.xml");
doCheckTest(
create(false, "example1.tld", "In use"),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
/**
* Test that premium names are shown as unavailable if the premium pricing extension is not
* declared at login.
*/
@Test
public void testAvailExtension_premiumDomainsAreUnavailableWithoutExtension() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
createTld("example");
setEppInput("domain_check_premium.xml");
doCheckTest(create(false, "rich.example", "Premium names require EPP ext."));
}
/**
* Test that premium names are always shown as available if the TLD does not require the premium
* pricing extension to register premium names.
*/
@Test
public void testAvailExtension_premiumDomainsAvailableIfNotRequiredByTld() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
setEppInput("domain_check_premium.xml");
doCheckTest(create(true, "rich.example", null));
}
/** Test multiyear periods and explicitly correct currency and that the avail extension is ok. */
@Test
public void testFeeExtension() throws Exception {
persistActiveDomain("example1.tld");
setEppInput("domain_check_fee.xml");
runFlowAssertResponse(readFile("domain_check_fee_response.xml"));
}
/** Test commands for create, renew, transfer and restore with implicit period and currency. */
@Test
public void testFeeExtension_multipleCommands() throws Exception {
setEppInput("domain_check_fee_multiple_commands.xml");
runFlowAssertResponse(readFile("domain_check_fee_multiple_commands_response.xml"));
}
/** Test the same as {@link #testFeeExtension_multipleCommands} with premium labels. */
@Test
public void testFeeExtension_premiumLabels() throws Exception {
createTld("example");
setEppInput("domain_check_fee_premium.xml");
runFlowAssertResponse(readFile("domain_check_fee_premium_response.xml"));
}
@Test
public void testFeeExtension_fractionalCost() throws Exception {
// Note that the response xml expects to see "11.10" with two digits after the decimal point.
// This works because Money.getAmount(), used in the flow, returns a BigDecimal that is set to
// display the number of digits that is conventional for the given currency.
persistResource(Registry.get("tld").asBuilder()
.setCreateBillingCost(Money.of(CurrencyUnit.USD, 11.1))
.build());
setEppInput("domain_check_fee_fractional.xml");
runFlowAssertResponse(readFile("domain_check_fee_fractional_response.xml"));
}
/** Test that create fees are properly omitted/classed on names on reserved lists. */
@Test
public void testFeeExtension_reservedName() throws Exception {
createTld("tld", TldState.QUIET_PERIOD);
persistResource(Registry.get("tld").asBuilder()
.setReservedLists(createReservedList())
.setPremiumList(persistPremiumList("tld", "premiumcollision,USD 70"))
.build());
setEppInput("domain_check_fee_reserved.xml");
runFlowAssertResponse(readFile("domain_check_fee_reserved_response.xml"));
}
@Test
public void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise() throws Exception {
createTld("tld", TldState.SUNRISE);
persistResource(Registry.get("tld").asBuilder()
.setReservedLists(createReservedList())
.setPremiumList(persistPremiumList("tld", "premiumcollision,USD 70"))
.build());
setEppInput("domain_check_fee_reserved.xml");
runFlowAssertResponse(readFile("domain_check_fee_reserved_sunrise_response.xml"));
}
@Test
public void testFeeExtension_wrongCurrency() throws Exception {
thrown.expect(CurrencyUnitMismatchException.class);
setEppInput("domain_check_fee_euro.xml");
runFlow();
}
@Test
public void testFeeExtension_periodNotInYears() throws Exception {
thrown.expect(BadPeriodUnitException.class);
setEppInput("domain_check_fee_bad_period.xml");
runFlow();
}
@Test
public void testFeeExtension_commandWithPhase() throws Exception {
thrown.expect(FeeChecksDontSupportPhasesException.class);
setEppInput("domain_check_fee_command_phase.xml");
runFlow();
}
@Test
public void testFeeExtension_commandSubphase() throws Exception {
thrown.expect(FeeChecksDontSupportPhasesException.class);
setEppInput("domain_check_fee_command_subphase.xml");
runFlow();
}
@Test
public void testFeeExtension_feeCheckNotInAvailabilityCheck() throws Exception {
thrown.expect(OnlyCheckedNamesCanBeFeeCheckedException.class);
setEppInput("domain_check_fee_not_in_avail.xml");
runFlow();
}
@Test
public void testFeeExtension_multiyearRestore() throws Exception {
thrown.expect(RestoresAreAlwaysForOneYearException.class);
setEppInput("domain_check_fee_multiyear_restore.xml");
runFlow();
}
@Test
public void testFeeExtension_unknownCommand() throws Exception {
thrown.expect(UnknownFeeCommandException.class);
setEppInput("domain_check_fee_unknown_command.xml");
runFlow();
}
@Test
public void testFeeExtension_invalidCommand() throws Exception {
thrown.expect(UnknownFeeCommandException.class);
setEppInput("domain_check_fee_invalid_command.xml");
runFlow();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,613 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.flows.domain.DomainTransferFlowTestCase.persistWithPendingTransfer;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.newHostResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import static com.google.domain.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
import static com.google.domain.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.SessionMetadata.SessionSource;
import com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
import com.google.domain.registry.flows.domain.DomainDeleteFlow.DomainToDeleteHasHostsException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.billing.BillingEvent.Flag;
import com.google.domain.registry.model.billing.BillingEvent.Reason;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferData;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainDeleteFlow}. */
public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, DomainResource> {
private DomainResource domain;
private HistoryEntry earlierHistoryEntry;
private static final DateTime TIME_BEFORE_FLOW = DateTime.parse("2000-06-06T22:00:00.0Z");
private static final DateTime A_MONTH_AGO = TIME_BEFORE_FLOW.minusMonths(1);
private static final DateTime A_MONTH_FROM_NOW = TIME_BEFORE_FLOW.plusMonths(1);
public DomainDeleteFlowTest() {
setEppInput("domain_delete.xml");
clock.setTo(TIME_BEFORE_FLOW);
}
@Before
public void initDomainTest() throws Exception {
createTld("tld");
}
private void setupSuccessfulTest() throws Exception {
createReferencedEntities(A_MONTH_FROM_NOW);
BillingEvent.Recurring autorenewBillingEvent = persistResource(
createAutorenewBillingEvent("TheRegistrar").build());
PollMessage.Autorenew autorenewPollMessage = persistResource(
createAutorenewPollMessage("TheRegistrar").build());
domain = persistResource(domain.asBuilder()
.setAutorenewBillingEvent(Ref.create(autorenewBillingEvent))
.setAutorenewPollMessage(Ref.create(autorenewPollMessage))
.build());
assertTransactionalFlow(true);
}
private void createReferencedEntities(DateTime expirationTime) throws Exception {
// Persist a linked contact.
ContactResource contact = persistActiveContact("sh8013");
domain = newDomainResource(getUniqueIdFromCommand()).asBuilder()
.setCreationTimeForTest(TIME_BEFORE_FLOW)
.setRegistrant(ReferenceUnion.create(contact))
.setRegistrationExpirationTime(expirationTime)
.build();
earlierHistoryEntry = persistResource(
new HistoryEntry.Builder()
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setParent(domain)
.build());
}
private void setupGracePeriods(GracePeriod... gracePeriods) throws Exception {
domain = persistResource(
domain.asBuilder().setGracePeriods(ImmutableSet.copyOf(gracePeriods)).build());
}
private void setupAutorenewGracePeriod() throws Exception {
createReferencedEntities(A_MONTH_AGO.plusYears(1));
BillingEvent.Recurring autorenewBillingEvent = persistResource(
createAutorenewBillingEvent("TheRegistrar")
.setEventTime(A_MONTH_AGO)
.build());
PollMessage.Autorenew autorenewPollMessage = persistResource(
createAutorenewPollMessage("TheRegistrar")
.setEventTime(A_MONTH_AGO)
.build());
domain = persistResource(
domain.asBuilder()
.setGracePeriods(ImmutableSet.of(GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
A_MONTH_AGO.plusDays(45),
"TheRegistrar",
Ref.create(autorenewBillingEvent))))
.setAutorenewBillingEvent(Ref.create(autorenewBillingEvent))
.setAutorenewPollMessage(Ref.create(autorenewPollMessage))
.build());
assertTransactionalFlow(true);
}
private void assertAutorenewClosedAndCancellationCreatedFor(
BillingEvent.OneTime graceBillingEvent,
HistoryEntry historyEntryDomainDelete) throws Exception {
DateTime eventTime = clock.nowUtc();
assertBillingEvents(
createAutorenewBillingEvent("TheRegistrar")
.setRecurrenceEndTime(eventTime)
.build(),
graceBillingEvent,
new BillingEvent.Cancellation.Builder()
.setReason(graceBillingEvent.getReason())
.setTargetId("example.tld")
.setClientId("TheRegistrar")
.setEventTime(eventTime)
.setBillingTime(TIME_BEFORE_FLOW.plusDays(1))
.setOneTimeEventRef(Ref.create(graceBillingEvent))
.setParent(historyEntryDomainDelete)
.build());
}
private void assertOnlyBillingEventIsClosedAutorenew(String clientId) throws Exception {
// There should be no billing events (even timed to when the transfer would have expired) except
// for the now closed autorenew one.
assertBillingEvents(
createAutorenewBillingEvent(clientId).setRecurrenceEndTime(clock.nowUtc()).build());
}
private BillingEvent.OneTime createBillingEvent(Reason reason, Money cost) {
return new BillingEvent.OneTime.Builder()
.setReason(reason)
.setTargetId("example.tld")
.setClientId("TheRegistrar")
.setCost(cost)
.setPeriodYears(2)
.setEventTime(TIME_BEFORE_FLOW.minusDays(4))
.setBillingTime(TIME_BEFORE_FLOW.plusDays(1))
.setParent(earlierHistoryEntry)
.build();
}
private BillingEvent.Recurring.Builder createAutorenewBillingEvent(String clientId) {
return new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.tld")
.setClientId(clientId)
.setEventTime(A_MONTH_FROM_NOW)
.setRecurrenceEndTime(END_OF_TIME)
.setParent(earlierHistoryEntry);
}
private PollMessage.Autorenew.Builder createAutorenewPollMessage(String clientId) {
return new PollMessage.Autorenew.Builder()
.setTargetId("example.tld")
.setClientId(clientId)
.setEventTime(A_MONTH_FROM_NOW)
.setAutorenewEndTime(END_OF_TIME)
.setParent(earlierHistoryEntry);
}
@Test
public void testDryRun() throws Exception {
setupSuccessfulTest();
setupGracePeriods(GracePeriod.create(
GracePeriodStatus.ADD, TIME_BEFORE_FLOW.plusDays(1), "foo", null));
dryRunFlowAssertResponse(readFile("domain_delete_response.xml"));
}
@Test
public void testDryRun_noGracePeriods() throws Exception {
setupSuccessfulTest();
dryRunFlowAssertResponse(readFile("domain_delete_response_pending.xml"));
}
private void doImmediateDeleteTest(GracePeriodStatus gracePeriodStatus, String responseFilename)
throws Exception {
// Persist the billing event so it can be retrieved for cancellation generation and checking.
setupSuccessfulTest();
BillingEvent.OneTime graceBillingEvent =
persistResource(createBillingEvent(Reason.CREATE, Money.of(USD, 123)));
setupGracePeriods(GracePeriod.forBillingEvent(gracePeriodStatus, graceBillingEvent));
// We should see exactly one poll message, which is for the autorenew 1 month in the future.
assertPollMessages(createAutorenewPollMessage("TheRegistrar").build());
clock.advanceOneMilli();
runFlowAssertResponse(readFile(responseFilename));
// Check that the domain is fully deleted.
assertThat(reloadResourceByUniqueId()).isNull();
// The add grace period is for a billable action, so it should trigger a cancellation.
assertAutorenewClosedAndCancellationCreatedFor(
graceBillingEvent,
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_DELETE));
assertDnsTasksEnqueued("example.tld");
// There should be no poll messages. The previous autorenew poll message should now be deleted.
assertThat(getPollMessages("TheRegistrar", A_MONTH_FROM_NOW)).isEmpty();
}
@Test
public void testSuccess_addGracePeriodResultsInImmediateDelete() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
doImmediateDeleteTest(GracePeriodStatus.ADD, "domain_delete_response.xml");
}
@Test
public void testSuccess_addGracePeriodCredit() throws Exception {
doImmediateDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml");
}
@Test
public void testSuccess_sunrushAddGracePeriodResultsInImmediateDelete() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
doImmediateDeleteTest(GracePeriodStatus.SUNRUSH_ADD, "domain_delete_response.xml");
}
private void doSuccessfulTest_noAddGracePeriod(String responseFilename) throws Exception {
// Persist the billing event so it can be retrieved for cancellation generation and checking.
setupSuccessfulTest();
BillingEvent.OneTime renewBillingEvent =
persistResource(createBillingEvent(Reason.RENEW, Money.of(USD, 456)));
setupGracePeriods(
GracePeriod.forBillingEvent(GracePeriodStatus.RENEW, renewBillingEvent),
// This grace period has no associated billing event, so it won't cause a cancellation.
GracePeriod.create(GracePeriodStatus.TRANSFER, TIME_BEFORE_FLOW.plusDays(1), "foo", null));
// We should see exactly one poll message, which is for the autorenew 1 month in the future.
assertPollMessages(createAutorenewPollMessage("TheRegistrar").build());
clock.advanceOneMilli();
runFlowAssertResponse(readFile(responseFilename));
DomainResource resource = reloadResourceByUniqueId();
// Check that the domain is in the pending delete state.
assertAboutDomains().that(resource)
.hasStatusValue(StatusValue.PENDING_DELETE).and()
.hasDeletionTime(clock.nowUtc().plus(Registry.get("tld").getRedemptionGracePeriodLength())
.plus(Registry.get("tld").getPendingDeleteLength())).and()
.hasDeletePollMessage().and()
.hasExactlyStatusValues(StatusValue.INACTIVE, StatusValue.PENDING_DELETE).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_DELETE);
// All existing grace periods that were for billable actions should cause cancellations.
assertAutorenewClosedAndCancellationCreatedFor(
renewBillingEvent,
getOnlyHistoryEntryOfType(resource, HistoryEntry.Type.DOMAIN_DELETE));
// All existing grace periods should be gone, and a new REDEMPTION one should be added.
assertThat(resource.getGracePeriods()).containsExactly(
GracePeriod.create(
GracePeriodStatus.REDEMPTION,
clock.nowUtc().plus(Registry.get("tld").getRedemptionGracePeriodLength()),
"TheRegistrar",
null));
// There should be a future poll message at the deletion time. The previous autorenew poll
// message should now be deleted.
DateTime deletionTime = resource.getDeletionTime();
assertThat(getPollMessages("TheRegistrar", deletionTime.minusMinutes(1)))
.isEmpty();
assertThat(getPollMessages("TheRegistrar", deletionTime)).hasSize(1);
assertThat(resource.getDeletePollMessage())
.isEqualTo(Key.create(getOnlyPollMessage("TheRegistrar")));
}
@Test
public void testSuccess_noAddGracePeriodResultsInPendingDelete() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending.xml");
}
@Test
public void testSuccess_renewGracePeriodCredit() throws Exception {
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml");
}
@Test
public void testSuccess_autorenewPollMessageIsNotDeleted() throws Exception {
setupSuccessfulTest();
// Modify the autorenew poll message so that it has unacked messages in the past. This should
// prevent it from being deleted when the domain is deleted.
persistResource(
reloadResourceByUniqueId().getAutorenewPollMessage().get().asBuilder()
.setEventTime(A_MONTH_FROM_NOW.minusYears(3))
.build());
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_pending.xml"));
// There should now be two poll messages; one for the delete of the domain (in the future), and
// another for the unacked autorenew messages.
DateTime deletionTime = reloadResourceByUniqueId().getDeletionTime();
assertThat(getPollMessages("TheRegistrar", deletionTime.minusMinutes(1)))
.hasSize(1);
assertThat(getPollMessages("TheRegistrar", deletionTime)).hasSize(2);
}
@Test
public void testSuccess_nonDefaultRedemptionGracePeriod() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
persistResource(
Registry.get("tld")
.asBuilder()
.setRedemptionGracePeriodLength(Duration.standardMinutes(7))
.build());
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending.xml");
}
@Test
public void testSuccess_nonDefaultPendingDeleteLength() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
persistResource(
Registry.get("tld")
.asBuilder()
.setPendingDeleteLength(Duration.standardMinutes(8))
.build());
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending.xml");
}
@Test
public void testSuccess_autoRenewGracePeriod() throws Exception {
setupAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_autorenew_fee.xml"));
}
@Test
public void testSuccess_autoRenewGracePeriod_priceChanges() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(
START_OF_TIME, Money.of(USD, 11), TIME_BEFORE_FLOW.minusDays(5), Money.of(USD, 20)))
.build());
setupAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_autorenew_fee.xml"));
}
@Test
public void testSuccess_pendingTransfer() throws Exception {
setClientIdForFlow("TheRegistrar");
setupSuccessfulTest();
// Modify the domain we are testing to include a pending transfer.
TransferData oldTransferData =
persistWithPendingTransfer(reloadResourceByUniqueId()).getTransferData();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_pending.xml"));
DomainResource domain = reloadResourceByUniqueId();
// Check that the domain is in the pending delete state.
// The PENDING_TRANSFER status should be gone.
assertAboutDomains().that(domain)
.hasExactlyStatusValues(StatusValue.INACTIVE, StatusValue.PENDING_DELETE).and()
.hasDeletionTime(clock.nowUtc().plus(Registry.get("tld").getRedemptionGracePeriodLength())
.plus(Registry.get("tld").getPendingDeleteLength())).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST,
HistoryEntry.Type.DOMAIN_DELETE);
// All existing grace periods should be gone, and a new REDEMPTION one should be added.
assertThat(domain.getGracePeriods()).containsExactly(
GracePeriod.create(
GracePeriodStatus.REDEMPTION,
clock.nowUtc().plus(Registry.get("tld").getRedemptionGracePeriodLength()),
"TheRegistrar",
null));
// The poll message (in the future) to the losing registrar for implicit ack should be gone.
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.isEmpty();
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar.
PollMessage gainingPollMessage = getOnlyPollMessage("NewRegistrar");
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.SERVER_CANCELLED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isFalse();
// There should be a future poll message to the losing registrar at the deletion time.
DateTime deletionTime = domain.getDeletionTime();
assertThat(getPollMessages("TheRegistrar", deletionTime.minusMinutes(1)))
.isEmpty();
assertThat(getPollMessages("TheRegistrar", deletionTime)).hasSize(1);
assertOnlyBillingEventIsClosedAutorenew("TheRegistrar");
// The keys written for server approve should be null, and the entities should all be deleted.
assertThat(domain.getTransferData().getServerApproveEntities()).isEmpty();
assertThat(domain.getTransferData().getServerApproveBillingEvent()).isNull();
assertThat(domain.getTransferData().getServerApproveAutorenewEvent()).isNull();
assertThat(domain.getTransferData().getServerApproveAutorenewPollMessage()).isNull();
assertThat(oldTransferData.getServerApproveBillingEvent().get()).isNull();
assertThat(oldTransferData.getServerApproveAutorenewEvent().get()).isNull();
assertThat(oldTransferData.getServerApproveAutorenewPollMessage().get()).isNull();
assertThat(oldTransferData.getServerApproveEntities()).isNotEmpty(); // Just a sanity check.
assertThat(ofy().load()
.keys(oldTransferData.getServerApproveEntities().toArray(new Key<?>[]{})))
.isEmpty();
}
@Test
public void testUnlinkingOfResources() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
setupSuccessfulTest();
// Persist the billing event so it can be retrieved for cancellation generation and checking.
BillingEvent.OneTime graceBillingEvent =
persistResource(createBillingEvent(Reason.CREATE, Money.of(USD, 123)));
// Use a grace period so that the delete is immediate, simplifying the assertions below.
setupGracePeriods(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, graceBillingEvent));
// Add a nameserver.
HostResource host = persistResource(newHostResource("ns1.example.tld"));
persistResource(loadByUniqueId(
DomainResource.class, getUniqueIdFromCommand(), clock.nowUtc())
.asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(host)))
.build());
// Persist another domain that's already been deleted and references this contact and host.
persistResource(newDomainResource("example1.tld").asBuilder()
.setRegistrant(
ReferenceUnion.create(loadByUniqueId(
ContactResource.class, "sh8013", clock.nowUtc())))
.setNameservers(ImmutableSet.of(ReferenceUnion.create(host)))
.setDeletionTime(START_OF_TIME)
.build());
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response.xml"));
assertDnsTasksEnqueued("example.tld");
assertAutorenewClosedAndCancellationCreatedFor(
graceBillingEvent,
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_DELETE));
}
@Test
public void testSuccess_deletedSubordinateDomain() throws Exception {
setupSuccessfulTest();
persistResource(
newHostResource("ns1." + getUniqueIdFromCommand()).asBuilder()
.setSuperordinateDomain(Ref.create(reloadResourceByUniqueId()))
.setDeletionTime(clock.nowUtc().minusDays(1))
.build());
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_pending.xml"));
assertDnsTasksEnqueued("example.tld");
assertOnlyBillingEventIsClosedAutorenew("TheRegistrar");
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistDeletedDomain(getUniqueIdFromCommand(), clock.nowUtc());
runFlow();
}
@Test
public void testFailure_hasSubordinateHosts() throws Exception {
thrown.expect(DomainToDeleteHasHostsException.class);
DomainResource domain = persistActiveDomain(getUniqueIdFromCommand());
HostResource subordinateHost = persistResource(
newHostResource("ns1." + getUniqueIdFromCommand()).asBuilder()
.setSuperordinateDomain(Ref.create(reloadResourceByUniqueId()))
.build());
domain = persistResource(domain.asBuilder()
.addSubordinateHost(subordinateHost.getFullyQualifiedHostName())
.build());
runFlow();
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistActiveDomain(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
setupSuccessfulTest();
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
setupSuccessfulTest();
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("domain_delete_response_pending.xml"));
}
@Test
public void testFailure_clientDeleteProhibited() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistResource(newDomainResource(getUniqueIdFromCommand()).asBuilder()
.addStatusValue(StatusValue.CLIENT_DELETE_PROHIBITED)
.build());
runFlow();
}
@Test
public void testFailure_serverDeleteProhibited() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistResource(newDomainResource(getUniqueIdFromCommand()).asBuilder()
.addStatusValue(StatusValue.SERVER_DELETE_PROHIBITED)
.build());
runFlow();
}
@Test
public void testFailure_pendingDelete() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistResource(newDomainResource(getUniqueIdFromCommand()).asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
runFlow();
}
@Test
public void testSuccess_metadata() throws Exception {
sessionMetadata.setSessionSource(SessionSource.TOOL);
setEppInput("domain_delete_metadata.xml");
setupSuccessfulTest();
clock.advanceOneMilli();
runFlow();
assertAboutDomains().that(reloadResourceByUniqueId())
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_DELETE);
assertAboutHistoryEntries()
.that(getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_DELETE))
.hasType(HistoryEntry.Type.DOMAIN_DELETE).and()
.hasMetadataReason("domain-delete-test").and()
.hasMetadataRequestedByRegistrar(false);
}
@Test
public void testFailure_metadataNotFromTool() throws Exception {
thrown.expect(OnlyToolCanPassMetadataException.class);
setEppInput("domain_delete_metadata.xml");
persistResource(newDomainResource(getUniqueIdFromCommand()));
runFlow();
}
}

View file

@ -0,0 +1,618 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TestDataHelper.loadFileWithSubstitutions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.RestoresAreAlwaysForOneYearException;
import com.google.domain.registry.model.billing.BillingEvent.Recurring;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.domain.DesignatedContact;
import com.google.domain.registry.model.domain.DesignatedContact.Type;
import com.google.domain.registry.model.domain.DomainAuthInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.testing.AppEngineRule;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainInfoFlow}. */
public class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainResource> {
private ContactResource registrant;
private ContactResource contact;
private HostResource host1;
private HostResource host2;
private HostResource host3;
private DomainResource domain;
@Before
public void setup() {
setEppInput("domain_info.xml");
sessionMetadata.setClientId("NewRegistrar");
clock.setTo(DateTime.parse("2005-03-03T22:00:00.000Z"));
createTld("tld");
persistResource(
AppEngineRule.makeRegistrar1().asBuilder().setClientIdentifier("ClientZ").build());
}
private void persistTestEntities(String domainName, boolean inactive) {
registrant = persistActiveContact("jd1234");
contact = persistActiveContact("sh8013");
host1 = persistActiveHost("ns1.example.tld");
host2 = persistActiveHost("ns1.example.net");
domain = persistResource(new DomainResource.Builder()
.setFullyQualifiedDomainName(domainName)
.setRepoId("2FF-TLD")
.setCurrentSponsorClientId("NewRegistrar")
.setCreationClientId("TheRegistrar")
.setLastEppUpdateClientId("NewRegistrar")
.setCreationTimeForTest(DateTime.parse("1999-04-03T22:00:00.0Z"))
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
.setRegistrationExpirationTime(DateTime.parse("2005-04-03T22:00:00.0Z"))
.setRegistrant(ReferenceUnion.create(registrant))
.setContacts(ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, ReferenceUnion.create(contact)),
DesignatedContact.create(Type.TECH, ReferenceUnion.create(contact))))
.setNameservers(inactive ? null
: ImmutableSet.of(ReferenceUnion.create(host1), ReferenceUnion.create(host2)))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR")))
.build());
// Set the superordinate domain of ns1.example.com to example.com. In reality, this would have
// happened in the flow that created it, but here we just overwrite it in the datastore.
host1 = persistResource(
host1.asBuilder().setSuperordinateDomain(Ref.create(domain)).build());
// Create a subordinate host that is not delegated to by anyone.
host3 = persistResource(
new HostResource.Builder()
.setFullyQualifiedHostName("ns2.example.tld")
.setRepoId("3FF-TLD")
.setSuperordinateDomain(Ref.create(domain))
.build());
// Add the subordinate host references to the existing domain.
domain = persistResource(domain.asBuilder()
.setSubordinateHosts(ImmutableSet.of(
host1.getFullyQualifiedHostName(),
host3.getFullyQualifiedHostName()))
.build());
}
private void persistTestEntities(boolean inactive) {
persistTestEntities("example.tld", inactive);
}
private void doSuccessfulTest(String expectedXmlFilename, boolean inactive) throws Exception {
assertTransactionalFlow(false);
String expected = loadFileWithSubstitutions(
getClass(), expectedXmlFilename, ImmutableMap.of("ROID", "2FF-TLD"));
if (inactive) {
expected = expected.replaceAll("\"ok\"", "\"inactive\"");
}
runFlowAssertResponse(expected);
assertNoHistory();
assertNoBillingEvents();
}
private void doSuccessfulTest(String expectedXmlFilename) throws Exception {
persistTestEntities(false);
doSuccessfulTest(expectedXmlFilename, false);
}
private void doSuccessfulTestNoNameservers(String expectedXmlFilename) throws Exception {
persistTestEntities(true);
doSuccessfulTest(expectedXmlFilename, true);
}
@Test
public void testSuccess_allHosts() throws Exception {
doSuccessfulTest("domain_info_response.xml");
}
@Test
public void testSuccess_allHosts_noDelegatedHosts() throws Exception {
// There aren't any delegated hosts.
doSuccessfulTestNoNameservers("domain_info_response_subordinate_hosts.xml");
}
@Test
public void testSuccess_defaultHosts() throws Exception {
setEppInput("domain_info_default_hosts.xml");
doSuccessfulTest("domain_info_response.xml");
}
@Test
public void testSuccess_defaultHosts_noDelegatedHosts() throws Exception {
setEppInput("domain_info_default_hosts.xml");
// There aren't any delegated hosts.
doSuccessfulTestNoNameservers("domain_info_response_subordinate_hosts.xml");
}
@Test
public void testSuccess_delegatedHosts() throws Exception {
setEppInput("domain_info_delegated_hosts.xml");
doSuccessfulTest("domain_info_response_delegated_hosts.xml");
}
@Test
public void testSuccess_delegatedHosts_noDelegatedHosts() throws Exception {
setEppInput("domain_info_delegated_hosts.xml");
// There aren't any delegated hosts.
doSuccessfulTestNoNameservers("domain_info_response_none_hosts.xml");
}
@Test
public void testSuccess_subordinateHosts() throws Exception {
setEppInput("domain_info_subordinate_hosts.xml");
doSuccessfulTest("domain_info_response_subordinate_hosts.xml");
}
@Test
public void testSuccess_subordinateHosts_noDelegatedHosts() throws Exception {
setEppInput("domain_info_subordinate_hosts.xml");
doSuccessfulTestNoNameservers("domain_info_response_subordinate_hosts.xml");
}
@Test
public void testSuccess_noneHosts() throws Exception {
setEppInput("domain_info_none_hosts.xml");
doSuccessfulTest("domain_info_response_none_hosts.xml");
}
@Test
public void testSuccess_noneHosts_noDelegatedHosts() throws Exception {
setEppInput("domain_info_none_hosts.xml");
doSuccessfulTestNoNameservers("domain_info_response_none_hosts.xml");
}
@Test
public void testSuccess_unauthorized() throws Exception {
sessionMetadata.setClientId("ClientZ");
doSuccessfulTest("domain_info_response_unauthorized.xml");
}
@Test
public void testSuccess_differentRegistrarWithAuthInfo() throws Exception {
setEppInput("domain_info_with_auth.xml");
sessionMetadata.setClientId("ClientZ");
doSuccessfulTest("domain_info_response.xml");
}
@Test
public void testSuccess_differentRegistrarWithRegistrantAuthInfo() throws Exception {
persistTestEntities(false);
setEppInput("domain_info_with_contact_auth.xml");
eppLoader.replaceAll("JD1234-REP", registrant.getRepoId());
sessionMetadata.setClientId("ClientZ");
doSuccessfulTest("domain_info_response.xml", false);
}
@Test
public void testSuccess_differentRegistrarWithContactAuthInfo() throws Exception {
persistTestEntities(false);
setEppInput("domain_info_with_contact_auth.xml");
eppLoader.replaceAll("JD1234-REP", registrant.getRepoId());
sessionMetadata.setClientId("ClientZ");
doSuccessfulTest("domain_info_response.xml", false);
}
@Test
public void testSuccess_secDns() throws Exception {
persistTestEntities(false);
// Add the dsData to the saved resource and change the nameservers to match the sample xml.
persistResource(domain.asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(
12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
.setNameservers(ImmutableSet.of(
ReferenceUnion.create(host1), ReferenceUnion.create(host3)))
.build());
doSuccessfulTest("domain_info_response_dsdata.xml", false);
}
private void doAddPeriodTest(GracePeriodStatus gracePeriodStatus) throws Exception {
persistTestEntities(false);
// Add the grace period to the saved resource, and change a few other fields to match the sample
// xml.
persistResource(domain.asBuilder()
.addGracePeriod(
GracePeriod.create(gracePeriodStatus, clock.nowUtc().plusDays(1), "foo", null))
.setCreationClientId("NewRegistrar")
.setCreationTimeForTest(DateTime.parse("2003-11-26T22:00:00.0Z"))
.setRegistrationExpirationTime(DateTime.parse("2005-11-26T22:00:00.0Z"))
.setLastTransferTime(null)
.setLastEppUpdateTime(null)
.setLastEppUpdateClientId(null)
.build());
doSuccessfulTest("domain_info_response_addperiod.xml", false);
}
@Test
public void testSuccess_addGracePeriod() throws Exception {
doAddPeriodTest(GracePeriodStatus.ADD);
}
@Test
public void testSuccess_sunrushAddGracePeriod() throws Exception {
doAddPeriodTest(GracePeriodStatus.SUNRUSH_ADD);
}
@Test
public void testSuccess_autoRenewGracePeriod() throws Exception {
persistTestEntities(false);
// Add an AUTO_RENEW grace period to the saved resource.
persistResource(domain.asBuilder()
.addGracePeriod(GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
clock.nowUtc().plusDays(1),
"foo",
Ref.create(Key.create(Recurring.class, 12345))))
.build());
doSuccessfulTest("domain_info_response_autorenewperiod.xml", false);
}
@Test
public void testSuccess_redemptionGracePeriod() throws Exception {
persistTestEntities(false);
// Add an REDEMPTION grace period to the saved resource, and change a few other fields to match
// the sample xml.
persistResource(domain.asBuilder()
.addGracePeriod(GracePeriod.create(
GracePeriodStatus.REDEMPTION, clock.nowUtc().plusDays(1), "foo", null))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
doSuccessfulTest("domain_info_response_redemptionperiod.xml", false);
}
@Test
public void testSuccess_renewGracePeriod() throws Exception {
persistTestEntities(false);
// Add an RENEW grace period to the saved resource.
persistResource(domain.asBuilder()
.addGracePeriod(
GracePeriod.create(GracePeriodStatus.RENEW, clock.nowUtc().plusDays(1), "foo", null))
.build());
doSuccessfulTest("domain_info_response_renewperiod.xml", false);
}
@Test
public void testSuccess_multipleRenewGracePeriods() throws Exception {
persistTestEntities(false);
// Add multiple RENEW grace periods to the saved resource.
persistResource(domain.asBuilder()
.addGracePeriod(
GracePeriod.create(GracePeriodStatus.RENEW, clock.nowUtc().plusDays(1), "foo", null))
.addGracePeriod(
GracePeriod.create(GracePeriodStatus.RENEW, clock.nowUtc().plusDays(2), "foo", null))
.build());
doSuccessfulTest("domain_info_response_renewperiod.xml", false);
}
@Test
public void testSuccess_transferGracePeriod() throws Exception {
persistTestEntities(false);
// Add an TRANSFER grace period to the saved resource.
persistResource(domain.asBuilder()
.addGracePeriod(GracePeriod.create(
GracePeriodStatus.TRANSFER, clock.nowUtc().plusDays(1), "foo", null))
.build());
doSuccessfulTest("domain_info_response_transferperiod.xml", false);
}
@Test
public void testSuccess_pendingDelete() throws Exception {
persistTestEntities(false);
// Set the domain to be pending delete with no grace period, which will cause an RGP status of
// pending delete to show up, too.
persistResource(domain.asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
doSuccessfulTest("domain_info_response_pendingdelete.xml", false);
}
@Test
public void testSuccess_stackedAddRenewGracePeriods() throws Exception {
persistTestEntities(false);
// Add both an ADD and RENEW grace period, both which should show up in the RGP status.
persistResource(domain.asBuilder()
.addGracePeriod(
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(1), "foo", null))
.addGracePeriod(
GracePeriod.create(GracePeriodStatus.RENEW, clock.nowUtc().plusDays(2), "foo", null))
.build());
doSuccessfulTest("domain_info_response_stackedaddrenewperiod.xml", false);
}
@Test
public void testSuccess_secDnsAndAddGracePeriod() throws Exception {
persistTestEntities(false);
// Add both an ADD grace period and SecDNS data.
persistResource(domain.asBuilder()
.addGracePeriod(
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(1), "foo", null))
.setDsData(ImmutableSet.of(DelegationSignerData.create(
12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
.build());
doSuccessfulTest("domain_info_response_dsdata_addperiod.xml", false);
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistResource(newDomainResource("example.tld").asBuilder()
.setDeletionTime(clock.nowUtc().minusDays(1))
.build());
runFlow();
}
@Test
public void testFailure_differentRegistrarWrongAuthInfo() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
persistTestEntities(false);
// Change the password of the domain so that it does not match the file.
persistResource(domain.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("diffpw")))
.build());
sessionMetadata.setClientId("ClientZ");
setEppInput("domain_info_with_auth.xml");
runFlow();
}
@Test
public void testFailure_wrongAuthInfo() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
persistTestEntities(false);
// Change the password of the domain so that it does not match the file.
persistResource(domain.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("diffpw")))
.build());
setEppInput("domain_info_with_auth.xml");
runFlow();
}
@Test
public void testFailure_differentRegistrarWrongRegistrantAuthInfo() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
persistTestEntities(false);
// Change the password of the registrant so that it does not match the file.
registrant = persistResource(
registrant.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("diffpw")))
.build());
sessionMetadata.setClientId("ClientZ");
setEppInput("domain_info_with_contact_auth.xml");
// Replace the ROID in the xml file with the one for our registrant.
eppLoader.replaceAll("JD1234-REP", registrant.getRepoId());
runFlow();
}
@Test
public void testFailure_wrongRegistrantAuthInfo() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
persistTestEntities(false);
// Change the password of the registrant so that it does not match the file.
registrant = persistResource(
registrant.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("diffpw")))
.build());
setEppInput("domain_info_with_contact_auth.xml");
// Replace the ROID in the xml file with the one for our registrant.
eppLoader.replaceAll("JD1234-REP", registrant.getRepoId());
runFlow();
}
@Test
public void testFailure_differentRegistrarWrongContactAuthInfo() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
persistTestEntities(false);
// Change the password of the contact so that it does not match the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("diffpw")))
.build());
sessionMetadata.setClientId("ClientZ");
setEppInput("domain_info_with_contact_auth.xml");
// Replace the ROID in the xml file with the one for our contact.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
runFlow();
}
@Test
public void testFailure_wrongContactAuthInfo() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
persistTestEntities(false);
// Change the password of the contact so that it does not match the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("diffpw")))
.build());
setEppInput("domain_info_with_contact_auth.xml");
// Replace the ROID in the xml file with the one for our contact.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
runFlow();
}
@Test
public void testFailure_differentRegistrarUnrelatedContactAuthInfo() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
persistTestEntities(false);
ContactResource unrelatedContact = persistActiveContact("foo1234");
sessionMetadata.setClientId("ClientZ");
setEppInput("domain_info_with_contact_auth.xml");
// Replace the ROID in the xml file with the one for our unrelated contact.
eppLoader.replaceAll("JD1234-REP", unrelatedContact.getRepoId());
runFlow();
}
@Test
public void testFailure_unrelatedContactAuthInfo() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
persistTestEntities(false);
ContactResource unrelatedContact = persistActiveContact("foo1234");
setEppInput("domain_info_with_contact_auth.xml");
// Replace the ROID in the xml file with the one for our unrelated contact.
eppLoader.replaceAll("JD1234-REP", unrelatedContact.getRepoId());
runFlow();
}
/** Test create command. */
@Test
public void testFeeExtension_createCommand() throws Exception {
setEppInput("domain_info_fee_create.xml");
persistTestEntities(false);
doSuccessfulTest("domain_info_fee_create_response.xml", false);
}
/** Test renew command. */
@Test
public void testFeeExtension_renewCommand() throws Exception {
setEppInput("domain_info_fee_renew.xml");
persistTestEntities(false);
doSuccessfulTest("domain_info_fee_renew_response.xml", false);
}
/** Test transfer command. */
@Test
public void testFeeExtension_transferCommand() throws Exception {
setEppInput("domain_info_fee_transfer.xml");
persistTestEntities(false);
doSuccessfulTest("domain_info_fee_transfer_response.xml", false);
}
/** Test restore command. */
@Test
public void testFeeExtension_restoreCommand() throws Exception {
setEppInput("domain_info_fee_restore.xml");
persistTestEntities(false);
doSuccessfulTest("domain_info_fee_restore_response.xml", false);
}
/** Test create command on a premium label. */
@Test
public void testFeeExtension_createCommandPremium() throws Exception {
createTld("example");
setEppInput("domain_info_fee_create_premium.xml");
persistTestEntities("rich.example", false);
doSuccessfulTest("domain_info_fee_create_premium_response.xml", false);
}
/** Test renew command on a premium label. */
@Test
public void testFeeExtension_renewCommandPremium() throws Exception {
createTld("example");
setEppInput("domain_info_fee_renew_premium.xml");
persistTestEntities("rich.example", false);
doSuccessfulTest("domain_info_fee_renew_premium_response.xml", false);
}
/** Test transfer command on a premium label. */
@Test
public void testFeeExtension_transferCommandPremium() throws Exception {
createTld("example");
setEppInput("domain_info_fee_transfer_premium.xml");
persistTestEntities("rich.example", false);
doSuccessfulTest("domain_info_fee_transfer_premium_response.xml", false);
}
/** Test restore command on a premium label. */
@Test
public void testFeeExtension_restoreCommandPremium() throws Exception {
createTld("example");
setEppInput("domain_info_fee_restore_premium.xml");
persistTestEntities("rich.example", false);
doSuccessfulTest("domain_info_fee_restore_premium_response.xml", false);
}
/** Test setting the currency explicitly to a wrong value. */
@Test
public void testFeeExtension_wrongCurrency() throws Exception {
thrown.expect(CurrencyUnitMismatchException.class);
setEppInput("domain_info_fee_create_euro.xml");
persistTestEntities(false);
runFlow();
}
/** Test requesting a period that isn't in years. */
@Test
public void testFeeExtension_periodNotInYears() throws Exception {
thrown.expect(BadPeriodUnitException.class);
setEppInput("domain_info_fee_bad_period.xml");
persistTestEntities(false);
runFlow();
}
/** Test a command that specifies a phase. */
@Test
public void testFeeExtension_commandPhase() throws Exception {
thrown.expect(FeeChecksDontSupportPhasesException.class);
setEppInput("domain_info_fee_command_phase.xml");
persistTestEntities(false);
runFlow();
}
/** Test a command that specifies a subphase. */
@Test
public void testFeeExtension_commandSubphase() throws Exception {
thrown.expect(FeeChecksDontSupportPhasesException.class);
setEppInput("domain_info_fee_command_subphase.xml");
persistTestEntities(false);
runFlow();
}
/** Test a restore for more than one year. */
@Test
public void testFeeExtension_multiyearRestore() throws Exception {
thrown.expect(RestoresAreAlwaysForOneYearException.class);
setEppInput("domain_info_fee_multiyear_restore.xml");
persistTestEntities(false);
runFlow();
}
}

View file

@ -0,0 +1,444 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.flows.domain.DomainTransferFlowTestCase.persistWithPendingTransfer;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import static com.google.domain.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.EUR;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
import com.google.domain.registry.flows.domain.DomainRenewFlow.DomainHasPendingTransferException;
import com.google.domain.registry.flows.domain.DomainRenewFlow.ExceedsMaxRegistrationYearsException;
import com.google.domain.registry.flows.domain.DomainRenewFlow.IncorrectCurrentExpirationDateException;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.billing.BillingEvent.Flag;
import com.google.domain.registry.model.billing.BillingEvent.Reason;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.googlecode.objectify.Ref;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainRenewFlow}. */
public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, DomainResource> {
final DateTime expirationTime = DateTime.parse("2000-04-03T22:00:00.0Z");
public DomainRenewFlowTest() {
clock.setTo(expirationTime.minusMillis(2));
setEppInput("domain_renew.xml");
}
@Before
public void initDomainTest() {
createTld("tld");
}
private void persistDomain(StatusValue... statusValues) throws Exception {
DomainResource domain = newDomainResource(getUniqueIdFromCommand());
HistoryEntry historyEntryDomainCreate = persistResource(
new HistoryEntry.Builder()
.setParent(domain)
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.build());
BillingEvent.Recurring autorenewEvent = persistResource(
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(expirationTime)
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntryDomainCreate)
.build());
PollMessage.Autorenew autorenewPollMessage = persistResource(
new PollMessage.Autorenew.Builder()
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(expirationTime)
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setParent(historyEntryDomainCreate)
.build());
domain = persistResource(domain.asBuilder()
.setRegistrationExpirationTime(expirationTime)
.setStatusValues(ImmutableSet.copyOf(statusValues))
.setAutorenewBillingEvent(Ref.create(autorenewEvent))
.setAutorenewPollMessage(Ref.create(autorenewPollMessage))
.build());
clock.advanceOneMilli();
}
private void doSuccessfulTest(String responseFilename, int renewalYears) throws Exception {
assertTransactionalFlow(true);
DateTime currentExpiration = reloadResourceByUniqueId().getRegistrationExpirationTime();
DateTime newExpiration = currentExpiration.plusYears(renewalYears);
runFlowAssertResponse(readFile(responseFilename));
DomainResource domain = reloadResourceByUniqueId();
HistoryEntry historyEntryDomainRenew =
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_RENEW);
assertThat(domain.getAutorenewBillingEvent().get().getEventTime()).isEqualTo(newExpiration);
assertAboutDomains().that(domain)
.isActiveAt(clock.nowUtc()).and()
.hasRegistrationExpirationTime(newExpiration).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_RENEW);
assertAboutHistoryEntries().that(historyEntryDomainRenew).hasPeriodYears(renewalYears);
BillingEvent.OneTime renewBillingEvent = new BillingEvent.OneTime.Builder()
.setReason(Reason.RENEW)
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setCost(Money.of(USD, 11).multipliedBy(renewalYears))
.setPeriodYears(renewalYears)
.setEventTime(clock.nowUtc())
.setBillingTime(clock.nowUtc().plus(Registry.get("tld").getRenewGracePeriodLength()))
.setParent(historyEntryDomainRenew)
.build();
assertBillingEvents(
renewBillingEvent,
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(expirationTime)
.setRecurrenceEndTime(clock.nowUtc())
.setParent(getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_CREATE))
.build(),
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(domain.getRegistrationExpirationTime())
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntryDomainRenew)
.build());
// There should only be the new autorenew poll message, as the old one will have been deleted
// since it had no messages left to deliver.
assertPollMessages(
new PollMessage.Autorenew.Builder()
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(domain.getRegistrationExpirationTime())
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setParent(historyEntryDomainRenew)
.build());
assertGracePeriods(
domain.getGracePeriods(),
ImmutableMap.of(
GracePeriod.create(
GracePeriodStatus.RENEW,
clock.nowUtc().plus(Registry.get("tld").getRenewGracePeriodLength()),
"TheRegistrar",
null),
renewBillingEvent));
}
@Test
public void testDryRun() throws Exception {
persistDomain();
dryRunFlowAssertResponse(readFile("domain_renew_response.xml"));
}
@Test
public void testSuccess() throws Exception {
persistDomain();
doSuccessfulTest("domain_renew_response.xml", 5);
}
@Test
public void testSuccess_fee() throws Exception {
setEppInput("domain_renew_fee.xml");
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5);
}
@Test
public void testSuccess_fee_withDefaultAttributes() throws Exception {
setEppInput("domain_renew_fee_defaults.xml");
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5);
}
@Test
public void testFailure_refundableFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
setEppInput("domain_renew_fee_refundable.xml");
persistDomain();
runFlow();
}
@Test
public void testFailure_gracePeriodFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
setEppInput("domain_renew_fee_grace_period.xml");
persistDomain();
runFlow();
}
@Test
public void testFailure_appliedFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
setEppInput("domain_renew_fee_applied.xml");
persistDomain();
runFlow();
}
@Test
public void testSuccess_nonDefaultRenewGracePeriod() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setRenewGracePeriodLength(Duration.standardMinutes(9))
.build());
persistDomain();
doSuccessfulTest("domain_renew_response.xml", 5);
}
@Test
public void testSuccess_missingPeriod() throws Exception {
setEppInput("domain_renew_missing_period.xml");
persistDomain();
doSuccessfulTest("domain_renew_response_missing_period.xml", 1);
}
@Test
public void testSuccess_autorenewPollMessageIsNotDeleted() throws Exception {
persistDomain();
// Modify the autorenew poll message so that it has an undelivered message in the past.
persistResource(
reloadResourceByUniqueId().getAutorenewPollMessage().get().asBuilder()
.setEventTime(expirationTime.minusYears(1))
.build());
runFlowAssertResponse(readFile("domain_renew_response.xml"));
HistoryEntry historyEntryDomainRenew =
getOnlyHistoryEntryOfType(reloadResourceByUniqueId(), HistoryEntry.Type.DOMAIN_RENEW);
assertPollMessages(
new PollMessage.Autorenew.Builder()
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(expirationTime.minusYears(1))
.setAutorenewEndTime(clock.nowUtc())
.setMsg("Domain was auto-renewed.")
.setParent(getOnlyHistoryEntryOfType(
reloadResourceByUniqueId(), HistoryEntry.Type.DOMAIN_CREATE))
.build(),
new PollMessage.Autorenew.Builder()
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(reloadResourceByUniqueId().getRegistrationExpirationTime())
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setParent(historyEntryDomainRenew)
.build());
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistDeletedDomain(getUniqueIdFromCommand(), clock.nowUtc());
runFlow();
}
@Test
public void testFailure_clientRenewProhibited() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistDomain(StatusValue.CLIENT_RENEW_PROHIBITED);
runFlow();
}
@Test
public void testFailure_serverRenewProhibited() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistDomain(StatusValue.SERVER_RENEW_PROHIBITED);
runFlow();
}
@Test
public void testFailure_wrongFeeAmount() throws Exception {
thrown.expect(FeesMismatchException.class);
setEppInput("domain_renew_fee.xml");
persistResource(
Registry.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
persistDomain();
runFlow();
}
@Test
public void testFailure_wrongCurrency() throws Exception {
thrown.expect(CurrencyUnitMismatchException.class);
setEppInput("domain_renew_fee.xml");
persistResource(
Registry.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCost(Money.of(EUR, 13))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.build());
persistDomain();
runFlow();
}
@Test
public void testFailure_feeGivenInWrongScale() throws Exception {
thrown.expect(CurrencyValueScaleException.class);
setEppInput("domain_renew_fee_bad_scale.xml");
persistDomain();
runFlow();
}
@Test
public void testFailure_pendingTransfer() throws Exception {
thrown.expect(DomainHasPendingTransferException.class);
persistDomain();
persistWithPendingTransfer(reloadResourceByUniqueId()
.asBuilder()
.setRegistrationExpirationTime(DateTime.parse("2001-09-08T22:00:00.0Z"))
.build());
runFlow();
}
@Test
public void testFailure_periodInMonths() throws Exception {
thrown.expect(BadPeriodUnitException.class);
setEppInput("domain_renew_months.xml");
persistDomain();
runFlow();
}
@Test
public void testFailure_max10Years() throws Exception {
thrown.expect(ExceedsMaxRegistrationYearsException.class);
setEppInput("domain_renew_11_years.xml");
persistDomain();
runFlow();
}
@Test
public void testFailure_pendingDelete() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistResource(newDomainResource(getUniqueIdFromCommand()).asBuilder()
.setRegistrationExpirationTime(expirationTime)
.setDeletionTime(clock.nowUtc().plusDays(1))
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
runFlow();
}
@Test
public void testFailure_curExpDateMustMatch() throws Exception {
thrown.expect(IncorrectCurrentExpirationDateException.class);
persistDomain();
// Note expiration time is off by one day.
persistResource(reloadResourceByUniqueId().asBuilder()
.setRegistrationExpirationTime(DateTime.parse("2000-04-04T22:00:00.0Z"))
.build());
runFlow();
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistActiveDomain(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
persistDomain();
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
persistDomain();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("domain_renew_response.xml"));
}
@Test
public void testFailure_feeNotProvidedOnPremiumName() throws Exception {
thrown.expect(FeesRequiredForPremiumNameException.class);
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(true).build());
setEppInput("domain_renew_premium.xml");
persistDomain();
runFlow();
}
}

View file

@ -0,0 +1,411 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistReservedList;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import static com.google.domain.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.EUR;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.domain.registry.flows.EppException.UnimplementedExtensionException;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.DomainReservedException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
import com.google.domain.registry.flows.domain.DomainRestoreRequestFlow.DomainNotEligibleForRestoreException;
import com.google.domain.registry.flows.domain.DomainRestoreRequestFlow.RestoreCommandIncludesChangesException;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.billing.BillingEvent.Flag;
import com.google.domain.registry.model.billing.BillingEvent.Reason;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.googlecode.objectify.Key;
import org.joda.money.Money;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainRestoreRequestFlow}. */
public class DomainRestoreRequestFlowTest extends
ResourceFlowTestCase<DomainRestoreRequestFlow, DomainResource> {
public DomainRestoreRequestFlowTest() {
setEppInput("domain_update_restore_request.xml");
}
@Before
public void initDomainTest() {
createTld("tld");
}
void persistPendingDeleteDomain() throws Exception {
DomainResource domain = newDomainResource(getUniqueIdFromCommand());
HistoryEntry historyEntry = persistResource(
new HistoryEntry.Builder()
.setType(HistoryEntry.Type.DOMAIN_DELETE)
.setParent(domain)
.build());
domain = persistResource(domain.asBuilder()
.setRegistrationExpirationTime(clock.nowUtc().plusYears(5).plusDays(45))
.setDeletionTime(clock.nowUtc().plusDays(35))
.addGracePeriod(GracePeriod.create(
GracePeriodStatus.REDEMPTION, clock.nowUtc().plusDays(1), "foo", null))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.setDeletePollMessage(Key.create(persistResource(
new PollMessage.OneTime.Builder()
.setClientId("TheRegistrar")
.setEventTime(clock.nowUtc().plusDays(5))
.setParent(historyEntry)
.build())))
.build());
clock.advanceOneMilli();
}
@Test
public void testDryRun() throws Exception {
setEppInput("domain_update_restore_request.xml");
persistPendingDeleteDomain();
dryRunFlowAssertResponse(readFile("domain_update_response.xml"));
}
@Test
public void testSuccess() throws Exception {
setEppInput("domain_update_restore_request.xml");
persistPendingDeleteDomain();
assertTransactionalFlow(true);
// Double check that we see a poll message in the future for when the delete happens.
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
runFlowAssertResponse(readFile("domain_update_response.xml"));
DomainResource domain = reloadResourceByUniqueId();
HistoryEntry historyEntryDomainRestore =
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_RESTORE);
assertThat(domain.getAutorenewBillingEvent().get().getEventTime())
.isEqualTo(clock.nowUtc().plusYears(1));
assertAboutDomains().that(domain)
// New expiration time should be exactly a year from now.
.hasRegistrationExpirationTime(clock.nowUtc().plusYears(1)).and()
.doesNotHaveStatusValue(StatusValue.PENDING_DELETE).and()
.hasDeletionTime(END_OF_TIME).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_DELETE,
HistoryEntry.Type.DOMAIN_RESTORE);
assertThat(domain.getGracePeriods()).isEmpty();
assertDnsTasksEnqueued("example.tld");
// The poll message for the delete should now be gone. The only poll message should be the new
// autorenew poll message.
assertPollMessages(
"TheRegistrar",
new PollMessage.Autorenew.Builder()
.setTargetId("example.tld")
.setClientId("TheRegistrar")
.setEventTime(domain.getRegistrationExpirationTime())
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setParent(historyEntryDomainRestore)
.build());
// There should be a bill for the restore and an explicit renew, along with a new recurring
// autorenew event.
assertBillingEvents(
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.tld")
.setClientId("TheRegistrar")
.setEventTime(domain.getRegistrationExpirationTime())
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntryDomainRestore)
.build(),
new BillingEvent.OneTime.Builder()
.setReason(Reason.RESTORE)
.setTargetId("example.tld")
.setClientId("TheRegistrar")
.setCost(Money.of(USD, 17))
.setEventTime(clock.nowUtc())
.setBillingTime(clock.nowUtc())
.setParent(historyEntryDomainRestore)
.build(),
new BillingEvent.OneTime.Builder()
.setReason(Reason.RENEW)
.setTargetId("example.tld")
.setClientId("TheRegistrar")
.setCost(Money.of(USD, 11))
.setPeriodYears(1)
.setEventTime(clock.nowUtc())
.setBillingTime(clock.nowUtc())
.setParent(historyEntryDomainRestore)
.build());
}
@Test
public void testSuccess_fee() throws Exception {
setEppInput("domain_update_restore_request_fee.xml");
persistPendingDeleteDomain();
runFlowAssertResponse(readFile("domain_update_restore_request_response_fee.xml"));
}
@Test
public void testSuccess_fee_withDefaultAttributes() throws Exception {
setEppInput("domain_update_restore_request_fee_defaults.xml");
persistPendingDeleteDomain();
runFlowAssertResponse(readFile("domain_update_restore_request_response_fee.xml"));
}
@Test
public void testFailure_refundableFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
setEppInput("domain_update_restore_request_fee_refundable.xml");
persistPendingDeleteDomain();
runFlow();
}
@Test
public void testFailure_gracePeriodFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
setEppInput("domain_update_restore_request_fee_grace_period.xml");
persistPendingDeleteDomain();
runFlow();
}
@Test
public void testFailure_appliedFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
setEppInput("domain_update_restore_request_fee_applied.xml");
persistPendingDeleteDomain();
runFlow();
}
@Test
public void testSuccess_premiumNotBlocked() throws Exception {
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
setEppInput("domain_update_restore_request_premium.xml");
persistPendingDeleteDomain();
runFlow();
}
@Test
public void testSuccess_superuserOverridesReservedList() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setReservedLists(persistReservedList("tld-reserved", "example,FULLY_BLOCKED"))
.build());
persistPendingDeleteDomain();
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
readFile("domain_update_response.xml"));
}
@Test
public void testSuccess_superuserOverridesPremiumNameBlock() throws Exception {
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
setEppInput("domain_update_restore_request_premium.xml");
persistPendingDeleteDomain();
// Modify the Registrar to block premium names.
persistResource(
Registrar.loadByClientId("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
readFile("domain_update_response.xml"));
}
@Test
public void testFailure_doesNotExist() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_wrongFeeAmount() throws Exception {
thrown.expect(FeesMismatchException.class);
setEppInput("domain_update_restore_request_fee.xml");
persistPendingDeleteDomain();
persistResource(
Registry.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
runFlow();
}
@Test
public void testFailure_wrongCurrency() throws Exception {
thrown.expect(CurrencyUnitMismatchException.class);
setEppInput("domain_update_restore_request_fee.xml");
persistPendingDeleteDomain();
persistResource(
Registry.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCost(Money.of(EUR, 13))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.build());
runFlow();
}
@Test
public void testFailure_feeGivenInWrongScale() throws Exception {
thrown.expect(CurrencyValueScaleException.class);
setEppInput("domain_update_restore_request_fee_bad_scale.xml");
persistPendingDeleteDomain();
runFlow();
}
@Test
public void testFailure_notInRedemptionPeriod() throws Exception {
thrown.expect(DomainNotEligibleForRestoreException.class);
persistResource(newDomainResource(getUniqueIdFromCommand()).asBuilder()
.setDeletionTime(clock.nowUtc().plusDays(4))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
runFlow();
}
@Test
public void testFailure_notDeleted() throws Exception {
thrown.expect(DomainNotEligibleForRestoreException.class);
persistActiveDomain(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testFailure_withChange() throws Exception {
thrown.expect(RestoreCommandIncludesChangesException.class);
persistPendingDeleteDomain();
setEppInput("domain_update_restore_request_with_change.xml");
runFlow();
}
@Test
public void testFailure_withAdd() throws Exception {
thrown.expect(RestoreCommandIncludesChangesException.class);
persistPendingDeleteDomain();
setEppInput("domain_update_restore_request_with_add.xml");
runFlow();
}
@Test
public void testFailure_withRemove() throws Exception {
thrown.expect(RestoreCommandIncludesChangesException.class);
persistPendingDeleteDomain();
setEppInput("domain_update_restore_request_with_remove.xml");
runFlow();
}
@Test
public void testFailure_withSecDnsExtension() throws Exception {
thrown.expect(UnimplementedExtensionException.class);
persistPendingDeleteDomain();
setEppInput("domain_update_restore_request_with_secdns.xml");
runFlow();
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistPendingDeleteDomain();
runFlow();
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
persistPendingDeleteDomain();
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
persistPendingDeleteDomain();
runFlowAssertResponse(readFile("domain_update_response.xml"));
}
@Test
public void testFailure_premiumBlocked() throws Exception {
thrown.expect(PremiumNameBlockedException.class);
createTld("example");
setEppInput("domain_update_restore_request_premium.xml");
persistPendingDeleteDomain();
// Modify the Registrar to block premium names.
persistResource(
Registrar.loadByClientId("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
runFlow();
}
@Test
public void testFailure_reservedBlocked() throws Exception {
thrown.expect(DomainReservedException.class);
createTld("tld");
persistResource(
Registry.get("tld")
.asBuilder()
.setReservedLists(persistReservedList("tld-reserved", "example,FULLY_BLOCKED"))
.build());
persistPendingDeleteDomain();
runFlow();
}
@Test
public void testFailure_feeNotProvidedOnPremiumName() throws Exception {
thrown.expect(FeesRequiredForPremiumNameException.class);
createTld("example");
setEppInput("domain_update_restore_request_premium.xml");
persistPendingDeleteDomain();
runFlow();
}
}

View file

@ -0,0 +1,487 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEventsForResource;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import static com.google.domain.registry.testing.GenericEppResourceSubject.assertAboutEppResources;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static java.util.Arrays.asList;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.billing.BillingEvent.Cancellation;
import com.google.domain.registry.model.billing.BillingEvent.Cancellation.Builder;
import com.google.domain.registry.model.billing.BillingEvent.Reason;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.domain.DomainAuthInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainTransferApproveFlow}. */
public class DomainTransferApproveFlowTest
extends DomainTransferFlowTestCase<DomainTransferApproveFlow, DomainResource> {
@Before
public void setUp() throws Exception {
setEppInput("domain_transfer_approve.xml");
// Change the registry so that the renew price changes a day minus 1 millisecond before the
// transfer (right after there will be an autorenew in the test case that has one) and then
// again a millisecond after the transfer request time. These changes help us ensure that the
// flows are using prices from the moment of transfer request (or autorenew) and not from the
// moment that the transfer is approved.
createTld("tld");
persistResource(
Registry.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(
new ImmutableSortedMap.Builder<DateTime, Money>(Ordering.natural())
.put(START_OF_TIME, Money.of(USD, 1))
.put(clock.nowUtc().minusDays(1).plusMillis(1), Money.of(USD, 22))
.put(TRANSFER_REQUEST_TIME.plusMillis(1), Money.of(USD, 333))
.build())
.build());
setClientIdForFlow("TheRegistrar");
setupDomainWithPendingTransfer();
clock.advanceOneMilli();
}
private void assertTransferApproved(EppResource resource) {
assertAboutEppResources().that(resource)
.hasTransferStatus(TransferStatus.CLIENT_APPROVED).and()
.hasCurrentSponsorClientId("NewRegistrar").and()
.hasLastTransferTime(clock.nowUtc()).and()
.hasPendingTransferExpirationTime(clock.nowUtc()).and()
.doesNotHaveStatusValue(StatusValue.PENDING_TRANSFER);
}
private void setEppLoader(String commandFilename) {
setEppInput(commandFilename);
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
}
/**
* Runs a successful test, with the expectedCancellationBillingEvents parameter containing a list
* of billing event builders that will be filled out with the correct HistoryEntry parent as it is
* created during the execution of this test.
*/
private void doSuccessfulTest(
String tld,
String commandFilename,
String expectedXmlFilename,
DateTime expectedExpirationTime,
int expectedYearsToCharge,
BillingEvent.Cancellation.Builder... expectedCancellationBillingEvents) throws Exception {
setEppLoader(commandFilename);
Registry registry = Registry.get(tld);
// Make sure the implicit billing event is there; it will be deleted by the flow.
// We also expect to see autorenew events for the gaining and losing registrars.
assertBillingEventsForResource(
domain,
getBillingEventForImplicitTransfer(),
getGainingClientAutorenewEvent(),
getLosingClientAutorenewEvent());
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages(domain, "NewRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1);
assertThat(getPollMessages(domain, "TheRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have succeeded. Verify correct fields were set.
domain = reloadResourceByUniqueId();
assertAboutDomains().that(domain).hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST,
HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE);
final HistoryEntry historyEntryTransferApproved =
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE);
assertTransferApproved(domain);
assertAboutDomains().that(domain).hasRegistrationExpirationTime(expectedExpirationTime);
assertThat(domain.getAutorenewBillingEvent().get().getEventTime())
.isEqualTo(expectedExpirationTime);
assertTransferApproved(reloadResourceAndCloneAtTime(subordinateHost, clock.nowUtc()));
// We expect three billing events: one for the transfer, a closed autorenew for the losing
// client and an open autorenew for the gaining client that begins at the new expiration time.
BillingEvent.OneTime transferBillingEvent = new BillingEvent.OneTime.Builder()
.setReason(Reason.TRANSFER)
.setTargetId(domain.getFullyQualifiedDomainName())
.setEventTime(clock.nowUtc())
.setBillingTime(clock.nowUtc().plus(registry.getTransferGracePeriodLength()))
.setClientId("NewRegistrar")
.setCost(Money.of(USD, 11).multipliedBy(expectedYearsToCharge))
.setPeriodYears(expectedYearsToCharge)
.setParent(historyEntryTransferApproved)
.build();
assertBillingEventsForResource(
domain,
FluentIterable.from(asList(expectedCancellationBillingEvents))
.transform(new Function<BillingEvent.Cancellation.Builder, BillingEvent>() {
@Override
public Cancellation apply(Builder builder) {
return builder.setParent(historyEntryTransferApproved).build();
}})
.append(
transferBillingEvent,
getLosingClientAutorenewEvent().asBuilder()
.setRecurrenceEndTime(clock.nowUtc())
.build(),
getGainingClientAutorenewEvent().asBuilder()
.setEventTime(domain.getRegistrationExpirationTime())
.setParent(historyEntryTransferApproved)
.build())
.toArray(BillingEvent.class));
// There should be a grace period for the new transfer billing event.
assertGracePeriods(
domain.getGracePeriods(),
ImmutableMap.of(
GracePeriod.create(
GracePeriodStatus.TRANSFER,
clock.nowUtc().plus(registry.getTransferGracePeriodLength()),
"NewRegistrar",
null),
transferBillingEvent));
// The poll message (in the future) to the losing registrar for implicit ack should be gone.
assertThat(getPollMessages(domain, "TheRegistrar", clock.nowUtc().plusMonths(1))).isEmpty();
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar, as well as one at the domain's
// autorenew time.
assertThat(getPollMessages(domain, "NewRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1);
assertThat(getPollMessages(domain, "NewRegistrar", domain.getRegistrationExpirationTime()))
.hasSize(2);
PollMessage gainingTransferPollMessage =
getOnlyPollMessage(domain, "NewRegistrar", clock.nowUtc(), PollMessage.OneTime.class);
PollMessage gainingAutorenewPollMessage = getOnlyPollMessage(
domain,
"NewRegistrar",
domain.getRegistrationExpirationTime(),
PollMessage.Autorenew.class);
assertThat(gainingTransferPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(gainingAutorenewPollMessage.getEventTime())
.isEqualTo(domain.getRegistrationExpirationTime());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(gainingTransferPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.CLIENT_APPROVED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingTransferPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isTrue();
// After the expected grace time, the grace period should be gone.
assertThat(
domain.cloneProjectedAtTime(clock.nowUtc().plus(registry.getTransferGracePeriodLength()))
.getGracePeriods()).isEmpty();
}
private void doSuccessfulTest(String tld, String commandFilename, String expectedXmlFilename)
throws Exception {
clock.advanceOneMilli();
doSuccessfulTest(
tld,
commandFilename,
expectedXmlFilename,
domain.getRegistrationExpirationTime().plusYears(1),
1);
}
private void doFailingTest(String commandFilename) throws Exception {
setEppLoader(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppLoader("domain_transfer_approve.xml");
dryRunFlowAssertResponse(readFile("domain_transfer_approve_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("tld", "domain_transfer_approve.xml", "domain_transfer_approve_response.xml");
}
@Test
public void testSuccess_nonDefaultTransferGracePeriod() throws Exception {
// We have to set up a new domain in a different TLD so that the billing event will be persisted
// with the new transfer grace period in mind.
createTld("net");
persistResource(
Registry.get("net")
.asBuilder()
.setTransferGracePeriodLength(Duration.standardMinutes(10))
.build());
setupDomainWithPendingTransfer("net");
doSuccessfulTest(
"net",
"domain_transfer_approve_net.xml",
"domain_transfer_approve_response_net.xml");
}
@Test
public void testSuccess_lastTransferTime_reflectedOnSubordinateHost() throws Exception {
domain = reloadResourceByUniqueId();
// Set an older last transfer time on the subordinate host.
subordinateHost = persistResource(
subordinateHost.asBuilder()
.setLastTransferTime(DateTime.parse("2000-02-03T22:00:00.0Z"))
.build());
doSuccessfulTest("tld", "domain_transfer_approve.xml", "domain_transfer_approve_response.xml");
subordinateHost = loadByUniqueId(
HostResource.class, subordinateHost.getFullyQualifiedHostName(), clock.nowUtc());
// Verify that the host's last transfer time is now that of when the superordinate domain was
// transferred.
assertThat(subordinateHost.getLastTransferTime()).isEqualTo(clock.nowUtc());
}
@Test
public void testSuccess_lastTransferTime_overridesExistingOnSubordinateHost() throws Exception {
domain = reloadResourceByUniqueId();
// Set an older last transfer time on the subordinate host.
subordinateHost = persistResource(
subordinateHost.asBuilder()
.setLastTransferTime(DateTime.parse("2000-02-03T22:00:00.0Z"))
.setLastSuperordinateChange(DateTime.parse("2000-03-03T22:00:00.0Z"))
.build());
doSuccessfulTest("tld", "domain_transfer_approve.xml", "domain_transfer_approve_response.xml");
subordinateHost = loadByUniqueId(
HostResource.class, subordinateHost.getFullyQualifiedHostName(), clock.nowUtc());
// Verify that the host's last transfer time is now that of when the superordinate domain was
// transferred.
assertThat(subordinateHost.getLastTransferTime()).isEqualTo(clock.nowUtc());
}
@Test
public void testSuccess_lastTransferTime_overridesExistingOnSubordinateHostWithNullTransferTime()
throws Exception {
domain = reloadResourceByUniqueId();
// Set an older last transfer time on the subordinate host.
subordinateHost = persistResource(
subordinateHost.asBuilder()
.setLastTransferTime(null)
.setLastSuperordinateChange(DateTime.parse("2000-03-03T22:00:00.0Z"))
.build());
doSuccessfulTest("tld", "domain_transfer_approve.xml", "domain_transfer_approve_response.xml");
subordinateHost = loadByUniqueId(
HostResource.class, subordinateHost.getFullyQualifiedHostName(), clock.nowUtc());
// Verify that the host's last transfer time is now that of when the superordinate domain was
// transferred.
assertThat(subordinateHost.getLastTransferTime()).isEqualTo(clock.nowUtc());
}
@Test
public void testSuccess_domainAuthInfo() throws Exception {
doSuccessfulTest(
"tld",
"domain_transfer_approve_domain_authinfo.xml",
"domain_transfer_approve_response.xml");
}
@Test
public void testSuccess_contactAuthInfo() throws Exception {
doSuccessfulTest(
"tld",
"domain_transfer_approve_contact_authinfo.xml",
"domain_transfer_approve_response.xml");
}
@Test
public void testSuccess_autorenewBeforeTransfer() throws Exception {
DomainResource domain = reloadResourceByUniqueId();
DateTime oldExpirationTime = clock.nowUtc().minusDays(1);
persistResource(domain.asBuilder()
.setRegistrationExpirationTime(oldExpirationTime)
.setTransferData(domain.getTransferData().asBuilder()
.setExtendedRegistrationYears(2)
.build())
.build());
// The autorenew should be subsumed into the transfer resulting in 2 years of renewal in total.
clock.advanceOneMilli();
doSuccessfulTest(
"tld",
"domain_transfer_approve_domain_authinfo.xml",
"domain_transfer_approve_response_autorenew.xml",
oldExpirationTime.plusYears(2),
2,
// Expect the grace period for autorenew to be cancelled.
new BillingEvent.Cancellation.Builder()
.setReason(Reason.RENEW)
.setTargetId("example.tld")
.setClientId("TheRegistrar")
.setEventTime(clock.nowUtc()) // The cancellation happens at the moment of transfer.
.setBillingTime(
oldExpirationTime.plus(Registry.get("tld").getAutoRenewGracePeriodLength()))
.setRecurringEventRef(domain.getAutorenewBillingEvent()));
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_approve_contact_authinfo.xml");
}
@Test
public void testFailure_badDomainPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the domain's password so it does not match the password in the file.
domain = persistResource(domain.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_approve_domain_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_gainingClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("NewRegistrar");
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("ClientZ");
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_deletedDomain() throws Exception {
thrown.expect(ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
domain = persistResource(
domain.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_nonexistentDomain() throws Exception {
thrown.expect(ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(domain);
doFailingTest("domain_transfer_approve.xml");
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
doSuccessfulTest("tld", "domain_transfer_approve.xml", "domain_transfer_approve_response.xml");
}
// NB: No need to test pending delete status since pending transfers will get cancelled upon
// entering pending delete phase. So it's already handled in that test case.
}

View file

@ -0,0 +1,303 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createPollMessageForImplicitTransfer;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.flows.ResourceTransferCancelFlow.NotTransferInitiatorException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.domain.DomainAuthInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse.DomainTransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainTransferCancelFlow}. */
public class DomainTransferCancelFlowTest
extends DomainTransferFlowTestCase<DomainTransferCancelFlow, DomainResource> {
@Before
public void setUp() throws Exception {
setEppInput("domain_transfer_cancel.xml");
setClientIdForFlow("NewRegistrar");
setupDomainWithPendingTransfer();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Make sure the implicit billing event is there; it will be deleted by the flow.
// We also expect to see autorenew events for the gaining and losing registrars.
assertBillingEvents(
getBillingEventForImplicitTransfer(),
getGainingClientAutorenewEvent(),
getLosingClientAutorenewEvent());
// We should see poll messages for the implicit ack case going to both registrars, and an
// autorenew poll message for the new registrar.
HistoryEntry historyEntryDomainTransferRequest =
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST);
assertPollMessages(
"NewRegistrar",
new PollMessage.Autorenew.Builder()
.setTargetId(getUniqueIdFromCommand())
.setClientId("NewRegistrar")
.setEventTime(EXTENDED_REGISTRATION_EXPIRATION_TIME)
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setParent(historyEntryDomainTransferRequest)
.build(),
createPollMessageForImplicitTransfer(
domain,
historyEntryDomainTransferRequest,
"NewRegistrar",
TRANSFER_REQUEST_TIME,
TRANSFER_EXPIRATION_TIME,
TRANSFER_REQUEST_TIME));
assertPollMessages(
"TheRegistrar",
createPollMessageForImplicitTransfer(
domain,
historyEntryDomainTransferRequest,
"TheRegistrar",
TRANSFER_REQUEST_TIME,
TRANSFER_EXPIRATION_TIME,
TRANSFER_REQUEST_TIME));
clock.advanceOneMilli();
// Setup done; run the test.
assertTransactionalFlow(true);
DateTime originalExpirationTime = domain.getRegistrationExpirationTime();
ImmutableSet<GracePeriod> originalGracePeriods = domain.getGracePeriods();
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have been cancelled. Verify correct fields were set.
domain = reloadResourceByUniqueId();
assertTransferFailed(domain, TransferStatus.CLIENT_CANCELLED);
assertAboutDomains().that(domain)
.hasRegistrationExpirationTime(originalExpirationTime).and()
.hasLastTransferTimeNotEqualTo(clock.nowUtc());
assertTransferFailed(
reloadResourceAndCloneAtTime(subordinateHost, clock.nowUtc()),
TransferStatus.CLIENT_CANCELLED);
assertAboutDomains().that(domain).hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST,
HistoryEntry.Type.DOMAIN_TRANSFER_CANCEL);
// The only billing event left should be the original autorenew event, now reopened.
assertBillingEvents(
getLosingClientAutorenewEvent().asBuilder().setRecurrenceEndTime(END_OF_TIME).build());
// The poll message (in the future) to the gaining registrar for implicit ack should be gone.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1)))
.isEmpty();
// The poll message in the future to the losing registrar should be gone too, but there should
// be two at the current time to the losing registrar - one for the original autorenew event,
// and another for the transfer being cancelled.
assertPollMessages(
"TheRegistrar",
new PollMessage.Autorenew.Builder()
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(originalExpirationTime)
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setParent(getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_CREATE))
.build(),
new PollMessage.OneTime.Builder()
.setClientId("TheRegistrar")
.setEventTime(clock.nowUtc())
.setResponseData(ImmutableList.of(new DomainTransferResponse.Builder()
.setFullyQualifiedDomainNameName(getUniqueIdFromCommand())
.setTransferStatus(TransferStatus.CLIENT_CANCELLED)
.setTransferRequestTime(TRANSFER_REQUEST_TIME)
.setGainingClientId("NewRegistrar")
.setLosingClientId("TheRegistrar")
.setPendingTransferExpirationTime(clock.nowUtc())
.build()))
.setMsg("Transfer cancelled.")
.setParent(getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_TRANSFER_CANCEL))
.build());
// The original grace periods should remain untouched.
assertThat(domain.getGracePeriods()).containsExactlyElementsIn(originalGracePeriods);
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("domain_transfer_cancel.xml");
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
dryRunFlowAssertResponse(readFile("domain_transfer_cancel_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("domain_transfer_cancel.xml", "domain_transfer_cancel_response.xml");
}
@Test
public void testSuccess_domainAuthInfo() throws Exception {
doSuccessfulTest("domain_transfer_cancel_domain_authinfo.xml",
"domain_transfer_cancel_response.xml");
}
@Test
public void testSuccess_contactAuthInfo() throws Exception {
doSuccessfulTest("domain_transfer_cancel_contact_authinfo.xml",
"domain_transfer_cancel_response.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_cancel_contact_authinfo.xml");
}
@Test
public void testFailure_badDomainPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the domain's password so it does not match the password in the file.
domain = persistResource(domain.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_cancel_domain_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_sponsoringClient() throws Exception {
thrown.expect(NotTransferInitiatorException.class);
setClientIdForFlow("TheRegistrar");
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(NotTransferInitiatorException.class);
setClientIdForFlow("ClientZ");
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_deletedDomain() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
domain = persistResource(
domain.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_nonexistentDomain() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(domain);
doFailingTest("domain_transfer_cancel.xml");
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
persistResource(
Registrar.loadByClientId("NewRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
doSuccessfulTest("domain_transfer_cancel.xml", "domain_transfer_cancel_response.xml");
}
// NB: No need to test pending delete status since pending transfers will get cancelled upon
// entering pending delete phase. So it's already handled in that test case.
}

View file

@ -0,0 +1,248 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.testing.DatastoreHelper.createBillingEventForTransfer;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDomainWithPendingTransfer;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.GenericEppResourceSubject.assertAboutEppResources;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.Flow;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.billing.BillingEvent.Flag;
import com.google.domain.registry.model.billing.BillingEvent.Reason;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.domain.DesignatedContact;
import com.google.domain.registry.model.domain.DesignatedContact.Type;
import com.google.domain.registry.model.domain.DomainAuthInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferData;
import com.google.domain.registry.model.transfer.TransferStatus;
import com.google.domain.registry.testing.AppEngineRule;
import com.googlecode.objectify.Ref;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
/**
* Base class for domain transfer flow unit tests.
*
* @param <F> the flow type
* @param <R> the resource type
*/
public class DomainTransferFlowTestCase<F extends Flow, R extends EppResource>
extends ResourceFlowTestCase<F, R>{
// Transfer is requested on the 6th and expires on the 11th.
// The "now" of this flow is on the 9th, 3 days in.
protected static final DateTime TRANSFER_REQUEST_TIME = DateTime.parse("2000-06-06T22:00:00.0Z");
protected static final DateTime TRANSFER_EXPIRATION_TIME =
TRANSFER_REQUEST_TIME.plus(Registry.DEFAULT_AUTOMATIC_TRANSFER_LENGTH);
protected static final Duration TIME_SINCE_REQUEST = Duration.standardDays(3);
protected static final int EXTENDED_REGISTRATION_YEARS = 1;
protected static final DateTime REGISTRATION_EXPIRATION_TIME =
DateTime.parse("2001-09-08T22:00:00.0Z");
protected static final DateTime EXTENDED_REGISTRATION_EXPIRATION_TIME =
REGISTRATION_EXPIRATION_TIME.plusYears(EXTENDED_REGISTRATION_YEARS);
protected ContactResource contact;
protected DomainResource domain;
protected HostResource subordinateHost;
protected HistoryEntry historyEntryDomainCreate;
public DomainTransferFlowTestCase() {
checkState(!Registry.DEFAULT_TRANSFER_GRACE_PERIOD.isShorterThan(TIME_SINCE_REQUEST));
clock.setTo(TRANSFER_REQUEST_TIME.plus(TIME_SINCE_REQUEST));
}
@Before
public void makeClientZ() {
// Registrar ClientZ is used in tests that need another registrar that definitely doesn't own
// the resources in question.
persistResource(
AppEngineRule.makeRegistrar1().asBuilder().setClientIdentifier("ClientZ").build());
}
static DomainResource persistWithPendingTransfer(DomainResource domain) {
return persistDomainWithPendingTransfer(
domain,
TRANSFER_REQUEST_TIME,
TRANSFER_EXPIRATION_TIME,
EXTENDED_REGISTRATION_EXPIRATION_TIME,
EXTENDED_REGISTRATION_YEARS,
TRANSFER_REQUEST_TIME);
}
protected void setupDomain(String tld) throws Exception {
setupDomain("example", tld);
}
/** Adds a domain with no pending transfer on it. */
protected void setupDomain(String label, String tld) throws Exception {
createTld(tld);
contact = persistActiveContact("jd1234");
domain = new DomainResource.Builder()
.setRepoId("1-".concat(tld.toUpperCase()))
.setFullyQualifiedDomainName(label + "." + tld)
.setCurrentSponsorClientId("TheRegistrar")
.setCreationClientId("TheRegistrar")
.setCreationTimeForTest(DateTime.parse("1999-04-03T22:00:00.0Z"))
.setRegistrationExpirationTime(REGISTRATION_EXPIRATION_TIME)
.setRegistrant(
ReferenceUnion.create(loadByUniqueId(
ContactResource.class, "jd1234", clock.nowUtc())))
.setContacts(ImmutableSet.of(
DesignatedContact.create(
Type.ADMIN,
ReferenceUnion.create(
loadByUniqueId(ContactResource.class, "jd1234", clock.nowUtc()))),
DesignatedContact.create(
Type.TECH,
ReferenceUnion.create(
loadByUniqueId(ContactResource.class, "jd1234", clock.nowUtc())))))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("fooBAR")))
.addGracePeriod(GracePeriod.create(
GracePeriodStatus.ADD, clock.nowUtc().plusDays(10), "foo", null))
.build();
historyEntryDomainCreate = persistResource(
new HistoryEntry.Builder()
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setParent(domain)
.build());
BillingEvent.Recurring autorenewEvent = persistResource(
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(label + "." + tld)
.setClientId("TheRegistrar")
.setEventTime(REGISTRATION_EXPIRATION_TIME)
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntryDomainCreate)
.build());
PollMessage.Autorenew autorenewPollMessage = persistResource(
new PollMessage.Autorenew.Builder()
.setTargetId(label + "." + tld)
.setClientId("TheRegistrar")
.setEventTime(REGISTRATION_EXPIRATION_TIME)
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setParent(historyEntryDomainCreate)
.build());
subordinateHost = persistResource(
new HostResource.Builder()
.setRepoId("2-".concat(tld.toUpperCase()))
.setFullyQualifiedHostName("ns1." + label + "." + tld)
.setCurrentSponsorClientId("TheRegistrar")
.setCreationClientId("TheRegistrar")
.setCreationTimeForTest(DateTime.parse("1999-04-03T22:00:00.0Z"))
.setSuperordinateDomain(Ref.create(domain))
.build());
domain = persistResource(domain.asBuilder()
.setAutorenewBillingEvent(Ref.create(autorenewEvent))
.setAutorenewPollMessage(Ref.create(autorenewPollMessage))
.addSubordinateHost(subordinateHost.getFullyQualifiedHostName())
.build());
}
protected BillingEvent.OneTime getBillingEventForImplicitTransfer() {
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST);
return createBillingEventForTransfer(
domain,
historyEntry,
TRANSFER_REQUEST_TIME,
TRANSFER_EXPIRATION_TIME,
EXTENDED_REGISTRATION_YEARS);
}
/** Get the autorenew event that the losing client will have after a SERVER_APPROVED transfer. */
protected BillingEvent.Recurring getLosingClientAutorenewEvent() {
return new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(domain.getFullyQualifiedDomainName())
.setClientId("TheRegistrar")
.setEventTime(REGISTRATION_EXPIRATION_TIME)
.setRecurrenceEndTime(TRANSFER_EXPIRATION_TIME)
.setParent(historyEntryDomainCreate)
.build();
}
/** Get the autorenew event that the gaining client will have after a SERVER_APPROVED transfer. */
protected BillingEvent.Recurring getGainingClientAutorenewEvent() {
return new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(domain.getFullyQualifiedDomainName())
.setClientId("NewRegistrar")
.setEventTime(EXTENDED_REGISTRATION_EXPIRATION_TIME)
.setRecurrenceEndTime(END_OF_TIME)
.setParent(getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST))
.build();
}
protected void assertTransferFailed(EppResource resource, TransferStatus status) {
assertAboutEppResources().that(resource)
.hasTransferStatus(status).and()
.hasPendingTransferExpirationTime(clock.nowUtc()).and()
.doesNotHaveStatusValue(StatusValue.PENDING_TRANSFER).and()
.hasCurrentSponsorClientId("TheRegistrar");
TransferData transferData = resource.getTransferData();
assertThat(transferData.getServerApproveBillingEvent()).isNull();
assertThat(transferData.getServerApproveAutorenewEvent()).isNull();
assertThat(transferData.getServerApproveAutorenewPollMessage()).isNull();
assertThat(transferData.getServerApproveEntities()).isEmpty();
}
/** Adds a .tld domain that has a pending transfer on it from TheRegistrar to NewRegistrar. */
protected void setupDomainWithPendingTransfer() throws Exception {
setupDomainWithPendingTransfer("tld");
}
/** Adds a domain that has a pending transfer on it from TheRegistrar to NewRegistrar. */
protected void setupDomainWithPendingTransfer(String tld) throws Exception {
setupDomain(tld);
domain = persistWithPendingTransfer(domain);
}
/** Changes the transfer status on the persisted domain. */
protected void changeTransferStatus(TransferStatus transferStatus) {
domain = persistResource(domain.asBuilder().setTransferData(
domain.getTransferData().asBuilder().setTransferStatus(transferStatus).build()).build());
clock.advanceOneMilli();
}
}

View file

@ -0,0 +1,201 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import com.google.domain.registry.flows.ResourceTransferQueryFlow.NoTransferHistoryToQueryException;
import com.google.domain.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.domain.DomainAuthInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainTransferQueryFlow}. */
public class DomainTransferQueryFlowTest
extends DomainTransferFlowTestCase<DomainTransferQueryFlow, DomainResource> {
@Before
public void setUp() throws Exception {
setEppInput("domain_transfer_query.xml");
setClientIdForFlow("NewRegistrar");
setupDomainWithPendingTransfer();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(false);
runFlowAssertResponse(readFile(expectedXmlFilename));
assertAboutDomains().that(domain).hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST);
assertBillingEvents(
getBillingEventForImplicitTransfer(),
getGainingClientAutorenewEvent(),
getLosingClientAutorenewEvent());
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusYears(1)))
.hasSize(1);
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusYears(1)))
.hasSize(1);
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(false);
runFlow();
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("domain_transfer_query.xml", "domain_transfer_query_response.xml");
}
@Test
public void testSuccess_sponsoringClient() throws Exception {
setClientIdForFlow("TheRegistrar");
doSuccessfulTest("domain_transfer_query.xml", "domain_transfer_query_response.xml");
}
@Test
public void testSuccess_domainAuthInfo() throws Exception {
setClientIdForFlow("ClientZ");
doSuccessfulTest("domain_transfer_query_domain_authinfo.xml",
"domain_transfer_query_response.xml");
}
@Test
public void testSuccess_contactAuthInfo() throws Exception {
setClientIdForFlow("ClientZ");
doSuccessfulTest("domain_transfer_query_contact_authinfo.xml",
"domain_transfer_query_response.xml");
}
@Test
public void testSuccess_clientApproved() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doSuccessfulTest("domain_transfer_query.xml",
"domain_transfer_query_response_client_approved.xml");
}
@Test
public void testSuccess_clientRejected() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doSuccessfulTest("domain_transfer_query.xml",
"domain_transfer_query_response_client_rejected.xml");
}
@Test
public void testSuccess_clientCancelled() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doSuccessfulTest("domain_transfer_query.xml",
"domain_transfer_query_response_client_cancelled.xml");
}
@Test
public void testSuccess_serverApproved() throws Exception {
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doSuccessfulTest("domain_transfer_query.xml",
"domain_transfer_query_response_server_approved.xml");
}
@Test
public void testSuccess_serverCancelled() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doSuccessfulTest("domain_transfer_query.xml",
"domain_transfer_query_response_server_cancelled.xml");
}
@Test
public void testFailure_pendingDeleteDomain() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
domain = persistResource(
domain.asBuilder().setDeletionTime(clock.nowUtc().plusDays(1)).build());
doSuccessfulTest("domain_transfer_query.xml",
"domain_transfer_query_response_server_cancelled.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_query_contact_authinfo.xml");
}
@Test
public void testFailure_badDomainPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the domain's password so it does not match the password in the file.
domain = persistResource(domain.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_query_domain_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NoTransferHistoryToQueryException.class);
changeTransferStatus(null);
doFailingTest("domain_transfer_query.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(NotAuthorizedToViewTransferException.class);
setClientIdForFlow("ClientZ");
doFailingTest("domain_transfer_query.xml");
}
@Test
public void testFailure_deletedDomain() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
domain = persistResource(
domain.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("domain_transfer_query.xml");
}
@Test
public void testFailure_nonexistentDomain() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(domain);
doFailingTest("domain_transfer_query.xml");
}
}

View file

@ -0,0 +1,265 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.domain.DomainAuthInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainTransferRejectFlow}. */
public class DomainTransferRejectFlowTest
extends DomainTransferFlowTestCase<DomainTransferRejectFlow, DomainResource> {
@Before
public void setUp() throws Exception {
setEppInput("domain_transfer_reject.xml");
setClientIdForFlow("TheRegistrar");
setupDomainWithPendingTransfer();
clock.advanceOneMilli();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Make sure the implicit billing event is there; it will be deleted by the flow.
// We also expect to see autorenew events for the gaining and losing registrars.
assertBillingEvents(
getBillingEventForImplicitTransfer(),
getGainingClientAutorenewEvent(),
getLosingClientAutorenewEvent());
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
// Setup done; run the test.
assertTransactionalFlow(true);
DateTime originalExpirationTime = domain.getRegistrationExpirationTime();
ImmutableSet<GracePeriod> originalGracePeriods = domain.getGracePeriods();
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have been rejected. Verify correct fields were set.
domain = reloadResourceByUniqueId();
assertTransferFailed(domain, TransferStatus.CLIENT_REJECTED);
assertTransferFailed(
reloadResourceAndCloneAtTime(subordinateHost, clock.nowUtc()),
TransferStatus.CLIENT_REJECTED);
assertAboutDomains().that(domain)
.hasRegistrationExpirationTime(originalExpirationTime).and()
.hasLastTransferTimeNotEqualTo(clock.nowUtc()).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST,
HistoryEntry.Type.DOMAIN_TRANSFER_REJECT);
// The only billing event left should be the original autorenew event, now reopened.
assertBillingEvents(
getLosingClientAutorenewEvent().asBuilder().setRecurrenceEndTime(END_OF_TIME).build());
// The poll message (in the future) to the losing registrar for implicit ack should be gone.
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.isEmpty();
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar.
PollMessage gainingPollMessage = getOnlyPollMessage("NewRegistrar");
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.CLIENT_REJECTED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isFalse();
// The original grace periods should remain untouched.
assertThat(domain.getGracePeriods()).isEqualTo(originalGracePeriods);
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("domain_transfer_reject.xml", "domain_transfer_reject_response.xml");
}
@Test
public void testDryRun() throws Exception {
setEppInput("domain_transfer_reject.xml");
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
dryRunFlowAssertResponse(readFile("domain_transfer_reject_response.xml"));
}
@Test
public void testSuccess_domainAuthInfo() throws Exception {
doSuccessfulTest("domain_transfer_reject_domain_authinfo.xml",
"domain_transfer_reject_response.xml");
}
@Test
public void testSuccess_contactAuthInfo() throws Exception {
doSuccessfulTest("domain_transfer_reject_contact_authinfo.xml",
"domain_transfer_reject_response.xml");
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
doSuccessfulTest("domain_transfer_reject.xml", "domain_transfer_reject_response.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_reject_contact_authinfo.xml");
}
@Test
public void testFailure_badDomainPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the domain's password so it does not match the password in the file.
domain = persistResource(
domain.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_reject_domain_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_gainingClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("NewRegistrar");
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("ClientZ");
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_deletedDomain() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
domain = persistResource(
domain.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("domain_transfer_reject.xml");
}
@Test
public void testFailure_nonexistentDomain() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(domain);
doFailingTest("domain_transfer_reject.xml");
}
// NB: No need to test pending delete status since pending transfers will get cancelled upon
// entering pending delete phase. So it's already handled in that test case.
}

View file

@ -0,0 +1,612 @@
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.assertBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DomainResourceSubject.assertAboutDomains;
import static com.google.domain.registry.testing.GenericEppResourceSubject.assertAboutEppResources;
import static com.google.domain.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
import static com.google.domain.registry.testing.HostResourceSubject.assertAboutHosts;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static java.util.Arrays.asList;
import static org.joda.money.CurrencyUnit.EUR;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.AlreadyPendingTransferException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.MissingTransferRequestAuthInfoException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.ObjectAlreadySponsoredException;
import com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.FeesRequiredForPremiumNameException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
import com.google.domain.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.billing.BillingEvent.Cancellation.Builder;
import com.google.domain.registry.model.billing.BillingEvent.Flag;
import com.google.domain.registry.model.billing.BillingEvent.Reason;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.domain.DomainAuthInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link DomainTransferRequestFlow}. */
public class DomainTransferRequestFlowTest
extends DomainTransferFlowTestCase<DomainTransferRequestFlow, DomainResource> {
@Before
public void setUp() throws Exception {
setEppInput("domain_transfer_request.xml");
setClientIdForFlow("NewRegistrar");
setupDomain("tld");
}
private void assertTransferRequested(EppResource resource) throws Exception {
assertAboutEppResources().that(resource)
.hasTransferStatus(TransferStatus.PENDING).and()
.hasTransferGainingClientId("NewRegistrar").and()
.hasTransferLosingClientId("TheRegistrar").and()
.hasTransferRequestTrid(getTrid()).and()
.hasCurrentSponsorClientId("TheRegistrar").and()
.hasPendingTransferExpirationTime(
clock.nowUtc().plus(Registry.get("tld").getAutomaticTransferLength())).and()
.hasStatusValue(StatusValue.PENDING_TRANSFER);
}
private void assertTransferApproved(EppResource resource) {
DateTime afterAutoAck = clock.nowUtc().plus(Registry.get("tld").getAutomaticTransferLength());
assertAboutEppResources().that(resource)
.hasTransferStatus(TransferStatus.SERVER_APPROVED).and()
.hasCurrentSponsorClientId("NewRegistrar").and()
.hasLastTransferTime(afterAutoAck).and()
.hasPendingTransferExpirationTime(afterAutoAck).and()
.doesNotHaveStatusValue(StatusValue.PENDING_TRANSFER);
}
/**
* Runs a successful test. The extraExpectedBillingEvents parameter consists of cancellation
* billing event builders that have had all of their attributes set except for the parent history
* entry, which is filled in during the execution of this method.
*/
private void doSuccessfulTest(
String commandFilename,
String expectedXmlFilename,
DateTime expectedExpirationTime,
BillingEvent.Cancellation.Builder... extraExpectedBillingEvents) throws Exception {
setEppInput(commandFilename);
ImmutableSet<GracePeriod> originalGracePeriods = domain.getGracePeriods();
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// For all of the other transfer flow tests, 'now' corresponds to day 3 of the transfer, but
// for the request test we want that same 'now' to be the initial request time, so we shift
// the transfer timeline 3 days later by adjusting the implicit transfer time here.
DateTime implicitTransferTime =
clock.nowUtc().plus(Registry.get("tld").getAutomaticTransferLength());
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have been requested. Verify correct fields were set.
domain = reloadResourceByUniqueId();
final HistoryEntry historyEntryTransferRequest =
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST);
int registrationYears = domain.getTransferData().getExtendedRegistrationYears();
subordinateHost = reloadResourceAndCloneAtTime(subordinateHost, clock.nowUtc());
assertTransferRequested(domain);
assertTransferRequested(subordinateHost);
assertAboutDomains().that(domain)
.hasPendingTransferExpirationTime(implicitTransferTime).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST);
assertAboutHistoryEntries().that(historyEntryTransferRequest).hasPeriodYears(registrationYears);
assertAboutHosts().that(subordinateHost).hasNoHistoryEntries();
assertThat(getPollMessages("TheRegistrar", clock.nowUtc())).hasSize(1);
// A BillingEvent should be created AUTOMATIC_TRANSFER_DAYS in the future, for the case when the
// transfer is implicitly acked, but there should be no grace period yet. There should also be
// two autorenew billing events, one for the losing client that ends at the transfer time, and
// one for the gaining client that starts at that time.
BillingEvent.OneTime transferBillingEvent = new BillingEvent.OneTime.Builder()
.setReason(Reason.TRANSFER)
.setTargetId(domain.getFullyQualifiedDomainName())
.setEventTime(implicitTransferTime)
.setBillingTime(
implicitTransferTime.plus(Registry.get("tld").getTransferGracePeriodLength()))
.setClientId("NewRegistrar")
.setCost(Money.of(USD, 11).multipliedBy(registrationYears))
.setPeriodYears(registrationYears)
.setParent(historyEntryTransferRequest)
.build();
assertBillingEvents(FluentIterable.from(asList(extraExpectedBillingEvents))
.transform(new Function<BillingEvent.Cancellation.Builder, BillingEvent>() {
@Override
public BillingEvent apply(Builder builder) {
return builder.setParent(historyEntryTransferRequest).build();
}})
.append(
transferBillingEvent,
// All of the other transfer flow tests happen on day 3 of the transfer, but the initial
// request by definition takes place on day 1, so we need to edit the times in the
// autorenew events from the base test case.
getLosingClientAutorenewEvent().asBuilder()
.setRecurrenceEndTime(implicitTransferTime)
.build(),
getGainingClientAutorenewEvent().asBuilder()
.setEventTime(expectedExpirationTime)
.build())
.toArray(BillingEvent.class));
// The domain's autorenew billing event should still point to the losing client's event.
BillingEvent.Recurring domainAutorenewEvent = domain.getAutorenewBillingEvent().get();
assertThat(domainAutorenewEvent.getClientId()).isEqualTo("TheRegistrar");
assertThat(domainAutorenewEvent.getRecurrenceEndTime()).isEqualTo(implicitTransferTime);
// The original grace periods should remain untouched.
assertThat(domain.getGracePeriods()).containsExactlyElementsIn(originalGracePeriods);
// If we fast forward AUTOMATIC_TRANSFER_DAYS, we should see the grace period appear, the
// transfer should have happened, and all other grace periods should be gone. Also, both the
// gaining and losing registrars should have a new poll message.
DomainResource domainAfterAutomaticTransfer = domain.cloneProjectedAtTime(implicitTransferTime);
assertGracePeriods(
domainAfterAutomaticTransfer.getGracePeriods(),
ImmutableMap.of(
GracePeriod.create(
GracePeriodStatus.TRANSFER,
clock.nowUtc()
.plus(Registry.get("tld").getAutomaticTransferLength())
.plus(Registry.get("tld").getTransferGracePeriodLength()),
"NewRegistrar",
null),
transferBillingEvent));
assertTransferApproved(domainAfterAutomaticTransfer);
assertTransferApproved(
subordinateHost.cloneProjectedAtTime(implicitTransferTime));
// Two poll messages on the gaining registrar's side at the expected expiration time: a
// (OneTime) transfer approved message, and an Autorenew poll message.
assertThat(getPollMessages("NewRegistrar", expectedExpirationTime))
.hasSize(2);
PollMessage transferApprovedPollMessage = getOnlyPollMessage(
"NewRegistrar", implicitTransferTime, PollMessage.OneTime.class);
PollMessage autorenewPollMessage = getOnlyPollMessage(
"NewRegistrar", expectedExpirationTime, PollMessage.Autorenew.class);
assertThat(transferApprovedPollMessage.getEventTime()).isEqualTo(implicitTransferTime);
assertThat(autorenewPollMessage.getEventTime()).isEqualTo(expectedExpirationTime);
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(transferApprovedPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.SERVER_APPROVED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(transferApprovedPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("ABC-12345", "server-trid"));
assertThat(panData.getActionResult()).isTrue();
// Two poll messages on the losing registrar's side at the implicit transfer time: a
// transfer pending message, and a transfer approved message (both OneTime messages).
assertThat(getPollMessages("TheRegistrar", implicitTransferTime)).hasSize(2);
PollMessage losingTransferPendingPollMessage =
getOnlyPollMessage("TheRegistrar", clock.nowUtc());
PollMessage losingTransferApprovedPollMessage = Iterables.getOnlyElement(FluentIterable
.from(getPollMessages("TheRegistrar", implicitTransferTime))
.filter(Predicates.not(Predicates.equalTo(losingTransferPendingPollMessage))));
assertThat(losingTransferPendingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(losingTransferApprovedPollMessage.getEventTime()).isEqualTo(implicitTransferTime);
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(losingTransferPendingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.PENDING);
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(losingTransferApprovedPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.SERVER_APPROVED);
assertAboutDomains().that(domainAfterAutomaticTransfer)
.hasRegistrationExpirationTime(expectedExpirationTime);
assertThat(domainAfterAutomaticTransfer.getAutorenewBillingEvent().get().getEventTime())
.isEqualTo(expectedExpirationTime);
// And after the expected grace time, the grace period should be gone.
DomainResource afterGracePeriod = domain.cloneProjectedAtTime(
clock.nowUtc().plus(Registry.get("tld").getAutomaticTransferLength()).plus(
Registry.get("tld").getTransferGracePeriodLength()));
assertThat(afterGracePeriod.getGracePeriods()).isEmpty();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
clock.advanceOneMilli();
doSuccessfulTest(
commandFilename, expectedXmlFilename, domain.getRegistrationExpirationTime().plusYears(1));
}
private void runTest(String commandFilename, UserPrivileges userPrivileges) throws Exception {
setEppInput(commandFilename);
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow(CommitMode.LIVE, userPrivileges);
}
private void doFailingTest(String commandFilename) throws Exception {
runTest(commandFilename, UserPrivileges.NORMAL);
}
@Test
public void testDryRun() throws Exception {
setEppInput("domain_transfer_request.xml");
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
dryRunFlowAssertResponse(readFile("domain_transfer_request_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("domain_transfer_request.xml", "domain_transfer_request_response.xml");
}
@Test
public void testSuccess_fee() throws Exception {
doSuccessfulTest("domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml");
}
@Test
public void testSuccess_fee_withDefaultAttributes() throws Exception {
doSuccessfulTest(
"domain_transfer_request_fee_defaults.xml", "domain_transfer_request_response_fee.xml");
}
@Test
public void testFailure_refundableFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
doFailingTest("domain_transfer_request_fee_refundable.xml");
}
@Test
public void testFailure_gracePeriodFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
doFailingTest("domain_transfer_request_fee_grace_period.xml");
}
@Test
public void testFailure_appliedFee() throws Exception {
thrown.expect(UnsupportedFeeAttributeException.class);
doFailingTest("domain_transfer_request_fee_applied.xml");
}
@Test
public void testSuccess_nonDefaultAutomaticTransferLength() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setAutomaticTransferLength(Duration.standardMinutes(15))
.build());
doSuccessfulTest(
"domain_transfer_request.xml",
"domain_transfer_request_response_15_minutes.xml");
}
@Test
public void testSuccess_nonDefaultTransferGracePeriod() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setTransferGracePeriodLength(Duration.standardMinutes(5))
.build());
doSuccessfulTest("domain_transfer_request.xml", "domain_transfer_request_response.xml");
}
@Test
public void testSuccess_missingPeriod() throws Exception {
doSuccessfulTest("domain_transfer_request_missing_period.xml",
"domain_transfer_request_response.xml");
}
@Test
public void testSuccess_cappedExpiration() throws Exception {
// The current expiration is in 15 months, so requesting 10 years would give 11 years 3 months,
// were it not that we cap at 10 years. (MAX_REGISTRATION_YEARS == 10 and is unlikely to ever
// change; we just use a constant for readability.)
clock.advanceOneMilli();
doSuccessfulTest(
"domain_transfer_request_10_years.xml",
"domain_transfer_request_response_10_years.xml",
clock.nowUtc().plus(Registry.get("tld").getAutomaticTransferLength()).plusYears(10));
}
@Test
public void testSuccess_domainAuthInfo() throws Exception {
doSuccessfulTest(
"domain_transfer_request_domain_authinfo.xml",
"domain_transfer_request_response.xml");
}
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
thrown.expect(NotAuthorizedForTldException.class);
persistResource(
Registrar.loadByClientId("NewRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.<String>of())
.build());
doSuccessfulTest("domain_transfer_request.xml", "domain_transfer_request_response.xml");
}
@Test
public void testSuccess_autorenewBeforeAutomaticTransfer() throws Exception {
DomainResource oldResource = persistResource(reloadResourceByUniqueId().asBuilder()
.setRegistrationExpirationTime(clock.nowUtc().plusDays(1).plus(1))
.build());
clock.advanceOneMilli();
// The autorenew should be subsumed into the transfer resulting in 2 years of renewal in total.
doSuccessfulTest(
"domain_transfer_request_2_years.xml",
"domain_transfer_request_response_2_years.xml",
clock.nowUtc().plusDays(1).plusYears(2),
new BillingEvent.Cancellation.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.tld")
.setClientId("TheRegistrar")
// The cancellation happens at the moment of transfer.
.setEventTime(clock.nowUtc().plus(Registry.get("tld").getAutomaticTransferLength()))
.setBillingTime(oldResource.getRegistrationExpirationTime().plus(
Registry.get("tld").getAutoRenewGracePeriodLength()))
// The cancellation should refer to the old autorenew billing event.
.setRecurringEventRef(oldResource.getAutorenewBillingEvent()));
}
@Test
public void testSuccess_premiumNotBlocked() throws Exception {
setupDomain("rich", "example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
clock.advanceOneMilli();
// We don't verify the results; just check that the flow doesn't fail.
runTest("domain_transfer_request_premium.xml", UserPrivileges.NORMAL);
}
@Test
public void testSuccess_premiumNotBlockedInSuperuserMode() throws Exception {
setupDomain("rich", "example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
clock.advanceOneMilli();
// Modify the Registrar to block premium names.
persistResource(
Registrar.loadByClientId("NewRegistrar").asBuilder().setBlockPremiumNames(true).build());
// We don't verify the results; just check that the flow doesn't fail.
runTest("domain_transfer_request_premium.xml", UserPrivileges.SUPERUSER);
}
@Test
public void testFailure_wrongCurrency() throws Exception {
thrown.expect(CurrencyUnitMismatchException.class);
persistResource(
Registry.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCost(Money.of(EUR, 13))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.build());
doFailingTest("domain_transfer_request_fee.xml");
}
@Test
public void testFailure_feeGivenInWrongScale() throws Exception {
thrown.expect(CurrencyValueScaleException.class);
doFailingTest("domain_transfer_request_fee_bad_scale.xml");
}
@Test
public void testFailure_wrongFeeAmount() throws Exception {
thrown.expect(FeesMismatchException.class);
persistResource(
Registry.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
doFailingTest("domain_transfer_request_fee.xml");
}
@Test
public void testFailure_premiumBlocked() throws Exception {
thrown.expect(PremiumNameBlockedException.class);
setupDomain("rich", "example");
// Modify the Registrar to block premium names.
persistResource(
Registrar.loadByClientId("NewRegistrar").asBuilder().setBlockPremiumNames(true).build());
doFailingTest("domain_transfer_request_premium.xml");
}
@Test
public void testFailure_feeNotProvidedOnPremiumName() throws Exception {
thrown.expect(FeesRequiredForPremiumNameException.class);
setupDomain("rich", "example");
doFailingTest("domain_transfer_request_premium.xml");
}
@Test
public void testFailure_noAuthInfo() throws Exception {
thrown.expect(MissingTransferRequestAuthInfoException.class);
doFailingTest("domain_transfer_request_no_authinfo.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_request.xml");
}
@Test
public void testFailure_badContactRepoId() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Set the contact to a different ROID, but don't persist it; this is just so the substitution
// code above will write the wrong ROID into the file.
contact = contact.asBuilder().setRepoId("DEADBEEF_TLD-ROID").build();
doFailingTest("domain_transfer_request.xml");
}
@Test
public void testSuccess_clientApproved() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doSuccessfulTest("domain_transfer_request.xml", "domain_transfer_request_response.xml");
}
@Test
public void testSuccess_clientRejected() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doSuccessfulTest("domain_transfer_request.xml", "domain_transfer_request_response.xml");
}
@Test
public void testSuccess_clientCancelled() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doSuccessfulTest("domain_transfer_request.xml", "domain_transfer_request_response.xml");
}
@Test
public void testSuccess_serverApproved() throws Exception {
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doSuccessfulTest("domain_transfer_request.xml", "domain_transfer_request_response.xml");
}
@Test
public void testSuccess_serverCancelled() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doSuccessfulTest("domain_transfer_request.xml", "domain_transfer_request_response.xml");
}
@Test
public void testFailure_pending() throws Exception {
thrown.expect(AlreadyPendingTransferException.class);
domain = persistResource(domain.asBuilder()
.setTransferData(domain.getTransferData().asBuilder()
.setTransferStatus(TransferStatus.PENDING)
.setPendingTransferExpirationTime(clock.nowUtc().plusDays(1))
.build())
.build());
doFailingTest("domain_transfer_request.xml");
}
@Test
public void testFailure_badDomainPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the domain's password so it does not match the password in the file.
domain = persistResource(domain.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("domain_transfer_request_domain_authinfo.xml");
}
@Test
public void testFailure_sponsoringClient() throws Exception {
thrown.expect(ObjectAlreadySponsoredException.class);
setClientIdForFlow("TheRegistrar");
doFailingTest("domain_transfer_request.xml");
}
@Test
public void testFailure_deletedDomain() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
domain = persistResource(
domain.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("domain_transfer_request.xml");
}
@Test
public void testFailure_invalidDomain() throws Exception {
setEppInput("domain_transfer_request_wildcard.xml", ImmutableMap.of("DOMAIN", "--invalid"));
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
assertTransactionalFlow(true);
thrown.expect(ResourceToMutateDoesNotExistException.class, "(--invalid)");
runFlow(CommitMode.LIVE, UserPrivileges.NORMAL);
}
@Test
public void testFailure_nonexistentDomain() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(domain);
doFailingTest("domain_transfer_request.xml");
}
@Test
public void testFailure_periodInMonths() throws Exception {
thrown.expect(BadPeriodUnitException.class);
doFailingTest("domain_transfer_request_months.xml");
}
@Test
public void testFailure_pendingDelete() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
domain = persistResource(
domain.asBuilder().addStatusValue(StatusValue.PENDING_DELETE).build());
doFailingTest("domain_transfer_request.xml");
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>%APPLICATIONID%</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,39 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
<domain:hostObj>ns3.example.net</domain:hostObj>
<domain:hostObj>ns4.example.net</domain:hostObj>
<domain:hostObj>ns5.example.net</domain:hostObj>
<domain:hostObj>ns6.example.net</domain:hostObj>
<domain:hostObj>ns7.example.net</domain:hostObj>
<domain:hostObj>ns8.example.net</domain:hostObj>
<domain:hostObj>ns9.example.net</domain:hostObj>
<domain:hostObj>ns10.example.net</domain:hostObj>
<domain:hostObj>ns11.example.net</domain:hostObj>
<domain:hostObj>ns12.example.net</domain:hostObj>
<domain:hostObj>ns13.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,28 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>8-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,33 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>1-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
<allocate:notice xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
<launch:noticeID>370c0b7c9223372036854775807</launch:noticeID>
<launch:notAfter>2010-08-16T09:00:00.0Z</launch:notAfter>
<launch:acceptedDate>2009-08-16T09:00:00.0Z</launch:acceptedDate>
</allocate:notice>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,33 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
<allocate:notice xmlns:launch="urn:ietf:params:xml:ns:launch-1.0">
<launch:noticeID>370d0b7c9223372036854775807</launch:noticeID>
<launch:notAfter>2011-08-16T09:00:00.0Z</launch:notAfter>
<launch:acceptedDate>2010-07-16T09:00:00.0Z</launch:acceptedDate>
</allocate:notice>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,28 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>collision-label.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,37 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
<secDNS:create
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
</secDNS:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,80 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
<secDNS:create
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12347</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12348</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12349</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12350</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12351</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12352</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
</secDNS:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,28 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>xn--abc-873b2e7eb1k8a4lpjvv.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,24 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,28 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>rich.example</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>2-EXAMPLE</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

Some files were not shown because too many files have changed in this diff Show more