Import code from internal repository to git

This commit is contained in:
Justine Tunney 2016-03-01 17:18:14 -05:00
commit 0ef0c933d2
2490 changed files with 281594 additions and 0 deletions

View file

@ -0,0 +1,39 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
java_library(
name = "backup",
srcs = glob(["*.java"]),
resources = glob(["testdata/*"]),
deps = [
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/net",
"//java/com/google/common/primitives",
"//java/com/google/common/util/concurrent",
"//java/com/google/domain/registry/backup",
"//java/com/google/domain/registry/config",
"//java/com/google/domain/registry/model",
"//java/com/google/domain/registry/util",
"//javatests/com/google/domain/registry/testing",
"//third_party/java/appengine:appengine-api-testonly",
"//third_party/java/appengine_gcs_client",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//third_party/java/truth",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":backup"],
)

View file

@ -0,0 +1,105 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.backup;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.ofy.CommitLogCheckpointRoot.loadRoot;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TaskQueueHelper.assertNoTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
import com.google.domain.registry.model.ofy.CommitLogCheckpoint;
import com.google.domain.registry.model.ofy.CommitLogCheckpointRoot;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import com.google.domain.registry.util.Retrier;
import com.google.domain.registry.util.TaskEnqueuer;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
/** Unit tests for {@link CommitLogCheckpointAction}. */
@RunWith(MockitoJUnitRunner.class)
public class CommitLogCheckpointActionTest {
private static final String QUEUE_NAME = "export-commits";
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Mock
CommitLogCheckpointStrategy strategy;
DateTime now = DateTime.now(UTC);
CommitLogCheckpointAction task = new CommitLogCheckpointAction();
@Before
public void before() throws Exception {
task.clock = new FakeClock(now);
task.strategy = strategy;
task.taskEnqueuer = new TaskEnqueuer(new Retrier(null, 1));
when(strategy.computeCheckpoint()).thenReturn(
CommitLogCheckpoint.create(now, ImmutableMap.of(1, START_OF_TIME)));
}
@Test
public void testRun_noCheckpointEverWritten_writesCheckpointAndEnqueuesTask() throws Exception {
task.run();
assertTasksEnqueued(
QUEUE_NAME,
new TaskMatcher()
.url(ExportCommitLogDiffAction.PATH)
.param(ExportCommitLogDiffAction.LOWER_CHECKPOINT_TIME_PARAM, START_OF_TIME.toString())
.param(ExportCommitLogDiffAction.UPPER_CHECKPOINT_TIME_PARAM, now.toString()));
assertThat(loadRoot().getLastWrittenTime()).isEqualTo(now);
}
@Test
public void testRun_checkpointWrittenBeforeNow_writesCheckpointAndEnqueuesTask()
throws Exception {
DateTime oneMinuteAgo = now.minusMinutes(1);
persistResource(CommitLogCheckpointRoot.create(oneMinuteAgo));
task.run();
assertTasksEnqueued(
QUEUE_NAME,
new TaskMatcher()
.url(ExportCommitLogDiffAction.PATH)
.param(ExportCommitLogDiffAction.LOWER_CHECKPOINT_TIME_PARAM, oneMinuteAgo.toString())
.param(ExportCommitLogDiffAction.UPPER_CHECKPOINT_TIME_PARAM, now.toString()));
assertThat(loadRoot().getLastWrittenTime()).isEqualTo(now);
}
@Test
public void testRun_checkpointWrittenAfterNow_doesntOverwrite_orEnqueueTask() throws Exception {
DateTime oneMinuteFromNow = now.plusMinutes(1);
persistResource(CommitLogCheckpointRoot.create(oneMinuteFromNow));
task.run();
assertNoTasksEnqueued(QUEUE_NAME);
assertThat(loadRoot().getLastWrittenTime()).isEqualTo(oneMinuteFromNow);
}
}

View file

@ -0,0 +1,327 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.backup;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.ofy.CommitLogBucket.getBucketKey;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.domain.registry.config.TestRegistryConfig;
import com.google.domain.registry.model.ofy.CommitLogBucket;
import com.google.domain.registry.model.ofy.CommitLogCheckpoint;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.RegistryCursor;
import com.google.domain.registry.model.registry.RegistryCursor.CursorType;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.RegistryConfigRule;
import com.googlecode.objectify.VoidWork;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
/** Unit tests for {@link CommitLogCheckpointStrategy}. */
@RunWith(MockitoJUnitRunner.class)
public class CommitLogCheckpointStrategyTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final RegistryConfigRule configRule = new RegistryConfigRule();
final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01TZ"));
final Ofy ofy = new Ofy(clock);
final CommitLogCheckpointStrategy strategy = new CommitLogCheckpointStrategy();
/**
* Supplier to inject into CommitLogBucket for doling out predictable bucket IDs.
*
* <p>If not overridden, the supplier returns 1 so that other saves won't hit an NPE (since even
* if they use saveWithoutBackup() the transaction still selects a bucket key early).
*/
final FakeSupplier<Integer> fakeBucketIdSupplier = new FakeSupplier<>(1);
/** Gross but necessary supplier that can be modified to return the desired value. */
private static class FakeSupplier<T> implements Supplier<T> {
/** Default value to return if 'value' is not set. */
final T defaultValue;
/** Set this value field to make the supplier return this value. */
T value = null;
public FakeSupplier(T defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public T get() {
return value == null ? defaultValue : value;
}
}
@Before
public void before() throws Exception {
strategy.clock = clock;
strategy.ofy = ofy;
// Use three commit log buckets for easier but sufficiently complex testing.
configRule.override(new TestRegistryConfig() {
@Override
public int getCommitLogBucketCount() {
return 3;
}});
// Need to inject clock into Ofy so that createTld() below will get the right time.
inject.setStaticField(Ofy.class, "clock", clock);
// Inject a fake bucket ID supplier so we can dole out specific bucket IDs to commit logs.
inject.setStaticField(CommitLogBucket.class, "bucketIdSupplier", fakeBucketIdSupplier);
// Create some fake TLDs to parent RegistryCursor test objects under.
createTld("tld1");
createTld("tld2");
createTld("tld3");
clock.advanceOneMilli();
}
@Test
public void test_readBucketTimestamps_noCommitLogs() throws Exception {
assertThat(strategy.readBucketTimestamps())
.containsExactly(1, START_OF_TIME, 2, START_OF_TIME, 3, START_OF_TIME);
}
@Test
public void test_readBucketTimestamps_withSomeCommitLogs() throws Exception {
DateTime startTime = clock.nowUtc();
writeCommitLogToBucket(1);
clock.advanceOneMilli();
writeCommitLogToBucket(2);
assertThat(strategy.readBucketTimestamps())
.containsExactly(1, startTime, 2, startTime.plusMillis(1), 3, START_OF_TIME);
}
@Test
public void test_readBucketTimestamps_againAfterUpdate_reflectsUpdate() throws Exception {
DateTime firstTime = clock.nowUtc();
writeCommitLogToBucket(1);
writeCommitLogToBucket(2);
writeCommitLogToBucket(3);
assertThat(strategy.readBucketTimestamps().values())
.containsExactly(firstTime, firstTime, firstTime);
clock.advanceOneMilli();
writeCommitLogToBucket(1);
DateTime secondTime = clock.nowUtc();
assertThat(strategy.readBucketTimestamps())
.containsExactly(1, secondTime, 2, firstTime, 3, firstTime);
}
@Test
public void test_readNewCommitLogsAndFindThreshold_noCommitsAtAll_returnsEndOfTime() {
ImmutableMap<Integer, DateTime> bucketTimes =
ImmutableMap.of(1, START_OF_TIME, 2, START_OF_TIME, 3, START_OF_TIME);
assertThat(strategy.readNewCommitLogsAndFindThreshold(bucketTimes)).isEqualTo(END_OF_TIME);
}
@Test
public void test_readNewCommitLogsAndFindThreshold_noNewCommits_returnsEndOfTime() {
DateTime now = clock.nowUtc();
writeCommitLogToBucket(1);
clock.advanceOneMilli();
writeCommitLogToBucket(2);
clock.advanceOneMilli();
writeCommitLogToBucket(3);
ImmutableMap<Integer, DateTime> bucketTimes =
ImmutableMap.of(1, now, 2, now.plusMillis(1), 3, now.plusMillis(2));
assertThat(strategy.readNewCommitLogsAndFindThreshold(bucketTimes)).isEqualTo(END_OF_TIME);
}
@Test
public void test_readNewCommitLogsAndFindThreshold_tiedNewCommits_returnsCommitTimeMinusOne() {
DateTime now = clock.nowUtc();
writeCommitLogToBucket(1);
writeCommitLogToBucket(2);
writeCommitLogToBucket(3);
assertThat(strategy.readNewCommitLogsAndFindThreshold(
ImmutableMap.of(1, START_OF_TIME, 2, START_OF_TIME, 3, START_OF_TIME)))
.isEqualTo(now.minusMillis(1));
}
@Test
public void test_readNewCommitLogsAndFindThreshold_someNewCommits_returnsEarliestTimeMinusOne() {
DateTime now = clock.nowUtc();
writeCommitLogToBucket(1); // 1A
writeCommitLogToBucket(2); // 2A
writeCommitLogToBucket(3); // 3A
clock.advanceBy(Duration.millis(5));
writeCommitLogToBucket(1); // 1B
writeCommitLogToBucket(2); // 2B
writeCommitLogToBucket(3); // 3B
clock.advanceBy(Duration.millis(5));
writeCommitLogToBucket(1); // 1C
writeCommitLogToBucket(2); // 2C
writeCommitLogToBucket(3); // 3C
// First pass times: 1 at T0, 2 at T+5, 3 at T+10.
// Commits 1A, 2B, 3C are the commits seen in the first pass.
// Commits 2A, 3A, 3B are all old prior commits that should be ignored.
// Commit 1B is the first new commit for bucket 1, at T+5.
// Commit 1C is the second new commit for bucket 1, at T+10, and should be ignored.
// Commit 2C is the first new commit for bucket 2, at T+10.
// Since 1B as a new commit is older than 1C, T+5 is the oldest new commit time.
// Therefore, expect T+4 as the threshold time.
assertThat(strategy.readNewCommitLogsAndFindThreshold(
ImmutableMap.of(1, now, 2, now.plusMillis(5), 3, now.plusMillis(10))))
.isEqualTo(now.plusMillis(4));
}
@Test
public void test_readNewCommitLogsAndFindThreshold_commitsAtBucketTimes() {
DateTime now = clock.nowUtc();
ImmutableMap<Integer, DateTime> bucketTimes =
ImmutableMap.of(1, now.minusMillis(1), 2, now, 3, now.plusMillis(1));
assertThat(strategy.readNewCommitLogsAndFindThreshold(bucketTimes)).isEqualTo(END_OF_TIME);
}
@Test
public void test_computeBucketCheckpointTimes_earlyThreshold_setsEverythingToThreshold() {
DateTime now = clock.nowUtc();
ImmutableMap<Integer, DateTime> bucketTimes =
ImmutableMap.of(1, now.minusMillis(1), 2, now, 3, now.plusMillis(1));
assertThat(strategy.computeBucketCheckpointTimes(bucketTimes, now.minusMillis(2)).values())
.containsExactly(now.minusMillis(2), now.minusMillis(2), now.minusMillis(2));
}
@Test
public void test_computeBucketCheckpointTimes_middleThreshold_clampsToThreshold() {
DateTime now = clock.nowUtc();
ImmutableMap<Integer, DateTime> bucketTimes =
ImmutableMap.of(1, now.minusMillis(1), 2, now, 3, now.plusMillis(1));
assertThat(strategy.computeBucketCheckpointTimes(bucketTimes, now))
.containsExactly(1, now.minusMillis(1), 2, now, 3, now);
}
@Test
public void test_computeBucketCheckpointTimes_lateThreshold_leavesBucketTimesAsIs() {
DateTime now = clock.nowUtc();
ImmutableMap<Integer, DateTime> bucketTimes =
ImmutableMap.of(1, now.minusMillis(1), 2, now, 3, now.plusMillis(1));
assertThat(strategy.computeBucketCheckpointTimes(bucketTimes, now.plusMillis(2)))
.isEqualTo(bucketTimes);
}
@Test
public void test_computeCheckpoint_noCommitsAtAll_bucketCheckpointTimesAreStartOfTime() {
assertThat(strategy.computeCheckpoint())
.isEqualTo(CommitLogCheckpoint.create(
clock.nowUtc(),
ImmutableMap.of(1, START_OF_TIME, 2, START_OF_TIME, 3, START_OF_TIME)));
}
@Test
public void test_computeCheckpoint_noNewCommitLogs_bucketCheckpointTimesAreBucketTimes() {
DateTime now = clock.nowUtc();
writeCommitLogToBucket(1);
clock.advanceOneMilli();
writeCommitLogToBucket(2);
clock.advanceOneMilli();
writeCommitLogToBucket(3);
clock.advanceOneMilli();
DateTime checkpointTime = clock.nowUtc();
assertThat(strategy.computeCheckpoint())
.isEqualTo(CommitLogCheckpoint.create(
checkpointTime,
ImmutableMap.of(1, now, 2, now.plusMillis(1), 3, now.plusMillis(2))));
}
@Test
public void test_computeCheckpoint_someNewCommits_bucketCheckpointTimesAreClampedToThreshold() {
DateTime now = clock.nowUtc();
writeCommitLogToBucket(1); // 1A
writeCommitLogToBucket(2); // 2A
writeCommitLogToBucket(3); // 3A
clock.advanceBy(Duration.millis(5));
writeCommitLogToBucket(1); // 1B
writeCommitLogToBucket(2); // 2B
writeCommitLogToBucket(3); // 3B
clock.advanceBy(Duration.millis(5));
writeCommitLogToBucket(1); // 1C
writeCommitLogToBucket(2); // 2C
writeCommitLogToBucket(3); // 3C
// Set first pass times: 1 at T0, 2 at T+5, 3 at T+10.
saveBucketWithLastWrittenTime(1, now);
saveBucketWithLastWrittenTime(2, now.plusMillis(5));
saveBucketWithLastWrittenTime(3, now.plusMillis(10));
// Commits 1A, 2B, 3C are the commits seen in the first pass.
// Commits 2A, 3A, 3B are all old prior commits that should be ignored.
// Commit 1B is the first new commit for bucket 1, at T+5.
// Commit 1C is the second new commit for bucket 1, at T+10, and should be ignored.
// Commit 2C is the first new commit for bucket 2, at T+10.
// Since 1B as a new commit is older than 1C, T+5 is the oldest new commit time.
// Therefore, expect T+4 as the threshold time.
DateTime threshold = now.plusMillis(4);
// Advance clock before taking checkpoint.
clock.advanceBy(Duration.millis(10));
DateTime checkpointTime = clock.nowUtc();
// Bucket checkpoint times should be clamped as expected.
assertThat(strategy.computeCheckpoint())
.isEqualTo(CommitLogCheckpoint.create(
checkpointTime,
ImmutableMap.of(1, now, 2, threshold, 3, threshold)));
}
private void writeCommitLogToBucket(final int bucketId) {
fakeBucketIdSupplier.value = bucketId;
ofy.transact(
new VoidWork() {
@Override
public void vrun() {
String tld = "tld" + bucketId;
RegistryCursor.save(Registry.get(tld), CursorType.RDE_REPORT, ofy.getTransactionTime());
}
});
fakeBucketIdSupplier.value = null;
}
private void saveBucketWithLastWrittenTime(final int bucketId, final DateTime lastWrittenTime) {
ofy.transact(new VoidWork() {
@Override
public void vrun() {
ofy.saveWithoutBackup().entity(
CommitLogBucket.loadBucket(getBucketKey(bucketId)).asBuilder()
.setLastWrittenTime(lastWrittenTime)
.build());
}});
}
}

View file

@ -0,0 +1,177 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.backup;
import static com.google.common.truth.Truth.assertThat;
import static org.joda.time.Duration.millis;
import com.google.domain.registry.config.TestRegistryConfig;
import com.google.domain.registry.model.ofy.CommitLogManifest;
import com.google.domain.registry.model.ofy.CommitLogMutation;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.RegistryConfigRule;
import com.googlecode.objectify.VoidWork;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DeleteOldCommitLogsAction}. */
@RunWith(JUnit4.class)
public class DeleteOldCommitLogsActionTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Rule
public final RegistryConfigRule configRule = new RegistryConfigRule();
@Rule
public final ExceptionRule thrown = new ExceptionRule();
private final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01TZ"));
private final Ofy ofy = new Ofy(clock);
private final DeleteOldCommitLogsAction task = new DeleteOldCommitLogsAction();
@Before
public void before() throws Exception {
task.bucketNum = 1;
task.clock = clock;
task.maxAge = Duration.millis(2);
task.maxDeletes = 4;
task.ofy = ofy;
}
@Test
public void testRun_noCommitLogs_doesNothing() throws Exception {
assertManifestAndMutationCounts(0, 0);
task.run();
assertManifestAndMutationCounts(0, 0);
}
@Test
public void testRun_commitLogNewerThanThreshold_doesntGetDeleted() throws Exception {
createCommitLog();
clock.advanceOneMilli();
assertManifestAndMutationCounts(1, 2);
task.run();
assertManifestAndMutationCounts(1, 2);
}
@Test
public void testRun_commitLogEqualToThreshold_doesntGetDeleted() throws Exception {
createCommitLog();
clock.advanceBy(millis(2));
task.run();
assertManifestAndMutationCounts(1, 2);
}
@Test
public void testRun_commitLogOlderThanThreshold_getsDeleted() throws Exception {
createCommitLog();
clock.advanceBy(millis(3));
task.run();
assertManifestAndMutationCounts(0, 0);
}
@Test
public void testRun_oneOlderThanThresholdAndOneNewer_onlyOldOneIsDeleted() throws Exception {
createCommitLog();
clock.advanceBy(millis(3));
createCommitLog();
assertManifestAndMutationCounts(2, 4);
task.run();
assertManifestAndMutationCounts(1, 2);
}
@Test
public void testRun_twoOlderThanThreshold_bothGetDeletedInSameTransaction() throws Exception {
task.maxDeletes = 2;
createCommitLog();
clock.advanceOneMilli();
createCommitLog();
clock.advanceBy(millis(3));
assertManifestAndMutationCounts(2, 4);
task.run();
assertManifestAndMutationCounts(0, 0);
}
@Test
public void testRun_twoOlderThanThreshold_bothGetDeletedInTwoTransactions() throws Exception {
task.maxDeletes = 1;
createCommitLog();
clock.advanceOneMilli();
createCommitLog();
clock.advanceBy(millis(3));
createCommitLog();
assertManifestAndMutationCounts(3, 6);
task.run();
assertManifestAndMutationCounts(2, 4);
task.run();
assertManifestAndMutationCounts(1, 2);
}
@Test
public void testRun_commitLogOlderButInADifferentBucket_doesntGetDeleted() throws Exception {
createCommitLog();
clock.advanceBy(millis(31337));
configRule.override(new TestRegistryConfig() {
@Override public int getCommitLogBucketCount() { return 2; }
});
task.bucketNum = 2;
task.run();
assertManifestAndMutationCounts(1, 2);
}
@Test
public void testRun_lessThanATenthOfOldData_doesntGetDeleted() throws Exception {
task.maxDeletes = 20;
createCommitLog();
clock.advanceBy(millis(2));
task.run();
assertManifestAndMutationCounts(1, 2);
}
private void assertManifestAndMutationCounts(int manifestCount, int mutationCount) {
assertThat(ofy.load().type(CommitLogManifest.class).count()).isEqualTo(manifestCount);
assertThat(ofy.load().type(CommitLogMutation.class).count()).isEqualTo(mutationCount);
}
private void createCommitLog() {
ofy.transact(new VoidWork() {
@Override
public void vrun() {
ofy.save().entity(
Registrar.loadByClientId("NewRegistrar").asBuilder()
.setEmailAddress("pumpkin@cat.test")
.build());
ofy.save().entity(
Registrar.loadByClientId("TheRegistrar").asBuilder()
.setReferralUrl("http://justine.test")
.build());
}});
}
}

View file

@ -0,0 +1,397 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.backup;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.domain.registry.backup.BackupUtils.GcsMetadataKeys.LOWER_BOUND_CHECKPOINT;
import static com.google.domain.registry.backup.BackupUtils.GcsMetadataKeys.NUM_TRANSACTIONS;
import static com.google.domain.registry.backup.BackupUtils.GcsMetadataKeys.UPPER_BOUND_CHECKPOINT;
import static com.google.domain.registry.backup.BackupUtils.deserializeEntities;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.appengine.tools.cloudstorage.GcsServiceFactory;
import com.google.common.collect.ImmutableMap;
import com.google.domain.registry.config.TestRegistryConfig;
import com.google.domain.registry.model.ImmutableObject;
import com.google.domain.registry.model.ofy.CommitLogBucket;
import com.google.domain.registry.model.ofy.CommitLogCheckpoint;
import com.google.domain.registry.model.ofy.CommitLogManifest;
import com.google.domain.registry.model.ofy.CommitLogMutation;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.GcsTestingUtils;
import com.google.domain.registry.testing.RegistryConfigRule;
import com.google.domain.registry.testing.TestObject;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyService;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.List;
/** Unit tests for {@link ExportCommitLogDiffAction}. */
@RunWith(JUnit4.class)
public class ExportCommitLogDiffActionTest {
private static final int NUM_BUCKETS = 3;
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Rule
public final RegistryConfigRule configRule = new RegistryConfigRule(
new TestRegistryConfig() {
@Override public int getCommitLogBucketCount() {
return NUM_BUCKETS;
}});
/** Local GCS service available for testing. */
private final GcsService gcsService = GcsServiceFactory.createGcsService();
private final DateTime now = DateTime.now(UTC);
private final DateTime oneMinuteAgo = now.minusMinutes(1);
private final ExportCommitLogDiffAction task = new ExportCommitLogDiffAction();
@Before
public void before() {
ObjectifyService.register(TestObject.class);
task.gcsService = gcsService;
task.gcsBucket = "gcs bucket";
task.batchSize = 5;
}
@Test
public void testRun_noCommitHistory_onlyUpperCheckpointExported() throws Exception {
task.lowerCheckpointTime = oneMinuteAgo;
task.upperCheckpointTime = now;
persistResource(CommitLogCheckpoint.create(
oneMinuteAgo,
ImmutableMap.of(1, oneMinuteAgo, 2, oneMinuteAgo, 3, oneMinuteAgo)));
CommitLogCheckpoint upperCheckpoint = persistResource(CommitLogCheckpoint.create(
now,
ImmutableMap.of(1, now, 2, now, 3, now)));
// Don't persist any manifests or mutations.
task.run();
GcsFilename expectedFilename = new GcsFilename("gcs bucket", "commit_diff_until_" + now);
assertWithMessage("GCS file not found: " + expectedFilename)
.that(gcsService.getMetadata(expectedFilename)).isNotNull();
assertThat(gcsService.getMetadata(expectedFilename).getOptions().getUserMetadata())
.containsExactly(
LOWER_BOUND_CHECKPOINT,
oneMinuteAgo.toString(),
UPPER_BOUND_CHECKPOINT,
now.toString(),
NUM_TRANSACTIONS,
"0");
List<ImmutableObject> exported =
deserializeEntities(GcsTestingUtils.readGcsFile(gcsService, expectedFilename));
assertThat(exported).containsExactly(upperCheckpoint);
}
@Test
public void testRun_regularCommitHistory_exportsCorrectCheckpointDiff() throws Exception {
task.lowerCheckpointTime = oneMinuteAgo;
task.upperCheckpointTime = now;
// Persist the lower and upper checkpoints, with 3 buckets each and staggered times. We respect
// the real invariant that the time for bucket n in the lower checkpoint is <= the time for
// that bucket in the upper.
persistResource(CommitLogCheckpoint.create(
oneMinuteAgo,
ImmutableMap.of(
1, oneMinuteAgo,
2, oneMinuteAgo.minusDays(1),
3, oneMinuteAgo.minusDays(2))));
CommitLogCheckpoint upperCheckpoint = persistResource(CommitLogCheckpoint.create(
now,
ImmutableMap.of(
1, now,
2, now.minusDays(1),
3, oneMinuteAgo.minusDays(2)))); // Note that this matches the lower bound.
// Persist some fake commit log manifests.
// These shouldn't be in the diff because the lower bound is exclusive.
persistManifestAndMutation(1, oneMinuteAgo);
persistManifestAndMutation(2, oneMinuteAgo.minusDays(1));
persistManifestAndMutation(3, oneMinuteAgo.minusDays(2)); // Even though it's == upper bound.
// These shouldn't be in the diff because they are above the upper bound.
persistManifestAndMutation(1, now.plusMillis(1));
persistManifestAndMutation(2, now.minusDays(1).plusMillis(1));
persistManifestAndMutation(3, oneMinuteAgo.minusDays(2).plusMillis(1));
// These should be in the diff because they are between the bounds. (Not possible for bucket 3.)
persistManifestAndMutation(1, now.minusMillis(1));
persistManifestAndMutation(2, now.minusDays(1).minusMillis(1));
// These should be in the diff because they are at the upper bound. (Not possible for bucket 3.)
persistManifestAndMutation(1, now);
persistManifestAndMutation(2, now.minusDays(1));
task.run();
GcsFilename expectedFilename = new GcsFilename("gcs bucket", "commit_diff_until_" + now);
assertWithMessage("GCS file not found: " + expectedFilename)
.that(gcsService.getMetadata(expectedFilename)).isNotNull();
assertThat(gcsService.getMetadata(expectedFilename).getOptions().getUserMetadata())
.containsExactly(
LOWER_BOUND_CHECKPOINT,
oneMinuteAgo.toString(),
UPPER_BOUND_CHECKPOINT,
now.toString(),
NUM_TRANSACTIONS,
"4");
List<ImmutableObject> exported =
deserializeEntities(GcsTestingUtils.readGcsFile(gcsService, expectedFilename));
assertThat(exported.get(0)).isEqualTo(upperCheckpoint);
// We expect these manifests, in time order, with matching mutations.
CommitLogManifest manifest1 = createManifest(2, now.minusDays(1).minusMillis(1));
CommitLogManifest manifest2 = createManifest(2, now.minusDays(1));
CommitLogManifest manifest3 = createManifest(1, now.minusMillis(1));
CommitLogManifest manifest4 = createManifest(1, now);
assertThat(exported).containsExactly(
upperCheckpoint,
manifest1,
createMutation(manifest1),
manifest2,
createMutation(manifest2),
manifest3,
createMutation(manifest3),
manifest4,
createMutation(manifest4))
.inOrder();
}
@Test
public void testRun_simultaneousTransactions_bothExported() throws Exception {
task.lowerCheckpointTime = oneMinuteAgo;
task.upperCheckpointTime = now;
persistResource(CommitLogCheckpoint.create(
oneMinuteAgo,
ImmutableMap.of(1, START_OF_TIME, 2, START_OF_TIME, 3, START_OF_TIME)));
CommitLogCheckpoint upperCheckpoint = persistResource(CommitLogCheckpoint.create(
now,
ImmutableMap.of(1, now, 2, now, 3, now)));
// Persist some fake commit log manifests that are at the same time but in different buckets.
persistManifestAndMutation(1, oneMinuteAgo);
persistManifestAndMutation(2, oneMinuteAgo);
persistManifestAndMutation(1, now);
persistManifestAndMutation(2, now);
task.run();
GcsFilename expectedFilename = new GcsFilename("gcs bucket", "commit_diff_until_" + now);
assertWithMessage("GCS file not found: " + expectedFilename)
.that(gcsService.getMetadata(expectedFilename)).isNotNull();
assertThat(gcsService.getMetadata(expectedFilename).getOptions().getUserMetadata())
.containsExactly(
LOWER_BOUND_CHECKPOINT,
oneMinuteAgo.toString(),
UPPER_BOUND_CHECKPOINT,
now.toString(),
NUM_TRANSACTIONS,
"4");
List<ImmutableObject> exported =
deserializeEntities(GcsTestingUtils.readGcsFile(gcsService, expectedFilename));
assertThat(exported.get(0)).isEqualTo(upperCheckpoint);
// We expect these manifests, in the order below, with matching mutations.
CommitLogManifest manifest1 = createManifest(1, oneMinuteAgo);
CommitLogManifest manifest2 = createManifest(2, oneMinuteAgo);
CommitLogManifest manifest3 = createManifest(1, now);
CommitLogManifest manifest4 = createManifest(2, now);
assertThat(exported).containsExactly(
upperCheckpoint,
manifest1,
createMutation(manifest1),
manifest2,
createMutation(manifest2),
manifest3,
createMutation(manifest3),
manifest4,
createMutation(manifest4))
.inOrder();
}
@Test
public void testRun_exportsAcrossMultipleBatches() throws Exception {
task.batchSize = 2;
task.lowerCheckpointTime = oneMinuteAgo;
task.upperCheckpointTime = now;
persistResource(CommitLogCheckpoint.create(
oneMinuteAgo,
ImmutableMap.of(1, START_OF_TIME, 2, START_OF_TIME, 3, START_OF_TIME)));
CommitLogCheckpoint upperCheckpoint = persistResource(CommitLogCheckpoint.create(
now,
ImmutableMap.of(1, now, 2, now, 3, now)));
// Persist some fake commit log manifests.
persistManifestAndMutation(1, oneMinuteAgo);
persistManifestAndMutation(2, oneMinuteAgo);
persistManifestAndMutation(3, oneMinuteAgo);
persistManifestAndMutation(1, now);
persistManifestAndMutation(2, now);
persistManifestAndMutation(3, now);
task.run();
GcsFilename expectedFilename = new GcsFilename("gcs bucket", "commit_diff_until_" + now);
assertWithMessage("GCS file not found: " + expectedFilename)
.that(gcsService.getMetadata(expectedFilename)).isNotNull();
assertThat(gcsService.getMetadata(expectedFilename).getOptions().getUserMetadata())
.containsExactly(
LOWER_BOUND_CHECKPOINT,
oneMinuteAgo.toString(),
UPPER_BOUND_CHECKPOINT,
now.toString(),
NUM_TRANSACTIONS,
"6");
List<ImmutableObject> exported =
deserializeEntities(GcsTestingUtils.readGcsFile(gcsService, expectedFilename));
assertThat(exported.get(0)).isEqualTo(upperCheckpoint);
// We expect these manifests, in the order below, with matching mutations.
CommitLogManifest manifest1 = createManifest(1, oneMinuteAgo);
CommitLogManifest manifest2 = createManifest(2, oneMinuteAgo);
CommitLogManifest manifest3 = createManifest(3, oneMinuteAgo);
CommitLogManifest manifest4 = createManifest(1, now);
CommitLogManifest manifest5 = createManifest(2, now);
CommitLogManifest manifest6 = createManifest(3, now);
assertThat(exported).containsExactly(
upperCheckpoint,
manifest1,
createMutation(manifest1),
manifest2,
createMutation(manifest2),
manifest3,
createMutation(manifest3),
manifest4,
createMutation(manifest4),
manifest5,
createMutation(manifest5),
manifest6,
createMutation(manifest6))
.inOrder();
}
@Test
public void testRun_checkpointDiffWithNeverTouchedBuckets_exportsCorrectly() throws Exception {
task.lowerCheckpointTime = oneMinuteAgo;
task.upperCheckpointTime = now;
persistResource(CommitLogCheckpoint.create(
oneMinuteAgo,
ImmutableMap.of(1, START_OF_TIME, 2, START_OF_TIME, 3, START_OF_TIME)));
CommitLogCheckpoint upperCheckpoint = persistResource(CommitLogCheckpoint.create(
now,
ImmutableMap.of(1, START_OF_TIME, 2, START_OF_TIME, 3, START_OF_TIME)));
// Don't persist any commit log manifests; we're just checking that the task runs correctly
// even if the upper timestamp contains START_OF_TIME values.
task.run();
GcsFilename expectedFilename = new GcsFilename("gcs bucket", "commit_diff_until_" + now);
assertWithMessage("GCS file not found: " + expectedFilename)
.that(gcsService.getMetadata(expectedFilename)).isNotNull();
assertThat(gcsService.getMetadata(expectedFilename).getOptions().getUserMetadata())
.containsExactly(
LOWER_BOUND_CHECKPOINT,
oneMinuteAgo.toString(),
UPPER_BOUND_CHECKPOINT,
now.toString(),
NUM_TRANSACTIONS,
"0");
List<ImmutableObject> exported =
deserializeEntities(GcsTestingUtils.readGcsFile(gcsService, expectedFilename));
// We expect no manifests or mutations, only the upper checkpoint.
assertThat(exported).containsExactly(upperCheckpoint);
}
@Test
public void testRun_exportingFromStartOfTime_exportsAllCommits() throws Exception {
task.lowerCheckpointTime = START_OF_TIME;
task.upperCheckpointTime = now;
CommitLogCheckpoint upperCheckpoint = persistResource(CommitLogCheckpoint.create(
now,
ImmutableMap.of(1, now, 2, now, 3, now)));
// Persist some fake commit log manifests.
persistManifestAndMutation(1, START_OF_TIME.plusMillis(1)); // Oldest possible manifest time.
persistManifestAndMutation(2, oneMinuteAgo);
persistManifestAndMutation(3, now);
task.run();
GcsFilename expectedFilename = new GcsFilename("gcs bucket", "commit_diff_until_" + now);
assertWithMessage("GCS file not found: " + expectedFilename)
.that(gcsService.getMetadata(expectedFilename)).isNotNull();
assertThat(gcsService.getMetadata(expectedFilename).getOptions().getUserMetadata())
.containsExactly(
LOWER_BOUND_CHECKPOINT,
START_OF_TIME.toString(),
UPPER_BOUND_CHECKPOINT,
now.toString(),
NUM_TRANSACTIONS,
"3");
List<ImmutableObject> exported =
deserializeEntities(GcsTestingUtils.readGcsFile(gcsService, expectedFilename));
assertThat(exported.get(0)).isEqualTo(upperCheckpoint);
// We expect these manifests, in the order below, with matching mutations.
CommitLogManifest manifest1 = createManifest(1, START_OF_TIME.plusMillis(1));
CommitLogManifest manifest2 = createManifest(2, oneMinuteAgo);
CommitLogManifest manifest3 = createManifest(3, now);
assertThat(exported).containsExactly(
upperCheckpoint,
manifest1,
createMutation(manifest1),
manifest2,
createMutation(manifest2),
manifest3,
createMutation(manifest3))
.inOrder();
}
private CommitLogManifest createManifest(int bucketNum, DateTime commitTime) {
return CommitLogManifest.create(CommitLogBucket.getBucketKey(bucketNum), commitTime, null);
}
private CommitLogMutation createMutation(CommitLogManifest manifest) {
return CommitLogMutation.create(
Key.create(manifest),
TestObject.create(manifest.getCommitTime().toString()));
}
private void persistManifestAndMutation(int bucketNum, DateTime commitTime) {
persistResource(
createMutation(persistResource(createManifest(bucketNum, commitTime))));
}
}

View file

@ -0,0 +1,153 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.backup;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService;
import static com.google.domain.registry.backup.BackupUtils.GcsMetadataKeys.LOWER_BOUND_CHECKPOINT;
import static com.google.domain.registry.backup.ExportCommitLogDiffAction.DIFF_FILE_PREFIX;
import static java.lang.reflect.Proxy.newProxyInstance;
import static org.joda.time.DateTimeZone.UTC;
import com.google.appengine.tools.cloudstorage.GcsFileMetadata;
import com.google.appengine.tools.cloudstorage.GcsFileOptions;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.appengine.tools.cloudstorage.GcsServiceFactory;
import com.google.appengine.tools.cloudstorage.ListItem;
import com.google.appengine.tools.cloudstorage.ListResult;
import com.google.common.base.Function;
import com.google.common.collect.Iterators;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.TestObject;
import com.googlecode.objectify.ObjectifyService;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
/** Unit tests for {@link GcsDiffFileLister}. */
@RunWith(JUnit4.class)
public class GcsDiffFileListerTest {
static final String GCS_BUCKET = "gcs bucket";
final DateTime now = DateTime.now(UTC);
final GcsDiffFileLister diffLister = new GcsDiffFileLister();
final GcsService gcsService = GcsServiceFactory.createGcsService();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Before
public void before() throws Exception {
ObjectifyService.register(TestObject.class);
diffLister.gcsService = gcsService;
diffLister.gcsBucket = GCS_BUCKET;
diffLister.executor = newDirectExecutorService();
for (int i = 0; i < 5; i++) {
gcsService.createOrReplace(
new GcsFilename(GCS_BUCKET, DIFF_FILE_PREFIX + now.minusMinutes(i)),
new GcsFileOptions.Builder()
.addUserMetadata(LOWER_BOUND_CHECKPOINT, now.minusMinutes(i + 1).toString())
.build(),
ByteBuffer.wrap(new byte[]{1, 2, 3}));
}
}
private Iterable<DateTime> extractTimesFromDiffFiles(List<GcsFileMetadata> diffFiles) {
return transform(
diffFiles,
new Function<GcsFileMetadata, DateTime>() {
@Override
public DateTime apply(GcsFileMetadata metadata) {
return DateTime.parse(
metadata.getFilename().getObjectName().substring(DIFF_FILE_PREFIX.length()));
}});
}
@Test
public void testList_noFilesFound() {
DateTime fromTime = now.plusMillis(1);
assertThat(extractTimesFromDiffFiles(diffLister.listDiffFiles(fromTime))).isEmpty();
}
@Test
public void testList_findsFiles_backToFromTimeExclusive() {
DateTime fromTime = now.minusMinutes(2);
assertThat(extractTimesFromDiffFiles(diffLister.listDiffFiles(fromTime)))
.containsExactly(now.minusMinutes(1), now)
.inOrder();
}
@Test
public void testList_patchesHoles() throws Exception {
// Fake out the GCS list() method to return only the first and last file.
// We can't use Mockito.spy() because GcsService's impl is final.
diffLister.gcsService = (GcsService) newProxyInstance(
GcsService.class.getClassLoader(),
new Class<?>[] {GcsService.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("list")) {
// ListResult is an incredibly annoying thing to construct. It needs to be fed from a
// Callable that returns Iterators, each representing a batch of results.
return new ListResult(new Callable<Iterator<ListItem>>() {
boolean called = false;
@Override
public Iterator<ListItem> call() throws Exception {
try {
return called ? null : Iterators.forArray(
new ListItem.Builder()
.setName(DIFF_FILE_PREFIX + now)
.build(),
new ListItem.Builder()
.setName(DIFF_FILE_PREFIX + now.minusMinutes(4))
.build());
} finally {
called = true;
}
}});
}
return method.invoke(gcsService, args);
}});
DateTime fromTime = now.minusMinutes(4).minusSeconds(1);
// Request all files with checkpoint > fromTime.
assertThat(extractTimesFromDiffFiles(diffLister.listDiffFiles(fromTime)))
.containsExactly(
now.minusMinutes(4),
now.minusMinutes(3),
now.minusMinutes(2),
now.minusMinutes(1),
now)
.inOrder();
}
}

View file

@ -0,0 +1,319 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.backup;
import static com.google.appengine.tools.cloudstorage.GcsServiceFactory.createGcsService;
import static com.google.common.base.Functions.constant;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Maps.toMap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService;
import static com.google.domain.registry.backup.BackupUtils.GcsMetadataKeys.LOWER_BOUND_CHECKPOINT;
import static com.google.domain.registry.backup.BackupUtils.serializeEntity;
import static com.google.domain.registry.backup.ExportCommitLogDiffAction.DIFF_FILE_PREFIX;
import static com.google.domain.registry.model.ofy.CommitLogBucket.getBucketIds;
import static com.google.domain.registry.model.ofy.CommitLogBucket.getBucketKey;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static java.util.Arrays.asList;
import static org.joda.time.DateTimeZone.UTC;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.tools.cloudstorage.GcsFileOptions;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.primitives.Longs;
import com.google.domain.registry.config.TestRegistryConfig;
import com.google.domain.registry.model.ImmutableObject;
import com.google.domain.registry.model.ofy.CommitLogBucket;
import com.google.domain.registry.model.ofy.CommitLogCheckpoint;
import com.google.domain.registry.model.ofy.CommitLogCheckpointRoot;
import com.google.domain.registry.model.ofy.CommitLogManifest;
import com.google.domain.registry.model.ofy.CommitLogMutation;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.FakeSleeper;
import com.google.domain.registry.testing.RegistryConfigRule;
import com.google.domain.registry.testing.TestObject;
import com.google.domain.registry.util.Retrier;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyService;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/** Unit tests for {@link RestoreCommitLogsAction}. */
@RunWith(JUnit4.class)
public class RestoreCommitLogsActionTest {
static final String GCS_BUCKET = "gcs bucket";
final DateTime now = DateTime.now(UTC);
final RestoreCommitLogsAction action = new RestoreCommitLogsAction();
final GcsService gcsService = createGcsService();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Rule
public final RegistryConfigRule configRule = new RegistryConfigRule();
@Before
public void init() {
ObjectifyService.register(TestObject.class);
action.gcsService = gcsService;
action.dryRun = false;
action.datastoreService = DatastoreServiceFactory.getDatastoreService();
action.fromTime = now.minusMillis(1);
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 1);
action.diffLister = new GcsDiffFileLister();
action.diffLister.gcsService = gcsService;
action.diffLister.gcsBucket = GCS_BUCKET;
action.diffLister.executor = newDirectExecutorService();
configRule.override(new TestRegistryConfig() {
@Override
public int getCommitLogBucketCount() {
return 3;
}});
}
@Test
public void testRestore_multipleDiffFiles() throws Exception {
ofy().saveWithoutBackup().entities(
TestObject.create("previous to keep"),
TestObject.create("previous to delete")).now();
// Create 3 transactions, across two diff files.
// Before: {"previous to keep", "previous to delete"}
// 1a: Add {"a", "b"}, Delete {"previous to delete"}
// 1b: Add {"c", "d"}, Delete {"a"}
// 2: Add {"e", "f"}, Delete {"c"}
// After: {"previous to keep", "b", "d", "e", "f"}
Key<CommitLogManifest> manifest1aKey =
CommitLogManifest.createKey(getBucketKey(1), now.minusMinutes(3));
Key<CommitLogManifest> manifest1bKey =
CommitLogManifest.createKey(getBucketKey(2), now.minusMinutes(2));
Key<CommitLogManifest> manifest2Key =
CommitLogManifest.createKey(getBucketKey(1), now.minusMinutes(1));
saveDiffFileNotToRestore(now.minusMinutes(2));
Iterable<ImmutableObject> file1CommitLogs = saveDiffFile(
createCheckpoint(now.minusMinutes(1)),
CommitLogManifest.create(
getBucketKey(1),
now.minusMinutes(3),
ImmutableSet.<Key<?>>of(Key.create(TestObject.create("previous to delete")))),
CommitLogMutation.create(manifest1aKey, TestObject.create("a")),
CommitLogMutation.create(manifest1aKey, TestObject.create("b")),
CommitLogManifest.create(
getBucketKey(2),
now.minusMinutes(2),
ImmutableSet.<Key<?>>of(Key.create(TestObject.create("a")))),
CommitLogMutation.create(manifest1bKey, TestObject.create("c")),
CommitLogMutation.create(manifest1bKey, TestObject.create("d")));
Iterable<ImmutableObject> file2CommitLogs = saveDiffFile(
createCheckpoint(now),
CommitLogManifest.create(
getBucketKey(1),
now.minusMinutes(1),
ImmutableSet.<Key<?>>of(Key.create(TestObject.create("c")))),
CommitLogMutation.create(manifest2Key, TestObject.create("e")),
CommitLogMutation.create(manifest2Key, TestObject.create("f")));
action.fromTime = now.minusMinutes(1).minusMillis(1);
action.run();
ofy().clearSessionCache();
assertExpectedIds("previous to keep", "b", "d", "e", "f");
assertInDatastore(file1CommitLogs);
assertInDatastore(file2CommitLogs);
assertInDatastore(asList(CommitLogCheckpointRoot.create(now)));
assertCommitLogBuckets(ImmutableMap.of(1, now.minusMinutes(1), 2, now.minusMinutes(2)));
}
@Test
public void testRestore_noManifests() throws Exception {
ofy().saveWithoutBackup().entity(
TestObject.create("previous to keep")).now();
saveDiffFileNotToRestore(now.minusMinutes(1));
Iterable<ImmutableObject> commitLogs = saveDiffFile(createCheckpoint(now));
action.run();
ofy().clearSessionCache();
assertExpectedIds("previous to keep");
assertInDatastore(commitLogs);
assertInDatastore(asList(CommitLogCheckpointRoot.create(now)));
assertCommitLogBuckets(ImmutableMap.<Integer, DateTime>of());
}
@Test
public void testRestore_manifestWithNoDeletions() throws Exception {
ofy().saveWithoutBackup().entity(TestObject.create("previous to keep")).now();
Key<CommitLogBucket> bucketKey = getBucketKey(1);
Key<CommitLogManifest> manifestKey = CommitLogManifest.createKey(bucketKey, now);
saveDiffFileNotToRestore(now.minusMinutes(1));
Iterable<ImmutableObject> commitLogs = saveDiffFile(
createCheckpoint(now),
CommitLogManifest.create(bucketKey, now, null),
CommitLogMutation.create(manifestKey, TestObject.create("a")),
CommitLogMutation.create(manifestKey, TestObject.create("b")));
action.run();
ofy().clearSessionCache();
assertExpectedIds("previous to keep", "a", "b");
assertInDatastore(commitLogs);
assertInDatastore(asList(CommitLogCheckpointRoot.create(now)));
assertCommitLogBuckets(ImmutableMap.of(1, now));
}
@Test
public void testRestore_manifestWithNoMutations() throws Exception {
ofy().saveWithoutBackup().entities(
TestObject.create("previous to keep"),
TestObject.create("previous to delete")).now();
saveDiffFileNotToRestore(now.minusMinutes(1));
Iterable<ImmutableObject> commitLogs = saveDiffFile(
createCheckpoint(now),
CommitLogManifest.create(
getBucketKey(1),
now,
ImmutableSet.<Key<?>>of(Key.create(TestObject.create("previous to delete")))));
action.run();
ofy().clearSessionCache();
assertExpectedIds("previous to keep");
assertInDatastore(commitLogs);
assertInDatastore(asList(CommitLogCheckpointRoot.create(now)));
assertCommitLogBuckets(ImmutableMap.of(1, now));
}
// This is a pathological case that shouldn't be possible, but we should be robust to it.
@Test
public void testRestore_manifestWithNoMutationsOrDeletions() throws Exception {
ofy().saveWithoutBackup().entities(
TestObject.create("previous to keep")).now();
saveDiffFileNotToRestore(now.minusMinutes(1));
Iterable<ImmutableObject> commitLogs = saveDiffFile(
createCheckpoint(now),
CommitLogManifest.create(getBucketKey(1), now, null));
action.run();
ofy().clearSessionCache();
assertExpectedIds("previous to keep");
assertInDatastore(commitLogs);
assertInDatastore(asList(CommitLogCheckpointRoot.create(now)));
assertCommitLogBuckets(ImmutableMap.of(1, now));
}
@Test
public void testRestore_mutateExistingEntity() throws Exception {
ofy().saveWithoutBackup().entity(TestObject.create("existing", "a")).now();
Key<CommitLogManifest> manifestKey = CommitLogManifest.createKey(getBucketKey(1), now);
saveDiffFileNotToRestore(now.minusMinutes(1));
Iterable<ImmutableObject> commitLogs = saveDiffFile(
createCheckpoint(now),
CommitLogManifest.create(getBucketKey(1), now, null),
CommitLogMutation.create(manifestKey, TestObject.create("existing", "b")));
action.run();
ofy().clearSessionCache();
assertThat(ofy().load().entity(TestObject.create("existing")).now().getField()).isEqualTo("b");
assertInDatastore(commitLogs);
assertInDatastore(asList(CommitLogCheckpointRoot.create(now)));
assertCommitLogBuckets(ImmutableMap.of(1, now));
}
// This should be harmless; deletes are idempotent.
@Test
public void testRestore_deleteMissingEntity() throws Exception {
ofy().saveWithoutBackup().entity(TestObject.create("previous to keep", "a")).now();
saveDiffFileNotToRestore(now.minusMinutes(1));
Iterable<ImmutableObject> commitLogs = saveDiffFile(
createCheckpoint(now),
CommitLogManifest.create(
getBucketKey(1),
now,
ImmutableSet.<Key<?>>of(Key.create(TestObject.create("previous to delete")))));
action.run();
ofy().clearSessionCache();
assertExpectedIds("previous to keep");
assertInDatastore(commitLogs);
assertCommitLogBuckets(ImmutableMap.of(1, now));
assertInDatastore(asList(CommitLogCheckpointRoot.create(now)));
}
private CommitLogCheckpoint createCheckpoint(DateTime now) {
return CommitLogCheckpoint.create(now, toMap(getBucketIds(), constant(now)));
}
private Iterable<ImmutableObject> saveDiffFile(
CommitLogCheckpoint checkpoint, ImmutableObject... entities) throws IOException {
DateTime now = checkpoint.getCheckpointTime();
List<ImmutableObject> allEntities = Lists.<ImmutableObject>asList(checkpoint, entities);
ByteArrayOutputStream output = new ByteArrayOutputStream();
for (ImmutableObject entity : allEntities) {
serializeEntity(entity, output);
}
gcsService.createOrReplace(
new GcsFilename(GCS_BUCKET, DIFF_FILE_PREFIX + now),
new GcsFileOptions.Builder()
.addUserMetadata(LOWER_BOUND_CHECKPOINT, now.minusMinutes(1).toString())
.build(),
ByteBuffer.wrap(output.toByteArray()));
return allEntities;
}
private void saveDiffFileNotToRestore(DateTime now) throws Exception {
saveDiffFile(
createCheckpoint(now),
CommitLogManifest.create(getBucketKey(1), now, null),
CommitLogMutation.create(
CommitLogManifest.createKey(getBucketKey(1), now),
TestObject.create("should not be restored")));
}
private void assertExpectedIds(String... ids) {
assertThat(transform(
ofy().load().type(TestObject.class),
new Function<TestObject, String>() {
@Override
public String apply(TestObject test) {
return test.getId();
}})).containsExactly((Object[]) ids);
}
private void assertInDatastore(Iterable<? extends ImmutableObject> entities) {
assertThat(ofy().load().entities(entities).values()).containsExactlyElementsIn(entities);
}
private void assertCommitLogBuckets(Map<Integer, DateTime> bucketIdsAndTimestamps) {
Map<Long, CommitLogBucket> buckets = ofy().load()
.type(CommitLogBucket.class)
.ids(Longs.asList(Longs.toArray(CommitLogBucket.getBucketIds())));
assertThat(buckets).hasSize(bucketIdsAndTimestamps.size());
for (Entry<Integer, DateTime> bucketIdAndTimestamp : bucketIdsAndTimestamps.entrySet()) {
assertThat(buckets.get((long) bucketIdAndTimestamp.getKey()).getLastWrittenTime())
.isEqualTo(bucketIdAndTimestamp.getValue());
}
}
}

View file

@ -0,0 +1,28 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
java_library(
name = "bigquery",
srcs = glob(["*.java"]),
resources = glob(["testdata/*"]),
deps = [
"//apiserving/discoverydata/bigquery:bigqueryv2",
"//java/com/google/domain/registry/bigquery",
"//java/com/google/domain/registry/util",
"//javatests/com/google/domain/registry/testing",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/junit",
"//third_party/java/truth",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":bigquery"],
)

View file

@ -0,0 +1,31 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.bigquery;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link BigqueryConnection}. */
@RunWith(JUnit4.class)
public class BigqueryConnectionTest {
@Test
public void testNothing() throws Exception {
// Placeholder test class for now.
// TODO(b/16569089): figure out a good way for testing our Bigquery usage overall - maybe unit
// tests here, maybe end-to-end testing.
}
}

View file

@ -0,0 +1,150 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.bigquery;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.bigquery.BigqueryUtils.fromBigqueryTimestampString;
import static com.google.domain.registry.bigquery.BigqueryUtils.toBigqueryTimestamp;
import static com.google.domain.registry.bigquery.BigqueryUtils.toBigqueryTimestampString;
import static com.google.domain.registry.bigquery.BigqueryUtils.toJobReferenceString;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.api.services.bigquery.model.JobReference;
import com.google.domain.registry.testing.ExceptionRule;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.concurrent.TimeUnit;
/** Unit tests for {@link BigqueryUtils}. */
@RunWith(JUnit4.class)
public class BigqueryUtilsTest {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
private static final DateTime DATE_0 = DateTime.parse("2014-07-17T20:35:42Z");
private static final DateTime DATE_1 = DateTime.parse("2014-07-17T20:35:42.1Z");
private static final DateTime DATE_2 = DateTime.parse("2014-07-17T20:35:42.12Z");
private static final DateTime DATE_3 = DateTime.parse("2014-07-17T20:35:42.123Z");
@Test
public void test_toBigqueryTimestampString() throws Exception {
assertThat(toBigqueryTimestampString(START_OF_TIME)).isEqualTo("1970-01-01 00:00:00.000");
assertThat(toBigqueryTimestampString(DATE_0)).isEqualTo("2014-07-17 20:35:42.000");
assertThat(toBigqueryTimestampString(DATE_1)).isEqualTo("2014-07-17 20:35:42.100");
assertThat(toBigqueryTimestampString(DATE_2)).isEqualTo("2014-07-17 20:35:42.120");
assertThat(toBigqueryTimestampString(DATE_3)).isEqualTo("2014-07-17 20:35:42.123");
assertThat(toBigqueryTimestampString(END_OF_TIME)).isEqualTo("294247-01-10 04:00:54.775");
}
@Test
public void test_toBigqueryTimestampString_convertsToUtc() throws Exception {
assertThat(toBigqueryTimestampString(START_OF_TIME.withZone(DateTimeZone.forOffsetHours(5))))
.isEqualTo("1970-01-01 00:00:00.000");
assertThat(toBigqueryTimestampString(DateTime.parse("1970-01-01T00:00:00-0500")))
.isEqualTo("1970-01-01 05:00:00.000");
}
@Test
public void test_fromBigqueryTimestampString_startAndEndOfTime() throws Exception {
assertThat(fromBigqueryTimestampString("1970-01-01 00:00:00 UTC")).isEqualTo(START_OF_TIME);
assertThat(fromBigqueryTimestampString("294247-01-10 04:00:54.775 UTC")).isEqualTo(END_OF_TIME);
}
@Test
public void test_fromBigqueryTimestampString_trailingZerosOkay() throws Exception {
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42 UTC")).isEqualTo(DATE_0);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.0 UTC")).isEqualTo(DATE_0);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.00 UTC")).isEqualTo(DATE_0);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.000 UTC")).isEqualTo(DATE_0);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.1 UTC")).isEqualTo(DATE_1);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.10 UTC")).isEqualTo(DATE_1);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.100 UTC")).isEqualTo(DATE_1);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.12 UTC")).isEqualTo(DATE_2);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.120 UTC")).isEqualTo(DATE_2);
assertThat(fromBigqueryTimestampString("2014-07-17 20:35:42.123 UTC")).isEqualTo(DATE_3);
}
@Test
public void testFailure_fromBigqueryTimestampString_nonUtcTimeZone() throws Exception {
thrown.expect(IllegalArgumentException.class);
fromBigqueryTimestampString("2014-01-01 01:01:01 +05:00");
}
@Test
public void testFailure_fromBigqueryTimestampString_noTimeZone() throws Exception {
thrown.expect(IllegalArgumentException.class);
fromBigqueryTimestampString("2014-01-01 01:01:01");
}
@Test
public void testFailure_fromBigqueryTimestampString_tooManyMillisecondDigits() throws Exception {
thrown.expect(IllegalArgumentException.class);
fromBigqueryTimestampString("2014-01-01 01:01:01.1234 UTC");
}
@Test
public void test_toBigqueryTimestamp_timeunitConversion() throws Exception {
assertThat(toBigqueryTimestamp(1234567890L, TimeUnit.SECONDS))
.isEqualTo("1234567890.000000");
assertThat(toBigqueryTimestamp(1234567890123L, TimeUnit.MILLISECONDS))
.isEqualTo("1234567890.123000");
assertThat(toBigqueryTimestamp(1234567890123000L, TimeUnit.MICROSECONDS))
.isEqualTo("1234567890.123000");
assertThat(toBigqueryTimestamp(1234567890123000000L, TimeUnit.NANOSECONDS))
.isEqualTo("1234567890.123000");
}
@Test
public void test_toBigqueryTimestamp_timeunitConversionForZero() throws Exception {
assertThat(toBigqueryTimestamp(0L, TimeUnit.SECONDS)).isEqualTo("0.000000");
assertThat(toBigqueryTimestamp(0L, TimeUnit.MILLISECONDS)).isEqualTo("0.000000");
assertThat(toBigqueryTimestamp(0L, TimeUnit.MICROSECONDS)).isEqualTo("0.000000");
}
@Test
public void test_toBigqueryTimestamp_datetimeConversion() throws Exception {
assertThat(toBigqueryTimestamp(START_OF_TIME)).isEqualTo("0.000000");
assertThat(toBigqueryTimestamp(DATE_0)).isEqualTo("1405629342.000000");
assertThat(toBigqueryTimestamp(DATE_1)).isEqualTo("1405629342.100000");
assertThat(toBigqueryTimestamp(DATE_2)).isEqualTo("1405629342.120000");
assertThat(toBigqueryTimestamp(DATE_3)).isEqualTo("1405629342.123000");
assertThat(toBigqueryTimestamp(END_OF_TIME)).isEqualTo("9223372036854.775000");
}
@Test
public void test_toJobReferenceString_normalSucceeds() throws Exception {
assertThat(toJobReferenceString(new JobReference().setProjectId("foo").setJobId("bar")))
.isEqualTo("foo:bar");
}
@Test
public void test_toJobReferenceString_emptyReferenceSucceeds() throws Exception {
assertThat(toJobReferenceString(new JobReference())).isEqualTo("null:null");
}
@Test
public void test_toJobReferenceString_nullThrowsNpe() throws Exception {
thrown.expect(NullPointerException.class);
toJobReferenceString(null);
}
}

View file

@ -0,0 +1,21 @@
package(default_visibility = ["//java/com/google/domain/registry:registry_project"])
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
java_library(
name = "config",
srcs = glob(["*.java"]),
deps = [
"//java/com/google/common/annotations",
"//java/com/google/domain/registry/config",
"//third_party/java/junit",
"//third_party/java/truth",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":config"],
)

View file

@ -0,0 +1,41 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.config;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link RegistryEnvironment}. */
@RunWith(JUnit4.class)
public class RegistryEnvironmentTest {
@Test
public void testGet() throws Exception {
RegistryEnvironment.get();
}
@Test
public void testOverride() throws Exception {
RegistryEnvironment.overrideConfigurationForTesting(new TestRegistryConfig() {
@Override
public String getSnapshotsBucket() {
return "black velvet";
}});
assertThat(RegistryEnvironment.get().config().getSnapshotsBucket()).isEqualTo("black velvet");
}
}

View file

@ -0,0 +1,32 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
java_library(
name = "cron",
srcs = glob(["*.java"]),
deps = [
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/domain/registry/cron",
"//java/com/google/domain/registry/model",
"//java/com/google/domain/registry/util",
"//javatests/com/google/domain/registry/testing",
"//third_party/java/appengine:appengine-api-testonly",
"//third_party/java/joda_time",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//third_party/java/truth",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":cron"],
)

View file

@ -0,0 +1,74 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.cron;
import static com.google.domain.registry.cron.CommitLogFanoutAction.BUCKET_PARAM;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.domain.registry.model.ofy.CommitLogBucket;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import com.google.domain.registry.util.Retrier;
import com.google.domain.registry.util.TaskEnqueuer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.ArrayList;
import java.util.List;
/** Unit tests for {@link CommitLogFanoutAction}. */
@RunWith(JUnit4.class)
public class CommitLogFanoutActionTest {
private static final String ENDPOINT = "/the/servlet";
private static final String QUEUE = "the-queue";
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue(Joiner.on('\n').join(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<queue-entries>",
" <queue>",
" <name>the-queue</name>",
" <rate>1/s</rate>",
" </queue>",
"</queue-entries>"))
.build();
@Test
public void testSuccess() throws Exception {
CommitLogFanoutAction action = new CommitLogFanoutAction();
action.taskEnqueuer = new TaskEnqueuer(new Retrier(null, 1));
action.endpoint = ENDPOINT;
action.queue = QUEUE;
action.jitterSeconds = Optional.absent();
action.run();
List<TaskMatcher> matchers = new ArrayList<>();
for (int bucketId : CommitLogBucket.getBucketIds()) {
matchers.add(new TaskMatcher().url(ENDPOINT).param(BUCKET_PARAM, Integer.toString(bucketId)));
}
assertTasksEnqueued(QUEUE, matchers);
}
}

View file

@ -0,0 +1,212 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.cron;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.collect.Lists.transform;
import static com.google.domain.registry.testing.DatastoreHelper.createTlds;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TaskQueueHelper.assertNoTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static java.util.Arrays.asList;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.Registry.TldType;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import com.google.domain.registry.util.Retrier;
import com.google.domain.registry.util.TaskEnqueuer;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link TldFanoutAction}. */
@RunWith(JUnit4.class)
public class TldFanoutActionTest {
private static final String ENDPOINT = "/the/servlet";
private static final String QUEUE = "the-queue";
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue(Joiner.on('\n').join(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<queue-entries>",
" <queue>",
" <name>the-queue</name>",
" <rate>1/s</rate>",
" </queue>",
"</queue-entries>"))
.build();
private static ImmutableListMultimap<String, String> getParamsMap(String... keysAndValues) {
ImmutableListMultimap.Builder<String, String> params = new ImmutableListMultimap.Builder<>();
params.put("queue", QUEUE);
params.put("endpoint", ENDPOINT);
for (int i = 0; i < keysAndValues.length; i += 2) {
params.put(keysAndValues[i], keysAndValues[i + 1]);
}
return params.build();
}
private static void run(ImmutableListMultimap<String, String> params) throws Exception {
TldFanoutAction action = new TldFanoutAction();
action.params = params;
action.endpoint = getLast(params.get("endpoint"));
action.queue = getLast(params.get("queue"));
action.excludes = params.containsKey("exclude")
? ImmutableSet.copyOf(Splitter.on(',').split(params.get("exclude").get(0)))
: ImmutableSet.<String>of();
action.taskEnqueuer = new TaskEnqueuer(new Retrier(null, 1));
action.runInEmpty = params.containsKey("runInEmpty");
action.forEachRealTld = params.containsKey("forEachRealTld");
action.forEachTestTld = params.containsKey("forEachTestTld");
action.jitterSeconds = Optional.absent();
action.run();
}
@Before
public void before() throws Exception {
createTlds("com", "net", "org", "example");
persistResource(Registry.get("example").asBuilder().setTldType(TldType.TEST).build());
}
private static void assertTasks(String... tasks) throws Exception {
assertTasksEnqueued(
QUEUE,
transform(asList(tasks), new Function<String, TaskMatcher>() {
@Override
public TaskMatcher apply(String namespace) {
return new TaskMatcher()
.url(ENDPOINT)
.header("content-type", "application/x-www-form-urlencoded")
.param("tld", namespace);
}}));
}
@Test
public void testSuccess_pathargTld() throws Exception {
run(getParamsMap(
"forEachRealTld", "",
"endpoint", "/the/servlet/:tld"));
assertTasksEnqueued(QUEUE,
new TaskMatcher().url("/the/servlet/com"),
new TaskMatcher().url("/the/servlet/net"),
new TaskMatcher().url("/the/servlet/org"));
}
@Test
public void testSuccess_methodPostIsDefault() throws Exception {
run(getParamsMap("runInEmpty", ""));
assertTasksEnqueued(QUEUE, new TaskMatcher().method("POST"));
}
@Test
public void testSuccess_noTlds() throws Exception {
run(getParamsMap());
assertNoTasksEnqueued(QUEUE);
}
@Test
public void testSuccess_runInEmpty() throws Exception {
run(getParamsMap("runInEmpty", ""));
assertTasks("");
}
@Test
public void testSuccess_forEachRealTld() throws Exception {
run(getParamsMap("forEachRealTld", ""));
assertTasks("com", "net", "org");
}
@Test
public void testSuccess_forEachTestTld() throws Exception {
run(getParamsMap("forEachTestTld", ""));
assertTasks("example");
}
@Test
public void testSuccess_runInEmptyAndRunInRealTld() throws Exception {
run(getParamsMap("runInEmpty", "", "forEachRealTld", ""));
assertTasks("", "com", "net", "org");
}
@Test
public void testSuccess_forEachTestTldAndForEachRealTld() throws Exception {
run(getParamsMap(
"forEachTestTld", "",
"forEachRealTld", ""));
assertTasks("com", "net", "org", "example");
}
@Test
public void testSuccess_runInEmptyAndForEachTestTld() throws Exception {
run(getParamsMap("runInEmpty", "", "forEachTestTld", ""));
assertTasks("", "example");
}
@Test
public void testSuccess_runEverywhere() throws Exception {
run(getParamsMap("runInEmpty", "", "forEachTestTld", "", "forEachRealTld", ""));
assertTasks("", "com", "net", "org", "example");
}
@Test
public void testSuccess_excludeRealTlds() throws Exception {
run(getParamsMap(
"forEachRealTld", "",
"exclude", "com,net"));
assertTasks("org");
}
@Test
public void testSuccess_excludeTestTlds() throws Exception {
run(getParamsMap(
"forEachTestTld", "",
"exclude", "example"));
assertNoTasksEnqueued(QUEUE);
}
@Test
public void testSuccess_excludeNonexistentTlds() throws Exception {
run(getParamsMap(
"runInEmpty", "",
"forEachTestTld", "",
"forEachRealTld", "",
"exclude", "foo"));
assertTasks("", "com", "net", "org", "example");
}
@Test
public void testSuccess_additionalArgsFlowThroughToPostParams() throws Exception {
run(getParamsMap("forEachTestTld", "", "newkey", "newval"));
assertTasksEnqueued(QUEUE,
new TaskMatcher().url("/the/servlet").param("newkey", "newval"));
}
}

View file

@ -0,0 +1,44 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
java_library(
name = "dns",
srcs = glob(["*.java"]),
resources = glob(["testdata/*.txt"]),
deps = [
"//java/com/google/common/annotations",
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/io",
"//java/com/google/common/net",
"//java/com/google/domain/registry/config",
"//java/com/google/domain/registry/dns",
"//java/com/google/domain/registry/dns:constants",
"//java/com/google/domain/registry/dns/writer/api",
"//java/com/google/domain/registry/model",
"//java/com/google/domain/registry/module/backend",
"//java/com/google/domain/registry/request",
"//java/com/google/domain/registry/util",
"//javatests/com/google/domain/registry/testing",
"//third_party/java/appengine:appengine-api-testonly",
"//third_party/java/dagger",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//third_party/java/truth",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":dns"],
)

View file

@ -0,0 +1,106 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.dns;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveSubordinateHost;
import static com.google.domain.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.request.HttpException.NotFoundException;
import com.google.domain.registry.request.RequestModule;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Unit tests for Dagger injection of the DNS package. */
@RunWith(JUnit4.class)
public final class DnsInjectionTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final InjectRule inject = new InjectRule();
private final HttpServletRequest req = mock(HttpServletRequest.class);
private final HttpServletResponse rsp = mock(HttpServletResponse.class);
private final StringWriter httpOutput = new StringWriter();
private final FakeClock clock = new FakeClock(DateTime.parse("2014-01-01TZ"));
private DnsTestComponent component;
private DnsQueue dnsQueue;
@Before
public void setUp() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock);
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
component = DaggerDnsTestComponent.builder()
.requestModule(new RequestModule(req, rsp))
.build();
dnsQueue = component.dnsQueue();
createTld("lol");
}
@Test
public void testWriteDnsTask_injectsAndWorks() throws Exception {
persistActiveSubordinateHost("ns1.example.lol", persistActiveDomain("example.lol"));
clock.advanceOneMilli();
dnsQueue.addDomainRefreshTask("example.lol");
when(req.getParameter("tld")).thenReturn("lol");
component.writeDnsTask().run();
assertNoDnsTasksEnqueued();
}
@Test
public void testWhoisHttpServer_injectsAndWorks() throws Exception {
persistActiveDomain("example.lol");
when(req.getParameter("type")).thenReturn("domain");
when(req.getParameter("name")).thenReturn("example.lol");
component.refreshDns().run();
assertDnsTasksEnqueued("example.lol");
}
@Test
public void testWhoisHttpServer_missingDomain_throwsNotFound() throws Exception {
when(req.getParameter("type")).thenReturn("domain");
when(req.getParameter("name")).thenReturn("example.lol");
thrown.expect(NotFoundException.class, "DOMAIN example.lol not found");
component.refreshDns().run();
}
}

View file

@ -0,0 +1,93 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.dns;
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.TaskQueueHelper.assertNoTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DnsQueue}. */
@RunWith(JUnit4.class)
public class DnsQueueTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final ExceptionRule thrown = new ExceptionRule();
private DnsQueue dnsQueue;
@Before
public void init() {
dnsQueue = new DnsQueue();
dnsQueue.queue = getQueue("dns-pull");
dnsQueue.writeBatchSize = 10;
dnsQueue.writeLockTimeout = Duration.standardSeconds(30);
}
@Test
public void test_addHostRefreshTask_success() throws Exception {
createTld("tld");
dnsQueue.addHostRefreshTask("octopus.tld");
assertTasksEnqueued("dns-pull",
new TaskMatcher().tag("tld").payload("Target-Type=HOST&Target-Name=octopus.tld&tld=tld"));
}
@Test
public void test_addHostRefreshTask_failsOnUnknownTld() throws Exception {
thrown.expect(IllegalArgumentException.class,
"octopus.notatld is not a subordinate host to a known tld");
try {
dnsQueue.addHostRefreshTask("octopus.notatld");
} finally {
assertNoTasksEnqueued("dns-pull");
}
}
@Test
public void test_addDomainRefreshTask_success() throws Exception {
createTld("tld");
dnsQueue.addDomainRefreshTask("octopus.tld");
assertTasksEnqueued("dns-pull",
new TaskMatcher().tag("tld").payload("Target-Type=DOMAIN&Target-Name=octopus.tld&tld=tld"));
}
@Test
public void test_addDomainRefreshTask_failsOnUnknownTld() throws Exception {
thrown.expect(IllegalArgumentException.class, "TLD notatld does not exist");
try {
dnsQueue.addDomainRefreshTask("fake.notatld");
} finally {
assertNoTasksEnqueued("dns-pull");
}
}
}

View file

@ -0,0 +1,37 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.dns;
import com.google.domain.registry.config.ConfigModule;
import com.google.domain.registry.dns.writer.api.VoidDnsWriterModule;
import com.google.domain.registry.module.backend.BackendModule;
import com.google.domain.registry.request.RequestModule;
import com.google.domain.registry.util.SystemClock.SystemClockModule;
import dagger.Component;
@Component(modules = {
SystemClockModule.class,
ConfigModule.class,
BackendModule.class,
DnsModule.class,
RequestModule.class,
VoidDnsWriterModule.class,
})
interface DnsTestComponent {
DnsQueue dnsQueue();
RefreshDns refreshDns();
WriteDnsTask writeDnsTask();
}

View file

@ -0,0 +1,134 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.dns;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveSubordinateHost;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.dns.writer.api.DnsWriter;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import javax.inject.Provider;
/** Unit tests for {@link WriteDnsTask}. */
@RunWith(MockitoJUnitRunner.class)
public class PublishDnsUpdatesActionTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final ExpectedException thrown = ExpectedException.none();
private final FakeClock clock = new FakeClock(DateTime.parse("1971-01-01TZ"));
private final DnsWriter dnsWriter = mock(DnsWriter.class);
private PublishDnsUpdatesAction action;
@Before
public void setUp() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock);
createTld("xn--q9jyb4c");
DomainResource domain1 = persistActiveDomain("example.xn--q9jyb4c");
persistActiveSubordinateHost("ns1.example.xn--q9jyb4c", domain1);
persistActiveSubordinateHost("ns2.example.xn--q9jyb4c", domain1);
DomainResource domain2 = persistActiveDomain("example2.xn--q9jyb4c");
persistActiveSubordinateHost("ns1.example.xn--q9jyb4c", domain2);
clock.advanceOneMilli();
}
private PublishDnsUpdatesAction createAction(String tld) {
PublishDnsUpdatesAction action = new PublishDnsUpdatesAction();
action.timeout = Duration.standardSeconds(10);
action.tld = tld;
action.hosts = ImmutableSet.<String>of();
action.domains = ImmutableSet.<String>of();
action.writerProvider = new Provider<DnsWriter>() {
@Override
public DnsWriter get() {
return dnsWriter;
}};
return action;
}
@Test
public void testHost_published() throws Exception {
action = createAction("xn--q9jyb4c");
action.hosts = ImmutableSet.of("ns1.example.xn--q9jyb4c");
action.run();
verify(dnsWriter).publishHost("ns1.example.xn--q9jyb4c");
verify(dnsWriter).close();
verifyNoMoreInteractions(dnsWriter);
}
@Test
public void testDomain_published() throws Exception {
action = createAction("xn--q9jyb4c");
action.domains = ImmutableSet.of("example.xn--q9jyb4c");
action.run();
verify(dnsWriter).publishDomain("example.xn--q9jyb4c");
verify(dnsWriter).close();
verifyNoMoreInteractions(dnsWriter);
}
@Test
public void testHostAndDomain_published() throws Exception {
action = createAction("xn--q9jyb4c");
action.domains = ImmutableSet.of("example.xn--q9jyb4c", "example2.xn--q9jyb4c");
action.hosts = ImmutableSet.of(
"ns1.example.xn--q9jyb4c", "ns2.example.xn--q9jyb4c", "ns1.example2.xn--q9jyb4c");
action.run();
verify(dnsWriter).publishDomain("example.xn--q9jyb4c");
verify(dnsWriter).publishDomain("example2.xn--q9jyb4c");
verify(dnsWriter).publishHost("ns1.example.xn--q9jyb4c");
verify(dnsWriter).publishHost("ns2.example.xn--q9jyb4c");
verify(dnsWriter).publishHost("ns1.example2.xn--q9jyb4c");
verify(dnsWriter).close();
verifyNoMoreInteractions(dnsWriter);
}
@Test
public void testWrongTld_notPublished() throws Exception {
action = createAction("xn--q9jyb4c");
action.domains = ImmutableSet.of("example.com", "example2.com");
action.hosts = ImmutableSet.of("ns1.example.com", "ns2.example.com", "ns1.example2.com");
action.run();
verify(dnsWriter).close();
verifyNoMoreInteractions(dnsWriter);
}
}

View file

@ -0,0 +1,267 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.dns;
import static com.google.common.collect.Lists.transform;
import static com.google.domain.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
import static com.google.domain.registry.dns.DnsConstants.DNS_TARGET_NAME_PARAM;
import static com.google.domain.registry.dns.DnsConstants.DNS_TARGET_TYPE_PARAM;
import static com.google.domain.registry.request.RequestParameters.PARAM_TLD;
import static com.google.domain.registry.testing.DatastoreHelper.createTlds;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TaskQueueHelper.assertNoTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static java.util.Arrays.asList;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.taskqueue.TaskOptions.Method;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.net.InternetDomainName;
import com.google.domain.registry.dns.DnsConstants.TargetType;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.Registry.TldType;
import com.google.domain.registry.request.RequestParameters;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import com.google.domain.registry.util.Retrier;
import com.google.domain.registry.util.TaskEnqueuer;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.ArrayList;
import java.util.List;
/** Unit tests for {@link ReadDnsQueueAction}. */
@RunWith(JUnit4.class)
public class ReadDnsQueueActionTest {
private static final int TEST_TLD_UPDATE_BATCH_SIZE = 100;
private DnsQueue dnsQueue;
private FakeClock clock = new FakeClock(DateTime.now(DateTimeZone.UTC));
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue(Joiner.on('\n').join(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<queue-entries>",
" <queue>",
" <name>dns-publish</name>",
" <rate>1/s</rate>",
" </queue>",
" <queue>",
" <name>dns-pull</name>",
" <mode>pull</mode>",
" </queue>",
"</queue-entries>"))
.withClock(clock)
.build();
@Before
public void before() throws Exception {
clock.setTo(DateTime.now(DateTimeZone.UTC));
createTlds("com", "net", "example");
persistResource(Registry.get("example").asBuilder().setTldType(TldType.TEST).build());
dnsQueue = DnsQueue.create();
dnsQueue.writeLockTimeout = Duration.standardSeconds(10);
}
private void run(boolean keepTasks) throws Exception {
ReadDnsQueueAction action = new ReadDnsQueueAction();
action.tldUpdateBatchSize = TEST_TLD_UPDATE_BATCH_SIZE;
action.dnsQueue = dnsQueue;
action.dnsPublishPushQueue = QueueFactory.getQueue(DNS_PUBLISH_PUSH_QUEUE_NAME);
action.taskEnqueuer = new TaskEnqueuer(new Retrier(null, 1));
action.jitterSeconds = Optional.absent();
action.keepTasks = keepTasks;
// Advance the time a little, to ensure that leaseTasks() returns all tasks.
clock.setTo(DateTime.now(DateTimeZone.UTC).plusMillis(1));
action.run();
}
// TODO(b/24564175): remove
private enum RefreshTld { AS_TAG, AS_PARAM }
private static TaskOptions createRefreshTask(
String name, TargetType type, RefreshTld refreshTld) {
TaskOptions options = TaskOptions.Builder
.withMethod(Method.PULL)
.param(DNS_TARGET_TYPE_PARAM, type.toString())
.param(DNS_TARGET_NAME_PARAM, name);
String tld = InternetDomainName.from(name).parts().reverse().get(0);
switch (refreshTld) {
case AS_TAG:
return options.tag(tld);
default:
return options.param(PARAM_TLD, tld);
}
}
private void assertTldsEnqueuedInPushQueue(String... tlds) throws Exception {
assertTasksEnqueued(
DNS_PUBLISH_PUSH_QUEUE_NAME,
transform(asList(tlds), new Function<String, TaskMatcher>() {
@Override
public TaskMatcher apply(String tld) {
return new TaskMatcher()
.url(PublishDnsUpdatesAction.PATH)
.param(RequestParameters.PARAM_TLD, tld)
.header("content-type", "application/x-www-form-urlencoded");
}}));
}
@Test
public void testSuccess_methodPostIsDefault() throws Exception {
dnsQueue.addDomainRefreshTask("domain.com");
dnsQueue.addDomainRefreshTask("domain.net");
dnsQueue.addDomainRefreshTask("domain.example");
run(false);
assertNoTasksEnqueued(DnsConstants.DNS_PULL_QUEUE_NAME);
assertTasksEnqueued(
DNS_PUBLISH_PUSH_QUEUE_NAME,
new TaskMatcher().method("POST"),
new TaskMatcher().method("POST"),
new TaskMatcher().method("POST"));
}
@Test
public void testSuccess_allTlds() throws Exception {
dnsQueue.addDomainRefreshTask("domain.com");
dnsQueue.addDomainRefreshTask("domain.net");
dnsQueue.addDomainRefreshTask("domain.example");
run(false);
assertNoTasksEnqueued(DnsConstants.DNS_PULL_QUEUE_NAME);
assertTldsEnqueuedInPushQueue("com", "net", "example");
}
@Test
public void testSuccess_allTldsKeepTasks() throws Exception {
dnsQueue.addDomainRefreshTask("domain.com");
dnsQueue.addDomainRefreshTask("domain.net");
dnsQueue.addDomainRefreshTask("domain.example");
run(true);
assertTasksEnqueued(
DnsConstants.DNS_PULL_QUEUE_NAME,
new TaskMatcher().tag("com"),
new TaskMatcher().tag("net"),
new TaskMatcher().tag("example"));
assertTldsEnqueuedInPushQueue("com", "net", "example");
}
@Test
public void testSuccess_allTldsNoTag() throws Exception {
dnsQueue.queue.add(createRefreshTask("domain.com", TargetType.DOMAIN, RefreshTld.AS_PARAM));
dnsQueue.queue.add(createRefreshTask("domain.net", TargetType.DOMAIN, RefreshTld.AS_PARAM));
dnsQueue.queue.add(createRefreshTask("domain.example", TargetType.DOMAIN, RefreshTld.AS_PARAM));
run(false);
assertNoTasksEnqueued(DnsConstants.DNS_PULL_QUEUE_NAME);
assertTldsEnqueuedInPushQueue("com", "net", "example");
}
@Test
public void testSuccess_allTldsMixedOldAndNewTldStyles() throws Exception {
dnsQueue.addDomainRefreshTask("domain.com");
dnsQueue.queue.add(createRefreshTask("domain.net", TargetType.DOMAIN, RefreshTld.AS_PARAM));
dnsQueue.queue.add(createRefreshTask("domain.example", TargetType.DOMAIN, RefreshTld.AS_TAG));
run(false);
assertNoTasksEnqueued(DnsConstants.DNS_PULL_QUEUE_NAME);
assertTldsEnqueuedInPushQueue("com", "net", "example");
}
@Test
public void testSuccess_oneTldPaused() throws Exception {
persistResource(Registry.get("net").asBuilder().setDnsPaused(true).build());
dnsQueue.addDomainRefreshTask("domain.com");
dnsQueue.addDomainRefreshTask("domain.net");
dnsQueue.addDomainRefreshTask("domain.example");
run(false);
assertTasksEnqueued(DnsConstants.DNS_PULL_QUEUE_NAME, new TaskMatcher().tag("net"));
assertTldsEnqueuedInPushQueue("com", "example");
}
@Test
public void testSuccess_zone_getsIgnored() throws Exception {
dnsQueue.addHostRefreshTask("ns1.domain.com");
dnsQueue.addDomainRefreshTask("domain.net");
dnsQueue.addZoneRefreshTask("example");
run(false);
assertNoTasksEnqueued(DnsConstants.DNS_PULL_QUEUE_NAME);
assertTasksEnqueued(DNS_PUBLISH_PUSH_QUEUE_NAME,
new TaskMatcher()
.url(PublishDnsUpdatesAction.PATH)
.param("domains", "domain.net"),
new TaskMatcher()
.url(PublishDnsUpdatesAction.PATH)
.param("hosts", "ns1.domain.com"));
}
@Test
public void testSuccess_manyDomainsAndHosts() throws Exception {
List<TaskMatcher> expectedTasks = new ArrayList<>();
for (String tld : ImmutableList.of("com", "net")) {
int refreshItemsInTask = 0;
TaskMatcher task = null;
// 0: domain; 1: host 1; 2: host 2
for (int thingType = 0; thingType < 3; thingType++) {
for (int i = 0; i < 150; i++) {
String domainName = String.format("domain%04d.%s", i, tld);
// If we don't have an existing task into which to dump new refreshes, create one.
if (task == null) {
task = new TaskMatcher().url(PublishDnsUpdatesAction.PATH);
expectedTasks.add(task);
refreshItemsInTask = 0;
}
switch (thingType) {
default:
dnsQueue.addDomainRefreshTask(domainName);
task.param("domains", domainName);
break;
case 1:
dnsQueue.queue.add(
createRefreshTask("ns1." + domainName, TargetType.HOST, RefreshTld.AS_TAG));
task.param("hosts", "ns1." + domainName);
break;
case 2:
dnsQueue.queue.add(
createRefreshTask("ns2." + domainName, TargetType.HOST, RefreshTld.AS_PARAM));
task.param("hosts", "ns2." + domainName);
break;
}
// If this task is now full up, wash our hands of it, so that we'll start a new one the
// next time through the loop.
refreshItemsInTask++;
if (refreshItemsInTask >= TEST_TLD_UPDATE_BATCH_SIZE) {
task = null;
}
}
}
}
run(false);
assertNoTasksEnqueued(DnsConstants.DNS_PULL_QUEUE_NAME);
assertTasksEnqueued(DNS_PUBLISH_PUSH_QUEUE_NAME, expectedTasks);
}
}

View file

@ -0,0 +1,116 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.dns;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveSubordinateHost;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.google.domain.registry.dns.DnsConstants.TargetType;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.google.domain.registry.request.HttpException.NotFoundException;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeClock;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link RefreshDns}. */
@RunWith(JUnit4.class)
public class RefreshDnsTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final ExceptionRule thrown = new ExceptionRule();
private final DnsQueue dnsQueue = mock(DnsQueue.class);
private final FakeClock clock = new FakeClock();
private void run(TargetType type, String name) {
RefreshDns refreshDns = new RefreshDns();
refreshDns.clock = clock;
refreshDns.domainOrHostName = name;
refreshDns.type = type;
refreshDns.dnsQueue = dnsQueue;
refreshDns.run();
}
@Before
public void before() {
createTld("xn--q9jyb4c");
}
@Test
public void testSuccess_host() throws Exception {
DomainResource domain = persistActiveDomain("example.xn--q9jyb4c");
persistActiveSubordinateHost("ns1.example.xn--q9jyb4c", domain);
run(TargetType.HOST, "ns1.example.xn--q9jyb4c");
verify(dnsQueue).addHostRefreshTask("ns1.example.xn--q9jyb4c");
verifyNoMoreInteractions(dnsQueue);
}
@Test
public void testSuccess_externalHostNotEnqueued() throws Exception {
persistActiveDomain("example.xn--q9jyb4c");
persistActiveHost("ns1.example.xn--q9jyb4c");
thrown.expect(BadRequestException.class,
"ns1.example.xn--q9jyb4c isn't a subordinate hostname");
try {
run(TargetType.HOST, "ns1.example.xn--q9jyb4c");
} finally {
verifyNoMoreInteractions(dnsQueue);
}
}
@Test
public void testSuccess_domain() throws Exception {
persistActiveDomain("example.xn--q9jyb4c");
run(TargetType.DOMAIN, "example.xn--q9jyb4c");
verify(dnsQueue).addDomainRefreshTask("example.xn--q9jyb4c");
verifyNoMoreInteractions(dnsQueue);
}
@Test
public void testFailure_unqualifiedName() throws Exception {
thrown.expect(BadRequestException.class);
run(TargetType.DOMAIN, "example");
}
@Test
public void testFailure_hostDoesNotExist() throws Exception {
thrown.expect(NotFoundException.class);
run(TargetType.HOST, "ns1.example.xn--q9jyb4c");
}
@Test
public void testFailure_domainDoesNotExist() throws Exception {
thrown.expect(NotFoundException.class);
run(TargetType.DOMAIN, "example.xn--q9jyb4c");
}
}

View file

@ -0,0 +1,197 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.dns;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveSubordinateHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
import static com.google.domain.registry.testing.TaskQueueHelper.clearTaskQueue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.domain.registry.dns.writer.api.DnsWriter;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import javax.inject.Provider;
/** Unit tests for {@link WriteDnsTask}. */
@RunWith(MockitoJUnitRunner.class)
public class WriteDnsTaskTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final ExpectedException thrown = ExpectedException.none();
private final FakeClock clock = new FakeClock(DateTime.parse("1971-01-01TZ"));
private final DnsWriter dnsWriter = mock(DnsWriter.class);
private final DnsQueue dnsQueue = new DnsQueue();
@Before
public void setUp() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock);
createTld("xn--q9jyb4c");
dnsQueue.queue = QueueFactory.getQueue(DnsConstants.DNS_PULL_QUEUE_NAME);
dnsQueue.writeBatchSize = 7;
dnsQueue.writeLockTimeout = Duration.standardSeconds(10);
}
private void run(String tld) {
WriteDnsTask task = new WriteDnsTask();
task.dnsQueue = dnsQueue;
task.timeout = Duration.standardSeconds(10);
task.tld = tld;
task.writerProvider = new Provider<DnsWriter>() {
@Override
public DnsWriter get() {
return dnsWriter;
}};
task.run();
}
@After
public void cleanUp() throws Exception {
clearTaskQueue("dns-pull");
}
@Test
public void testSuccess_host() throws Exception {
persistActiveSubordinateHost(
"ns1.example.xn--q9jyb4c", persistActiveDomain("example.xn--q9jyb4c"));
clock.advanceOneMilli();
dnsQueue.addHostRefreshTask("ns1.example.xn--q9jyb4c");
assertDnsTasksEnqueued("ns1.example.xn--q9jyb4c");
run("xn--q9jyb4c");
verify(dnsWriter).publishHost("ns1.example.xn--q9jyb4c");
verify(dnsWriter).close();
verifyNoMoreInteractions(dnsWriter);
assertNoDnsTasksEnqueued();
}
@Test
public void testSuccess_domain() throws Exception {
persistActiveSubordinateHost(
"ns1.example.xn--q9jyb4c", persistActiveDomain("example.xn--q9jyb4c"));
clock.advanceOneMilli();
dnsQueue.addDomainRefreshTask("example.xn--q9jyb4c");
assertDnsTasksEnqueued("example.xn--q9jyb4c");
run("xn--q9jyb4c");
verify(dnsWriter).publishDomain("example.xn--q9jyb4c");
verify(dnsWriter).close();
verifyNoMoreInteractions(dnsWriter);
assertNoDnsTasksEnqueued();
}
@Test
public void testSuccess_zone() throws Exception {
dnsQueue.addZoneRefreshTask("xn--q9jyb4c");
assertDnsTasksEnqueued("xn--q9jyb4c");
run("xn--q9jyb4c");
verify(dnsWriter).close();
verifyNoMoreInteractions(dnsWriter);
assertNoDnsTasksEnqueued();
}
@Test
public void testSuccess_dnsPaused() throws Exception {
persistActiveSubordinateHost(
"ns1.example.xn--q9jyb4c", persistActiveDomain("example.xn--q9jyb4c"));
clock.advanceOneMilli();
dnsQueue.addDomainRefreshTask("example.xn--q9jyb4c");
assertDnsTasksEnqueued("example.xn--q9jyb4c");
persistResource(Registry.get("xn--q9jyb4c").asBuilder().setDnsPaused(true).build());
clock.advanceOneMilli();
run("xn--q9jyb4c");
verifyZeroInteractions(dnsWriter);
assertDnsTasksEnqueued("example.xn--q9jyb4c");
}
@Test
public void testSuccess_twoTasksFromTheSameTld() throws Exception {
persistActiveSubordinateHost(
"ns1.example.xn--q9jyb4c", persistActiveDomain("example.xn--q9jyb4c"));
clock.advanceOneMilli();
dnsQueue.addDomainRefreshTask("example.xn--q9jyb4c");
persistActiveSubordinateHost(
"ns1.example2.xn--q9jyb4c", persistActiveDomain("example2.xn--q9jyb4c"));
clock.advanceOneMilli();
dnsQueue.addDomainRefreshTask("example2.xn--q9jyb4c");
assertDnsTasksEnqueued("example.xn--q9jyb4c", "example2.xn--q9jyb4c");
run("xn--q9jyb4c");
assertNoDnsTasksEnqueued();
}
@Test
public void testSuccess_twoTasksInDifferentTlds() throws Exception {
// refresh example.xn--q9jyb4c
persistActiveSubordinateHost(
"ns1.example.xn--q9jyb4c", persistActiveDomain("example.xn--q9jyb4c"));
clock.advanceOneMilli();
dnsQueue.addDomainRefreshTask("example.xn--q9jyb4c");
// refresh example.example
createTld("example");
persistActiveSubordinateHost(
"ns1.example.example", persistActiveDomain("example.example"));
clock.advanceOneMilli();
dnsQueue.addDomainRefreshTask("example.example");
// there should now be two tasks enqueued
assertDnsTasksEnqueued("example.example", "example.xn--q9jyb4c");
// process one, leaving one
run("example");
assertDnsTasksEnqueued("example.xn--q9jyb4c");
// process the other, leaving none
run("xn--q9jyb4c");
assertNoDnsTasksEnqueued();
}
@Test
public void testSuccess_domainDeleted() throws Exception {
dnsQueue.addDomainRefreshTask("example.xn--q9jyb4c");
assertDnsTasksEnqueued("example.xn--q9jyb4c");
run("xn--q9jyb4c");
assertNoDnsTasksEnqueued();
}
}

View file

@ -0,0 +1,4 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)

View file

@ -0,0 +1,52 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
java_library(
name = "export",
srcs = glob(["*.java"]),
resources = glob([
"testdata/*",
"backup_kinds.txt",
]),
deps = [
"//apiserving/discoverydata/bigquery:bigqueryv2",
"//apiserving/discoverydata/drive",
"//java/com/google/api/client/http",
"//java/com/google/api/client/json",
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/io",
"//java/com/google/common/net",
"//java/com/google/domain/registry/bigquery",
"//java/com/google/domain/registry/config",
"//java/com/google/domain/registry/export",
"//java/com/google/domain/registry/gcs",
"//java/com/google/domain/registry/groups",
"//java/com/google/domain/registry/model",
"//java/com/google/domain/registry/request",
"//java/com/google/domain/registry/storage/drive",
"//java/com/google/domain/registry/util",
"//javatests/com/google/domain/registry/testing",
"//third_party/java/appengine:appengine-api-testonly",
"//third_party/java/appengine:appengine-stubs",
"//third_party/java/appengine_gcs_client",
"//third_party/java/dagger",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//third_party/java/truth",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":export"],
)

View file

@ -0,0 +1,224 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import static org.mockito.Mockito.when;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.model.ErrorProto;
import com.google.api.services.bigquery.model.Job;
import com.google.api.services.bigquery.model.JobReference;
import com.google.api.services.bigquery.model.JobStatus;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.taskqueue.TaskOptions.Method;
import com.google.appengine.api.taskqueue.dev.QueueStateInfo.TaskStateInfo;
import com.google.domain.registry.export.BigqueryPollJobAction.BigqueryPollJobEnqueuer;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.google.domain.registry.request.HttpException.NotModifiedException;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.FakeSleeper;
import com.google.domain.registry.testing.TaskQueueHelper;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import com.google.domain.registry.util.CapturingLogHandler;
import com.google.domain.registry.util.Retrier;
import com.google.domain.registry.util.TaskEnqueuer;
import dagger.Lazy;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/** Unit tests for {@link BigqueryPollJobAction}. */
@RunWith(MockitoJUnitRunner.class)
public class BigqueryPollJobActionTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Mock Bigquery bigquery;
@Mock Bigquery.Jobs bigqueryJobs;
@Mock Bigquery.Jobs.Get bigqueryJobsGet;
static final String PROJECT_ID = "project_id";
static final String JOB_ID = "job_id";
static final String CHAINED_QUEUE_NAME = "default";
static final TaskEnqueuer ENQUEUER =
new TaskEnqueuer(new Retrier(new FakeSleeper(new FakeClock()), 1));
private final CapturingLogHandler logHandler = new CapturingLogHandler();
private BigqueryPollJobAction action = new BigqueryPollJobAction();
@Before
public void before() throws Exception {
action.bigquery = bigquery;
when(bigquery.jobs()).thenReturn(bigqueryJobs);
when(bigqueryJobs.get(PROJECT_ID, JOB_ID)).thenReturn(bigqueryJobsGet);
action.enqueuer = ENQUEUER;
action.projectId = PROJECT_ID;
action.jobId = JOB_ID;
action.chainedQueueName =
new Lazy<String>() {
@Override
public String get() {
return CHAINED_QUEUE_NAME;
}};
Logger.getLogger(BigqueryPollJobAction.class.getName()).addHandler(logHandler);
}
private static TaskMatcher newPollJobTaskMatcher(String method) throws Exception {
return new TaskMatcher()
.method(method)
.url(BigqueryPollJobAction.PATH)
.header(BigqueryPollJobAction.PROJECT_ID_HEADER, PROJECT_ID)
.header(BigqueryPollJobAction.JOB_ID_HEADER, JOB_ID);
}
@Test
public void testSuccess_enqueuePollTask() throws Exception {
new BigqueryPollJobEnqueuer(ENQUEUER).enqueuePollTask(
new JobReference().setProjectId(PROJECT_ID).setJobId(JOB_ID));
assertTasksEnqueued(BigqueryPollJobAction.QUEUE, newPollJobTaskMatcher("GET"));
}
@Test
public void testSuccess_enqueuePollTask_withChainedTask() throws Exception {
TaskOptions chainedTask = TaskOptions.Builder
.withUrl("/_dr/something")
.method(Method.POST)
.header("X-Testing", "foo")
.param("testing", "bar");
new BigqueryPollJobEnqueuer(ENQUEUER).enqueuePollTask(
new JobReference().setProjectId(PROJECT_ID).setJobId(JOB_ID),
chainedTask,
QueueFactory.getQueue(CHAINED_QUEUE_NAME));
assertTasksEnqueued(BigqueryPollJobAction.QUEUE, newPollJobTaskMatcher("POST"));
TaskStateInfo taskInfo = getOnlyElement(
TaskQueueHelper.getQueueInfo(BigqueryPollJobAction.QUEUE).getTaskInfo());
ByteArrayInputStream taskBodyBytes = new ByteArrayInputStream(taskInfo.getBodyAsBytes());
TaskOptions taskOptions = (TaskOptions) new ObjectInputStream(taskBodyBytes).readObject();
assertThat(taskOptions).isEqualTo(chainedTask);
}
private void assertLogMessage(Level level, String message) {
for (LogRecord logRecord : logHandler.getRecords()) {
if (logRecord.getLevel() == level && logRecord.getMessage().contains(message)) {
return;
}
}
assert_().fail(String.format("Log message \"%s\" not found", message));
}
@Test
public void testSuccess_jobCompletedSuccessfully() throws Exception {
when(bigqueryJobsGet.execute()).thenReturn(
new Job().setStatus(new JobStatus().setState("DONE")));
action.run();
assertLogMessage(INFO,
String.format("Bigquery job succeeded - %s:%s", PROJECT_ID, JOB_ID));
}
@Test
public void testSuccess_chainedPayloadAndJobSucceeded_enqueuesChainedTask() throws Exception {
when(bigqueryJobsGet.execute()).thenReturn(
new Job().setStatus(new JobStatus().setState("DONE")));
TaskOptions chainedTask = TaskOptions.Builder
.withUrl("/_dr/something")
.method(Method.POST)
.header("X-Testing", "foo")
.param("testing", "bar")
.taskName("my_task_name");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
new ObjectOutputStream(bytes).writeObject(chainedTask);
action.payload = bytes.toByteArray();
action.run();
assertLogMessage(INFO,
String.format("Bigquery job succeeded - %s:%s", PROJECT_ID, JOB_ID));
assertLogMessage(INFO,
String.format(
"Added chained task my_task_name for /_dr/something to queue default:",
PROJECT_ID, JOB_ID));
assertTasksEnqueued(CHAINED_QUEUE_NAME, new TaskMatcher()
.url("/_dr/something")
.method("POST")
.header("X-Testing", "foo")
.param("testing", "bar")
.taskName("my_task_name"));
}
@Test
public void testJobFailed() throws Exception {
when(bigqueryJobsGet.execute()).thenReturn(new Job().setStatus(
new JobStatus()
.setState("DONE")
.setErrorResult(new ErrorProto().setMessage("Job failed"))));
action.run();
assertLogMessage(SEVERE, String.format("Bigquery job failed - %s:%s", PROJECT_ID, JOB_ID));
}
@Test
public void testJobPending() throws Exception {
when(bigqueryJobsGet.execute()).thenReturn(
new Job().setStatus(new JobStatus().setState("PENDING")));
thrown.expect(NotModifiedException.class);
action.run();
}
@Test
@SuppressWarnings("unchecked")
public void testJobStatusUnreadable() throws Exception {
when(bigqueryJobsGet.execute()).thenThrow(IOException.class);
thrown.expect(NotModifiedException.class);
action.run();
}
@Test
public void testFailure_badChainedTaskPayload() throws Exception {
when(bigqueryJobsGet.execute()).thenReturn(
new Job().setStatus(new JobStatus().setState("DONE")));
action.payload = "payload".getBytes();
thrown.expect(BadRequestException.class, "Cannot deserialize task from payload");
action.run();
}
}

View file

@ -0,0 +1,271 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Unit tests for {@link CheckSnapshotServlet}. */
@RunWith(MockitoJUnitRunner.class)
public class CheckSnapshotServletTest {
static final DateTime START_TIME = DateTime.parse("2014-08-01T01:02:03Z");
static final DateTime COMPLETE_TIME = START_TIME.plus(Duration.standardMinutes(30));
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withTaskQueue()
.build();
@Mock
private HttpServletRequest req;
@Mock
private HttpServletResponse rsp;
private DatastoreBackupInfo backupInfo;
@Mock
private DatastoreBackupService backupService;
@Mock
private LoadSnapshotServlet loadSnapshotServlet;
private final FakeClock clock = new FakeClock(COMPLETE_TIME.plusMillis(1000));
private final StringWriter httpOutput = new StringWriter();
private final CheckSnapshotServlet servlet = new CheckSnapshotServlet();
@Before
public void before() throws Exception {
inject.setStaticField(CheckSnapshotServlet.class, "backupService", backupService);
inject.setStaticField(CheckSnapshotServlet.class, "loadSnapshotServlet", loadSnapshotServlet);
inject.setStaticField(DatastoreBackupInfo.class, "clock", clock);
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
servlet.init(mock(ServletConfig.class));
when(req.getMethod()).thenReturn("POST");
backupInfo = new DatastoreBackupInfo(
"some_backup",
START_TIME,
Optional.of(COMPLETE_TIME),
ImmutableSet.of("one", "two", "three"),
Optional.of("gs://somebucket/some_backup_20140801.backup_info"));
}
private void setPendingBackup() {
backupInfo = new DatastoreBackupInfo(
backupInfo.getName(),
backupInfo.getStartTime(),
Optional.<DateTime>absent(),
backupInfo.getKinds(),
backupInfo.getGcsFilename());
}
@Test
public void testSuccess_enqueuePollTask() throws Exception {
servlet.enqueuePollTask("some_snapshot_name", ImmutableSet.of("one", "two", "three"));
assertTasksEnqueued(CheckSnapshotServlet.QUEUE,
new TaskMatcher()
.url(CheckSnapshotServlet.PATH)
.param(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM, "some_snapshot_name")
.param(CheckSnapshotServlet.SNAPSHOT_KINDS_TO_LOAD_PARAM, "one,two,three")
.method("POST"));
}
@Test
public void testPost_forPendingBackup_returnsNotModified() throws Exception {
setPendingBackup();
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(backupService.findByName("some_backup")).thenReturn(backupInfo);
servlet.service(req, rsp);
verify(rsp).sendError(SC_NOT_MODIFIED, "Datastore backup some_backup still pending");
}
@Test
public void testPost_forStalePendingBackupBackup_returnsAccepted() throws Exception {
setPendingBackup();
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(backupService.findByName("some_backup")).thenReturn(backupInfo);
clock.setTo(START_TIME
.plus(Duration.standardHours(20))
.plus(Duration.standardMinutes(3))
.plus(Duration.millis(1234)));
servlet.service(req, rsp);
verify(rsp).sendError(SC_ACCEPTED,
"Datastore backup some_backup abandoned - "
+ "not complete after 20 hours, 3 minutes and 1 second");
}
@Test
public void testPost_forCompleteBackup_enqueuesLoadTask() throws Exception {
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_KINDS_TO_LOAD_PARAM)).thenReturn("one,two");
when(backupService.findByName("some_backup")).thenReturn(backupInfo);
servlet.service(req, rsp);
verify(rsp).setStatus(SC_OK);
verify(loadSnapshotServlet).enqueueLoadTask(
"20140801_010203",
"gs://somebucket/some_backup_20140801.backup_info",
ImmutableSet.of("one", "two"));
}
@Test
public void testPost_forCompleteAutoBackup_enqueuesLoadTask_usingBackupName() throws Exception {
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM))
.thenReturn("auto_snapshot_somestring");
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_KINDS_TO_LOAD_PARAM)).thenReturn("one,two");
when(backupService.findByName("auto_snapshot_somestring")).thenReturn(backupInfo);
servlet.service(req, rsp);
verify(rsp).setStatus(SC_OK);
verify(loadSnapshotServlet).enqueueLoadTask(
"somestring",
"gs://somebucket/some_backup_20140801.backup_info",
ImmutableSet.of("one", "two"));
}
@Test
public void testPost_forCompleteBackup_missingKindsToLoad_enqueuesLoadTask() throws Exception {
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(backupService.findByName("some_backup")).thenReturn(backupInfo);
servlet.service(req, rsp);
verify(rsp).setStatus(SC_OK);
verify(loadSnapshotServlet).enqueueLoadTask(
"20140801_010203",
"gs://somebucket/some_backup_20140801.backup_info",
ImmutableSet.of("one", "two", "three"));
}
@Test
public void testPost_forCompleteBackup_withExtraKindsToLoad_enqueuesLoadTask() throws Exception {
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_KINDS_TO_LOAD_PARAM)).thenReturn("one,foo");
when(backupService.findByName("some_backup")).thenReturn(backupInfo);
servlet.service(req, rsp);
verify(rsp).setStatus(SC_OK);
verify(loadSnapshotServlet).enqueueLoadTask(
"20140801_010203",
"gs://somebucket/some_backup_20140801.backup_info",
ImmutableSet.of("one"));
}
@Test
public void testPost_forCompleteBackup_withEmptyKindsToLoad_skipsLoadTask() throws Exception {
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_KINDS_TO_LOAD_PARAM)).thenReturn("");
when(backupService.findByName("some_backup")).thenReturn(backupInfo);
servlet.service(req, rsp);
verify(rsp).setStatus(SC_OK);
verifyZeroInteractions(loadSnapshotServlet);
}
@Test
public void testPost_forBadBackup_returnsBadRequest() throws Exception {
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(backupService.findByName("some_backup")).thenThrow(
new IllegalArgumentException("No backup found"));
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Bad backup name some_backup: No backup found");
}
@Test
public void testPost_noBackupSpecified_returnsError() throws Exception {
when(req.getMethod()).thenReturn("POST");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Missing required parameter: name");
}
@Test
public void testGet_returnsInformation() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(backupService.findByName("some_backup")).thenReturn(backupInfo);
servlet.service(req, rsp);
verify(rsp).setStatus(SC_OK);
assertThat(httpOutput.toString()).isEqualTo("OK\n\n" + Joiner.on("\n").join(ImmutableList.of(
"Backup name: some_backup",
"Status: COMPLETE",
"Started: 2014-08-01T01:02:03.000Z",
"Ended: 2014-08-01T01:32:03.000Z",
"Duration: 30m",
"GCS: gs://somebucket/some_backup_20140801.backup_info",
"Kinds: [one, two, three]",
"")));
}
@Test
public void testGet_forBadBackup_returnsError() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getParameter(CheckSnapshotServlet.SNAPSHOT_NAME_PARAM)).thenReturn("some_backup");
when(backupService.findByName("some_backup")).thenThrow(
new IllegalArgumentException("No backup found"));
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "No backup found");
}
@Test
public void testGet_noBackupSpecified_returnsError() throws Exception {
when(req.getMethod()).thenReturn("GET");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Missing required parameter: name");
}
}

View file

@ -0,0 +1,135 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService;
import static com.google.common.truth.Truth.assertThat;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Text;
import com.google.common.collect.ImmutableList;
import com.google.domain.registry.export.DatastoreBackupInfo.BackupStatus;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Date;
/** Unit tests for {@link DatastoreBackupInfo}. */
@RunWith(JUnit4.class)
public class DatastoreBackupInfoTest {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
private FakeClock clock = new FakeClock();
private DateTime startTime = DateTime.parse("2014-08-01T01:02:03Z");
private Entity backupEntity; // Can't initialize until AppEngineRule has set up datastore.
@Before
public void before() throws Exception {
inject.setStaticField(DatastoreBackupInfo.class, "clock", clock);
backupEntity = new Entity("_unused_");
backupEntity.setProperty("name", "backup1");
backupEntity.setProperty("kinds", ImmutableList.of("one", "two", "three"));
backupEntity.setProperty("start_time", new Date(startTime.getMillis()));
}
/** Force a roundtrip to datastore to ensure that we're simulating a freshly fetched entity. */
private static Entity persistEntity(Entity entity) throws EntityNotFoundException {
return getDatastoreService().get(getDatastoreService().put(entity));
}
@Test
public void testSuccess_pendingBackup() throws Exception {
DatastoreBackupInfo backup = new DatastoreBackupInfo(persistEntity(backupEntity));
assertThat(backup.getName()).isEqualTo("backup1");
assertThat(backup.getKinds()).containsExactly("one", "two", "three");
assertThat(backup.getStartTime()).isEqualTo(startTime);
assertThat(backup.getCompleteTime()).isAbsent();
assertThat(backup.getGcsFilename()).isAbsent();
assertThat(backup.getStatus()).isEqualTo(BackupStatus.PENDING);
clock.setTo(startTime.plusMinutes(1));
assertThat(backup.getRunningTime()).isEqualTo(Duration.standardMinutes(1));
clock.setTo(startTime.plusMinutes(1).plusSeconds(42));
assertThat(backup.getRunningTime()).isEqualTo(Duration.standardSeconds(102));
}
@Test
public void testSuccess_completeBackup() throws Exception {
DateTime completeTime = startTime.plusMinutes(1).plusSeconds(42);
backupEntity.setProperty("complete_time", new Date(completeTime.getMillis()));
backupEntity.setProperty("gs_handle", new Text("/gs/somebucket/timestamp.backup_info"));
DatastoreBackupInfo backup = new DatastoreBackupInfo(persistEntity(backupEntity));
assertThat(backup.getName()).isEqualTo("backup1");
assertThat(backup.getKinds()).containsExactly("one", "two", "three");
assertThat(backup.getStartTime()).isEqualTo(startTime);
assertThat(backup.getCompleteTime().get()).isEqualTo(completeTime);
assertThat(backup.getGcsFilename()).hasValue("gs://somebucket/timestamp.backup_info");
assertThat(backup.getStatus()).isEqualTo(BackupStatus.COMPLETE);
assertThat(backup.getRunningTime()).isEqualTo(Duration.standardSeconds(102));
}
@Test
public void testFailure_missingName() throws Exception {
thrown.expect(NullPointerException.class);
backupEntity.removeProperty("name");
new DatastoreBackupInfo(persistEntity(backupEntity));
}
@Test
public void testFailure_missingKinds() throws Exception {
thrown.expect(NullPointerException.class);
backupEntity.removeProperty("kinds");
new DatastoreBackupInfo(persistEntity(backupEntity));
}
@Test
public void testFailure_missingStartTime() throws Exception {
thrown.expect(NullPointerException.class);
backupEntity.removeProperty("start_time");
new DatastoreBackupInfo(persistEntity(backupEntity));
}
@Test
public void testFailure_badGcsFilenameFormat() throws Exception {
thrown.expect(IllegalArgumentException.class);
backupEntity.setProperty("gs_handle", new Text("foo"));
new DatastoreBackupInfo(persistEntity(backupEntity));
}
}

View file

@ -0,0 +1,141 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static org.mockito.Mockito.when;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.modules.ModulesService;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.Date;
/** Unit tests for {@link DatastoreBackupService}. */
@RunWith(MockitoJUnitRunner.class)
public class DatastoreBackupServiceTest {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Mock
private ModulesService modulesService;
private static final DateTime START_TIME = DateTime.parse("2014-08-01T01:02:03Z");
private final DatastoreBackupService backupService = DatastoreBackupService.get();
@Before
public void before() throws Exception {
inject.setStaticField(DatastoreBackupService.class, "modulesService", modulesService);
when(modulesService.getVersionHostname("default", "ah-builtin-python-bundle"))
.thenReturn("ah-builtin-python-bundle.default.localhost");
persistBackupEntityWithName("backupA1");
persistBackupEntityWithName("backupA2");
persistBackupEntityWithName("backupA3");
persistBackupEntityWithName("backupB1");
persistBackupEntityWithName("backupB42");
}
private static void persistBackupEntityWithName(String name) {
Entity entity = new Entity(DatastoreBackupService.BACKUP_INFO_KIND);
entity.setProperty("name", name);
entity.setProperty("kinds", ImmutableList.of("one", "two", "three"));
entity.setProperty("start_time", new Date(START_TIME.getMillis()));
getDatastoreService().put(entity);
}
@Test
public void testSuccess_launchBackup() throws Exception {
backupService.launchNewBackup(
"default", "backup1", "somebucket", ImmutableSet.of("foo", "bar"));
assertTasksEnqueued("default",
new TaskMatcher()
.url("/_ah/datastore_admin/backup.create")
.header("Host", "ah-builtin-python-bundle.default.localhost")
.method("GET")
.param("name", "backup1_")
.param("filesystem", "gs")
.param("gs_bucket_name", "somebucket")
.param("queue", "default")
.param("run_as_a_service", "true")
.param("kind", "foo")
.param("kind", "bar"));
}
private static final Function<DatastoreBackupInfo, String> BACKUP_NAME_GETTER =
new Function<DatastoreBackupInfo, String>() {
@Override
public String apply(DatastoreBackupInfo backup) {
return backup.getName();
}};
@Test
public void testSuccess_findAllByNamePrefix() throws Exception {
assertThat(transform(backupService.findAllByNamePrefix("backupA"), BACKUP_NAME_GETTER))
.containsExactly("backupA1", "backupA2", "backupA3");
assertThat(transform(backupService.findAllByNamePrefix("backupB"), BACKUP_NAME_GETTER))
.containsExactly("backupB1", "backupB42");
assertThat(transform(backupService.findAllByNamePrefix("backupB4"), BACKUP_NAME_GETTER))
.containsExactly("backupB42");
assertThat(backupService.findAllByNamePrefix("backupX")).isEmpty();
}
@Test
public void testSuccess_findByName() throws Exception {
assertThat(BACKUP_NAME_GETTER.apply(backupService.findByName("backupA1")))
.isEqualTo("backupA1");
assertThat(BACKUP_NAME_GETTER.apply(backupService.findByName("backupB4")))
.isEqualTo("backupB42");
}
@Test
public void testFailure_findByName_multipleMatchingBackups() throws Exception {
thrown.expect(IllegalArgumentException.class);
backupService.findByName("backupA");
}
@Test
public void testFailure_findByName_noMatchingBackups() throws Exception {
thrown.expect(IllegalArgumentException.class);
backupService.findByName("backupX");
}
}

View file

@ -0,0 +1,84 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.common.io.Resources.getResource;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Resources;
import com.google.domain.registry.model.ImmutableObject;
import com.googlecode.objectify.annotation.Entity;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.net.URL;
import java.util.List;
/** Unit tests for {@link ExportConstants}. */
@RunWith(JUnit4.class)
public class ExportConstantsTest {
private static final String GOLDEN_BACKUP_KINDS_FILENAME = "backup_kinds.txt";
private static final String UPDATE_INSTRUCTIONS_TEMPLATE = Joiner.on('\n').join(
"",
"---------------------------------------------------------------------------------",
"Your changes affect the list of backed-up kinds in the golden file:",
" %s",
"If these changes are desired, update the golden file with the following contents:",
"=================================================================================",
"%s",
"=================================================================================",
"");
@Test
public void testBackupKinds_matchGoldenBackupKindsFile() throws Exception {
URL goldenBackupKindsResource =
getResource(ExportConstantsTest.class, GOLDEN_BACKUP_KINDS_FILENAME);
List<String> goldenKinds = Splitter.on('\n').splitToList(
Resources.toString(goldenBackupKindsResource, UTF_8).trim());
ImmutableSet<String> actualKinds = ExportConstants.getBackupKinds();
String updateInstructions = String.format(
UPDATE_INSTRUCTIONS_TEMPLATE,
goldenBackupKindsResource.toString(),
Joiner.on('\n').join(actualKinds));
assertWithMessage(updateInstructions)
.that(actualKinds)
.containsExactlyElementsIn(goldenKinds)
.inOrder();
}
@Test
public void testReportingKinds_areSubsetOfBackupKinds() throws Exception {
assertThat(ExportConstants.getBackupKinds()).containsAllIn(ExportConstants.getReportingKinds());
}
@Test
public void testReportingEntityClasses_areAllBaseEntityClasses() throws Exception {
for (Class<? extends ImmutableObject> clazz : ExportConstants.REPORTING_ENTITY_CLASSES) {
assertThat(clazz.isAnnotationPresent(Entity.class))
.named(String.format("class %s is an @Entity", clazz.getSimpleName()))
.isTrue();
}
}
}

View file

@ -0,0 +1,147 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.domain.registry.export.ExportReservedTermsTask.EXPORT_MIME_TYPE;
import static com.google.domain.registry.export.ExportReservedTermsTask.RESERVED_TERMS_FILENAME;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistReservedList;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.MediaType;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.Registry.RegistryNotFoundException;
import com.google.domain.registry.model.registry.label.ReservedList;
import com.google.domain.registry.request.Response;
import com.google.domain.registry.storage.drive.DriveConnection;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.InjectRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.IOException;
/** Unit tests for {@link ExportReservedTermsTask}. */
@RunWith(MockitoJUnitRunner.class)
public class ExportReservedTermsTaskTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final InjectRule inject = new InjectRule();
@Mock
private DriveConnection driveConnection;
@Mock
private Response response;
private void runTask(String tld) {
ExportReservedTermsTask task = new ExportReservedTermsTask();
task.response = response;
task.driveConnection = driveConnection;
task.tld = tld;
task.run();
}
@Before
public void init() throws Exception {
ReservedList rl = persistReservedList(
"tld-reserved",
"lol,FULLY_BLOCKED",
"cat,FULLY_BLOCKED",
"jimmy,UNRESERVED");
createTld("tld");
persistResource(Registry.get("tld").asBuilder()
.setReservedLists(rl)
.setDriveFolderId("brouhaha").build());
when(driveConnection.createOrUpdateFile(
anyString(),
any(MediaType.class),
anyString(),
any(byte[].class))).thenReturn("1001");
}
@Test
public void test_uploadFileToDrive_succeeds() throws Exception {
runTask("tld");
byte[] expected =
("This is a disclaimer.\ncat\nlol\n")
.getBytes(UTF_8);
verify(driveConnection)
.createOrUpdateFile(RESERVED_TERMS_FILENAME, EXPORT_MIME_TYPE, "brouhaha", expected);
verify(response).setStatus(SC_OK);
verify(response).setPayload("1001");
}
@Test
public void test_uploadFileToDrive_doesNothingIfReservedListsNotConfigured() throws Exception {
persistResource(Registry.get("tld").asBuilder()
.setReservedLists(ImmutableSet.<ReservedList>of())
.setDriveFolderId(null)
.build());
runTask("tld");
verify(response).setStatus(SC_OK);
verify(response).setPayload("No reserved lists configured");
}
@Test
public void test_uploadFileToDrive_failsWhenDriveFolderIdIsNull() throws Exception {
thrown.expectRootCause(NullPointerException.class, "No drive folder associated with this TLD");
persistResource(Registry.get("tld").asBuilder().setDriveFolderId(null).build());
runTask("tld");
verify(response).setStatus(SC_INTERNAL_SERVER_ERROR);
}
@Test
public void test_uploadFileToDrive_failsWhenDriveCannotBeReached() throws Exception {
thrown.expectRootCause(IOException.class, "errorMessage");
when(driveConnection.createOrUpdateFile(
anyString(),
any(MediaType.class),
anyString(),
any(byte[].class))).thenThrow(new IOException("errorMessage"));
runTask("tld");
verify(response).setStatus(SC_INTERNAL_SERVER_ERROR);
}
@Test
public void test_uploadFileToDrive_failsWhenTldDoesntExist() throws Exception {
thrown.expectRootCause(
RegistryNotFoundException.class, "No registry object found for fakeTld");
runTask("fakeTld");
verify(response).setStatus(SC_INTERNAL_SERVER_ERROR);
}
}

View file

@ -0,0 +1,98 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.domain.registry.config.TestRegistryConfig;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.RegistryConfigRule;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Unit tests for {@link ExportSnapshotServlet}. */
@RunWith(MockitoJUnitRunner.class)
public class ExportSnapshotServletTest {
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final RegistryConfigRule configRule = new RegistryConfigRule(new TestRegistryConfig() {
@Override public String getSnapshotsBucket() {
return "Bucket-Id";
}});
@Mock
private HttpServletRequest req;
@Mock
private HttpServletResponse rsp;
@Mock
private DatastoreBackupService backupService;
@Mock
private CheckSnapshotServlet checkSnapshotServlet;
private final FakeClock clock = new FakeClock();
private final StringWriter httpOutput = new StringWriter();
private final ExportSnapshotServlet servlet = new ExportSnapshotServlet();
private static final DateTime START_TIME = DateTime.parse("2014-08-01T01:02:03Z");
@Before
public void before() throws Exception {
clock.setTo(START_TIME);
inject.setStaticField(ExportSnapshotServlet.class, "clock", clock);
inject.setStaticField(ExportSnapshotServlet.class, "backupService", backupService);
inject.setStaticField(
ExportSnapshotServlet.class, "checkSnapshotServlet", checkSnapshotServlet);
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
servlet.init(mock(ServletConfig.class));
when(req.getMethod()).thenReturn("POST");
}
@Test
public void testPost_launchesBackup_andEnqueuesPollTask() throws Exception {
servlet.service(req, rsp);
verify(rsp).setStatus(SC_OK);
verify(backupService).launchNewBackup(
ExportSnapshotServlet.QUEUE,
"auto_snapshot_20140801_010203",
"Bucket-Id",
ExportConstants.getBackupKinds());
verify(checkSnapshotServlet)
.enqueuePollTask("auto_snapshot_20140801_010203", ExportConstants.getReportingKinds());
}
}

View file

@ -0,0 +1,63 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistReservedList;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.label.ReservedList;
import com.google.domain.registry.testing.AppEngineRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link ExportUtils}. */
@RunWith(JUnit4.class)
public class ExportUtilsTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Test
public void test_exportReservedTerms() {
ReservedList rl1 = persistReservedList(
"tld-reserved1",
"lol,FULLY_BLOCKED",
"cat,FULLY_BLOCKED",
"jimmy,UNRESERVED");
ReservedList rl2 = persistReservedList(
"tld-reserved2",
"lol,NAME_COLLISION",
"cat,UNRESERVED",
"snow,FULLY_BLOCKED");
ReservedList rl3 = persistReservedList(
"tld-reserved3",
false,
"tine,FULLY_BLOCKED",
"oval,UNRESERVED");
createTld("tld");
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2, rl3).build());
// Should not contain jimmy, tine, or oval.
assertThat(ExportUtils.exportReservedTerms(Registry.get("tld")))
.isEqualTo("This is a disclaimer.\ncat\nlol\nsnow\n");
}
}

View file

@ -0,0 +1,294 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.joda.time.DateTimeZone.UTC;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.model.Dataset;
import com.google.api.services.bigquery.model.Job;
import com.google.api.services.bigquery.model.JobConfigurationLoad;
import com.google.api.services.bigquery.model.JobReference;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.domain.registry.bigquery.BigqueryFactory;
import com.google.domain.registry.config.TestRegistryConfig;
import com.google.domain.registry.export.BigqueryPollJobAction.BigqueryPollJobEnqueuer;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.RegistryConfigRule;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Unit tests for {@link LoadSnapshotServlet}. */
@RunWith(MockitoJUnitRunner.class)
public class LoadSnapshotServletTest {
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withTaskQueue()
.build();
@Rule
public final RegistryConfigRule configRule = new RegistryConfigRule(new TestRegistryConfig() {
@Override public String getProjectId() {
return "Project-Id";
}
@Override public String getSnapshotsDataset() {
return "testdataset";
}
});
@Mock
private HttpServletRequest req;
@Mock
private HttpServletResponse rsp;
@Mock
private BigqueryFactory bigqueryFactory;
@Mock
private Bigquery bigquery;
@Mock
private Bigquery.Jobs bigqueryJobs;
@Mock
private Bigquery.Jobs.Insert bigqueryJobsInsert;
@Mock
private Bigquery.Datasets bigqueryDatasets;
@Mock
private Bigquery.Datasets.Insert bigqueryDatasetsInsert;
@Mock
private BigqueryPollJobEnqueuer bigqueryPollEnqueuer;
private static final DateTime NOW = new DateTime(1391096117045L, UTC);
private FakeClock clock = new FakeClock();
private final StringWriter httpOutput = new StringWriter();
private final LoadSnapshotServlet servlet = new LoadSnapshotServlet();
@Before
public void before() throws Exception {
clock.setTo(NOW);
inject.setStaticField(LoadSnapshotServlet.class, "clock", clock);
inject.setStaticField(LoadSnapshotServlet.class, "bigqueryFactory", bigqueryFactory);
inject.setStaticField(LoadSnapshotServlet.class, "bigqueryPollEnqueuer", bigqueryPollEnqueuer);
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
when(bigqueryFactory.create(
anyString(),
any(HttpTransport.class),
any(JsonFactory.class),
any(HttpRequestInitializer.class)))
.thenReturn(bigquery);
when(bigquery.jobs()).thenReturn(bigqueryJobs);
when(bigqueryJobs.insert(eq("Project-Id"), any(Job.class))).thenReturn(bigqueryJobsInsert);
when(bigquery.datasets()).thenReturn(bigqueryDatasets);
when(bigqueryDatasets.insert(eq("Project-Id"), any(Dataset.class)))
.thenReturn(bigqueryDatasetsInsert);
servlet.init(mock(ServletConfig.class));
when(req.getMethod()).thenReturn("POST");
}
@Test
public void testSuccess_enqueueLoadTask() throws Exception {
servlet.enqueueLoadTask(
"id12345", "gs://bucket/snapshot.backup_info", ImmutableSet.of("one", "two", "three"));
assertTasksEnqueued(LoadSnapshotServlet.QUEUE,
new TaskMatcher()
.url(LoadSnapshotServlet.PATH)
.method("POST")
.param(LoadSnapshotServlet.SNAPSHOT_ID_PARAM, "id12345")
.param(LoadSnapshotServlet.SNAPSHOT_FILE_PARAM, "gs://bucket/snapshot.backup_info")
.param(LoadSnapshotServlet.SNAPSHOT_KINDS_PARAM, "one,two,three"));
}
@Test
public void testSuccess_doPost() throws Exception {
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_ID_PARAM)).thenReturn("id12345");
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_FILE_PARAM))
.thenReturn("gs://bucket/snapshot.backup_info");
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_KINDS_PARAM))
.thenReturn("one,two,three");
servlet.service(req, rsp);
// Check that we attempted to create the snapshots dataset.
ArgumentCaptor<Dataset> datasetArgument = ArgumentCaptor.forClass(Dataset.class);
verify(bigqueryDatasets).insert(eq("Project-Id"), datasetArgument.capture());
assertThat(datasetArgument.getValue().getDatasetReference().getProjectId())
.isEqualTo("Project-Id");
assertThat(datasetArgument.getValue().getDatasetReference().getDatasetId())
.isEqualTo("testdataset");
verify(bigqueryDatasetsInsert).execute();
// Capture the load jobs we inserted to do additional checking on them.
ArgumentCaptor<Job> jobArgument = ArgumentCaptor.forClass(Job.class);
verify(bigqueryJobs, times(3)).insert(eq("Project-Id"), jobArgument.capture());
List<Job> jobs = jobArgument.getAllValues();
assertThat(jobs).hasSize(3);
// Check properties that should be common to all load jobs.
for (Job job : jobs) {
assertThat(job.getJobReference().getProjectId()).isEqualTo("Project-Id");
JobConfigurationLoad config = job.getConfiguration().getLoad();
assertThat(config.getSourceFormat()).isEqualTo("DATASTORE_BACKUP");
assertThat(config.getDestinationTable().getProjectId()).isEqualTo("Project-Id");
assertThat(config.getDestinationTable().getDatasetId()).isEqualTo("testdataset");
}
// Check the job IDs for each load job.
assertThat(transform(jobs, new Function<Job, String>() {
@Override
public String apply(Job job) {
return job.getJobReference().getJobId();
}})).containsExactly(
"load-snapshot-id12345-one-1391096117045",
"load-snapshot-id12345-two-1391096117045",
"load-snapshot-id12345-three-1391096117045");
// Check the source URI for each load job.
assertThat(transform(jobs, new Function<Job, String>() {
@Override
public String apply(Job job) {
return Iterables.getOnlyElement(job.getConfiguration().getLoad().getSourceUris());
}})).containsExactly(
"gs://bucket/snapshot.one.backup_info",
"gs://bucket/snapshot.two.backup_info",
"gs://bucket/snapshot.three.backup_info");
// Check the destination table ID for each load job.
assertThat(transform(jobs, new Function<Job, String>() {
@Override
public String apply(Job job) {
return job.getConfiguration().getLoad().getDestinationTable().getTableId();
}})).containsExactly("id12345_one", "id12345_two", "id12345_three");
// Check that we executed the inserted jobs.
verify(bigqueryJobsInsert, times(3)).execute();
// Check that the poll tasks for each load job were enqueued.
verify(bigqueryPollEnqueuer).enqueuePollTask(
new JobReference()
.setProjectId("Project-Id")
.setJobId("load-snapshot-id12345-one-1391096117045"),
UpdateSnapshotViewServlet.createViewUpdateTask("testdataset", "id12345_one", "one"),
QueueFactory.getQueue(UpdateSnapshotViewServlet.QUEUE));
verify(bigqueryPollEnqueuer).enqueuePollTask(
new JobReference()
.setProjectId("Project-Id")
.setJobId("load-snapshot-id12345-two-1391096117045"),
UpdateSnapshotViewServlet.createViewUpdateTask("testdataset", "id12345_two", "two"),
QueueFactory.getQueue(UpdateSnapshotViewServlet.QUEUE));
verify(bigqueryPollEnqueuer).enqueuePollTask(
new JobReference()
.setProjectId("Project-Id")
.setJobId("load-snapshot-id12345-three-1391096117045"),
UpdateSnapshotViewServlet.createViewUpdateTask("testdataset", "id12345_three", "three"),
QueueFactory.getQueue(UpdateSnapshotViewServlet.QUEUE));
verify(rsp).setStatus(SC_OK);
}
@Test
public void testFailure_doPost_missingIdHeader() throws Exception {
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_FILE_PARAM))
.thenReturn("gs://bucket/snapshot.backup_info");
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_KINDS_PARAM))
.thenReturn("one,two,three");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Missing required parameter: id");
}
@Test
public void testFailure_doPost_missingFileHeader() throws Exception {
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_ID_PARAM)).thenReturn("id12345");
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_KINDS_PARAM))
.thenReturn("one,two,three");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Missing required parameter: file");
}
@Test
public void testFailure_doPost_missingKindHeader() throws Exception {
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_ID_PARAM)).thenReturn("id12345");
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_FILE_PARAM))
.thenReturn("gs://bucket/snapshot.backup_info");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Missing required parameter: kinds");
}
@Test
public void testFailure_doPost_badGcsFilename() throws Exception {
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_ID_PARAM)).thenReturn("id12345");
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_FILE_PARAM))
.thenReturn("gs://bucket/snapshot");
when(req.getParameter(LoadSnapshotServlet.SNAPSHOT_KINDS_PARAM))
.thenReturn("one,two,three");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "backup info file extension missing");
}
}

View file

@ -0,0 +1,203 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.export.PublishDetailReportAction.DETAIL_REPORT_NAME_PARAM;
import static com.google.domain.registry.export.PublishDetailReportAction.GCS_BUCKET_PARAM;
import static com.google.domain.registry.export.PublishDetailReportAction.GCS_FOLDER_PREFIX_PARAM;
import static com.google.domain.registry.export.PublishDetailReportAction.REGISTRAR_ID_PARAM;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.appengine.tools.cloudstorage.GcsFileOptions;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.appengine.tools.cloudstorage.GcsServiceFactory;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.MediaType;
import com.google.domain.registry.gcs.GcsUtils;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.google.domain.registry.request.HttpException.InternalServerErrorException;
import com.google.domain.registry.storage.drive.DriveConnection;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
/** Unit tests for {@link PublishDetailReportAction}. */
@RunWith(MockitoJUnitRunner.class)
public class PublishDetailReportActionTest {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Mock
private DriveConnection driveConnection;
private final PublishDetailReportAction action = new PublishDetailReportAction();
private final GcsService gcsService = GcsServiceFactory.createGcsService();
private final GcsUtils gcsUtils = new GcsUtils(gcsService, 1024);
@Before
public void setUp() throws Exception {
action.driveConnection = driveConnection;
action.gcsUtils = gcsUtils;
when(driveConnection.createFile(
anyString(), any(MediaType.class), anyString(), any(byte[].class)))
.thenReturn("drive-id-123");
persistResource(
Registrar.loadByClientId("TheRegistrar").asBuilder().setDriveFolderId("0B-12345").build());
// Persist an empty GCS file to the local GCS service so that failure tests won't fail
// prematurely on the file not existing.
gcsService.createOrReplace(
new GcsFilename("mah-buckit", "some/folder/detail_report.csv"),
GcsFileOptions.getDefaultInstance(),
ByteBuffer.allocate(0));
}
@Test
public void testSuccess() throws Exception {
// Create a dummy file in the local GCS service to read in the servlet.
gcsService.createOrReplace(
new GcsFilename("mah-buckit", "some/folder/detail_report.csv"),
GcsFileOptions.getDefaultInstance(),
ByteBuffer.wrap("one,two,three\n".getBytes(UTF_8)));
Map<String, Object> response =
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "TheRegistrar",
GCS_BUCKET_PARAM, "mah-buckit",
GCS_FOLDER_PREFIX_PARAM, "some/folder/",
DETAIL_REPORT_NAME_PARAM, "detail_report.csv"));
verify(driveConnection).createFile(
"detail_report.csv", MediaType.CSV_UTF_8, "0B-12345", "one,two,three\n".getBytes(UTF_8));
assertThat(response).containsEntry("driveId", "drive-id-123");
}
@Test
public void testFailure_noRegistrarParameter() throws Exception {
thrown.expect(BadRequestException.class, REGISTRAR_ID_PARAM);
action.handleJsonRequest(ImmutableMap.of(
GCS_BUCKET_PARAM, "mah-buckit",
GCS_FOLDER_PREFIX_PARAM, "some/folder/",
DETAIL_REPORT_NAME_PARAM, "detail_report.csv"));
}
@Test
public void testFailure_noGcsBucketParameter() throws Exception {
thrown.expect(BadRequestException.class, GCS_BUCKET_PARAM);
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "TheRegistrar",
GCS_FOLDER_PREFIX_PARAM, "some/folder/",
DETAIL_REPORT_NAME_PARAM, "detail_report.csv"));
}
@Test
public void testFailure_noGcsFolderPrefixParameter() throws Exception {
thrown.expect(BadRequestException.class, GCS_FOLDER_PREFIX_PARAM);
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "TheRegistrar",
GCS_BUCKET_PARAM, "mah-buckit",
DETAIL_REPORT_NAME_PARAM, "detail_report.csv"));
}
@Test
public void testFailure_noReportNameParameter() throws Exception {
thrown.expect(BadRequestException.class, DETAIL_REPORT_NAME_PARAM);
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "TheRegistrar",
GCS_BUCKET_PARAM, "mah-buckit",
GCS_FOLDER_PREFIX_PARAM, "some/folder/"));
}
@Test
public void testFailure_registrarNotFound() throws Exception {
thrown.expect(BadRequestException.class, "FakeRegistrar");
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "FakeRegistrar",
GCS_BUCKET_PARAM, "mah-buckit",
GCS_FOLDER_PREFIX_PARAM, "some/folder/",
DETAIL_REPORT_NAME_PARAM, "detail_report.csv"));
}
@Test
public void testFailure_registrarHasNoDriveFolder() throws Exception {
persistResource(
Registrar.loadByClientId("TheRegistrar").asBuilder().setDriveFolderId(null).build());
thrown.expect(BadRequestException.class, "drive folder");
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "TheRegistrar",
GCS_BUCKET_PARAM, "mah-buckit",
GCS_FOLDER_PREFIX_PARAM, "some/folder/",
DETAIL_REPORT_NAME_PARAM, "detail_report.csv"));
}
@Test
public void testFailure_gcsBucketNotFound() throws Exception {
thrown.expect(BadRequestException.class, "fake-buckit");
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "TheRegistrar",
GCS_BUCKET_PARAM, "fake-buckit",
GCS_FOLDER_PREFIX_PARAM, "some/folder/",
DETAIL_REPORT_NAME_PARAM, "detail_report.csv"));
}
@Test
public void testFailure_gcsFileNotFound() throws Exception {
thrown.expect(BadRequestException.class, "some/folder/fake_file.csv");
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "TheRegistrar",
GCS_BUCKET_PARAM, "mah-buckit",
GCS_FOLDER_PREFIX_PARAM, "some/folder/",
DETAIL_REPORT_NAME_PARAM, "fake_file.csv"));
}
@Test
public void testFailure_driveApiThrowsException() throws Exception {
when(driveConnection.createFile(
anyString(), any(MediaType.class), anyString(), any(byte[].class)))
.thenThrow(new IOException("Drive is down"));
thrown.expect(InternalServerErrorException.class, "Drive is down");
action.handleJsonRequest(ImmutableMap.of(
REGISTRAR_ID_PARAM, "TheRegistrar",
GCS_BUCKET_PARAM, "mah-buckit",
GCS_FOLDER_PREFIX_PARAM, "some/folder/",
DETAIL_REPORT_NAME_PARAM, "detail_report.csv"));
}
}

View file

@ -0,0 +1,224 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.export.SyncGroupMembersTask.getGroupEmailAddressForContactType;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.model.registrar.RegistrarContact.Type.ADMIN;
import static com.google.domain.registry.model.registrar.RegistrarContact.Type.MARKETING;
import static com.google.domain.registry.model.registrar.RegistrarContact.Type.TECH;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.domain.registry.groups.DirectoryGroupsConnection;
import com.google.domain.registry.groups.GroupsConnection.Role;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registrar.RegistrarContact;
import com.google.domain.registry.request.Response;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.InjectRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.IOException;
/**
* Unit tests for {@link SyncGroupMembersTask}.
*
* <p>Note that this relies on the registrars NewRegistrar and TheRegistrar created by default in
* {@link AppEngineRule}.
*/
@RunWith(MockitoJUnitRunner.class)
public class SyncGroupMembersTaskTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final InjectRule inject = new InjectRule();
@Mock
private DirectoryGroupsConnection connection;
@Mock
private Response response;
private void runTask() {
SyncGroupMembersTask task = new SyncGroupMembersTask();
task.groupsConnection = connection;
task.response = response;
task.publicDomainName = "domain-registry.example";
task.run();
}
@Test
public void test_getGroupEmailAddressForContactType_convertsToLowercase() {
assertThat(getGroupEmailAddressForContactType(
"SomeRegistrar",
RegistrarContact.Type.ADMIN,
"domain-registry.example"))
.isEqualTo("someregistrar-primary-contacts@domain-registry.example");
}
@Test
public void test_getGroupEmailAddressForContactType_convertsNonAlphanumericChars() {
assertThat(getGroupEmailAddressForContactType(
"Weird.ಠ_ಠRegistrar",
MARKETING,
"domain-registry.example"))
.isEqualTo("weirdregistrar-marketing-contacts@domain-registry.example");
}
@Test
public void test_doPost_noneModified() throws Exception {
persistResource(Registrar.loadByClientId("NewRegistrar")
.asBuilder()
.setContactsRequireSyncing(false)
.build());
persistResource(Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setContactsRequireSyncing(false)
.build());
runTask();
verify(response).setStatus(SC_OK);
verify(response).setPayload("NOT_MODIFIED No registrar contacts have been updated "
+ "since the last time servlet ran.\n");
assertThat(Registrar.loadByClientId("NewRegistrar").getContactsRequireSyncing()).isFalse();
}
@Test
public void test_doPost_syncsNewContact() throws Exception {
runTask();
verify(connection).addMemberToGroup(
"newregistrar-primary-contacts@domain-registry.example",
"janedoe@theregistrar.com",
Role.MEMBER);
verify(response).setStatus(SC_OK);
verify(response).setPayload("OK Group memberships successfully updated.\n");
assertThat(Registrar.loadByClientId("NewRegistrar").getContactsRequireSyncing()).isFalse();
}
@Test
public void test_doPost_removesOldContact() throws Exception {
when(connection.getMembersOfGroup("newregistrar-primary-contacts@domain-registry.example"))
.thenReturn(ImmutableSet.of("defunct@example.com", "janedoe@theregistrar.com"));
runTask();
verify(connection).removeMemberFromGroup(
"newregistrar-primary-contacts@domain-registry.example", "defunct@example.com");
verify(response).setStatus(SC_OK);
assertThat(Registrar.loadByClientId("NewRegistrar").getContactsRequireSyncing()).isFalse();
}
@Test
public void test_doPost_removesAllContactsFromGroup() throws Exception {
when(connection.getMembersOfGroup("newregistrar-primary-contacts@domain-registry.example"))
.thenReturn(ImmutableSet.of("defunct@example.com", "janedoe@theregistrar.com"));
ofy().deleteWithoutBackup()
.entities(Registrar.loadByClientId("NewRegistrar").getContacts())
.now();
runTask();
verify(connection).removeMemberFromGroup(
"newregistrar-primary-contacts@domain-registry.example", "defunct@example.com");
verify(connection).removeMemberFromGroup(
"newregistrar-primary-contacts@domain-registry.example", "janedoe@theregistrar.com");
verify(response).setStatus(SC_OK);
assertThat(Registrar.loadByClientId("NewRegistrar").getContactsRequireSyncing()).isFalse();
}
@Test
public void test_doPost_addsAndRemovesContacts_acrossMultipleRegistrars() throws Exception {
when(connection.getMembersOfGroup("newregistrar-primary-contacts@domain-registry.example"))
.thenReturn(ImmutableSet.of("defunct@example.com", "janedoe@theregistrar.com"));
when(connection.getMembersOfGroup("newregistrar-marketing-contacts@domain-registry.example"))
.thenReturn(ImmutableSet.<String> of());
when(connection.getMembersOfGroup("theregistrar-technical-contacts@domain-registry.example"))
.thenReturn(ImmutableSet.<String> of());
when(connection.getMembersOfGroup("theregistrar-primary-contacts@domain-registry.example"))
.thenReturn(ImmutableSet.<String> of());
persistResource(
new RegistrarContact.Builder()
.setParent(Registrar.loadByClientId("NewRegistrar"))
.setName("Binary Star")
.setEmailAddress("binarystar@example.tld")
.setTypes(ImmutableSet.of(ADMIN, MARKETING))
.build());
persistResource(
new RegistrarContact.Builder()
.setParent(Registrar.loadByClientId("TheRegistrar"))
.setName("Hexadecimal")
.setEmailAddress("hexadecimal@snow.fall")
.setTypes(ImmutableSet.of(TECH))
.build());
runTask();
verify(connection).removeMemberFromGroup(
"newregistrar-primary-contacts@domain-registry.example", "defunct@example.com");
verify(connection).addMemberToGroup(
"newregistrar-primary-contacts@domain-registry.example",
"binarystar@example.tld",
Role.MEMBER);
verify(connection).addMemberToGroup(
"newregistrar-marketing-contacts@domain-registry.example",
"binarystar@example.tld",
Role.MEMBER);
verify(connection).addMemberToGroup(
"theregistrar-primary-contacts@domain-registry.example",
"johndoe@theregistrar.com",
Role.MEMBER);
verify(connection).addMemberToGroup(
"theregistrar-technical-contacts@domain-registry.example",
"hexadecimal@snow.fall",
Role.MEMBER);
verify(response).setStatus(SC_OK);
assertThat(Iterables.filter(Registrar.loadAll(), new Predicate<Registrar>() {
@Override
public boolean apply(Registrar registrar) {
return registrar.getContactsRequireSyncing();
}})).isEmpty();
}
@Test
public void test_doPost_gracefullyHandlesExceptionForSingleRegistrar() throws Exception {
when(connection.getMembersOfGroup("newregistrar-primary-contacts@domain-registry.example"))
.thenReturn(ImmutableSet.<String> of());
when(connection.getMembersOfGroup("theregistrar-primary-contacts@domain-registry.example"))
.thenThrow(new IOException("Internet was deleted"));
runTask();
verify(connection).addMemberToGroup(
"newregistrar-primary-contacts@domain-registry.example",
"janedoe@theregistrar.com",
Role.MEMBER);
verify(response).setStatus(SC_INTERNAL_SERVER_ERROR);
verify(response).setPayload("FAILED Error occurred while updating registrar contacts.\n");
assertThat(Registrar.loadByClientId("NewRegistrar").getContactsRequireSyncing()).isFalse();
assertThat(Registrar.loadByClientId("TheRegistrar").getContactsRequireSyncing()).isTrue();
}
}

View file

@ -0,0 +1,203 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.model.Dataset;
import com.google.api.services.bigquery.model.Table;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.domain.registry.bigquery.BigqueryFactory;
import com.google.domain.registry.config.TestRegistryConfig;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.RegistryConfigRule;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Unit tests for {@link UpdateSnapshotViewServlet}. */
@RunWith(MockitoJUnitRunner.class)
public class UpdateSnapshotViewServletTest {
@Rule
public final InjectRule inject = new InjectRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withTaskQueue()
.build();
@Rule
public final RegistryConfigRule configRule = new RegistryConfigRule(new TestRegistryConfig() {
@Override public String getProjectId() {
return "Project-Id";
}
@Override public String getLatestSnapshotDataset() {
return "testdataset";
}
});
@Mock
private HttpServletRequest req;
@Mock
private HttpServletResponse rsp;
@Mock
private BigqueryFactory bigqueryFactory;
@Mock
private Bigquery bigquery;
@Mock
private Bigquery.Datasets bigqueryDatasets;
@Mock
private Bigquery.Datasets.Insert bigqueryDatasetsInsert;
@Mock
private Bigquery.Tables bigqueryTables;
@Mock
private Bigquery.Tables.Update bigqueryTablesUpdate;
private final StringWriter httpOutput = new StringWriter();
private final UpdateSnapshotViewServlet servlet = new UpdateSnapshotViewServlet();
@Before
public void before() throws Exception {
inject.setStaticField(UpdateSnapshotViewServlet.class, "bigqueryFactory", bigqueryFactory);
when(req.getMethod()).thenReturn("POST");
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
when(bigqueryFactory.create(
anyString(),
any(HttpTransport.class),
any(JsonFactory.class),
any(HttpRequestInitializer.class)))
.thenReturn(bigquery);
when(bigquery.datasets()).thenReturn(bigqueryDatasets);
when(bigqueryDatasets.insert(eq("Project-Id"), any(Dataset.class)))
.thenReturn(bigqueryDatasetsInsert);
when(bigquery.tables()).thenReturn(bigqueryTables);
when(bigqueryTables.update(
eq("Project-Id"), any(String.class), any(String.class), any(Table.class)))
.thenReturn(bigqueryTablesUpdate);
servlet.init(mock(ServletConfig.class));
}
@Test
public void testSuccess_createViewUpdateTask() throws Exception {
QueueFactory.getDefaultQueue().add(
UpdateSnapshotViewServlet.createViewUpdateTask("some_dataset", "12345_fookind", "fookind"));
assertTasksEnqueued("default",
new TaskMatcher()
.url(UpdateSnapshotViewServlet.PATH)
.method("POST")
.param(UpdateSnapshotViewServlet.SNAPSHOT_DATASET_ID_PARAM, "some_dataset")
.param(UpdateSnapshotViewServlet.SNAPSHOT_TABLE_ID_PARAM, "12345_fookind")
.param(UpdateSnapshotViewServlet.SNAPSHOT_KIND_PARAM, "fookind"));
}
@Test
public void testSuccess_doPost() throws Exception {
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_DATASET_ID_PARAM))
.thenReturn("some_dataset");
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_TABLE_ID_PARAM))
.thenReturn("12345_fookind");
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_KIND_PARAM))
.thenReturn("fookind");
servlet.service(req, rsp);
// Check that we attempted to create the latest_snapshot dataset.
ArgumentCaptor<Dataset> datasetArgument = ArgumentCaptor.forClass(Dataset.class);
verify(bigqueryDatasets).insert(eq("Project-Id"), datasetArgument.capture());
assertThat(datasetArgument.getValue().getDatasetReference().getProjectId())
.isEqualTo("Project-Id");
assertThat(datasetArgument.getValue().getDatasetReference().getDatasetId())
.isEqualTo("testdataset");
verify(bigqueryDatasetsInsert).execute();
// Check that we updated the view.
ArgumentCaptor<Table> tableArg = ArgumentCaptor.forClass(Table.class);
verify(bigqueryTables).update(
eq("Project-Id"), eq("testdataset"), eq("fookind"), tableArg.capture());
assertThat(tableArg.getValue().getView().getQuery())
.isEqualTo("SELECT * FROM [some_dataset.12345_fookind]");
verify(rsp).setStatus(SC_OK);
}
@Test
public void testFailure_doPost_missingDatasetIdHeader() throws Exception {
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_TABLE_ID_PARAM))
.thenReturn("12345_fookind");
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_KIND_PARAM)).thenReturn("fookind");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Missing required parameter: dataset");
}
@Test
public void testFailure_doPost_missingTableIdHeader() throws Exception {
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_DATASET_ID_PARAM))
.thenReturn("some_dataset");
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_KIND_PARAM)).thenReturn("fookind");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Missing required parameter: table");
}
@Test
public void testFailure_doPost_missingKindHeader() throws Exception {
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_DATASET_ID_PARAM))
.thenReturn("some_dataset");
when(req.getParameter(UpdateSnapshotViewServlet.SNAPSHOT_TABLE_ID_PARAM))
.thenReturn("12345_fookind");
servlet.service(req, rsp);
verify(rsp).sendError(SC_BAD_REQUEST, "Missing required parameter: kind");
}
}

View file

@ -0,0 +1,27 @@
Cancellation
ContactResource
DomainApplicationIndex
DomainBase
EntityGroupRoot
EppResourceIndex
ForeignKeyContactIndex
ForeignKeyDomainIndex
ForeignKeyHostIndex
HistoryEntry
HostResource
LogsExportCursor
Modification
OneTime
PollMessage
PremiumList
PremiumListEntry
RdeRevision
Recurring
Registrar
RegistrarBillingEntry
RegistrarContact
RegistrarCredit
RegistrarCreditBalance
Registry
RegistryCursor
ReservedList

View file

@ -0,0 +1,31 @@
package(default_visibility = ["//java/com/google/domain/registry:registry_project"])
java_library(
name = "sheet",
srcs = glob(["*.java"]),
deps = [
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/net",
"//java/com/google/domain/registry/config",
"//java/com/google/domain/registry/export/sheet",
"//java/com/google/domain/registry/model",
"//java/com/google/gdata:spreadsheet",
"//javatests/com/google/domain/registry/testing",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/servlet/servlet_api",
"//third_party/java/truth",
],
)
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":sheet"],
)

View file

@ -0,0 +1,148 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export.sheet;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.spreadsheet.CustomElementCollection;
import com.google.gdata.data.spreadsheet.ListEntry;
import com.google.gdata.data.spreadsheet.ListFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.net.URL;
/** Unit tests for {@link SheetSynchronizer}. */
@RunWith(JUnit4.class)
public class SheetSynchronizerTest {
private final SpreadsheetService spreadsheetService = mock(SpreadsheetService.class);
private final SpreadsheetEntry spreadsheet = mock(SpreadsheetEntry.class);
private final WorksheetEntry worksheet = mock(WorksheetEntry.class);
private final ListFeed listFeed = mock(ListFeed.class);
private final SheetSynchronizer sheetSynchronizer = new SheetSynchronizer();
@Before
public void before() throws Exception {
sheetSynchronizer.spreadsheetService = spreadsheetService;
when(spreadsheetService.getEntry(any(URL.class), eq(SpreadsheetEntry.class)))
.thenReturn(spreadsheet);
when(spreadsheet.getWorksheets())
.thenReturn(ImmutableList.of(worksheet));
when(worksheet.getListFeedUrl())
.thenReturn(new URL("http://example.com/spreadsheet"));
when(spreadsheetService.getFeed(any(URL.class), eq(ListFeed.class)))
.thenReturn(listFeed);
when(worksheet.update())
.thenReturn(worksheet);
}
@After
public void after() throws Exception {
verify(spreadsheetService).getEntry(
new URL("https://spreadsheets.google.com/feeds/spreadsheets/foobar"),
SpreadsheetEntry.class);
verify(spreadsheet).getWorksheets();
verify(worksheet).getListFeedUrl();
verify(spreadsheetService).getFeed(new URL("http://example.com/spreadsheet"), ListFeed.class);
verify(listFeed).getEntries();
verifyNoMoreInteractions(spreadsheetService, spreadsheet, worksheet, listFeed);
}
@Test
public void testSynchronize_bothEmpty_doNothing() throws Exception {
when(listFeed.getEntries()).thenReturn(ImmutableList.<ListEntry>of());
sheetSynchronizer.synchronize("foobar", ImmutableList.<ImmutableMap<String, String>>of());
verify(worksheet).setRowCount(0);
verify(worksheet).update();
}
@Test
public void testSynchronize_bothContainSameRow_doNothing() throws Exception {
ListEntry entry = makeListEntry(ImmutableMap.of("key", "value"));
when(listFeed.getEntries()).thenReturn(ImmutableList.of(entry));
sheetSynchronizer.synchronize("foobar", ImmutableList.of(
ImmutableMap.of("key", "value")));
verify(worksheet).setRowCount(1);
verify(worksheet).update();
verify(entry, atLeastOnce()).getCustomElements();
verifyNoMoreInteractions(entry);
}
@Test
public void testSynchronize_cellIsDifferent_updateRow() throws Exception {
ListEntry entry = makeListEntry(ImmutableMap.of("key", "value"));
when(listFeed.getEntries()).thenReturn(ImmutableList.of(entry));
sheetSynchronizer.synchronize("foobar", ImmutableList.of(
ImmutableMap.of("key", "new value")));
verify(entry.getCustomElements()).setValueLocal("key", "new value");
verify(entry).update();
verify(worksheet).setRowCount(1);
verify(worksheet).update();
verify(entry, atLeastOnce()).getCustomElements();
verifyNoMoreInteractions(entry);
}
@Test
public void testSynchronize_spreadsheetMissingRow_insertRow() throws Exception {
ListEntry entry = makeListEntry(ImmutableMap.<String, String>of());
when(listFeed.getEntries()).thenReturn(ImmutableList.<ListEntry>of());
when(listFeed.createEntry()).thenReturn(entry);
sheetSynchronizer.synchronize("foobar", ImmutableList.of(
ImmutableMap.of("key", "value")));
verify(entry.getCustomElements()).setValueLocal("key", "value");
verify(listFeed).insert(entry);
verify(worksheet).setRowCount(1);
verify(worksheet).update();
verify(listFeed).createEntry();
verify(entry, atLeastOnce()).getCustomElements();
verifyNoMoreInteractions(entry);
}
@Test
public void testSynchronize_spreadsheetRowNoLongerInData_deleteRow() throws Exception {
ListEntry entry = makeListEntry(ImmutableMap.of("key", "value"));
when(listFeed.getEntries()).thenReturn(ImmutableList.of(entry));
sheetSynchronizer.synchronize("foobar", ImmutableList.<ImmutableMap<String, String>>of());
verify(worksheet).setRowCount(0);
verify(worksheet).update();
verifyNoMoreInteractions(entry);
}
private static ListEntry makeListEntry(ImmutableMap<String, String> values) {
CustomElementCollection collection = mock(CustomElementCollection.class);
for (ImmutableMap.Entry<String, String> entry : values.entrySet()) {
when(collection.getValue(eq(entry.getKey()))).thenReturn(entry.getValue());
}
ListEntry listEntry = mock(ListEntry.class);
when(listEntry.getCustomElements()).thenReturn(collection);
return listEntry;
}
}

View file

@ -0,0 +1,115 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export.sheet;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.google.common.base.Optional;
import com.google.domain.registry.model.server.Lock;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeResponse;
import org.joda.time.Duration;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.concurrent.Callable;
import javax.annotation.Nullable;
/** Unit tests for {@link SyncRegistrarsSheetTask}. */
@RunWith(JUnit4.class)
public class SyncRegistrarsSheetTaskTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
private final FakeResponse response = new FakeResponse();
private final SyncRegistrarsSheet syncRegistrarsSheet = mock(SyncRegistrarsSheet.class);
private void runTask(@Nullable String idConfig, @Nullable String idParam) {
SyncRegistrarsSheetTask task = new SyncRegistrarsSheetTask();
task.response = response;
task.syncRegistrarsSheet = syncRegistrarsSheet;
task.idConfig = Optional.fromNullable(idConfig);
task.idParam = Optional.fromNullable(idParam);
task.interval = Duration.standardHours(1);
task.timeout = Duration.standardHours(1);
task.run();
}
@Test
public void testPost_withoutParamsOrSystemProperty_dropsTask() throws Exception {
runTask(null, null);
assertThat(response.getPayload()).startsWith("MISSINGNO");
verifyZeroInteractions(syncRegistrarsSheet);
}
@Test
public void testPost_withoutParams_runsSyncWithDefaultIdAndChecksIfModified() throws Exception {
when(syncRegistrarsSheet.wasRegistrarsModifiedInLast(any(Duration.class))).thenReturn(true);
runTask("jazz", null);
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).startsWith("OK");
verify(syncRegistrarsSheet).wasRegistrarsModifiedInLast(any(Duration.class));
verify(syncRegistrarsSheet).run(eq("jazz"));
verifyNoMoreInteractions(syncRegistrarsSheet);
}
@Test
public void testPost_noModificationsToRegistrarEntities_doesNothing() throws Exception {
when(syncRegistrarsSheet.wasRegistrarsModifiedInLast(any(Duration.class))).thenReturn(false);
runTask("NewRegistrar", null);
assertThat(response.getPayload()).startsWith("NOTMODIFIED");
verify(syncRegistrarsSheet).wasRegistrarsModifiedInLast(any(Duration.class));
verifyNoMoreInteractions(syncRegistrarsSheet);
}
@Test
public void testPost_overrideId_runsSyncWithCustomIdAndDoesNotCheckModified() throws Exception {
runTask(null, "foobar");
assertThat(response.getPayload()).startsWith("OK");
verify(syncRegistrarsSheet).run(eq("foobar"));
verifyNoMoreInteractions(syncRegistrarsSheet);
}
@Test
public void testPost_failToAquireLock_servletDoesNothingAndReturns() throws Exception {
String lockName = "Synchronize registrars sheet: foobar";
Lock.executeWithLocks(new Callable<Void>() {
@Override
public Void call() throws Exception {
runTask(null, "foobar");
return null;
}
}, SyncRegistrarsSheetTask.class, "", Duration.standardHours(1), lockName);
assertThat(response.getPayload()).startsWith("LOCKED");
verifyZeroInteractions(syncRegistrarsSheet);
}
}

View file

@ -0,0 +1,349 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.export.sheet;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistSimpleGlobalResources;
import static org.joda.time.DateTimeZone.UTC;
import static org.joda.time.Duration.standardHours;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.config.RegistryEnvironment;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registrar.RegistrarAddress;
import com.google.domain.registry.model.registrar.RegistrarContact;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
/** Unit tests for {@link SyncRegistrarsSheet}. */
@RunWith(MockitoJUnitRunner.class)
public class SyncRegistrarsSheetTest {
private static final RegistryEnvironment ENVIRONMENT = RegistryEnvironment.get();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Rule
public final InjectRule inject = new InjectRule();
@Captor
private ArgumentCaptor<ImmutableList<ImmutableMap<String, String>>> rowsCaptor;
@Mock
private SheetSynchronizer sheetSynchronizer;
private final FakeClock clock = new FakeClock(DateTime.now(UTC));
private SyncRegistrarsSheet newSyncRegistrarsSheet() {
SyncRegistrarsSheet result = new SyncRegistrarsSheet();
result.clock = clock;
result.sheetSynchronizer = sheetSynchronizer;
return result;
}
@Before
public void before() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock);
createTld("example");
// Remove Registrar entities created by AppEngineRule.
for (Registrar registrar : Registrar.loadAll()) {
deleteResource(registrar);
}
}
@Test
public void testWasRegistrarsModifiedInLast_noRegistrars_returnsFalse() throws Exception {
SyncRegistrarsSheet sync = newSyncRegistrarsSheet();
assertThat(sync.wasRegistrarsModifiedInLast(Duration.standardHours(1))).isFalse();
}
@Test
public void testWasRegistrarsModifiedInLastInterval() throws Exception {
Duration interval = standardHours(1);
persistResource(new Registrar.Builder()
.setClientIdentifier("SomeRegistrar")
.setRegistrarName("Some Registrar Inc.")
.setType(Registrar.Type.REAL)
.setIanaIdentifier(8L)
.setState(Registrar.State.ACTIVE)
.build());
clock.advanceBy(interval);
assertThat(newSyncRegistrarsSheet().wasRegistrarsModifiedInLast(interval)).isTrue();
clock.advanceOneMilli();
assertThat(newSyncRegistrarsSheet().wasRegistrarsModifiedInLast(interval)).isFalse();
}
@Test
public void testRun() throws Exception {
persistResource(new Registrar.Builder()
.setClientIdentifier("anotherregistrar")
.setRegistrarName("Another Registrar LLC")
.setType(Registrar.Type.REAL)
.setIanaIdentifier(1L)
.setState(Registrar.State.ACTIVE)
.setInternationalizedAddress(new RegistrarAddress.Builder()
.setStreet(ImmutableList.of("I will get ignored :'("))
.setCity("Williamsburg")
.setState("NY")
.setZip("11211")
.setCountryCode("US")
.build())
.setLocalizedAddress(new RegistrarAddress.Builder()
.setStreet(ImmutableList.of(
"123 Main St",
"Suite 100"))
.setCity("Smalltown")
.setState("NY")
.setZip("11211")
.setCountryCode("US")
.build())
.setPhoneNumber("+1.2125551212")
.setFaxNumber("+1.2125551213")
.setEmailAddress("contact-us@example.com")
.setWhoisServer("whois.example.com")
.setUrl("http://www.example.org/another_registrar")
.setIcannReferralEmail("jim@example.net")
.build());
Registrar registrar = new Registrar.Builder()
.setClientIdentifier("aaaregistrar")
.setRegistrarName("AAA Registrar Inc.")
.setType(Registrar.Type.REAL)
.setIanaIdentifier(8L)
.setState(Registrar.State.SUSPENDED)
.setPassword("pa$$word")
.setEmailAddress("nowhere@example.org")
.setInternationalizedAddress(new RegistrarAddress.Builder()
.setStreet(ImmutableList.of("I get fallen back upon since there's no l10n addr"))
.setCity("Williamsburg")
.setState("NY")
.setZip("11211")
.setCountryCode("US")
.build())
.setAllowedTlds(ImmutableSet.of("example"))
.setPhoneNumber("+1.2223334444")
.setUrl("http://www.example.org/aaa_registrar")
.build();
ImmutableList<RegistrarContact> contacts = ImmutableList.of(
new RegistrarContact.Builder()
.setParent(registrar)
.setName("Jane Doe")
.setEmailAddress("contact@example.com")
.setPhoneNumber("+1.1234567890")
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN, RegistrarContact.Type.BILLING))
.build(),
new RegistrarContact.Builder()
.setParent(registrar)
.setName("John Doe")
.setEmailAddress("john.doe@example.tld")
.setPhoneNumber("+1.1234567890")
.setFaxNumber("+1.1234567891")
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
// Purposely flip the internal/external admin/tech
// distinction to make sure we're not relying on it. Sigh.
.setVisibleInWhoisAsAdmin(false)
.setVisibleInWhoisAsTech(true)
.setGaeUserId("light")
.build(),
new RegistrarContact.Builder()
.setParent(registrar)
.setName("Jane Smith")
.setEmailAddress("pride@example.net")
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
.build());
// Use registrar ref for contacts' parent.
persistSimpleGlobalResources(contacts);
persistResource(registrar);
newSyncRegistrarsSheet().run("foobar");
verify(sheetSynchronizer).synchronize(eq("foobar"), rowsCaptor.capture());
ImmutableList<ImmutableMap<String, String>> rows = getOnlyElement(rowsCaptor.getAllValues());
assertThat(rows).hasSize(2);
ImmutableMap<String, String> row = rows.get(0);
assertThat(row).containsEntry("clientIdentifier", "aaaregistrar");
assertThat(row).containsEntry("registrarName", "AAA Registrar Inc.");
assertThat(row).containsEntry("state", "SUSPENDED");
assertThat(row).containsEntry("ianaIdentifier", "8");
assertThat(row).containsEntry("billingIdentifier", "");
assertThat(row).containsEntry("primaryContacts", ""
+ "Jane Doe\n"
+ "contact@example.com\n"
+ "Tel: +1.1234567890\n"
+ "Types: [ADMIN, BILLING]\n"
+ "Visible in WHOIS as Admin contact: No\n"
+ "Visible in WHOIS as Technical contact: No\n"
+ "\n"
+ "John Doe\n"
+ "john.doe@example.tld\n"
+ "Tel: +1.1234567890\n"
+ "Fax: +1.1234567891\n"
+ "Types: [ADMIN]\n"
+ "Visible in WHOIS as Admin contact: No\n"
+ "Visible in WHOIS as Technical contact: Yes\n"
+ "GAE-UserID: light\n");
assertThat(row).containsEntry("techContacts", ""
+ "Jane Smith\n"
+ "pride@example.net\n"
+ "Types: [TECH]\n"
+ "Visible in WHOIS as Admin contact: No\n"
+ "Visible in WHOIS as Technical contact: No\n");
assertThat(row).containsEntry("marketingContacts", "");
assertThat(row).containsEntry("abuseContacts", "");
assertThat(row).containsEntry("whoisInquiryContacts", "");
assertThat(row).containsEntry("legalContacts", "");
assertThat(row).containsEntry("billingContacts", ""
+ "Jane Doe\n"
+ "contact@example.com\n"
+ "Tel: +1.1234567890\n"
+ "Types: [ADMIN, BILLING]\n"
+ "Visible in WHOIS as Admin contact: No\n"
+ "Visible in WHOIS as Technical contact: No\n");
assertThat(row).containsEntry("contactsMarkedAsWhoisAdmin", "");
assertThat(row).containsEntry("contactsMarkedAsWhoisTech", ""
+ "John Doe\n"
+ "john.doe@example.tld\n"
+ "Tel: +1.1234567890\n"
+ "Fax: +1.1234567891\n"
+ "Types: [ADMIN]\n"
+ "Visible in WHOIS as Admin contact: No\n"
+ "Visible in WHOIS as Technical contact: Yes\n"
+ "GAE-UserID: light\n");
assertThat(row).containsEntry("emailAddress", "nowhere@example.org");
assertThat(row).containsEntry(
"address.street", "I get fallen back upon since there's no l10n addr");
assertThat(row).containsEntry("address.city", "Williamsburg");
assertThat(row).containsEntry("address.state", "NY");
assertThat(row).containsEntry("address.zip", "11211");
assertThat(row).containsEntry("address.countryCode", "US");
assertThat(row).containsEntry("phoneNumber", "+1.2223334444");
assertThat(row).containsEntry("faxNumber", "");
assertThat(row.get("creationTime")).isEqualTo(clock.nowUtc().toString());
assertThat(row.get("lastUpdateTime")).isEqualTo(clock.nowUtc().toString());
assertThat(row).containsEntry("allowedTlds", "example");
assertThat(row).containsEntry("blockPremiumNames", "false");
assertThat(row).containsEntry("ipAddressWhitelist", "");
assertThat(row).containsEntry("url", "http://www.example.org/aaa_registrar");
assertThat(row).containsEntry("icannReferralEmail", "");
assertThat(row).containsEntry("whoisServer",
ENVIRONMENT.config().getRegistrarDefaultWhoisServer());
assertThat(row).containsEntry("referralUrl",
ENVIRONMENT.config().getRegistrarDefaultReferralUrl().toString());
row = rows.get(1);
assertThat(row).containsEntry("clientIdentifier", "anotherregistrar");
assertThat(row).containsEntry("registrarName", "Another Registrar LLC");
assertThat(row).containsEntry("state", "ACTIVE");
assertThat(row).containsEntry("ianaIdentifier", "1");
assertThat(row).containsEntry("billingIdentifier", "");
assertThat(row).containsEntry("primaryContacts", "");
assertThat(row).containsEntry("techContacts", "");
assertThat(row).containsEntry("marketingContacts", "");
assertThat(row).containsEntry("abuseContacts", "");
assertThat(row).containsEntry("whoisInquiryContacts", "");
assertThat(row).containsEntry("legalContacts", "");
assertThat(row).containsEntry("billingContacts", "");
assertThat(row).containsEntry("contactsMarkedAsWhoisAdmin", "");
assertThat(row).containsEntry("contactsMarkedAsWhoisTech", "");
assertThat(row).containsEntry("emailAddress", "contact-us@example.com");
assertThat(row).containsEntry("address.street", "123 Main St\nSuite 100");
assertThat(row).containsEntry("address.city", "Smalltown");
assertThat(row).containsEntry("address.state", "NY");
assertThat(row).containsEntry("address.zip", "11211");
assertThat(row).containsEntry("address.countryCode", "US");
assertThat(row).containsEntry("phoneNumber", "+1.2125551212");
assertThat(row).containsEntry("faxNumber", "+1.2125551213");
assertThat(row.get("creationTime")).isEqualTo(clock.nowUtc().toString());
assertThat(row.get("lastUpdateTime")).isEqualTo(clock.nowUtc().toString());
assertThat(row).containsEntry("allowedTlds", "");
assertThat(row).containsEntry("whoisServer", "whois.example.com");
assertThat(row).containsEntry("blockPremiumNames", "false");
assertThat(row).containsEntry("ipAddressWhitelist", "");
assertThat(row).containsEntry("url", "http://www.example.org/another_registrar");
assertThat(row).containsEntry("referralUrl",
ENVIRONMENT.config().getRegistrarDefaultReferralUrl().toString());
assertThat(row).containsEntry("icannReferralEmail", "jim@example.net");
}
@Test
public void testRun_missingValues_stillWorks() throws Exception {
persistResource(new Registrar.Builder()
.setClientIdentifier("SomeRegistrar")
.setType(Registrar.Type.REAL)
.setIanaIdentifier(8L)
.build());
newSyncRegistrarsSheet().run("foobar");
verify(sheetSynchronizer).synchronize(eq("foobar"), rowsCaptor.capture());
ImmutableMap<String, String> row = getOnlyElement(getOnlyElement(rowsCaptor.getAllValues()));
assertThat(row).containsEntry("clientIdentifier", "SomeRegistrar");
assertThat(row).containsEntry("registrarName", "");
assertThat(row).containsEntry("state", "");
assertThat(row).containsEntry("ianaIdentifier", "8");
assertThat(row).containsEntry("billingIdentifier", "");
assertThat(row).containsEntry("primaryContacts", "");
assertThat(row).containsEntry("techContacts", "");
assertThat(row).containsEntry("marketingContacts", "");
assertThat(row).containsEntry("abuseContacts", "");
assertThat(row).containsEntry("whoisInquiryContacts", "");
assertThat(row).containsEntry("legalContacts", "");
assertThat(row).containsEntry("billingContacts", "");
assertThat(row).containsEntry("contactsMarkedAsWhoisAdmin", "");
assertThat(row).containsEntry("contactsMarkedAsWhoisTech", "");
assertThat(row).containsEntry("emailAddress", "");
assertThat(row).containsEntry("address.street", "UNKNOWN");
assertThat(row).containsEntry("address.city", "UNKNOWN");
assertThat(row).containsEntry("address.state", "");
assertThat(row).containsEntry("address.zip", "");
assertThat(row).containsEntry("address.countryCode", "US");
assertThat(row).containsEntry("phoneNumber", "");
assertThat(row).containsEntry("faxNumber", "");
assertThat(row).containsEntry("allowedTlds", "");
assertThat(row).containsEntry("whoisServer",
ENVIRONMENT.config().getRegistrarDefaultWhoisServer());
assertThat(row).containsEntry("blockPremiumNames", "false");
assertThat(row).containsEntry("ipAddressWhitelist", "");
assertThat(row).containsEntry("url", "");
assertThat(row).containsEntry("referralUrl",
ENVIRONMENT.config().getRegistrarDefaultReferralUrl().toString());
assertThat(row).containsEntry("icannReferralEmail", "");
}
}

View file

@ -0,0 +1,33 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="0">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,28 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-one.tld</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<allocate:create xmlns:allocate="urn:google:params:xml:ns:allocate-1.0">
<allocate:applicationRoid>1-TLD</allocate:applicationRoid>
<allocate:applicationTime>2010-08-16T10:00:00.0Z</allocate:applicationTime>
</allocate:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.com</domain:name>
<domain:period unit="y">2</domain:period>
<domain:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,13 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<renew>
<domain:renew
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.tld</domain:name>
<domain:curExpDate>2000-04-03</domain:curExpDate>
<domain:period unit="y">5</domain:period>
</domain:renew>
</renew>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,72 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
# Needed for the documentation tests
filegroup(
name = "flows_files",
srcs = glob([
"*.java",
"**/*.java",
]),
)
java_library(
name = "flows",
srcs = glob([
"*.java",
"**/*.java",
]),
resources = glob(["**/testdata/*.xml"]),
deps = [
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/io",
"//java/com/google/common/net",
"//java/com/google/domain/registry/config",
"//java/com/google/domain/registry/dns",
"//java/com/google/domain/registry/flows",
"//java/com/google/domain/registry/mapreduce",
"//java/com/google/domain/registry/model",
"//java/com/google/domain/registry/request",
"//java/com/google/domain/registry/security",
"//java/com/google/domain/registry/security:servlets",
"//java/com/google/domain/registry/tmch",
"//java/com/google/domain/registry/util",
"//java/com/google/domain/registry/xml",
"//javatests/com/google/domain/registry/model",
"//javatests/com/google/domain/registry/testing",
"//javatests/com/google/domain/registry/testing/mapreduce",
"//javatests/com/google/domain/registry/xml",
"//third_party/java/appengine:appengine-api-testonly",
"//third_party/java/appengine:appengine-testing",
"//third_party/java/joda_money",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//third_party/java/truth",
],
)
# If the flows tests should grow again to the point that they last longer than
# sixty seconds, then shard_count should be tuned. You can binary search for a
# good value that balances time reduction with environmental impact. However,
# any unit test that contains fewer @Test methods than the shard count will
# If you grep for testNothing you can find the existing dummy methods.
GenTestRules(
name = "GeneratedTestRules",
default_test_size = "medium",
jvm_flags = ["-XX:MaxPermSize=256m"],
shard_count = 4,
test_files = glob([
"*Test.java",
"*/*Test.java",
]),
deps = [":flows"],
)

View file

@ -0,0 +1,65 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.UserInfo;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
/** Tests for {@link EppConsoleServlet} running in admin mode. */
@RunWith(MockitoJUnitRunner.class)
public class EppConsoleAsAdminServletTest extends EppServletXmlLoginTestCase<EppConsoleServlet> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.withUserService(UserInfo.createAdmin(GAE_USER_EMAIL, GAE_USER_ID))
.build();
private static final String GAE_USER_ID = "12345";
private static final String GAE_USER_EMAIL = "someone@example.com";
// Note that the setup done in EppConsoleServletTest, of allowing
// the test user to login as the Registrar, is not done here.
@Before
public void initTest() throws Exception {
persistResource(
Registrar.loadByClientId("NewRegistrar").asBuilder().setPassword("PwAdminDNKnow").build());
}
@Test
public void testNonAuthedLogin() throws Exception {
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
}
@Test
public void testMultiLogin() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
}
}

View file

@ -0,0 +1,71 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registrar.RegistrarContact;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.UserInfo;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
/** Tests for {@link EppConsoleServlet}. */
@RunWith(MockitoJUnitRunner.class)
public class EppConsoleServletTest extends EppServletXmlLoginTestCase<EppConsoleServlet> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.withUserService(UserInfo.create(GAE_USER_EMAIL, GAE_USER_ID))
.build();
private static final String GAE_USER_ID = "12345";
private static final String GAE_USER_EMAIL = "person@example.com";
@Before
public void initTest() throws Exception {
Registrar registrar = Registrar.loadByClientId("NewRegistrar");
RegistrarContact contact = new RegistrarContact.Builder()
.setParent(registrar)
.setEmailAddress(GAE_USER_EMAIL)
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
.setGaeUserId(GAE_USER_ID)
.build();
persistResource(contact);
}
@Test
public void testNonAuthedLogin() throws Exception {
assertCommandAndResponse("login2_valid.xml", "login_response_unauthorized_role.xml");
}
@Test
public void testMultiLogin() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login2_valid.xml", "login_response_unauthorized_role.xml");
}
}

View file

@ -0,0 +1,50 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.domain.registry.flows.EppController.getErrorResponse;
import static com.google.domain.registry.flows.EppXmlTransformer.marshal;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.eppoutput.Result;
import com.google.domain.registry.model.eppoutput.Result.Code;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.xml.ValidationMode;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link EppController}. */
@RunWith(JUnit4.class)
public class EppControllerTest {
@Rule
public AppEngineRule appEngineRule = new AppEngineRule.Builder().build();
@Test
public void testMarshallingUnknownError() throws Exception {
marshal(
getErrorResponse(Result.create(Code.CommandFailed), Trid.create(null)),
ValidationMode.STRICT);
}
// Extra methods so the test runner doesn't produce empty shards.
@Test public void testNothing1() {}
@Test public void testNothing2() {}
@Test public void testNothing3() {}
}

View file

@ -0,0 +1,301 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
import static com.google.domain.registry.security.XsrfTokenManager.generateToken;
import static com.google.domain.registry.testing.DatastoreHelper.createTlds;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TestDataHelper.loadFileWithSubstitutions;
import static com.google.domain.registry.xml.XmlTestUtils.assertXmlEqualsWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.joda.time.DateTimeZone.UTC;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
import com.google.domain.registry.security.XsrfProtectedServlet;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.FakeServletInputStream;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.util.BasicHttpSession;
import com.google.domain.registry.util.TypeUtils.TypeInstantiator;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.ByteArrayOutputStream;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Test setup for all EppServletTest subclasses.
*
* @param <S> The EppXXXServlet class to test.
*/
public abstract class EppServletTestCase<S extends HttpServlet> {
@Rule
public final InjectRule inject = new InjectRule();
@Mock
HttpServletRequest req;
@Mock
HttpServletResponse rsp;
HttpSession session;
FakeClock clock = new FakeClock();
private String currentTld = null;
private Optional<Boolean> isSuperuser = Optional.<Boolean> absent();
private Optional<String> clientIdentifier = Optional.<String> absent();
void setSuperuser(boolean isSuperuser) {
this.isSuperuser = Optional.of(isSuperuser);
}
void setClientIdentifier(String clientIdentifier) {
this.clientIdentifier = Optional.of(clientIdentifier);
}
static final DateTime START_OF_GA = DateTime.parse("2014-03-01T00:00:00Z");
@Before
public final void init() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock); // For transactional flows.
inject.setStaticField(FlowRunner.class, "clock", clock); // For non-transactional flows.
// Create RegistryData for all TLDs used in these tests.
// We want to create all of these even for tests that don't use them to make sure that
// tld-selection works correctly.
createTlds("net", "xn--q9jyb4c", "example");
ofy().saveWithoutBackup().entity(new ClaimsListSingleton()).now();
session = new BasicHttpSession();
persistResource(
Registrar.loadByClientId("NewRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.of("net", "example", "xn--q9jyb4c"))
.build());
persistResource(
Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setAllowedTlds(ImmutableSet.of("net", "example", "xn--q9jyb4c"))
.build());
}
void assertCommandAndResponse(
String inputFilename,
Map<String, String> inputSubstitutions,
String outputFilename,
Map<String, String> outputSubstitutions) throws Exception {
assertCommandAndResponse(
inputFilename,
inputSubstitutions,
outputFilename,
outputSubstitutions,
DateTime.now(UTC));
}
String assertCommandAndResponse(String inputFilename, String outputFilename) throws Exception {
return assertCommandAndResponse(inputFilename, outputFilename, DateTime.now(UTC));
}
String assertCommandAndResponse(
String inputFilename,
Map<String, String> inputSubstitutions,
String outputFilename,
Map<String, String> outputSubstitutions,
String nowString) throws Exception {
return assertCommandAndResponse(
inputFilename,
inputSubstitutions,
outputFilename,
outputSubstitutions,
DateTime.parse(nowString));
}
String assertCommandAndResponse(String inputFilename, String outputFilename, String nowString)
throws Exception {
return assertCommandAndResponse(inputFilename, outputFilename, DateTime.parse(nowString));
}
String assertCommandAndResponse(String inputFilename, String outputFilename, DateTime now)
throws Exception {
return assertCommandAndResponse(inputFilename, null, outputFilename, null, now);
}
String assertCommandAndResponse(
String inputFilename,
Map<String, String> inputSubstitutions,
String outputFilename,
Map<String, String> outputSubstitutions,
DateTime now) throws Exception {
String outputFile =
loadFileWithSubstitutions(EppServletTestCase.class, outputFilename, outputSubstitutions);
String actualOutput = expectXmlCommand(loadFileWithSubstitutions(
EppServletTestCase.class, inputFilename, inputSubstitutions), now);
assertXmlEqualsWithMessage(
outputFile,
actualOutput,
"Running " + inputFilename + " => " + outputFilename,
"epp.response.resData.infData.roid",
"epp.response.trID.svTRID");
ofy().clearSessionCache(); // Clear the cache like OfyFilter would.
return actualOutput;
}
HttpSession getOrRenewSession() {
// Try an idempotent op on the session to see if it's valid.
try {
session.getAttribute(null);
return session;
} catch (IllegalStateException e) {
// Session is invalid.
session = new BasicHttpSession();
return session;
}
}
@SuppressWarnings("resource")
String expectXmlCommand(String inputFile, DateTime now) throws Exception {
clock.setTo(now); // Makes Ofy use 'now' as its time
reset(req, rsp);
HttpServlet servlet = new TypeInstantiator<S>(getClass()){}.instantiate();
if (servlet instanceof XsrfProtectedServlet) {
when(req.getHeader(X_CSRF_TOKEN))
.thenReturn(generateToken(((XsrfProtectedServlet) servlet).getScope()));
}
when(req.getInputStream()).thenReturn(new FakeServletInputStream(inputFile.getBytes(UTF_8)));
when(req.getParameter("xml")).thenReturn(inputFile);
if (isSuperuser.isPresent()) {
when(req.getParameter("superuser")).thenReturn(isSuperuser.get().toString());
}
if (clientIdentifier.isPresent()) {
when(req.getParameter("clientIdentifier")).thenReturn(clientIdentifier.get());
}
when(req.getParameter("tld")).thenReturn(currentTld);
when(req.getServletPath()).thenReturn("");
when(req.getMethod()).thenReturn("POST");
when(req.getHeader("X-Requested-With")).thenReturn("XMLHttpRequest");
when(req.getSession(true)).thenAnswer(new Answer<HttpSession>() {
@Override
public HttpSession answer(InvocationOnMock invocation) {
return getOrRenewSession();
}});
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
when(rsp.getOutputStream()).thenReturn(new ServletOutputStream() {
@Override
public void write(int b) {
byteArrayOutputStream.write(b);
}});
extendedSessionConfig(inputFile);
servlet.init(mock(ServletConfig.class));
servlet.service(req, rsp);
verify(rsp).setStatus(HttpServletResponse.SC_OK);
String result = new String(byteArrayOutputStream.toByteArray(), UTF_8);
// Run the resulting xml through the unmarshaller to verify that it was valid.
EppXmlTransformer.validateOutput(result);
return result;
}
/** Create the two administrative contacts and two hosts that are used by a lot of our tests. */
protected void createContactsAndHosts() throws Exception {
DateTime startTime = DateTime.parse("2000-06-01T00:00:00Z");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
startTime);
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
startTime.plusMinutes(1));
assertCommandAndResponse(
"host_create.xml",
"host_create_response.xml",
startTime.plusMinutes(2));
assertCommandAndResponse(
"host_create2.xml",
"host_create2_response.xml",
startTime.plusMinutes(3));
}
/**
* Creates the domain fakesite.example with two nameservers on it.
*/
protected void createFakesite() throws Exception {
createContactsAndHosts();
assertCommandAndResponse(
"domain_create_fakesite.xml",
"domain_create_response_fakesite.xml",
"2000-06-01T00:04:00Z");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_ok.xml",
"2000-06-06T00:00:00Z");
}
// Adds ns3.fakesite.example as a host, then adds it to fakesite.
protected void createSubordinateHost() throws Exception {
// Add the fakesite nameserver (requires that domain is already created).
assertCommandAndResponse(
"host_create_fakesite.xml",
"host_create_response_fakesite.xml",
"2000-06-06T00:01:00Z");
// Add new nameserver to domain.
assertCommandAndResponse(
"domain_update_add_nameserver_fakesite.xml",
"domain_update_add_nameserver_response_fakesite.xml",
"2000-06-08T00:00:00Z");
// Verify new nameserver was added.
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_3_nameservers.xml",
"2000-06-08T00:01:00Z");
// Verify that nameserver's data was set correctly.
assertCommandAndResponse(
"host_info_fakesite.xml",
"host_info_response_fakesite.xml",
"2000-06-08T00:02:00Z");
}
/** For subclasses to further setup the session. */
protected void extendedSessionConfig(
@SuppressWarnings("unused") String inputFile) throws Exception {}
}

View file

@ -0,0 +1,355 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static com.google.domain.registry.util.ResourceUtils.readResourceUtf8;
import static com.google.domain.registry.xml.XmlTestUtils.assertXmlEquals;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.util.DateTimeUtils;
import org.joda.time.DateTime;
import org.junit.Test;
import javax.servlet.http.HttpServlet;
/**
* Test setup for EppServletTest subclasses which use XML-based authentication.
*
* @param <S> The EppXXXServlet class to test.
*/
public abstract class EppServletXmlLoginTestCase<S extends HttpServlet> extends
EppServletTestCase<S> {
@Test
public void testHello() throws Exception {
assertXmlEquals(
readResourceUtf8(getClass(), "testdata/greeting_crr.xml"),
expectXmlCommand(readResourceUtf8(getClass(), "testdata/hello.xml"), DateTime.now(UTC)),
"epp.greeting.svDate");
}
@Test
public void testLoginLogout() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testPdtLogin() throws Exception {
assertCommandAndResponse("pdt_login.xml", "login_response.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testSyntaxError() throws Exception {
assertCommandAndResponse("syntax_error.xml", "syntax_error_response.xml");
}
@Test
public void testContactLifecycle() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse(
"contact_info.xml",
"contact_info_from_create_response.xml",
"2000-06-01T00:01:00Z");
assertCommandAndResponse("contact_delete_sh8013.xml", "contact_delete_response_sh8013.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainDeleteRestore() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create contacts sh8013 and jd1234.
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
"2000-06-01T00:01:00Z");
// Create domain example.tld.
assertCommandAndResponse(
"domain_create_no_hosts_or_dsdata.xml",
"domain_create_response.xml",
"2000-06-01T00:02:00Z");
// Delete domain example.com after its add grace period has expired.
assertCommandAndResponse(
"domain_delete.xml",
"generic_success_action_pending_response.xml",
"2000-07-01T00:02:00Z");
// Restore the domain.
assertCommandAndResponse(
"domain_update_restore_request.xml",
"domain_update_restore_request_response.xml",
"2000-07-01T00:03:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainDeletion_withinAddGracePeriod() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create contacts sh8013 and jd1234.
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
"2000-06-01T00:01:00Z");
// Create domain example.tld.
assertCommandAndResponse(
"domain_create_no_hosts_or_dsdata.xml",
"domain_create_response.xml",
"2000-06-01T00:02:00Z");
// Delete domain example.tld after its add grace period has expired.
assertCommandAndResponse(
"domain_delete.xml",
"generic_success_action_pending_response.xml",
"2000-07-01T00:02:00Z");
// Poke the domain a little at various times to see its status
assertCommandAndResponse(
"domain_info.xml",
"domain_info_response_pendingdelete.xml",
"2000-08-01T00:02:00Z"); // 1 day out.
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainDeletionWithSubordinateHost_fails() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
createSubordinateHost();
assertCommandAndResponse(
"domain_delete_fakesite.xml",
"domain_delete_response_prohibited.xml",
"2002-05-30T01:01:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDeletionOfDomain_afterRenameOfSubordinateHost_succeeds() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
createSubordinateHost();
// Update the ns3 host to no longer be on fakesite.example domain.
assertCommandAndResponse(
"host_update_fakesite.xml",
"generic_success_response.xml",
"2002-05-30T01:01:00Z");
// Delete the fakesite.example domain (which should succeed since it no longer has subords).
assertCommandAndResponse(
"domain_delete_fakesite.xml",
"generic_success_action_pending_response.xml",
"2002-05-30T01:02:00Z");
// Check info on the renamed host and verify that it's still around and wasn't deleted.
assertCommandAndResponse(
"host_info_ns9000_example.xml",
"host_info_response_ns9000_example.xml",
"2002-06-30T01:03:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDeletionOfDomain_afterUpdateThatCreatesSubordinateHost_fails() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
// Update the ns1 host to be on the fakesite.example domain.
assertCommandAndResponse(
"host_update_ns1_to_fakesite.xml",
"generic_success_response.xml",
"2002-05-30T01:01:00Z");
// Attempt to delete the fakesite.example domain (which should fail since it now has a
// subordinate host).
assertCommandAndResponse(
"domain_delete_fakesite.xml",
"domain_delete_response_prohibited.xml",
"2002-05-30T01:02:00Z");
// Check info on the renamed host and verify that it's still around and wasn't deleted.
assertCommandAndResponse(
"host_info_fakesite.xml",
"host_info_response_fakesite_post_update.xml",
"2002-06-30T01:03:00Z");
// Verify that fakesite.example domain is still around and wasn't deleted.
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_ok_post_host_update.xml",
"2002-05-30T01:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testRenamingHostToExistingHost_fails() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create the two hosts.
assertCommandAndResponse(
"host_create.xml",
"host_create_response.xml",
"2000-06-01T00:02:00Z");
assertCommandAndResponse(
"host_create2.xml",
"host_create2_response.xml",
"2000-06-01T00:03:00Z");
// Verify that host1 and host2 were created as we expect them.
assertCommandAndResponse(
"host_info_ns1.xml",
"host_info_response_ns1.xml",
"2000-06-01T00:04:00Z");
assertCommandAndResponse(
"host_info_ns2.xml",
"host_info_response_ns2.xml",
"2000-06-01T00:05:00Z");
// Attempt overwriting of host1 on top of host2 (and verify that it fails).
assertCommandAndResponse(
"host_update_ns1_to_ns2.xml",
"host_update_failed_response.xml",
"2000-06-01T00:06:00Z");
// Verify that host1 and host2 still exist in their unmodified states.
assertCommandAndResponse(
"host_info_ns1.xml",
"host_info_response_ns1.xml",
"2000-06-01T00:07:00Z");
assertCommandAndResponse(
"host_info_ns2.xml",
"host_info_response_ns2.xml",
"2000-06-01T00:08:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testApplicationDuringSunrise_doesntCreateDomainWithoutAllocation() throws Exception {
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
DateTimeUtils.START_OF_TIME, TldState.SUNRISE,
START_OF_GA, TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createContactsAndHosts();
// Note that the trademark is valid from 2013-08-09 to 2017-07-23, hence the domain creation
// in 2014.
assertCommandAndResponse(
"domain_create_sunrise_encoded_mark.xml",
"domain_create_sunrise_encoded_signed_mark_response.xml",
"2014-01-01T00:00:00Z");
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_doesnt_exist.xml",
"2014-01-01T00:01:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainCreation_failsBeforeSunrise() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
DateTime sunriseDate = DateTime.parse("2000-05-30T00:00:00Z");
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
sunriseDate, TldState.SUNRISE,
sunriseDate.plusMonths(2), TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
setClientIdentifier("NewRegistrar");
createContactsAndHosts();
assertCommandAndResponse(
"domain_create_sunrise_encoded_mark.xml",
"domain_create_testvalidate_invalid_phase.xml",
sunriseDate.minusDays(1));
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_doesnt_exist.xml",
sunriseDate.plusDays(1));
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainCheckFee_succeeds() throws Exception {
assertCommandAndResponse("login_valid_fee_extension.xml", "login_response.xml");
DateTime gaDate = DateTime.parse("2000-05-30T00:00:00Z");
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
gaDate, TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
assertCommandAndResponse(
"domain_check_fee_premium.xml",
"domain_check_fee_premium_response.xml",
gaDate.plusDays(1));
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testRemoteXmlExternalEntity() throws Exception {
// Check go/XXE
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_remote_xxe.xml",
"contact_create_remote_response_xxe.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testLocalXmlExternalEntity() throws Exception {
// Check go/XXE
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_local_xxe.xml",
"contact_create_local_response_xxe.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testBillionLaughsAttack() throws Exception {
// Check go/XXE
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_billion_laughs.xml",
"contact_create_response_billion_laughs.xml");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
}

View file

@ -0,0 +1,510 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.CertificateSamples;
import com.google.domain.registry.testing.FakeServletInputStream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** Test setup for EppServletTest subclasses. */
@RunWith(MockitoJUnitRunner.class)
public class EppTlsServletTest extends EppServletXmlLoginTestCase<EppTlsServlet> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
String ipAddressAndPort = "192.168.1.100:54321";
String clientCert = CertificateSamples.SAMPLE_CERT_HASH;
String clientCert2 = CertificateSamples.SAMPLE_CERT2_HASH;
String requestedServername = "test.example";
private String gfeRequestClientCertificateHashField;
@Before
public void initTest() throws Exception {
persistResource(Registrar.loadByClientId("NewRegistrar")
.asBuilder()
.setClientCertificateHash(clientCert)
.build());
persistResource(Registrar.loadByClientId("TheRegistrar")
.asBuilder()
.setClientCertificateHash(clientCert2)
.build());
}
@Test
public void testSetTldViaSni() throws Exception {
requestedServername = "epp.nic.xn--q9jyb4c";
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse(
"domain_create_minna.xml",
"domain_create_response_minna.xml",
"2000-06-01T01:02:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
/** This test requires multiple registrars, which EppConsoleServlet doesn't allow. */
@Test
public void testContactTransferPollMessage() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
"2000-06-01T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Initiate a transfer of the newly created contact.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_transfer_request.xml",
"contact_transfer_request_response_alternate.xml",
"2000-06-08T22:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in with the losing registrar, read the poll message, and then ack it.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"poll.xml",
"poll_response_contact_transfer.xml",
"2000-06-08T22:01:00Z");
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "2-4-ROID-6-7"),
"poll_ack_response_empty.xml",
null,
"2000-06-08T22:02:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
/** This test requires multiple registrars, which EppConsoleServlet doesn't allow. */
@Test
public void testDomainTransferPollMessage_serverApproved() throws Exception {
// As the losing registrar, create the domain.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
assertCommandAndResponse("logout.xml", "logout_response.xml");
// As the winning registrar, request a transfer. Capture the server trid; we'll need it later.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
String response = assertCommandAndResponse(
"domain_transfer_request_1_year.xml",
"domain_transfer_response_1_year.xml",
"2001-01-01T00:00:00Z");
Matcher matcher = Pattern.compile("<svTRID>(.*)</svTRID>").matcher(response);
matcher.find();
String transferRequestTrid = matcher.group(1);
assertCommandAndResponse("logout.xml", "logout_response.xml");
// As the losing registrar, read the request poll message, and then ack it.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"poll.xml",
"poll_response_domain_transfer_request.xml",
"2001-01-01T00:01:00Z");
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-C-EXAMPLE-18-22"),
"poll_ack_response_empty.xml",
null,
"2001-01-01T00:01:00Z");
// Five days in the future, expect a server approval poll message to the loser, and ack it.
assertCommandAndResponse(
"poll.xml",
"poll_response_domain_transfer_server_approve_loser.xml",
"2001-01-06T00:01:00Z");
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-C-EXAMPLE-18-24"),
"poll_ack_response_empty.xml",
null,
"2001-01-06T00:01:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Also expect a server approval poll message to the winner, with the transfer request trid.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"poll.xml",
null,
"poll_response_domain_transfer_server_approve_winner.xml",
ImmutableMap.of("SERVER_TRID", transferRequestTrid),
"2001-01-06T00:02:00Z");
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-C-EXAMPLE-18-23"),
"poll_ack_response_empty.xml",
null,
"2001-01-06T00:02:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Override
protected void extendedSessionConfig(String inputFile) throws Exception {
when(req.getHeader(EppTlsServlet.REQUESTED_SERVERNAME_VIA_SNI_FIELD))
.thenReturn(requestedServername);
when(req.getHeader(EppTlsServlet.FORWARDED_FOR_FIELD))
.thenReturn(ipAddressAndPort);
if (gfeRequestClientCertificateHashField != null) {
when(req.getHeader(EppTlsServlet.SSL_CLIENT_CERTIFICATE_HASH_FIELD))
.thenReturn(gfeRequestClientCertificateHashField);
} else {
when(req.getHeader(EppTlsServlet.SSL_CLIENT_CERTIFICATE_HASH_FIELD))
.thenReturn(inputFile.contains("TheRegistrar") ? clientCert2 : clientCert);
}
when(req.getInputStream()).thenReturn(new FakeServletInputStream(inputFile.getBytes(UTF_8)));
}
@Test
public void testIgnoredTransferDuringAutoRenewPeriod_succeeds() throws Exception {
// Register the domain as the first registrar.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_2_years.xml",
"domain_transfer_response_2_years.xml",
"2002-05-30T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as the first registrar and verify things.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_pending_transfer.xml",
"2002-05-30T01:00:00Z");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_pending_transfer_autorenew.xml",
"2002-06-02T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as the second registrar and verify transfer details.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
// Verify that domain is in the transfer period now with expiration date two years out.
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_transfer_period.xml",
"2002-06-06T00:00:00Z");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_transfer_complete.xml",
"2002-06-12T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testBadCertificate_failsBadCertificate2200() throws Exception {
gfeRequestClientCertificateHashField = "laffo";
assertCommandAndResponse("login_valid.xml", "login_response_bad_certificate.xml");
}
@Test
public void testGfeDidntProvideClientCertificate_failsMissingCertificate2200() throws Exception {
gfeRequestClientCertificateHashField = "";
assertCommandAndResponse("login_valid.xml", "login_response_missing_certificate.xml");
}
@Test
public void testGoodPrimaryCertificate() throws Exception {
gfeRequestClientCertificateHashField = CertificateSamples.SAMPLE_CERT_HASH;
persistResource(Registrar.loadByClientId("NewRegistrar").asBuilder()
.setClientCertificate(CertificateSamples.SAMPLE_CERT, clock.nowUtc())
.setFailoverClientCertificate(CertificateSamples.SAMPLE_CERT2, clock.nowUtc())
.build());
assertCommandAndResponse("login_valid.xml", "login_response.xml");
}
@Test
public void testGoodFailoverCertificate() throws Exception {
gfeRequestClientCertificateHashField = CertificateSamples.SAMPLE_CERT2_HASH;
persistResource(Registrar.loadByClientId("NewRegistrar").asBuilder()
.setClientCertificate(CertificateSamples.SAMPLE_CERT, clock.nowUtc())
.setFailoverClientCertificate(CertificateSamples.SAMPLE_CERT2, clock.nowUtc())
.build());
assertCommandAndResponse("login_valid.xml", "login_response.xml");
}
@Test
public void testMissingPrimaryCertificateButHasFailover_usesFailover() throws Exception {
gfeRequestClientCertificateHashField = CertificateSamples.SAMPLE_CERT2_HASH;
persistResource(Registrar.loadByClientId("NewRegistrar").asBuilder()
.setClientCertificate(null, clock.nowUtc())
.setFailoverClientCertificate(CertificateSamples.SAMPLE_CERT2, clock.nowUtc())
.build());
assertCommandAndResponse("login_valid.xml", "login_response.xml");
}
@Test
public void testRegistrarHasNoCertificatesOnFile_disablesCertChecking() throws Exception {
gfeRequestClientCertificateHashField = "laffo";
persistResource(Registrar.loadByClientId("NewRegistrar").asBuilder()
.setClientCertificate(null, clock.nowUtc())
.setFailoverClientCertificate(null, clock.nowUtc())
.build());
assertCommandAndResponse("login_valid.xml", "login_response.xml");
}
@Test
public void testNameserversTransferWithDomain_successfully() throws Exception {
// Log in as the first registrar and set up domains with hosts.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
createSubordinateHost();
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_2_years.xml",
"domain_transfer_response_2_years.xml",
"2002-05-30T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as the first registrar and verify domain is pending transfer.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_3_nameservers_pending_transfer.xml",
"2002-05-30T01:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as second registrar and verify transfer was successful.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
// Expect transfer complete with all three nameservers on it.
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_3_nameservers_transfer_successful.xml",
"2002-06-09T00:00:00Z");
// Verify that host's client ID was set to the new registrar and has the transfer date set.
assertCommandAndResponse(
"host_info_fakesite.xml",
null,
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("trDate", "2002-06-04T00:00:00Z"),
"2002-06-09T00:01:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainDeletionCancelsPendingTransfer() throws Exception {
// Register the domain as the first registrar.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_2_years.xml",
"domain_transfer_response_2_years.xml",
"2002-05-30T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Log back in as the first registrar and delete then restore the domain while the transfer
// is still pending.
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_pending_transfer.xml",
"2002-05-30T01:00:00Z");
assertCommandAndResponse(
"domain_delete_fakesite.xml",
"generic_success_action_pending_response.xml",
"2002-05-30T01:01:00Z");
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_pending_delete.xml",
"2002-05-30T01:02:00Z");
assertCommandAndResponse(
"domain_update_restore_fakesite.xml",
"domain_update_restore_request_response.xml",
"2002-05-30T01:03:00Z");
// Expect domain is ok now, not pending delete or transfer, and has been extended by a year from
// the date of the restore. (Not from the original expiration date.)
assertCommandAndResponse(
"domain_info_fakesite.xml",
"domain_info_response_fakesite_restored_ok.xml",
"2002-05-30T01:04:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testDomainTransfer_subordinateHost_showsChangeInTransferQuery() throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
createSubordinateHost();
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_no_transfer_history.xml",
"2000-09-02T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_1_year.xml",
"domain_transfer_response_1_year.xml",
"2001-01-01T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Verify that reID is set correctly.
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_fakesite.xml",
"2001-01-02T00:00:00Z");
// Verify that status went from 'pending' to 'serverApproved'.
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_completed_fakesite.xml",
"2001-01-08T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
/**
* Tests that when a superordinate domain of a host is transferred, and then the host is updated
* to be subordinate to a different domain, that the host retains the transfer time of the first
* superordinate domain, not whatever the transfer time from the second domain is.
*/
@Test
public void testSuccess_lastTransferTime_superordinateDomainTransferFollowedByHostUpdate()
throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create fakesite.example with subordinate host ns3.fakesite.example
createFakesite();
createSubordinateHost();
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_no_transfer_history.xml",
"2000-09-02T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_1_year.xml",
"domain_transfer_response_1_year.xml",
"2001-01-01T00:00:00Z");
// Verify that the lastTransferTime now reflects the superordinate domain's transfer.
assertCommandAndResponse(
"host_info.xml",
ImmutableMap.of("hostname", "ns3.fakesite.example"),
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("trDate", "2001-01-06T00:00:00.000Z"),
"2001-01-07T00:00:00Z");
assertCommandAndResponse(
"domain_create_secondsite.xml",
"domain_create_response_secondsite.xml",
"2001-01-08T00:00:00Z");
// Update the host to be subordinate to a different domain by renaming it to
// ns3.secondsite.example
assertCommandAndResponse(
"host_update_rename_only.xml",
ImmutableMap.of("oldName", "ns3.fakesite.example", "newName", "ns3.secondsite.example"),
"generic_success_response.xml",
null,
"2002-05-30T01:01:00Z");
// The last transfer time on the host should still be what it was from the transfer.
assertCommandAndResponse(
"host_info.xml",
ImmutableMap.of("hostname", "ns3.secondsite.example"),
"host_info_response_fakesite_post_transfer_and_update.xml",
ImmutableMap.of(
"hostname", "ns3.secondsite.example",
"trDate", "2001-01-06T00:00:00.000Z"),
"2003-01-07T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
/**
* Tests that when a superordinate domain of a host is transferred, and then the host is updated
* to be external, that the host retains the transfer time of the first superordinate domain.
*/
@Test
public void testSuccess_lastTransferTime_superordinateDomainTransferThenHostUpdateToExternal()
throws Exception {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
// Create fakesite.example with subordinate host ns3.fakesite.example
createFakesite();
createSubordinateHost();
assertCommandAndResponse(
"domain_transfer_query_fakesite.xml",
"domain_transfer_query_response_no_transfer_history.xml",
"2000-09-02T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
// Request a transfer of the domain to the second registrar.
assertCommandAndResponse("login2_valid.xml", "login_response.xml");
assertCommandAndResponse(
"domain_transfer_request_1_year.xml",
"domain_transfer_response_1_year.xml",
"2001-01-01T00:00:00Z");
// Verify that the lastTransferTime now reflects the superordinate domain's transfer.
assertCommandAndResponse(
"host_info_fakesite.xml",
null,
"host_info_response_fakesite_post_transfer.xml",
ImmutableMap.of("trDate", "2001-01-06T00:00:00.000Z"),
"2001-01-07T00:00:00Z");
// Update the host to be external by renaming it to ns3.notarealsite.external
assertCommandAndResponse(
"host_update_rename_and_remove_addresses.xml",
ImmutableMap.of(
"oldName", "ns3.fakesite.example",
"newName", "ns3.notarealsite.external"),
"generic_success_response.xml",
null,
"2002-05-30T01:01:00Z");
// The last transfer time on the host should still be what it was from the transfer.
assertCommandAndResponse(
"host_info.xml",
ImmutableMap.of("hostname", "ns3.notarealsite.external"),
"host_info_response_fakesite_post_transfer_and_update_no_addresses.xml",
ImmutableMap.of(
"hostname", "ns3.notarealsite.external",
"trDate", "2001-01-06T00:00:00.000Z"),
"2001-01-07T00:00:00Z");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
}

View file

@ -0,0 +1,136 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registrar.RegistrarContact;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.UserInfo;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
/** Tests for {@link EppToolServlet}. */
@RunWith(MockitoJUnitRunner.class)
public class EppToolServletTest extends EppServletTestCase<EppToolServlet> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.withUserService(UserInfo.createAdmin(GAE_USER_EMAIL, GAE_USER_ID))
.build();
private static final String GAE_USER_ID = "12345";
private static final String GAE_USER_EMAIL = "person@example.com";
@Before
public void initTest() throws Exception {
Registrar registrar = Registrar.loadByClientId("NewRegistrar");
persistResource(
new RegistrarContact.Builder()
.setParent(registrar)
.setEmailAddress(GAE_USER_EMAIL)
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
.setGaeUserId(GAE_USER_ID)
.build());
}
@Test
public void testDomainAllocation_succeedsOnlyAsSuperuser() throws Exception {
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.SUNRISE,
START_OF_GA, TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
setClientIdentifier("NewRegistrar");
setSuperuser(false);
createContactsAndHosts();
// Note that the trademark is valid from 20130809 to 20170723, hence the domain creation
// in 2014.
assertCommandAndResponse(
"domain_create_sunrise_encoded_mark.xml",
"domain_create_sunrise_encoded_signed_mark_response.xml",
"2014-01-01T00:00:00Z");
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_doesnt_exist.xml",
"2014-01-01T00:01:00Z");
assertCommandAndResponse(
"domain_allocate_testvalidate.xml",
"domain_allocate_response_testvalidate_only_superuser.xml",
START_OF_GA.plusDays(1));
setSuperuser(true);
assertCommandAndResponse(
"domain_allocate_testvalidate.xml",
"domain_allocate_response_testvalidate.xml",
START_OF_GA.plusDays(1).plusMinutes(1));
setSuperuser(false);
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_ok.xml",
START_OF_GA.plusDays(1).plusMinutes(2));
}
@Test
public void testDomainCreation_failsBeforeSunrise() throws Exception {
DateTime sunriseDate = DateTime.parse("2000-05-30T00:00:00Z");
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
sunriseDate, TldState.SUNRISE,
sunriseDate.plusMonths(2), TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
setClientIdentifier("NewRegistrar");
createContactsAndHosts();
assertCommandAndResponse(
"domain_create_sunrise_encoded_mark.xml",
"domain_create_testvalidate_invalid_phase.xml",
sunriseDate.minusDays(1));
assertCommandAndResponse(
"domain_info_testvalidate.xml",
"domain_info_response_testvalidate_doesnt_exist.xml",
sunriseDate.plusDays(1));
}
@Test
public void testDomainCheckFee_succeeds() throws Exception {
DateTime gaDate = DateTime.parse("2000-05-30T00:00:00Z");
ImmutableSortedMap<DateTime, TldState> transitions = ImmutableSortedMap.of(
START_OF_TIME, TldState.PREDELEGATION,
gaDate, TldState.GENERAL_AVAILABILITY);
createTld("example", transitions);
setClientIdentifier("NewRegistrar");
assertCommandAndResponse(
"domain_check_fee_premium.xml",
"domain_check_fee_premium_response.xml",
gaDate.plusDays(1));
}
// Extra method so the test runner doesn't produce empty shards.
@Test public void testNothing1() {}
}

View file

@ -0,0 +1,311 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.Sets.difference;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.flows.EppXmlTransformer.marshal;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.testing.DatastoreHelper.BILLING_EVENT_ID_STRIPPER;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.util.ResourceUtils.readResourceUtf8;
import static com.google.domain.registry.xml.XmlTestUtils.assertXmlEquals;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.SessionMetadata.SessionSource;
import com.google.domain.registry.model.billing.BillingEvent;
import com.google.domain.registry.model.domain.GracePeriod;
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
import com.google.domain.registry.model.eppcommon.ProtocolDefinition;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.eppinput.EppInput;
import com.google.domain.registry.model.eppoutput.EppOutput;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.EppLoader;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.TestSessionMetadata;
import com.google.domain.registry.util.TypeUtils.TypeInstantiator;
import com.google.domain.registry.xml.ValidationMode;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.List;
import java.util.Map;
/**
* Base class for resource flow unit tests.
*
* @param <F> the flow type
*/
@RunWith(MockitoJUnitRunner.class)
public abstract class FlowTestCase<F extends Flow> {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final InjectRule inject = new InjectRule();
private FlowRunner flowRunner;
protected EppLoader eppLoader;
protected Class<? extends Flow> flowClass;
protected SessionMetadata sessionMetadata;
protected FakeClock clock = new FakeClock(DateTime.now(UTC));
@Before
public void init() throws Exception {
flowRunner = null;
sessionMetadata = new TestSessionMetadata();
sessionMetadata.setClientId("TheRegistrar");
sessionMetadata.setServiceExtensionUris(ProtocolDefinition.getVisibleServiceExtensionUris());
sessionMetadata.setSessionSource(SessionSource.NONE);
ofy().saveWithoutBackup().entity(new ClaimsListSingleton()).now();
inject.setStaticField(Ofy.class, "clock", clock); // For transactional flows.
inject.setStaticField(FlowRunner.class, "clock", clock); // For non-transactional flows.
}
protected void removeServiceExtensionUri(String uri) {
sessionMetadata.setServiceExtensionUris(
difference(sessionMetadata.getServiceExtensionUris(), ImmutableSet.of(uri)));
}
protected void setEppInput(String inputFilename) {
eppLoader = new EppLoader(this, inputFilename, ImmutableMap.<String, String>of());
}
protected void setEppInput(String inputFilename, Map<String, String> substitutions) {
eppLoader = new EppLoader(this, inputFilename, substitutions);
}
protected String readFile(String filename) {
return readResourceUtf8(getClass(), "testdata/" + filename);
}
/** Lazily load the flow, since it may fail to initialize if the environment isn't set up yet. */
public FlowRunner getFlowRunner() throws Exception {
if (flowRunner == null) {
flowRunner = createFlowRunner();
}
return flowRunner;
}
/** Load a flow from an epp object. */
private FlowRunner createFlowRunner() throws Exception {
EppInput eppInput = eppLoader.getEpp();
flowClass = firstNonNull(flowClass, FlowRegistry.getFlowClass(eppInput));
Class<?> expectedFlowClass = new TypeInstantiator<F>(getClass()){}.getExactType();
assertThat(flowClass).isEqualTo(expectedFlowClass);
return new FlowRunner(
flowClass,
eppInput,
getTrid(),
sessionMetadata,
"<xml></xml>".getBytes(),
null);
}
protected Trid getTrid() throws Exception {
return Trid.create(eppLoader.getEpp().getCommandWrapper().getClTrid(), "server-trid");
}
/** Gets the client ID that the flow will run as. */
protected String getClientIdForFlow() {
return sessionMetadata.getClientId();
}
/** Sets the client ID that the flow will run as. */
protected void setClientIdForFlow(String clientId) {
sessionMetadata.setClientId(clientId);
}
public void assertTransactionalFlow(boolean isTransactional) throws Exception {
assertThat(getFlowRunner().isTransactional()).isEqualTo(isTransactional);
}
public void assertNoHistory() throws Exception {
assertThat(ofy().load().type(HistoryEntry.class)).isEmpty();
}
public <T> T getOnlyGlobalResource(Class<T> clazz) throws Exception {
return Iterables.getOnlyElement(ofy().load().type(clazz));
}
/** Helper to remove the grace period's billing event key to facilitate comparison. */
private static final Function<GracePeriod, GracePeriod> GRACE_PERIOD_KEY_STRIPPER =
new Function<GracePeriod, GracePeriod>() {
@Override
public GracePeriod apply(GracePeriod gracePeriod) {
return GracePeriod.create(
gracePeriod.isSunrushAddGracePeriod()
? GracePeriodStatus.SUNRUSH_ADD
: gracePeriod.getType(),
gracePeriod.getExpirationTime(),
gracePeriod.getClientId(),
null);
}};
/** A helper class that sets the billing event parent history entry to facilitate comparison. */
public static class BillingEventParentSetter implements Function<BillingEvent, BillingEvent> {
private HistoryEntry historyEntry;
public static BillingEventParentSetter withParent(HistoryEntry historyEntry) {
BillingEventParentSetter instance = new BillingEventParentSetter();
instance.historyEntry = historyEntry;
return instance;
}
@Override
public BillingEvent apply(BillingEvent billingEvent) {
return billingEvent.asBuilder().setParent(historyEntry).build();
}
private BillingEventParentSetter() {}
}
/**
* Helper to facilitate comparison of maps of GracePeriods to BillingEvents. This takes a map of
* GracePeriods to BillingEvents and returns a map of the same entries that ignores the keys
* on the grace periods and the IDs on the billing events (by setting them all to the same dummy
* values), since they will vary between instantiations even when the other data is the same.
*/
private ImmutableMap<GracePeriod, BillingEvent>
canonicalizeGracePeriods(ImmutableMap<GracePeriod, ? extends BillingEvent> gracePeriods) {
ImmutableMap.Builder<GracePeriod, BillingEvent> builder = new ImmutableMap.Builder<>();
for (Map.Entry<GracePeriod, ? extends BillingEvent> entry : gracePeriods.entrySet()) {
builder.put(
GRACE_PERIOD_KEY_STRIPPER.apply(entry.getKey()),
BILLING_EVENT_ID_STRIPPER.apply(entry.getValue()));
}
return builder.build();
}
/**
* Assert that the actual grace periods and the corresponding billing events referenced from
* their keys match the expected map of grace periods to billing events. For the expected map,
* the keys on the grace periods and IDs on the billing events are ignored.
*/
public void assertGracePeriods(
Iterable<GracePeriod> actual,
ImmutableMap<GracePeriod, ? extends BillingEvent> expected) {
Function<GracePeriod, BillingEvent> gracePeriodExpander =
new Function<GracePeriod, BillingEvent>() {
@Override
public BillingEvent apply(GracePeriod gracePeriod) {
return gracePeriod.getBillingEvent().get();
}};
assertThat(canonicalizeGracePeriods(Maps.toMap(actual, gracePeriodExpander)))
.isEqualTo(canonicalizeGracePeriods(expected));
}
public void assertPollMessages(
String clientId,
PollMessage... expected) throws Exception {
assertPollMessagesHelper(getPollMessages(clientId), expected);
}
public void assertPollMessages(
String clientId,
DateTime now,
PollMessage... expected) throws Exception {
assertPollMessagesHelper(getPollMessages(clientId, now), expected);
}
public void assertPollMessages(PollMessage... expected) throws Exception {
assertPollMessagesHelper(getPollMessages(), expected);
}
/** Assert that the list matches all the poll messages in the fake datastore. */
public void assertPollMessagesHelper(Iterable<PollMessage> pollMessages, PollMessage... expected)
throws Exception {
// To facilitate comparison, remove the ids.
Function<PollMessage, PollMessage> idStripper =
new Function<PollMessage, PollMessage>() {
@Override
public PollMessage apply(PollMessage pollMessage) {
return pollMessage.asBuilder().setId(1L).build();
}};
// Ordering is irrelevant but duplicates should be considered independently.
assertThat(FluentIterable.from(pollMessages).transform(idStripper))
.containsExactlyElementsIn(FluentIterable.from(asList(expected)).transform(idStripper));
}
/** Run a flow, and attempt to marshal the result to EPP or throw if it doesn't validate. */
public EppOutput runFlow(CommitMode commitMode, UserPrivileges userPrivileges) throws Exception {
EppOutput output = getFlowRunner().run(commitMode, userPrivileges);
marshal(output, ValidationMode.STRICT);
return output;
}
public EppOutput runFlow() throws Exception {
return runFlow(CommitMode.LIVE, UserPrivileges.NORMAL);
}
public void runFlowAssertResponse(
CommitMode commitMode, UserPrivileges userPrivileges, String xml, String... ignoredPaths)
throws Exception {
EppOutput eppOutput = getFlowRunner().run(commitMode, userPrivileges);
assertThat(eppOutput.isSuccess()).isTrue();
try {
assertXmlEquals(
xml, new String(marshal(eppOutput, ValidationMode.STRICT), UTF_8), ignoredPaths);
} catch (Throwable e) {
assertXmlEquals(
xml, new String(marshal(eppOutput, ValidationMode.LENIENT), UTF_8), ignoredPaths);
// If it was a marshaling error, augment the output.
throw new Exception(
String.format(
"Invalid xml.\nExpected:\n%s\n\nActual:\n%s\n",
xml,
marshal(eppOutput, ValidationMode.LENIENT)),
e);
}
// Clear the cache so that we don't see stale results in tests.
ofy().clearSessionCache();
}
public void dryRunFlowAssertResponse(String xml, String... ignoredPaths) throws Exception {
List<Object> beforeEntities = ofy().load().list();
runFlowAssertResponse(CommitMode.DRY_RUN, UserPrivileges.NORMAL, xml, ignoredPaths);
assertThat(ofy().load()).containsExactlyElementsIn(beforeEntities);
}
public void runFlowAssertResponse(String xml, String... ignoredPaths) throws Exception {
runFlowAssertResponse(CommitMode.LIVE, UserPrivileges.NORMAL, xml, ignoredPaths);
}
}

View file

@ -0,0 +1,39 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.eppoutput.CheckData;
/**
* Base class for resource check flow unit tests.
*
* @param <F> the flow type
* @param <R> the resource type
*/
public class ResourceCheckFlowTestCase<F extends Flow, R extends EppResource>
extends ResourceFlowTestCase<F, R> {
protected void doCheckTest(CheckData.Check... expected) throws Exception {
assertTransactionalFlow(false);
assertThat(expected).asList().containsExactlyElementsIn(
((CheckData) runFlow().getResponse().getResponseData().get(0)).getChecks());
assertNoHistory(); // Checks don't create a history event.
assertNoBillingEvents(); // Checks are always free.
}
}

View file

@ -0,0 +1,140 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.model.tmch.ClaimsListShardTest.createTestClaimsListShard;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.domain.registry.flows.EppException.CommandUseErrorException;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.domain.launch.ApplicationIdTargetExtension;
import com.google.domain.registry.model.eppinput.EppInput.ResourceCommandWrapper;
import com.google.domain.registry.model.eppinput.ResourceCommand;
import com.google.domain.registry.model.index.EppResourceIndex;
import com.google.domain.registry.model.index.EppResourceIndexBucket;
import com.google.domain.registry.model.tmch.ClaimsListShard.ClaimsListRevision;
import com.google.domain.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.util.TypeUtils.TypeInstantiator;
import com.googlecode.objectify.Key;
import org.joda.time.DateTime;
import org.junit.Rule;
import org.junit.Test;
/**
* Base class for resource flow unit tests.
*
* @param <F> the flow type
* @param <R> the resource type
*/
public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource>
extends FlowTestCase<F> {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
private R reloadResourceByUniqueId(DateTime now) throws Exception {
// Force the session to be cleared so that when we read it back, we read from the datastore and
// not from the transaction cache or memcache.
ofy().clearSessionCache();
return loadByUniqueId(getResourceClass(), getUniqueIdFromCommand(), now);
}
protected R reloadResourceByUniqueId() throws Exception {
return reloadResourceByUniqueId(clock.nowUtc());
}
protected R reloadResourceByUniqueIdYesterday() throws Exception {
return reloadResourceByUniqueId(clock.nowUtc().minusDays(1));
}
protected <T extends EppResource> T reloadResourceAndCloneAtTime(T resource, DateTime now) {
// Force the session to be cleared.
ofy().clearSessionCache();
@SuppressWarnings("unchecked")
T refreshedResource = (T) ofy().load().entity(resource).now().cloneProjectedAtTime(now);
return refreshedResource;
}
protected ResourceCommand.SingleResourceCommand getResourceCommand() throws Exception {
return (ResourceCommand.SingleResourceCommand)
((ResourceCommandWrapper) eppLoader.getEpp().getCommandWrapper().getCommand())
.getResourceCommand();
}
/**
* We have to duplicate the logic from SingleResourceFlow.getTargetId() here. However, given the
* choice between making that method public, and duplicating its logic, it seems better to muddy
* the test code rather than the production code.
*/
protected String getUniqueIdFromCommand() throws Exception {
ApplicationIdTargetExtension extension =
eppLoader.getEpp().getSingleExtension(ApplicationIdTargetExtension.class);
return extension == null ? getResourceCommand().getTargetId() : extension.getApplicationId();
}
protected Class<R> getResourceClass() {
return new TypeInstantiator<R>(getClass()){}.getExactType();
}
/**
* Persists a testing claims list to Datastore that contains a single shard.
*/
protected void persistClaimsList(ImmutableMap<String, String> labelsToKeys) {
ClaimsListSingleton singleton = new ClaimsListSingleton();
Key<ClaimsListRevision> revision = ClaimsListRevision.createKey(singleton);
singleton.setActiveRevision(revision);
ofy().saveWithoutBackup().entity(singleton).now();
if (!labelsToKeys.isEmpty()) {
ofy().saveWithoutBackup()
.entity(createTestClaimsListShard(clock.nowUtc(), labelsToKeys, revision))
.now();
}
}
@Test
public void testRequiresLogin() throws Exception {
thrown.expect(CommandUseErrorException.class);
sessionMetadata.setClientId(null);
runFlow();
}
/**
* Confirms that an EppResourceIndex entity exists in datastore for a given resource.
*/
protected static <T extends EppResource> void assertEppResourceIndexEntityFor(final T resource) {
ImmutableList<EppResourceIndex> indices = FluentIterable
.from(ofy().load()
.type(EppResourceIndex.class)
.filter("kind", Key.getKind(resource.getClass())))
.filter(new Predicate<EppResourceIndex>() {
@Override
public boolean apply(EppResourceIndex index) {
return index.getReference().get().equals(resource);
}})
.toList();
assertThat(indices).hasSize(1);
assertThat(indices.get(0).getBucket())
.isEqualTo(EppResourceIndexBucket.getBucketKey(Key.create(resource)));
}
}

View file

@ -0,0 +1,293 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.async;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistContactWithPendingTransfer;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.domain.registry.model.contact.ContactAddress;
import com.google.domain.registry.model.contact.ContactPhoneNumber;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.contact.PostalInfo;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DeleteContactResourceAction}. */
@RunWith(JUnit4.class)
public class DeleteContactResourceActionTest
extends DeleteEppResourceActionTestCase<DeleteContactResourceAction> {
ContactResource contactUnused;
@Before
public void setup() throws Exception {
setupDeleteEppResourceAction(new DeleteContactResourceAction());
contactUnused = persistActiveContact("blah1235");
}
@Test
public void testSuccess_contact_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
contactUsed = persistResource(
contactUsed.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
runMapreduceWithKeyParam(Key.create(contactUsed).getString());
contactUsed = loadByUniqueId(ContactResource.class, "blah1234", now);
assertAboutContacts().that(contactUsed).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
.and().hasDeletionTime(END_OF_TIME);
domain = loadByUniqueId(DomainResource.class, "example.tld", now);
assertThat(domain.getReferencedContacts())
.contains(ReferenceUnion.<ContactResource>create(Ref.create(contactUsed)));
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactUsed, HistoryEntry.Type.CONTACT_DELETE_FAILURE);
assertPollMessageFor(
historyEntry,
"TheRegistrar",
"Can't delete contact blah1234 because it is referenced by a domain.");
}
@Test
public void testSuccess_contact_notReferenced_getsDeleted() throws Exception {
contactUnused = persistResource(
contactUnused.asBuilder()
.setLocalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.LOCALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Grand Ave"))
.build())
.build())
.setInternationalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.INTERNATIONALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Avenida Grande"))
.build())
.build())
.setEmailAddress("bob@bob.com")
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
.setFaxNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
assertAboutContacts().that(contactUnused).hasNonNullLocalizedPostalInfo()
.and().hasNonNullInternationalizedPostalInfo()
.and().hasNonNullEmailAddress()
.and().hasNonNullVoiceNumber()
.and().hasNonNullFaxNumber();
Key<ContactResource> key = Key.create(contactUnused);
runMapreduceWithKeyParam(key.getString());
assertThat(loadByUniqueId(ContactResource.class, "blah1235", now)).isNull();
ContactResource contactAfterDeletion = ofy().load().key(key).now();
assertAboutContacts().that(contactAfterDeletion).hasDeletionTime(now)
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
assertAboutContacts().that(contactAfterDeletion).hasNullLocalizedPostalInfo()
.and().hasNullInternationalizedPostalInfo()
.and().hasNullEmailAddress()
.and().hasNullVoiceNumber()
.and().hasNullFaxNumber();
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactAfterDeletion, HistoryEntry.Type.CONTACT_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact blah1235.");
}
@Test
public void testSuccess_contactWithPendingTransfer_getsDeleted() throws Exception {
ContactResource contact = persistContactWithPendingTransfer(
persistActiveContact("sh8013").asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build(),
transferRequestTime,
transferExpirationTime,
clock.nowUtc());
runMapreduceWithKeyParam(Key.create(contact).getString());
// Check that the contact is deleted as of now.
assertThat(loadByUniqueId(ContactResource.class, "sh8013", now)).isNull();
// Check that it's still there (it wasn't deleted yesterday) and that it has history.
assertAboutContacts()
.that(loadByUniqueId(ContactResource.class, "sh8013", now.minusDays(1)))
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
HistoryEntry.Type.CONTACT_DELETE);
assertNoBillingEvents();
PollMessage deletePollMessage = Iterables.getOnlyElement(
getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)));
assertThat(deletePollMessage.getMsg()).isEqualTo("Deleted contact sh8013.");
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar.
PollMessage gainingPollMessage = Iterables.getOnlyElement(
getPollMessages("NewRegistrar", clock.nowUtc()));
System.out.println(gainingPollMessage);
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable.from(gainingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.SERVER_CANCELLED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isFalse();
}
@Test
public void testSuccess_contact_referencedByDeleteDomain_getsDeleted() throws Exception {
contactUsed = persistResource(
contactUsed.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
domain = persistResource(
domain.asBuilder()
.setDeletionTime(now.minusDays(3))
.build());
runMapreduceWithKeyParam(Key.create(contactUsed).getString());
assertThat(loadByUniqueId(ContactResource.class, "blah1234", now)).isNull();
ContactResource contactBeforeDeletion =
loadByUniqueId(ContactResource.class, "blah1234", now.minusDays(1));
assertAboutContacts().that(contactBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactBeforeDeletion, HistoryEntry.Type.CONTACT_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact blah1234.");
}
@Test
public void testFailure_notPendingDelete() throws Exception {
thrown.expect(IllegalStateException.class, "Resource blah1235 is not set as PENDING_DELETE");
runMapreduceWithKeyParam(Key.create(contactUnused).getString());
assertThat(
loadByUniqueId(ContactResource.class, "blah1235", now)).isEqualTo(contactUnused);
}
@Test
public void testSuccess_notRequestedByOwner_doesNotGetDeleted() throws Exception {
contactUnused = persistResource(
contactUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
Key<ContactResource> key = Key.create(contactUnused);
runMapreduceWithParams(key.getString(), "OtherRegistrar", false);
contactUnused = loadByUniqueId(ContactResource.class, "blah1235", now);
assertAboutContacts().that(contactUnused).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
.and().hasDeletionTime(END_OF_TIME);
domain = loadByUniqueId(DomainResource.class, "example.tld", now);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactUnused, HistoryEntry.Type.CONTACT_DELETE_FAILURE);
assertPollMessageFor(
historyEntry,
"OtherRegistrar",
"Can't delete contact blah1235 because it was transferred prior to deletion.");
}
@Test
public void testSuccess_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
contactUnused = persistResource(
contactUnused.asBuilder()
.setLocalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.LOCALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Grand Ave"))
.build())
.build())
.setInternationalizedPostalInfo(
new PostalInfo.Builder()
.setType(PostalInfo.Type.INTERNATIONALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Avenida Grande"))
.build())
.build())
.setEmailAddress("bob@bob.com")
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
.setFaxNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
Key<ContactResource> key = Key.create(contactUnused);
runMapreduceWithParams(key.getString(), "OtherRegistrar", true);
assertThat(loadByUniqueId(ContactResource.class, "blah1235", now)).isNull();
ContactResource contactAfterDeletion = ofy().load().key(key).now();
assertAboutContacts().that(contactAfterDeletion).hasDeletionTime(now)
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
assertAboutContacts().that(contactAfterDeletion).hasNullLocalizedPostalInfo()
.and().hasNullInternationalizedPostalInfo()
.and().hasNullEmailAddress()
.and().hasNullVoiceNumber()
.and().hasNullFaxNumber();
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactAfterDeletion, HistoryEntry.Type.CONTACT_DELETE);
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted contact blah1235.");
}
@Test
public void testFailure_targetResourceDoesntExist() throws Exception {
createTld("tld");
ContactResource notPersisted = newContactResource("somecontact");
thrown.expect(
BadRequestException.class,
"Could not load resource for key: Key<?>(ContactResource(\"7-ROID\"))");
runMapreduceWithKeyParam(Key.create(notPersisted).getString());
}
@Test
public void testFailure_contactAlreadyDeleted() throws Exception {
ContactResource contactDeleted = persistResource(
newContactResource("blah1236").asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusDays(2))
.setDeletionTime(clock.nowUtc().minusDays(1))
.build());
thrown.expect(
IllegalStateException.class,
"Resource blah1236 is already deleted.");
runMapreduceWithKeyParam(Key.create(contactDeleted).getString());
}
}

View file

@ -0,0 +1,142 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.async;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessageForHistoryEntry;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveDomain;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.mapreduce.MapreduceRunner;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.poll.PollMessage.OneTime;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.FakeResponse;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.mapreduce.MapreduceTestCase;
import com.googlecode.objectify.Key;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Rule;
import org.junit.Test;
/** Unit tests for {@link DeleteEppResourceAction}. */
public abstract class DeleteEppResourceActionTestCase<T extends DeleteEppResourceAction<?>>
extends MapreduceTestCase<T> {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final InjectRule inject = new InjectRule();
DateTime now = DateTime.now(DateTimeZone.UTC);
FakeClock clock = new FakeClock(now);
final DateTime transferRequestTime = now.minusDays(3);
final DateTime transferExpirationTime =
transferRequestTime.plus(Registry.DEFAULT_TRANSFER_GRACE_PERIOD);
ContactResource contactUsed;
HostResource hostUsed;
DomainResource domain;
public void setupDeleteEppResourceAction(T deleteEppResourceAction) throws Exception {
action = deleteEppResourceAction;
action.mrRunner = new MapreduceRunner(Optional.<Integer>absent(), Optional.<Integer>absent());
action.response = new FakeResponse();
inject.setStaticField(Ofy.class, "clock", clock);
inject.setStaticField(DeleteEppResourceAction.class, "clock", clock);
createTld("tld");
contactUsed = persistActiveContact("blah1234");
hostUsed = persistActiveHost("ns1.example.tld");
domain = persistResource(
newDomainResource("example.tld", contactUsed).asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(hostUsed)))
.build());
}
void runMapreduce() throws Exception {
clock.advanceOneMilli();
action.run();
executeTasksUntilEmpty("mapreduce");
ofy().clearSessionCache();
now = clock.nowUtc();
}
void runMapreduceWithParams(
String resourceKeyString,
String requestingClientId,
boolean isSuperuser) throws Exception {
action.resourceKeyString = resourceKeyString;
action.requestingClientId = requestingClientId;
action.isSuperuser = isSuperuser;
runMapreduce();
}
void runMapreduceWithKeyParam(String resourceKeyString) throws Exception {
runMapreduceWithParams(resourceKeyString, "TheRegistrar", false);
}
/**
* Helper method to check that one poll message exists with a given history entry, resource,
* client id, and message.
*/
void assertPollMessageFor(
HistoryEntry historyEntry,
String clientId,
String msg) {
PollMessage.OneTime pollMessage = (OneTime) getOnlyPollMessageForHistoryEntry(historyEntry);
assertThat(msg).isEqualTo(pollMessage.getMsg());
assertThat(now).isEqualTo(pollMessage.getEventTime());
assertThat(clientId).isEqualTo(pollMessage.getClientId());
assertThat(pollMessage.getClientId()).isEqualTo(clientId);
}
@Test
public void testFailure_domainKeyPassed() throws Exception {
DomainResource domain = persistActiveDomain("fail.tld");
thrown.expect(
IllegalArgumentException.class, "Cannot delete a DomainResource via this action.");
runMapreduceWithKeyParam(Key.create(domain).getString());
assertThat(loadByUniqueId(DomainResource.class, "fail.tld", now)).isEqualTo(domain);
}
@Test
public void testFailure_badKeyPassed() throws Exception {
createTld("tld");
thrown.expect(BadRequestException.class, "Could not parse key string: a bad key");
runMapreduceWithKeyParam("a bad key");
}
}

View file

@ -0,0 +1,226 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.async;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.newHostResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.HostResourceSubject.assertAboutHosts;
import static com.google.domain.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.model.domain.DomainResource;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DeleteHostResourceAction}. */
@RunWith(JUnit4.class)
public class DeleteHostResourceActionTest
extends DeleteEppResourceActionTestCase<DeleteHostResourceAction> {
HostResource hostUnused;
@Before
public void setup() throws Exception {
setupDeleteEppResourceAction(new DeleteHostResourceAction());
hostUnused = persistActiveHost("ns2.example.tld");
}
@Test
public void testSuccess_host_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
hostUsed = persistResource(
hostUsed.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
runMapreduceWithKeyParam(Key.create(hostUsed).getString());
hostUsed = loadByUniqueId(HostResource.class, "ns1.example.tld", now);
assertAboutHosts().that(hostUsed).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
.and().hasDeletionTime(END_OF_TIME);
domain = loadByUniqueId(DomainResource.class, "example.tld", now);
assertThat(domain.getNameservers())
.contains(ReferenceUnion.<HostResource>create(Ref.create(hostUsed)));
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostUsed, HistoryEntry.Type.HOST_DELETE_FAILURE);
assertPollMessageFor(
historyEntry,
"TheRegistrar",
"Can't delete host ns1.example.tld because it is referenced by a domain.");
}
@Test
public void testSuccess_host_notReferenced_getsDeleted() throws Exception {
hostUnused = persistResource(
hostUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
assertThat(loadByUniqueId(HostResource.class, "ns2.example.tld", now)).isNull();
HostResource hostBeforeDeletion =
loadByUniqueId(HostResource.class, "ns2.example.tld", now.minusDays(1));
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.");
}
@Test
public void testSuccess_host_referencedByDeletedDomain_getsDeleted() throws Exception {
hostUsed = persistResource(
hostUsed.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
domain = persistResource(
domain.asBuilder()
.setDeletionTime(now.minusDays(3))
.build());
runMapreduceWithKeyParam(Key.create(hostUsed).getString());
assertThat(loadByUniqueId(HostResource.class, "ns1.example.tld", now)).isNull();
HostResource hostBeforeDeletion =
loadByUniqueId(HostResource.class, "ns1.example.tld", now.minusDays(1));
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns1.example.tld.");
}
@Test
public void testSuccess_subordinateHost_getsDeleted() throws Exception {
domain = persistResource(
newDomainResource("example.tld").asBuilder()
.setSubordinateHosts(ImmutableSet.of("ns2.example.tld"))
.build());
persistResource(
hostUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.setSuperordinateDomain(Ref.create(domain))
.build());
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
// Check that the host is deleted as of now.
assertThat(loadByUniqueId(HostResource.class, "ns2.example.tld", clock.nowUtc()))
.isNull();
assertNoBillingEvents();
assertThat(loadByUniqueId(DomainResource.class, "example.tld", clock.nowUtc())
.getSubordinateHosts())
.isEmpty();
assertDnsTasksEnqueued("ns2.example.tld");
HostResource hostBeforeDeletion =
loadByUniqueId(HostResource.class, "ns2.example.tld", now.minusDays(1));
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.");
}
@Test
public void testFailure_notPendingDelete() throws Exception {
thrown.expect(
IllegalStateException.class, "Resource ns2.example.tld is not set as PENDING_DELETE");
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
assertThat(
loadByUniqueId(HostResource.class, "ns2.example.tld", now)).isEqualTo(hostUnused);
}
@Test
public void testSuccess_notRequestedByOwner_doesNotGetDeleted() throws Exception {
hostUnused = persistResource(
hostUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
Key<HostResource> key = Key.create(hostUnused);
runMapreduceWithParams(key.getString(), "OtherRegistrar", false);
hostUnused = loadByUniqueId(HostResource.class, "ns2.example.tld", now);
assertAboutHosts().that(hostUnused).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
.and().hasDeletionTime(END_OF_TIME);
domain = loadByUniqueId(DomainResource.class, "example.tld", now);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostUnused, HistoryEntry.Type.HOST_DELETE_FAILURE);
assertPollMessageFor(
historyEntry,
"OtherRegistrar",
"Can't delete host ns2.example.tld because it was transferred prior to deletion.");
}
@Test
public void testSuccess_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
hostUnused = persistResource(
hostUnused.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
Key<HostResource> key = Key.create(hostUnused);
runMapreduceWithParams(key.getString(), "OtherRegistrar", true);
assertThat(loadByUniqueId(HostResource.class, "ns2.example.tld", now)).isNull();
HostResource hostBeforeDeletion =
loadByUniqueId(HostResource.class, "ns2.example.tld", now.minusDays(1));
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
.and().hasExactlyStatusValues(StatusValue.OK)
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
// added by the flow and not the mapreduce itself.
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted host ns2.example.tld.");
}
@Test
public void testFailure_targetResourceDoesntExist() throws Exception {
createTld("tld");
HostResource notPersisted = newHostResource("ns1.example.tld");
thrown.expect(
BadRequestException.class,
"Could not load resource for key: Key<?>(HostResource(\"7-ROID\"))");
runMapreduceWithKeyParam(Key.create(notPersisted).getString());
}
@Test
public void testFailure_hostAlreadyDeleted() throws Exception {
HostResource hostDeleted = persistResource(
newHostResource("ns3.example.tld").asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusDays(2))
.setDeletionTime(clock.nowUtc().minusDays(1))
.build());
thrown.expect(
IllegalStateException.class,
"Resource ns3.example.tld is already deleted.");
runMapreduceWithKeyParam(Key.create(hostDeleted).getString());
}
}

View file

@ -0,0 +1,127 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.async;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainApplication;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.newHostResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.dns.DnsQueue;
import com.google.domain.registry.mapreduce.MapreduceRunner;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.host.HostResource;
import com.google.domain.registry.request.HttpException.BadRequestException;
import com.google.domain.registry.testing.ExceptionRule;
import com.google.domain.registry.testing.FakeResponse;
import com.google.domain.registry.testing.InjectRule;
import com.google.domain.registry.testing.mapreduce.MapreduceTestCase;
import com.googlecode.objectify.Key;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DnsRefreshForHostRenameAction}. */
@RunWith(JUnit4.class)
public class DnsRefreshForHostRenameActionTest
extends MapreduceTestCase<DnsRefreshForHostRenameAction> {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public InjectRule inject = new InjectRule();
private final DnsQueue dnsQueue = mock(DnsQueue.class);
@Before
public void setup() throws Exception {
inject.setStaticField(DnsRefreshForHostRenameAction.class, "dnsQueue", dnsQueue);
}
private void runMapreduce(String hostKeyString) throws Exception {
action = new DnsRefreshForHostRenameAction();
action.hostKeyString = hostKeyString;
action.mrRunner = new MapreduceRunner(Optional.<Integer>absent(), Optional.<Integer>absent());
action.response = new FakeResponse();
action.run();
executeTasksUntilEmpty("mapreduce");
}
@Test
public void testSuccess_dnsUpdateEnqueued() throws Exception {
createTld("tld");
HostResource renamedHost = persistActiveHost("ns1.example.tld");
HostResource otherHost = persistActiveHost("ns2.example.tld");
persistResource(newDomainApplication("notadomain.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(renamedHost)))
.build());
persistResource(newDomainResource("example.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(renamedHost)))
.build());
persistResource(newDomainResource("otherexample.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(renamedHost)))
.build());
persistResource(newDomainResource("untouched.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(otherHost)))
.build());
runMapreduce(Key.create(renamedHost).getString());
verify(dnsQueue).addDomainRefreshTask("example.tld");
verify(dnsQueue).addDomainRefreshTask("otherexample.tld");
verifyNoMoreInteractions(dnsQueue);
}
@Test
public void testSuccess_noDnsTasksForDeletedDomain() throws Exception {
createTld("tld");
HostResource renamedHost = persistActiveHost("ns1.example.tld");
persistResource(newDomainResource("example.tld").asBuilder()
.setNameservers(ImmutableSet.of(ReferenceUnion.create(renamedHost)))
.setDeletionTime(START_OF_TIME)
.build());
runMapreduce(Key.create(renamedHost).getString());
verifyZeroInteractions(dnsQueue);
}
@Test
public void testFailure_badKeyPassed() throws Exception {
createTld("tld");
thrown.expect(BadRequestException.class, "Could not parse key string: a bad key");
runMapreduce("a bad key");
}
@Test
public void testFailure_hostDoesntExist() throws Exception {
createTld("tld");
HostResource notPersistedHost = newHostResource("ns1.example.tld");
thrown.expect(
BadRequestException.class,
"Could not load resource for key: Key<?>(HostResource(\"2-ROID\"))");
runMapreduce(Key.create(notPersistedHost).getString());
}
}

View file

@ -0,0 +1,83 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.model.eppoutput.CheckData.ContactCheck.create;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedContact;
import com.google.domain.registry.flows.ResourceCheckFlow.TooManyResourceChecksException;
import com.google.domain.registry.flows.ResourceCheckFlowTestCase;
import com.google.domain.registry.model.contact.ContactResource;
import org.junit.Test;
/** Unit tests for {@link ContactCheckFlow}. */
public class ContactCheckFlowTest
extends ResourceCheckFlowTestCase<ContactCheckFlow, ContactResource> {
public ContactCheckFlowTest() {
setEppInput("contact_check.xml");
}
@Test
public void testNothingExists() throws Exception {
// These ids come from the check xml.
doCheckTest(
create(true, "sh8013", null),
create(true, "sah8013", null),
create(true, "8013sah", null));
}
@Test
public void testOneExists() throws Exception {
persistActiveContact("sh8013");
// These ids come from the check xml.
doCheckTest(
create(false, "sh8013", "In use"),
create(true, "sah8013", null),
create(true, "8013sah", null));
}
@Test
public void testOneExistsButWasDeleted() throws Exception {
persistDeletedContact("sh8013", clock.nowUtc());
// These ids come from the check xml.
doCheckTest(
create(true, "sh8013", null),
create(true, "sah8013", null),
create(true, "8013sah", null));
}
@Test
public void testXmlMatches() throws Exception {
persistActiveContact("sah8013");
runFlowAssertResponse(readFile("contact_check_response.xml"));
}
@Test
public void test50IdsAllowed() throws Exception {
// Make sure we don't have a regression that reduces the number of allowed checks.
setEppInput("contact_check_50.xml");
runFlow();
}
@Test
public void testTooManyIds() throws Exception {
setEppInput("contact_check_51.xml");
thrown.expect(TooManyResourceChecksException.class);
runFlow();
}
}

View file

@ -0,0 +1,95 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedContact;
import com.google.domain.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
import com.google.domain.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException;
import com.google.domain.registry.model.contact.ContactResource;
import org.joda.time.DateTime;
import org.junit.Test;
/** Unit tests for {@link ContactCreateFlow}. */
public class ContactCreateFlowTest
extends ResourceFlowTestCase<ContactCreateFlow, ContactResource> {
public ContactCreateFlowTest() {
setEppInput("contact_create.xml");
clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z"));
}
private void doSuccessfulTest() throws Exception {
assertTransactionalFlow(true);
runFlowAssertResponse(readFile("contact_create_response.xml"));
// Check that the contact was created and persisted with a history entry.
assertAboutContacts().that(reloadResourceByUniqueId())
.hasOnlyOneHistoryEntryWhich().hasNoXml();
assertNoBillingEvents();
assertEppResourceIndexEntityFor(reloadResourceByUniqueId());
}
@Test
public void testDryRun() throws Exception {
dryRunFlowAssertResponse(readFile("contact_create_response.xml"));
}
@Test
public void testSuccess_neverExisted() throws Exception {
doSuccessfulTest();
}
@Test
public void testSuccess_existedButWasDeleted() throws Exception {
persistDeletedContact(getUniqueIdFromCommand(), clock.nowUtc());
clock.advanceOneMilli();
doSuccessfulTest();
}
@Test
public void testFailure_alreadyExists() throws Exception {
thrown.expect(
ResourceAlreadyExistsException.class,
String.format("Object with given ID (%s) already exists", getUniqueIdFromCommand()));
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testSuccess_nonAsciiInLocAddress() throws Exception {
setEppInput("contact_create_hebrew_loc.xml");
doSuccessfulTest();
}
@Test
public void testFailure_nonAsciiInIntAddress() throws Exception {
thrown.expect(BadInternationalizedPostalInfoException.class);
setEppInput("contact_create_hebrew_int.xml");
runFlow();
}
@Test
public void testFailure_declineDisclosure() throws Exception {
thrown.expect(DeclineContactDisclosureFieldDisallowedPolicyException.class);
setEppInput("contact_create_decline_disclosure.xml");
runFlow();
}
}

View file

@ -0,0 +1,189 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.flows.async.AsyncFlowUtils.ASYNC_FLOW_QUEUE_NAME;
import static com.google.domain.registry.request.Actions.getPathForAction;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import static com.google.domain.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceAsyncDeleteFlow.ResourceToDeleteIsReferencedException;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
import com.google.domain.registry.flows.async.DeleteContactResourceAction;
import com.google.domain.registry.flows.async.DeleteEppResourceAction;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.StatusValue;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.testing.TaskQueueHelper.TaskMatcher;
import com.googlecode.objectify.Key;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactDeleteFlow}. */
public class ContactDeleteFlowTest
extends ResourceFlowTestCase<ContactDeleteFlow, ContactResource> {
@Before
public void initFlowTest() {
setEppInput("contact_delete.xml");
}
private void doFailingStatusTest(StatusValue statusValue, Class<? extends Exception> exception)
throws Exception {
thrown.expect(exception);
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(statusValue))
.build());
runFlow();
}
@Test
public void testDryRun() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
dryRunFlowAssertResponse(readFile("contact_delete_response.xml"));
}
@Test
public void testSuccess() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
assertTransactionalFlow(true);
runFlowAssertResponse(readFile("contact_delete_response.xml"));
ContactResource deletedContact = reloadResourceByUniqueId();
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
.url(getPathForAction(DeleteContactResourceAction.class))
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.param(
DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID,
"TheRegistrar")
.param(
DeleteEppResourceAction.PARAM_IS_SUPERUSER,
Boolean.toString(false))
.param(
DeleteEppResourceAction.PARAM_RESOURCE_KEY,
Key.create(deletedContact).getString()));
assertAboutContacts().that(deletedContact)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
assertNoBillingEvents();
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistDeletedContact(getUniqueIdFromCommand(), clock.nowUtc());
runFlow();
}
@Test
public void testFailure_existedButWasClientDeleteProhibited() throws Exception {
doFailingStatusTest(
StatusValue.CLIENT_DELETE_PROHIBITED, ResourceStatusProhibitsOperationException.class);
}
@Test
public void testFailure_existedButWasServerDeleteProhibited() throws Exception {
doFailingStatusTest(
StatusValue.SERVER_DELETE_PROHIBITED, ResourceStatusProhibitsOperationException.class);
}
@Test
public void testFailure_existedButWasPendingDelete() throws Exception {
doFailingStatusTest(
StatusValue.PENDING_DELETE, ResourceStatusProhibitsOperationException.class);
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("contact_delete_response.xml"));
ContactResource deletedContact = reloadResourceByUniqueId();
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
.url(getPathForAction(DeleteContactResourceAction.class))
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.param(
DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID,
"NewRegistrar")
.param(
DeleteEppResourceAction.PARAM_IS_SUPERUSER,
Boolean.toString(true))
.param(
DeleteEppResourceAction.PARAM_RESOURCE_KEY,
Key.create(deletedContact).getString()));
assertAboutContacts().that(deletedContact)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
assertNoBillingEvents();
}
@Test
public void testFailure_failfastWhenLinkedToDomain() throws Exception {
createTld("tld");
persistResource(
newDomainResource("example.tld", persistActiveContact(getUniqueIdFromCommand())));
thrown.expect(ResourceToDeleteIsReferencedException.class);
runFlow();
}
@Test
public void testFailure_failfastWhenLinkedToApplication() throws Exception {
createTld("tld");
persistResource(
newDomainResource("example.tld", persistActiveContact(getUniqueIdFromCommand())));
thrown.expect(ResourceToDeleteIsReferencedException.class);
runFlow();
}
}

View file

@ -0,0 +1,141 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.model.EppResourceUtils.isDeleted;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import com.google.domain.registry.model.contact.ContactAddress;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactPhoneNumber;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.contact.Disclose;
import com.google.domain.registry.model.contact.PostalInfo;
import com.google.domain.registry.model.contact.PostalInfo.Type;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.PresenceMarker;
import com.google.domain.registry.model.eppcommon.StatusValue;
import org.joda.time.DateTime;
import org.junit.Test;
/** Unit tests for {@link ContactInfoFlow}. */
public class ContactInfoFlowTest extends ResourceFlowTestCase<ContactInfoFlow, ContactResource> {
public ContactInfoFlowTest() {
setEppInput("contact_info.xml");
}
private ContactResource persistContactResource(boolean active) {
ContactResource contact = persistResource(
new ContactResource.Builder()
.setContactId("sh8013")
.setRepoId("2FF-ROID")
.setDeletionTime(active ? null : DateTime.now().minusDays(1))
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_DELETE_PROHIBITED))
.setInternationalizedPostalInfo(new PostalInfo.Builder()
.setType(Type.INTERNATIONALIZED)
.setName("John Doe")
.setOrg("Example Inc.")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Example Dr.", "Suite 100"))
.setCity("Dulles")
.setState("VA")
.setZip("20166-6503")
.setCountryCode("US")
.build())
.build())
.setVoiceNumber(
new ContactPhoneNumber.Builder()
.setPhoneNumber("+1.7035555555")
.setExtension("1234")
.build())
.setFaxNumber(
new ContactPhoneNumber.Builder()
.setPhoneNumber("+1.7035555556")
.build())
.setEmailAddress("jdoe@example.com")
.setCurrentSponsorClientId("TheRegistrar")
.setCreationClientId("NewRegistrar")
.setLastEppUpdateClientId("NewRegistrar")
.setCreationTimeForTest(DateTime.parse("1999-04-03T22:00:00.0Z"))
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("2fooBAR")))
.setDisclose(new Disclose.Builder()
.setFlag(true)
.setVoice(new PresenceMarker())
.setEmail(new PresenceMarker())
.build())
.build());
assertThat(isDeleted(contact, DateTime.now())).isNotEqualTo(active);
return contact;
}
@Test
public void testSuccess() throws Exception {
persistContactResource(true);
// Check that the persisted contact info was returned.
assertTransactionalFlow(false);
runFlowAssertResponse(
readFile("contact_info_response.xml"),
// We use a different roid scheme than the samples so ignore it.
"epp.response.resData.infData.roid");
assertNoHistory();
assertNoBillingEvents();
}
@Test
public void testSuccess_linked() throws Exception {
createTld("foobar");
persistResource(newDomainResource("example.foobar", persistContactResource(true)));
// Check that the persisted contact info was returned.
assertTransactionalFlow(false);
runFlowAssertResponse(
readFile("contact_info_response_linked.xml"),
// We use a different roid scheme than the samples so ignore it.
"epp.response.resData.infData.roid");
assertNoHistory();
assertNoBillingEvents();
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistContactResource(false);
runFlow();
}
// Extra methods so the test runner doesn't produce empty shards.
@Test public void testNothing1() {}
}

View file

@ -0,0 +1,213 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.createTld;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferApproveFlow}. */
public class ContactTransferApproveFlowTest
extends ContactTransferFlowTestCase<ContactTransferApproveFlow, ContactResource> {
@Before
public void setUp() throws Exception {
setEppInput("contact_transfer_approve.xml");
setClientIdForFlow("TheRegistrar");
setupContactWithPendingTransfer();
clock.advanceOneMilli();
createTld("foobar");
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have succeeded. Verify correct fields were set.
contact = reloadResourceByUniqueId();
assertAboutContacts().that(contact)
.hasCurrentSponsorClientId("NewRegistrar").and()
.hasLastTransferTime(clock.nowUtc()).and()
.hasTransferStatus(TransferStatus.CLIENT_APPROVED).and()
.hasPendingTransferExpirationTime(clock.nowUtc()).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
HistoryEntry.Type.CONTACT_TRANSFER_APPROVE);
assertNoBillingEvents();
// The poll message (in the future) to the losing registrar for implicit ack should be gone.
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1))).isEmpty();
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar.
PollMessage gainingPollMessage = getOnlyPollMessage("NewRegistrar");
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.CLIENT_APPROVED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isTrue();
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("contact_transfer_approve.xml");
dryRunFlowAssertResponse(readFile("contact_transfer_approve_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_approve.xml", "contact_transfer_approve_response.xml");
}
@Test
public void testSuccess_withAuthinfo() throws Exception {
doSuccessfulTest("contact_transfer_approve_with_authinfo.xml",
"contact_transfer_approve_response.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_approve_with_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_gainingClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("NewRegistrar");
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("ClientZ");
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_approve.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_approve.xml");
}
}

View file

@ -0,0 +1,199 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.flows.ResourceTransferCancelFlow.NotTransferInitiatorException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferCancelFlow}. */
public class ContactTransferCancelFlowTest
extends ContactTransferFlowTestCase<ContactTransferCancelFlow, ContactResource> {
@Before
public void setUp() throws Exception {
this.setEppInput("contact_transfer_cancel.xml");
setClientIdForFlow("NewRegistrar");
setupContactWithPendingTransfer();
clock.advanceOneMilli();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
this.setEppInput(commandFilename);
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1);
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1))).hasSize(1);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have been cancelled. Verify correct fields were set.
contact = reloadResourceByUniqueId();
assertAboutContacts().that(contact)
.hasCurrentSponsorClientId("TheRegistrar").and()
.hasLastTransferTimeNotEqualTo(clock.nowUtc()).and()
.hasTransferStatus(TransferStatus.CLIENT_CANCELLED).and()
.hasPendingTransferExpirationTime(clock.nowUtc()).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
HistoryEntry.Type.CONTACT_TRANSFER_CANCEL);
assertNoBillingEvents();
// The poll message (in the future) to the gaining registrar for implicit ack should be gone.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1))).isEmpty();
// The poll message in the future to the losing registrar should be gone too, but there
// should be one at the current time to the losing registrar.
PollMessage losingPollMessage = getOnlyPollMessage("TheRegistrar");
assertThat(losingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(losingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.CLIENT_CANCELLED);
}
private void doFailingTest(String commandFilename) throws Exception {
this.setEppInput(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("contact_transfer_cancel.xml");
dryRunFlowAssertResponse(readFile("contact_transfer_cancel_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_cancel.xml", "contact_transfer_cancel_response.xml");
}
@Test
public void testSuccess_withAuthinfo() throws Exception {
doSuccessfulTest("contact_transfer_cancel_with_authinfo.xml",
"contact_transfer_cancel_response.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_cancel_with_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_sponsoringClient() throws Exception {
thrown.expect(NotTransferInitiatorException.class);
setClientIdForFlow("TheRegistrar");
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(NotTransferInitiatorException.class);
setClientIdForFlow("ClientZ");
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_cancel.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
doFailingTest("contact_transfer_cancel.xml");
}
}

View file

@ -0,0 +1,90 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.common.base.Preconditions.checkState;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistContactWithPendingTransfer;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.flows.Flow;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.model.EppResource;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.transfer.TransferStatus;
import com.google.domain.registry.testing.AppEngineRule;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
/**
* Base class for contact transfer flow unit tests.
*
* @param <F> the flow type
* @param <R> the resource type
*/
public class ContactTransferFlowTestCase<F extends Flow, R extends EppResource>
extends ResourceFlowTestCase<F, R>{
// Transfer is requested on the 6th and expires on the 11th.
// The "now" of this flow is on the 9th, 3 days in.
private static final DateTime TRANSFER_REQUEST_TIME = DateTime.parse("2000-06-06T22:00:00.0Z");
private static final DateTime TRANSFER_EXPIRATION_TIME =
TRANSFER_REQUEST_TIME.plus(Registry.DEFAULT_TRANSFER_GRACE_PERIOD);
private static final Duration TIME_SINCE_REQUEST = Duration.standardDays(3);
protected ContactResource contact;
public ContactTransferFlowTestCase() {
checkState(!Registry.DEFAULT_TRANSFER_GRACE_PERIOD.isShorterThan(TIME_SINCE_REQUEST));
clock.setTo(TRANSFER_REQUEST_TIME.plus(TIME_SINCE_REQUEST));
}
@Before
public void initContactTest() {
// Registrar ClientZ is used in tests that need another registrar that definitely doesn't own
// the resources in question.
persistResource(
AppEngineRule.makeRegistrar1().asBuilder().setClientIdentifier("ClientZ").build());
}
/** Adds a contact that has a pending transfer on it from TheRegistrar to NewRegistrar. */
protected void setupContactWithPendingTransfer() throws Exception {
contact = persistContactWithPendingTransfer(
newContactResource("sh8013"),
TRANSFER_REQUEST_TIME,
TRANSFER_EXPIRATION_TIME,
TRANSFER_REQUEST_TIME);
}
/** Changes the transfer status on the persisted contact. */
protected void changeTransferStatus(TransferStatus transferStatus) {
contact = persistResource(
contact.asBuilder()
.setTransferData(
contact.getTransferData().asBuilder().setTransferStatus(transferStatus).build())
.build());
clock.advanceOneMilli();
}
/** Changes the client ID that the flow will run as. */
@Override
protected void setClientIdForFlow(String clientId) {
sessionMetadata.setClientId(clientId);
}
}

View file

@ -0,0 +1,187 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import com.google.domain.registry.flows.ResourceTransferQueryFlow.NoTransferHistoryToQueryException;
import com.google.domain.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferQueryFlow}. */
public class ContactTransferQueryFlowTest
extends ContactTransferFlowTestCase<ContactTransferQueryFlow, ContactResource> {
@Before
public void setUp() throws Exception {
setEppInput("contact_transfer_query.xml");
clock.setTo(DateTime.parse("2000-06-10T22:00:00.0Z"));
setClientIdForFlow("NewRegistrar");
setupContactWithPendingTransfer();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(false);
runFlowAssertResponse(readFile(expectedXmlFilename));
assertAboutContacts().that(reloadResourceByUniqueIdYesterday())
.hasOneHistoryEntryEachOfTypes(HistoryEntry.Type.CONTACT_TRANSFER_REQUEST);
assertNoBillingEvents();
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertTransactionalFlow(false);
runFlow();
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_query.xml", "contact_transfer_query_response.xml");
}
@Test
public void testSuccess_withContactRoid() throws Exception {
doSuccessfulTest("contact_transfer_query_with_roid.xml", "contact_transfer_query_response.xml");
}
@Test
public void testSuccess_sponsoringClient() throws Exception {
setClientIdForFlow("TheRegistrar");
doSuccessfulTest("contact_transfer_query.xml", "contact_transfer_query_response.xml");
}
@Test
public void testSuccess_withAuthinfo() throws Exception {
setClientIdForFlow("ClientZ");
doSuccessfulTest("contact_transfer_query_with_authinfo.xml",
"contact_transfer_query_response.xml");
}
@Test
public void testSuccess_clientApproved() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_client_approved.xml");
}
@Test
public void testSuccess_clientRejected() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_client_rejected.xml");
}
@Test
public void testSuccess_clientCancelled() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_client_cancelled.xml");
}
@Test
public void testSuccess_serverApproved() throws Exception {
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_server_approved.xml");
}
@Test
public void testSuccess_serverCancelled() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_server_cancelled.xml");
}
@Test
public void testFailure_pendingDeleteContact() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().plusDays(1)).build());
doSuccessfulTest("contact_transfer_query.xml",
"contact_transfer_query_response_server_cancelled.xml");
}
@Test
public void testFailure_badContactPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_query_with_authinfo.xml");
}
@Test
public void testFailure_badContactRoid() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Set the contact to a different ROID, but don't persist it; this is just so the substitution
// code above will write the wrong ROID into the file.
contact = contact.asBuilder().setRepoId("DEADBEEF_TLD-ROID").build();
doFailingTest("contact_transfer_query_with_roid.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NoTransferHistoryToQueryException.class);
changeTransferStatus(null);
doFailingTest("contact_transfer_query.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(NotAuthorizedToViewTransferException.class);
setClientIdForFlow("ClientZ");
doFailingTest("contact_transfer_query.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_query.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToQueryDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
doFailingTest("contact_transfer_query.xml");
}
}

View file

@ -0,0 +1,209 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getOnlyPollMessage;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.eppcommon.Trid;
import com.google.domain.registry.model.poll.PendingActionNotificationResponse;
import com.google.domain.registry.model.poll.PollMessage;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferResponse;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferRejectFlow}. */
public class ContactTransferRejectFlowTest
extends ContactTransferFlowTestCase<ContactTransferRejectFlow, ContactResource> {
@Before
public void setUp() throws Exception {
setEppInput("contact_transfer_reject.xml");
setClientIdForFlow("TheRegistrar");
setupContactWithPendingTransfer();
clock.advanceOneMilli();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
// Look in the future and make sure the poll messages for implicit ack are there.
assertThat(getPollMessages("NewRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.hasSize(1);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have failed. Verify correct fields were set.
contact = reloadResourceByUniqueId();
assertAboutContacts().that(contact)
.hasCurrentSponsorClientId("TheRegistrar").and()
.hasLastTransferTimeNotEqualTo(clock.nowUtc()).and()
.hasTransferStatus(TransferStatus.CLIENT_REJECTED).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
HistoryEntry.Type.CONTACT_TRANSFER_REJECT);
// The poll message (in the future) to the losing registrar for implicit ack should be gone.
assertThat(getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)))
.isEmpty();
// The poll message in the future to the gaining registrar should be gone too, but there
// should be one at the current time to the gaining registrar.
PollMessage gainingPollMessage = getOnlyPollMessage("NewRegistrar");
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(
Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(TransferResponse.class))
.getTransferStatus())
.isEqualTo(TransferStatus.CLIENT_REJECTED);
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(gainingPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid())
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
assertThat(panData.getActionResult()).isFalse();
assertNoBillingEvents();
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("contact_transfer_reject.xml");
dryRunFlowAssertResponse(readFile("contact_transfer_reject_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_reject.xml", "contact_transfer_reject_response.xml");
}
@Test
public void testSuccess_domainAuthInfo() throws Exception {
doSuccessfulTest("contact_transfer_reject_with_authinfo.xml",
"contact_transfer_reject_response.xml");
}
@Test
public void testFailure_badPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_reject_with_authinfo.xml");
}
@Test
public void testFailure_neverBeenTransferred() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(null);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_clientApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_clientRejected() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_clientCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_serverApproved() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_serverCancelled() throws Exception {
thrown.expect(NotPendingTransferException.class);
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_gainingClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("NewRegistrar");
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_unrelatedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
setClientIdForFlow("ClientZ");
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_reject.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
doFailingTest("contact_transfer_reject.xml");
}
}

View file

@ -0,0 +1,192 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.common.truth.Truth.assertThat;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.deleteResource;
import static com.google.domain.registry.testing.DatastoreHelper.getPollMessages;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.domain.registry.config.RegistryEnvironment;
import com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.AlreadyPendingTransferException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.MissingTransferRequestAuthInfoException;
import com.google.domain.registry.flows.ResourceTransferRequestFlow.ObjectAlreadySponsoredException;
import com.google.domain.registry.model.contact.ContactAuthInfo;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.eppcommon.AuthInfo.PasswordAuth;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.model.transfer.TransferStatus;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link ContactTransferRequestFlow}. */
public class ContactTransferRequestFlowTest
extends ContactTransferFlowTestCase<ContactTransferRequestFlow, ContactResource> {
public ContactTransferRequestFlowTest() {
// We need the transfer to happen at exactly this time in order for the response to match up.
clock.setTo(DateTime.parse("2000-06-08T22:00:00.0Z"));
}
@Before
public void setUp() throws Exception {
setEppInput("contact_transfer_request.xml");
setClientIdForFlow("NewRegistrar");
contact = persistActiveContact("sh8013");
clock.advanceOneMilli();
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
setEppInput(commandFilename);
DateTime afterTransfer =
clock.nowUtc().plus(RegistryEnvironment.get().config().getContactAutomaticTransferLength());
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(readFile(expectedXmlFilename));
// Transfer should have been requested. Verify correct fields were set.
contact = reloadResourceByUniqueId();
assertAboutContacts().that(contact)
.hasTransferStatus(TransferStatus.PENDING).and()
.hasTransferGainingClientId("NewRegistrar").and()
.hasTransferLosingClientId("TheRegistrar").and()
.hasTransferRequestTrid(getTrid()).and()
.hasCurrentSponsorClientId("TheRegistrar").and()
.hasPendingTransferExpirationTime(afterTransfer).and()
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.CONTACT_TRANSFER_REQUEST);
assertNoBillingEvents();
assertThat(getPollMessages("TheRegistrar", clock.nowUtc())).hasSize(1);
// If we fast forward AUTOMATIC_TRANSFER_DAYS the transfer should have happened.
assertAboutContacts().that(contact.cloneProjectedAtTime(afterTransfer))
.hasCurrentSponsorClientId("NewRegistrar");
assertThat(getPollMessages("NewRegistrar", afterTransfer)).hasSize(1);
assertThat(getPollMessages("TheRegistrar", afterTransfer)).hasSize(2);
}
private void doFailingTest(String commandFilename) throws Exception {
setEppInput(commandFilename);
// Setup done; run the test.
assertTransactionalFlow(true);
runFlow();
}
@Test
public void testDryRun() throws Exception {
setEppInput("contact_transfer_request.xml");
dryRunFlowAssertResponse(readFile("contact_transfer_request_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testFailure_noAuthInfo() throws Exception {
thrown.expect(MissingTransferRequestAuthInfoException.class);
doFailingTest("contact_transfer_request_no_authinfo.xml");
}
@Test
public void testFailure_badPassword() throws Exception {
thrown.expect(BadAuthInfoForResourceException.class);
// Change the contact's password so it does not match the password in the file.
contact = persistResource(
contact.asBuilder()
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("badpassword")))
.build());
doFailingTest("contact_transfer_request.xml");
}
@Test
public void testSuccess_clientApproved() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_APPROVED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testSuccess_clientRejected() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_REJECTED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testSuccess_clientCancelled() throws Exception {
changeTransferStatus(TransferStatus.CLIENT_CANCELLED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testSuccess_serverApproved() throws Exception {
changeTransferStatus(TransferStatus.SERVER_APPROVED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testSuccess_serverCancelled() throws Exception {
changeTransferStatus(TransferStatus.SERVER_CANCELLED);
doSuccessfulTest("contact_transfer_request.xml", "contact_transfer_request_response.xml");
}
@Test
public void testFailure_pending() throws Exception {
thrown.expect(AlreadyPendingTransferException.class);
contact = persistResource(
contact.asBuilder()
.setTransferData(contact.getTransferData().asBuilder()
.setTransferStatus(TransferStatus.PENDING)
.setPendingTransferExpirationTime(clock.nowUtc().plusDays(1))
.build())
.build());
doFailingTest("contact_transfer_request.xml");
}
@Test
public void testFailure_sponsoringClient() throws Exception {
thrown.expect(ObjectAlreadySponsoredException.class);
setClientIdForFlow("TheRegistrar");
doFailingTest("contact_transfer_request.xml");
}
@Test
public void testFailure_deletedContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
contact = persistResource(
contact.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
doFailingTest("contact_transfer_request.xml");
}
@Test
public void testFailure_nonexistentContact() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
deleteResource(contact);
doFailingTest("contact_transfer_request.xml");
}
}

View file

@ -0,0 +1,265 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.flows.contact;
import static com.google.domain.registry.testing.ContactResourceSubject.assertAboutContacts;
import static com.google.domain.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistDeletedContact;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.flows.FlowRunner.CommitMode;
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
import com.google.domain.registry.flows.ResourceFlowTestCase;
import com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException;
import com.google.domain.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException;
import com.google.domain.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException;
import com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException;
import com.google.domain.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
import com.google.domain.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException;
import com.google.domain.registry.model.contact.ContactAddress;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.contact.PostalInfo;
import com.google.domain.registry.model.contact.PostalInfo.Type;
import com.google.domain.registry.model.eppcommon.StatusValue;
import org.junit.Test;
/** Unit tests for {@link ContactUpdateFlow}. */
public class ContactUpdateFlowTest
extends ResourceFlowTestCase<ContactUpdateFlow, ContactResource> {
public ContactUpdateFlowTest() {
setEppInput("contact_update.xml");
}
private void doSuccessfulTest() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
assertTransactionalFlow(true);
runFlowAssertResponse(readFile("contact_update_response.xml"));
// Check that the contact was updated. This value came from the xml.
assertAboutContacts().that(reloadResourceByUniqueId())
.hasAuthInfoPwd("2fooBAR").and()
.hasOnlyOneHistoryEntryWhich()
.hasNoXml();
assertNoBillingEvents();
}
@Test
public void testDryRun() throws Exception {
persistActiveContact(getUniqueIdFromCommand());
dryRunFlowAssertResponse(readFile("contact_update_response.xml"));
}
@Test
public void testSuccess() throws Exception {
doSuccessfulTest();
}
@Test
public void testSuccess_removeClientUpdateProhibited() throws Exception {
setEppInput("contact_update_remove_client_update_prohibited.xml");
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
.build());
doSuccessfulTest();
assertAboutContacts().that(reloadResourceByUniqueId())
.doesNotHaveStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED);
}
@Test
public void testSuccess_updatingOnePostalInfoDeletesTheOther() throws Exception {
ContactResource contact =
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setLocalizedPostalInfo(new PostalInfo.Builder()
.setType(Type.LOCALIZED)
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
.setCity("New York")
.setState("NY")
.setZip("10011")
.setCountryCode("US")
.build())
.build())
.build());
clock.advanceOneMilli();
// The test xml updates the internationalized postal info and should therefore implicitly delete
// the localized one since they are treated as a pair for update purposes.
assertAboutContacts().that(contact)
.hasNonNullLocalizedPostalInfo().and()
.hasInternationalizedPostalInfo(null);
runFlowAssertResponse(readFile("contact_update_response.xml"));
assertAboutContacts().that(reloadResourceByUniqueId())
.hasLocalizedPostalInfo(null).and()
.hasNonNullInternationalizedPostalInfo();
}
@Test
public void testSuccess_partialPostalInfoUpdate() throws Exception {
setEppInput("contact_update_partial_postalinfo.xml");
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setLocalizedPostalInfo(new PostalInfo.Builder()
.setType(Type.LOCALIZED)
.setName("A. Person")
.setOrg("Company Inc.")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 4th st", "5th Floor"))
.setCity("City")
.setState("AB")
.setZip("12345")
.setCountryCode("US")
.build())
.build())
.build());
clock.advanceOneMilli();
// The test xml updates the address of the postal info and should leave the name untouched.
runFlowAssertResponse(readFile("contact_update_response.xml"));
assertAboutContacts().that(reloadResourceByUniqueId()).hasLocalizedPostalInfo(
new PostalInfo.Builder()
.setType(Type.LOCALIZED)
.setName("A. Person")
.setOrg("Company Inc.")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("456 5th st"))
.setCity("Place")
.setState("CD")
.setZip("54321")
.setCountryCode("US")
.build())
.build());
}
@Test
public void testFailure_neverExisted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
runFlow();
}
@Test
public void testFailure_existedButWasDeleted() throws Exception {
thrown.expect(
ResourceToMutateDoesNotExistException.class,
String.format("(%s)", getUniqueIdFromCommand()));
persistDeletedContact(getUniqueIdFromCommand(), clock.nowUtc());
runFlow();
}
@Test
public void testFailure_clientProhibitedStatusValue() throws Exception {
thrown.expect(StatusNotClientSettableException.class);
setEppInput("contact_update_prohibited_status.xml");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testSuccess_superuserClientProhibitedStatusValue() throws Exception {
setEppInput("contact_update_prohibited_status.xml");
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
readFile("contact_update_response.xml"));
}
@Test
public void testFailure_unauthorizedClient() throws Exception {
thrown.expect(ResourceNotOwnedException.class);
sessionMetadata.setClientId("NewRegistrar");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setSuperuser(true);
sessionMetadata.setClientId("NewRegistrar");
persistActiveContact(getUniqueIdFromCommand());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("contact_update_response.xml"));
}
@Test
public void testSuccess_superuserClientUpdateProhibited() throws Exception {
setEppInput("contact_update_prohibited_status.xml");
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
.build());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
readFile("contact_update_response.xml"));
assertAboutContacts().that(reloadResourceByUniqueId())
.hasStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED).and()
.hasStatusValue(StatusValue.SERVER_DELETE_PROHIBITED);
}
@Test
public void testFailure_clientUpdateProhibited() throws Exception {
thrown.expect(ResourceHasClientUpdateProhibitedException.class);
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
.build());
runFlow();
}
@Test
public void testFailure_serverUpdateProhibited() throws Exception {
thrown.expect(ResourceStatusProhibitsOperationException.class);
persistResource(
newContactResource(getUniqueIdFromCommand()).asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.SERVER_UPDATE_PROHIBITED))
.build());
runFlow();
}
@Test
public void testSuccess_nonAsciiInLocAddress() throws Exception {
setEppInput("contact_update_hebrew_loc.xml");
doSuccessfulTest();
}
@Test
public void testFailure_nonAsciiInIntAddress() throws Exception {
thrown.expect(BadInternationalizedPostalInfoException.class);
setEppInput("contact_update_hebrew_int.xml");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
@Test
public void testFailure_declineDisclosure() throws Exception {
thrown.expect(DeclineContactDisclosureFieldDisallowedPolicyException.class);
setEppInput("contact_update_decline_disclosure.xml");
persistActiveContact(getUniqueIdFromCommand());
runFlow();
}
}

View file

@ -0,0 +1,13 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<contact:check
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:id>sah8013</contact:id>
<contact:id>8013sah</contact:id>
</contact:check>
</check>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,60 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<contact:check
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>contact1</contact:id>
<contact:id>contact2</contact:id>
<contact:id>contact3</contact:id>
<contact:id>contact4</contact:id>
<contact:id>contact5</contact:id>
<contact:id>contact6</contact:id>
<contact:id>contact7</contact:id>
<contact:id>contact8</contact:id>
<contact:id>contact9</contact:id>
<contact:id>contact10</contact:id>
<contact:id>contact11</contact:id>
<contact:id>contact12</contact:id>
<contact:id>contact13</contact:id>
<contact:id>contact14</contact:id>
<contact:id>contact15</contact:id>
<contact:id>contact16</contact:id>
<contact:id>contact17</contact:id>
<contact:id>contact18</contact:id>
<contact:id>contact19</contact:id>
<contact:id>contact20</contact:id>
<contact:id>contact21</contact:id>
<contact:id>contact22</contact:id>
<contact:id>contact23</contact:id>
<contact:id>contact24</contact:id>
<contact:id>contact25</contact:id>
<contact:id>contact26</contact:id>
<contact:id>contact27</contact:id>
<contact:id>contact28</contact:id>
<contact:id>contact29</contact:id>
<contact:id>contact30</contact:id>
<contact:id>contact31</contact:id>
<contact:id>contact32</contact:id>
<contact:id>contact33</contact:id>
<contact:id>contact34</contact:id>
<contact:id>contact35</contact:id>
<contact:id>contact36</contact:id>
<contact:id>contact37</contact:id>
<contact:id>contact38</contact:id>
<contact:id>contact39</contact:id>
<contact:id>contact40</contact:id>
<contact:id>contact41</contact:id>
<contact:id>contact42</contact:id>
<contact:id>contact43</contact:id>
<contact:id>contact44</contact:id>
<contact:id>contact45</contact:id>
<contact:id>contact46</contact:id>
<contact:id>contact47</contact:id>
<contact:id>contact48</contact:id>
<contact:id>contact49</contact:id>
<contact:id>contact50</contact:id>
</contact:check>
</check>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,61 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<contact:check
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>contact1</contact:id>
<contact:id>contact2</contact:id>
<contact:id>contact3</contact:id>
<contact:id>contact4</contact:id>
<contact:id>contact5</contact:id>
<contact:id>contact6</contact:id>
<contact:id>contact7</contact:id>
<contact:id>contact8</contact:id>
<contact:id>contact9</contact:id>
<contact:id>contact10</contact:id>
<contact:id>contact11</contact:id>
<contact:id>contact12</contact:id>
<contact:id>contact13</contact:id>
<contact:id>contact14</contact:id>
<contact:id>contact15</contact:id>
<contact:id>contact16</contact:id>
<contact:id>contact17</contact:id>
<contact:id>contact18</contact:id>
<contact:id>contact19</contact:id>
<contact:id>contact20</contact:id>
<contact:id>contact21</contact:id>
<contact:id>contact22</contact:id>
<contact:id>contact23</contact:id>
<contact:id>contact24</contact:id>
<contact:id>contact25</contact:id>
<contact:id>contact26</contact:id>
<contact:id>contact27</contact:id>
<contact:id>contact28</contact:id>
<contact:id>contact29</contact:id>
<contact:id>contact30</contact:id>
<contact:id>contact31</contact:id>
<contact:id>contact32</contact:id>
<contact:id>contact33</contact:id>
<contact:id>contact34</contact:id>
<contact:id>contact35</contact:id>
<contact:id>contact36</contact:id>
<contact:id>contact37</contact:id>
<contact:id>contact38</contact:id>
<contact:id>contact39</contact:id>
<contact:id>contact40</contact:id>
<contact:id>contact41</contact:id>
<contact:id>contact42</contact:id>
<contact:id>contact43</contact:id>
<contact:id>contact44</contact:id>
<contact:id>contact45</contact:id>
<contact:id>contact46</contact:id>
<contact:id>contact47</contact:id>
<contact:id>contact48</contact:id>
<contact:id>contact49</contact:id>
<contact:id>contact50</contact:id>
<contact:id>contact51</contact:id>
</contact:check>
</check>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,26 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:chkData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:cd>
<contact:id avail="1">sh8013</contact:id>
</contact:cd>
<contact:cd>
<contact:id avail="0">sah8013</contact:id>
<contact:reason>In use</contact:reason>
</contact:cd>
<contact:cd>
<contact:id avail="1">8013sah</contact:id>
</contact:cd>
</contact:chkData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,33 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,33 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="0">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,45 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>חנוך גולדפדר</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:postalInfo type="loc">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,45 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:postalInfo type="loc">
<contact:name>חנוך גולדפדר</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,18 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:creData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:crDate>1999-04-03T22:00:00.0Z</contact:crDate>
</contact:creData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<delete>
<contact:delete
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:delete>
</delete>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1001">
<msg>Command completed successfully; action pending</msg>
</result>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<info>
<contact:info
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:info>
</info>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,47 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:infData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:roid>SH8013-REP</contact:roid>
<contact:status s="clientDeleteProhibited"/>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:clID>TheRegistrar</contact:clID>
<contact:crID>NewRegistrar</contact:crID>
<contact:crDate>1999-04-03T22:00:00.0Z</contact:crDate>
<contact:upID>NewRegistrar</contact:upID>
<contact:upDate>1999-12-03T09:00:00.0Z</contact:upDate>
<contact:trDate>2000-04-08T09:00:00.0Z</contact:trDate>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:infData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,48 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:infData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:roid>SH8013-REP</contact:roid>
<contact:status s="linked"/>
<contact:status s="clientDeleteProhibited"/>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:clID>TheRegistrar</contact:clID>
<contact:crID>NewRegistrar</contact:crID>
<contact:crDate>1999-04-03T22:00:00.0Z</contact:crDate>
<contact:upID>NewRegistrar</contact:upID>
<contact:upDate>1999-12-03T09:00:00.0Z</contact:upDate>
<contact:trDate>2000-04-08T09:00:00.0Z</contact:trDate>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:infData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="approve">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientApproved</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-09T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="approve">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="cancel">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientCancelled</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-09T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,14 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="cancel">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,11 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<transfer op="query">
<contact:transfer
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
</contact:transfer>
</transfer>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>pending</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientApproved</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientCancelled</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>clientRejected</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>serverApproved</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,22 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<contact:trnData
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>sh8013</contact:id>
<contact:trStatus>serverCancelled</contact:trStatus>
<contact:reID>NewRegistrar</contact:reID>
<contact:reDate>2000-06-06T22:00:00.0Z</contact:reDate>
<contact:acID>TheRegistrar</contact:acID>
<contact:acDate>2000-06-11T22:00:00.0Z</contact:acDate>
</contact:trnData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

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