Delete code relating to SQL init and scheduling (#1661)

One of the more significant changes introduced in this PR is that we use
SQL as the backing database in all tests unless otherwise specified,
e.g. by using the TmOverrideExtension. We change various ofy-related
tests to use this.

This includes various changes:
- Deletion of SqlEntity/DatastoreEntity and related classes. Includes
  any necessary changes because of that (e.g. getting a nice SQL key on
  error in RegistryJpaIO).
- Deletion of classes that used libraries from the init-sql code
  (RefreshDnsOnHostRenameAction)
- Removal of the JpaTransactionManager's backup implementation
- Modification of RegistryJpaWriteTest to not use init-sql code
- Removal of the Transaction class and related classes, however it does
  not remove the TransactionEntity class as that would require DB
  changes
- Removal of anything related to the actual usage of the database
  migration schedule or read-only phases
- Various test changes and fixes to account for the differences in SQL
  (like how foreign keys need to exist)

This deliberately doesn't do anything to alter the objects actually
stored in the DB yet, just how we use them
This commit is contained in:
gbrodman
2022-06-13 15:10:35 -04:00
committed by GitHub
parent dcc11379c8
commit 2f8be045c7
232 changed files with 457 additions and 8245 deletions
@@ -29,8 +29,10 @@ import google.registry.testing.AppEngineExtension;
import google.registry.testing.CloudTasksHelper;
import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.FakeClock;
import google.registry.testing.TmOverrideExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -39,6 +41,10 @@ public class CommitLogCheckpointActionTest {
private static final String QUEUE_NAME = "export-commits";
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
@@ -26,10 +26,12 @@ import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.testing.InjectExtension;
import google.registry.testing.TmOverrideExtension;
import google.registry.testing.mapreduce.MapreduceTestCase;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -37,6 +39,10 @@ import org.junit.jupiter.api.extension.RegisterExtension;
public class DeleteOldCommitLogsActionTest
extends MapreduceTestCase<DeleteOldCommitLogsAction> {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
private final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01TZ"));
private final FakeResponse response = new FakeResponse();
private ContactResource contact;
@@ -36,15 +36,21 @@ import google.registry.model.ofy.CommitLogManifest;
import google.registry.model.ofy.CommitLogMutation;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.TestObject;
import google.registry.testing.TmOverrideExtension;
import java.util.List;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ExportCommitLogDiffAction}. */
public class ExportCommitLogDiffActionTest {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
@@ -1,279 +0,0 @@
// 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.batch;
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_HOST_KEY;
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME;
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_HOST_RENAME;
import static google.registry.batch.AsyncTaskMetrics.OperationType.DNS_REFRESH;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.newDomainBase;
import static google.registry.testing.DatabaseHelper.newHostResource;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistDeletedHost;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static google.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
import static google.registry.testing.TaskQueueHelper.assertNoTasksEnqueued;
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.Duration.millis;
import static org.joda.time.Duration.standardDays;
import static org.joda.time.Duration.standardHours;
import static org.joda.time.Duration.standardSeconds;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import google.registry.batch.AsyncTaskMetrics.OperationResult;
import google.registry.batch.RefreshDnsOnHostRenameAction.RefreshDnsOnHostRenameReducer;
import google.registry.dns.DnsQueue;
import google.registry.model.host.HostResource;
import google.registry.model.server.Lock;
import google.registry.testing.CloudTasksHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.testing.FakeSleeper;
import google.registry.testing.InjectExtension;
import google.registry.testing.TaskQueueHelper.TaskMatcher;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.testing.mapreduce.MapreduceTestCase;
import google.registry.util.RequestStatusChecker;
import google.registry.util.Retrier;
import google.registry.util.Sleeper;
import google.registry.util.SystemSleeper;
import java.util.Optional;
import org.apache.http.HttpStatus;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
/** Unit tests for {@link RefreshDnsOnHostRenameAction}. */
@DualDatabaseTest
public class RefreshDnsOnHostRenameActionTest
extends MapreduceTestCase<RefreshDnsOnHostRenameAction> {
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private AsyncTaskEnqueuer enqueuer;
private final FakeClock clock = new FakeClock(DateTime.parse("2015-01-15T11:22:33Z"));
private final FakeResponse fakeResponse = new FakeResponse();
@Mock private RequestStatusChecker requestStatusChecker;
@BeforeEach
void beforeEach() {
createTld("tld");
enqueuer =
AsyncTaskEnqueuerTest.createForTesting(
new CloudTasksHelper(clock).getTestCloudTasksUtils(), clock, Duration.ZERO);
AsyncTaskMetrics asyncTaskMetricsMock = mock(AsyncTaskMetrics.class);
action = new RefreshDnsOnHostRenameAction();
action.asyncTaskMetrics = asyncTaskMetricsMock;
inject.setStaticField(
RefreshDnsOnHostRenameReducer.class, "asyncTaskMetrics", asyncTaskMetricsMock);
action.clock = clock;
action.mrRunner = makeDefaultRunner();
action.pullQueue = getQueue(QUEUE_ASYNC_HOST_RENAME);
action.dnsQueue = DnsQueue.createForTesting(clock);
action.requestStatusChecker = requestStatusChecker;
action.response = fakeResponse;
action.retrier = new Retrier(new FakeSleeper(clock), 1);
when(requestStatusChecker.getLogId()).thenReturn("requestId");
when(requestStatusChecker.isRunning(anyString()))
.thenThrow(new AssertionError("Should not be called"));
}
private void runAction() throws Exception {
clock.advanceOneMilli();
// Use hard sleeps to ensure that the tasks are enqueued properly and will be leased.
Sleeper sleeper = new SystemSleeper();
sleeper.sleep(millis(50));
action.run();
sleeper.sleep(millis(50));
executeTasksUntilEmpty("mapreduce", clock);
sleeper.sleep(millis(50));
clock.advanceBy(standardSeconds(5));
tm().clearSessionCache();
}
/** Kicks off, but does not run, the mapreduce tasks. Useful for testing validation/setup. */
private void enqueueMapreduceOnly() {
clock.advanceOneMilli();
action.run();
clock.advanceBy(standardSeconds(5));
tm().clearSessionCache();
}
@TestSqlOnly
void testFailure_dnsUpdateEnqueueFailed() throws Exception {
HostResource host = persistActiveHost("ns1.example.tld");
persistResource(newDomainBase("example.tld", host));
persistResource(newDomainBase("otherexample.tld", host));
persistResource(newDomainBase("untouched.tld", persistActiveHost("ns2.example.tld")));
DateTime timeEnqueued = clock.nowUtc();
enqueuer.enqueueAsyncDnsRefresh(host, timeEnqueued);
DnsQueue mockedQueue = mock(DnsQueue.class);
action.dnsQueue = mockedQueue;
when(mockedQueue.addDomainRefreshTask(anyString()))
.thenThrow(new RuntimeException("Cannot enqueue task."));
runAction();
assertNoDnsTasksEnqueued();
assertTasksEnqueued(
QUEUE_ASYNC_HOST_RENAME,
new TaskMatcher()
.param(PARAM_HOST_KEY, host.createVKey().stringify())
.param(PARAM_REQUESTED_TIME, timeEnqueued.toString()));
verify(action.asyncTaskMetrics).recordDnsRefreshBatchSize(1L);
verifyNoMoreInteractions(action.asyncTaskMetrics);
assertThat(fakeResponse.getStatus()).isEqualTo(HttpStatus.SC_INTERNAL_SERVER_ERROR);
assertThat(acquireLock()).isPresent();
}
@TestOfyAndSql
void testSuccess_dnsUpdateEnqueued() throws Exception {
HostResource host = persistActiveHost("ns1.example.tld");
persistResource(newDomainBase("example.tld", host));
persistResource(newDomainBase("otherexample.tld", host));
persistResource(newDomainBase("untouched.tld", persistActiveHost("ns2.example.tld")));
DateTime timeEnqueued = clock.nowUtc();
enqueuer.enqueueAsyncDnsRefresh(host, timeEnqueued);
runAction();
assertDnsTasksEnqueued("example.tld", "otherexample.tld");
assertNoTasksEnqueued(QUEUE_ASYNC_HOST_RENAME);
verify(action.asyncTaskMetrics).recordDnsRefreshBatchSize(1L);
verify(action.asyncTaskMetrics)
.recordAsyncFlowResult(DNS_REFRESH, OperationResult.SUCCESS, timeEnqueued);
verifyNoMoreInteractions(action.asyncTaskMetrics);
assertThat(acquireLock()).isPresent();
}
@TestOfyAndSql
void testSuccess_multipleHostsProcessedInBatch() throws Exception {
HostResource host1 = persistActiveHost("ns1.example.tld");
HostResource host2 = persistActiveHost("ns2.example.tld");
HostResource host3 = persistActiveHost("ns3.example.tld");
persistResource(newDomainBase("example1.tld", host1, host2));
persistResource(newDomainBase("example2.tld", host2));
persistResource(newDomainBase("example3.tld", host3));
DateTime timeEnqueued = clock.nowUtc();
DateTime laterTimeEnqueued = timeEnqueued.plus(standardSeconds(10));
enqueuer.enqueueAsyncDnsRefresh(host1, timeEnqueued);
enqueuer.enqueueAsyncDnsRefresh(host2, timeEnqueued);
enqueuer.enqueueAsyncDnsRefresh(host3, laterTimeEnqueued);
runAction();
assertDnsTasksEnqueued("example1.tld", "example2.tld", "example3.tld");
assertNoTasksEnqueued(QUEUE_ASYNC_HOST_RENAME);
verify(action.asyncTaskMetrics).recordDnsRefreshBatchSize(3L);
verify(action.asyncTaskMetrics, times(2))
.recordAsyncFlowResult(DNS_REFRESH, OperationResult.SUCCESS, timeEnqueued);
verify(action.asyncTaskMetrics)
.recordAsyncFlowResult(DNS_REFRESH, OperationResult.SUCCESS, laterTimeEnqueued);
verifyNoMoreInteractions(action.asyncTaskMetrics);
assertThat(acquireLock()).isPresent();
}
@TestOfyAndSql
void testSuccess_deletedHost_doesntTriggerDnsRefresh() throws Exception {
HostResource host = persistDeletedHost("ns11.fakesss.tld", clock.nowUtc().minusDays(4));
persistResource(newDomainBase("example1.tld", host));
DateTime timeEnqueued = clock.nowUtc();
enqueuer.enqueueAsyncDnsRefresh(host, timeEnqueued);
runAction();
assertNoDnsTasksEnqueued();
assertNoTasksEnqueued(QUEUE_ASYNC_HOST_RENAME);
verify(action.asyncTaskMetrics).recordDnsRefreshBatchSize(1L);
verify(action.asyncTaskMetrics)
.recordAsyncFlowResult(DNS_REFRESH, OperationResult.STALE, timeEnqueued);
verifyNoMoreInteractions(action.asyncTaskMetrics);
assertThat(acquireLock()).isPresent();
}
@TestOfyAndSql
void testSuccess_noDnsTasksForDeletedDomain() throws Exception {
HostResource renamedHost = persistActiveHost("ns1.example.tld");
persistResource(
newDomainBase("example.tld", renamedHost)
.asBuilder()
.setDeletionTime(START_OF_TIME)
.build());
enqueuer.enqueueAsyncDnsRefresh(renamedHost, clock.nowUtc());
runAction();
assertNoDnsTasksEnqueued();
assertNoTasksEnqueued(QUEUE_ASYNC_HOST_RENAME);
assertThat(acquireLock()).isPresent();
}
@TestOfyAndSql
void testRun_hostDoesntExist_delaysTask() {
HostResource host = newHostResource("ns1.example.tld");
enqueuer.enqueueAsyncDnsRefresh(host, clock.nowUtc());
enqueueMapreduceOnly();
assertNoDnsTasksEnqueued();
assertTasksEnqueued(
QUEUE_ASYNC_HOST_RENAME,
new TaskMatcher()
.etaDelta(standardHours(23), standardHours(25))
.param(PARAM_HOST_KEY, host.createVKey().stringify()));
assertThat(acquireLock()).isPresent();
}
@TestOfyAndSql
void test_cannotAcquireLock() {
// Make lock acquisition fail.
acquireLock();
enqueueMapreduceOnly();
assertThat(fakeResponse.getPayload()).isEqualTo("Can't acquire lock; aborting.");
assertNoDnsTasksEnqueued();
assertThat(acquireLock()).isEmpty();
}
@TestOfyOnly
void test_mapreduceHasWorkToDo_lockIsAcquired() {
HostResource host = persistActiveHost("ns1.example.tld");
enqueuer.enqueueAsyncDnsRefresh(host, clock.nowUtc());
enqueueMapreduceOnly();
assertThat(acquireLock()).isEmpty();
}
@TestOfyAndSql
void test_noTasksToLease_releasesLockImmediately() {
enqueueMapreduceOnly();
assertNoDnsTasksEnqueued();
assertNoTasksEnqueued(QUEUE_ASYNC_HOST_RENAME);
// If the Lock was correctly released, then we can acquire it now.
assertThat(acquireLock()).isPresent();
}
private Optional<Lock> acquireLock() {
return Lock.acquire(
RefreshDnsOnHostRenameAction.class.getSimpleName(),
null,
standardDays(30),
requestStatusChecker,
false);
}
}
@@ -24,10 +24,13 @@ import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.contact.ContactResource;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.FakeResponse;
import google.registry.testing.TmOverrideExtension;
import google.registry.testing.mapreduce.MapreduceTestCase;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ResaveAllEppResourcesAction}. */
// No longer needed in SQL. Subject to future removal.
@@ -35,6 +38,10 @@ import org.junit.jupiter.api.Test;
@DeleteAfterMigration
class ResaveAllEppResourcesActionTest extends MapreduceTestCase<ResaveAllEppResourcesAction> {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@BeforeEach
void beforeEach() {
action = new ResaveAllEppResourcesAction();
@@ -18,30 +18,18 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatabaseHelper.newContactResource;
import static google.registry.testing.DatabaseHelper.putInDb;
import com.google.appengine.api.datastore.Entity;
import com.google.common.collect.ImmutableList;
import google.registry.backup.VersionedEntity;
import google.registry.beam.TestPipelineExtension;
import google.registry.beam.initsql.BackupTestStore;
import google.registry.beam.initsql.InitSqlTestUtils;
import google.registry.beam.initsql.Transforms;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource;
import google.registry.model.ofy.Ofy;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import java.io.Serializable;
import java.util.stream.Collectors;
import org.apache.beam.sdk.transforms.Create;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -49,17 +37,13 @@ import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit test for {@link RegistryJpaIO.Write}. */
class RegistryJpaWriteTest implements Serializable {
private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z");
private final FakeClock fakeClock = new FakeClock(START_TIME);
private final FakeClock fakeClock = new FakeClock(DateTime.parse("2000-01-01T00:00:00.0Z"));
@RegisterExtension
@Order(Order.DEFAULT - 1)
final transient DatastoreEntityExtension datastore =
new DatastoreEntityExtension().allThreads(true);
@RegisterExtension final transient InjectExtension injectExtension = new InjectExtension();
@RegisterExtension
final transient JpaIntegrationTestExtension database =
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
@@ -68,52 +52,25 @@ class RegistryJpaWriteTest implements Serializable {
final transient TestPipelineExtension testPipeline =
TestPipelineExtension.create().enableAbandonedNodeEnforcement(true);
private ImmutableList<Entity> contacts;
@BeforeEach
void beforeEach() throws Exception {
try (BackupTestStore store = new BackupTestStore(fakeClock)) {
injectExtension.setStaticField(Ofy.class, "clock", fakeClock);
// Required for contacts created below.
Registrar ofyRegistrar = AppEngineExtension.makeRegistrar2();
store.insertOrUpdate(ofyRegistrar);
putInDb(store.loadAsOfyEntity(ofyRegistrar));
ImmutableList.Builder<Entity> builder = new ImmutableList.Builder<>();
for (int i = 0; i < 3; i++) {
ContactResource contact = newContactResource("contact_" + i);
store.insertOrUpdate(contact);
builder.add(store.loadAsDatastoreEntity(contact));
}
contacts = builder.build();
}
}
@Test
void writeToSql_twoWriters() {
jpaTm().transact(() -> jpaTm().put(AppEngineExtension.makeRegistrar2()));
ImmutableList.Builder<ContactResource> contactsBuilder = new ImmutableList.Builder<>();
for (int i = 0; i < 3; i++) {
contactsBuilder.add(newContactResource("contact_" + i));
}
ImmutableList<ContactResource> contacts = contactsBuilder.build();
testPipeline
.apply(Create.of(contacts))
.apply(
Create.of(
contacts.stream()
.map(InitSqlTestUtils::entityToBytes)
.map(bytes -> VersionedEntity.from(0L, bytes))
.collect(Collectors.toList())))
.apply(
RegistryJpaIO.<VersionedEntity>write()
RegistryJpaIO.<ContactResource>write()
.withName("ContactResource")
.withBatchSize(4)
.withShards(2)
.withJpaConverter(Transforms::convertVersionedEntityToSqlEntity));
.withShards(2));
testPipeline.run().waitUntilFinish();
assertThat(jpaTm().transact(() -> jpaTm().loadAllOf(ContactResource.class)))
.comparingElementsUsing(immutableObjectCorrespondence("revisions", "updateTimestamp"))
.containsExactlyElementsIn(
contacts.stream()
.map(InitSqlTestUtils::datastoreToOfyEntity)
.map(ImmutableObject.class::cast)
.collect(ImmutableList.toImmutableList()));
.containsExactlyElementsIn(contacts);
}
}
@@ -1,63 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.beam.initsql.BackupPaths.getCloudSQLCredentialFilePatterns;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link google.registry.beam.initsql.BackupPaths}. */
public class BackupPathsTest {
@Test
void getCloudSQLCredentialFilePatterns_alpha() {
assertThat(getCloudSQLCredentialFilePatterns("alpha"))
.containsExactly(
"gs://domain-registry-dev-deploy/cloudsql-credentials/alpha/admin_credential.enc");
}
@Test
void getCloudSQLCredentialFilePatterns_crash() {
assertThat(getCloudSQLCredentialFilePatterns("crash"))
.containsExactly(
"gs://domain-registry-dev-deploy/cloudsql-credentials/crash/admin_credential.enc");
}
@Test
void getCloudSQLCredentialFilePatterns_sandbox() {
assertThat(getCloudSQLCredentialFilePatterns("sandbox"))
.containsExactly(
"gs://domain-registry-dev-deploy/cloudsql-credentials/sandbox/admin_credential.enc");
}
@Test
void getCloudSQLCredentialFilePatterns_production() {
assertThat(getCloudSQLCredentialFilePatterns("production"))
.containsExactly(
"gs://domain-registry-dev-deploy/cloudsql-credentials/production/admin_credential.enc");
}
@Test
void getEnvFromProject_illegal() {
assertThrows(IllegalArgumentException.class, () -> getCloudSQLCredentialFilePatterns("bad"));
}
@Test
void getEnvFromProject_null() {
assertThrows(IllegalArgumentException.class, () -> getCloudSQLCredentialFilePatterns(null));
}
}
@@ -1,204 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.googlecode.objectify.Key;
import google.registry.backup.CommitLogExports;
import google.registry.backup.VersionedEntity;
import google.registry.model.ImmutableObject;
import google.registry.model.ofy.CommitLogCheckpoint;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import google.registry.tools.LevelDbFileBuilder;
import java.io.File;
import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.Set;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* Wrapper of a Datastore test instance that can generate backups.
*
* <p>A Datastore backup consists of an unsynchronized data export and a sequence of incremental
* Commit Logs that overlap with the export process. Together they can be used to recreate a
* consistent snapshot of the Datastore.
*
* <p>For convenience of test-writing, the {@link #fakeClock} is advanced by 1 millisecond after
* every transaction is invoked on this store, ensuring strictly increasing timestamps on causally
* dependent transactions. In production, the same ordering is ensured by sleep and retry.
*/
public final class BackupTestStore implements AutoCloseable {
private static final DateTimeFormatter EXPORT_TIMESTAMP_FORMAT =
DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss_SSS");
private final FakeClock fakeClock;
private AppEngineExtension appEngine;
/** For fetching the persisted Datastore Entity directly. */
private DatastoreService datastoreService;
private CommitLogCheckpoint prevCommitLogCheckpoint;
public BackupTestStore(FakeClock fakeClock) throws Exception {
this.fakeClock = fakeClock;
this.appEngine =
new AppEngineExtension.Builder()
.withDatastoreAndCloudSql()
.withoutCannedData()
.withClock(fakeClock)
.build();
this.appEngine.setUp();
datastoreService = DatastoreServiceFactory.getDatastoreService();
}
/** Returns the timestamp of the transaction. */
long transact(Iterable<Object> deletes, Iterable<Object> newOrUpdated) {
long timestamp = fakeClock.nowUtc().getMillis();
ofyTm()
.transact(
() -> {
auditedOfy().delete().entities(deletes);
auditedOfy().save().entities(newOrUpdated);
});
fakeClock.advanceOneMilli();
return timestamp;
}
/**
* Inserts or updates {@code entities} in the Datastore and returns the timestamp of this
* transaction.
*/
@SafeVarargs
public final long insertOrUpdate(Object... entities) {
long timestamp = fakeClock.nowUtc().getMillis();
ofyTm().transact(() -> auditedOfy().save().entities(entities).now());
fakeClock.advanceOneMilli();
return timestamp;
}
/** Deletes {@code entities} from the Datastore and returns the timestamp of this transaction. */
@SafeVarargs
public final long delete(Object... entities) {
long timestamp = fakeClock.nowUtc().getMillis();
ofyTm().transact(() -> auditedOfy().delete().entities(entities).now());
fakeClock.advanceOneMilli();
return timestamp;
}
/**
* Returns the persisted data that corresponds to {@code ofyEntity} as a Datastore {@link Entity}.
*
* <p>A typical use case for this method is in a test, when the caller has persisted newly created
* Objectify entity and want to find out the values of certain assign-on-persist properties. See
* {@link VersionedEntity} for more information.
*/
public Entity loadAsDatastoreEntity(Object ofyEntity) {
try {
return datastoreService.get(Key.create(ofyEntity).getRaw());
} catch (EntityNotFoundException e) {
throw new NoSuchElementException(e.getMessage());
}
}
/**
* Returns the persisted data that corresponds to {@code ofyEntity} as an Objectify entity.
*
* <p>See {@link #loadAsDatastoreEntity} and {@link VersionedEntity} for more information.
*/
public ImmutableObject loadAsOfyEntity(ImmutableObject ofyEntity) {
try {
return auditedOfy().load().fromEntity(datastoreService.get(Key.create(ofyEntity).getRaw()));
} catch (EntityNotFoundException e) {
throw new NoSuchElementException(e.getMessage());
}
}
/**
* Exports entities of the caller provided types and returns the directory where data is exported.
*
* @param exportRootPath path to the root directory of all exports. A subdirectory will be created
* for this export
* @param pojoTypes java class of all entities to be exported
* @param excludes {@link Set} of {@link Key keys} of the entities not to export.This can be used
* to simulate an inconsistent export
* @return directory where data is exported
*/
File export(String exportRootPath, Iterable<Class<?>> pojoTypes, Set<Key<?>> excludes)
throws IOException {
File exportDirectory = getExportDirectory(exportRootPath);
for (Class<?> pojoType : pojoTypes) {
File perKindFile =
new File(
BackupPaths.getExportFileNameByShard(
exportDirectory.getAbsolutePath(), Key.getKind(pojoType), 0));
checkState(
perKindFile.getParentFile().mkdirs(),
"Failed to create per-kind export directory for %s.",
perKindFile.getParentFile().getAbsolutePath());
exportOneKind(perKindFile, pojoType, excludes);
}
return exportDirectory;
}
private void exportOneKind(File perKindFile, Class<?> pojoType, Set<Key<?>> excludes)
throws IOException {
LevelDbFileBuilder builder = new LevelDbFileBuilder(perKindFile);
for (Object pojo : auditedOfy().load().type(pojoType).iterable()) {
if (!excludes.contains(Key.create(pojo))) {
try {
// Must preserve UpdateTimestamp. Do not use auditedOfy().save().toEntity(pojo)!
builder.addEntity(datastoreService.get(Key.create(pojo).getRaw()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
builder.build();
}
File saveCommitLogs(String commitLogDir) {
CommitLogCheckpoint checkpoint = CommitLogExports.computeCheckpoint(fakeClock);
File commitLogFile =
CommitLogExports.saveCommitLogs(commitLogDir, prevCommitLogCheckpoint, checkpoint);
prevCommitLogCheckpoint = checkpoint;
return commitLogFile;
}
@Override
public void close() throws Exception {
if (appEngine != null) {
appEngine.tearDown();
appEngine = null;
}
}
private File getExportDirectory(String exportRootPath) {
File exportDirectory =
new File(exportRootPath, fakeClock.nowUtc().toString(EXPORT_TIMESTAMP_FORMAT));
checkState(
exportDirectory.mkdirs(),
"Failed to create export directory %s.",
exportDirectory.getAbsolutePath());
return exportDirectory;
}
}
@@ -1,242 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.testing.DatabaseHelper.newContactResource;
import static google.registry.testing.DatabaseHelper.newDomainBase;
import static google.registry.testing.DatabaseHelper.newRegistry;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import google.registry.backup.CommitLogImports;
import google.registry.backup.VersionedEntity;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DomainBase;
import google.registry.model.ofy.Ofy;
import google.registry.model.tld.Registry;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import google.registry.tools.LevelDbLogReader;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import org.apache.beam.sdk.values.KV;
import org.joda.time.DateTime;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
/** Unit tests for {@link BackupTestStore}. */
public class BackupTestStoreTest {
private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z");
@TempDir File tempDir;
@RegisterExtension
final transient JpaIntegrationTestExtension jpaIntegrationTestExtension =
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
@RegisterExtension
@Order(value = 1)
final transient DatastoreEntityExtension datastoreEntityExtension =
new DatastoreEntityExtension();
@RegisterExtension InjectExtension injectExtension = new InjectExtension();
private FakeClock fakeClock;
private BackupTestStore store;
// Test data:
private Registry registry;
private ContactResource contact;
private DomainBase domain;
@BeforeEach
void beforeEach() throws Exception {
fakeClock = new FakeClock(START_TIME);
store = new BackupTestStore(fakeClock);
injectExtension.setStaticField(Ofy.class, "clock", fakeClock);
registry = newRegistry("tld1", "TLD1");
store.insertOrUpdate(registry);
contact = newContactResource("contact_1");
domain = newDomainBase("domain1.tld1", contact);
store.insertOrUpdate(contact, domain);
// Save persisted data for assertions.
registry = (Registry) store.loadAsOfyEntity(registry);
contact = (ContactResource) store.loadAsOfyEntity(contact);
domain = (DomainBase) store.loadAsOfyEntity(domain);
}
@AfterEach
void afterEach() throws Exception {
store.close();
}
@Test
void export_filesCreated() throws IOException {
String exportRootPath = tempDir.getAbsolutePath();
assertThat(fakeClock.nowUtc().toString()).isEqualTo("2000-01-01T00:00:00.002Z");
File exportFolder = new File(exportRootPath, "2000-01-01T00:00:00_002");
assertWithMessage("Directory %s should not exist.", exportFolder.getAbsoluteFile())
.that(exportFolder.exists())
.isFalse();
File actualExportFolder = export(exportRootPath, ImmutableSet.of());
assertThat(actualExportFolder).isEquivalentAccordingToCompareTo(exportFolder);
try (Stream<String> files =
Files.walk(exportFolder.toPath())
.filter(Files::isRegularFile)
.map(Path::toString)
.map(string -> string.substring(exportFolder.getAbsolutePath().length()))) {
assertThat(files)
.containsExactly(
"/all_namespaces/kind_Registry/output-0",
"/all_namespaces/kind_DomainBase/output-0",
"/all_namespaces/kind_ContactResource/output-0");
}
}
@Test
void export_folderNameChangesWithTime() throws IOException {
String exportRootPath = tempDir.getAbsolutePath();
fakeClock.advanceOneMilli();
File exportFolder = new File(exportRootPath, "2000-01-01T00:00:00_003");
assertWithMessage("Directory %s should not exist.", exportFolder.getAbsoluteFile())
.that(exportFolder.exists())
.isFalse();
assertThat(export(exportRootPath, ImmutableSet.of()))
.isEquivalentAccordingToCompareTo(exportFolder);
}
@Test
void export_dataReadBack() throws IOException {
String exportRootPath = tempDir.getAbsolutePath();
File exportFolder = export(exportRootPath, ImmutableSet.of());
ImmutableList<Object> loadedRegistries =
loadExportedEntities(new File(exportFolder, "/all_namespaces/kind_Registry/output-0"));
assertThat(loadedRegistries).containsExactly(registry);
ImmutableList<Object> loadedDomains =
loadExportedEntities(new File(exportFolder, "/all_namespaces/kind_DomainBase/output-0"));
assertThat(loadedDomains).containsExactly(domain);
ImmutableList<Object> loadedContacts =
loadExportedEntities(
new File(exportFolder, "/all_namespaces/kind_ContactResource/output-0"));
assertThat(loadedContacts).containsExactly(contact);
}
@Test
void export_excludeSomeEntity() throws IOException {
Registry newRegistry = newRegistry("tld2", "TLD2");
store.insertOrUpdate(newRegistry);
newRegistry = (Registry) store.loadAsOfyEntity(newRegistry);
String exportRootPath = tempDir.getAbsolutePath();
File exportFolder =
export(
exportRootPath, ImmutableSet.of(Key.create(getCrossTldKey(), Registry.class, "tld1")));
ImmutableList<Object> loadedRegistries =
loadExportedEntities(new File(exportFolder, "/all_namespaces/kind_Registry/output-0"));
assertThat(loadedRegistries).containsExactly(newRegistry);
}
@Test
void saveCommitLogs_fileCreated() {
File commitLogFile = store.saveCommitLogs(tempDir.getAbsolutePath());
assertThat(commitLogFile.exists()).isTrue();
assertThat(commitLogFile.getName()).isEqualTo("commit_diff_until_2000-01-01T00:00:00.002Z");
}
@Test
void saveCommitLogs_inserts() {
File commitLogFile = store.saveCommitLogs(tempDir.getAbsolutePath());
assertThat(commitLogFile.exists()).isTrue();
ImmutableList<VersionedEntity> mutations = CommitLogImports.loadEntities(commitLogFile);
InitSqlTestUtils.assertContainsExactlyElementsIn(
mutations,
KV.of(fakeClock.nowUtc().getMillis() - 2, store.loadAsDatastoreEntity(registry)),
KV.of(fakeClock.nowUtc().getMillis() - 1, store.loadAsDatastoreEntity(contact)),
KV.of(fakeClock.nowUtc().getMillis() - 1, store.loadAsDatastoreEntity(domain)));
}
@Test
void saveCommitLogs_deletes() {
fakeClock.advanceOneMilli();
store.saveCommitLogs(tempDir.getAbsolutePath());
ContactResource newContact = newContactResource("contact2");
VKey<ContactResource> vKey = newContact.createVKey();
domain =
domain
.asBuilder()
.setRegistrant(vKey)
.setContacts(
ImmutableSet.of(
DesignatedContact.create(DesignatedContact.Type.ADMIN, vKey),
DesignatedContact.create(DesignatedContact.Type.TECH, vKey)))
.build();
store.insertOrUpdate(domain, newContact);
store.delete(contact);
File commitLogFile = store.saveCommitLogs(tempDir.getAbsolutePath());
ImmutableList<VersionedEntity> mutations = CommitLogImports.loadEntities(commitLogFile);
InitSqlTestUtils.assertContainsExactlyElementsIn(
mutations,
KV.of(fakeClock.nowUtc().getMillis() - 1, Key.create(contact).getRaw()),
KV.of(fakeClock.nowUtc().getMillis() - 2, store.loadAsDatastoreEntity(domain)),
KV.of(fakeClock.nowUtc().getMillis() - 2, store.loadAsDatastoreEntity(newContact)));
}
@Test
void saveCommitLogs_empty() {
fakeClock.advanceOneMilli();
store.saveCommitLogs(tempDir.getAbsolutePath());
fakeClock.advanceOneMilli();
File commitLogFile = store.saveCommitLogs(tempDir.getAbsolutePath());
assertThat(commitLogFile.exists()).isTrue();
assertThat(CommitLogImports.loadEntities(commitLogFile)).isEmpty();
}
private File export(String exportRootPath, ImmutableSet<Key<?>> excludes) throws IOException {
return store.export(
exportRootPath,
ImmutableList.of(ContactResource.class, DomainBase.class, Registry.class),
excludes);
}
private static ImmutableList<Object> loadExportedEntities(File dataFile) throws IOException {
return Streams.stream(LevelDbLogReader.from(dataFile.toPath()))
.map(InitSqlTestUtils::bytesToEntity)
.map(InitSqlTestUtils::datastoreToOfyEntity)
.collect(ImmutableList.toImmutableList());
}
}
@@ -1,245 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static google.registry.testing.DatabaseHelper.newContactResource;
import static google.registry.testing.DatabaseHelper.newDomainBase;
import static google.registry.testing.DatabaseHelper.newRegistry;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.backup.VersionedEntity;
import google.registry.beam.TestPipelineExtension;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.ofy.Ofy;
import google.registry.model.tld.Registry;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.beam.sdk.coders.StringUtf8Coder;
import org.apache.beam.sdk.io.fs.MatchResult.Metadata;
import org.apache.beam.sdk.testing.PAssert;
import org.apache.beam.sdk.transforms.Create;
import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.ParDo;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollection;
import org.joda.time.DateTime;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
/** Unit tests for {@link Transforms} related to loading CommitLogs. */
class CommitLogTransformsTest implements Serializable {
private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z");
private final FakeClock fakeClock = new FakeClock(START_TIME);
@SuppressWarnings("WeakerAccess")
@TempDir
transient Path tmpDir;
@RegisterExtension final transient InjectExtension injectExtension = new InjectExtension();
@RegisterExtension
final transient JpaIntegrationTestExtension jpaIntegrationTestExtension =
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
@RegisterExtension
@Order(value = 1)
final transient DatastoreEntityExtension datastoreEntityExtension =
new DatastoreEntityExtension().allThreads(true);
@RegisterExtension
final transient TestPipelineExtension testPipeline =
TestPipelineExtension.create().enableAbandonedNodeEnforcement(true);
private transient BackupTestStore store;
private File commitLogsDir;
private File firstCommitLogFile;
// Canned data:
private transient Registry registry;
private transient ContactResource contact;
private transient DomainBase domain;
@BeforeEach
void beforeEach() throws Exception {
store = new BackupTestStore(fakeClock);
injectExtension.setStaticField(Ofy.class, "clock", fakeClock);
registry = newRegistry("tld1", "TLD1");
store.insertOrUpdate(registry);
contact = newContactResource("contact_1");
domain = newDomainBase("domain1.tld1", contact);
store.insertOrUpdate(contact, domain);
// Save persisted data for assertions.
registry = (Registry) store.loadAsOfyEntity(registry);
contact = (ContactResource) store.loadAsOfyEntity(contact);
domain = (DomainBase) store.loadAsOfyEntity(domain);
commitLogsDir = Files.createDirectory(tmpDir.resolve("commit_logs")).toFile();
firstCommitLogFile = store.saveCommitLogs(commitLogsDir.getAbsolutePath());
}
@AfterEach
void afterEach() throws Exception {
if (store != null) {
store.close();
store = null;
}
}
@Test
void getCommitLogFilePatterns() {
PCollection<String> patterns =
testPipeline.apply(
"Get CommitLog file patterns",
Transforms.getCommitLogFilePatterns(commitLogsDir.getAbsolutePath()));
ImmutableList<String> expectedPatterns =
ImmutableList.of(commitLogsDir.getAbsolutePath() + "/commit_diff_until_*");
PAssert.that(patterns).containsInAnyOrder(expectedPatterns);
testPipeline.run();
}
@Test
void getFilesByPatterns() {
PCollection<Metadata> fileMetas =
testPipeline
.apply(
"File patterns to metadata",
Create.of(commitLogsDir.getAbsolutePath() + "/commit_diff_until_*")
.withCoder(StringUtf8Coder.of()))
.apply(Transforms.getFilesByPatterns());
// Transform fileMetas to file names for assertions.
PCollection<String> fileNames =
fileMetas.apply(
"File metadata to path string",
ParDo.of(
new DoFn<Metadata, String>() {
@ProcessElement
public void processElement(
@Element Metadata metadata, OutputReceiver<String> out) {
out.output(metadata.resourceId().toString());
}
}));
ImmutableList<String> expectedFilenames =
ImmutableList.of(firstCommitLogFile.getAbsolutePath());
PAssert.that(fileNames).containsInAnyOrder(expectedFilenames);
testPipeline.run();
}
@Test
void filterCommitLogsByTime() throws IOException {
ImmutableList<String> commitLogFilenames =
ImmutableList.of(
"commit_diff_until_2000-01-01T00:00:00.000Z",
"commit_diff_until_2000-01-01T00:00:00.001Z",
"commit_diff_until_2000-01-01T00:00:00.002Z",
"commit_diff_until_2000-01-01T00:00:00.003Z",
"commit_diff_until_2000-01-01T00:00:00.004Z");
for (String name : commitLogFilenames) {
new File(commitLogsDir, name).createNewFile();
}
PCollection<String> filteredFilenames =
testPipeline
.apply(
"Get commitlog file patterns",
Transforms.getCommitLogFilePatterns(commitLogsDir.getAbsolutePath()))
.apply("Find commitlog files", Transforms.getFilesByPatterns())
.apply(
"Filtered by Time",
Transforms.filterCommitLogsByTime(
DateTime.parse("2000-01-01T00:00:00.001Z"),
DateTime.parse("2000-01-01T00:00:00.003Z")))
.apply(
"Extract path strings",
ParDo.of(
new DoFn<Metadata, String>() {
@ProcessElement
public void processElement(
@Element Metadata fileMeta, OutputReceiver<String> out) {
out.output(fileMeta.resourceId().getFilename());
}
}));
PAssert.that(filteredFilenames)
.containsInAnyOrder(
"commit_diff_until_2000-01-01T00:00:00.001Z",
"commit_diff_until_2000-01-01T00:00:00.002Z");
testPipeline.run();
}
@Test
void loadOneCommitLogFile() {
PCollection<VersionedEntity> entities =
testPipeline
.apply(
"Get CommitLog file patterns",
Transforms.getCommitLogFilePatterns(commitLogsDir.getAbsolutePath()))
.apply("Find CommitLogs", Transforms.getFilesByPatterns())
.apply(
Transforms.loadCommitLogsFromFiles(
ImmutableSet.of("Registry", "ContactResource", "DomainBase")));
InitSqlTestUtils.assertContainsExactlyElementsIn(
entities,
KV.of(fakeClock.nowUtc().getMillis() - 2, store.loadAsDatastoreEntity(registry)),
KV.of(fakeClock.nowUtc().getMillis() - 1, store.loadAsDatastoreEntity(contact)),
KV.of(fakeClock.nowUtc().getMillis() - 1, store.loadAsDatastoreEntity(domain)));
testPipeline.run();
}
@Test
void loadOneCommitLogFile_filterByKind() {
PCollection<VersionedEntity> entities =
testPipeline
.apply(
"Get CommitLog file patterns",
Transforms.getCommitLogFilePatterns(commitLogsDir.getAbsolutePath()))
.apply("Find CommitLogs", Transforms.getFilesByPatterns())
.apply(
Transforms.loadCommitLogsFromFiles(ImmutableSet.of("Registry", "ContactResource")));
InitSqlTestUtils.assertContainsExactlyElementsIn(
entities,
KV.of(fakeClock.nowUtc().getMillis() - 2, store.loadAsDatastoreEntity(registry)),
KV.of(fakeClock.nowUtc().getMillis() - 1, store.loadAsDatastoreEntity(contact)));
testPipeline.run();
}
}
@@ -1,295 +0,0 @@
// 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.beam.initsql;
import static google.registry.model.common.Cursor.CursorType.BRDA;
import static google.registry.model.common.Cursor.CursorType.RECURRING_BILLING;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.testing.DatabaseHelper.newRegistry;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.domain.DomainFlowUtils;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.common.Cursor;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.HostResource;
import google.registry.model.poll.PollMessage;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import org.joda.money.Money;
/**
* Sets up a test scenario in Datastore.
*
* <p>The {@link #initializeData} populates Datastore with test data, including {@link DomainBase},
* {@link DomainHistory}, and commit logs. The up-to-date version of the relevant entities are saved
* in public instance variables (e.g., {@link #domain} for easy access.
*/
public class DatastoreSetupHelper {
/**
* All kinds of entities to be set up in the Datastore. Must contain all kinds known to {@link
* InitSqlPipeline}.
*/
public static final ImmutableList<Class<?>> ALL_KINDS =
ImmutableList.of(
Registry.class,
Cursor.class,
Registrar.class,
ContactResource.class,
RegistrarContact.class,
DomainBase.class,
HostResource.class,
HistoryEntry.class,
AllocationToken.class,
BillingEvent.Recurring.class,
BillingEvent.OneTime.class,
BillingEvent.Cancellation.class,
PollMessage.class);
private final Path tmpDir;
private final FakeClock fakeClock;
public File exportRootDir;
public File exportDir;
public File commitLogDir;
public Registrar registrar1;
public Registrar registrar2;
public DomainBase domain;
public ContactResource contact1;
public ContactResource contact2;
public HostResource hostResource;
public DomainHistory historyEntry;
public Cursor globalCursor;
public Cursor tldCursor;
public DatastoreSetupHelper(Path tempDir, FakeClock fakeClock) {
this.tmpDir = tempDir;
this.fakeClock = fakeClock;
}
public DatastoreSetupHelper initializeData() throws Exception {
try (BackupTestStore store = new BackupTestStore(fakeClock)) {
exportRootDir = Files.createDirectory(tmpDir.resolve("exports")).toFile();
persistResource(newRegistry("com", "COM"));
registrar1 = persistResource(AppEngineExtension.makeRegistrar1());
registrar2 = persistResource(AppEngineExtension.makeRegistrar2());
Key<DomainBase> domainKey = Key.create(null, DomainBase.class, "4-COM");
hostResource =
persistResource(
new HostResource.Builder()
.setHostName("ns1.example.com")
.setSuperordinateDomain(VKey.from(domainKey))
.setRepoId("1-COM")
.setCreationRegistrarId(registrar1.getRegistrarId())
.setPersistedCurrentSponsorRegistrarId(registrar2.getRegistrarId())
.build());
contact1 =
persistResource(
new ContactResource.Builder()
.setContactId("contact_id1")
.setRepoId("2-COM")
.setCreationRegistrarId(registrar1.getRegistrarId())
.setPersistedCurrentSponsorRegistrarId(registrar2.getRegistrarId())
.build());
contact2 =
persistResource(
new ContactResource.Builder()
.setContactId("contact_id2")
.setRepoId("3-COM")
.setCreationRegistrarId(registrar1.getRegistrarId())
.setPersistedCurrentSponsorRegistrarId(registrar1.getRegistrarId())
.build());
persistSimpleResource(
new RegistrarContact.Builder()
.setParent(registrar1)
.setName("John Abused")
.setEmailAddress("johnabuse@example.com")
.setVisibleInWhoisAsAdmin(true)
.setVisibleInWhoisAsTech(false)
.setPhoneNumber("+1.2125551213")
.setFaxNumber("+1.2125551213")
.setTypes(ImmutableSet.of(RegistrarContact.Type.ABUSE, RegistrarContact.Type.ADMIN))
.build());
historyEntry =
persistResource(
new DomainHistory.Builder()
.setDomainRepoId(domainKey.getName())
.setModificationTime(fakeClock.nowUtc())
.setRegistrarId(registrar1.getRegistrarId())
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.build());
persistResource(
new AllocationToken.Builder().setToken("abc123").setTokenType(SINGLE_USE).build());
Key<DomainHistory> historyEntryKey = Key.create(historyEntry);
BillingEvent.OneTime onetimeBillEvent =
new BillingEvent.OneTime.Builder()
.setId(1)
.setReason(Reason.RENEW)
.setTargetId("example.com")
.setRegistrarId("TheRegistrar")
.setCost(Money.parse("USD 44.00"))
.setPeriodYears(4)
.setEventTime(fakeClock.nowUtc())
.setBillingTime(fakeClock.nowUtc())
.setParent(historyEntryKey)
.build();
persistResource(onetimeBillEvent);
Key<BillingEvent.OneTime> oneTimeBillKey = Key.create(onetimeBillEvent);
BillingEvent.Recurring recurringBillEvent =
new BillingEvent.Recurring.Builder()
.setId(2)
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.com")
.setRegistrarId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntryKey)
.build();
persistResource(recurringBillEvent);
VKey<BillingEvent.Recurring> recurringBillKey = recurringBillEvent.createVKey();
PollMessage.Autorenew autorenewPollMessage =
new PollMessage.Autorenew.Builder()
.setId(3L)
.setTargetId("example.com")
.setRegistrarId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.setMsg("Domain was auto-renewed.")
.setParent(historyEntry)
.build();
persistResource(autorenewPollMessage);
VKey<PollMessage.Autorenew> autorenewPollKey = autorenewPollMessage.createVKey();
PollMessage.OneTime oneTimePollMessage =
new PollMessage.OneTime.Builder()
.setId(1L)
.setParent(historyEntry)
.setEventTime(fakeClock.nowUtc())
.setRegistrarId("TheRegistrar")
.setMsg(DomainFlowUtils.COLLISION_MESSAGE)
.build();
persistResource(oneTimePollMessage);
VKey<PollMessage.OneTime> onetimePollKey = oneTimePollMessage.createVKey();
domain =
persistResource(
new DomainBase.Builder()
.setDomainName("example.com")
.setRepoId("4-COM")
.setCreationRegistrarId(registrar1.getRegistrarId())
.setLastEppUpdateTime(fakeClock.nowUtc())
.setLastEppUpdateRegistrarId(registrar2.getRegistrarId())
.setLastTransferTime(fakeClock.nowUtc())
.setStatusValues(
ImmutableSet.of(
StatusValue.CLIENT_DELETE_PROHIBITED,
StatusValue.SERVER_DELETE_PROHIBITED,
StatusValue.SERVER_TRANSFER_PROHIBITED,
StatusValue.SERVER_UPDATE_PROHIBITED,
StatusValue.SERVER_RENEW_PROHIBITED,
StatusValue.SERVER_HOLD))
.setRegistrant(contact1.createVKey())
.setContacts(
ImmutableSet.of(
DesignatedContact.create(
DesignatedContact.Type.ADMIN, contact2.createVKey())))
.setNameservers(ImmutableSet.of(hostResource.createVKey()))
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
.setPersistedCurrentSponsorRegistrarId(registrar2.getRegistrarId())
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
.setDsData(
ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.setLaunchNotice(
LaunchNotice.create("tcnid", "validatorId", START_OF_TIME, START_OF_TIME))
.setTransferData(
new DomainTransferData.Builder()
.setGainingRegistrarId(registrar1.getRegistrarId())
.setLosingRegistrarId(registrar2.getRegistrarId())
.setPendingTransferExpirationTime(fakeClock.nowUtc())
.setServerApproveEntities(
ImmutableSet.of(
VKey.from(oneTimeBillKey), recurringBillKey, autorenewPollKey))
.setServerApproveBillingEvent(VKey.from(oneTimeBillKey))
.setServerApproveAutorenewEvent(recurringBillKey)
.setServerApproveAutorenewPollMessage(autorenewPollKey)
.setTransferRequestTime(fakeClock.nowUtc().plusDays(1))
.setTransferStatus(TransferStatus.SERVER_APPROVED)
.setTransferRequestTrid(Trid.create("client-trid", "server-trid"))
.build())
.setDeletePollMessage(onetimePollKey)
.setAutorenewBillingEvent(recurringBillKey)
.setAutorenewPollMessage(autorenewPollKey)
.setSmdId("smdid")
.addGracePeriod(
GracePeriod.create(
GracePeriodStatus.ADD,
"4-COM",
fakeClock.nowUtc().plusDays(1),
"TheRegistrar",
null))
.build());
persistResource(
new BillingEvent.Cancellation.Builder()
.setReason(Reason.RENEW)
.setTargetId(domain.getDomainName())
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
.setEventTime(fakeClock.nowUtc())
.setBillingTime(fakeClock.nowUtc())
.setRecurringEventKey(recurringBillEvent.createVKey())
.setParent(historyEntryKey)
.build());
globalCursor = persistResource(Cursor.createGlobal(RECURRING_BILLING, fakeClock.nowUtc()));
tldCursor = persistResource(Cursor.create(BRDA, fakeClock.nowUtc(), Registry.get("com")));
exportDir = store.export(exportRootDir.getAbsolutePath(), ALL_KINDS, ImmutableSet.of());
commitLogDir = Files.createDirectory(tmpDir.resolve("commits")).toFile();
fakeClock.advanceOneMilli();
}
return this;
}
}
@@ -1,233 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.cloneAndSetAutoTimestamps;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.appengine.api.datastore.Entity;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.HostResource;
import google.registry.model.ofy.Ofy;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import org.joda.time.Instant;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link DomainBaseUtil}. */
public class DomainBaseUtilTest {
private final FakeClock fakeClock = new FakeClock(Instant.now());
private DomainBase domain;
private Entity domainEntity;
private Key<OneTime> oneTimeBillKey;
private VKey<BillingEvent.Recurring> recurringBillKey;
private Key<DomainBase> domainKey;
@RegisterExtension
AppEngineExtension appEngineExtension =
AppEngineExtension.builder().withDatastoreAndCloudSql().withClock(fakeClock).build();
@RegisterExtension InjectExtension injectExtension = new InjectExtension();
@BeforeEach
void beforeEach() {
injectExtension.setStaticField(Ofy.class, "clock", fakeClock);
createTld("com");
domainKey = Key.create(null, DomainBase.class, "4-COM");
VKey<HostResource> hostKey =
persistResource(
new HostResource.Builder()
.setHostName("ns1.example.com")
.setSuperordinateDomain(VKey.from(domainKey))
.setRepoId("1-COM")
.build())
.createVKey();
VKey<ContactResource> contact1Key =
persistResource(
new ContactResource.Builder()
.setContactId("contact_id1")
.setRepoId("2-COM")
.build())
.createVKey();
VKey<ContactResource> contact2Key =
persistResource(
new ContactResource.Builder()
.setContactId("contact_id2")
.setRepoId("3-COM")
.build())
.createVKey();
Key<HistoryEntry> historyEntryKey =
Key.create(
persistResource(
new DomainHistory.Builder()
.setDomainRepoId(domainKey.getName())
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setRegistrarId("TheRegistrar")
.setModificationTime(fakeClock.nowUtc().minusYears(1))
.build()));
oneTimeBillKey = Key.create(historyEntryKey, BillingEvent.OneTime.class, 1);
recurringBillKey = VKey.from(Key.create(historyEntryKey, BillingEvent.Recurring.class, 2));
VKey<PollMessage.Autorenew> autorenewPollKey =
VKey.from(Key.create(historyEntryKey, PollMessage.Autorenew.class, 3));
VKey<PollMessage.OneTime> onetimePollKey =
VKey.from(Key.create(historyEntryKey, PollMessage.OneTime.class, 1));
// Set up a new persisted domain entity.
domain =
persistResource(
cloneAndSetAutoTimestamps(
new DomainBase.Builder()
.setDomainName("example.com")
.setRepoId("4-COM")
.setCreationRegistrarId("a registrar")
.setLastEppUpdateTime(fakeClock.nowUtc())
.setLastEppUpdateRegistrarId("AnotherRegistrar")
.setLastTransferTime(fakeClock.nowUtc())
.setStatusValues(
ImmutableSet.of(
StatusValue.CLIENT_DELETE_PROHIBITED,
StatusValue.SERVER_DELETE_PROHIBITED,
StatusValue.SERVER_TRANSFER_PROHIBITED,
StatusValue.SERVER_UPDATE_PROHIBITED,
StatusValue.SERVER_RENEW_PROHIBITED,
StatusValue.SERVER_HOLD))
.setRegistrant(contact1Key)
.setContacts(
ImmutableSet.of(
DesignatedContact.create(DesignatedContact.Type.ADMIN, contact2Key)))
.setNameservers(ImmutableSet.of(hostKey))
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
.setPersistedCurrentSponsorRegistrarId("losing")
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
.setDsData(
ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.setLaunchNotice(
LaunchNotice.create("tcnid", "validatorId", START_OF_TIME, START_OF_TIME))
.setTransferData(
new DomainTransferData.Builder()
.setGainingRegistrarId("gaining")
.setLosingRegistrarId("losing")
.setPendingTransferExpirationTime(fakeClock.nowUtc())
.setServerApproveEntities(
ImmutableSet.of(
VKey.from(oneTimeBillKey), recurringBillKey, autorenewPollKey))
.setServerApproveBillingEvent(VKey.from(oneTimeBillKey))
.setServerApproveAutorenewEvent(recurringBillKey)
.setServerApproveAutorenewPollMessage(autorenewPollKey)
.setTransferRequestTime(fakeClock.nowUtc().plusDays(1))
.setTransferStatus(TransferStatus.SERVER_APPROVED)
.setTransferRequestTrid(Trid.create("client-trid", "server-trid"))
.build())
.setDeletePollMessage(onetimePollKey)
.setAutorenewBillingEvent(recurringBillKey)
.setAutorenewPollMessage(autorenewPollKey)
.setSmdId("smdid")
.addGracePeriod(
GracePeriod.create(
GracePeriodStatus.ADD,
"4-COM",
fakeClock.nowUtc().plusDays(1),
"registrar",
null))
.build()));
domainEntity = tm().transact(() -> auditedOfy().toEntity(domain));
}
@Test
void removeBillingAndPollAndHosts_allFkeysPresent() {
DomainBase domainTransformedByOfy =
domain
.asBuilder()
.setAutorenewBillingEvent(null)
.setAutorenewPollMessage(null)
.setNameservers(ImmutableSet.of())
.setDeletePollMessage(null)
.setTransferData(null)
.setGracePeriods(ImmutableSet.of())
.build();
DomainBase domainTransformedByUtil =
(DomainBase) auditedOfy().toPojo(DomainBaseUtil.removeBillingAndPollAndHosts(domainEntity));
// Compensates for the missing INACTIVE status.
domainTransformedByUtil = domainTransformedByUtil.asBuilder().build();
assertAboutImmutableObjects()
.that(domainTransformedByUtil)
.isEqualExceptFields(domainTransformedByOfy, "revisions", "updateTimestamp");
}
@Test
void removeBillingAndPollAndHosts_noFkeysPresent() {
DomainBase domainWithoutFKeys =
domain
.asBuilder()
.setAutorenewBillingEvent(null)
.setAutorenewPollMessage(null)
.setNameservers(ImmutableSet.of())
.setDeletePollMessage(null)
.setTransferData(null)
.setGracePeriods(ImmutableSet.of())
.build();
Entity entityWithoutFkeys = tm().transact(() -> auditedOfy().toEntity(domainWithoutFKeys));
DomainBase domainTransformedByUtil =
(DomainBase)
auditedOfy().toPojo(DomainBaseUtil.removeBillingAndPollAndHosts(entityWithoutFkeys));
// Compensates for the missing INACTIVE status.
domainTransformedByUtil = domainTransformedByUtil.asBuilder().build();
assertAboutImmutableObjects()
.that(domainTransformedByUtil)
.isEqualExceptFields(domainWithoutFKeys, "revisions", "updateTimestamp");
}
@Test
void removeBillingAndPollAndHosts_notDomainBase() {
Entity contactEntity =
tm().transact(() -> auditedOfy().toEntity(DatabaseHelper.newContactResource("contact")));
assertThrows(
IllegalArgumentException.class,
() -> DomainBaseUtil.removeBillingAndPollAndHosts(contactEntity));
}
}
@@ -1,201 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static google.registry.testing.DatabaseHelper.newContactResource;
import static google.registry.testing.DatabaseHelper.newDomainBase;
import static google.registry.testing.DatabaseHelper.newRegistry;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.backup.VersionedEntity;
import google.registry.beam.TestPipelineExtension;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.ofy.Ofy;
import google.registry.model.tld.Registry;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import java.io.File;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.Collections;
import org.apache.beam.sdk.coders.StringUtf8Coder;
import org.apache.beam.sdk.io.fs.MatchResult.Metadata;
import org.apache.beam.sdk.testing.PAssert;
import org.apache.beam.sdk.transforms.Create;
import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.ParDo;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollection;
import org.joda.time.DateTime;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
/**
* Unit tests for {@link Transforms} related to loading Datastore exports.
*
* <p>This class implements {@link Serializable} so that test {@link DoFn} classes may be inlined.
*/
class ExportLoadingTransformsTest implements Serializable {
private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z");
private static final ImmutableList<Class<?>> ALL_KINDS =
ImmutableList.of(Registry.class, ContactResource.class, DomainBase.class);
private static final ImmutableSet<String> ALL_KIND_STRS =
ALL_KINDS.stream().map(Key::getKind).collect(ImmutableSet.toImmutableSet());
@SuppressWarnings("WeakerAccess")
@TempDir
transient Path tmpDir;
@RegisterExtension final transient InjectExtension injectExtension = new InjectExtension();
@RegisterExtension
final transient JpaIntegrationTestExtension jpaIntegrationTestExtension =
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
@RegisterExtension
@Order(value = 1)
final transient DatastoreEntityExtension datastoreEntityExtension =
new DatastoreEntityExtension().allThreads(true);
@RegisterExtension
final transient TestPipelineExtension testPipeline =
TestPipelineExtension.create().enableAbandonedNodeEnforcement(true);
private FakeClock fakeClock;
private transient BackupTestStore store;
private File exportDir;
// Canned data:
private transient Registry registry;
private transient ContactResource contact;
private transient DomainBase domain;
@BeforeEach
void beforeEach() throws Exception {
fakeClock = new FakeClock(START_TIME);
store = new BackupTestStore(fakeClock);
injectExtension.setStaticField(Ofy.class, "clock", fakeClock);
registry = newRegistry("tld1", "TLD1");
store.insertOrUpdate(registry);
contact = newContactResource("contact_1");
domain = newDomainBase("domain1.tld1", contact);
store.insertOrUpdate(contact, domain);
// Save persisted data for assertions.
registry = (Registry) store.loadAsOfyEntity(registry);
contact = (ContactResource) store.loadAsOfyEntity(contact);
domain = (DomainBase) store.loadAsOfyEntity(domain);
exportDir = store.export(tmpDir.toAbsolutePath().toString(), ALL_KINDS, Collections.EMPTY_SET);
}
@AfterEach
void afterEach() throws Exception {
if (store != null) {
store.close();
store = null;
}
}
@Test
void getExportFilePatterns() {
PCollection<String> patterns =
testPipeline.apply(
"Get Datastore file patterns",
Transforms.getDatastoreExportFilePatterns(exportDir.getAbsolutePath(), ALL_KIND_STRS));
ImmutableList<String> expectedPatterns =
ImmutableList.of(
exportDir.getAbsolutePath() + "/all_namespaces/kind_Registry/output-*",
exportDir.getAbsolutePath() + "/all_namespaces/kind_DomainBase/output-*",
exportDir.getAbsolutePath() + "/all_namespaces/kind_ContactResource/output-*");
PAssert.that(patterns).containsInAnyOrder(expectedPatterns);
testPipeline.run();
}
@Test
void getFilesByPatterns() {
PCollection<Metadata> fileMetas =
testPipeline
.apply(
"File patterns to metadata",
Create.of(
exportDir.getAbsolutePath() + "/all_namespaces/kind_Registry/output-*",
exportDir.getAbsolutePath() + "/all_namespaces/kind_DomainBase/output-*",
exportDir.getAbsolutePath()
+ "/all_namespaces/kind_ContactResource/output-*")
.withCoder(StringUtf8Coder.of()))
.apply(Transforms.getFilesByPatterns());
// Transform fileMetas to file names for assertions.
PCollection<String> fileNames =
fileMetas.apply(
"File metadata to path string",
ParDo.of(
new DoFn<Metadata, String>() {
@ProcessElement
public void processElement(
@Element Metadata metadata, OutputReceiver<String> out) {
out.output(metadata.resourceId().toString());
}
}));
ImmutableList<String> expectedFilenames =
ImmutableList.of(
exportDir.getAbsolutePath() + "/all_namespaces/kind_Registry/output-0",
exportDir.getAbsolutePath() + "/all_namespaces/kind_DomainBase/output-0",
exportDir.getAbsolutePath() + "/all_namespaces/kind_ContactResource/output-0");
PAssert.that(fileNames).containsInAnyOrder(expectedFilenames);
testPipeline.run();
}
@Test
void loadDataFromFiles() {
PCollection<VersionedEntity> entities =
testPipeline
.apply(
"Get Datastore file patterns",
Transforms.getDatastoreExportFilePatterns(
exportDir.getAbsolutePath(), ALL_KIND_STRS))
.apply("Find Datastore files", Transforms.getFilesByPatterns())
.apply("Load from Datastore files", Transforms.loadExportDataFromFiles());
InitSqlTestUtils.assertContainsExactlyElementsIn(
entities,
KV.of(Transforms.EXPORT_ENTITY_TIME_STAMP, store.loadAsDatastoreEntity(registry)),
KV.of(Transforms.EXPORT_ENTITY_TIME_STAMP, store.loadAsDatastoreEntity(contact)),
KV.of(Transforms.EXPORT_ENTITY_TIME_STAMP, store.loadAsDatastoreEntity(domain)));
testPipeline.run();
}
}
@@ -1,67 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static google.registry.testing.truth.TextDiffSubject.assertWithMessageAboutUrlSource;
import com.google.common.io.Resources;
import google.registry.beam.TestPipelineExtension;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import org.apache.beam.runners.core.construction.renderer.PipelineDotRenderer;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Manages visualization of {@link InitSqlPipeline}. */
class InitSqlPipelineGraphTest {
private static final String GOLDEN_DOT_FILE = "pipeline_golden.dot";
private static final String[] OPTIONS_ARGS =
new String[] {
"--commitLogStartTimestamp=2000-01-01TZ",
"--commitLogEndTimestamp=2000-01-02TZ",
"--datastoreExportDir=/somedir",
"--commitLogDir=/someotherdir",
"--registryEnvironment=ALPHA"
};
private static final transient InitSqlPipelineOptions options =
PipelineOptionsFactory.fromArgs(OPTIONS_ARGS)
.withValidation()
.as(InitSqlPipelineOptions.class);
@RegisterExtension
final transient TestPipelineExtension testPipeline =
TestPipelineExtension.create().enableAbandonedNodeEnforcement(false);
@Test
void createPipeline_compareGraph() throws IOException {
new InitSqlPipeline(options).setupPipeline(testPipeline);
String dotString = PipelineDotRenderer.toDotString(testPipeline);
URL goldenDotUrl = Resources.getResource(InitSqlPipelineGraphTest.class, GOLDEN_DOT_FILE);
File outputFile = new File(new File(goldenDotUrl.getFile()).getParent(), "pipeline_curr.dot");
try (PrintStream ps = new PrintStream(outputFile)) {
ps.print(dotString);
}
assertWithMessageAboutUrlSource(
"InitSqlPipeline graph changed. Run :core:updateInitSqlPipelineGraph to update.")
.that(outputFile.toURI().toURL())
.hasSameContentAs(goldenDotUrl);
}
}
@@ -1,27 +0,0 @@
// Copyright 2020 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.beam.initsql;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link google.registry.beam.initsql.InitSqlPipelineOptions}. * */
public class InitSqlPipelineOptionsTest {
@Test
void registerToValidate() {
PipelineOptionsFactory.register(InitSqlPipelineOptions.class);
}
}
@@ -1,136 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import google.registry.beam.TestPipelineExtension;
import google.registry.model.common.Cursor;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.model.ofy.Ofy;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import java.nio.file.Path;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
/** Unit tests for {@link InitSqlPipeline}. */
class InitSqlPipelineTest {
private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z");
private FakeClock fakeClock = new FakeClock(START_TIME);
@RegisterExtension
@Order(Order.DEFAULT - 1)
final transient DatastoreEntityExtension datastore =
new DatastoreEntityExtension().allThreads(true);
@RegisterExtension final transient InjectExtension injectExtension = new InjectExtension();
@SuppressWarnings("WeakerAccess")
@TempDir
transient Path tmpDir;
@RegisterExtension
final transient TestPipelineExtension testPipeline =
TestPipelineExtension.create().enableAbandonedNodeEnforcement(true);
@RegisterExtension
final transient JpaIntegrationTestExtension database =
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
DatastoreSetupHelper setupHelper;
@BeforeEach
void beforeEach() throws Exception {
injectExtension.setStaticField(Ofy.class, "clock", fakeClock);
setupHelper = new DatastoreSetupHelper(tmpDir, fakeClock).initializeData();
}
@Test
void runPipeline() {
InitSqlPipelineOptions options =
PipelineOptionsFactory.fromArgs(
"--commitLogStartTimestamp=" + START_TIME,
"--commitLogEndTimestamp=" + fakeClock.nowUtc().plusMillis(1),
"--datastoreExportDir=" + setupHelper.exportDir.getAbsolutePath(),
"--commitLogDir=" + setupHelper.commitLogDir.getAbsolutePath())
.withValidation()
.as(InitSqlPipelineOptions.class);
InitSqlPipeline initSqlPipeline = new InitSqlPipeline(options);
initSqlPipeline.run(testPipeline).waitUntilFinish();
assertHostResourceEquals(
jpaTm().transact(() -> jpaTm().loadByKey(setupHelper.hostResource.createVKey())),
setupHelper.hostResource);
assertThat(jpaTm().transact(() -> jpaTm().loadAllOf(Registrar.class)))
.comparingElementsUsing(immutableObjectCorrespondence("lastUpdateTime"))
.containsExactly(setupHelper.registrar1, setupHelper.registrar2);
assertThat(jpaTm().transact(() -> jpaTm().loadAllOf(ContactResource.class)))
.comparingElementsUsing(immutableObjectCorrespondence("revisions", "updateTimestamp"))
.containsExactly(setupHelper.contact1, setupHelper.contact2);
assertDomainEquals(
jpaTm().transact(() -> jpaTm().loadByKey(setupHelper.domain.createVKey())),
setupHelper.domain);
assertThat(jpaTm().transact(() -> jpaTm().loadAllOf(Cursor.class)))
.comparingElementsUsing(immutableObjectCorrespondence())
.containsExactly(setupHelper.globalCursor, setupHelper.tldCursor);
}
private static void assertHostResourceEquals(HostResource actual, HostResource expected) {
assertAboutImmutableObjects()
.that(actual)
.isEqualExceptFields(expected, "superordinateDomain", "revisions", "updateTimestamp");
assertThat(actual.getSuperordinateDomain().getSqlKey())
.isEqualTo(expected.getSuperordinateDomain().getSqlKey());
}
private static void assertDomainEquals(DomainBase actual, DomainBase expected) {
assertAboutImmutableObjects()
.that(actual)
.isEqualExceptFields(
expected,
"revisions",
"updateTimestamp",
"autorenewPollMessage",
"deletePollMessage",
"nsHosts",
"gracePeriods",
"transferData");
assertThat(actual.getAdminContact().getSqlKey())
.isEqualTo(expected.getAdminContact().getSqlKey());
assertThat(actual.getRegistrant().getSqlKey()).isEqualTo(expected.getRegistrant().getSqlKey());
assertThat(actual.getNsHosts()).isEqualTo(expected.getNsHosts());
assertThat(actual.getAutorenewPollMessage().getOfyKey())
.isEqualTo(expected.getAutorenewPollMessage().getOfyKey());
assertThat(actual.getDeletePollMessage().getOfyKey())
.isEqualTo(expected.getDeletePollMessage().getOfyKey());
assertThat(actual.getUpdateTimestamp()).isEqualTo(expected.getUpdateTimestamp());
// TODO(weiminyu): check gracePeriods and transferData when it is easier to do
}
}
@@ -1,157 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static org.apache.beam.sdk.values.TypeDescriptors.kvs;
import static org.apache.beam.sdk.values.TypeDescriptors.strings;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityTranslator;
import com.google.appengine.api.datastore.Key;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.common.truth.Truth;
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
import google.registry.backup.VersionedEntity;
import java.io.Serializable;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.apache.beam.sdk.testing.PAssert;
import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.GroupByKey;
import org.apache.beam.sdk.transforms.MapElements;
import org.apache.beam.sdk.transforms.ParDo;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollection;
import org.apache.beam.sdk.values.TypeDescriptor;
/** Test helpers for populating SQL with Datastore backups. */
public final class InitSqlTestUtils {
// Generates unique ids to distinguish reused transforms.
private static final AtomicInteger TRANSFORM_ID_GEN = new AtomicInteger(0);
/** Converts a Datastore {@link Entity} to an Objectify entity. */
public static Object datastoreToOfyEntity(Entity entity) {
return auditedOfy().load().fromEntity(entity);
}
/** Serializes a Datastore {@link Entity} to byte array. */
public static byte[] entityToBytes(Entity entity) {
return EntityTranslator.convertToPb(entity).toByteArray();
}
/** Deserializes raw bytes into {@link Entity}. */
public static Entity bytesToEntity(byte[] bytes) {
EntityProto proto = new EntityProto();
proto.parseFrom(bytes);
return EntityTranslator.createFromPb(proto);
}
/**
* Asserts that the {@code actual} {@link Collection} of {@link VersionedEntity VersionedEntities}
* contains exactly the same elements in the {@code expected} array.
*
* <p>Each {@code expected} {@link KV key-value pair} refers to a versioned state of an Ofy
* entity. The {@link KV#getKey key} is the timestamp, while the {@link KV#getValue value} is
* either a Datastore {@link Entity} (for an existing entity) or a Datastore {@link Key} (for a
* deleted entity).
*
* <p>The {@Entity} instances in both actual and expected data are converted to Objectify entities
* so that value-equality checks can be performed. Datastore {@link Entity#equals Entity's equals
* method} only checks key-equality.
*/
@SafeVarargs
public static void assertContainsExactlyElementsIn(
Collection<VersionedEntity> actual, KV<Long, Serializable>... expected) {
assertThat(actual.stream().map(InitSqlTestUtils::rawEntityToOfyWithTimestamp))
.containsExactlyElementsIn(
Stream.of(expected)
.map(InitSqlTestUtils::expectedToOfyWithTimestamp)
.collect(ImmutableList.toImmutableList()));
}
/**
* Asserts that the {@code actual} {@link PCollection} of {@link VersionedEntity
* VersionedEntities} contains exactly the same elements in the {@code expected} array.
*
* <p>This method makes assertions in the pipeline and only use {@link PAssert} on the result.
* This way it supports assertions on Objectify entities, which {@code PAssert} cannot do ( since
* we have not implemented Coders for them). Compared with PAssert-compatible options like {@code
* google.registry.tools.EntityWrapper} or {@link EntityProto}, Objectify entities in Java give
* better-formatted error messages when assertions fail.
*
* <p>Each {@code expected} {@link KV key-value pair} refers to a versioned state of an Ofy
* entity. The {@link KV#getKey key} is the timestamp, while the {@link KV#getValue value} is
* either a Datastore {@link Entity} (for an existing entity) or a Datastore {@link Key} (for a
* deleted entity).
*
* <p>The {@Entity} instances in both actual and expected data are converted to Objectify entities
* so that value-equality checks can be performed. Datastore {@link Entity#equals Entity's equals
* method} only checks key-equality.
*/
@SafeVarargs
public static void assertContainsExactlyElementsIn(
PCollection<VersionedEntity> actual, KV<Long, Serializable>... expected) {
PCollection<String> errMsgs =
actual
.apply(
"MapElements_" + TRANSFORM_ID_GEN.getAndIncrement(),
MapElements.into(kvs(strings(), TypeDescriptor.of(VersionedEntity.class)))
.via(rawEntity -> KV.of("The One Key", rawEntity)))
.apply("GroupByKey_" + TRANSFORM_ID_GEN.getAndIncrement(), GroupByKey.create())
.apply(
"assertContainsExactlyElementsIn_" + TRANSFORM_ID_GEN.getAndIncrement(),
ParDo.of(
new DoFn<KV<String, Iterable<VersionedEntity>>, String>() {
@ProcessElement
public void processElement(
@Element KV<String, Iterable<VersionedEntity>> input,
OutputReceiver<String> out) {
ImmutableList<KV<Long, Object>> actual =
Streams.stream(input.getValue())
.map(InitSqlTestUtils::rawEntityToOfyWithTimestamp)
.collect(ImmutableList.toImmutableList());
try {
Truth.assertThat(actual)
.containsExactlyElementsIn(
Stream.of(expected)
.map(InitSqlTestUtils::expectedToOfyWithTimestamp)
.collect(ImmutableList.toImmutableList()));
} catch (AssertionError e) {
out.output(e.toString());
}
}
}));
PAssert.that(errMsgs).empty();
}
private static KV<Long, Object> rawEntityToOfyWithTimestamp(VersionedEntity rawEntity) {
return KV.of(
rawEntity.commitTimeMills(),
rawEntity.getEntity().map(InitSqlTestUtils::datastoreToOfyEntity).orElse(rawEntity.key()));
}
private static KV<Long, Object> expectedToOfyWithTimestamp(KV<Long, Serializable> kv) {
return KV.of(
kv.getKey(),
kv.getValue() instanceof Key
? kv.getValue()
: datastoreToOfyEntity((Entity) kv.getValue()));
}
}
@@ -1,190 +0,0 @@
// Copyright 2020 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.beam.initsql;
import static google.registry.testing.DatabaseHelper.newContactResource;
import static google.registry.testing.DatabaseHelper.newDomainBase;
import static google.registry.testing.DatabaseHelper.newRegistry;
import com.google.appengine.api.datastore.Entity;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.beam.TestPipelineExtension;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainBase;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.ofy.Ofy;
import google.registry.model.tld.Registry;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollectionTuple;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
/**
* Unit test for {@link Transforms#loadDatastoreSnapshot}.
*
* <p>The test setup involves three entities, one Registry, one Domain, and two Contacts. Events
* happen in the following order:
*
* <ol>
* <li>Registry and a filler Contact are inserted to Datastore.
* <li>A CommitLog is persisted.
* <li>Registry is updated.
* <li>Another Contact and Domain are inserted into Datastore.
* <li>Datastore is exported, but misses the newly inserted Contact.
* <li>Filler Contact is deleted.
* <li>A second CommitLog is persisted.
* <li>Domain is updated in the Datastore.
* <li>The third and last CommitLog is persisted.
* </ol>
*
* The final snapshot includes Registry, Domain, and Contact. This scenario verifies that:
*
* <ul>
* <li>Incremental changes committed before an export does not override the exported valie.
* <li>Entity missed by an export can be recovered from later CommitLogs.
* <li>Multiple changes to an entity is applied in order.
* <li>Deletes are properly handled.
* </ul>
*/
class LoadDatastoreSnapshotTest {
private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z");
private static final ImmutableList<Class<?>> ALL_KINDS =
ImmutableList.of(Registry.class, ContactResource.class, DomainBase.class);
private static final ImmutableSet<String> ALL_KIND_STRS =
ALL_KINDS.stream().map(Key::getKind).collect(ImmutableSet.toImmutableSet());
@SuppressWarnings("WeakerAccess")
@TempDir
transient Path tmpDir;
@RegisterExtension final transient InjectExtension injectExtension = new InjectExtension();
@RegisterExtension
final transient JpaIntegrationTestExtension jpaIntegrationTestExtension =
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
@RegisterExtension
@Order(value = 1)
final transient DatastoreEntityExtension datastoreEntityExtension =
new DatastoreEntityExtension().allThreads(true);
@RegisterExtension
final transient TestPipelineExtension testPipeline =
TestPipelineExtension.create().enableAbandonedNodeEnforcement(true);
private FakeClock fakeClock;
private File exportRootDir;
private File exportDir;
private File commitLogsDir;
// Canned data:
private transient Entity dsRegistry;
private transient Entity dsContact;
private transient Entity dsDomain;
private transient DateTime registryLastUpdateTime;
private transient DateTime contactLastUpdateTime;
private transient DateTime domainLastUpdateTime;
@BeforeEach
void beforeEach() throws Exception {
fakeClock = new FakeClock(START_TIME);
try (BackupTestStore store = new BackupTestStore(fakeClock)) {
injectExtension.setStaticField(Ofy.class, "clock", fakeClock);
exportRootDir = Files.createDirectory(tmpDir.resolve("export_root")).toFile();
commitLogsDir = Files.createDirectory(tmpDir.resolve("commit_logs")).toFile();
Registry registry = newRegistry("tld1", "TLD1");
ContactResource fillerContact = newContactResource("contact_filler");
store.insertOrUpdate(registry, fillerContact);
store.saveCommitLogs(commitLogsDir.getAbsolutePath());
registry =
registry
.asBuilder()
.setCreateBillingCost(registry.getStandardCreateCost().plus(1.0d))
.build();
registryLastUpdateTime = fakeClock.nowUtc();
store.insertOrUpdate(registry);
ContactResource contact = newContactResource("contact");
DomainBase domain = newDomainBase("domain1.tld1", contact);
contactLastUpdateTime = fakeClock.nowUtc();
store.insertOrUpdate(contact, domain);
exportDir =
store.export(
exportRootDir.getAbsolutePath(), ALL_KINDS, ImmutableSet.of(Key.create(contact)));
store.delete(fillerContact);
store.saveCommitLogs(commitLogsDir.getAbsolutePath());
domain =
domain
.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("NewPass")))
.build();
domainLastUpdateTime = fakeClock.nowUtc();
store.insertOrUpdate(domain);
store.saveCommitLogs(commitLogsDir.getAbsolutePath());
fakeClock.advanceOneMilli();
// Save persisted data for assertions.
dsRegistry = store.loadAsDatastoreEntity(registry);
dsContact = store.loadAsDatastoreEntity(contact);
dsDomain = store.loadAsDatastoreEntity(domain);
}
}
@Test
void loadDatastoreSnapshot() {
PCollectionTuple snapshot =
testPipeline.apply(
Transforms.loadDatastoreSnapshot(
exportDir.getAbsolutePath(),
commitLogsDir.getAbsolutePath(),
START_TIME,
fakeClock.nowUtc(),
ALL_KIND_STRS));
InitSqlTestUtils.assertContainsExactlyElementsIn(
snapshot.get(Transforms.createTagForKind("DomainBase")),
KV.of(domainLastUpdateTime.getMillis(), dsDomain));
InitSqlTestUtils.assertContainsExactlyElementsIn(
snapshot.get(Transforms.createTagForKind("Registry")),
KV.of(registryLastUpdateTime.getMillis(), dsRegistry));
InitSqlTestUtils.assertContainsExactlyElementsIn(
snapshot.get(Transforms.createTagForKind("ContactResource")),
KV.of(contactLastUpdateTime.getMillis(), dsContact));
testPipeline.run();
}
}
@@ -1,64 +0,0 @@
// 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.beam.initsql;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.beam.initsql.Transforms.repairBadData;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.newDomainBase;
import static google.registry.testing.DatabaseHelper.newHostResource;
import com.google.appengine.api.datastore.Entity;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.testing.AppEngineExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link Transforms}. */
public class TransformsTest {
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@BeforeEach
void beforeEach() {
createTld("tld");
}
@Test
void testRepairBadData_canonicalizesDomainName() {
DomainBase domain = newDomainBase("foobar.tld");
Entity entity = ofyTm().transact(() -> auditedOfy().toEntity(domain));
entity.setIndexedProperty("fullyQualifiedDomainName", "FOOBäR.TLD");
assertThat(((DomainBase) auditedOfy().toPojo(repairBadData(entity))).getDomainName())
.isEqualTo("xn--foobr-jra.tld");
}
@Test
void testRepairBadData_canonicalizesHostName() {
HostResource host = newHostResource("baz.foobar.tld");
Entity entity = ofyTm().transact(() -> auditedOfy().toEntity(host));
entity.setIndexedProperty(
"fullyQualifiedHostName", "b̴̹͔͓̣̭̫͇͕̻̬̱͇͗͌́̆̋͒a̶̬̖͚̋̈́̽̇͝͠z̵͠.FOOBäR.TLD");
assertThat(((HostResource) auditedOfy().toPojo(repairBadData(entity))).getHostName())
.isEqualTo(
"xn--baz-kdcb2ajgzb4jtg6doej4e6b9am7c7b6c5nd4k7gpa2a9a7dufyewec.xn--foobr-jra.tld");
}
}
@@ -51,7 +51,6 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.TestDataHelper;
import google.registry.testing.TmOverrideExtension;
import google.registry.util.ResourceUtils;
import java.io.File;
import java.nio.file.Files;
@@ -95,10 +94,6 @@ class InvoicingPipelineTest {
final JpaIntegrationTestExtension database =
new JpaTestExtensions.Builder().withClock(new FakeClock()).buildIntegrationTestExtension();
@RegisterExtension
@Order(Order.DEFAULT + 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
@TempDir Path tmpDir;
private static final String BILLING_BUCKET_URL = "billing_bucket";
@@ -282,7 +277,6 @@ class InvoicingPipelineTest {
@Test
void testSuccess_fullSqlPipeline() throws Exception {
setupCloudSql();
options.setDatabase("CLOUD_SQL");
InvoicingPipeline invoicingPipeline = new InvoicingPipeline(options);
invoicingPipeline.setupPipeline(pipeline);
pipeline.run(options).waitUntilFinish();
@@ -87,7 +87,6 @@ import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeKeyringModule;
import google.registry.testing.TmOverrideExtension;
import java.io.IOException;
import java.util.function.Function;
import java.util.regex.Matcher;
@@ -166,10 +165,6 @@ public class RdePipelineTest {
final JpaIntegrationTestExtension database =
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
@RegisterExtension
@Order(Order.DEFAULT + 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
@RegisterExtension
final TestPipelineExtension pipeline =
TestPipelineExtension.fromOptions(options).enableAbandonedNodeEnforcement(true);
@@ -42,7 +42,6 @@ import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.persistence.transaction.TransactionManagerFactory;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.TmOverrideExtension;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.joda.time.DateTime;
import org.joda.time.Duration;
@@ -70,10 +69,6 @@ public class ResaveAllEppResourcesPipelineTest {
final JpaIntegrationTestExtension database =
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
@RegisterExtension
@Order(Order.DEFAULT + 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
private final ResaveAllEppResourcesPipelineOptions options =
PipelineOptionsFactory.create().as(ResaveAllEppResourcesPipelineOptions.class);
@@ -50,7 +50,6 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeSleeper;
import google.registry.testing.TmOverrideExtension;
import google.registry.util.ResourceUtils;
import google.registry.util.Retrier;
import java.io.File;
@@ -128,10 +127,6 @@ class Spec11PipelineTest {
final JpaIntegrationTestExtension database =
new JpaTestExtensions.Builder().withClock(new FakeClock()).buildIntegrationTestExtension();
@RegisterExtension
@Order(Order.DEFAULT + 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
private final Spec11PipelineOptions options =
PipelineOptionsFactory.create().as(Spec11PipelineOptions.class);
@@ -146,7 +141,6 @@ class Spec11PipelineTest {
options.setDate(DATE);
options.setSafeBrowsingApiKey(SAFE_BROWSING_API_KEY);
options.setReportingBucketUrl(reportingBucketUrl.getAbsolutePath());
options.setDatabase("DATASTORE");
threatMatches =
pipeline.apply(
Create.of(
@@ -199,7 +193,6 @@ class Spec11PipelineTest {
@Test
void testSuccess_fullSqlPipeline() throws Exception {
setupCloudSql();
options.setDatabase("CLOUD_SQL");
EvaluateSafeBrowsingFn safeBrowsingFn =
new EvaluateSafeBrowsingFn(
SAFE_BROWSING_API_KEY,
@@ -25,7 +25,6 @@ import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptio
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
@@ -33,10 +32,8 @@ import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFi
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
import google.registry.flows.exceptions.ResourceCreateContentionException;
import google.registry.model.contact.ContactResource;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.joda.time.DateTime;
/** Unit tests for {@link ContactCreateFlow}. */
@@ -137,12 +134,4 @@ class ContactCreateFlowTest extends ResourceFlowTestCase<ContactCreateFlow, Cont
runFlow();
assertIcannReportingActivityFieldLogged("srs-cont-create");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -34,7 +34,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -52,10 +51,8 @@ import google.registry.model.tld.Registry;
import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -224,24 +221,6 @@ class ContactDeleteFlowTest extends ResourceFlowTestCase<ContactDeleteFlow, Cont
assertIcannReportingActivityFieldLogged("srs-cont-delete");
}
@TestOfyOnly
void testModification_duringNoAsyncPhase() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
private void assertSqlDeleteSuccess(HistoryEntry.Type... historyEntryTypes) throws Exception {
assertThat(reloadResourceByForeignKey()).isNull();
assertAboutContacts()
@@ -26,7 +26,6 @@ import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptio
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -42,10 +41,8 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link ContactTransferApproveFlow}. */
@@ -269,12 +266,4 @@ class ContactTransferApproveFlowTest
runFlow();
assertIcannReportingActivityFieldLogged("srs-cont-transfer-approve");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -25,7 +25,6 @@ import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptio
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -39,10 +38,8 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link ContactTransferCancelFlow}. */
@@ -255,12 +252,4 @@ class ContactTransferCancelFlowTest
runFlow();
assertIcannReportingActivityFieldLogged("srs-cont-transfer-cancel");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -25,7 +25,6 @@ import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptio
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -41,10 +40,8 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link ContactTransferRejectFlow}. */
@@ -268,12 +265,4 @@ class ContactTransferRejectFlowTest
runFlow();
assertIcannReportingActivityFieldLogged("srs-cont-transfer-reject");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -35,7 +35,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -52,10 +51,8 @@ import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.ContactTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -317,12 +314,4 @@ class ContactTransferRequestFlowTest
runFlow();
assertIcannReportingActivityFieldLogged("srs-cont-transfer-request");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -27,7 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException;
@@ -43,10 +42,8 @@ import google.registry.model.contact.ContactResource;
import google.registry.model.contact.PostalInfo;
import google.registry.model.contact.PostalInfo.Type;
import google.registry.model.eppcommon.StatusValue;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
/** Unit tests for {@link ContactUpdateFlow}. */
@DualDatabaseTest
@@ -430,13 +427,4 @@ class ContactUpdateFlowTest extends ResourceFlowTestCase<ContactUpdateFlow, Cont
runFlow();
assertIcannReportingActivityFieldLogged("srs-cont-update");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -75,7 +75,6 @@ import com.google.common.collect.Ordering;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.ExtensionManager.UndeclaredServiceExtensionException;
@@ -175,11 +174,9 @@ import google.registry.model.tld.Registry.TldState;
import google.registry.model.tld.Registry.TldType;
import google.registry.monitoring.whitebox.EppMetric;
import google.registry.persistence.VKey;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TaskQueueHelper.TaskMatcher;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Optional;
@@ -845,15 +842,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
doSuccessfulTest();
}
@TestOfyOnly
void testSuccess_inNoAsyncPhase() throws Exception {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
persistContactsAndHosts();
runFlowAssertResponse(
loadFile("domain_create_response_noasync.xml", ImmutableMap.of("DOMAIN", "example.tld")));
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@TestOfyAndSql
void testSuccess_maxNumberOfNameservers() throws Exception {
setEppInput("domain_create_13_nameservers.xml");
@@ -2611,15 +2599,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
assertThat(eppMetric.getCommandName()).hasValue("DomainCreate");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
persistContactsAndHosts();
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@TestOfyAndSql
void testGetRenewalPriceInfo_isAnchorTenantWithoutToken_returnsNonPremiumAndNullPrice() {
assertThat(
@@ -67,7 +67,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.batch.ResaveEntityAction;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowUtils.NotLoggedInException;
@@ -102,10 +101,8 @@ import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import java.util.Map;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -1244,13 +1241,4 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
EppException thrown = assertThrows(UnimplementedExtensionException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() throws Exception {
setUpSuccessfulTest();
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -43,7 +43,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
@@ -77,11 +76,9 @@ import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.SetClockExtension;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import java.util.Map;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -902,20 +899,4 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, DomainBa
TransactionReportField.netRenewsFieldFromYears(5),
1));
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() throws Exception {
persistDomain();
DomainBase domain = reloadResourceByForeignKey();
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(domain.getRegistrationExpirationTime().minusYears(1))
.build());
clock.setTo(clock.nowUtc().minusSeconds(2));
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -42,7 +42,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
@@ -76,10 +75,8 @@ import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import java.util.Map;
import java.util.Optional;
import org.joda.money.Money;
@@ -801,13 +798,4 @@ class DomainRestoreRequestFlowTest
assertThat(thrown).hasMessageThat().contains("domain restore reports are not supported");
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() throws Exception {
persistPendingDeleteDomain();
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -43,7 +43,6 @@ import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering;
import com.google.common.collect.Streams;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -74,10 +73,8 @@ import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import java.util.Arrays;
import java.util.stream.Stream;
import org.joda.money.Money;
@@ -678,12 +675,4 @@ class DomainTransferApproveFlowTest
domain.getRegistrationExpirationTime());
assertHistoryEntriesDoNotContainTransferBillingEventsOrGracePeriods();
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -37,7 +37,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -57,10 +56,8 @@ import google.registry.model.tld.Registry;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
@@ -420,12 +417,4 @@ class DomainTransferCancelFlowTest
assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly(previousSuccessRecord.asBuilder().setReportAmount(-1).build());
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -37,7 +37,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -59,10 +58,8 @@ import google.registry.model.tld.Registry;
import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
@@ -388,12 +385,4 @@ class DomainTransferRejectFlowTest
previousSuccessRecord.asBuilder().setReportAmount(-1).build(),
DomainTransactionRecord.create("tld", clock.nowUtc(), TRANSFER_NACKED, 1));
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -60,7 +60,6 @@ import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import google.registry.batch.ResaveEntityAction;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
@@ -108,10 +107,8 @@ import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
@@ -1567,15 +1564,4 @@ class DomainTransferRequestFlowTest
DomainTransactionRecord.create(
"tld", clock.nowUtc().plusDays(5), TRANSFER_SUCCESSFUL, 1));
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
setupDomain("example", "tld");
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown =
assertThrows(
ReadOnlyModeEppException.class, () -> doFailingTest("domain_transfer_request.xml"));
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -59,7 +59,6 @@ import com.google.common.collect.ImmutableSortedMap;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowUtils.NotLoggedInException;
@@ -106,10 +105,8 @@ import google.registry.model.poll.PendingActionNotificationResponse.DomainPendin
import google.registry.model.poll.PollMessage;
import google.registry.model.tld.Registry;
import google.registry.persistence.VKey;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import java.util.Optional;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -1745,14 +1742,4 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
runFlowAsSuperuser();
assertAboutDomains().that(reloadResourceByForeignKey()).hasNoAutorenewEndTime();
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() throws Exception {
persistReferencedEntities();
persistDomain();
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -35,7 +35,6 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.IpAddressVersionMismatchException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowTestCase;
@@ -55,10 +54,8 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.joda.time.DateTime;
/** Unit tests for {@link HostCreateFlow}. */
@@ -329,12 +326,4 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
runFlow();
assertIcannReportingActivityFieldLogged("srs-host-create");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -34,7 +34,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
@@ -51,10 +50,8 @@ import google.registry.model.reporting.HistoryEntry.Type;
import google.registry.model.tld.Registry;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -310,24 +307,6 @@ class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, HostResour
assertIcannReportingActivityFieldLogged("srs-host-delete");
}
@TestOfyOnly
void testModification_duringReadOnlyPhase() {
persistActiveHost("ns1.example.tld");
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@TestOfyOnly
void testModification_duringNoAsyncPhase() {
persistActiveHost("ns1.example.tld");
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
private void assertSqlDeleteSuccess(boolean isSubordinate) throws Exception {
assertThat(reloadResourceByForeignKey()).isNull();
HostResource deletedHost = reloadResourceByForeignKey(clock.nowUtc().minusMillis(1));
@@ -47,7 +47,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.InetAddresses;
import google.registry.flows.EppException;
import google.registry.flows.EppException.ReadOnlyModeEppException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowTestCase;
@@ -79,11 +78,9 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TaskQueueHelper.TaskMatcher;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
@@ -1335,50 +1332,4 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, HostResour
runFlow();
assertIcannReportingActivityFieldLogged("srs-host-update");
}
@TestOfyOnly
void testSuccess_nonHostRename_inNoAsyncPhase_succeeds() throws Exception {
setEppInput("host_update_name_unchanged.xml");
createTld("tld");
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
DomainBase domain = persistActiveDomain("example.tld");
HostResource oldHost = persistActiveSubordinateHost(oldHostName(), domain);
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("generic_success_response.xml"));
// The example xml doesn't do a host rename, so reloading the host should work.
assertAboutHosts()
.that(reloadResourceByForeignKey())
.hasLastSuperordinateChange(oldHost.getLastSuperordinateChange())
.and()
.hasSuperordinateDomain(domain.createVKey())
.and()
.hasPersistedCurrentSponsorRegistrarId("TheRegistrar")
.and()
.hasLastTransferTime(null)
.and()
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.HOST_UPDATE);
assertDnsTasksEnqueued("ns1.example.tld");
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@TestOfyOnly
void testRename_duringNoAsyncPhase_fails() throws Exception {
createTld("tld");
persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld"));
DatabaseHelper.setMigrationScheduleToDatastorePrimaryNoAsync(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@TestOfyOnly
void testModification_duringReadOnlyPhase_fails() throws Exception {
createTld("tld");
persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld"));
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(clock);
EppException thrown = assertThrows(ReadOnlyModeEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -36,6 +36,7 @@ import google.registry.flows.session.LoginFlow.UnsupportedLanguageException;
import google.registry.model.eppoutput.EppOutput;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.testing.DatabaseHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -123,7 +124,8 @@ public abstract class LoginFlowTestCase extends FlowTestCase<LoginFlow> {
@Test
void testFailure_unknownRegistrar() {
deleteResource(getRegistrarBuilder().build());
registrar.getContacts().forEach(DatabaseHelper::deleteResource);
deleteResource(registrar);
doFailingTest("login_valid.xml", BadRegistrarIdException.class);
}
@@ -42,6 +42,7 @@ import google.registry.model.domain.DomainHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.TmOverrideExtension;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
@@ -52,6 +53,7 @@ import java.util.Set;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -60,6 +62,10 @@ class ChildEntityInputTest {
private static final DateTime now = DateTime.now(DateTimeZone.UTC);
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@@ -24,12 +24,14 @@ import google.registry.model.ofy.CommitLogBucket;
import google.registry.model.ofy.CommitLogManifest;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.TmOverrideExtension;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -44,6 +46,10 @@ final class CommitLogManifestInputTest {
private static final DateTime DATE_TIME_NEW = DateTime.parse("2016-12-19T12:01Z");
private static final DateTime DATE_TIME_NEW2 = DateTime.parse("2017-12-19T12:00Z");
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@@ -37,6 +37,7 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.model.index.EppResourceIndex;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.TmOverrideExtension;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
@@ -44,6 +45,7 @@ import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.Set;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -52,6 +54,10 @@ class EppResourceInputsTest {
private static final double EPSILON = 0.0001;
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@@ -22,7 +22,6 @@ import static org.joda.time.DateTimeZone.UTC;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.common.CrossTldSingleton;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
@@ -43,7 +42,6 @@ public class CreateAutoTimestampTest {
/** Timestamped class. */
@Entity(name = "CatTestEntity")
@EntityForTesting
@javax.persistence.Entity
public static class CreateAutoTimestampTestObject extends CrossTldSingleton {
@Ignore @javax.persistence.Id long id = SINGLETON_ID;
@@ -19,7 +19,6 @@ import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObject.cloneEmptyToNull;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableList;
@@ -29,7 +28,6 @@ import com.google.common.collect.Iterables;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.testing.AppEngineExtension;
import google.registry.util.CidrAddressBlock;
import java.lang.reflect.Field;
@@ -51,6 +49,7 @@ public class ImmutableObjectTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
.withDatastoreAndCloudSql()
.withJpaUnitTestEntities(ValueObject.class)
.withOfyTestEntities(ValueObject.class)
.build();
@@ -279,10 +278,9 @@ public class ImmutableObjectTest {
/** Simple subclass of ImmutableObject. */
@Entity
@EntityForTesting
@javax.persistence.Entity
public static class ValueObject extends ImmutableObject {
@Id
long id;
@Id @javax.persistence.Id long id;
String value;
@@ -294,32 +292,6 @@ public class ImmutableObjectTest {
}
}
@Test
void testToHydratedString_skipsDoNotHydrate() {
RootObject root = new RootObject();
root.hydrateMe = Key.create(persistResource(ValueObject.create(1, "foo")));
root.skipMe = Key.create(persistResource(ValueObject.create(2, "bar")));
String hydratedString = root.toHydratedString();
assertThat(hydratedString).contains("foo");
assertThat(hydratedString).doesNotContain("bar");
}
@Test
void testToHydratedString_expandsMaps() {
RootObject root = new RootObject();
root.map = ImmutableMap.of("foo", Key.create(persistResource(ValueObject.create(1, "bar"))));
String hydratedString = root.toHydratedString();
assertThat(hydratedString).contains("foo");
assertThat(hydratedString).contains("bar");
}
@Test
void testToHydratedString_expandsCollections() {
RootObject root = new RootObject();
root.set = ImmutableSet.of(Key.create(persistResource(ValueObject.create(1, "foo"))));
assertThat(root.toHydratedString()).contains("foo");
}
@Test
void testInsignificantFields() {
HasInsignificantFields instance1 =
@@ -23,7 +23,6 @@ import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.common.CrossTldSingleton;
import google.registry.model.ofy.Ofy;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DualDatabaseTest;
@@ -59,7 +58,6 @@ public class UpdateAutoTimestampTest {
/** Timestamped class. */
@Entity(name = "UatTestEntity")
@javax.persistence.Entity
@EntityForTesting
public static class UpdateAutoTimestampTestObject extends CrossTldSingleton {
@Ignore @javax.persistence.Id long id = SINGLETON_ID;
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null);
@@ -1,217 +0,0 @@
// 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.model.common;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_ONLY;
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY;
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_NO_ASYNC;
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_READ_ONLY;
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_ONLY;
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_PRIMARY;
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_PRIMARY_READ_ONLY;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.EntityTestCase;
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.persistence.transaction.TransactionManagerFactory.ReadOnlyModeException;
import google.registry.testing.DatabaseHelper;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Tests for {@link DatabaseMigrationStateSchedule}. */
public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
@BeforeEach
void beforeEach() {
fakeClock.setAutoIncrementByOneMilli();
}
@AfterEach
void afterEach() {
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@Test
void testEmpty_returnsDatastoreOnlyMap() {
assertThat(DatabaseMigrationStateSchedule.getUncached())
.isEqualTo(DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP);
}
@Test
void testValidTransitions() {
// First, verify that no-ops are safe
for (MigrationState migrationState : MigrationState.values()) {
runValidTransition(migrationState, migrationState);
}
// Next, the transitions that will actually cause a change
runValidTransition(DATASTORE_ONLY, DATASTORE_PRIMARY);
runValidTransition(DATASTORE_PRIMARY, DATASTORE_ONLY);
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_NO_ASYNC);
runValidTransition(DATASTORE_PRIMARY_NO_ASYNC, DATASTORE_PRIMARY_READ_ONLY);
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_ONLY);
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_PRIMARY);
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, DATASTORE_PRIMARY_NO_ASYNC);
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY_READ_ONLY);
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY);
runValidTransition(SQL_PRIMARY_READ_ONLY, DATASTORE_PRIMARY_READ_ONLY);
runValidTransition(SQL_PRIMARY_READ_ONLY, SQL_PRIMARY);
runValidTransition(SQL_PRIMARY, SQL_PRIMARY_READ_ONLY);
runValidTransition(SQL_PRIMARY, SQL_ONLY);
runValidTransition(SQL_ONLY, SQL_PRIMARY);
}
@Test
void testInvalidTransitions() {
runInvalidTransition(DATASTORE_ONLY, DATASTORE_PRIMARY_READ_ONLY);
runInvalidTransition(DATASTORE_ONLY, SQL_PRIMARY_READ_ONLY);
runInvalidTransition(DATASTORE_ONLY, SQL_PRIMARY);
runInvalidTransition(DATASTORE_ONLY, SQL_ONLY);
runInvalidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_READ_ONLY);
runInvalidTransition(DATASTORE_PRIMARY, SQL_PRIMARY_READ_ONLY);
runInvalidTransition(DATASTORE_PRIMARY, SQL_PRIMARY);
runInvalidTransition(DATASTORE_PRIMARY, SQL_ONLY);
runInvalidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_ONLY);
runInvalidTransition(SQL_PRIMARY_READ_ONLY, DATASTORE_ONLY);
runInvalidTransition(SQL_PRIMARY_READ_ONLY, DATASTORE_PRIMARY);
runInvalidTransition(SQL_PRIMARY_READ_ONLY, SQL_ONLY);
runInvalidTransition(SQL_PRIMARY, DATASTORE_ONLY);
runInvalidTransition(SQL_PRIMARY, DATASTORE_PRIMARY);
runInvalidTransition(SQL_PRIMARY, DATASTORE_PRIMARY_READ_ONLY);
runInvalidTransition(SQL_ONLY, DATASTORE_ONLY);
runInvalidTransition(SQL_ONLY, DATASTORE_PRIMARY);
runInvalidTransition(SQL_ONLY, DATASTORE_PRIMARY_READ_ONLY);
}
@Test
void testFailure_newMapImpliesInvalidChangeNow() {
DateTime startTime = fakeClock.nowUtc();
fakeClock.advanceBy(Duration.standardHours(6));
// The new map is valid by itself, but not with the current state of DATASTORE_ONLY because the
// new map implies that the current state is DATASTORE_PRIMARY_READ_ONLY
ImmutableSortedMap<DateTime, MigrationState> nowInvalidMap =
ImmutableSortedMap.<DateTime, MigrationState>naturalOrder()
.put(START_OF_TIME, DATASTORE_ONLY)
.put(startTime.plusHours(1), DATASTORE_PRIMARY)
.put(startTime.plusHours(2), DATASTORE_PRIMARY_NO_ASYNC)
.put(startTime.plusHours(3), DATASTORE_PRIMARY_READ_ONLY)
.build();
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(nowInvalidMap)));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"Cannot transition from current state-as-of-now DATASTORE_ONLY "
+ "to new state-as-of-now DATASTORE_PRIMARY_READ_ONLY");
}
@Test
void testFailure_notInTransaction() {
IllegalStateException thrown =
assertThrows(
IllegalStateException.class,
() ->
DatabaseMigrationStateSchedule.set(
DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP.toValueMap()));
assertThat(thrown).hasMessageThat().isEqualTo("Not in a transaction");
}
@Test
void testSuccess_factoryUsesSchedule() {
assertThat(tm().isOfy()).isTrue();
// set the schedule to have converted to SQL_PRIMARY in the past
fakeClock.setTo(START_OF_TIME.plusDays(1));
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY);
assertThat(tm().isOfy()).isFalse();
}
@Test
void testSuccess_factoryUsesReadOnly() {
createTld("tld");
fakeClock.setTo(START_OF_TIME.plusDays(1));
AllocationToken token =
new AllocationToken.Builder().setToken("token").setTokenType(TokenType.SINGLE_USE).build();
runValidTransition(DATASTORE_PRIMARY, DATASTORE_PRIMARY_NO_ASYNC);
runValidTransition(DATASTORE_PRIMARY_NO_ASYNC, DATASTORE_PRIMARY_READ_ONLY);
assertThrows(ReadOnlyModeException.class, () -> persistResource(token));
runValidTransition(DATASTORE_PRIMARY_READ_ONLY, SQL_PRIMARY_READ_ONLY);
assertThrows(ReadOnlyModeException.class, () -> persistResource(token));
runValidTransition(SQL_PRIMARY_READ_ONLY, SQL_PRIMARY);
persistResource(token);
}
private void runValidTransition(MigrationState from, MigrationState to) {
ImmutableSortedMap<DateTime, MigrationState> transitions =
createMapEndingWithTransition(from, to);
jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions));
assertThat(DatabaseMigrationStateSchedule.getUncached().toValueMap())
.containsExactlyEntriesIn(transitions);
}
private void runInvalidTransition(MigrationState from, MigrationState to) {
ImmutableSortedMap<DateTime, MigrationState> transitions =
createMapEndingWithTransition(from, to);
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions)));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
String.format("validStateTransitions map cannot transition from %s to %s.", from, to));
}
// Create a transition map that is valid up to the "from" transition, then add the "to" transition
private ImmutableSortedMap<DateTime, MigrationState> createMapEndingWithTransition(
MigrationState from, MigrationState to) {
ImmutableSortedMap.Builder<DateTime, MigrationState> builder =
ImmutableSortedMap.naturalOrder();
builder.put(START_OF_TIME, DATASTORE_ONLY);
MigrationState[] allMigrationStates = MigrationState.values();
for (int i = 0; i < allMigrationStates.length; i++) {
builder.put(fakeClock.nowUtc().plusMinutes(i), allMigrationStates[i]);
if (allMigrationStates[i].equals(from)) {
break;
}
}
builder.put(fakeClock.nowUtc().plusDays(1), to);
return builder.build();
}
}
@@ -23,8 +23,12 @@ import static google.registry.testing.DatabaseHelper.cloneAndSetAutoTimestamps;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.newDomainBase;
import static google.registry.testing.DatabaseHelper.newHostResource;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DomainBaseSubject.assertAboutDomains;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC;
@@ -41,6 +45,7 @@ import google.registry.model.EntityTestCase;
import google.registry.model.ImmutableObject;
import google.registry.model.ImmutableObjectSubject;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact.Type;
@@ -70,50 +75,55 @@ public class DomainBaseTest extends EntityTestCase {
private DomainBase domain;
private VKey<BillingEvent.OneTime> oneTimeBillKey;
private VKey<BillingEvent.Recurring> recurringBillKey;
private Key<HistoryEntry> historyEntryKey;
private DomainHistory domainHistory;
private VKey<ContactResource> contact1Key, contact2Key;
@BeforeEach
void setUp() {
createTld("com");
VKey<DomainBase> domainKey = VKey.from(Key.create(null, DomainBase.class, "4-COM"));
VKey<HostResource> hostKey =
domain = persistActiveDomain("example.com");
VKey<HostResource> hostKey = persistActiveHost("ns1.example.com").createVKey();
contact1Key = persistActiveContact("contact_id1").createVKey();
contact2Key = persistActiveContact("contact_id1").createVKey();
domainHistory =
persistResource(
new HostResource.Builder()
.setHostName("ns1.example.com")
.setSuperordinateDomain(domainKey)
.setRepoId("1-COM")
new DomainHistory.Builder()
.setDomainRepoId(domain.createVKey().getOfyKey().getName())
.setModificationTime(fakeClock.nowUtc())
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setRegistrarId("TheRegistrar")
.build());
oneTimeBillKey =
persistResource(
new BillingEvent.OneTime.Builder()
// Use SERVER_STATUS so we don't have to add a period.
.setReason(Reason.SERVER_STATUS)
.setTargetId(domain.getDomainName())
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
.setDomainRepoId(domain.getRepoId())
.setBillingTime(DateTime.now(UTC))
.setCost(Money.of(USD, 100))
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(domainHistory)
.build())
.createVKey();
contact1Key =
recurringBillKey =
persistResource(
new ContactResource.Builder()
.setContactId("contact_id1")
.setRepoId("2-COM")
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(domain.getDomainName())
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
.setDomainRepoId(domain.getRepoId())
.setEventTime(DateTime.now(UTC).plusYears(1))
.setRecurrenceEndTime(END_OF_TIME)
.setParent(domainHistory)
.build())
.createVKey();
contact2Key =
persistResource(
new ContactResource.Builder()
.setContactId("contact_id2")
.setRepoId("3-COM")
.build())
.createVKey();
historyEntryKey =
Key.create(
persistResource(
new DomainHistory.Builder()
.setDomainRepoId(domainKey.getOfyKey().getName())
.setModificationTime(fakeClock.nowUtc())
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setRegistrarId("aregistrar")
.build()));
oneTimeBillKey = VKey.from(Key.create(historyEntryKey, BillingEvent.OneTime.class, 1));
recurringBillKey = VKey.from(Key.create(historyEntryKey, BillingEvent.Recurring.class, 2));
VKey<PollMessage.Autorenew> autorenewPollKey =
VKey.from(Key.create(historyEntryKey, PollMessage.Autorenew.class, 3));
VKey.from(Key.create(Key.create(domainHistory), PollMessage.Autorenew.class, 3));
VKey<PollMessage.OneTime> onetimePollKey =
VKey.from(Key.create(historyEntryKey, PollMessage.OneTime.class, 1));
VKey.from(Key.create(Key.create(domainHistory), PollMessage.OneTime.class, 1));
// Set up a new persisted domain entity.
domain =
persistResource(
@@ -121,9 +131,9 @@ public class DomainBaseTest extends EntityTestCase {
new DomainBase.Builder()
.setDomainName("example.com")
.setRepoId("4-COM")
.setCreationRegistrarId("aregistrar")
.setCreationRegistrarId("TheRegistrar")
.setLastEppUpdateTime(fakeClock.nowUtc())
.setLastEppUpdateRegistrarId("AnotherRegistrar")
.setLastEppUpdateRegistrarId("NewRegistrar")
.setLastTransferTime(fakeClock.nowUtc())
.setStatusValues(
ImmutableSet.of(
@@ -137,7 +147,7 @@ public class DomainBaseTest extends EntityTestCase {
.setContacts(ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
.setNameservers(ImmutableSet.of(hostKey))
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
.setPersistedCurrentSponsorRegistrarId("losing")
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
.setDsData(
@@ -146,8 +156,8 @@ public class DomainBaseTest extends EntityTestCase {
LaunchNotice.create("tcnid", "validatorId", START_OF_TIME, START_OF_TIME))
.setTransferData(
new DomainTransferData.Builder()
.setGainingRegistrarId("gaining")
.setLosingRegistrarId("losing")
.setGainingRegistrarId("TheRegistrar")
.setLosingRegistrarId("NewRegistrar")
.setPendingTransferExpirationTime(fakeClock.nowUtc())
.setServerApproveEntities(
ImmutableSet.of(oneTimeBillKey, recurringBillKey, autorenewPollKey))
@@ -165,10 +175,10 @@ public class DomainBaseTest extends EntityTestCase {
.addGracePeriod(
GracePeriod.create(
GracePeriodStatus.ADD,
"4-COM",
domain.getRepoId(),
fakeClock.nowUtc().plusDays(1),
"registrar",
null))
"TheRegistrar",
oneTimeBillKey))
.setAutorenewEndTime(Optional.of(fakeClock.nowUtc().plusYears(2)))
.setDnsRefreshRequestTime(Optional.of(fakeClock.nowUtc()))
.build()));
@@ -192,28 +202,15 @@ public class DomainBaseTest extends EntityTestCase {
@Test
void testVKeyRestoration() {
assertThat(domain.deletePollMessageHistoryId).isEqualTo(historyEntryKey.getId());
assertThat(domain.autorenewBillingEventHistoryId).isEqualTo(historyEntryKey.getId());
assertThat(domain.autorenewPollMessageHistoryId).isEqualTo(historyEntryKey.getId());
assertThat(domain.deletePollMessageHistoryId).isEqualTo(domainHistory.getId());
assertThat(domain.autorenewBillingEventHistoryId).isEqualTo(domainHistory.getId());
assertThat(domain.autorenewPollMessageHistoryId).isEqualTo(domainHistory.getId());
assertThat(domain.getTransferData().getServerApproveBillingEventHistoryId())
.isEqualTo(historyEntryKey.getId());
.isEqualTo(domainHistory.getId());
assertThat(domain.getTransferData().getServerApproveAutorenewEventHistoryId())
.isEqualTo(historyEntryKey.getId());
.isEqualTo(domainHistory.getId());
assertThat(domain.getTransferData().getServerApproveAutorenewPollMessageHistoryId())
.isEqualTo(historyEntryKey.getId());
}
@Test
void testIndexing() throws Exception {
verifyDatastoreIndexing(
domain,
"allContacts.contact",
"fullyQualifiedDomainName",
"nsHosts",
"currentSponsorClientId",
"deletionTime",
"tld",
"autorenewEndTime");
.isEqualTo(domainHistory.getId());
}
@Test
@@ -366,7 +363,7 @@ public class DomainBaseTest extends EntityTestCase {
VKey<BillingEvent.Recurring> newAutorenewEvent) {
assertThat(domain.getTransferData().getTransferStatus())
.isEqualTo(TransferStatus.SERVER_APPROVED);
assertThat(domain.getCurrentSponsorRegistrarId()).isEqualTo("winner");
assertThat(domain.getCurrentSponsorRegistrarId()).isEqualTo("TheRegistrar");
assertThat(domain.getLastTransferTime()).isEqualTo(fakeClock.nowUtc().plusDays(1));
assertThat(domain.getRegistrationExpirationTime()).isEqualTo(newExpirationTime);
assertThat(domain.getAutorenewBillingEvent()).isEqualTo(newAutorenewEvent);
@@ -374,18 +371,19 @@ public class DomainBaseTest extends EntityTestCase {
private void doExpiredTransferTest(DateTime oldExpirationTime) {
DomainHistory historyEntry =
new DomainHistory.Builder()
.setDomain(domain)
.setModificationTime(fakeClock.nowUtc())
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST)
.build();
persistResource(
new DomainHistory.Builder()
.setDomain(domain)
.setModificationTime(fakeClock.nowUtc())
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST)
.build());
BillingEvent.OneTime transferBillingEvent =
persistResource(
new BillingEvent.OneTime.Builder()
.setReason(Reason.TRANSFER)
.setRegistrarId("winner")
.setTargetId("example.com")
.setRegistrarId("TheRegistrar")
.setTargetId(domain.getDomainName())
.setEventTime(fakeClock.nowUtc())
.setBillingTime(
fakeClock
@@ -407,7 +405,7 @@ public class DomainBaseTest extends EntityTestCase {
.setTransferStatus(TransferStatus.PENDING)
.setTransferRequestTime(fakeClock.nowUtc().minusDays(4))
.setPendingTransferExpirationTime(fakeClock.nowUtc().plusDays(1))
.setGainingRegistrarId("winner")
.setGainingRegistrarId("TheRegistrar")
.setServerApproveBillingEvent(transferBillingEvent.createVKey())
.setServerApproveEntities(ImmutableSet.of(transferBillingEvent.createVKey()))
.build())
@@ -418,8 +416,8 @@ public class DomainBaseTest extends EntityTestCase {
GracePeriodStatus.ADD,
domain.getRepoId(),
fakeClock.nowUtc().plusDays(100),
"foo",
null))
"TheRegistrar",
oneTimeBillKey))
.build();
DomainBase afterTransfer = domain.cloneProjectedAtTime(fakeClock.nowUtc().plusDays(1));
DateTime newExpirationTime = oldExpirationTime.plusYears(1);
@@ -435,7 +433,7 @@ public class DomainBaseTest extends EntityTestCase {
.nowUtc()
.plusDays(1)
.plus(Registry.get("com").getTransferGracePeriodLength()),
"winner",
"TheRegistrar",
transferBillingEvent.createVKey(),
afterTransfer.getGracePeriods().iterator().next().getGracePeriodId()));
// If we project after the grace period expires all should be the same except the grace period.
@@ -491,13 +489,13 @@ public class DomainBaseTest extends EntityTestCase {
DomainBase beforeAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.minusDays(1));
assertThat(beforeAutoRenew.getLastEppUpdateTime()).isEqualTo(transferRequestDateTime);
assertThat(beforeAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("gaining");
assertThat(beforeAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("TheRegistrar");
// If autorenew happens before transfer succeeds(before transfer grace period starts as well),
// lastEppUpdateClientId should still be the current sponsor client id
DomainBase afterAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.plusDays(1));
assertThat(afterAutoRenew.getLastEppUpdateTime()).isEqualTo(autorenewDateTime);
assertThat(afterAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("losing");
assertThat(afterAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("NewRegistrar");
}
@Test
@@ -510,12 +508,12 @@ public class DomainBaseTest extends EntityTestCase {
DomainBase beforeAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.minusDays(1));
assertThat(beforeAutoRenew.getLastEppUpdateTime()).isEqualTo(transferRequestDateTime);
assertThat(beforeAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("gaining");
assertThat(beforeAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("TheRegistrar");
DomainBase afterTransferSuccess =
domain.cloneProjectedAtTime(transferSuccessDateTime.plusDays(1));
assertThat(afterTransferSuccess.getLastEppUpdateTime()).isEqualTo(transferSuccessDateTime);
assertThat(afterTransferSuccess.getLastEppUpdateRegistrarId()).isEqualTo("gaining");
assertThat(afterTransferSuccess.getLastEppUpdateRegistrarId()).isEqualTo("TheRegistrar");
}
private void setupUnmodifiedDomain(DateTime oldExpirationTime) {
@@ -542,7 +540,7 @@ public class DomainBaseTest extends EntityTestCase {
DomainBase afterAutoRenew = domain.cloneProjectedAtTime(autorenewDateTime.plusDays(1));
assertThat(afterAutoRenew.getLastEppUpdateTime()).isEqualTo(autorenewDateTime);
assertThat(afterAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("losing");
assertThat(afterAutoRenew.getLastEppUpdateRegistrarId()).isEqualTo("NewRegistrar");
}
@Test
@@ -911,20 +909,8 @@ public class DomainBaseTest extends EntityTestCase {
@Test
void testContactFields() {
VKey<ContactResource> contact3Key =
persistResource(
new ContactResource.Builder()
.setContactId("contact_id3")
.setRepoId("4-COM")
.build())
.createVKey();
VKey<ContactResource> contact4Key =
persistResource(
new ContactResource.Builder()
.setContactId("contact_id4")
.setRepoId("5-COM")
.build())
.createVKey();
VKey<ContactResource> contact3Key = persistActiveContact("contact_id3").createVKey();
VKey<ContactResource> contact4Key = persistActiveContact("contact_id4").createVKey();
// Set all of the contacts.
domain.setContactFields(
@@ -26,12 +26,19 @@ import com.google.common.collect.Iterables;
import com.googlecode.objectify.Key;
import google.registry.model.EntityTestCase;
import google.registry.model.contact.ContactResource;
import google.registry.testing.TmOverrideExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link EppResourceIndex}. */
class EppResourceIndexTest extends EntityTestCase {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
private ContactResource contact;
@BeforeEach
@@ -27,13 +27,19 @@ import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Cache;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.InjectExtension;
import google.registry.testing.TmOverrideExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Tests for {@link CommitLogBucket}. */
public class CommitLogBucketTest {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@@ -27,14 +27,19 @@ import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.InCrossTld;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.TmOverrideExtension;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link DatastoreTransactionManager}. */
public class DatastoreTransactionManagerTest {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
@@ -54,7 +59,6 @@ public class DatastoreTransactionManagerTest {
}
@Entity
@EntityForTesting
@InCrossTld
private static class InCrossTldTestEntity extends ImmutableObject {
@@ -34,14 +34,20 @@ import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import google.registry.testing.TestObject.TestVirtualObject;
import google.registry.testing.TmOverrideExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests ensuring {@link Ofy} saves transactions to {@link CommitLogManifest}. */
public class OfyCommitLogTest {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
@@ -45,17 +45,17 @@ import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.eppcommon.Trid;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.transaction.TransactionManagerFactory.ReadOnlyModeException;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.TmOverrideExtension;
import google.registry.util.SystemClock;
import java.util.ConcurrentModificationException;
import java.util.function.Supplier;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -64,6 +64,10 @@ public class OfyTest {
private final FakeClock fakeClock = new FakeClock(DateTime.parse("2000-01-01TZ"));
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().withClock(fakeClock).build();
@@ -178,7 +182,6 @@ public class OfyTest {
/** Simple entity class with lifecycle callbacks. */
@com.googlecode.objectify.annotation.Entity
@EntityForTesting
public static class LifecycleObject extends ImmutableObject {
@Parent Key<?> parent = getCrossTldKey();
@@ -437,12 +440,4 @@ public class OfyTest {
// Test the normal loading again to verify that we've restored the original session unchanged.
assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject.asHistoryEntry());
}
@Test
void testReadOnly_failsWrite() {
Ofy ofy = new Ofy(fakeClock);
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(fakeClock);
assertThrows(ReadOnlyModeException.class, () -> ofy.save().entity(someObject).now());
DatabaseHelper.removeDatabaseMigrationSchedule();
}
}
@@ -1,152 +0,0 @@
// Copyright 2020 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.model.replay;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ModelUtils;
import google.registry.model.common.GaeUserIdConverter;
import google.registry.persistence.VKey;
import google.registry.testing.DatastoreEntityExtension;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/**
* Test to verify classes implement {@link SqlEntity} and {@link DatastoreEntity} when they should.
*/
public class EntityTest {
@RegisterExtension
final DatastoreEntityExtension datastoreEntityExtension = new DatastoreEntityExtension();
private static final ImmutableSet<Class<?>> NON_CONVERTED_CLASSES =
ImmutableSet.of(GaeUserIdConverter.class);
@Test
void testSqlEntityPersistence() {
try (ScanResult scanResult = scanForClasses()) {
// All javax.persistence entities must implement SqlEntity and vice versa
ImmutableSet<String> javaxPersistenceClasses =
getAllClassesWithAnnotation(scanResult, javax.persistence.Entity.class.getName());
ImmutableSet<String> sqlEntityClasses =
getClassNames(scanResult.getClassesImplementing(SqlEntity.class.getName()));
assertThat(sqlEntityClasses).containsExactlyElementsIn(javaxPersistenceClasses);
// All com.googlecode.objectify entities must implement DatastoreEntity and vice versa
ImmutableSet<String> objectifyClasses =
getAllClassesWithAnnotation(
scanResult, com.googlecode.objectify.annotation.Entity.class.getName());
ImmutableSet<String> datastoreEntityClasses =
getClassNames(scanResult.getClassesImplementing(DatastoreEntity.class.getName()));
assertThat(datastoreEntityClasses).containsExactlyElementsIn(objectifyClasses);
}
}
@Test
void testDatastoreEntityVKeyCreation() {
// For replication, we need to be able to convert from Key -> VKey for the relevant classes.
// This means that the relevant classes must have non-composite Objectify keys or must have a
// createVKey method
try (ScanResult scanResult = scanForClasses()) {
ImmutableSet<Class<?>> datastoreEntityClasses =
getClasses(scanResult.getClassesImplementing(DatastoreEntity.class.getName()));
// some classes aren't converted so they aren't relevant
ImmutableSet<Class<?>> vkeyConversionNecessaryClasses =
datastoreEntityClasses.stream()
.filter(clazz -> !DatastoreOnlyEntity.class.isAssignableFrom(clazz))
.filter(clazz -> !NonReplicatedEntity.class.isAssignableFrom(clazz))
.collect(toImmutableSet());
ImmutableSet.Builder<Class<?>> failedClasses = new ImmutableSet.Builder<>();
for (Class<?> clazz : vkeyConversionNecessaryClasses) {
if (hasKeyWithParent(clazz)) {
try {
Method createVKeyMethod = clazz.getMethod("createVKey", Key.class);
if (!createVKeyMethod.getReturnType().equals(VKey.class)) {
failedClasses.add(clazz);
}
} catch (NoSuchMethodException e) {
failedClasses.add(clazz);
}
}
}
assertWithMessage(
"Some DatastoreEntity classes with parents were missing createVKey methods: ")
.that(failedClasses.build())
.isEmpty();
}
}
private boolean hasKeyWithParent(Class<?> clazz) {
return ModelUtils.getAllFields(clazz).values().stream()
.anyMatch(field -> field.getAnnotation(Parent.class) != null);
}
private ImmutableSet<String> getAllClassesWithAnnotation(
ScanResult scanResult, String annotation) {
ImmutableSet.Builder<String> result = new ImmutableSet.Builder<>();
ClassInfoList classesWithAnnotation = scanResult.getClassesWithAnnotation(annotation);
result.addAll(getClassNames(classesWithAnnotation));
classesWithAnnotation.stream()
.map(ClassInfo::getSubclasses)
.forEach(classInfoList -> result.addAll(getClassNames(classInfoList)));
return result.build();
}
private ImmutableSet<Class<?>> getClasses(ClassInfoList classInfoList) {
return classInfoList.stream()
.filter(ClassInfo::isStandardClass)
.map(ClassInfo::loadClass)
.filter(
clazz ->
!clazz.isAnnotationPresent(EntityForTesting.class)
&& !clazz.isAnnotationPresent(Embed.class)
&& !NON_CONVERTED_CLASSES.contains(clazz)
&& !clazz.getName().contains("Test"))
.collect(toImmutableSet());
}
private ImmutableSet<String> getClassNames(ClassInfoList classInfoList) {
return getClasses(classInfoList).stream().map(Class::getName).collect(toImmutableSet());
}
private ScanResult scanForClasses() {
return new ClassGraph()
.enableAnnotationInfo()
.ignoreClassVisibility()
.acceptPackages("google.registry")
.scan();
}
/** Entities that are solely used for testing, to avoid scanning them in {@link EntityTest}. */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityForTesting {}
}
@@ -27,13 +27,14 @@ import com.googlecode.objectify.annotation.Entity;
import google.registry.model.common.CrossTldSingleton;
import google.registry.model.ofy.CommitLogManifest;
import google.registry.model.ofy.Ofy;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import google.registry.testing.TmOverrideExtension;
import java.util.List;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -43,11 +44,14 @@ public class CommitLogRevisionsTranslatorFactoryTest {
private static final DateTime START_TIME = DateTime.parse("2000-01-01TZ");
@Entity(name = "ClrtfTestEntity")
@EntityForTesting
public static class TestObject extends CrossTldSingleton {
ImmutableSortedMap<DateTime, Key<CommitLogManifest>> revisions = ImmutableSortedMap.of();
}
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
@@ -26,7 +26,6 @@ import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.domain.DomainBase;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.BillingVKey.BillingEventVKey;
import google.registry.persistence.BillingVKey.BillingRecurrenceVKey;
@@ -80,7 +79,6 @@ class BillingVKeyTest {
assertThat(persisted).isEqualTo(original);
}
@EntityForTesting
@Entity
@javax.persistence.Entity
private static class BillingVKeyTestEntity extends ImmutableObject {
@@ -26,7 +26,6 @@ import google.registry.model.ImmutableObject;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DualDatabaseTest;
@@ -82,7 +81,6 @@ class DomainHistoryVKeyTest {
VKey.create(HistoryEntry.class, new DomainHistoryId("domainRepoId", 10L), ofyKey));
}
@EntityForTesting
@Entity
@javax.persistence.Entity(name = "TestEntity")
private static class TestEntity extends ImmutableObject {
@@ -22,7 +22,6 @@ import static google.registry.testing.DatabaseHelper.insertInDb;
import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject;
import google.registry.model.replay.NonReplicatedEntity;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import java.lang.reflect.Method;
@@ -169,7 +168,7 @@ class EntityCallbacksListenerTest {
}
@Entity(name = "TestEntity")
private static class TestEntity extends ParentEntity implements NonReplicatedEntity {
private static class TestEntity extends ParentEntity {
@Id String name = "id";
int nonTransientField = 0;
@@ -22,7 +22,6 @@ import static google.registry.testing.DatabaseHelper.insertInDb;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.BloomFilter;
import google.registry.model.ImmutableObject;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import javax.persistence.Entity;
@@ -49,7 +48,6 @@ class BloomFilterConverterTest {
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
@EntityForTesting
public static class TestEntity extends ImmutableObject {
@Id String name = "id";
@@ -20,7 +20,6 @@ import static google.registry.testing.DatabaseHelper.insertInDb;
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.model.ImmutableObject;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import javax.persistence.Entity;
@@ -77,7 +76,6 @@ public class CurrencyUnitConverterTest {
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
@EntityForTesting
public static class TestEntity extends ImmutableObject {
@Id String name = "id";
@@ -19,7 +19,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.testing.DatabaseHelper.insertInDb;
import google.registry.model.ImmutableObject;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import javax.persistence.Entity;
@@ -88,7 +87,6 @@ public class DurationConverterTest {
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
@EntityForTesting
public static class DurationTestEntity extends ImmutableObject {
@Id String name = "id";
@@ -21,7 +21,6 @@ import static google.registry.testing.DatabaseHelper.insertInDb;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.InetAddresses;
import google.registry.model.ImmutableObject;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import java.net.InetAddress;
@@ -73,7 +72,6 @@ public class InetAddressSetConverterTest {
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
@EntityForTesting
private static class InetAddressSetTestEntity extends ImmutableObject {
@Id String name = "id";
@@ -20,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import google.registry.model.ImmutableObject;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import java.math.BigDecimal;
@@ -289,7 +288,6 @@ public class JodaMoneyConverterTest {
// Override entity name to exclude outer-class name in table name. Not necessary if class is not
// inner class.
@Entity(name = "TestEntity")
@EntityForTesting
public static class TestEntity extends ImmutableObject {
@Id String name = "id";
@@ -307,7 +305,6 @@ public class JodaMoneyConverterTest {
// See comments on the annotation for TestEntity above for reason.
@Entity(name = "ComplexTestEntity")
@EntityForTesting
// This entity is used to test column override for embedded fields and collections.
public static class ComplexTestEntity extends ImmutableObject {
@@ -19,7 +19,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.testing.DatabaseHelper.insertInDb;
import google.registry.model.ImmutableObject;
import google.registry.model.replay.EntityTest;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
@@ -63,7 +62,6 @@ public class LocalDateConverterTest {
/** Override entity name to avoid the nested class reference. */
@Entity(name = "LocalDateConverterTestEntity")
@EntityTest.EntityForTesting
private static class LocalDateConverterTestEntity extends ImmutableObject {
@Id String name = "id";
@@ -27,7 +27,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Streams;
import com.google.common.io.Resources;
import google.registry.model.common.DatabaseMigrationStateSchedule;
import google.registry.persistence.HibernateSchemaExporter;
import google.registry.persistence.NomulusPostgreSql;
import google.registry.persistence.PersistenceModule;
@@ -168,7 +167,7 @@ abstract class JpaTransactionManagerExtension implements BeforeEachCallback, Aft
if (!includeNomulusSchema) {
File tempSqlFile = File.createTempFile("tempSqlFile", ".sql");
tempSqlFile.deleteOnExit();
exporter.export(getTestEntities(), tempSqlFile);
exporter.export(extraEntityClasses, tempSqlFile);
executeSql(new String(Files.readAllBytes(tempSqlFile.toPath()), StandardCharsets.UTF_8));
}
assertReasonableNumDbConnections();
@@ -238,14 +237,16 @@ abstract class JpaTransactionManagerExtension implements BeforeEachCallback, Aft
ResultSet rs =
statement.executeQuery(
"SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';");
ImmutableList.Builder<String> tableNames = new ImmutableList.Builder<>();
ImmutableList.Builder<String> tableNamesBuilder = new ImmutableList.Builder<>();
while (rs.next()) {
tableNames.add('"' + rs.getString(1) + '"');
tableNamesBuilder.add('"' + rs.getString(1) + '"');
}
ImmutableList<String> tableNames = tableNamesBuilder.build();
if (!tableNames.isEmpty()) {
String sql =
String.format("TRUNCATE %s RESTART IDENTITY CASCADE", Joiner.on(',').join(tableNames));
executeSql(sql);
}
String sql =
String.format(
"TRUNCATE %s RESTART IDENTITY CASCADE", Joiner.on(',').join(tableNames.build()));
executeSql(sql);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -344,17 +345,7 @@ abstract class JpaTransactionManagerExtension implements BeforeEachCallback, Aft
descriptor.getManagedClassNames().addAll(nonEntityClasses);
}
getTestEntities().stream().map(Class::getName).forEach(descriptor::addClasses);
extraEntityClasses.stream().map(Class::getName).forEach(descriptor::addClasses);
return Bootstrap.getEntityManagerFactoryBuilder(descriptor, properties).build();
}
private ImmutableList<Class<?>> getTestEntities() {
// We have to add the DatabaseMigrationStateSchedule and TransactionEntity classes to extra
// entities, as they are required by the transaction manager factory and transaction replication
// mechanism, respectively.
return Stream.concat(
extraEntityClasses.stream(),
Stream.of(DatabaseMigrationStateSchedule.class, TransactionEntity.class))
.collect(toImmutableList());
}
}
@@ -36,7 +36,6 @@ import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.TmOverrideExtension;
import java.io.Serializable;
import java.math.BigInteger;
import java.sql.SQLException;
@@ -49,7 +48,6 @@ import javax.persistence.IdClass;
import javax.persistence.OptimisticLockException;
import javax.persistence.RollbackException;
import org.hibernate.exception.JDBCConnectionException;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -84,10 +82,6 @@ class JpaTransactionManagerImplTest {
TestEntity.class, TestCompoundIdEntity.class, TestNamedCompoundIdEntity.class)
.buildUnitTestExtension();
@RegisterExtension
@Order(Order.DEFAULT + 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
@Test
void transact_succeeds() {
assertPersonEmpty();
@@ -340,16 +340,6 @@ public class ReplicaSimulatingJpaTransactionManager implements JpaTransactionMan
return delegate.isOfy();
}
@Override
public void putIgnoringReadOnlyWithoutBackup(Object entity) {
delegate.putIgnoringReadOnlyWithoutBackup(entity);
}
@Override
public void deleteIgnoringReadOnlyWithoutBackup(VKey<?> key) {
delegate.deleteIgnoringReadOnlyWithoutBackup(key);
}
@Override
public <T> void assertDelete(VKey<T> key) {
delegate.assertDelete(key);
@@ -30,11 +30,8 @@ import com.googlecode.objectify.annotation.Id;
import google.registry.model.ImmutableObject;
import google.registry.model.ofy.DatastoreTransactionManager;
import google.registry.model.ofy.Ofy;
import google.registry.model.replay.NonReplicatedEntity;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.TransactionManagerFactory.ReadOnlyModeException;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
@@ -409,13 +406,6 @@ public class TransactionManagerTest {
assertThat(tm().transact(() -> tm().loadByKey(theEntity.key())).data).isEqualTo("foo");
}
@TestOfyAndSql
void testReadOnly_writeFails() {
DatabaseHelper.setMigrationScheduleToDatastorePrimaryReadOnly(fakeClock);
assertThrows(ReadOnlyModeException.class, () -> tm().transact(() -> tm().put(theEntity)));
DatabaseHelper.removeDatabaseMigrationSchedule();
}
private static void assertEntityExists(TestEntity entity) {
assertThat(tm().transact(() -> tm().exists(entity))).isTrue();
}
@@ -449,7 +439,7 @@ public class TransactionManagerTest {
@Entity(name = "TxnMgrTestEntity")
@javax.persistence.Entity(name = "TestEntity")
private static class TestEntity extends TestEntityBase implements NonReplicatedEntity {
private static class TestEntity extends TestEntityBase {
private String data;
@@ -1,189 +0,0 @@
// Copyright 2020 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.persistence.transaction;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.ImmutableObject;
import google.registry.model.ofy.CommitLogManifest;
import google.registry.model.ofy.CommitLogMutation;
import google.registry.model.ofy.Ofy;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.util.Comparator;
import java.util.List;
import org.joda.time.DateTime;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
class TransactionTest {
private final FakeClock fakeClock =
new FakeClock(DateTime.parse("2000-01-01TZ")).setAutoIncrementByOneMilli();
@RegisterExtension
final AppEngineExtension appEngine =
AppEngineExtension.builder()
.withDatastoreAndCloudSql()
.withClock(fakeClock)
.withOfyTestEntities(TestEntity.class)
.withJpaUnitTestEntities(TestEntity.class)
.build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private TestEntity fooEntity, barEntity;
@BeforeEach
void beforeEach() {
inject.setStaticField(Ofy.class, "clock", fakeClock);
fooEntity = new TestEntity("foo");
barEntity = new TestEntity("bar");
}
@AfterEach
void afterEach() {
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@Test
void testTransactionReplay() {
Transaction txn = new Transaction.Builder().addUpdate(fooEntity).addUpdate(barEntity).build();
txn.writeToDatastore();
ofyTm()
.transact(
() -> {
assertThat(ofyTm().loadByKey(fooEntity.key())).isEqualTo(fooEntity);
assertThat(ofyTm().loadByKey(barEntity.key())).isEqualTo(barEntity);
});
txn = new Transaction.Builder().addDelete(barEntity.key()).build();
txn.writeToDatastore();
assertThat(ofyTm().exists(barEntity.key())).isEqualTo(false);
assertThat(
auditedOfy().load().type(CommitLogMutation.class).list().stream()
.map(clm -> auditedOfy().load().<TestEntity>fromEntity(clm.getEntity()))
.collect(toImmutableSet()))
.containsExactly(fooEntity, barEntity);
List<CommitLogManifest> manifests = auditedOfy().load().type(CommitLogManifest.class).list();
manifests.sort(Comparator.comparing(CommitLogManifest::getCommitTime));
assertThat(manifests.get(0).getDeletions()).isEmpty();
assertThat(manifests.get(1).getDeletions()).containsExactly(Key.create(barEntity));
}
@Test
void testSerialization() throws Exception {
Transaction txn = new Transaction.Builder().addUpdate(barEntity).build();
txn.writeToDatastore();
txn = new Transaction.Builder().addUpdate(fooEntity).addDelete(barEntity.key()).build();
txn = Transaction.deserialize(txn.serialize());
txn.writeToDatastore();
ofyTm()
.transact(
() -> {
assertThat(ofyTm().loadByKey(fooEntity.key())).isEqualTo(fooEntity);
assertThat(ofyTm().exists(barEntity.key())).isEqualTo(false);
});
}
@Test
void testDeserializationErrors() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeInt(12345);
out.close();
assertThrows(IllegalArgumentException.class, () -> Transaction.deserialize(baos.toByteArray()));
// Test with a short byte array.
assertThrows(
StreamCorruptedException.class, () -> Transaction.deserialize(new byte[] {1, 2, 3, 4}));
}
@Test
void testTransactionSerialization() throws IOException {
DatabaseHelper.setMigrationScheduleToSqlPrimary(fakeClock);
jpaTm()
.transact(
() -> {
jpaTm().insert(fooEntity);
jpaTm().insert(barEntity);
});
TransactionEntity txnEnt =
jpaTm().transact(() -> jpaTm().loadByKey(VKey.createSql(TransactionEntity.class, 1L)));
Transaction txn = Transaction.deserialize(txnEnt.getContents());
txn.writeToDatastore();
ofyTm()
.transact(
() -> {
assertThat(ofyTm().loadByKey(fooEntity.key())).isEqualTo(fooEntity);
assertThat(ofyTm().loadByKey(barEntity.key())).isEqualTo(barEntity);
});
// Verify that no transaction was persisted for the load transaction.
assertThat(
jpaTm().transact(() -> jpaTm().exists(VKey.createSql(TransactionEntity.class, 2L))))
.isFalse();
}
@Test
void testTransactionSerializationDisabledByDefault() {
jpaTm()
.transact(
() -> {
jpaTm().insert(fooEntity);
jpaTm().insert(barEntity);
});
assertThat(jpaTm().transact(() -> jpaTm().exists(VKey.createSql(TransactionEntity.class, 1L))))
.isFalse();
}
@Entity(name = "TxnTestEntity")
@javax.persistence.Entity(name = "TestEntity")
private static class TestEntity extends ImmutableObject {
@Id @javax.persistence.Id private String name;
private TestEntity() {}
private TestEntity(String name) {
this.name = name;
}
public VKey<TestEntity> key() {
return VKey.create(TestEntity.class, name, Key.create(this));
}
}
}
@@ -56,6 +56,7 @@ import google.registry.testing.FakeKeyringModule;
import google.registry.testing.FakeLockHandler;
import google.registry.testing.FakeResponse;
import google.registry.testing.InjectExtension;
import google.registry.testing.TmOverrideExtension;
import google.registry.testing.mapreduce.MapreduceTestCase;
import google.registry.tldconfig.idn.IdnTableEnum;
import google.registry.xjc.XjcXmlTransformer;
@@ -84,12 +85,17 @@ import org.joda.time.DateTimeConstants;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link RdeStagingAction} in Datastore. */
public class RdeStagingActionDatastoreTest extends MapreduceTestCase<RdeStagingAction> {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
private static final BlobId XML_FILE =
BlobId.of("rde-bucket", "lol_2000-01-01_full_S1_R0.xml.ghostryde");
private static final BlobId LENGTH_FILE =
@@ -27,10 +27,12 @@ import com.google.common.collect.ImmutableSetMultimap;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.TmOverrideExtension;
import google.registry.xml.ValidationMode;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -53,6 +55,10 @@ class RdeStagingMapperTest {
private ArgumentCaptor<DepositFragment> depositFragmentCaptor =
ArgumentCaptor.forClass(DepositFragment.class);
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
AppEngineExtension appEngineExtension =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@@ -43,6 +43,7 @@ import google.registry.testing.CloudTasksHelper;
import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.FakeKeyringModule;
import google.registry.testing.FakeLockHandler;
import google.registry.testing.TmOverrideExtension;
import google.registry.xml.ValidationMode;
import java.io.IOException;
import java.util.Iterator;
@@ -52,12 +53,17 @@ import org.bouncycastle.openpgp.PGPPublicKey;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link RdeStagingReducer}. */
class RdeStagingReducerTest {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
AppEngineExtension appEngineExtension =
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
@@ -21,13 +21,11 @@ import static google.registry.xjc.XjcXmlTransformer.marshalStrict;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import google.registry.model.ofy.Ofy;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import google.registry.xjc.rderegistrar.XjcRdeRegistrar;
import google.registry.xjc.rderegistrar.XjcRdeRegistrarAddrType;
import google.registry.xjc.rderegistrar.XjcRdeRegistrarPostalInfoEnumType;
@@ -47,11 +45,11 @@ import org.junit.jupiter.api.extension.RegisterExtension;
*/
public class RegistrarToXjcConverterTest {
private final FakeClock clock = new FakeClock(DateTime.parse("2013-01-01T00:00:00Z"));
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
AppEngineExtension.builder().withDatastoreAndCloudSql().withClock(clock).build();
private Registrar registrar;
@@ -86,9 +84,8 @@ public class RegistrarToXjcConverterTest {
.setWhoisServer("whois.goblinmen.example")
.setUrl("http://www.goblinmen.example")
.build();
FakeClock clock = new FakeClock(DateTime.parse("2013-01-01T00:00:00Z"));
inject.setStaticField(Ofy.class, "clock", clock);
registrar = cloneAndSetAutoTimestamps(registrar); // Set the creation time in 2013.
registrar = registrar.asBuilder().setLastUpdateTime(null).build();
clock.setTo(DateTime.parse("2014-01-01T00:00:00Z"));
registrar = cloneAndSetAutoTimestamps(registrar); // Set the update time in 2014.
}
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.when;
import com.google.cloud.tasks.v2.HttpMethod;
import com.google.common.net.MediaType;
import google.registry.beam.BeamActionTestBase;
import google.registry.model.common.DatabaseMigrationStateSchedule.PrimaryDatabase;
import google.registry.reporting.ReportingModule;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.CloudTasksHelper;
@@ -62,7 +61,6 @@ class GenerateInvoicesActionTest extends BeamActionTestBase {
"billing_bucket",
"REG-INV",
true,
PrimaryDatabase.DATASTORE,
new YearMonth(2017, 10),
emailUtils,
cloudTasksUtils,
@@ -97,7 +95,6 @@ class GenerateInvoicesActionTest extends BeamActionTestBase {
"billing_bucket",
"REG-INV",
false,
PrimaryDatabase.DATASTORE,
new YearMonth(2017, 10),
emailUtils,
cloudTasksUtils,
@@ -122,7 +119,6 @@ class GenerateInvoicesActionTest extends BeamActionTestBase {
"billing_bucket",
"REG-INV",
false,
PrimaryDatabase.DATASTORE,
new YearMonth(2017, 10),
emailUtils,
cloudTasksUtils,
@@ -22,7 +22,6 @@ import static org.mockito.Mockito.when;
import com.google.cloud.tasks.v2.HttpMethod;
import com.google.common.net.MediaType;
import google.registry.beam.BeamActionTestBase;
import google.registry.model.common.DatabaseMigrationStateSchedule.PrimaryDatabase;
import google.registry.reporting.ReportingModule;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.CloudTasksHelper;
@@ -57,7 +56,6 @@ class GenerateSpec11ReportActionTest extends BeamActionTestBase {
"gs://reporting-project/reporting-bucket/",
"api_key/a",
clock.nowUtc().toLocalDate(),
PrimaryDatabase.DATASTORE,
true,
clock,
response,
@@ -81,7 +79,6 @@ class GenerateSpec11ReportActionTest extends BeamActionTestBase {
"gs://reporting-project/reporting-bucket/",
"api_key/a",
clock.nowUtc().toLocalDate(),
PrimaryDatabase.DATASTORE,
true,
clock,
response,
@@ -115,7 +112,6 @@ class GenerateSpec11ReportActionTest extends BeamActionTestBase {
"gs://reporting-project/reporting-bucket/",
"api_key/a",
clock.nowUtc().toLocalDate(),
PrimaryDatabase.DATASTORE,
false,
clock,
response,
@@ -27,7 +27,6 @@ import google.registry.model.registrar.RegistrarContact;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.TmOverrideExtension;
import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
@@ -45,10 +44,6 @@ class RegistrarContactTest {
JpaIntegrationWithCoverageExtension jpa =
new JpaTestExtensions.Builder().buildIntegrationWithCoverageExtension();
@RegisterExtension
@Order(Order.DEFAULT + 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
private Registrar testRegistrar;
private RegistrarContact testRegistrarPoc;
@@ -30,7 +30,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.TmOverrideExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
@@ -50,10 +49,6 @@ public class RegistrarDaoTest {
JpaIntegrationWithCoverageExtension jpa =
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
@RegisterExtension
@Order(Order.DEFAULT + 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
private final VKey<Registrar> registrarKey = VKey.createSql(Registrar.class, "registrarId");
private Registrar testRegistrar;
@@ -1,70 +0,0 @@
// 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.schema.replay;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.registrar.RegistrarContact.RegistrarPocId;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.TmOverrideExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link SqlEntity#getPrimaryKeyString}. */
public class SqlEntityTest {
@RegisterExtension
@Order(1)
final DatastoreEntityExtension datastoreEntityExtension = new DatastoreEntityExtension();
@RegisterExtension
final AppEngineExtension database =
new AppEngineExtension.Builder().withCloudSql().withoutCannedData().build();
@RegisterExtension
@Order(Order.DEFAULT + 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withJpa();
@BeforeEach
void setup() throws Exception {
AppEngineExtension.loadInitialData();
}
@Test
void getPrimaryKeyString_oneIdColumn() {
// AppEngineExtension canned data: Registrar1
assertThat(
tm().transact(() -> tm().loadByKey(Registrar.createVKey("NewRegistrar")))
.getPrimaryKeyString())
.contains("NewRegistrar");
}
@Test
void getPrimaryKeyString_multiId() {
// AppEngineExtension canned data: RegistrarContact1
VKey<RegistrarContact> key =
VKey.createSql(
RegistrarContact.class, new RegistrarPocId("janedoe@theregistrar.com", "NewRegistrar"));
String expected = "emailAddress=janedoe@theregistrar.com\n registrarId=NewRegistrar";
assertThat(tm().transact(() -> tm().loadByKey(key)).getPrimaryKeyString()).contains(expected);
}
}
@@ -71,8 +71,6 @@ import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.common.DatabaseMigrationStateSchedule;
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
import google.registry.model.contact.ContactAuthInfo;
import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactResource;
@@ -126,7 +124,6 @@ import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Duration;
/** Static utils for setting up test resources. */
public class DatabaseHelper {
@@ -1210,7 +1207,7 @@ public class DatabaseHelper {
* entities.
*/
public static <R> void insertSimpleResources(final Iterable<R> resources) {
tm().transact(() -> tm().insertAllWithoutBackup(ImmutableList.copyOf(resources)));
tm().transact(() -> tm().putAllWithoutBackup(ImmutableList.copyOf(resources)));
maybeAdvanceClock();
// Force the session to be cleared so that when we read it back, we read from Datastore
// and not from the transaction's session cache.
@@ -1226,18 +1223,10 @@ public class DatabaseHelper {
/** Force the create and update timestamps to get written into the resource. */
public static <R> R cloneAndSetAutoTimestamps(final R resource) {
R result;
if (tm().isOfy()) {
result =
tm().transact(
() -> auditedOfy().load().fromEntity(auditedOfy().save().toEntity(resource)));
} else {
// We have to separate the read and write operation into different transactions
// otherwise JPA would just return the input entity instead of actually creating a
// clone.
tm().transact(() -> tm().put(resource));
result = tm().transact(() -> tm().loadByEntity(resource));
}
// We have to separate the read and write operation into different transactions otherwise JPA
// would just return the input entity instead of actually creating a clone.
tm().transact(() -> tm().put(resource));
R result = tm().transact(() -> tm().loadByEntity(resource));
maybeAdvanceClock();
return result;
}
@@ -1408,104 +1397,5 @@ public class DatabaseHelper {
return entity;
}
/**
* Sets a DATASTORE_PRIMARY_NO_ASYNC state on the {@link DatabaseMigrationStateSchedule}.
*
* <p>In order to allow for tests to manipulate the clock how they need, we start the transitions
* one millisecond after the clock's current time (in case the clock's current value is
* START_OF_TIME). We then advance the clock one second so that we're in the
* DATASTORE_PRIMARY_READ_ONLY phase.
*
* <p>We must use the current time, otherwise the setting of the migration state will fail due to
* an invalid transition.
*/
public static void setMigrationScheduleToDatastorePrimaryNoAsync(FakeClock fakeClock) {
DateTime now = fakeClock.nowUtc();
jpaTm()
.transact(
() ->
DatabaseMigrationStateSchedule.set(
ImmutableSortedMap.of(
START_OF_TIME,
MigrationState.DATASTORE_ONLY,
now.plusMillis(1),
MigrationState.DATASTORE_PRIMARY,
now.plusMillis(2),
MigrationState.DATASTORE_PRIMARY_NO_ASYNC)));
fakeClock.advanceBy(Duration.standardSeconds(1));
}
/**
* Sets a DATASTORE_PRIMARY_READ_ONLY state on the {@link DatabaseMigrationStateSchedule}.
*
* <p>In order to allow for tests to manipulate the clock how they need, we start the transitions
* one millisecond after the clock's current time (in case the clock's current value is
* START_OF_TIME). We then advance the clock one second so that we're in the
* DATASTORE_PRIMARY_READ_ONLY phase.
*
* <p>We must use the current time, otherwise the setting of the migration state will fail due to
* an invalid transition.
*/
public static void setMigrationScheduleToDatastorePrimaryReadOnly(FakeClock fakeClock) {
DateTime now = fakeClock.nowUtc();
jpaTm()
.transact(
() ->
DatabaseMigrationStateSchedule.set(
ImmutableSortedMap.of(
START_OF_TIME,
MigrationState.DATASTORE_ONLY,
now.plusMillis(1),
MigrationState.DATASTORE_PRIMARY,
now.plusMillis(2),
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
now.plusMillis(3),
MigrationState.DATASTORE_PRIMARY_READ_ONLY)));
fakeClock.advanceBy(Duration.standardSeconds(1));
}
/**
* Sets a SQL_PRIMARY state on the {@link DatabaseMigrationStateSchedule}.
*
* <p>In order to allow for tests to manipulate the clock how they need, we start the transitions
* one millisecond after the clock's current time (in case the clock's current value is
* START_OF_TIME). We then advance the clock one second so that we're in the SQL_PRIMARY phase.
*
* <p>We must use the current time, otherwise the setting of the migration state will fail due to
* an invalid transition.
*/
public static void setMigrationScheduleToSqlPrimary(FakeClock fakeClock) {
DateTime now = fakeClock.nowUtc();
jpaTm()
.transact(
() ->
DatabaseMigrationStateSchedule.set(
ImmutableSortedMap.of(
START_OF_TIME,
MigrationState.DATASTORE_ONLY,
now.plusMillis(1),
MigrationState.DATASTORE_PRIMARY,
now.plusMillis(2),
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
now.plusMillis(3),
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
now.plusMillis(4),
MigrationState.SQL_PRIMARY)));
fakeClock.advanceBy(Duration.standardSeconds(1));
}
/** Removes the database migration schedule, in essence transitioning to DATASTORE_ONLY. */
public static void removeDatabaseMigrationSchedule() {
// use the raw calls because going SQL_PRIMARY -> DATASTORE_ONLY is not valid
jpaTm()
.transact(
() ->
jpaTm()
.putIgnoringReadOnlyWithoutBackup(
new DatabaseMigrationStateSchedule(
DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP)));
DatabaseMigrationStateSchedule.CACHE.invalidateAll();
}
private DatabaseHelper() {}
}
@@ -23,16 +23,13 @@ import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.VirtualEntity;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.replay.DatastoreAndSqlEntity;
import google.registry.model.replay.EntityTest.EntityForTesting;
import google.registry.persistence.VKey;
import javax.persistence.Transient;
/** A test model object that can be persisted in any entity group. */
@Entity
@javax.persistence.Entity
@EntityForTesting
public class TestObject extends ImmutableObject implements DatastoreAndSqlEntity {
public class TestObject extends ImmutableObject {
@Parent @Transient Key<EntityGroupRoot> parent;
@@ -75,7 +72,6 @@ public class TestObject extends ImmutableObject implements DatastoreAndSqlEntity
/** A test @VirtualEntity model object, which should not be persisted. */
@Entity
@VirtualEntity
@EntityForTesting
public static class TestVirtualObject extends ImmutableObject {
@Id String id;
@@ -14,9 +14,9 @@
package google.registry.testing;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.persistence.transaction.TransactionManager;
import google.registry.persistence.transaction.TransactionManagerFactory;
import org.junit.jupiter.api.extension.AfterEachCallback;
@@ -35,35 +35,17 @@ import org.junit.jupiter.api.extension.ExtensionContext;
* <p>This extension is incompatible with {@link DualDatabaseTest}. Use either that or this, but not
* both.
*/
@DeleteAfterMigration
public final class TmOverrideExtension implements BeforeEachCallback, AfterEachCallback {
private static enum TmOverride {
OFY,
JPA;
}
private final TmOverride tmOverride;
private TmOverrideExtension(TmOverride tmOverride) {
this.tmOverride = tmOverride;
}
/** Use the {@link google.registry.model.ofy.DatastoreTransactionManager} for all tests. */
public static TmOverrideExtension withOfy() {
return new TmOverrideExtension(TmOverride.OFY);
}
/**
* Use the {@link google.registry.persistence.transaction.JpaTransactionManager} for all tests.
*/
public static TmOverrideExtension withJpa() {
return new TmOverrideExtension(TmOverride.JPA);
return new TmOverrideExtension();
}
@Override
public void beforeEach(ExtensionContext context) {
TransactionManagerFactory.setTmOverrideForTest(
tmOverride == TmOverride.OFY ? ofyTm() : jpaTm());
TransactionManagerFactory.setTmOverrideForTest(ofyTm());
}
@Override
@@ -89,13 +89,18 @@ class NordnUploadActionTest {
private static final String LOCATION_URL = "http://trololol";
private final FakeClock clock = new FakeClock(DateTime.parse("2010-05-01T10:11:12Z"));
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
AppEngineExtension.builder()
.withDatastoreAndCloudSql()
.withClock(clock)
.withTaskQueue()
.build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private final FakeClock clock = new FakeClock(DateTime.parse("2010-05-01T10:11:12Z"));
private final LordnRequestInitializer lordnRequestInitializer =
new LordnRequestInitializer(Optional.of("attack"));
private final NordnUploadAction action = new NordnUploadAction();
@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.io.Resources;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.tools.EntityWrapper.Property;
import java.io.ByteArrayOutputStream;
@@ -32,6 +33,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
@DeleteAfterMigration
public class CompareDbBackupsTest {
private static final int BASE_ID = 1001;
@@ -55,9 +55,9 @@ class CreateTldCommandTest extends CommandTestCase<CreateTldCommand> {
@Test
void testSuccess() throws Exception {
DateTime before = DateTime.now(UTC);
DateTime before = fakeClock.nowUtc();
runCommandForced("xn--q9jyb4c", "--roid_suffix=Q9JYB4C", "--dns_writers=FooDnsWriter");
DateTime after = DateTime.now(UTC);
DateTime after = fakeClock.nowUtc();
Registry registry = Registry.get("xn--q9jyb4c");
assertThat(registry).isNotNull();
@@ -1,151 +0,0 @@
// Copyright 2020 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.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadAllOf;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistResource;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.DomainTransferData;
import org.joda.money.Money;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link DedupeOneTimeBillingEventIdsCommand}.
*
* <p>Note that these are _not_ dual database tests even though the action has been converted. The
* dedupe was strictly a one-time event that needed to be done prior to moving to SQL. It should no
* longer be necessary and we may want to simply remove the command.
*/
class DedupeOneTimeBillingEventIdsCommandTest
extends CommandTestCase<DedupeOneTimeBillingEventIdsCommand> {
DomainBase domain;
DomainHistory historyEntry;
PollMessage.Autorenew autorenewToResave;
BillingEvent.OneTime billingEventToResave;
@BeforeEach
void beforeEach() {
createTld("foobar");
domain = persistActiveDomain("foo.foobar");
historyEntry = persistHistoryEntry(domain);
autorenewToResave = persistAutorenewPollMessage(historyEntry);
billingEventToResave = persistBillingEvent(historyEntry);
}
@Test
void resaveBillingEvent_succeeds() throws Exception {
runCommand(
"--force",
"--key_paths_file",
writeToNamedTmpFile("keypath.txt", getKeyPathLiteral(billingEventToResave)));
int count = 0;
for (BillingEvent.OneTime billingEvent : loadAllOf(BillingEvent.OneTime.class)) {
count++;
assertThat(billingEvent.getId()).isNotEqualTo(billingEventToResave.getId());
assertThat(billingEvent.asBuilder().setId(billingEventToResave.getId()).build())
.isEqualTo(billingEventToResave);
}
assertThat(count).isEqualTo(1);
}
@Test
void resaveBillingEvent_failsWhenReferredByDomain() {
persistResource(
domain
.asBuilder()
.setTransferData(
new DomainTransferData.Builder()
.setServerApproveEntities(ImmutableSet.of(billingEventToResave.createVKey()))
.build())
.build());
assertThrows(
IllegalStateException.class,
() ->
runCommand(
"--force",
"--key_paths_file",
writeToNamedTmpFile("keypath.txt", getKeyPathLiteral(billingEventToResave))));
}
private PollMessage.Autorenew persistAutorenewPollMessage(HistoryEntry historyEntry) {
return persistResource(
new PollMessage.Autorenew.Builder()
.setRegistrarId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.setMsg("Test poll message")
.setParent(historyEntry)
.setAutorenewEndTime(fakeClock.nowUtc().plusDays(365))
.setTargetId("foobar.foo")
.build());
}
private BillingEvent.OneTime persistBillingEvent(DomainHistory historyEntry) {
return persistResource(
new BillingEvent.OneTime.Builder()
.setRegistrarId("a registrar")
.setTargetId("foo.tld")
.setParent(historyEntry)
.setReason(Reason.CREATE)
.setFlags(ImmutableSet.of(BillingEvent.Flag.ANCHOR_TENANT))
.setPeriodYears(2)
.setCost(Money.of(USD, 1))
.setEventTime(fakeClock.nowUtc())
.setBillingTime(fakeClock.nowUtc().plusDays(5))
.build());
}
private DomainHistory persistHistoryEntry(DomainBase parent) {
return persistResource(
new DomainHistory.Builder()
.setDomain(parent)
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS))
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
.setModificationTime(fakeClock.nowUtc())
.setRegistrarId("foo")
.setTrid(Trid.create("ABC-123", "server-trid"))
.setBySuperuser(false)
.setReason("reason")
.setRequestedByRegistrar(false)
.build());
}
private static String getKeyPathLiteral(Object entity) {
Key<?> key = Key.create(entity);
return String.format(
"\"DomainBase\", \"%s\", \"HistoryEntry\", %s, \"%s\", %s",
key.getParent().getParent().getName(), key.getParent().getId(), key.getKind(), key.getId());
}
}
@@ -1,68 +0,0 @@
// 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 google.registry.model.common.DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.common.DatabaseMigrationStateSchedule;
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import org.joda.time.DateTime;
import org.junit.jupiter.api.AfterEach;
/** Tests for {@link GetDatabaseMigrationStateCommand}. */
@DualDatabaseTest
public class GetDatabaseMigrationStateCommandTest
extends CommandTestCase<GetDatabaseMigrationStateCommand> {
@AfterEach
void afterEach() {
DatabaseHelper.removeDatabaseMigrationSchedule();
}
@TestOfyAndSql
void testInitial_returnsDatastoreOnly() throws Exception {
runCommand();
assertStdoutIs(
String.format("Current migration schedule: %s\n", DEFAULT_TRANSITION_MAP.toValueMap()));
}
@TestOfyAndSql
void testFullSchedule() throws Exception {
DateTime now = fakeClock.nowUtc();
ImmutableSortedMap<DateTime, MigrationState> transitions =
ImmutableSortedMap.of(
START_OF_TIME,
MigrationState.DATASTORE_ONLY,
now.plusHours(1),
MigrationState.DATASTORE_PRIMARY,
now.plusHours(2),
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
now.plusHours(3),
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
now.plusHours(4),
MigrationState.SQL_PRIMARY,
now.plusHours(5),
MigrationState.SQL_ONLY);
jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions));
runCommand();
assertStdoutIs(String.format("Current migration schedule: %s\n", transitions));
}
}
@@ -19,19 +19,15 @@ import static google.registry.testing.DatabaseHelper.newDomainBase;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
import static google.registry.testing.DatabaseHelper.persistResource;
import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link GetDomainCommand}. */
class GetDomainCommandTest extends CommandTestCase<GetDomainCommand> {
private DateTime now = DateTime.now(UTC);
@BeforeEach
void beforeEach() {
createTld("tld");
@@ -55,7 +51,7 @@ class GetDomainCommandTest extends CommandTestCase<GetDomainCommand> {
persistActiveDomain("example.tld");
runCommand("example.tld", "--expand");
assertInStdout("fullyQualifiedDomainName=example.tld");
assertInStdout("contactId=contact1234");
assertInStdout("contact=Key<?>(ContactResource(\"3-ROID\"))");
assertInStdout(
"Websafe key: "
+ "kind:DomainBase"
@@ -70,7 +66,7 @@ class GetDomainCommandTest extends CommandTestCase<GetDomainCommand> {
persistActiveDomain("xn--aualito-txac.xn--q9jyb4c");
runCommand("çauçalito.みんな", "--expand");
assertInStdout("fullyQualifiedDomainName=xn--aualito-txac.xn--q9jyb4c");
assertInStdout("contactId=contact1234");
assertInStdout("contact=Key<?>(ContactResource(\"4-ROID\"))");
}
@Test
@@ -94,15 +90,18 @@ class GetDomainCommandTest extends CommandTestCase<GetDomainCommand> {
@Test
void testSuccess_domainDeletedInFuture() throws Exception {
persistResource(newDomainBase("example.tld").asBuilder()
.setDeletionTime(now.plusDays(1)).build());
runCommand("example.tld", "--read_timestamp=" + now.plusMonths(1));
persistResource(
newDomainBase("example.tld")
.asBuilder()
.setDeletionTime(fakeClock.nowUtc().plusDays(1))
.build());
runCommand("example.tld", "--read_timestamp=" + fakeClock.nowUtc().plusMonths(1));
assertInStdout("Domain 'example.tld' does not exist or is deleted");
}
@Test
void testSuccess_deletedDomain() throws Exception {
persistDeletedDomain("example.tld", now.minusDays(1));
persistDeletedDomain("example.tld", fakeClock.nowUtc().minusDays(1));
runCommand("example.tld");
assertInStdout("Domain 'example.tld' does not exist or is deleted");
}
@@ -26,13 +26,20 @@ import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
import google.registry.testing.TmOverrideExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link GetResourceByKeyCommand}. */
class GetResourceByKeyCommandTest extends CommandTestCase<GetResourceByKeyCommand> {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
private DateTime now = DateTime.now(UTC);
@BeforeEach
@@ -20,6 +20,7 @@ import static google.registry.tools.LevelDbLogReader.HEADER_SIZE;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityTranslator;
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.tools.LevelDbLogReader.ChunkType;
import java.io.File;
import java.io.FileNotFoundException;
@@ -27,6 +28,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
/** Utility class for building a leveldb logfile. */
@DeleteAfterMigration
public final class LevelDbFileBuilder {
private final FileOutputStream out;
@@ -25,10 +25,12 @@ import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
import google.registry.model.contact.ContactResource;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.TmOverrideExtension;
import google.registry.tools.EntityWrapper.Property;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
@@ -40,6 +42,10 @@ public class LevelDbFileBuilderTest {
@TempDir Path tmpDir;
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.deleteResource;
import static google.registry.testing.DatabaseHelper.existsInDb;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
@@ -181,10 +182,10 @@ public class MutatingCommandTest {
+ registrar2 + "\n");
String results = command.execute();
assertThat(results).isEqualTo("Updated 4 entities.\n");
assertThat(loadByEntity(host1)).isNull();
assertThat(loadByEntity(host2)).isNull();
assertThat(loadByEntity(registrar1)).isNull();
assertThat(loadByEntity(registrar2)).isNull();
assertThat(existsInDb(host1)).isFalse();
assertThat(existsInDb(host2)).isFalse();
assertThat(existsInDb(registrar1)).isFalse();
assertThat(existsInDb(registrar2)).isFalse();
}
@Test
@@ -241,9 +242,9 @@ public class MutatingCommandTest {
+ "blockPremiumNames: false -> true\n");
String results = command.execute();
assertThat(results).isEqualTo("Updated 4 entities.\n");
assertThat(loadByEntity(host1)).isNull();
assertThat(existsInDb(host1)).isFalse();
assertThat(loadByEntity(host2)).isEqualTo(newHost2);
assertThat(loadByEntity(registrar1)).isNull();
assertThat(existsInDb(registrar1)).isFalse();
assertThat(loadByEntity(registrar2)).isEqualTo(newRegistrar2);
}
@@ -282,7 +283,7 @@ public class MutatingCommandTest {
IllegalStateException thrown = assertThrows(IllegalStateException.class, command::execute);
assertThat(thrown).hasMessageThat().contains("Entity changed since init() was called.");
assertThat(loadByEntity(host1)).isNull();
assertThat(existsInDb(host1)).isFalse();
assertThat(loadByEntity(host2)).isEqualTo(newHost2);
// These two shouldn't've changed.
assertThat(loadByEntity(registrar1)).isEqualTo(registrar1);
@@ -23,6 +23,7 @@ import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
import static google.registry.testing.DatabaseHelper.putInDb;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -334,8 +335,7 @@ class RegistrarContactCommandTest extends CommandTestCase<RegistrarContactComman
@Test
void testDelete_failsOnDomainWhoisAbuseContact() {
RegistrarContact registrarContact = loadRegistrar("NewRegistrar").getContacts().asList().get(0);
persistSimpleResource(
registrarContact.asBuilder().setVisibleInDomainWhoisAsAbuse(true).build());
putInDb(registrarContact.asBuilder().setVisibleInDomainWhoisAsAbuse(true).build());
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
@@ -31,12 +31,20 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.testing.TmOverrideExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit test for {@link RemoveRegistryOneKeyCommand}. */
public class RemoveRegistryOneKeyCommandTest extends CommandTestCase<RemoveRegistryOneKeyCommand> {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
DomainBase domain;
HistoryEntry historyEntry;
@@ -25,29 +25,20 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableMap;
import google.registry.model.domain.DomainBase;
import google.registry.model.ofy.Ofy;
import google.registry.model.registrar.Registrar;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import google.registry.util.Clock;
import java.util.List;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
/** Unit tests for {@link RenewDomainCommand}. */
public class RenewDomainCommandTest extends EppToolCommandTestCase<RenewDomainCommand> {
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private final Clock clock = new FakeClock(DateTime.parse("2015-04-05T05:05:05Z"));
@BeforeEach
void beforeEach() {
inject.setStaticField(Ofy.class, "clock", clock);
command.clock = clock;
fakeClock.setTo(DateTime.parse("2015-04-05T05:05:05Z"));
command.clock = fakeClock;
}
@Test
@@ -25,11 +25,18 @@ import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource;
import google.registry.model.ofy.CommitLogManifest;
import google.registry.model.ofy.CommitLogMutation;
import google.registry.testing.TmOverrideExtension;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ResaveEntitiesCommand}. */
class ResaveEntitiesCommandTest extends CommandTestCase<ResaveEntitiesCommand> {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@Test
void testSuccess_createsCommitLogs() throws Exception {
ContactResource contact1 = persistActiveContact("contact1");
@@ -28,12 +28,19 @@ import google.registry.model.ofy.CommitLogMutation;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.tld.Registry;
import google.registry.testing.TmOverrideExtension;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ResaveEnvironmentEntitiesCommand}. */
class ResaveEnvironmentEntitiesCommandTest
extends CommandTestCase<ResaveEnvironmentEntitiesCommand> {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@Test
void testSuccess_noop() throws Exception {
// Get rid of all the entities that this command runs on so that it does nothing.
@@ -22,11 +22,18 @@ import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource;
import google.registry.model.ofy.CommitLogManifest;
import google.registry.model.ofy.CommitLogMutation;
import google.registry.testing.TmOverrideExtension;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ResaveEppResourceCommand}. */
class ResaveEppResourcesCommandTest extends CommandTestCase<ResaveEppResourceCommand> {
@RegisterExtension
@Order(Order.DEFAULT - 1)
TmOverrideExtension tmOverrideExtension = TmOverrideExtension.withOfy();
@Test
void testSuccess_createsCommitLogs() throws Exception {
ContactResource contact = persistActiveContact("contact");

Some files were not shown because too many files have changed in this diff Show More