diff --git a/java/google/registry/tools/server/VerifyOteAction.java b/java/google/registry/tools/server/VerifyOteAction.java index 4ab0834f1..70a5bc4a8 100644 --- a/java/google/registry/tools/server/VerifyOteAction.java +++ b/java/google/registry/tools/server/VerifyOteAction.java @@ -14,10 +14,10 @@ package google.registry.tools.server; +import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.not; import static com.google.common.collect.Maps.toMap; import static google.registry.flows.EppXmlTransformer.unmarshal; -import static google.registry.flows.picker.FlowPicker.getFlowClass; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.DomainNameUtils.ACE_PREFIX; @@ -25,6 +25,7 @@ import static google.registry.util.DomainNameUtils.ACE_PREFIX; import com.google.common.base.Ascii; import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; @@ -32,29 +33,6 @@ import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multiset; import google.registry.flows.EppException; -import google.registry.flows.Flow; -import google.registry.flows.contact.ContactCreateFlow; -import google.registry.flows.contact.ContactDeleteFlow; -import google.registry.flows.contact.ContactTransferApproveFlow; -import google.registry.flows.contact.ContactTransferCancelFlow; -import google.registry.flows.contact.ContactTransferRejectFlow; -import google.registry.flows.contact.ContactTransferRequestFlow; -import google.registry.flows.contact.ContactUpdateFlow; -import google.registry.flows.domain.DomainApplicationCreateFlow; -import google.registry.flows.domain.DomainApplicationDeleteFlow; -import google.registry.flows.domain.DomainApplicationUpdateFlow; -import google.registry.flows.domain.DomainCreateFlow; -import google.registry.flows.domain.DomainDeleteFlow; -import google.registry.flows.domain.DomainRenewFlow; -import google.registry.flows.domain.DomainRestoreRequestFlow; -import google.registry.flows.domain.DomainTransferApproveFlow; -import google.registry.flows.domain.DomainTransferCancelFlow; -import google.registry.flows.domain.DomainTransferRejectFlow; -import google.registry.flows.domain.DomainTransferRequestFlow; -import google.registry.flows.domain.DomainUpdateFlow; -import google.registry.flows.host.HostCreateFlow; -import google.registry.flows.host.HostDeleteFlow; -import google.registry.flows.host.HostUpdateFlow; import google.registry.model.domain.DomainCommand; import google.registry.model.domain.fee.FeeCreateCommandExtension; import google.registry.model.domain.launch.LaunchCreateExtension; @@ -64,6 +42,7 @@ import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput.ResourceCommandWrapper; import google.registry.model.host.HostCommand; import google.registry.model.reporting.HistoryEntry; +import google.registry.model.reporting.HistoryEntry.Type; import google.registry.request.Action; import google.registry.request.JsonActionRunner; import google.registry.request.JsonActionRunner.JsonAction; @@ -95,7 +74,9 @@ public class VerifyOteAction implements Runnable, JsonAction { public static final String PATH = "/_dr/admin/verifyOte"; @Inject JsonActionRunner jsonActionRunner; - @Inject VerifyOteAction() {} + + @Inject + VerifyOteAction() {} @Override public void run() { @@ -113,7 +94,8 @@ public class VerifyOteAction implements Runnable, JsonAction { @Override public Object apply(@Nonnull String registrar) { return checkRegistrar(registrar, summarize); - }}); + } + }); } /** Checks whether the provided registrar has passed OT&E and returns relevant information. */ @@ -121,138 +103,158 @@ public class VerifyOteAction implements Runnable, JsonAction { HistoryEntryStats historyEntryStats = new HistoryEntryStats().recordRegistrarHistory(registrarName); List failureMessages = historyEntryStats.findFailures(); - String passedFraction = String.format( - "%2d/%2d", StatType.NUM_REQUIREMENTS - failureMessages.size(), StatType.NUM_REQUIREMENTS); + String passedFraction = + String.format( + "%2d/%2d", + StatType.NUM_REQUIREMENTS - failureMessages.size(), StatType.NUM_REQUIREMENTS); String status = failureMessages.isEmpty() ? "PASS" : "FAIL"; return summarize ? String.format( "Num actions: %4d - Reqs passed: %s - Overall: %s", - historyEntryStats.statCounts.size(), - passedFraction, - status) + historyEntryStats.statCounts.size(), passedFraction, status) : String.format( "%s\n%s\nRequirements passed: %s\nOverall OT&E status: %s\n", - historyEntryStats, - Joiner.on('\n').join(failureMessages), - passedFraction, - status); + historyEntryStats, Joiner.on('\n').join(failureMessages), passedFraction, status); } - private static final Predicate HAS_CLAIMS_NOTICE = new Predicate() { - @Override - public boolean apply(@Nonnull EppInput eppInput) { - LaunchCreateExtension launchCreate = - eppInput.getSingleExtension(LaunchCreateExtension.class); - return launchCreate != null && launchCreate.getNotice() != null; - }}; - - private static final Predicate HAS_FEE = new Predicate() { - @Override - public boolean apply(@Nonnull EppInput eppInput) { - return eppInput.getSingleExtension(FeeCreateCommandExtension.class) != null; - }}; - - private static final Predicate HAS_SEC_DNS = new Predicate() { - @Override - public boolean apply(@Nonnull EppInput eppInput) { - return (eppInput.getSingleExtension(SecDnsCreateExtension.class) != null) - || (eppInput.getSingleExtension(SecDnsUpdateExtension.class) != null); - }}; - - private static final Predicate IS_SUNRISE = new Predicate() { - @Override - public boolean apply(@Nonnull EppInput eppInput) { - LaunchCreateExtension launchCreate = - eppInput.getSingleExtension(LaunchCreateExtension.class); - return launchCreate != null && !isNullOrEmpty(launchCreate.getSignedMarks()); - }}; - - private static final Predicate IS_IDN = new Predicate() { - @Override - public boolean apply(@Nonnull EppInput eppInput) { - return ((DomainCommand.Create) ((ResourceCommandWrapper) - eppInput.getCommandWrapper().getCommand()).getResourceCommand()) - .getFullyQualifiedDomainName().startsWith(ACE_PREFIX); - }}; - - private static final Predicate IS_SUBORDINATE = new Predicate() { - @Override - public boolean apply(@Nonnull EppInput eppInput) { - return !isNullOrEmpty(((HostCommand.Create) ((ResourceCommandWrapper) - eppInput.getCommandWrapper().getCommand()).getResourceCommand()) - .getInetAddresses()); - }}; - - private static Predicate isFlow(final Class flowClass) { - return new Predicate() { - @Override - public boolean apply(@Nonnull EppInput eppInput) { - try { - return flowClass.equals(getFlowClass(eppInput)); - } catch (EppException e) { - throw new RuntimeException(e); + private static final Predicate HAS_CLAIMS_NOTICE = + new Predicate() { + @Override + public boolean apply(@Nonnull EppInput eppInput) { + LaunchCreateExtension launchCreate = + eppInput.getSingleExtension(LaunchCreateExtension.class); + return launchCreate != null && launchCreate.getNotice() != null; } - }}; - } + }; + + private static final Predicate HAS_FEE = + new Predicate() { + @Override + public boolean apply(@Nonnull EppInput eppInput) { + return eppInput.getSingleExtension(FeeCreateCommandExtension.class) != null; + } + }; + + private static final Predicate HAS_SEC_DNS = + new Predicate() { + @Override + public boolean apply(@Nonnull EppInput eppInput) { + return (eppInput.getSingleExtension(SecDnsCreateExtension.class) != null) + || (eppInput.getSingleExtension(SecDnsUpdateExtension.class) != null); + } + }; + + private static final Predicate IS_SUNRISE = + new Predicate() { + @Override + public boolean apply(@Nonnull EppInput eppInput) { + LaunchCreateExtension launchCreate = + eppInput.getSingleExtension(LaunchCreateExtension.class); + return launchCreate != null && !isNullOrEmpty(launchCreate.getSignedMarks()); + } + }; + + private static final Predicate IS_IDN = + new Predicate() { + @Override + public boolean apply(@Nonnull EppInput eppInput) { + return ((DomainCommand.Create) + ((ResourceCommandWrapper) eppInput.getCommandWrapper().getCommand()) + .getResourceCommand()) + .getFullyQualifiedDomainName() + .startsWith(ACE_PREFIX); + } + }; + + private static final Predicate IS_SUBORDINATE = + new Predicate() { + @Override + public boolean apply(@Nonnull EppInput eppInput) { + return !isNullOrEmpty( + ((HostCommand.Create) + ((ResourceCommandWrapper) eppInput.getCommandWrapper().getCommand()) + .getResourceCommand()) + .getInetAddresses()); + } + }; /** Enum defining the distinct statistics (types of registrar actions) to record. */ public enum StatType { - CONTACT_CREATES(0, isFlow(ContactCreateFlow.class)), - CONTACT_DELETES(0, isFlow(ContactDeleteFlow.class)), - CONTACT_TRANSFER_APPROVES(0, isFlow(ContactTransferApproveFlow.class)), - CONTACT_TRANSFER_CANCELS(0, isFlow(ContactTransferCancelFlow.class)), - CONTACT_TRANSFER_REJECTS(0, isFlow(ContactTransferRejectFlow.class)), - CONTACT_TRANSFER_REQUESTS(0, isFlow(ContactTransferRequestFlow.class)), - CONTACT_UPDATES(0, isFlow(ContactUpdateFlow.class)), - DOMAIN_APPLICATION_CREATES(0, isFlow(DomainApplicationCreateFlow.class)), + CONTACT_CREATES(0, equalTo(Type.CONTACT_CREATE)), + CONTACT_DELETES(0, equalTo(Type.CONTACT_DELETE)), + CONTACT_TRANSFER_APPROVES(0, equalTo(Type.CONTACT_TRANSFER_APPROVE)), + CONTACT_TRANSFER_CANCELS(0, equalTo(Type.CONTACT_TRANSFER_CANCEL)), + CONTACT_TRANSFER_REJECTS(0, equalTo(Type.CONTACT_TRANSFER_REJECT)), + CONTACT_TRANSFER_REQUESTS(0, equalTo(Type.CONTACT_TRANSFER_REQUEST)), + CONTACT_UPDATES(0, equalTo(Type.CONTACT_UPDATE)), + DOMAIN_APPLICATION_CREATES(0, equalTo(Type.DOMAIN_APPLICATION_CREATE)), DOMAIN_APPLICATION_CREATES_LANDRUSH( - 1, isFlow(DomainApplicationCreateFlow.class), not(IS_SUNRISE)), - DOMAIN_APPLICATION_CREATES_SUNRISE(1, isFlow(DomainApplicationCreateFlow.class), IS_SUNRISE), - DOMAIN_APPLICATION_DELETES(2, isFlow(DomainApplicationDeleteFlow.class)), - DOMAIN_APPLICATION_UPDATES(2, isFlow(DomainApplicationUpdateFlow.class)), - DOMAIN_CREATES(0, isFlow(DomainCreateFlow.class)), - DOMAIN_CREATES_ASCII(1, isFlow(DomainCreateFlow.class), not(IS_IDN)), - DOMAIN_CREATES_IDN(1, isFlow(DomainCreateFlow.class), IS_IDN), - DOMAIN_CREATES_WITH_CLAIMS_NOTICE(1, isFlow(DomainCreateFlow.class), HAS_CLAIMS_NOTICE), - DOMAIN_CREATES_WITH_FEE(1, isFlow(DomainCreateFlow.class), HAS_FEE), - DOMAIN_CREATES_WITH_SEC_DNS(1, isFlow(DomainCreateFlow.class), HAS_SEC_DNS), - DOMAIN_CREATES_WITHOUT_SEC_DNS(0, isFlow(DomainCreateFlow.class), not(HAS_SEC_DNS)), - DOMAIN_DELETES(2, isFlow(DomainDeleteFlow.class)), - DOMAIN_RENEWS(0, isFlow(DomainRenewFlow.class)), - DOMAIN_RESTORES(1, isFlow(DomainRestoreRequestFlow.class)), - DOMAIN_TRANSFER_APPROVES(1, isFlow(DomainTransferApproveFlow.class)), - DOMAIN_TRANSFER_CANCELS(1, isFlow(DomainTransferCancelFlow.class)), - DOMAIN_TRANSFER_REJECTS(1, isFlow(DomainTransferRejectFlow.class)), - DOMAIN_TRANSFER_REQUESTS(1, isFlow(DomainTransferRequestFlow.class)), - DOMAIN_UPDATES(0, isFlow(DomainUpdateFlow.class)), - DOMAIN_UPDATES_WITH_SEC_DNS(1, isFlow(DomainUpdateFlow.class), HAS_SEC_DNS), - DOMAIN_UPDATES_WITHOUT_SEC_DNS(0, isFlow(DomainUpdateFlow.class), not(HAS_SEC_DNS)), - HOST_CREATES(0, isFlow(HostCreateFlow.class)), - HOST_CREATES_EXTERNAL(0, isFlow(HostCreateFlow.class), not(IS_SUBORDINATE)), - HOST_CREATES_SUBORDINATE(1, isFlow(HostCreateFlow.class), IS_SUBORDINATE), - HOST_DELETES(1, isFlow(HostDeleteFlow.class)), - HOST_UPDATES(1, isFlow(HostUpdateFlow.class)), - UNCLASSIFIED_FLOWS(0, Predicates.alwaysFalse()); + 1, equalTo(Type.DOMAIN_APPLICATION_CREATE), not(IS_SUNRISE)), + DOMAIN_APPLICATION_CREATES_SUNRISE(1, equalTo(Type.DOMAIN_APPLICATION_CREATE), IS_SUNRISE), + DOMAIN_APPLICATION_DELETES(2, equalTo(Type.DOMAIN_APPLICATION_DELETE)), + DOMAIN_APPLICATION_UPDATES(2, equalTo(Type.DOMAIN_APPLICATION_UPDATE)), + DOMAIN_CREATES(0, equalTo(Type.DOMAIN_CREATE)), + DOMAIN_CREATES_ASCII(1, equalTo(Type.DOMAIN_CREATE), not(IS_IDN)), + DOMAIN_CREATES_IDN(1, equalTo(Type.DOMAIN_CREATE), IS_IDN), + DOMAIN_CREATES_WITH_CLAIMS_NOTICE(1, equalTo(Type.DOMAIN_CREATE), HAS_CLAIMS_NOTICE), + DOMAIN_CREATES_WITH_FEE(1, equalTo(Type.DOMAIN_CREATE), HAS_FEE), + DOMAIN_CREATES_WITH_SEC_DNS(1, equalTo(Type.DOMAIN_CREATE), HAS_SEC_DNS), + DOMAIN_CREATES_WITHOUT_SEC_DNS(0, equalTo(Type.DOMAIN_CREATE), not(HAS_SEC_DNS)), + DOMAIN_DELETES(2, equalTo(Type.DOMAIN_DELETE)), + DOMAIN_RENEWS(0, equalTo(Type.DOMAIN_RENEW)), + DOMAIN_RESTORES(1, equalTo(Type.DOMAIN_RESTORE)), + DOMAIN_TRANSFER_APPROVES(1, equalTo(Type.DOMAIN_TRANSFER_APPROVE)), + DOMAIN_TRANSFER_CANCELS(1, equalTo(Type.DOMAIN_TRANSFER_CANCEL)), + DOMAIN_TRANSFER_REJECTS(1, equalTo(Type.DOMAIN_TRANSFER_REJECT)), + DOMAIN_TRANSFER_REQUESTS(1, equalTo(Type.DOMAIN_TRANSFER_REQUEST)), + DOMAIN_UPDATES(0, equalTo(Type.DOMAIN_UPDATE)), + DOMAIN_UPDATES_WITH_SEC_DNS(1, equalTo(Type.DOMAIN_UPDATE), HAS_SEC_DNS), + DOMAIN_UPDATES_WITHOUT_SEC_DNS(0, equalTo(Type.DOMAIN_UPDATE), not(HAS_SEC_DNS)), + HOST_CREATES(0, equalTo(Type.HOST_CREATE)), + HOST_CREATES_EXTERNAL(0, equalTo(Type.HOST_CREATE), not(IS_SUBORDINATE)), + HOST_CREATES_SUBORDINATE(1, equalTo(Type.HOST_CREATE), IS_SUBORDINATE), + HOST_DELETES(1, equalTo(Type.HOST_DELETE)), + HOST_UPDATES(1, equalTo(Type.HOST_UPDATE)), + UNCLASSIFIED_FLOWS(0, Predicates.alwaysFalse()); /** The number of StatTypes with a non-zero requirement. */ - private static final int NUM_REQUIREMENTS = FluentIterable.from(values()) - .filter(new Predicate() { - @Override - public boolean apply(@Nonnull StatType statType) { - return statType.requirement > 0; - }}) - .size(); + private static final int NUM_REQUIREMENTS = + FluentIterable.from(values()) + .filter( + new Predicate() { + @Override + public boolean apply(@Nonnull StatType statType) { + return statType.requirement > 0; + } + }) + .size(); /** Required number of times registrars must complete this action. */ final int requirement; - /** Filters to determine if this action was performed by an EppInput. */ - private Predicate[] filters; + /** Filter to check the HistoryEntry Type */ + @SuppressWarnings("ImmutableEnumChecker") // Predicates are immutable. + private final Predicate typeFilter; - @SafeVarargs - StatType(int requirement, Predicate... filters) { + /** Optional filter on the EppInput. */ + @SuppressWarnings("ImmutableEnumChecker") // Predicates are immutable. + private final Optional> eppInputFilter; + + StatType(int requirement, Predicate typeFilter) { + this(requirement, typeFilter, null); + } + + StatType( + int requirement, + Predicate typeFilter, + Predicate eppInputFilter) { this.requirement = requirement; - this.filters = filters; + this.typeFilter = typeFilter; + if (eppInputFilter == null) { + this.eppInputFilter = Optional.absent(); + } else { + this.eppInputFilter = Optional.of(eppInputFilter); + } } /** Returns a more human-readable translation of the enum constant. */ @@ -260,9 +262,15 @@ public class VerifyOteAction implements Runnable, JsonAction { return Ascii.toLowerCase(this.name().replace('_', ' ')); } - /** An {@link EppInput} might match multiple actions, so check if this action matches. */ - boolean matches(EppInput eppInput) { - return Predicates.and(filters).apply(eppInput); + /** + * Check if the {@link HistoryEntry} type matches as well as the {@link EppInput} if supplied. + */ + boolean matches(HistoryEntry.Type historyType, Optional eppInput) { + if (eppInputFilter.isPresent() && eppInput.isPresent()) { + return typeFilter.apply(historyType) && eppInputFilter.get().apply(eppInput.get()); + } else { + return typeFilter.apply(historyType); + } } } @@ -293,22 +301,18 @@ public class VerifyOteAction implements Runnable, JsonAction { } /** Interprets the data in the provided HistoryEntry and increments counters. */ - void record(HistoryEntry historyEntry) throws EppException { + void record(final HistoryEntry historyEntry) throws EppException { byte[] xmlBytes = historyEntry.getXmlBytes(); // xmlBytes can be null on contact create and update for safe-harbor compliance. - // - // TODO(b/26161587): inspect the history entry itself to handle this properly. - if (xmlBytes == null) { - return; - } - final EppInput eppInput = unmarshal(EppInput.class, xmlBytes); + final Optional eppInput = + (xmlBytes == null) ? Optional.absent() : Optional.of(unmarshal(EppInput.class, xmlBytes)); if (!statCounts.addAll( FluentIterable.from(EnumSet.allOf(StatType.class)) .filter( new Predicate() { @Override public boolean apply(@Nonnull StatType statType) { - return statType.matches(eppInput); + return statType.matches(historyEntry.getType(), eppInput); } }) .toList())) { @@ -317,17 +321,17 @@ public class VerifyOteAction implements Runnable, JsonAction { } /** - * Returns a list of failure messages describing any cases where the passed stats fail to - * meet the required thresholds, or the empty list if all requirements are met. + * Returns a list of failure messages describing any cases where the passed stats fail to meet + * the required thresholds, or the empty list if all requirements are met. */ List findFailures() { List messages = new ArrayList<>(); for (StatType statType : StatType.values()) { if (statCounts.count(statType) < statType.requirement) { - messages.add(String.format( - "Failure: %s %s found.", - (statType.requirement == 1 ? "No" : "Not enough"), - statType.description())); + messages.add( + String.format( + "Failure: %s %s found.", + (statType.requirement == 1 ? "No" : "Not enough"), statType.description())); } } return messages; diff --git a/javatests/google/registry/tools/VerifyOteCommandTest.java b/javatests/google/registry/tools/VerifyOteCommandTest.java new file mode 100644 index 000000000..8d289b4d4 --- /dev/null +++ b/javatests/google/registry/tools/VerifyOteCommandTest.java @@ -0,0 +1,80 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.tools; + +import static google.registry.testing.DatastoreHelper.persistResource; +import static org.mockito.Matchers.anyMapOf; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import google.registry.model.registrar.Registrar; +import google.registry.tools.ServerSideCommand.Connection; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +/** Unit tests for {@link VerifyOteCommand}. */ +public class VerifyOteCommandTest extends CommandTestCase { + + @Mock private Connection connection; + + @Before + public void init() throws Exception { + command.setConnection(connection); + ImmutableMap response = + ImmutableMap.of( + "blobio", "Num actions: 19 - Reqs passed: 19/19 - Overall: PASS"); + when(connection.sendJson(anyString(), anyMapOf(String.class, Object.class))) + .thenReturn(ImmutableMap.of("blobio", response)); + } + + @Test + public void testSuccess_pass() throws Exception { + Registrar registrar = + Registrar.loadByClientId("TheRegistrar") + .asBuilder() + .setClientId("blobio-1") + .setRegistrarName("blobio-1") + .build(); + persistResource(registrar); + runCommand("blobio"); + + verify(connection) + .sendJson( + eq("/_dr/admin/verifyOte"), + eq(ImmutableMap.of("summarize", "false", "registrars", ImmutableList.of("blobio")))); + assertInStdout("blobio OT&E status"); + assertInStdout("Overall: PASS"); + } + + @Test + public void testFailure_registrarDoesntExist() throws Exception { + thrown.expect(VerifyException.class, "Registrar blobio does not exist."); + runCommand("blobio"); + } + + @Test + public void testFailure_noRegistrarsNoCheckAll() throws Exception { + thrown.expect( + IllegalArgumentException.class, + "Must provide at least one registrar name, or supply --check-all with no names."); + runCommand(""); + } +} diff --git a/javatests/google/registry/tools/server/VerifyOteActionTest.java b/javatests/google/registry/tools/server/VerifyOteActionTest.java new file mode 100644 index 000000000..9552102a8 --- /dev/null +++ b/javatests/google/registry/tools/server/VerifyOteActionTest.java @@ -0,0 +1,272 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.tools.server; + +import static com.google.common.truth.Truth.assertThat; +import static google.registry.testing.DatastoreHelper.deleteResource; +import static google.registry.testing.DatastoreHelper.persistResource; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import google.registry.model.reporting.HistoryEntry; +import google.registry.model.reporting.HistoryEntry.Type; +import google.registry.testing.AppEngineRule; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link VerifyOteAction}. */ +@RunWith(JUnit4.class) +public class VerifyOteActionTest { + + @Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); + + private final VerifyOteAction action = new VerifyOteAction(); + + HistoryEntry hostDeleteHistoryEntry; + + @Before + public void init() throws Exception { + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_APPLICATION_CREATE) + .setXmlBytes(ToolsTestData.get("domain_create_complete.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_APPLICATION_CREATE) + .setXmlBytes(ToolsTestData.get("domain_create_sunrise.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_APPLICATION_DELETE) + .setXmlBytes(ToolsTestData.get("domain_delete.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-2") + .setType(Type.DOMAIN_APPLICATION_DELETE) + .setXmlBytes(ToolsTestData.get("domain_delete.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_APPLICATION_UPDATE) + .setXmlBytes(ToolsTestData.get("domain_update_complete.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-2") + .setType(Type.DOMAIN_APPLICATION_UPDATE) + .setXmlBytes(ToolsTestData.get("domain_update_complete.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_CREATE) + .setXmlBytes(ToolsTestData.get("domain_create_idn.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_CREATE) + .setXmlBytes(ToolsTestData.get("domain_create_claim_notice.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_CREATE) + .setXmlBytes(ToolsTestData.get("domain_create_anchor_tenant_fee_standard.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_CREATE) + .setXmlBytes(ToolsTestData.get("allocate_domain.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_DELETE) + .setXmlBytes(ToolsTestData.get("domain_delete.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-2") + .setType(Type.DOMAIN_DELETE) + .setXmlBytes(ToolsTestData.get("domain_delete.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_RESTORE) + .setXmlBytes(ToolsTestData.get("domain_restore.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_TRANSFER_APPROVE) + .setXmlBytes(ToolsTestData.get("domain_transfer_approve.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_TRANSFER_CANCEL) + .setXmlBytes(ToolsTestData.get("domain_transfer_cancel.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_TRANSFER_REJECT) + .setXmlBytes(ToolsTestData.get("domain_transfer_reject.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_TRANSFER_REQUEST) + .setXmlBytes(ToolsTestData.get("domain_transfer_request.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.DOMAIN_UPDATE) + .setXmlBytes(ToolsTestData.get("domain_update_with_secdns.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.HOST_CREATE) + .setXmlBytes(ToolsTestData.get("host_create_complete.xml").read()) + .build()); + hostDeleteHistoryEntry = + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.HOST_DELETE) + .setXmlBytes(ToolsTestData.get("host_delete.xml").read()) + .build()); + persistResource( + new HistoryEntry.Builder() + .setClientId("blobio-1") + .setType(Type.HOST_UPDATE) + .setXmlBytes(ToolsTestData.get("host_update.xml").read()) + .build()); + } + + @Test + public void testSuccess_passSummarize() throws Exception { + Map response = + action.handleJsonRequest( + ImmutableMap.of("summarize", "true", "registrars", ImmutableList.of("blobio"))); + + for (Entry registrar : response.entrySet()) { + assertThat(registrar.getKey()).matches("blobio"); + assertThat(registrar.getValue().toString()).containsMatch("Reqs passed: 19/19"); + assertThat(registrar.getValue().toString()).containsMatch("Overall: PASS"); + } + } + + @Test + public void testSuccess_passNotSummarized() throws Exception { + Map response = + action.handleJsonRequest( + ImmutableMap.of("summarize", "false", "registrars", ImmutableList.of("blobio"))); + + for (Entry registrar : response.entrySet()) { + assertThat(registrar.getKey()).matches("blobio"); + String expectedOteStatus = + "domain application creates landrush: 1\n" + + "domain application creates sunrise: 1\n" + + "domain application deletes: 2\n" + + "domain application updates: 2\n" + + ".*" + + "domain creates idn: 1\n" + + "domain creates with claims notice: 1\n" + + "domain creates with fee: 1\n" + + "domain creates with sec dns: 1\n" + + ".*" + + "domain deletes: 2\n" + + ".*" + + "domain restores: 1\n" + + "domain transfer approves: 1\n" + + "domain transfer cancels: 1\n" + + "domain transfer rejects: 1\n" + + "domain transfer requests: 1\n" + + ".*" + + "domain updates with sec dns: 1\n" + + ".*" + + "host creates subordinate: 1\n" + + "host deletes: 1\n" + + "host updates: 1\n" + + ".*" + + "Requirements passed: 19/19\n" + + "Overall OT&E status: PASS\n"; + Pattern expectedOteStatusPattern = Pattern.compile(expectedOteStatus, Pattern.DOTALL); + assertThat(registrar.getValue().toString()).containsMatch(expectedOteStatusPattern); + } + } + + @Test + public void testFailure_missingHostDelete() throws Exception { + deleteResource(hostDeleteHistoryEntry); + + Map response = + action.handleJsonRequest( + ImmutableMap.of("summarize", "false", "registrars", ImmutableList.of("blobio"))); + + for (Entry registrar : response.entrySet()) { + assertThat(registrar.getKey()).matches("blobio"); + String oteStatus = registrar.getValue().toString(); + + String expectedOteStatus = + "domain application creates landrush: 1\n" + + "domain application creates sunrise: 1\n" + + "domain application deletes: 2\n" + + "domain application updates: 2\n" + + ".*" + + "domain creates idn: 1\n" + + "domain creates with claims notice: 1\n" + + "domain creates with fee: 1\n" + + "domain creates with sec dns: 1\n" + + ".*" + + "domain deletes: 2\n" + + ".*" + + "domain restores: 1\n" + + "domain transfer approves: 1\n" + + "domain transfer cancels: 1\n" + + "domain transfer rejects: 1\n" + + "domain transfer requests: 1\n" + + ".*" + + "domain updates with sec dns: 1\n" + + ".*" + + "host creates subordinate: 1\n" + + "host deletes: 0\n" + + "host updates: 1\n" + + ".*" + + "Requirements passed: 18/19\n" + + "Overall OT&E status: FAIL\n"; + Pattern expectedOteStatusPattern = Pattern.compile(expectedOteStatus, Pattern.DOTALL); + assertThat(oteStatus).containsMatch(expectedOteStatusPattern); + } + } +} diff --git a/javatests/google/registry/tools/server/testdata/domain_create_claim_notice.xml b/javatests/google/registry/tools/server/testdata/domain_create_claim_notice.xml new file mode 100644 index 000000000..fb1e1f94a --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_create_claim_notice.xml @@ -0,0 +1,33 @@ + + + + + + example-one.tld + 2 + + ns1.example.net + ns2.example.net + + jd1234 + sh8013 + sh8013 + + 2fooBAR + + + + + + claims + + 370d0b7c9223372036854775807 + 2010-08-16T09:00:00.0Z + 2009-08-16T09:00:00.0Z + + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/domain_create_idn.xml b/javatests/google/registry/tools/server/testdata/domain_create_idn.xml new file mode 100644 index 000000000..bd4ed7eca --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_create_idn.xml @@ -0,0 +1,22 @@ + + + + + xn--abc-873b2e7eb1k8a4lpjvv.tld + 2 + + ns1.example.net + ns2.example.net + + jd1234 + sh8013 + sh8013 + + 2fooBAR + + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/domain_create_sunrise.xml b/javatests/google/registry/tools/server/testdata/domain_create_sunrise.xml new file mode 100644 index 000000000..ddfac90ca --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_create_sunrise.xml @@ -0,0 +1,137 @@ + + + + + + exampleone.tld + + ns1.example.net + ns2.example.net + + jd1234 + sh8013 + sh8013 + + 2fooBAR + + + + + + sunrise + + 1-2 + + Example Inc. + support@example.tld + http://www.example.tld + +1.7035555555 + + 2009-08-16T09:00:00.0Z + 2010-08-16T09:00:00.0Z + + + 1234-2 + Example One + + Example Inc. + + 123 Example Dr. + Suite 100 + Reston + VA + 20190 + US + + + US + 35 + 36 + example-one + exampleone + Dirigendas et eiusmodi featuring infringo in airfare et cartam servicia. + 234235 + 2009-08-16T09:00:00.0Z + 2015-08-16T09:00:00.0Z + + + + + + + + + + + + + miF4M2aTd1Y3tKOzJtiyl2VpzAnVPnV1Hq7Zax+yzrA= + + + + + MELpHTWEVfG1JcsG1/a//o54OnlJ5A864+X5JwfqgGBBeZSzGHNzwzTKFzIyyyfn + lGxVwNMoBV5aSvkF7oEKMNVzfcl/P0czNQZ/LJ83p3Ol27/iUNsqgCaGf9Zupw+M + XT4Q2lOrIw+qSx5g7q9T83siMLvkD5uEYlU5dPqgsObLTW8/doTQrA14RcxgY4kG + a4+t5B1cT+5VaghTOPb8uUSEDKjnOsGdy8p24wgyK9n8h0CTSS2ZQ6Zq/RmQeT7D + sbceUHheQ+mkQWIljpMQqsiBjw5XXh4jkEgfAzrb6gkYEF+X8ReuPZuOYC4QjIET + yx8ifN4KE3GIbMXeF4LDsA== + + + + + + o/cwvXhbVYl0RDWWvoyeZpETVZVVcMCovUVNg/swWinuMgEWgVQFrz0xA04pEhXC + FVv4evbUpekJ5buqU1gmQyOsCKQlhOHTdPjvkC5upDqa51Flk0TMaMkIQjs7aUKC + mA4RG4tTTGK/EjR1ix8/D0gHYVRldy1YPrMP+ou75bOVnIos+HifrAtrIv4qEqwL + L4FTZAUpaCa2BmgXfy2CSRQbxD5Or1gcSa3vurh5sPMCNxqaXmIXmQipS+DuEBqM + M8tldaN7RYojUEKrGVsNk5i9y2/7sjn1zyyUPf7vL4GgDYqhJYWV61DnXgx/Jd6C + WxvsnDF6scscQzUTEl+hyw== + + + AQAB + + + + + + MIIESTCCAzGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEL + MAkGA1UECBMCQ0ExFDASBgNVBAcTC0xvcyBBbmdlbGVzMRMwEQYDVQQKEwpJQ0FO + TiBUTUNIMRswGQYDVQQDExJJQ0FOTiBUTUNIIFRFU1QgQ0EwHhcNMTMwMjA4MDAw + MDAwWhcNMTgwMjA3MjM1OTU5WjBsMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex + FDASBgNVBAcTC0xvcyBBbmdlbGVzMRcwFQYDVQQKEw5WYWxpZGF0b3IgVE1DSDEh + MB8GA1UEAxMYVmFsaWRhdG9yIFRNQ0ggVEVTVCBDRVJUMIIBIjANBgkqhkiG9w0B + AQEFAAOCAQ8AMIIBCgKCAQEAo/cwvXhbVYl0RDWWvoyeZpETVZVVcMCovUVNg/sw + WinuMgEWgVQFrz0xA04pEhXCFVv4evbUpekJ5buqU1gmQyOsCKQlhOHTdPjvkC5u + pDqa51Flk0TMaMkIQjs7aUKCmA4RG4tTTGK/EjR1ix8/D0gHYVRldy1YPrMP+ou7 + 5bOVnIos+HifrAtrIv4qEqwLL4FTZAUpaCa2BmgXfy2CSRQbxD5Or1gcSa3vurh5 + sPMCNxqaXmIXmQipS+DuEBqMM8tldaN7RYojUEKrGVsNk5i9y2/7sjn1zyyUPf7v + L4GgDYqhJYWV61DnXgx/Jd6CWxvsnDF6scscQzUTEl+hywIDAQABo4H/MIH8MAwG + A1UdEwEB/wQCMAAwHQYDVR0OBBYEFPZEcIQcD/Bj2IFz/LERuo2ADJviMIGMBgNV + HSMEgYQwgYGAFO0/7kEh3FuEKS+Q/kYHaD/W6wihoWakZDBiMQswCQYDVQQGEwJV + UzELMAkGA1UECBMCQ0ExFDASBgNVBAcTC0xvcyBBbmdlbGVzMRMwEQYDVQQKEwpJ + Q0FOTiBUTUNIMRswGQYDVQQDExJJQ0FOTiBUTUNIIFRFU1QgQ0GCAQEwDgYDVR0P + AQH/BAQDAgeAMC4GA1UdHwQnMCUwI6AhoB+GHWh0dHA6Ly9jcmwuaWNhbm4ub3Jn + L3RtY2guY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQB2qSy7ui+43cebKUKwWPrzz9y/ + IkrMeJGKjo40n+9uekaw3DJ5EqiOf/qZ4pjBD++oR6BJCb6NQuQKwnoAz5lE4Ssu + y5+i93oT3HfyVc4gNMIoHm1PS19l7DBKrbwbzAea/0jKWVzrvmV7TBfjxD3AQo1R + bU5dBr6IjbdLFlnO5x0G0mrG7x5OUPuurihyiURpFDpwH8KAH1wMcCpXGXFRtGKk + wydgyVYAty7otkl/z3bZkCVT34gPvF70sR6+QxUy8u0LzF5A/beYaZpxSYG31amL + AdXitTWFipaIGea9lEGFM0L9+Bg7XzNn4nVLXokyEB3bgS4scG6QznX23FGk + + + + + + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/domain_restore.xml b/javatests/google/registry/tools/server/testdata/domain_restore.xml new file mode 100644 index 000000000..72082bd98 --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_restore.xml @@ -0,0 +1,17 @@ + + + + + example.tld + + + + + + + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/domain_transfer_approve.xml b/javatests/google/registry/tools/server/testdata/domain_transfer_approve.xml new file mode 100644 index 000000000..b3abd462c --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_transfer_approve.xml @@ -0,0 +1,11 @@ + + + + + example.tld + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/domain_transfer_cancel.xml b/javatests/google/registry/tools/server/testdata/domain_transfer_cancel.xml new file mode 100644 index 000000000..fd88e2da8 --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_transfer_cancel.xml @@ -0,0 +1,11 @@ + + + + + example.tld + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/domain_transfer_reject.xml b/javatests/google/registry/tools/server/testdata/domain_transfer_reject.xml new file mode 100644 index 000000000..b0766d153 --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_transfer_reject.xml @@ -0,0 +1,11 @@ + + + + + example.tld + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/domain_transfer_request.xml b/javatests/google/registry/tools/server/testdata/domain_transfer_request.xml new file mode 100644 index 000000000..a9f74670b --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_transfer_request.xml @@ -0,0 +1,15 @@ + + + + + example.tld + 1 + + 2fooBAR + + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/domain_update_with_secdns.xml b/javatests/google/registry/tools/server/testdata/domain_update_with_secdns.xml new file mode 100644 index 000000000..dd113cec5 --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_update_with_secdns.xml @@ -0,0 +1,28 @@ + + + + + example.tld + + + + + + + + + + + 12346 + 3 + 1 + 38EC35D5B3A34B44C39B + + + + + ABC-12345 + + diff --git a/javatests/google/registry/tools/server/testdata/host_update.xml b/javatests/google/registry/tools/server/testdata/host_update.xml new file mode 100644 index 000000000..c460fc169 --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/host_update.xml @@ -0,0 +1,14 @@ + + + + + example.tld + + example2.tld + + + + ABC-12345 + +