google-nomulus/javatests/google/registry/rdap/RdapNameserverSearchActionTest.java
guyben 55673ceb2d Update the rdapConformance and notices fields
We're changing rdapConformance from rdap_level_0 to icann_rdap_response_profile_0, and changing the EPP status notices title from "EPP Status Codes" to "Status Codes" to conform with the changing spec.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=241527211
2019-04-05 11:35:08 -04:00

919 lines
32 KiB
Java

// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.request.Action.Method.GET;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistResources;
import static google.registry.testing.DatastoreHelper.persistSimpleResources;
import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistHostResource;
import static google.registry.testing.FullFieldsTestEntityHelper.makeContactResource;
import static google.registry.testing.FullFieldsTestEntityHelper.makeDomainBase;
import static google.registry.testing.FullFieldsTestEntityHelper.makeHostResource;
import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrar;
import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrarContacts;
import static google.registry.testing.TestDataHelper.loadFile;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.model.registrar.Registrar;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapMetrics.SearchType;
import google.registry.rdap.RdapMetrics.WildcardType;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.testing.FakeResponse;
import java.net.URLDecoder;
import java.util.Optional;
import javax.annotation.Nullable;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link RdapNameserverSearchAction}. */
@RunWith(JUnit4.class)
public class RdapNameserverSearchActionTest
extends RdapSearchActionTestCase<RdapNameserverSearchAction> {
public RdapNameserverSearchActionTest() {
super(RdapNameserverSearchAction.class, RdapNameserverSearchAction.PATH);
}
private DomainBase domainCatLol;
private HostResource hostNs1CatLol;
private HostResource hostNs2CatLol;
private Object generateActualJsonWithName(String name) {
return generateActualJsonWithName(name, null);
}
private Object generateActualJsonWithName(String name, String cursor) {
metricSearchType = SearchType.BY_NAMESERVER_NAME;
rememberWildcardType(name);
action.nameParam = Optional.of(name);
if (cursor == null) {
action.parameterMap = ImmutableListMultimap.of("name", name);
action.cursorTokenParam = Optional.empty();
} else {
action.parameterMap = ImmutableListMultimap.of("name", name, "cursor", cursor);
action.cursorTokenParam = Optional.of(cursor);
}
action.run();
return JSONValue.parse(response.getPayload());
}
private Object generateActualJsonWithIp(String ipString) {
return generateActualJsonWithIp(ipString, null);
}
private Object generateActualJsonWithIp(String ipString, String cursor) {
metricSearchType = SearchType.BY_NAMESERVER_ADDRESS;
action.parameterMap = ImmutableListMultimap.of("ip", ipString);
action.ipParam = Optional.of(ipString);
if (cursor == null) {
action.parameterMap = ImmutableListMultimap.of("ip", ipString);
action.cursorTokenParam = Optional.empty();
} else {
action.parameterMap = ImmutableListMultimap.of("ip", ipString, "cursor", cursor);
action.cursorTokenParam = Optional.of(cursor);
}
action.run();
return JSONValue.parse(response.getPayload());
}
@Before
public void setUp() {
// cat.lol and cat2.lol
createTld("lol");
Registrar registrar =
persistResource(
makeRegistrar("evilregistrar", "Yes Virginia <script>", Registrar.State.ACTIVE));
persistSimpleResources(makeRegistrarContacts(registrar));
hostNs1CatLol = makeAndPersistHostResource(
"ns1.cat.lol", "1.2.3.4", clock.nowUtc().minusYears(1));
hostNs2CatLol = makeAndPersistHostResource(
"ns2.cat.lol", "bad:f00d:cafe::15:beef", clock.nowUtc().minusYears(1));
makeAndPersistHostResource(
"ns1.cat2.lol", "1.2.3.3", "bad:f00d:cafe::15:beef", clock.nowUtc().minusYears(1));
makeAndPersistHostResource("ns1.cat.external", null, null, clock.nowUtc().minusYears(1));
// cat.みんな
createTld("xn--q9jyb4c");
registrar = persistResource(makeRegistrar("unicoderegistrar", "みんな", Registrar.State.ACTIVE));
persistSimpleResources(makeRegistrarContacts(registrar));
makeAndPersistHostResource("ns1.cat.みんな", "1.2.3.5", clock.nowUtc().minusYears(1));
// cat.1.test
createTld("1.test");
registrar = persistResource(makeRegistrar("multiregistrar", "1.test", Registrar.State.ACTIVE));
persistSimpleResources(makeRegistrarContacts(registrar));
makeAndPersistHostResource("ns1.cat.1.test", "1.2.3.6", clock.nowUtc().minusYears(1));
// create a domain so that we can use it as a test nameserver search string suffix
domainCatLol = persistResource(
makeDomainBase(
"cat.lol",
persistResource(
makeContactResource("5372808-ERL", "Goblin Market", "lol@cat.lol", registrar)),
persistResource(
makeContactResource("5372808-IRL", "Santa Claus", "BOFH@cat.lol", registrar)),
persistResource(
makeContactResource("5372808-TRL", "The Raven", "bog@cat.lol", registrar)),
hostNs1CatLol,
hostNs2CatLol,
registrar)
.asBuilder()
.setSubordinateHosts(ImmutableSet.of("ns1.cat.lol", "ns2.cat.lol"))
.build());
persistResource(
hostNs1CatLol.asBuilder().setSuperordinateDomain(Key.create(domainCatLol)).build());
persistResource(
hostNs2CatLol.asBuilder().setSuperordinateDomain(Key.create(domainCatLol)).build());
action.ipParam = Optional.empty();
action.nameParam = Optional.empty();
}
private Object generateExpectedJson(String expectedOutputFile) {
return generateExpectedJson(null, null, null, null, null, expectedOutputFile);
}
private Object generateExpectedJson(String name, String expectedOutputFile) {
return generateExpectedJson(name, null, null, null, null, expectedOutputFile);
}
private Object generateExpectedJson(
@Nullable String name,
@Nullable String punycodeName,
@Nullable String handle,
@Nullable String ipAddressType,
@Nullable String ipAddress,
String expectedOutputFile) {
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
if (name != null) {
builder.put("NAME", name);
}
if ((name != null) || (punycodeName != null)) {
builder.put("PUNYCODENAME", (punycodeName == null) ? name : punycodeName);
}
if (handle != null) {
builder.put("HANDLE", handle);
}
if (ipAddressType != null) {
builder.put("ADDRESSTYPE", ipAddressType);
}
if (ipAddress != null) {
builder.put("ADDRESS", ipAddress);
}
builder.put("STATUS", "active");
builder.put("TYPE", "nameserver");
return JSONValue.parse(
loadFile(this.getClass(), expectedOutputFile, builder.build()));
}
private Object generateExpectedJsonForNameserver(
String name,
String punycodeName,
String handle,
String ipAddressType,
String ipAddress,
String expectedOutputFile) {
Object obj =
generateExpectedJson(
name, punycodeName, handle, ipAddressType, ipAddress, expectedOutputFile);
ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
builder.put("nameserverSearchResults", ImmutableList.of(obj));
builder.put("rdapConformance", ImmutableList.of("icann_rdap_response_profile_0"));
RdapTestHelper.addNonDomainBoilerplateNotices(
builder, RdapTestHelper.createNotices("https://example.tld/rdap/"));
return builder.build();
}
private void createManyHosts(int numHosts) {
ImmutableList.Builder<HostResource> hostsBuilder = new ImmutableList.Builder<>();
ImmutableSet.Builder<String> subordinateHostsBuilder = new ImmutableSet.Builder<>();
for (int i = 1; i <= numHosts; i++) {
String hostName = String.format("nsx%d.cat.lol", i);
subordinateHostsBuilder.add(hostName);
hostsBuilder.add(makeHostResource(hostName, "5.5.5.1", "5.5.5.2"));
}
persistResources(hostsBuilder.build());
domainCatLol = persistResource(
domainCatLol.asBuilder()
.setSubordinateHosts(subordinateHostsBuilder.build())
.build());
}
private void createDeletedHost() {
persistResource(
makeAndPersistHostResource("nsdeleted.cat.lol", "4.3.2.1", clock.nowUtc().minusYears(1))
.asBuilder()
.setDeletionTime(clock.nowUtc().minusMonths(1))
.build());
}
private void verifyMetrics(long numHostsRetrieved) {
verifyMetrics(Optional.of(numHostsRetrieved), IncompletenessWarningType.COMPLETE);
}
private void verifyMetrics(
Optional<Long> numHostsRetrieved, IncompletenessWarningType incompletenessWarningType) {
verifyMetrics(
EndpointType.NAMESERVERS,
GET,
action.includeDeletedParam.orElse(false),
action.registrarParam.isPresent(),
Optional.empty(),
numHostsRetrieved,
Optional.empty(),
incompletenessWarningType);
}
private void verifyErrorMetrics() {
metricStatusCode = 404;
verifyMetrics(0);
}
private void verifyErrorMetrics(Optional<Long> numHostsRetrieved, int statusCode) {
metricStatusCode = statusCode;
verifyMetrics(numHostsRetrieved, IncompletenessWarningType.COMPLETE);
}
@Test
public void testInvalidPath_rejected() {
action.requestPath = RdapDomainSearchAction.PATH + "/path";
action.run();
assertThat(response.getStatus()).isEqualTo(400);
verifyErrorMetrics(Optional.empty(), 400);
}
@Test
public void testInvalidRequest_rejected() {
action.run();
assertThat(JSONValue.parse(response.getPayload()))
.isEqualTo(
generateExpectedJson(
"You must specify either name=XXXX or ip=YYYY", "rdap_error_400.json"));
assertThat(response.getStatus()).isEqualTo(400);
verifyErrorMetrics(Optional.empty(), 400);
}
@Test
public void testInvalidSuffix_rejected() {
assertThat(generateActualJsonWithName("exam*ple"))
.isEqualTo(
generateExpectedJson(
"Suffix after wildcard must be one or more domain"
+ " name labels, e.g. exam*.tld, ns*.example.tld",
"rdap_error_422.json"));
assertThat(response.getStatus()).isEqualTo(422);
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
public void testNonexistentDomainSuffix_unprocessable() {
assertThat(generateActualJsonWithName("exam*.foo.bar"))
.isEqualTo(
generateExpectedJson(
"A suffix after a wildcard in a nameserver lookup must be an in-bailiwick domain",
"rdap_error_422.json"));
assertThat(response.getStatus()).isEqualTo(422);
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
public void testMultipleWildcards_rejected() {
assertThat(generateActualJsonWithName("*.*"))
.isEqualTo(generateExpectedJson("Only one wildcard allowed", "rdap_error_422.json"));
assertThat(response.getStatus()).isEqualTo(422);
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
public void testNoCharactersToMatch_rejected() {
assertThat(generateActualJsonWithName("*"))
.isEqualTo(
generateExpectedJson(
"Initial search string must be at least 2 characters", "rdap_error_422.json"));
assertThat(response.getStatus()).isEqualTo(422);
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
public void testFewerThanTwoCharactersToMatch_rejected() {
assertThat(generateActualJsonWithName("a*"))
.isEqualTo(
generateExpectedJson(
"Initial search string must be at least 2 characters", "rdap_error_422.json"));
assertThat(response.getStatus()).isEqualTo(422);
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
public void testNameMatch_ns1_cat_lol_found() {
assertThat(generateActualJsonWithName("ns1.cat.lol"))
.isEqualTo(
generateExpectedJsonForNameserver(
"ns1.cat.lol", null, "2-ROID", "v4", "1.2.3.4", "rdap_host_linked.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_ns1_cat_lol_foundWithUpperCase() {
assertThat(generateActualJsonWithName("Ns1.CaT.lOl"))
.isEqualTo(
generateExpectedJsonForNameserver(
"ns1.cat.lol", null, "2-ROID", "v4", "1.2.3.4", "rdap_host_linked.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_ns1_cat_lol_found_sameRegistrarRequested() {
action.registrarParam = Optional.of("TheRegistrar");
generateActualJsonWithName("ns1.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_ns1_cat_lol_notFound_differentRegistrarRequested() {
action.registrarParam = Optional.of("unicoderegistrar");
generateActualJsonWithName("ns1.cat.lol");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics(Optional.of(0L), 404);
}
@Test
public void testNameMatch_ns2_cat_lol_found() {
assertThat(generateActualJsonWithName("ns2.cat.lol"))
.isEqualTo(generateExpectedJsonForNameserver(
"ns2.cat.lol",
null,
"4-ROID",
"v6",
"bad:f00d:cafe::15:beef",
"rdap_host_linked.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_ns1_cat2_lol_found() {
// ns1.cat2.lol has two IP addresses; just test that we are able to find it
generateActualJsonWithName("ns1.cat2.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_ns1_cat_external_found() {
assertThat(generateActualJsonWithName("ns1.cat.external"))
.isEqualTo(
generateExpectedJsonForNameserver(
"ns1.cat.external", null, "8-ROID", null, null, "rdap_host_external.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_ns1_cat_idn_unicode_badRequest() {
// name must use punycode.
generateActualJsonWithName("ns1.cat.みんな");
metricWildcardType = WildcardType.INVALID;
metricPrefixLength = 0;
assertThat(response.getStatus()).isEqualTo(400);
verifyErrorMetrics(Optional.empty(), 400);
}
@Test
public void testNameMatch_ns1_cat_idn_punycode_found() {
assertThat(generateActualJsonWithName("ns1.cat.xn--q9jyb4c"))
.isEqualTo(
generateExpectedJsonForNameserver(
"ns1.cat.みんな",
"ns1.cat.xn--q9jyb4c",
"B-ROID",
"v4",
"1.2.3.5",
"rdap_host_unicode.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_ns1_cat_1_test_found() {
assertThat(generateActualJsonWithName("ns1.cat.1.test"))
.isEqualTo(
generateExpectedJsonForNameserver(
"ns1.cat.1.test", null, "E-ROID", "v4", "1.2.3.6", "rdap_host.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_nsstar_cat_lol_found() {
generateActualJsonWithName("ns*.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(2);
}
@Test
public void testNameMatch_nsstar_cat_lol_found_sameRegistrarRequested() {
action.registrarParam = Optional.of("TheRegistrar");
generateActualJsonWithName("ns*.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(2);
}
@Test
public void testNameMatch_nsstar_cat_lol_notFound_differentRegistrarRequested() {
action.registrarParam = Optional.of("unicoderegistrar");
generateActualJsonWithName("ns*.cat.lol");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics(Optional.of(2L), 404);
}
@Test
public void testNameMatch_nstar_cat_lol_found() {
generateActualJsonWithName("n*.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(2);
}
@Test
public void testNameMatch_star_cat_lol_found() {
generateActualJsonWithName("*.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(2);
}
@Test
public void testNameMatch_star_cat_lol_found_sameRegistrarRequested() {
action.registrarParam = Optional.of("TheRegistrar");
generateActualJsonWithName("*.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(2);
}
@Test
public void testNameMatch_star_cat_lol_notFound_differentRegistrarRequested() {
action.registrarParam = Optional.of("unicoderegistrar");
generateActualJsonWithName("*.cat.lol");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics(Optional.of(2L), 404);
}
@Test
public void testNameMatch_nsstar_found() {
generateActualJsonWithName("ns*");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(5);
}
@Test
public void testNameMatch_ns1_cat_lstar_found() {
generateActualJsonWithName("ns1.cat.l*");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatch_ns1_castar_found() {
generateActualJsonWithName("ns1.ca*");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(5);
}
@Test
public void testNameMatch_dogstar_notFound() {
generateActualJsonWithName("dog*");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testNameMatch_nontruncatedResultSet() {
createManyHosts(4);
assertThat(generateActualJsonWithName("nsx*.cat.lol"))
.isEqualTo(generateExpectedJson("rdap_nontruncated_hosts.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(4);
}
@Test
public void testNameMatch_truncatedResultSet() {
createManyHosts(5);
assertThat(generateActualJsonWithName("nsx*.cat.lol"))
.isEqualTo(
generateExpectedJson(
"name=nsx*.cat.lol&cursor=bnN4NC5jYXQubG9s", "rdap_truncated_hosts.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(5);
}
@Test
public void testNameMatch_reallyTruncatedResultSet() {
createManyHosts(9);
assertThat(generateActualJsonWithName("nsx*.cat.lol"))
.isEqualTo(
generateExpectedJson(
"name=nsx*.cat.lol&cursor=bnN4NC5jYXQubG9s", "rdap_truncated_hosts.json"));
assertThat(response.getStatus()).isEqualTo(200);
// When searching names, we look for additional matches, in case some are not visible.
verifyMetrics(9);
}
@Test
public void testNameMatchDeletedHost_foundTheOtherHost() {
persistResource(
hostNs1CatLol.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
assertThat(generateActualJsonWithName("ns*.cat.lol"))
.isEqualTo(
generateExpectedJsonForNameserver(
"ns2.cat.lol",
null,
"4-ROID",
"v6",
"bad:f00d:cafe::15:beef",
"rdap_host_linked.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(2);
}
@Test
public void testNameMatchDeletedHost_notFound() {
persistResource(hostNs1CatLol.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
assertThat(generateActualJsonWithName("ns1.cat.lol"))
.isEqualTo(generateExpectedJson("No nameservers found", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testNameMatchDeletedHostWithWildcard_notFound() {
persistResource(hostNs1CatLol.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
assertThat(generateActualJsonWithName("cat.lo*"))
.isEqualTo(generateExpectedJson("No nameservers found", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testNameMatchDeleted_notFound_includeDeletedNotSpecified() {
createDeletedHost();
generateActualJsonWithName("nsdeleted.cat.lol");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testNameMatchDeleted_notFound_notLoggedIn() {
createDeletedHost();
action.includeDeletedParam = Optional.of(true);
generateActualJsonWithName("nsdeleted.cat.lol");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testNameMatchDeleted_notFound_loggedInAsDifferentRegistrar() {
createDeletedHost();
action.includeDeletedParam = Optional.of(true);
login("unicoderegistrar");
generateActualJsonWithName("nsdeleted.cat.lol");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics(Optional.of(1L), 404);
}
@Test
public void testNameMatchDeleted_found_loggedInAsCorrectRegistrar() {
createDeletedHost();
action.includeDeletedParam = Optional.of(true);
login("TheRegistrar");
generateActualJsonWithName("nsdeleted.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatchDeleted_found_loggedInAsAdmin() {
createDeletedHost();
action.includeDeletedParam = Optional.of(true);
loginAsAdmin();
generateActualJsonWithName("nsdeleted.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatchDeleted_found_loggedInAndRequestingSameRegistrar() {
createDeletedHost();
action.registrarParam = Optional.of("TheRegistrar");
action.includeDeletedParam = Optional.of(true);
login("TheRegistrar");
generateActualJsonWithName("nsdeleted.cat.lol");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testNameMatchDeleted_notFound_loggedInButRequestingDifferentRegistrar() {
createDeletedHost();
action.registrarParam = Optional.of("unicoderegistrar");
action.includeDeletedParam = Optional.of(true);
login("TheRegistrar");
generateActualJsonWithName("nsdeleted.cat.lol");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
/**
* Checks multi-page result set navigation using the cursor.
*
* <p>If there are more results than the max result set size, the RDAP code returns a cursor token
* which can be used in a subsequent call to get the next chunk of results.
*
* @param byName true if we are searching by name; false if we are searching by address
* @param paramValue the query string
* @param expectedNames an immutable list of the host names we expect to retrieve
*/
private void checkCursorNavigation(
boolean byName, String paramValue, ImmutableList<String> expectedNames)
throws Exception {
String cursor = null;
int expectedNameOffset = 0;
int expectedPageCount =
(expectedNames.size() + action.rdapResultSetMaxSize - 1) / action.rdapResultSetMaxSize;
for (int pageNum = 0; pageNum < expectedPageCount; pageNum++) {
Object results =
byName
? generateActualJsonWithName(paramValue, cursor)
: generateActualJsonWithIp(paramValue, cursor);
assertThat(response.getStatus()).isEqualTo(200);
String linkToNext = RdapTestHelper.getLinkToNext(results);
if (pageNum == expectedPageCount - 1) {
assertThat(linkToNext).isNull();
} else {
assertThat(linkToNext).isNotNull();
int pos = linkToNext.indexOf("cursor=");
assertThat(pos).isAtLeast(0);
cursor = URLDecoder.decode(linkToNext.substring(pos + 7), "UTF-8");
Object searchResults = ((JSONObject) results).get("nameserverSearchResults");
assertThat(searchResults).isInstanceOf(JSONArray.class);
assertThat(((JSONArray) searchResults)).hasSize(action.rdapResultSetMaxSize);
for (Object item : ((JSONArray) searchResults)) {
assertThat(item).isInstanceOf(JSONObject.class);
Object name = ((JSONObject) item).get("ldhName");
assertThat(name).isNotNull();
assertThat(name).isInstanceOf(String.class);
assertThat(name).isEqualTo(expectedNames.get(expectedNameOffset++));
}
response = new FakeResponse();
action.response = response;
}
}
}
@Test
public void testNameMatch_cursorNavigationWithSuperordinateDomain() throws Exception {
createManyHosts(9);
checkCursorNavigation(
true,
"ns*.cat.lol",
ImmutableList.of(
"nsx1.cat.lol",
"nsx2.cat.lol",
"nsx3.cat.lol",
"nsx4.cat.lol",
"nsx5.cat.lol",
"nsx6.cat.lol",
"nsx7.cat.lol",
"nsx8.cat.lol",
"nsx9.cat.lol"));
}
@Test
public void testNameMatch_cursorNavigationWithPrefix() throws Exception {
createManyHosts(9);
checkCursorNavigation(
true,
"ns*",
ImmutableList.of(
"ns1.cat.1.test",
"ns1.cat.external",
"ns1.cat.lol",
"ns1.cat.xn--q9jyb4c",
"ns1.cat2.lol",
"ns2.cat.lol",
"nsx1.cat.lol",
"nsx2.cat.lol",
"nsx3.cat.lol",
"nsx4.cat.lol",
"nsx5.cat.lol",
"nsx6.cat.lol",
"nsx7.cat.lol",
"nsx8.cat.lol",
"nsx9.cat.lol"));
}
@Test
public void testAddressMatch_invalidAddress() {
generateActualJsonWithIp("It is to laugh");
assertThat(response.getStatus()).isEqualTo(400);
verifyErrorMetrics(Optional.empty(), 400);
}
@Test
public void testAddressMatchV4Address_found() {
assertThat(generateActualJsonWithIp("1.2.3.4"))
.isEqualTo(
generateExpectedJsonForNameserver(
"ns1.cat.lol", null, "2-ROID", "v4", "1.2.3.4", "rdap_host_linked.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testAddressMatchV4Address_found_sameRegistrarRequested() {
action.registrarParam = Optional.of("TheRegistrar");
generateActualJsonWithIp("1.2.3.4");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testAddressMatchV4Address_notFound_differentRegistrarRequested() {
action.registrarParam = Optional.of("unicoderegistrar");
generateActualJsonWithIp("1.2.3.4");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testAddressMatchV6Address_foundMultiple() {
assertThat(generateActualJsonWithIp("bad:f00d:cafe::15:beef"))
.isEqualTo(generateExpectedJson("rdap_multiple_hosts.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(2);
}
@Test
public void testAddressMatchLocalhost_notFound() {
generateActualJsonWithIp("127.0.0.1");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testAddressMatchDeletedHost_notFound() {
persistResource(hostNs1CatLol.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
assertThat(generateActualJsonWithIp("1.2.3.4"))
.isEqualTo(generateExpectedJson("No nameservers found", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testAddressMatch_nontruncatedResultSet() {
createManyHosts(4);
assertThat(generateActualJsonWithIp("5.5.5.1"))
.isEqualTo(generateExpectedJson("rdap_nontruncated_hosts.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(4);
}
@Test
public void testAddressMatch_truncatedResultSet() {
createManyHosts(5);
assertThat(generateActualJsonWithIp("5.5.5.1"))
.isEqualTo(
generateExpectedJson(
"ip=5.5.5.1&cursor=MTctUk9JRA%3D%3D", "rdap_truncated_hosts.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(5);
}
@Test
public void testAddressMatch_reallyTruncatedResultSet() {
createManyHosts(9);
assertThat(generateActualJsonWithIp("5.5.5.1"))
.isEqualTo(
generateExpectedJson(
"ip=5.5.5.1&cursor=MTctUk9JRA%3D%3D", "rdap_truncated_hosts.json"));
assertThat(response.getStatus()).isEqualTo(200);
// When searching by address and not including deleted, we don't need to search for extra
// matches.
verifyMetrics(5);
}
@Test
public void testAddressMatchDeleted_notFound_includeDeletedNotSpecified() {
createDeletedHost();
generateActualJsonWithIp("4.3.2.1");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testAddressMatchDeleted_notFound_notLoggedIn() {
createDeletedHost();
action.includeDeletedParam = Optional.of(true);
generateActualJsonWithIp("4.3.2.1");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testAddressMatchDeleted_notFound_loggedInAsDifferentRegistrar() {
createDeletedHost();
action.includeDeletedParam = Optional.of(true);
login("unicoderegistrar");
generateActualJsonWithIp("4.3.2.1");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics(Optional.of(1L), 404);
}
@Test
public void testAddressMatchDeleted_found_loggedInAsCorrectRegistrar() {
createDeletedHost();
action.includeDeletedParam = Optional.of(true);
login("TheRegistrar");
generateActualJsonWithIp("4.3.2.1");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testAddressMatchDeleted_found_loggedInAsAdmin() {
createDeletedHost();
action.includeDeletedParam = Optional.of(true);
loginAsAdmin();
generateActualJsonWithIp("4.3.2.1");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testAddressMatchDeleted_found_loggedInAndRequestingSameRegisrar() {
createDeletedHost();
action.registrarParam = Optional.of("TheRegistrar");
action.includeDeletedParam = Optional.of(true);
login("TheRegistrar");
generateActualJsonWithIp("4.3.2.1");
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(1);
}
@Test
public void testAddressMatchDeleted_notFound_loggedButRequestingDiffentRegistrar() {
createDeletedHost();
action.registrarParam = Optional.of("unicoderegistrar");
action.includeDeletedParam = Optional.of(true);
login("TheRegistrar");
generateActualJsonWithIp("4.3.2.1");
assertThat(response.getStatus()).isEqualTo(404);
verifyErrorMetrics();
}
@Test
public void testAddressMatch_cursorNavigation() throws Exception {
createManyHosts(9);
checkCursorNavigation(
false,
"5.5.5.1",
ImmutableList.of(
"nsx1.cat.lol",
"nsx2.cat.lol",
"nsx3.cat.lol",
"nsx4.cat.lol",
"nsx5.cat.lol",
"nsx6.cat.lol",
"nsx7.cat.lol",
"nsx8.cat.lol",
"nsx9.cat.lol"));
}
}