google-nomulus/javatests/google/registry/flows/EppLifecycleDomainTest.java
mcilwain 218c4517eb Stop exporting EPP flow metrics to BigQuery
These are simply too costly in their current form now that we are handling double-digit QPS, so at a minimum we'd want to refactor these for batched exports using a background thread (like how Stackdriver metrics work). However, upon further review, that work isn't worth doing if this BigQuery table isn't actually being used for anything, and it seems that we aren't using it anymore given that ICANN transaction reporting no longer requires it.

So the simplest thing to do is simply to get rid of this entirely, and just use a combination of Stackdriver metrics and App Engine logs. The eppMetrics BigQuery table is ~1.2 billion rows and takes up 223 GB, so that's not an insignificant GCP billings saving if we can delete it.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=215905466
2018-10-08 16:59:29 -04:00

1162 lines
49 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.flows;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import static google.registry.model.eppoutput.Result.Code.SUCCESS_AND_CLOSE;
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.assertBillingEventsForResource;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.stripBillingEventId;
import static google.registry.testing.EppMetricSubject.assertThat;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
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.re2j.Matcher;
import com.google.re2j.Pattern;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.DomainResource;
import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState;
import google.registry.model.reporting.HistoryEntry.Type;
import google.registry.testing.AppEngineRule;
import java.util.Objects;
import java.util.Optional;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for domain lifecycle. */
@RunWith(JUnit4.class)
public class EppLifecycleDomainTest extends EppTestCase {
@Rule
public final AppEngineRule appEngine =
AppEngineRule.builder().withDatastore().withTaskQueue().build();
@Before
public void initTld() {
createTlds("example", "tld");
}
/** Create the two administrative contacts and two hosts. */
void createContactsAndHosts() throws Exception {
DateTime createTime = DateTime.parse("2000-06-01T00:00:00Z");
createContacts(createTime);
assertThatCommand("host_create.xml", ImmutableMap.of("HOSTNAME", "ns1.example.external"))
.atTime(createTime.plusMinutes(2))
.hasResponse(
"host_create_response.xml",
ImmutableMap.of(
"HOSTNAME", "ns1.example.external",
"CRDATE", createTime.plusMinutes(2).toString()));
assertThatCommand("host_create.xml", ImmutableMap.of("HOSTNAME", "ns2.example.external"))
.atTime(createTime.plusMinutes(3))
.hasResponse(
"host_create_response.xml",
ImmutableMap.of(
"HOSTNAME", "ns2.example.external",
"CRDATE", createTime.plusMinutes(3).toString()));
}
private void createContacts(DateTime createTime) throws Exception {
assertThatCommand("contact_create_sh8013.xml")
.atTime(createTime)
.hasResponse(
"contact_create_response_sh8013.xml", ImmutableMap.of("CRDATE", createTime.toString()));
assertThatCommand("contact_create_jd1234.xml")
.atTime(createTime.plusMinutes(1))
.hasResponse(
"contact_create_response_jd1234.xml",
ImmutableMap.of("CRDATE", createTime.plusMinutes(1).toString()));
}
/** Creates the domain fakesite.example with two nameservers on it. */
void createFakesite() throws Exception {
createContactsAndHosts();
assertThatCommand("domain_create_fakesite.xml")
.atTime("2000-06-01T00:04:00Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "fakesite.example",
"CRDATE", "2000-06-01T00:04:00.0Z",
"EXDATE", "2002-06-01T00:04:00.0Z"));
assertThatCommand("domain_info_fakesite.xml")
.atTime("2000-06-06T00:00:00Z")
.hasResponse("domain_info_response_fakesite_ok.xml");
}
/** Creates ns3.fakesite.example as a host, then adds it to fakesite. */
void createSubordinateHost() throws Exception {
// Add the fakesite nameserver (requires that domain is already created).
assertThatCommand("host_create_fakesite.xml")
.atTime("2000-06-06T00:01:00Z")
.hasResponse("host_create_response_fakesite.xml");
// Add new nameserver to domain.
assertThatCommand("domain_update_add_nameserver_fakesite.xml")
.atTime("2000-06-08T00:00:00Z")
.hasResponse("generic_success_response.xml");
// Verify new nameserver was added.
assertThatCommand("domain_info_fakesite.xml")
.atTime("2000-06-08T00:01:00Z")
.hasResponse("domain_info_response_fakesite_3_nameservers.xml");
// Verify that nameserver's data was set correctly.
assertThatCommand("host_info_fakesite.xml")
.atTime("2000-06-08T00:02:00Z")
.hasResponse("host_info_response_fakesite_linked.xml");
}
@Test
public void testDomainDeleteRestore() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
// Create domain example.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-01T00:02:00Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00.0Z",
"EXDATE", "2002-06-01T00:02:00.0Z"));
// Delete domain example.tld after its add grace period has expired.
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-07-01T00:02:00Z")
.hasResponse("generic_success_action_pending_response.xml");
// Restore the domain.
assertThatCommand("domain_update_restore_request.xml")
.atTime("2000-07-01T00:03:00Z")
.hasResponse("generic_success_response.xml");
assertThatLogoutSucceeds();
}
@Test
public void testDomainDeletion_withinAddGracePeriod_deletesImmediately() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
// Create domain example.tld
DateTime createTime = DateTime.parse("2000-06-01T00:02:00Z");
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime(createTime)
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00.0Z",
"EXDATE", "2002-06-01T00:02:00.0Z"));
DomainResource domain =
loadByForeignKey(DomainResource.class, "example.tld", createTime.plusHours(1));
// Delete domain example.tld within the add grace period.
DateTime deleteTime = createTime.plusDays(1);
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime(deleteTime)
.hasResponse("generic_success_response.xml");
// Verify that it is immediately non-existent.
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime(deleteTime.plusSeconds(1))
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2303", "MSG", "The domain with given ID (example.tld) doesn't exist."));
// The expected one-time billing event, that should have an associated Cancellation.
OneTime oneTimeCreateBillingEvent = makeOneTimeCreateBillingEvent(domain, createTime);
// Verify that the OneTime billing event associated with the domain creation is canceled.
assertBillingEventsForResource(
domain,
// Check the existence of the expected create one-time billing event.
oneTimeCreateBillingEvent,
makeRecurringCreateBillingEvent(domain, createTime, deleteTime),
// Check for the existence of a cancellation for the given one-time billing event.
makeCancellationBillingEventFor(
domain, oneTimeCreateBillingEvent, createTime, deleteTime));
assertThatLogoutSucceeds();
}
@Test
public void testDomainDeletion_outsideAddGracePeriod_showsRedemptionPeriod() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
DateTime createTime = DateTime.parse("2000-06-01T00:02:00Z");
// Create domain example.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime(createTime)
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00.0Z",
"EXDATE", "2002-06-01T00:02:00.0Z"));
DateTime deleteTime = DateTime.parse("2000-07-07T00:02:00Z"); // 1 month and 6 days after
// Delete domain example.tld after its add grace period has expired.
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime(deleteTime)
.hasResponse("generic_success_action_pending_response.xml");
// Verify that domain shows redemptionPeriod soon after deletion.
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-07-08T00:00:00Z")
.hasResponse(
"domain_info_response_wildcard.xml", ImmutableMap.of("STATUS", "redemptionPeriod"));
// Verify that the domain shows pendingDelete next.
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-08-08T00:00:00Z")
.hasResponse(
"domain_info_response_wildcard.xml", ImmutableMap.of("STATUS", "pendingDelete"));
// Verify that the domain is non-existent (available for registration) later.
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-09-01T00:00:00Z")
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2303", "MSG", "The domain with given ID (example.tld) doesn't exist."));
DomainResource domain =
loadByForeignKey(
DomainResource.class, "example.tld", DateTime.parse("2000-08-01T00:02:00Z"));
// Verify that the autorenew was ended and that the one-time billing event is not canceled.
assertBillingEventsForResource(
domain,
makeOneTimeCreateBillingEvent(domain, createTime),
makeRecurringCreateBillingEvent(domain, createTime, deleteTime));
assertThatLogoutSucceeds();
}
@Test
public void testEapDomainDeletion_withinAddGracePeriod_eapFeeIsNotRefunded() throws Exception {
assertThatCommand("login_valid_fee_extension.xml").hasResponse("generic_success_response.xml");
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
// Set the EAP schedule.
persistResource(
Registry.get("tld")
.asBuilder()
.setEapFeeSchedule(
ImmutableSortedMap.of(
START_OF_TIME, Money.of(USD, 0),
DateTime.parse("2000-06-01T00:00:00Z"), Money.of(USD, 100),
DateTime.parse("2000-06-02T00:00:00Z"), Money.of(USD, 0)))
.build());
// Create domain example.tld, which should have an EAP fee of USD 100.
DateTime createTime = DateTime.parse("2000-06-01T00:02:00Z");
assertThatCommand("domain_create_eap_fee.xml")
.atTime(createTime)
.hasResponse("domain_create_response_eap_fee.xml");
DomainResource domain =
loadByForeignKey(
DomainResource.class, "example.tld", DateTime.parse("2000-06-01T00:03:00Z"));
// Delete domain example.tld within the add grade period.
DateTime deleteTime = createTime.plusDays(1);
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime(deleteTime)
.hasResponse("domain_delete_response_fee.xml");
// Verify that the OneTime billing event associated with the base fee of domain registration and
// is canceled and the autorenew is ended, but that the EAP fee is not canceled.
OneTime expectedCreateEapBillingEvent =
new BillingEvent.OneTime.Builder()
.setReason(Reason.FEE_EARLY_ACCESS)
.setTargetId("example.tld")
.setClientId("NewRegistrar")
.setPeriodYears(1)
.setCost(Money.parse("USD 100.00"))
.setEventTime(createTime)
.setBillingTime(createTime.plus(Registry.get("tld").getRenewGracePeriodLength()))
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE))
.build();
// The expected one-time billing event, that should have an associated Cancellation.
OneTime expectedOneTimeCreateBillingEvent = makeOneTimeCreateBillingEvent(domain, createTime);
assertBillingEventsForResource(
domain,
// Check for the expected create one-time billing event ...
expectedOneTimeCreateBillingEvent,
// ... and the expected one-time EAP fee billing event ...
expectedCreateEapBillingEvent,
makeRecurringCreateBillingEvent(domain, createTime, deleteTime),
// ... and verify that the create one-time billing event was canceled ...
makeCancellationBillingEventFor(
domain, expectedOneTimeCreateBillingEvent, createTime, deleteTime));
// ... but there was NOT a Cancellation for the EAP fee, as this would fail if additional
// billing events were present.
assertThatLogoutSucceeds();
}
/** Makes a one-time billing event corresponding to the given domain's creation. */
private static BillingEvent.OneTime makeOneTimeCreateBillingEvent(
DomainResource domain, DateTime createTime) {
return new BillingEvent.OneTime.Builder()
.setReason(Reason.CREATE)
.setTargetId(domain.getFullyQualifiedDomainName())
.setClientId(domain.getCurrentSponsorClientId())
.setCost(Money.parse("USD 26.00"))
.setPeriodYears(2)
.setEventTime(createTime)
.setBillingTime(createTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength()))
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE))
.build();
}
/** Makes a recurring billing event corresponding to the given domain's creation. */
private static BillingEvent.Recurring makeRecurringCreateBillingEvent(
DomainResource domain, DateTime createTime, DateTime endTime) {
return new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(domain.getFullyQualifiedDomainName())
.setClientId(domain.getCurrentSponsorClientId())
.setEventTime(createTime.plusYears(2))
.setRecurrenceEndTime(endTime)
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE))
.build();
}
/** Makes a cancellation billing event cancelling out the given domain create billing event. */
private static BillingEvent.Cancellation makeCancellationBillingEventFor(
DomainResource domain,
OneTime billingEventToCancel,
DateTime createTime,
DateTime deleteTime) {
return new BillingEvent.Cancellation.Builder()
.setTargetId(domain.getFullyQualifiedDomainName())
.setClientId(domain.getCurrentSponsorClientId())
.setEventTime(deleteTime)
.setOneTimeEventKey(findKeyToActualOneTimeBillingEvent(billingEventToCancel))
.setBillingTime(createTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength()))
.setReason(Reason.CREATE)
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_DELETE))
.build();
}
/**
* Finds the Key to the actual one-time create billing event associated with a domain's creation.
*
* <p>This is used in the situation where we have created an expected billing event associated
* with the domain's creation (which is passed as the parameter here), then need to locate the key
* to the actual billing event in Datastore that would be seen on a Cancellation billing event.
* This is necessary because the ID will be different even though all the rest of the fields are
* the same.
*/
private static Key<OneTime> findKeyToActualOneTimeBillingEvent(OneTime expectedBillingEvent) {
Optional<OneTime> actualCreateBillingEvent =
ofy()
.load()
.type(BillingEvent.OneTime.class)
.list()
.stream()
.filter(
b ->
Objects.equals(
stripBillingEventId(b), stripBillingEventId(expectedBillingEvent)))
.findFirst();
assertThat(actualCreateBillingEvent).isPresent();
return Key.create(actualCreateBillingEvent.get());
}
@Test
public void testDomainDeletionWithSubordinateHost_fails() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createFakesite();
createSubordinateHost();
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "fakesite.example"))
.atTime("2002-05-30T01:01:00Z")
.hasResponse(
"response_error.xml",
ImmutableMap.of("CODE", "2305", "MSG", "Domain to be deleted has subordinate hosts"));
assertThatLogoutSucceeds();
}
@Test
public void testDeletionOfDomain_afterRenameOfSubordinateHost_succeeds() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThat(getRecordedEppMetric())
.hasClientId("NewRegistrar")
.and()
.hasNoTld()
.and()
.hasCommandName("Login")
.and()
.hasStatus(SUCCESS);
createFakesite();
createSubordinateHost();
// Update the ns3 host to no longer be on fakesite.example domain.
assertThatCommand("host_update_fakesite.xml")
.atTime("2002-05-30T01:01:00Z")
.hasResponse("generic_success_response.xml");
// Add assert about EppMetric
assertThat(getRecordedEppMetric())
.hasClientId("NewRegistrar")
.and()
.hasCommandName("HostUpdate")
.and()
.hasStatus(SUCCESS);
// Delete the fakesite.example domain (which should succeed since it no longer has subords).
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "fakesite.example"))
.atTime("2002-05-30T01:02:00Z")
.hasResponse("generic_success_action_pending_response.xml");
assertThat(getRecordedEppMetric())
.hasClientId("NewRegistrar")
.and()
.hasTld("example")
.and()
.hasCommandName("DomainDelete")
.and()
.hasStatus(SUCCESS_WITH_ACTION_PENDING);
// Check info on the renamed host and verify that it's still around and wasn't deleted.
assertThatCommand("host_info_ns9000_example.xml")
.atTime("2002-06-30T01:03:00Z")
.hasResponse("host_info_response_ns9000_example.xml");
assertThat(getRecordedEppMetric())
.hasClientId("NewRegistrar")
.and()
.hasCommandName("HostInfo")
.and()
.hasStatus(SUCCESS);
assertThatLogoutSucceeds();
assertThat(getRecordedEppMetric())
.hasClientId("NewRegistrar")
.and()
.hasCommandName("Logout")
.and()
.hasStatus(SUCCESS_AND_CLOSE);
}
@Test
public void testDeletionOfDomain_afterUpdateThatCreatesSubordinateHost_fails() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createFakesite();
// Create domain example.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-02T00:00:00Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-02T00:00:00.0Z",
"EXDATE", "2002-06-02T00:00:00.0Z"));
// Create nameserver ns1.example.tld
assertThatCommand("host_create_example.xml")
.atTime("2000-06-02T00:01:00Z")
.hasResponse("host_create_response_example.xml");
// Update the ns1 host to be on the fakesite.example domain.
assertThatCommand("host_update_ns1_to_fakesite.xml")
.atTime("2002-05-30T01:01:00Z")
.hasResponse("generic_success_response.xml");
// Attempt to delete the fakesite.example domain (which should fail since it now has a
// subordinate host).
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "fakesite.example"))
.atTime("2002-05-30T01:02:00Z")
.hasResponse(
"response_error.xml",
ImmutableMap.of("CODE", "2305", "MSG", "Domain to be deleted has subordinate hosts"));
// Check info on the renamed host and verify that it's still around and wasn't deleted.
assertThatCommand("host_info_fakesite.xml")
.atTime("2002-06-30T01:03:00Z")
.hasResponse("host_info_response_fakesite_post_update.xml");
// Verify that fakesite.example domain is still around and wasn't deleted.
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-05-30T01:00:00Z")
.hasResponse("domain_info_response_fakesite_ok_post_host_update.xml");
assertThatLogoutSucceeds();
}
@Test
public void testDomainCreation_failsBeforeSunrise() throws Exception {
DateTime sunriseDate = DateTime.parse("2000-05-30T00:00:00Z");
createTld(
"example",
ImmutableSortedMap.of(
START_OF_TIME,
TldState.PREDELEGATION,
sunriseDate,
TldState.SUNRISE,
sunriseDate.plusMonths(2),
TldState.GENERAL_AVAILABILITY));
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContactsAndHosts();
assertThatCommand("domain_create_sunrise_encoded_mark.xml")
.atTime(sunriseDate.minusDays(1))
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2002", "MSG", "Command is not allowed in the current registry phase"));
assertThatCommand("domain_info_testvalidate.xml")
.atTime(sunriseDate.plusDays(1))
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2303",
"MSG", "The domain with given ID (test-validate.example) doesn't exist."));
assertThatLogoutSucceeds();
}
@Test
public void testDomainCheckFee_succeeds() throws Exception {
DateTime gaDate = DateTime.parse("2000-05-30T00:00:00Z");
createTld(
"example",
ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
gaDate, TldState.GENERAL_AVAILABILITY));
assertThatCommand("login_valid_fee_extension.xml").hasResponse("generic_success_response.xml");
assertThatCommand("domain_check_fee_premium.xml")
.atTime(gaDate.plusDays(1))
.hasResponse("domain_check_fee_premium_response.xml");
assertThat(getRecordedEppMetric())
.hasClientId("NewRegistrar")
.and()
.hasCommandName("DomainCheck")
.and()
.hasTld("example")
.and()
.hasStatus(SUCCESS);
assertThatLogoutSucceeds();
}
@Test
public void testDomainCreate_annualAutoRenewPollMessages_haveUniqueIds() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
// Create the domain.
createFakesite();
// The first autorenew poll message isn't seen until after the initial two years of registration
// are up.
assertThatCommand("poll.xml")
.atTime("2001-01-01T00:01:00Z")
.hasResponse("poll_response_empty.xml");
assertThatCommand("poll.xml")
.atTime("2002-07-01T00:01:00Z")
.hasResponse(
"poll_response_autorenew.xml",
ImmutableMap.of(
"ID", "1-C-EXAMPLE-13-16-2002",
"QDATE", "2002-06-01T00:04:00Z",
"DOMAIN", "fakesite.example",
"EXDATE", "2003-06-01T00:04:00Z"));
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-13-16-2002"))
.atTime("2002-07-01T00:02:00Z")
.hasResponse("poll_ack_response_empty.xml");
// The second autorenew poll message isn't seen until after another year, and it should have a
// different ID.
assertThatCommand("poll.xml")
.atTime("2002-07-01T00:05:00Z")
.hasResponse("poll_response_empty.xml");
assertThatCommand("poll.xml")
.atTime("2003-07-01T00:05:00Z")
.hasResponse(
"poll_response_autorenew.xml",
ImmutableMap.of(
"ID", "1-C-EXAMPLE-13-16-2003", // Note -- Year is different from previous ID.
"QDATE", "2003-06-01T00:04:00Z",
"DOMAIN", "fakesite.example",
"EXDATE", "2004-06-01T00:04:00Z"));
// Ack the second poll message and verify that none remain.
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-13-16-2003"))
.atTime("2003-07-01T00:05:05Z")
.hasResponse("poll_ack_response_empty.xml");
assertThatCommand("poll.xml")
.atTime("2003-07-01T00:05:10Z")
.hasResponse("poll_response_empty.xml");
assertThatLogoutSucceeds();
}
@Test
public void testDomainTransferPollMessage_serverApproved() throws Exception {
// As the losing registrar, create the domain.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createFakesite();
assertThatLogoutSucceeds();
// As the winning registrar, request a transfer. Capture the server trid; we'll need it later.
assertThatLoginSucceeds("TheRegistrar", "password2");
String response =
assertThatCommand("domain_transfer_request_1_year.xml")
.atTime("2001-01-01T00:00:00Z")
.hasResponse("domain_transfer_response_1_year.xml");
Matcher matcher = Pattern.compile("<svTRID>(.*)</svTRID>").matcher(response);
matcher.find();
String transferRequestTrid = matcher.group(1);
assertThatLogoutSucceeds();
// As the losing registrar, read the request poll message, and then ack it.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatCommand("poll.xml")
.atTime("2001-01-01T00:01:00Z")
.hasResponse("poll_response_domain_transfer_request.xml");
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-23-2001"))
.atTime("2001-01-01T00:01:00Z")
.hasResponse("poll_ack_response_empty.xml");
// Five days in the future, expect a server approval poll message to the loser, and ack it.
assertThatCommand("poll.xml")
.atTime("2001-01-06T00:01:00Z")
.hasResponse("poll_response_domain_transfer_server_approve_loser.xml");
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-22-2001"))
.atTime("2001-01-06T00:01:00Z")
.hasResponse("poll_ack_response_empty.xml");
assertThatLogoutSucceeds();
// Also expect a server approval poll message to the winner, with the transfer request trid.
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("poll.xml")
.atTime("2001-01-06T00:02:00Z")
.hasResponse(
"poll_response_domain_transfer_server_approve_winner.xml",
ImmutableMap.of("SERVER_TRID", transferRequestTrid));
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-21-2001"))
.atTime("2001-01-06T00:02:00Z")
.hasResponse("poll_ack_response_empty.xml");
assertThatLogoutSucceeds();
}
@Test
public void testTransfer_autoRenewGraceActive_onlyAtAutomaticTransferTime_getsSubsumed()
throws Exception {
// Register the domain as the first registrar.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createFakesite();
assertThatLogoutSucceeds();
// Request a transfer of the domain to the second registrar.
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("domain_transfer_request.xml")
.atTime("2002-05-30T00:00:00Z")
.hasResponse("domain_transfer_response.xml");
assertThatLogoutSucceeds();
// Log back in as the first registrar and verify things.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-05-30T01:00:00Z")
.hasResponse("domain_info_response_fakesite_pending_transfer.xml");
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-06-02T00:00:00Z")
.hasResponse("domain_info_response_fakesite_pending_transfer_autorenew.xml");
assertThatLogoutSucceeds();
// Log back in as the second registrar and verify transfer details.
assertThatLoginSucceeds("TheRegistrar", "password2");
// Verify that domain is in the transfer period now with expiration date still one year out,
// since the transfer should subsume the autorenew that happened during the transfer window.
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-06-06T00:00:00Z")
.hasResponse("domain_info_response_fakesite_transfer_period.xml");
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-06-12T00:00:00Z")
.hasResponse("domain_info_response_fakesite_transfer_complete.xml");
assertThatLogoutSucceeds();
}
@Test
public void testNameserversTransferWithDomain_successfully() throws Exception {
// Log in as the first registrar and set up domains with hosts.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createFakesite();
createSubordinateHost();
assertThatLogoutSucceeds();
// Request a transfer of the domain to the second registrar.
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("domain_transfer_request.xml")
.atTime("2002-05-30T00:00:00Z")
.hasResponse("domain_transfer_response.xml");
assertThatLogoutSucceeds();
// Log back in as the first registrar and verify domain is pending transfer.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-05-30T01:00:00Z")
.hasResponse("domain_info_response_fakesite_3_nameservers_pending_transfer.xml");
assertThatLogoutSucceeds();
// Log back in as second registrar and verify transfer was successful.
assertThatLoginSucceeds("TheRegistrar", "password2");
// Expect transfer complete with all three nameservers on it.
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-06-09T00:00:00Z")
.hasResponse("domain_info_response_fakesite_3_nameservers_transfer_successful.xml");
// Verify that host's client ID was set to the new registrar and has the transfer date set.
assertThatCommand("host_info_fakesite.xml")
.atTime("2002-06-09T00:01:00Z")
.hasResponse(
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("TRDATE", "2002-06-04T00:00:00Z"));
assertThatLogoutSucceeds();
}
@Test
public void testRenewalFails_whenTotalTermExceeds10Years() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
// Creates domain with 2 year expiration.
createFakesite();
// Attempt to renew for 9 years, adding up to a total greater than the allowed max of 10 years.
assertThatCommand(
"domain_renew.xml",
ImmutableMap.of("DOMAIN", "fakesite.example", "EXPDATE", "2002-06-01", "YEARS", "9"))
.atTime("2000-06-07T00:00:00Z")
.hasResponse("domain_renew_response_exceeds_max_years.xml");
assertThatLogoutSucceeds();
}
@Test
public void testDomainDeletionCancelsPendingTransfer() throws Exception {
// Register the domain as the first registrar.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createFakesite();
assertThatLogoutSucceeds();
// Request a transfer of the domain to the second registrar.
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("domain_transfer_request.xml")
.atTime("2002-05-30T00:00:00Z")
.hasResponse("domain_transfer_response.xml");
assertThatLogoutSucceeds();
// Log back in as the first registrar and delete then restore the domain while the transfer
// is still pending.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-05-30T01:00:00Z")
.hasResponse("domain_info_response_fakesite_pending_transfer.xml");
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "fakesite.example"))
.atTime("2002-05-30T01:01:00Z")
.hasResponse("generic_success_action_pending_response.xml");
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-05-30T01:02:00Z")
.hasResponse("domain_info_response_fakesite_pending_delete.xml");
assertThatCommand("domain_update_restore_fakesite.xml")
.atTime("2002-05-30T01:03:00Z")
.hasResponse("generic_success_response.xml");
// 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.)
assertThatCommand("domain_info_fakesite.xml")
.atTime("2002-05-30T01:04:00Z")
.hasResponse("domain_info_response_fakesite_restored_ok.xml");
assertThatLogoutSucceeds();
}
@Test
public void testDomainTransfer_subordinateHost_showsChangeInTransferQuery() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createFakesite();
createSubordinateHost();
assertThatCommand("domain_transfer_query_fakesite.xml")
.atTime("2000-09-02T00:00:00Z")
.hasResponse(
"response_error.xml",
ImmutableMap.of("CODE", "2002", "MSG", "Object has no transfer history"));
assertThatLogoutSucceeds();
// Request a transfer of the domain to the second registrar.
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("domain_transfer_request_1_year.xml")
.atTime("2001-01-01T00:00:00Z")
.hasResponse("domain_transfer_response_1_year.xml");
assertThatLogoutSucceeds();
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
// Verify that reID is set correctly.
assertThatCommand("domain_transfer_query_fakesite.xml")
.atTime("2001-01-02T00:00:00Z")
.hasResponse("domain_transfer_query_response_fakesite.xml");
// Verify that status went from 'pending' to 'serverApproved'.
assertThatCommand("domain_transfer_query_fakesite.xml")
.atTime("2001-01-08T00:00:00Z")
.hasResponse("domain_transfer_query_response_completed_fakesite.xml");
assertThatLogoutSucceeds();
}
/**
* 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 {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
// Create fakesite.example with subordinate host ns3.fakesite.example
createFakesite();
createSubordinateHost();
assertThatCommand("domain_transfer_query_fakesite.xml")
.atTime("2000-09-02T00:00:00Z")
.hasResponse(
"response_error.xml",
ImmutableMap.of("CODE", "2002", "MSG", "Object has no transfer history"));
assertThatLogoutSucceeds();
// Request a transfer of the domain to the second registrar.
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("domain_transfer_request_1_year.xml")
.atTime("2001-01-01T00:00:00Z")
.hasResponse("domain_transfer_response_1_year.xml");
// Verify that the lastTransferTime now reflects the superordinate domain's transfer.
assertThatCommand("host_info.xml", ImmutableMap.of("HOSTNAME", "ns3.fakesite.example"))
.atTime("2001-01-07T00:00:00Z")
.hasResponse(
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("TRDATE", "2001-01-06T00:00:00.000Z"));
assertThatCommand("domain_create_secondsite.xml")
.atTime("2001-01-08T00:00:00Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "secondsite.example",
"CRDATE", "2001-01-08T00:00:00.0Z",
"EXDATE", "2003-01-08T00:00:00.0Z"));
// Update the host to be subordinate to a different domain by renaming it to
// ns3.secondsite.example
assertThatCommand(
"host_update_rename_only.xml",
ImmutableMap.of("oldName", "ns3.fakesite.example", "newName", "ns3.secondsite.example"))
.atTime("2002-05-30T01:01:00Z")
.hasResponse("generic_success_response.xml");
// The last transfer time on the host should still be what it was from the transfer.
assertThatCommand("host_info.xml", ImmutableMap.of("HOSTNAME", "ns3.secondsite.example"))
.atTime("2003-01-07T00:00:00Z")
.hasResponse(
"host_info_response_fakesite_post_transfer_and_update.xml",
ImmutableMap.of(
"HOSTNAME", "ns3.secondsite.example",
"TRDATE", "2001-01-06T00:00:00.000Z"));
assertThatLogoutSucceeds();
}
/**
* 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 {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
// Create fakesite.example with subordinate host ns3.fakesite.example
createFakesite();
createSubordinateHost();
assertThatCommand("domain_transfer_query_fakesite.xml")
.atTime("2000-09-02T00:00:00Z")
.hasResponse(
"response_error.xml",
ImmutableMap.of("CODE", "2002", "MSG", "Object has no transfer history"));
assertThatLogoutSucceeds();
// Request a transfer of the domain to the second registrar.
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("domain_transfer_request_1_year.xml")
.atTime("2001-01-01T00:00:00Z")
.hasResponse("domain_transfer_response_1_year.xml");
// Verify that the lastTransferTime now reflects the superordinate domain's transfer.
assertThatCommand("host_info_fakesite.xml")
.atTime("2001-01-07T00:00:00Z")
.hasResponse(
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("TRDATE", "2001-01-06T00:00:00.000Z"));
// Update the host to be external by renaming it to ns3.notarealsite.external
assertThatCommand(
"host_update_rename_and_remove_addresses.xml",
ImmutableMap.of(
"oldName", "ns3.fakesite.example",
"newName", "ns3.notarealsite.external"))
.atTime("2002-05-30T01:01:00Z")
.hasResponse("generic_success_response.xml");
// The last transfer time on the host should still be what it was from the transfer.
assertThatCommand("host_info.xml", ImmutableMap.of("HOSTNAME", "ns3.notarealsite.external"))
.atTime("2001-01-07T00:00:00Z")
.hasResponse(
"host_info_response_fakesite_post_transfer_and_update_no_addresses.xml",
ImmutableMap.of(
"HOSTNAME", "ns3.notarealsite.external",
"TRDATE", "2001-01-06T00:00:00.000Z"));
assertThatLogoutSucceeds();
}
@Test
public void testSuccess_multipartTldsWithSharedSuffixes() throws Exception {
createTlds("bar.foo.tld", "foo.tld");
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContacts(DateTime.parse("2000-06-01T00:00:00.000Z"));
// Create domain example.bar.foo.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml",
ImmutableMap.of("DOMAIN", "example.bar.foo.tld"))
.atTime("2000-06-01T00:02:00.001Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.bar.foo.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
// Create domain example.foo.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.foo.tld"))
.atTime("2000-06-01T00:02:00.002Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.foo.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
// Create domain example.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-01T00:02:00.003Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
assertThatLogoutSucceeds();
}
@Test
public void testSuccess_multipartTldsWithSharedPrefixes() throws Exception {
createTld("tld.foo");
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContacts(DateTime.parse("2000-06-01T00:00:00.000Z"));
// Create domain example.tld.foo
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld.foo"))
.atTime("2000-06-01T00:02:00.001Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld.foo",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
// Create domain example.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-01T00:02:00.002Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
assertThatLogoutSucceeds();
}
/**
* Test a full launch of start-date sunrise.
*
* We show that we can't create during pre-delegation, can only create with an encoded mark during
* start-date sunrise - which we can then delete "as normal" (no need for a signed mark or
* anything for delete), and then use "regular" create during general-availability.
*/
@Test
public void testDomainCreation_startDateSunriseFull() throws Exception {
// The signed mark is valid between 2013 and 2017
DateTime sunriseDate = DateTime.parse("2014-09-08T09:09:09Z");
DateTime gaDate = sunriseDate.plusDays(30);
createTld(
"example",
ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
sunriseDate, TldState.START_DATE_SUNRISE,
gaDate, TldState.GENERAL_AVAILABILITY));
assertThatLogin("NewRegistrar", "foo-BAR2")
.atTime(sunriseDate.minusDays(3))
.hasResponse("generic_success_response.xml");
createContactsAndHosts();
// During pre-delegation, any create should fail both with and without mark
assertThatCommand("domain_create_start_date_sunrise_encoded_mark.xml")
.atTime(sunriseDate.minusDays(2))
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2306",
"MSG",
"Declared launch extension phase does not match the current registry phase"));
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml",
ImmutableMap.of("DOMAIN", "general.example"))
.atTime(sunriseDate.minusDays(1))
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2002",
"MSG", "The current registry phase does not allow for general registrations"));
// During start-date sunrise, create with mark will succeed but without will fail.
// We also test we can delete without a mark.
assertThatCommand("domain_create_start_date_sunrise_encoded_mark.xml")
.atTime(sunriseDate.plusDays(1))
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "test-validate.example",
"CRDATE", "2014-09-09T09:09:09Z",
"EXDATE", "2015-09-09T09:09:09Z"));
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "test-validate.example"))
.atTime(sunriseDate.plusDays(1).plusMinutes(1))
.hasResponse("generic_success_response.xml");
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml",
ImmutableMap.of("DOMAIN", "general.example"))
.atTime(sunriseDate.plusDays(2))
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2002",
"MSG", "The current registry phase requires a signed mark for registrations"));
// During general availability, sunrise creates will fail but regular creates succeed
assertThatCommand("domain_create_start_date_sunrise_encoded_mark.xml")
.atTime(gaDate.plusDays(1))
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2306",
"MSG",
"Declared launch extension phase does not match the current registry phase"));
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml",
ImmutableMap.of("DOMAIN", "general.example"))
.atTime(gaDate.plusDays(2))
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "general.example",
"CRDATE", "2014-10-10T09:09:09Z",
"EXDATE", "2016-10-10T09:09:09Z"));
assertThatLogoutSucceeds();
}
/**
* Test that missing type= argument on launch create works in start-date sunrise.
*
* <p>TODO(b/76095570):have the same exact test on end-date sunrise - using the same .xml file -
* that checks that an application was created.
*/
@Test
public void testDomainCreation_startDateSunrise_noType() throws Exception {
// The signed mark is valid between 2013 and 2017
DateTime sunriseDate = DateTime.parse("2014-09-08T09:09:09Z");
DateTime gaDate = sunriseDate.plusDays(30);
createTld(
"example",
ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
sunriseDate, TldState.START_DATE_SUNRISE,
gaDate, TldState.GENERAL_AVAILABILITY));
assertThatLogin("NewRegistrar", "foo-BAR2")
.atTime(sunriseDate.minusDays(3))
.hasResponse("generic_success_response.xml");
createContactsAndHosts();
// During start-date sunrise, create with mark will succeed but without will fail.
// We also test we can delete without a mark.
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "test-validate.example"))
.atTime(sunriseDate.plusDays(1))
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2303",
"MSG", "The domain with given ID (test-validate.example) doesn't exist."));
assertThatCommand("domain_create_start_date_sunrise_encoded_mark_no_type.xml")
.atTime(sunriseDate.plusDays(1).plusMinutes(1))
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "test-validate.example",
"CRDATE", "2014-09-09T09:10:09Z",
"EXDATE", "2015-09-09T09:10:09Z"));
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "test-validate.example"))
.atTime(sunriseDate.plusDays(1).plusMinutes(2))
.hasResponse(
"domain_info_response_ok_wildcard.xml",
ImmutableMap.of(
"DOMAIN", "test-validate.example",
"CRDATE", "2014-09-09T09:10:09Z",
"EXDATE", "2015-09-09T09:10:09Z"));
assertThatLogoutSucceeds();
}
}