diff --git a/java/google/registry/rdap/BUILD b/java/google/registry/rdap/BUILD index 0f21676fa..5abbf22e2 100644 --- a/java/google/registry/rdap/BUILD +++ b/java/google/registry/rdap/BUILD @@ -11,6 +11,7 @@ java_library( "//java/google/registry/config", "//java/google/registry/flows", "//java/google/registry/model", + "//java/google/registry/monitoring/metrics", "//java/google/registry/request", "//java/google/registry/request/auth", "//java/google/registry/ui/server/registrar", diff --git a/java/google/registry/rdap/RdapActionBase.java b/java/google/registry/rdap/RdapActionBase.java index f1961ad22..25cf11b44 100644 --- a/java/google/registry/rdap/RdapActionBase.java +++ b/java/google/registry/rdap/RdapActionBase.java @@ -419,7 +419,7 @@ public abstract class RdapActionBase implements Runnable { resources, (someExcluded && (resources.size() < rdapResultSetMaxSize + 1)) ? IncompletenessWarningType.MIGHT_BE_INCOMPLETE - : IncompletenessWarningType.NONE, + : IncompletenessWarningType.COMPLETE, numResourcesQueried); } } diff --git a/java/google/registry/rdap/RdapDomainSearchAction.java b/java/google/registry/rdap/RdapDomainSearchAction.java index 7eb789a22..e5ff5a16d 100644 --- a/java/google/registry/rdap/RdapDomainSearchAction.java +++ b/java/google/registry/rdap/RdapDomainSearchAction.java @@ -464,14 +464,14 @@ public class RdapDomainSearchAction extends RdapActionBase { domains, (numHostKeysSearched >= MAX_NAMESERVERS_IN_FIRST_STAGE) ? IncompletenessWarningType.MIGHT_BE_INCOMPLETE - : IncompletenessWarningType.NONE, + : IncompletenessWarningType.COMPLETE, now); } } /** Output JSON for a list of domains, with no incompleteness warnings. */ private RdapSearchResults makeSearchResults(List domains, DateTime now) { - return makeSearchResults(domains, IncompletenessWarningType.NONE, now); + return makeSearchResults(domains, IncompletenessWarningType.COMPLETE, now); } /** Output JSON from data in an {@link RdapResultSet} object. */ diff --git a/java/google/registry/rdap/RdapEntitySearchAction.java b/java/google/registry/rdap/RdapEntitySearchAction.java index 2d81f3925..96ecb24b6 100644 --- a/java/google/registry/rdap/RdapEntitySearchAction.java +++ b/java/google/registry/rdap/RdapEntitySearchAction.java @@ -219,7 +219,7 @@ public class RdapEntitySearchAction extends RdapActionBase { ((contactResource != null) && shouldBeVisible(contactResource, now)) ? ImmutableList.of(contactResource) : ImmutableList.of(), - IncompletenessWarningType.NONE, + IncompletenessWarningType.COMPLETE, getMatchingRegistrars(partialStringQuery.getInitialString()), now); // Handle queries with a wildcard (or including deleted), but no suffix. Because the handle @@ -328,6 +328,6 @@ public class RdapEntitySearchAction extends RdapActionBase { ImmutableList.copyOf(jsonOutputList), (jsonOutputList.size() < rdapResultSetMaxSize) ? incompletenessWarningType - : IncompletenessWarningType.NONE); + : IncompletenessWarningType.COMPLETE); } } diff --git a/java/google/registry/rdap/RdapMetrics.java b/java/google/registry/rdap/RdapMetrics.java index 7fa9e55a4..02e6fbdf0 100644 --- a/java/google/registry/rdap/RdapMetrics.java +++ b/java/google/registry/rdap/RdapMetrics.java @@ -14,6 +14,20 @@ package google.registry.rdap; +import com.google.auto.value.AutoValue; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableSet; +import google.registry.monitoring.metrics.DistributionFitter; +import google.registry.monitoring.metrics.EventMetric; +import google.registry.monitoring.metrics.FibonacciFitter; +import google.registry.monitoring.metrics.IncrementableMetric; +import google.registry.monitoring.metrics.LabelDescriptor; +import google.registry.monitoring.metrics.MetricRegistryImpl; +import google.registry.rdap.RdapSearchResults.IncompletenessWarningType; +import google.registry.request.Action; +import java.util.Optional; +import javax.inject.Inject; + /** RDAP Instrumentation. */ public class RdapMetrics { @@ -28,4 +42,259 @@ public class RdapMetrics { NAMESERVER, NAMESERVERS } + + enum SearchType { + NONE, + BY_DOMAIN_NAME, + BY_NAMESERVER_NAME, + BY_NAMESERVER_ADDRESS, + BY_FULL_NAME, + BY_HANDLE + } + + enum WildcardType { + NO_WILDCARD, + PREFIX, + SUFFIX, + PREFIX_AND_SUFFIX, + INVALID, + TREATED_AS_WILDCARD + } + + private static final int MAX_RECORDED_PREFIX_LENGTH = 5; + private static final String MAX_PREFIX_LENGTH_LABEL = "5+"; + + private static final ImmutableSet LABEL_DESCRIPTORS_FOR_REQUESTS = + ImmutableSet.of( + LabelDescriptor.create("endpoint_type", "The RDAP endpoint."), + LabelDescriptor.create("include_deleted", "Whether deleted records are included."), + LabelDescriptor.create("registrar_specified", "Whether a registrar was specified"), + LabelDescriptor.create("authorization", "Type of user authorization"), + LabelDescriptor.create("httpMethod", "HTTP request method")); + + private static final ImmutableSet LABEL_DESCRIPTORS_FOR_RESPONSES = + ImmutableSet.of( + LabelDescriptor.create("endpoint_type", "The RDAP endpoint."), + LabelDescriptor.create("search_type", "The identifier type used to search."), + LabelDescriptor.create("wildcard_type", "The search string wildcard type."), + LabelDescriptor.create("status_code", "Returned HTTP status code"), + LabelDescriptor.create( + "incompleteness_warning_type", + "Warning status returned with result set (e.g. truncated, incomplete")); + + private static final ImmutableSet LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS = + ImmutableSet.of( + LabelDescriptor.create("endpoint_type", "The RDAP endpoint."), + LabelDescriptor.create("search_type", "The identifier type used to search."), + LabelDescriptor.create("wildcard_type", "The search string wildcard type."), + LabelDescriptor.create( + "prefix_length", + String.format( + "The length of the prefix before the wildcard (limited to %d).", + MAX_RECORDED_PREFIX_LENGTH)), + LabelDescriptor.create("include_deleted", "Whether deleted records are included.")); + + // Fibonacci fitter more suitable for integer-type values. Allows values between 0 and 4181, + // which is the 19th Fibonacci number. + private static final DistributionFitter FIBONACCI_FITTER = FibonacciFitter.create(4181); + + @VisibleForTesting + static final IncrementableMetric requests = + MetricRegistryImpl.getDefault() + .newIncrementableMetric( + "/rdap/requests", + "Count of RDAP Requests", + "count", + LABEL_DESCRIPTORS_FOR_REQUESTS); + + @VisibleForTesting + static final IncrementableMetric responses = + MetricRegistryImpl.getDefault() + .newIncrementableMetric( + "/rdap/responses", + "Count of RDAP Responses", + "count", + LABEL_DESCRIPTORS_FOR_RESPONSES); + + @VisibleForTesting + static final EventMetric numberOfDomainsRetrieved = + MetricRegistryImpl.getDefault() + .newEventMetric( + "/rdap/num_domains_retrieved", + "Number of domains retrieved", + "count", + LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS, + FIBONACCI_FITTER); + + @VisibleForTesting + static final EventMetric numberOfHostsRetrieved = + MetricRegistryImpl.getDefault() + .newEventMetric( + "/rdap/num_hosts_retrieved", + "Number of hosts retrieved", + "count", + LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS, + FIBONACCI_FITTER); + + @VisibleForTesting + static final EventMetric numberOfContactsRetrieved = + MetricRegistryImpl.getDefault() + .newEventMetric( + "/rdap/num_contacts_retrieved", + "Number of contacts retrieved", + "count", + LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS, + FIBONACCI_FITTER); + + @Inject + public RdapMetrics() {} + + private static String getLabelStringForPrefixLength(int prefixLength) { + return (prefixLength >= MAX_RECORDED_PREFIX_LENGTH) + ? MAX_PREFIX_LENGTH_LABEL + : String.valueOf(prefixLength); + } + + /** + * Increments the RDAP metrics. + * + *

This is intended to be called at the conclusion of a query, with the parameters specifying + * everything that happened. This method takes the data and updates metrics which offer several + * ways of looking at the data, since cardinality constraints prevent us from saving all the + * information in a single metric. + */ + public void updateMetrics( + RdapMetricInformation rdapMetricInformation) { + requests.increment( + rdapMetricInformation.endpointType().toString(), + rdapMetricInformation.includeDeleted() ? "YES" : "NO", + rdapMetricInformation.registrarSpecified() ? "YES" : "NO", + rdapMetricInformation.role().toString(), + rdapMetricInformation.requestMethod().toString()); + responses.increment( + rdapMetricInformation.endpointType().toString(), + rdapMetricInformation.searchType().toString(), + rdapMetricInformation.wildcardType().toString(), + String.valueOf(rdapMetricInformation.statusCode()), + rdapMetricInformation.incompletenessWarningType().toString()); + if (rdapMetricInformation.numDomainsRetrieved().isPresent()) { + numberOfDomainsRetrieved.record( + rdapMetricInformation.numDomainsRetrieved().get(), + rdapMetricInformation.endpointType().toString(), + rdapMetricInformation.searchType().toString(), + rdapMetricInformation.wildcardType().toString(), + getLabelStringForPrefixLength(rdapMetricInformation.prefixLength()), + rdapMetricInformation.includeDeleted() ? "YES" : "NO"); + } + if (rdapMetricInformation.numHostsRetrieved().isPresent()) { + numberOfHostsRetrieved.record( + rdapMetricInformation.numHostsRetrieved().get(), + rdapMetricInformation.endpointType().toString(), + rdapMetricInformation.searchType().toString(), + rdapMetricInformation.wildcardType().toString(), + getLabelStringForPrefixLength(rdapMetricInformation.prefixLength()), + rdapMetricInformation.includeDeleted() ? "YES" : "NO"); + } + if (rdapMetricInformation.numContactsRetrieved().isPresent()) { + numberOfContactsRetrieved.record( + rdapMetricInformation.numContactsRetrieved().get(), + rdapMetricInformation.endpointType().toString(), + rdapMetricInformation.searchType().toString(), + rdapMetricInformation.wildcardType().toString(), + getLabelStringForPrefixLength(rdapMetricInformation.prefixLength()), + rdapMetricInformation.includeDeleted() ? "YES" : "NO"); + } + } + + @AutoValue + abstract static class RdapMetricInformation { + + /** The type of RDAP endpoint (domain, domains, nameserver, etc.). */ + abstract EndpointType endpointType(); + + /** The search type (by domain name, by nameserver name, etc.). */ + abstract SearchType searchType(); + + /** The type of wildcarding requested (prefix, suffix, etc.). */ + abstract WildcardType wildcardType(); + + /** + * The length of the prefix string before the wildcard, if any; any length longer than + * MAX_RECORDED_PREFIX_LENGTH is limited to MAX_RECORDED_PREFIX_LENGTH when recording the + * metric, to avoid cardinality problems. + */ + abstract int prefixLength(); + + /** Whether the search included deleted records. */ + abstract boolean includeDeleted(); + + /** Whether the search requested a specific registrar. */ + abstract boolean registrarSpecified(); + + /** Type of authentication/authorization: public, admin or registrar. */ + abstract RdapAuthorization.Role role(); + + /** Http request method (GET, POST, HEAD, etc.). */ + abstract Action.Method requestMethod(); + + /** Http status code. */ + abstract int statusCode(); + + /** Incompleteness warning type (e.g. truncated). */ + abstract IncompletenessWarningType incompletenessWarningType(); + + /** + * Number of domains retrieved from the database; this might be more than were actually returned + * in the response; absent if a search was not performed. + */ + abstract Optional numDomainsRetrieved(); + + /** + * Number of hosts retrieved from the database; this might be more than were actually returned + * in the response; absent if a search was not performed. + */ + abstract Optional numHostsRetrieved(); + + /** + * Number of contacts retrieved from the database; this might be more than were actually + * returned in the response; absent if a search was not performed. + */ + abstract Optional numContactsRetrieved(); + + @AutoValue.Builder + abstract static class Builder { + abstract Builder setEndpointType(EndpointType endpointType); + + abstract Builder setSearchType(SearchType searchType); + + abstract Builder setWildcardType(WildcardType wildcardType); + + abstract Builder setPrefixLength(int prefixLength); + + abstract Builder setIncludeDeleted(boolean includeDeleted); + + abstract Builder setRegistrarSpecified(boolean registrarSpecified); + + abstract Builder setRole(RdapAuthorization.Role role); + + abstract Builder setRequestMethod(Action.Method requestMethod); + + abstract Builder setStatusCode(int statusCode); + + abstract Builder setIncompletenessWarningType( + IncompletenessWarningType incompletenessWarningType); + + abstract Builder setNumDomainsRetrieved(long numDomainsRetrieved); + + abstract Builder setNumHostsRetrieved(long numHostsRetrieved); + + abstract Builder setNumContactsRetrieved(long numContactRetrieved); + + abstract RdapMetricInformation build(); + } + + static Builder builder() { + return new AutoValue_RdapMetrics_RdapMetricInformation.Builder(); + } + } } diff --git a/java/google/registry/rdap/RdapNameserverSearchAction.java b/java/google/registry/rdap/RdapNameserverSearchAction.java index ec9e27818..e1324669f 100644 --- a/java/google/registry/rdap/RdapNameserverSearchAction.java +++ b/java/google/registry/rdap/RdapNameserverSearchAction.java @@ -212,7 +212,7 @@ public class RdapNameserverSearchAction extends RdapActionBase { } } } - return makeSearchResults(hostList, IncompletenessWarningType.NONE, now); + return makeSearchResults(hostList, IncompletenessWarningType.COMPLETE, now); } /** diff --git a/java/google/registry/rdap/RdapResultSet.java b/java/google/registry/rdap/RdapResultSet.java index d56bb898c..0bfb84672 100644 --- a/java/google/registry/rdap/RdapResultSet.java +++ b/java/google/registry/rdap/RdapResultSet.java @@ -23,7 +23,7 @@ import java.util.List; abstract class RdapResultSet { static RdapResultSet create(List resources) { - return create(resources, IncompletenessWarningType.NONE, resources.size()); + return create(resources, IncompletenessWarningType.COMPLETE, resources.size()); } static RdapResultSet create( @@ -43,4 +43,3 @@ abstract class RdapResultSet { /** Number of resources retrieved from the database in the process of assembling the data set. */ abstract int numResourcesRetrieved(); } - diff --git a/java/google/registry/rdap/RdapSearchResults.java b/java/google/registry/rdap/RdapSearchResults.java index 93b42861f..47c98a584 100644 --- a/java/google/registry/rdap/RdapSearchResults.java +++ b/java/google/registry/rdap/RdapSearchResults.java @@ -33,7 +33,7 @@ abstract class RdapSearchResults { enum IncompletenessWarningType { /** Result set is complete. */ - NONE, + COMPLETE, /** Result set has been limited to the maximum size. */ TRUNCATED, @@ -46,7 +46,7 @@ abstract class RdapSearchResults { } static RdapSearchResults create(ImmutableList> jsonList) { - return create(jsonList, IncompletenessWarningType.NONE); + return create(jsonList, IncompletenessWarningType.COMPLETE); } static RdapSearchResults create( diff --git a/javatests/google/registry/rdap/BUILD b/javatests/google/registry/rdap/BUILD index d23bd2ae7..4f3b6678f 100644 --- a/javatests/google/registry/rdap/BUILD +++ b/javatests/google/registry/rdap/BUILD @@ -14,6 +14,7 @@ java_library( deps = [ "//java/google/registry/config", "//java/google/registry/model", + "//java/google/registry/monitoring/metrics/contrib", "//java/google/registry/rdap", "//java/google/registry/request", "//java/google/registry/request/auth", diff --git a/javatests/google/registry/rdap/RdapMetricsTest.java b/javatests/google/registry/rdap/RdapMetricsTest.java new file mode 100644 index 000000000..712305de2 --- /dev/null +++ b/javatests/google/registry/rdap/RdapMetricsTest.java @@ -0,0 +1,290 @@ +// 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 google.registry.monitoring.metrics.contrib.DistributionMetricSubject.assertThat; +import static google.registry.monitoring.metrics.contrib.LongMetricSubject.assertThat; + +import com.google.common.collect.ImmutableSet; +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.request.Action; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link RdapMetrics}. */ +@RunWith(JUnit4.class) +public class RdapMetricsTest { + + private final RdapMetrics rdapMetrics = new RdapMetrics(); + + @Before + public void setUp() throws Exception { + RdapMetrics.requests.reset(); + RdapMetrics.responses.reset(); + RdapMetrics.numberOfDomainsRetrieved.reset(); + RdapMetrics.numberOfHostsRetrieved.reset(); + RdapMetrics.numberOfContactsRetrieved.reset(); + } + + private RdapMetrics.RdapMetricInformation.Builder getBuilder() { + return RdapMetrics.RdapMetricInformation.builder() + .setEndpointType(EndpointType.DOMAINS) + .setSearchType(SearchType.NONE) + .setWildcardType(WildcardType.NO_WILDCARD) + .setPrefixLength(0) + .setIncludeDeleted(false) + .setRegistrarSpecified(false) + .setRole(RdapAuthorization.Role.PUBLIC) + .setRequestMethod(Action.Method.GET) + .setStatusCode(200) + .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE); + } + + @Test + public void testPost() throws Exception { + rdapMetrics.updateMetrics(getBuilder().setRequestMethod(Action.Method.POST).build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "NO", "NO", "PUBLIC", "POST") + .and() + .hasNoOtherValues(); + } + + @Test + public void testHead() throws Exception { + rdapMetrics.updateMetrics(getBuilder().setRequestMethod(Action.Method.HEAD).build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "NO", "NO", "PUBLIC", "HEAD") + .and() + .hasNoOtherValues(); + } + + @Test + public void testPrefixLength_cappedAt5() throws Exception { + rdapMetrics.updateMetrics( + getBuilder().setPrefixLength(6).setNumDomainsRetrieved(1).build()); + assertThat(RdapMetrics.numberOfDomainsRetrieved) + .hasDataSetForLabels(ImmutableSet.of(1), "DOMAINS", "NONE", "NO_WILDCARD", "5+", "NO") + .and() + .hasNoOtherValues(); + } + + @Test + public void testIncludeDeleted() throws Exception { + rdapMetrics.updateMetrics(getBuilder().setIncludeDeleted(true).build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "YES", "NO", "PUBLIC", "GET") + .and() + .hasNoOtherValues(); + } + + @Test + public void testDesiredRegistrar() throws Exception { + rdapMetrics.updateMetrics(getBuilder().setRegistrarSpecified(true).build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "NO", "YES", "PUBLIC", "GET") + .and() + .hasNoOtherValues(); + } + + @Test + public void testCompleteResultSet() throws Exception { + rdapMetrics.updateMetrics( + getBuilder() + .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) + .build()); + assertThat(RdapMetrics.responses) + .hasValueForLabels(1, "DOMAINS", "NONE", "NO_WILDCARD", "200", "COMPLETE") + .and() + .hasNoOtherValues(); + } + + @Test + public void testTruncatedResultSet() throws Exception { + rdapMetrics.updateMetrics( + getBuilder() + .setIncompletenessWarningType(IncompletenessWarningType.TRUNCATED) + .build()); + assertThat(RdapMetrics.responses) + .hasValueForLabels(1, "DOMAINS", "NONE", "NO_WILDCARD", "200", "TRUNCATED") + .and() + .hasNoOtherValues(); + } + + @Test + public void testPossiblyIncompleteResultSet() throws Exception { + rdapMetrics.updateMetrics( + getBuilder() + .setIncompletenessWarningType(IncompletenessWarningType.MIGHT_BE_INCOMPLETE) + .build()); + assertThat(RdapMetrics.responses) + .hasValueForLabels(1, "DOMAINS", "NONE", "NO_WILDCARD", "200", "MIGHT_BE_INCOMPLETE") + .and() + .hasNoOtherValues(); + } + + @Test + public void testPublicRole() throws Exception { + rdapMetrics.updateMetrics(getBuilder().setRole(RdapAuthorization.Role.PUBLIC).build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "NO", "NO", "PUBLIC", "GET") + .and() + .hasNoOtherValues(); + } + + @Test + public void testRegistrarRole() throws Exception { + rdapMetrics.updateMetrics(getBuilder().setRole(RdapAuthorization.Role.REGISTRAR).build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "NO", "NO", "REGISTRAR", "GET") + .and() + .hasNoOtherValues(); + } + + @Test + public void testAdminRole() throws Exception { + rdapMetrics.updateMetrics(getBuilder().setRole(RdapAuthorization.Role.ADMINISTRATOR).build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "NO", "NO", "ADMINISTRATOR", "GET") + .and() + .hasNoOtherValues(); + } + + /** Tests what would happen in a domain search for "cat.lol" which found that domain. */ + @Test + public void testSimpleDomainSearch() throws Exception { + rdapMetrics.updateMetrics( + getBuilder() + .setSearchType(SearchType.BY_DOMAIN_NAME) + .setPrefixLength(7) + .setNumDomainsRetrieved(1) + .build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "NO", "NO", "PUBLIC", "GET") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.responses) + .hasValueForLabels(1, "DOMAINS", "BY_DOMAIN_NAME", "NO_WILDCARD", "200", "COMPLETE") + .and() + .hasNoOtherValues(); + // The prefix length is capped at 5. + assertThat(RdapMetrics.numberOfDomainsRetrieved) + .hasDataSetForLabels( + ImmutableSet.of(1), "DOMAINS", "BY_DOMAIN_NAME", "NO_WILDCARD", "5+", "NO") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.numberOfHostsRetrieved).hasNoOtherValues(); + assertThat(RdapMetrics.numberOfContactsRetrieved).hasNoOtherValues(); + } + + /** + * Tests what would happen in a domain search by nameserver name for "ns*.cat.lol", including + * deleted domains, which found 10 matching hosts, then looked for domains and found 5 matches. + */ + @Test + public void testDomainSearchByNameserverWithWildcardAndDeleted() throws Exception { + rdapMetrics.updateMetrics( + getBuilder() + .setSearchType(SearchType.BY_NAMESERVER_NAME) + .setWildcardType(WildcardType.PREFIX_AND_SUFFIX) + .setPrefixLength(2) + .setIncludeDeleted(true) + .setNumDomainsRetrieved(5) + .setNumHostsRetrieved(10) + .build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "DOMAINS", "YES", "NO", "PUBLIC", "GET") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.responses) + .hasValueForLabels( + 1, "DOMAINS", "BY_NAMESERVER_NAME", "PREFIX_AND_SUFFIX", "200", "COMPLETE") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.numberOfDomainsRetrieved) + .hasDataSetForLabels( + ImmutableSet.of(5), "DOMAINS", "BY_NAMESERVER_NAME", "PREFIX_AND_SUFFIX", "2", "YES") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.numberOfHostsRetrieved) + .hasDataSetForLabels( + ImmutableSet.of(10), "DOMAINS", "BY_NAMESERVER_NAME", "PREFIX_AND_SUFFIX", "2", "YES") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.numberOfContactsRetrieved).hasNoOtherValues(); + } + + /** Tests what would happen in a nameserver search for "*.cat.lol", which found no matches. */ + @Test + public void testNoNameserversFound() throws Exception { + rdapMetrics.updateMetrics( + getBuilder() + .setEndpointType(EndpointType.NAMESERVERS) + .setSearchType(SearchType.BY_NAMESERVER_NAME) + .setWildcardType(WildcardType.SUFFIX) + .setStatusCode(404) + .setNumHostsRetrieved(0) + .build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "NAMESERVERS", "NO", "NO", "PUBLIC", "GET") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.responses) + .hasValueForLabels( + 1, "NAMESERVERS", "BY_NAMESERVER_NAME", "SUFFIX", "404", "COMPLETE") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.numberOfDomainsRetrieved).hasNoOtherValues(); + assertThat(RdapMetrics.numberOfHostsRetrieved) + .hasDataSetForLabels( + ImmutableSet.of(0), "NAMESERVERS", "BY_NAMESERVER_NAME", "SUFFIX", "0", "NO") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.numberOfContactsRetrieved).hasNoOtherValues(); + } + + /** Tests what would happen in an entity search for "Mike*" which found 50 contacts. */ + @Test + public void testEntitySearchByNameWithWildcard() throws Exception { + rdapMetrics.updateMetrics( + getBuilder() + .setEndpointType(EndpointType.ENTITIES) + .setSearchType(SearchType.BY_FULL_NAME) + .setWildcardType(WildcardType.PREFIX) + .setPrefixLength(4) + .setNumContactsRetrieved(50) + .build()); + assertThat(RdapMetrics.requests) + .hasValueForLabels(1, "ENTITIES", "NO", "NO", "PUBLIC", "GET") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.responses) + .hasValueForLabels( + 1, "ENTITIES", "BY_FULL_NAME", "PREFIX", "200", "COMPLETE") + .and() + .hasNoOtherValues(); + assertThat(RdapMetrics.numberOfDomainsRetrieved).hasNoOtherValues(); + assertThat(RdapMetrics.numberOfHostsRetrieved).hasNoOtherValues(); + assertThat(RdapMetrics.numberOfContactsRetrieved) + .hasDataSetForLabels( + ImmutableSet.of(50), "ENTITIES", "BY_FULL_NAME", "PREFIX", "4", "NO") + .and() + .hasNoOtherValues(); + } +}