From 994af085d8cadf93bcd9f58616eca78d48e0b515 Mon Sep 17 00:00:00 2001 From: sarahcaseybot Date: Tue, 13 Apr 2021 11:45:45 -0400 Subject: [PATCH] Add a CompareReservedListCommand (#1054) * Add a CompareReservedListCommand * compare maps * output format fixes * Clean up loops * Inline Sets.difference() * Remove ImmutableCopy() --- .../tools/CompareReservedListsCommand.java | 82 +++++++++++++ .../google/registry/tools/RegistryTool.java | 1 + .../tools/CompareReservedListCommandTest.java | 115 ++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 core/src/main/java/google/registry/tools/CompareReservedListsCommand.java create mode 100644 core/src/test/java/google/registry/tools/CompareReservedListCommandTest.java diff --git a/core/src/main/java/google/registry/tools/CompareReservedListsCommand.java b/core/src/main/java/google/registry/tools/CompareReservedListsCommand.java new file mode 100644 index 000000000..2449b17e6 --- /dev/null +++ b/core/src/main/java/google/registry/tools/CompareReservedListsCommand.java @@ -0,0 +1,82 @@ +// Copyright 2021 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 com.google.common.collect.ImmutableSet.toImmutableSet; +import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; +import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; + +import com.beust.jcommander.Parameters; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import google.registry.model.registry.label.ReservedList; +import google.registry.model.registry.label.ReservedList.ReservedListEntry; +import google.registry.model.registry.label.ReservedListDatastoreDao; +import google.registry.model.registry.label.ReservedListSqlDao; + +/** Command to compare all ReservedLists in Datastore to all ReservedLists in Cloud SQL. */ +@Parameters( + separators = " =", + commandDescription = "Compare all the ReservedLists in Datastore to those in Cloud SQL.") +final class CompareReservedListsCommand implements CommandWithRemoteApi { + + @Override + public void run() { + ImmutableSet datastoreLists = + ofy().load().type(ReservedList.class).ancestor(getCrossTldKey()).list().stream() + .map(ReservedList::getName) + .collect(toImmutableSet()); + + ImmutableSet cloudSqlLists = + jpaTm() + .transact( + () -> + jpaTm().loadAllOf(ReservedList.class).stream() + .map(ReservedList::getName) + .collect(toImmutableSet())); + + int listsWithDiffs = 0; + + for (String listName : Sets.difference(datastoreLists, cloudSqlLists)) { + listsWithDiffs++; + System.out.printf( + "ReservedList '%s' is present in Datastore, but not in Cloud SQL.%n", listName); + } + for (String listName : Sets.difference(cloudSqlLists, datastoreLists)) { + listsWithDiffs++; + System.out.printf( + "ReservedList '%s' is present in Cloud SQL, but not in Datastore.%n", listName); + } + + for (String listName : Sets.intersection(datastoreLists, cloudSqlLists)) { + ImmutableMap namesInSql = + ReservedListSqlDao.getLatestRevision(listName).get().getReservedListEntries(); + + ImmutableMap namesInDatastore = + ReservedListDatastoreDao.getLatestRevision(listName).get().getReservedListEntries(); + + // This will only print out the name of the unequal list. GetReservedListCommand should be + // used to determine what the actual differences are. + if (!namesInDatastore.equals(namesInSql)) { + listsWithDiffs++; + System.out.printf("ReservedList '%s' has different entries in each database.%n", listName); + } + } + + System.out.printf("Found %d unequal list(s).%n", listsWithDiffs); + } +} diff --git a/core/src/main/java/google/registry/tools/RegistryTool.java b/core/src/main/java/google/registry/tools/RegistryTool.java index 2835ed0a3..d1b965d7c 100644 --- a/core/src/main/java/google/registry/tools/RegistryTool.java +++ b/core/src/main/java/google/registry/tools/RegistryTool.java @@ -37,6 +37,7 @@ public final class RegistryTool { .put("canonicalize_labels", CanonicalizeLabelsCommand.class) .put("check_domain", CheckDomainCommand.class) .put("check_domain_claims", CheckDomainClaimsCommand.class) + .put("compare_reserved_lists", CompareReservedListsCommand.class) .put("convert_idn", ConvertIdnCommand.class) .put("count_domains", CountDomainsCommand.class) .put("create_anchor_tenant", CreateAnchorTenantCommand.class) diff --git a/core/src/test/java/google/registry/tools/CompareReservedListCommandTest.java b/core/src/test/java/google/registry/tools/CompareReservedListCommandTest.java new file mode 100644 index 000000000..93b723df3 --- /dev/null +++ b/core/src/test/java/google/registry/tools/CompareReservedListCommandTest.java @@ -0,0 +1,115 @@ +// Copyright 2021 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 com.google.common.truth.Truth.assertThat; +import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; +import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; +import static google.registry.testing.DatabaseHelper.persistReservedList; + +import com.google.common.collect.ImmutableMap; +import google.registry.model.registry.label.ReservationType; +import google.registry.model.registry.label.ReservedList; +import google.registry.model.registry.label.ReservedList.ReservedListEntry; +import google.registry.model.registry.label.ReservedListDatastoreDao; +import google.registry.model.registry.label.ReservedListDualDatabaseDao; +import google.registry.model.registry.label.ReservedListSqlDao; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link CompareReservedListsCommand}. */ +public class CompareReservedListCommandTest extends CommandTestCase { + + @BeforeEach + void setUp() { + persistReservedList( + "testlist", "food, RESERVED_FOR_SPECIFIC_USE", "music, FULLY_BLOCKED # fully blocked"); + persistReservedList("testlist2", "candy, ALLOWED_IN_SUNRISE"); + } + + @Test + void test_success() throws Exception { + runCommand(); + assertThat(getStdoutAsString()).isEqualTo("Found 0 unequal list(s).\n"); + } + + @Test + void test_listMissingFromCloudSql() throws Exception { + jpaTm().transact(() -> jpaTm().delete(ReservedListSqlDao.getLatestRevision("testlist").get())); + runCommand(); + assertThat(getStdoutAsString()) + .isEqualTo( + "ReservedList 'testlist' is present in Datastore, but not in Cloud SQL.\n" + + "Found 1 unequal list(s).\n"); + } + + @Test + void test_listMissingFromDatastore() throws Exception { + ofyTm() + .transact( + () -> ofyTm().delete(ReservedListDatastoreDao.getLatestRevision("testlist").get())); + runCommand(); + assertThat(getStdoutAsString()) + .isEqualTo( + "ReservedList 'testlist' is present in Cloud SQL, but not in Datastore.\n" + + "Found 1 unequal list(s).\n"); + } + + @Test + void test_listsDiffer() throws Exception { + ImmutableMap newReservations = + ImmutableMap.of( + "food", + ReservedListEntry.create("food", ReservationType.RESERVED_FOR_SPECIFIC_USE, null)); + ReservedList secondList = + new ReservedList.Builder() + .setName("testlist") + .setLastUpdateTime(fakeClock.nowUtc()) + .setShouldPublish(false) + .setReservedListMap(newReservations) + .build(); + ReservedListDatastoreDao.save(secondList); + runCommand(); + assertThat(getStdoutAsString()) + .isEqualTo( + "ReservedList 'testlist' has different entries in each database.\n" + + "Found 1 unequal list(s).\n"); + } + + @Test + void test_listsDifferAndMissing() throws Exception { + ofyTm() + .transact( + () -> ofyTm().delete(ReservedListDualDatabaseDao.getLatestRevision("testlist2").get())); + ImmutableMap newReservations = + ImmutableMap.of( + "food", + ReservedListEntry.create("food", ReservationType.RESERVED_FOR_SPECIFIC_USE, null)); + ReservedList secondList = + new ReservedList.Builder() + .setName("testlist") + .setLastUpdateTime(fakeClock.nowUtc()) + .setShouldPublish(false) + .setReservedListMap(newReservations) + .build(); + ReservedListDatastoreDao.save(secondList); + runCommand(); + assertThat(getStdoutAsString()) + .isEqualTo( + "ReservedList 'testlist2' is present in Cloud SQL, but not in Datastore.\n" + + "ReservedList 'testlist' has different entries in each database.\n" + + "Found 2 unequal list(s).\n"); + } +}