// Copyright 2016 The Domain Registry 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.rde; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.persistNewRegistrar; import static google.registry.testing.DatastoreHelper.persistResource; import static org.mockito.Matchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.appengine.tools.cloudstorage.GcsFilename; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.io.ByteSource; import com.googlecode.objectify.Key; import com.googlecode.objectify.Work; import google.registry.gcs.GcsUtils; import google.registry.model.EppResource; import google.registry.model.contact.ContactResource; import google.registry.model.index.EppResourceIndex; import google.registry.model.index.EppResourceIndexBucket; import google.registry.model.index.ForeignKeyIndex; import google.registry.model.registry.Registry.TldState; import google.registry.testing.AppEngineRule; import google.registry.testing.ExceptionRule; import google.registry.testing.FakeClock; import google.registry.testing.ShardableTestCase; import java.io.IOException; import java.io.InputStream; import org.joda.time.DateTime; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; /** Unit tests for {@link RdeImportUtils} */ @RunWith(MockitoJUnitRunner.class) public class RdeImportUtilsTest extends ShardableTestCase { private static final ByteSource DEPOSIT_XML = RdeTestData.get("deposit_full.xml"); private static final ByteSource DEPOSIT_BADTLD_XML = RdeTestData.get("deposit_full_badtld.xml"); private static final ByteSource DEPOSIT_GETLD_XML = RdeTestData.get("deposit_full_getld.xml"); private static final ByteSource DEPOSIT_BADREGISTRAR_XML = RdeTestData.get("deposit_full_badregistrar.xml"); private InputStream xmlInput; @Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); @Rule public final ExceptionRule thrown = new ExceptionRule(); @Mock private GcsUtils gcsUtils; private RdeImportUtils rdeImportUtils; private FakeClock clock; @Before public void before() { clock = new FakeClock(); clock.setTo(DateTime.now()); rdeImportUtils = new RdeImportUtils(ofy(), clock, "import-bucket", gcsUtils); createTld("test", TldState.PREDELEGATION); createTld("getld", TldState.GENERAL_AVAILABILITY); persistNewRegistrar("RegistrarX", 1L); } @After public void after() throws IOException { if (xmlInput != null) { xmlInput.close(); } } /** Verifies import of a contact that has not been previously imported */ @Test public void testImportNewContact() { ContactResource newContact = buildNewContact(); assertThat(rdeImportUtils.importContact(newContact)).isTrue(); assertEppResourceIndexEntityFor(newContact); assertForeignKeyIndexFor(newContact); // verify the new contact was saved ContactResource saved = getContact("TEST-123"); assertThat(saved).isNotNull(); assertThat(saved.getContactId()).isEqualTo(newContact.getContactId()); assertThat(saved.getEmailAddress()).isEqualTo(newContact.getEmailAddress()); assertThat(saved.getLastEppUpdateTime()).isEqualTo(newContact.getLastEppUpdateTime()); } /** Verifies that a contact will not be imported more than once */ @Test public void testImportExistingContact() { ContactResource newContact = buildNewContact(); persistResource(newContact); ContactResource updatedContact = newContact .asBuilder() .setLastEppUpdateTime(newContact.getLastEppUpdateTime().plusSeconds(1)) .build(); assertThat(rdeImportUtils.importContact(updatedContact)).isFalse(); // verify the updated contact was saved ContactResource saved = getContact("TEST-123"); assertThat(saved).isNotNull(); assertThat(saved.getContactId()).isEqualTo(newContact.getContactId()); assertThat(saved.getEmailAddress()).isEqualTo(newContact.getEmailAddress()); assertThat(saved.getLastEppUpdateTime()).isEqualTo(newContact.getLastEppUpdateTime()); } private static ContactResource buildNewContact() { return new ContactResource.Builder() .setContactId("sh8013") .setEmailAddress("jdoe@example.com") .setLastEppUpdateTime(DateTime.parse("2010-10-10T00:00:00.000Z")) .setRepoId("TEST-123") .build(); } /** Verifies that no errors are thrown when a valid escrow file is validated */ @Test public void testValidateEscrowFile_valid() throws Exception { xmlInput = DEPOSIT_XML.openBufferedStream(); when(gcsUtils.openInputStream(any(GcsFilename.class))).thenReturn(xmlInput); rdeImportUtils.validateEscrowFileForImport("valid-deposit-file.xml"); verify(gcsUtils).openInputStream(new GcsFilename("import-bucket", "valid-deposit-file.xml")); } /** Verifies thrown error when tld in escrow file is not in the registry */ @Test public void testValidateEscrowFile_tldNotFound() throws Exception { thrown.expect(IllegalArgumentException.class, "Tld 'badtld' not found in the registry"); xmlInput = DEPOSIT_BADTLD_XML.openBufferedStream(); when(gcsUtils.openInputStream(any(GcsFilename.class))).thenReturn(xmlInput); rdeImportUtils.validateEscrowFileForImport("invalid-deposit-badtld.xml"); } /** Verifies thrown errer when tld in escrow file is not in PREDELEGATION state */ @Test public void testValidateEscrowFile_tldWrongState() throws Exception { thrown.expect( IllegalArgumentException.class, "Tld 'getld' is in state GENERAL_AVAILABILITY and cannot be imported"); xmlInput = DEPOSIT_GETLD_XML.openBufferedStream(); when(gcsUtils.openInputStream(any(GcsFilename.class))).thenReturn(xmlInput); rdeImportUtils.validateEscrowFileForImport("invalid-deposit-getld.xml"); } /** Verifies thrown error when registrar in escrow file is not in the registry */ @Test public void testValidateEscrowFile_badRegistrar() throws Exception { thrown.expect( IllegalArgumentException.class, "Registrar 'RegistrarY' not found in the registry"); xmlInput = DEPOSIT_BADREGISTRAR_XML.openBufferedStream(); when(gcsUtils.openInputStream(any(GcsFilename.class))).thenReturn(xmlInput); rdeImportUtils.validateEscrowFileForImport("invalid-deposit-badregistrar.xml"); } /** Gets the contact with the specified ROID */ private static ContactResource getContact(String repoId) { final Key key = Key.create(ContactResource.class, repoId); return ofy().transact(new Work() { @Override public ContactResource run() { return ofy().load().key(key).now(); }}); } /** Confirms that a ForeignKeyIndex exists in the datastore for a given resource. */ private static void assertForeignKeyIndexFor(final T resource) { assertThat(ForeignKeyIndex.load(resource.getClass(), resource.getForeignKey(), DateTime.now())) .isNotNull(); } /** Confirms that an EppResourceIndex entity exists in datastore for a given resource. */ private static void assertEppResourceIndexEntityFor(final T resource) { ImmutableList indices = FluentIterable .from(ofy().load() .type(EppResourceIndex.class) .filter("kind", Key.getKind(resource.getClass()))) .filter(new Predicate() { @Override public boolean apply(EppResourceIndex index) { return index.getReference().get().equals(resource); }}) .toList(); assertThat(indices).hasSize(1); assertThat(indices.get(0).getBucket()) .isEqualTo(EppResourceIndexBucket.getBucketKey(Key.create(resource))); } }