Refactor to be more in line with a standard Gradle project structure

This commit is contained in:
Gus Brodman 2019-05-21 14:12:47 -04:00
parent 98f87bcc03
commit 38cfc9f693
3141 changed files with 99 additions and 100 deletions

View file

@ -0,0 +1,147 @@
// Copyright 2018 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.appengine.api.modules.ModulesService;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
/** Unit tests for {@link AppEngineServiceUtilsImpl}. */
@RunWith(JUnit4.class)
public class AppEngineServiceUtilsImplTest {
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
@Mock private ModulesService modulesService;
private AppEngineServiceUtils appEngineServiceUtils;
@Before
public void before() {
appEngineServiceUtils = new AppEngineServiceUtilsImpl(modulesService);
when(modulesService.getVersionHostname(anyString(), isNull()))
.thenReturn("1234.servicename.projectid.appspot.fake");
when(modulesService.getVersionHostname(anyString(), eq("2345")))
.thenReturn("2345.servicename.projectid.appspot.fake");
}
@Test
public void test_getServiceHostname_doesntIncludeVersionId() {
assertThat(appEngineServiceUtils.getServiceHostname("servicename"))
.isEqualTo("servicename.projectid.appspot.fake");
}
@Test
public void test_getVersionHostname_doesIncludeVersionId() {
assertThat(appEngineServiceUtils.getCurrentVersionHostname("servicename"))
.isEqualTo("1234.servicename.projectid.appspot.fake");
}
@Test
public void test_getVersionHostname_worksWithVersionId() {
assertThat(appEngineServiceUtils.getVersionHostname("servicename", "2345"))
.isEqualTo("2345.servicename.projectid.appspot.fake");
}
@Test
public void test_getVersionHostname_throwsWhenVersionIdIsNull() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> appEngineServiceUtils.getVersionHostname("servicename", null));
assertThat(thrown).hasMessageThat().isEqualTo("Must specify the version");
}
@Test
public void test_setNumInstances_worksWithValidParameters() {
appEngineServiceUtils.setNumInstances("service", "version", 10L);
verify(modulesService, times(1)).setNumInstances("service", "version", 10L);
}
@Test
public void test_setNumInstances_throwsWhenServiceIsNull() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> appEngineServiceUtils.setNumInstances(null, "version", 10L));
assertThat(thrown).hasMessageThat().isEqualTo("Must specify the service");
}
@Test
public void test_setNumInstances_throwsWhenVersionIsNull() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> appEngineServiceUtils.setNumInstances("service", null, 10L));
assertThat(thrown).hasMessageThat().isEqualTo("Must specify the version");
}
@Test
public void test_setNumInstances_throwsWhenNumInstancesIsInvalid() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> appEngineServiceUtils.setNumInstances("service", "version", -10L));
assertThat(thrown).hasMessageThat().isEqualTo("Number of instances must be greater than 0");
}
@Test
public void test_convertToSingleSubdomain_doesNothingWithoutServiceOrHostname() {
assertThat(appEngineServiceUtils.convertToSingleSubdomain("projectid.appspot.com"))
.isEqualTo("projectid.appspot.com");
}
@Test
public void test_convertToSingleSubdomain_doesNothingWhenItCannotParseCorrectly() {
assertThat(appEngineServiceUtils.convertToSingleSubdomain("garbage.notrealhost.example"))
.isEqualTo("garbage.notrealhost.example");
}
@Test
public void test_convertToSingleSubdomain_convertsWithServiceName() {
assertThat(appEngineServiceUtils.convertToSingleSubdomain("service.projectid.appspot.com"))
.isEqualTo("service-dot-projectid.appspot.com");
}
@Test
public void test_convertToSingleSubdomain_convertsWithVersionAndServiceName() {
assertThat(
appEngineServiceUtils.convertToSingleSubdomain("version.service.projectid.appspot.com"))
.isEqualTo("version-dot-service-dot-projectid.appspot.com");
}
@Test
public void test_convertToSingleSubdomain_convertsWithInstanceAndVersionAndServiceName() {
assertThat(
appEngineServiceUtils.convertToSingleSubdomain(
"instanceid.version.service.projectid.appspot.com"))
.isEqualTo("instanceid-dot-version-dot-service-dot-projectid.appspot.com");
}
}

View file

@ -0,0 +1,34 @@
package(
default_testonly = 1,
default_visibility = ["//java/google/registry:registry_project"],
)
licenses(["notice"]) # Apache 2.0
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
java_library(
name = "util",
srcs = glob(["*.java"]),
deps = [
"//java/google/registry/util",
"//javatests/google/registry/testing",
"@com_google_appengine_api_1_0_sdk",
"@com_google_code_findbugs_jsr305",
"@com_google_flogger",
"@com_google_flogger_system_backend",
"@com_google_guava",
"@com_google_guava_testlib",
"@com_google_truth",
"@com_google_truth_extensions_truth_java8_extension",
"@joda_time",
"@junit",
"@org_mockito_core",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":util"],
)

View file

@ -0,0 +1,315 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static google.registry.testing.JUnitBackports.assertThrows;
import static org.junit.Assert.assertNotEquals;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.InetAddresses;
import com.google.common.testing.NullPointerTester;
import com.google.common.testing.SerializableTester;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import junit.framework.TestCase;
/**
* Tests for {@link CidrAddressBlock}.
*
*/
public class CidrAddressBlockTest extends TestCase {
public void testNulls() {
NullPointerTester tester = new NullPointerTester();
tester.testAllPublicStaticMethods(CidrAddressBlock.class);
tester.testAllPublicConstructors(CidrAddressBlock.class);
tester.testAllPublicInstanceMethods(new CidrAddressBlock("::/0"));
}
public void testConstructorWithNetmask() {
CidrAddressBlock b0 = new CidrAddressBlock("22.24.66.0/24");
assertEquals("22.24.66.0", b0.getIp());
assertEquals(24, b0.getNetmask());
}
public void testConstructorPicksNetmask() {
CidrAddressBlock b0 = new CidrAddressBlock("64.132.1.2");
assertEquals(32, b0.getNetmask());
}
public void testConstructorDoesntThrow() {
new CidrAddressBlock("64.132.0.0/16");
new CidrAddressBlock("128.142.217.0/24");
new CidrAddressBlock("35.213.0.0", 16);
new CidrAddressBlock("89.23.164.0", 24);
}
public void testInetAddressConstructor() {
CidrAddressBlock b0 = new CidrAddressBlock(InetAddresses.forString("1.2.3.4"));
assertEquals(32, b0.getNetmask());
assertEquals("1.2.3.4", b0.getIp());
CidrAddressBlock b1 = new CidrAddressBlock("2001:db8::/32");
assertEquals(InetAddresses.forString("2001:db8::"), b1.getInetAddress());
assertEquals(32, b1.getNetmask());
b1 = new CidrAddressBlock("2001:db8::1");
assertEquals(128, b1.getNetmask());
b1 = new CidrAddressBlock(InetAddresses.forString("5ffe::1"));
assertEquals(128, b1.getNetmask());
assertEquals("5ffe:0:0:0:0:0:0:1", b1.getIp());
}
public void testCornerCasesSucceed() {
new CidrAddressBlock("0.0.0.0/32");
new CidrAddressBlock("255.255.255.255/32");
new CidrAddressBlock("255.255.255.254/31");
new CidrAddressBlock("128.0.0.0/1");
new CidrAddressBlock("0.0.0.0/0");
new CidrAddressBlock("::");
new CidrAddressBlock("::/128");
new CidrAddressBlock("::/0");
new CidrAddressBlock("8000::/1");
new CidrAddressBlock("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
new CidrAddressBlock("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
new CidrAddressBlock("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe/127");
}
public void testFailure() {
assertConstructionFails("");
assertConstructionFails("0");
assertConstructionFails("1");
assertConstructionFails("alskjewosvdhfshjwklerjlkj");
assertConstructionFails("lawkejrlaksdj/24");
assertConstructionFails("192.168.34.23/awlejkrhlsdhf");
assertConstructionFails("192.168.34.23/");
assertConstructionFails("192.168.34.23/-1");
assertConstructionFails("192.239.0.0/12");
assertConstructionFails("192.168.223.15/33");
assertConstructionFails("268.23.53.0/24");
assertConstructionFails("192..23.53.0/24");
assertConstructionFails("192..53.0/24");
assertConstructionFails("192.23.230.0/16");
assertConstructionFails("192.23.255.0/16");
assertConstructionFails("192.23.18.1/16");
assertConstructionFails("123.34.111.240/35");
assertConstructionFails("160.32.34.23", 240);
assertConstructionFails("alskjewosvdhfshjwklerjlkj", 24);
assertConstructionFails("160.32.34.23", 1);
assertConstructionFails("2001:db8::1/");
assertConstructionFails("2001:db8::1", -1);
assertConstructionFails("2001:db8::1", 0);
assertConstructionFails("2001:db8::1", 32);
assertConstructionFails("2001:db8::1", 129);
assertConstructionFails("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/127");
}
public void testTruncation() {
ImmutableMap<String, String> netblocks = new ImmutableMap.Builder<String, String>()
// IPv4
.put("1.2.3.4/0", "0.0.0.0/0")
.put("1.2.3.4/24", "1.2.3.0/24")
.put("1.2.3.255/27", "1.2.3.224/27")
.put("1.2.3.255/28", "1.2.3.240/28")
// IPv6
.put("2001:db8::1/0", "::/0")
.put("2001:db8::1/16", "2001::/16")
.put("2001:db8::1/21", "2001:800::/21")
.put("2001:db8::1/22", "2001:c00::/22")
.build();
for (Map.Entry<String, String> pair : netblocks.entrySet()) {
assertConstructionFails(pair.getKey());
assertEquals(
new CidrAddressBlock(pair.getValue()),
CidrAddressBlock.create(pair.getKey()));
assertEquals(
CidrAddressBlock.create(pair.getKey()),
CidrAddressBlock.create(pair.getValue()));
}
}
public void testContains() {
CidrAddressBlock b0 = CidrAddressBlock.create("172.24.255.0/24");
assertTrue(b0.contains(b0));
assertTrue(b0.contains(b0.getIp()));
assertTrue(b0.contains(b0.getInetAddress()));
/*
* Test an "IPv4 compatible" IPv6 address.
*
* "IPv4 compatible" addresses are not IPv4 addresses. They are
* written this way for "convenience" and appear on the wire as
* 128bit address with 96 leading bits of 0.
*/
assertFalse(b0.contains("::172.24.255.0"));
/*
* Test an "IPv4 mapped" IPv6 address.
*
* "IPv4 mapped" addresses in Java create only Inet4Address objects.
* For more detailed history see the discussion of "mapped" addresses
* in com.google.common.net.InetAddresses.
*/
assertTrue(b0.contains("::ffff:172.24.255.0"));
assertFalse(b0.contains((InetAddress) null));
assertFalse(b0.contains((CidrAddressBlock) null));
assertFalse(b0.contains((String) null));
assertFalse(b0.contains("bogus IP address or CIDR block"));
CidrAddressBlock b1 = CidrAddressBlock.create("172.24.255.0/23");
assertFalse(b0.contains(b1));
assertTrue(b1.contains(b0));
CidrAddressBlock b2 = CidrAddressBlock.create("2001:db8::/48");
assertFalse(b0.contains(b2));
assertFalse(b0.contains(b2.getIp()));
assertFalse(b0.contains(b2.getInetAddress()));
assertFalse(b1.contains(b2));
assertFalse(b1.contains(b2.getIp()));
assertFalse(b1.contains(b2.getInetAddress()));
assertFalse(b2.contains(b0));
assertFalse(b2.contains(b0.getIp()));
assertFalse(b2.contains(b0.getInetAddress()));
assertFalse(b2.contains(b1));
assertFalse(b2.contains(b1.getIp()));
assertFalse(b2.contains(b1.getInetAddress()));
assertTrue(b2.contains(b2));
assertTrue(b2.contains(b2.getIp()));
assertTrue(b2.contains(b2.getInetAddress()));
CidrAddressBlock b3 = CidrAddressBlock.create("2001:db8::/32");
assertFalse(b2.contains(b3));
assertTrue(b3.contains(b2));
CidrAddressBlock allIPv4 = CidrAddressBlock.create("0.0.0.0/0");
assertTrue(allIPv4.contains(b0));
assertTrue(allIPv4.contains(b1));
assertFalse(b0.contains(allIPv4));
assertFalse(b1.contains(allIPv4));
assertFalse(allIPv4.contains(b2));
assertFalse(allIPv4.contains(b3));
assertFalse(b2.contains(allIPv4));
assertFalse(b3.contains(allIPv4));
assertFalse(allIPv4.contains("::172.24.255.0"));
assertTrue(allIPv4.contains("::ffff:172.24.255.0"));
CidrAddressBlock allIPv6 = CidrAddressBlock.create("::/0");
assertTrue(allIPv6.contains(b2));
assertTrue(allIPv6.contains(b3));
assertFalse(b2.contains(allIPv6));
assertFalse(b3.contains(allIPv6));
assertFalse(allIPv6.contains(b0));
assertFalse(allIPv6.contains(b1));
assertFalse(b0.contains(allIPv6));
assertFalse(b1.contains(allIPv6));
assertTrue(allIPv6.contains("::172.24.255.0"));
assertFalse(allIPv6.contains("::ffff:172.24.255.0"));
assertFalse(allIPv4.contains(allIPv6));
assertFalse(allIPv6.contains(allIPv4));
}
public void testGetAllOnesAddress() {
// <CIDR block> -> <expected getAllOnesAddress()>
ImmutableMap<String, String> testCases = new ImmutableMap.Builder<String, String>()
.put("172.24.255.0/24", "172.24.255.255")
.put("172.24.0.0/15", "172.25.255.255")
.put("172.24.254.0/23", "172.24.255.255")
.put("172.24.255.0/32", "172.24.255.0")
.put("0.0.0.0/0", "255.255.255.255")
.put("2001:db8::/48", "2001:db8::ffff:ffff:ffff:ffff:ffff")
.put("2001:db8::/32", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff")
.put("2001:db8::/128", "2001:db8::")
.put("::/0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
.build();
for (Map.Entry<String, String> testCase : testCases.entrySet()) {
assertEquals(
InetAddresses.forString(testCase.getValue()),
CidrAddressBlock.create(testCase.getKey()).getAllOnesAddress());
}
}
public void testEqualsAndHashCode() {
CidrAddressBlock b0 = new CidrAddressBlock("172.24.66.0/24");
CidrAddressBlock b1 = new CidrAddressBlock("172.24.66.0", 24);
CidrAddressBlock b2 = new CidrAddressBlock("172.24.0.0/16");
CidrAddressBlock b3 = new CidrAddressBlock("172.24.65.0/24");
assertEquals(b0, b1);
assertEquals(b0, new CidrAddressBlock(b0.toString()));
assertEquals(b0.hashCode(), b1.hashCode());
assertTrue(!b0.equals(b2));
assertTrue(!b0.equals(b3));
b0 = new CidrAddressBlock("2001:db8::/64");
b1 = new CidrAddressBlock("2001:0DB8:0:0::", 64);
b2 = new CidrAddressBlock("2001:db8::/32");
b3 = new CidrAddressBlock("2001:0DB8:0:1::", 64);
assertEquals(b0, b1);
assertEquals(b0, new CidrAddressBlock(b0.toString()));
assertEquals(b0.hashCode(), b1.hashCode());
assertNotEquals(b0, b2);
assertNotEquals(b0, b3);
}
public void testIterate() {
CidrAddressBlock b0 = new CidrAddressBlock("172.24.66.0/24");
int count = 0;
for (InetAddress addr : b0) {
assertTrue(b0.contains(addr));
++count;
}
assertEquals(256, count);
CidrAddressBlock b1 = new CidrAddressBlock("2001:0DB8:0:0::/120");
count = 0;
for (InetAddress addr : b1) {
assertTrue(b1.contains(addr));
++count;
}
assertEquals(256, count);
CidrAddressBlock b2 = new CidrAddressBlock("255.255.255.254/31");
Iterator<InetAddress> i = b2.iterator();
i.next();
i.next();
assertThrows(NoSuchElementException.class, i::next);
}
public void testSerializability() {
SerializableTester.reserializeAndAssert(new CidrAddressBlock("22.24.66.0/24"));
SerializableTester.reserializeAndAssert(new CidrAddressBlock("64.132.1.2"));
SerializableTester.reserializeAndAssert(
new CidrAddressBlock(InetAddresses.forString("1.2.3.4")));
SerializableTester.reserializeAndAssert(new CidrAddressBlock("2001:db8::/32"));
SerializableTester.reserializeAndAssert(new CidrAddressBlock("2001:db8::1"));
SerializableTester.reserializeAndAssert(
new CidrAddressBlock(InetAddresses.forString("5ffe::1")));
}
private static void assertConstructionFails(String ip) {
assertThrows(IllegalArgumentException.class, () -> new CidrAddressBlock(ip));
}
private static void assertConstructionFails(String ip, int netmask) {
assertThrows(IllegalArgumentException.class, () -> new CidrAddressBlock(ip, netmask));
}
}

View file

@ -0,0 +1,76 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.partitionMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link CollectionUtils} */
@RunWith(JUnit4.class)
public class CollectionUtilsTest {
@Test
public void testNullToEmptyMap_leavesNonNullAlone() {
Map<String, Integer> map = ImmutableMap.of("hello", 1);
assertThat(nullToEmpty(map)).isEqualTo(map);
}
@Test
public void testNullToEmptyMap_convertsNullToEmptyMap() {
Map<String, Integer> map = null;
Map<String, Integer> convertedMap = nullToEmpty(map);
assertThat(map).isNull();
assertThat(convertedMap).isNotNull();
assertThat(convertedMap).isEmpty();
}
@Test
public void testPartitionMap() {
Map<String, String> map = ImmutableMap.of("ka", "va", "kb", "vb", "kc", "vc");
assertThat(partitionMap(map, 2)).containsExactlyElementsIn(ImmutableList.of(
ImmutableMap.of("ka", "va", "kb", "vb"),
ImmutableMap.of("kc", "vc")));
}
@Test
public void testPartitionMap_emptyInput() {
assertThat(partitionMap(ImmutableMap.of(), 100)).isEmpty();
}
@Test
public void testPartitionMap_negativePartitionSize() {
assertThrows(IllegalArgumentException.class, () -> partitionMap(ImmutableMap.of("A", "b"), -2));
}
@Test
public void testPartitionMap_nullMap() {
assertThrows(NullPointerException.class, () -> partitionMap(null, 100));
}
@Test
public void testDeadCodeWeDontWantToDelete() {
CollectionUtils.nullToEmpty(HashMultimap.create());
}
}

View file

@ -0,0 +1,225 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.Method;
import java.util.ArrayList;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link ComparingInvocationHandler}. */
@RunWith(JUnit4.class)
public class ComparingInvocationHandlerTest {
static class Dummy {}
interface MyInterface {
String func(int a, String b);
Dummy func();
}
static class MyException extends RuntimeException {
MyException(String msg) {
super(msg);
}
}
static class MyOtherException extends RuntimeException {
MyOtherException(String msg) {
super(msg);
}
}
static final ArrayList<String> log = new ArrayList<>();
static final class MyInterfaceComparingInvocationHandler
extends ComparingInvocationHandler<MyInterface> {
private boolean dummyEqualsResult = true;
private boolean exceptionEqualsResult = true;
MyInterfaceComparingInvocationHandler(MyInterface actual, MyInterface second) {
super(MyInterface.class, actual, second);
}
MyInterfaceComparingInvocationHandler setExeptionsEquals(boolean result) {
this.exceptionEqualsResult = result;
return this;
}
MyInterfaceComparingInvocationHandler setDummyEquals(boolean result) {
this.dummyEqualsResult = result;
return this;
}
@Override
protected void log(Method method, String message) {
log.add(String.format("%s: %s", method.getName(), message));
}
@Override
protected boolean compareResults(Method method, @Nullable Object a, @Nullable Object b) {
if (method.getReturnType().equals(Dummy.class)) {
return dummyEqualsResult;
}
return super.compareResults(method, a, b);
}
@Override
protected String stringifyResult(Method method, @Nullable Object a) {
if (method.getReturnType().equals(Dummy.class)) {
return "dummy";
}
return super.stringifyResult(method, a);
}
@Override
protected boolean compareThrown(Method method, Throwable a, Throwable b) {
return exceptionEqualsResult && super.compareThrown(method, a, b);
}
@Override
protected String stringifyThrown(Method method, Throwable a) {
return String.format("testException(%s)", super.stringifyThrown(method, a));
}
}
private static final String ACTUAL_RESULT = "actual result";
private static final String SECOND_RESULT = "second result";
private final MyInterface myActualMock = mock(MyInterface.class);
private final MyInterface mySecondMock = mock(MyInterface.class);
private MyInterfaceComparingInvocationHandler invocationHandler;
@Before
public void setUp() {
log.clear();
invocationHandler = new MyInterfaceComparingInvocationHandler(myActualMock, mySecondMock);
}
@Test
public void test_actualThrows_logDifference() {
MyInterface comparator = invocationHandler.makeProxy();
MyException myException = new MyException("message");
when(myActualMock.func(3, "str")).thenThrow(myException);
when(mySecondMock.func(3, "str")).thenReturn(SECOND_RESULT);
assertThrows(MyException.class, () -> comparator.func(3, "str"));
assertThat(log)
.containsExactly(
String.format(
"func: Only actual implementation threw exception: testException(%s)",
myException.toString()));
}
@Test
public void test_secondThrows_logDifference() {
MyInterface comparator = invocationHandler.makeProxy();
MyOtherException myOtherException = new MyOtherException("message");
when(myActualMock.func(3, "str")).thenReturn(ACTUAL_RESULT);
when(mySecondMock.func(3, "str")).thenThrow(myOtherException);
assertThat(comparator.func(3, "str")).isEqualTo(ACTUAL_RESULT);
assertThat(log)
.containsExactly(
String.format(
"func: Only second implementation threw exception: testException(%s)",
myOtherException.toString()));
}
@Test
public void test_bothThrowEqual_noLog() {
MyInterface comparator = invocationHandler.setExeptionsEquals(true).makeProxy();
MyException myException = new MyException("actual message");
MyOtherException myOtherException = new MyOtherException("second message");
when(myActualMock.func(3, "str")).thenThrow(myException);
when(mySecondMock.func(3, "str")).thenThrow(myOtherException);
assertThrows(MyException.class, () -> comparator.func(3, "str"));
assertThat(log).isEmpty();
}
@Test
public void test_bothThrowDifferent_logDifference() {
MyInterface comparator = invocationHandler.setExeptionsEquals(false).makeProxy();
MyException myException = new MyException("actual message");
MyOtherException myOtherException = new MyOtherException("second message");
when(myActualMock.func(3, "str")).thenThrow(myException);
when(mySecondMock.func(3, "str")).thenThrow(myOtherException);
assertThrows(MyException.class, () -> comparator.func(3, "str"));
assertThat(log)
.containsExactly(
String.format(
"func: Both implementations threw, but got different exceptions! "
+ "'testException(%s)' vs 'testException(%s)'",
myException.toString(), myOtherException.toString()));
}
@Test
public void test_bothReturnSame_noLog() {
MyInterface comparator = invocationHandler.makeProxy();
when(myActualMock.func(3, "str")).thenReturn(ACTUAL_RESULT);
when(mySecondMock.func(3, "str")).thenReturn(ACTUAL_RESULT);
assertThat(comparator.func(3, "str")).isEqualTo(ACTUAL_RESULT);
assertThat(log).isEmpty();
}
@Test
public void test_bothReturnDifferent_logDifference() {
MyInterface comparator = invocationHandler.makeProxy();
when(myActualMock.func(3, "str")).thenReturn(ACTUAL_RESULT);
when(mySecondMock.func(3, "str")).thenReturn(SECOND_RESULT);
assertThat(comparator.func(3, "str")).isEqualTo(ACTUAL_RESULT);
assertThat(log)
.containsExactly("func: Got different results! 'actual result' vs 'second result'");
}
@Test
public void test_usesOverriddenMethods_noDifference() {
MyInterface comparator = invocationHandler.setDummyEquals(true).makeProxy();
when(myActualMock.func()).thenReturn(new Dummy());
when(mySecondMock.func()).thenReturn(new Dummy());
comparator.func();
assertThat(log).isEmpty();
}
@Test
public void test_usesOverriddenMethods_logDifference() {
MyInterface comparator = invocationHandler.setDummyEquals(false).makeProxy();
when(myActualMock.func()).thenReturn(new Dummy());
when(mySecondMock.func()).thenReturn(new Dummy());
comparator.func();
assertThat(log).containsExactly("func: Got different results! 'dummy' vs 'dummy'");
}
}

View file

@ -0,0 +1,71 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.testing.NullPointerTester;
import com.google.common.util.concurrent.UncheckedExecutionException;
import google.registry.testing.AppEngineRule;
import java.util.function.Function;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link Concurrent}. */
@RunWith(JUnit4.class)
public class ConcurrentTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Test
public void testTransform_emptyList_returnsEmptyList() {
assertThat(Concurrent.transform(ImmutableList.of(), x -> x)).isEmpty();
}
@Test
public void testTransform_addIntegers() {
assertThat(Concurrent.transform(ImmutableList.of(1, 2, 3), input -> input + 1))
.containsExactly(2, 3, 4)
.inOrder();
}
@Test
public void testTransform_throwsException_isSinglyWrappedByUee() {
UncheckedExecutionException e =
assertThrows(
UncheckedExecutionException.class,
() ->
Concurrent.transform(
ImmutableList.of(1, 2, 3),
input -> {
throw new RuntimeException("hello");
}));
assertThat(e).hasCauseThat().isInstanceOf(RuntimeException.class);
assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("hello");
}
@Test
public void testNullness() {
NullPointerTester tester = new NullPointerTester().setDefault(Function.class, x -> x);
tester.testAllPublicStaticMethods(Concurrent.class);
}
}

View file

@ -0,0 +1,97 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.earliestOf;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.latestOf;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import static google.registry.util.DateTimeUtils.leapSafeSubtractYears;
import com.google.common.collect.ImmutableList;
import org.joda.time.DateTime;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DateTimeUtils}. */
@RunWith(JUnit4.class)
public class DateTimeUtilsTest {
ImmutableList<DateTime> sampleDates = ImmutableList.of(
START_OF_TIME, START_OF_TIME.plusDays(1), END_OF_TIME, END_OF_TIME);
@Test
public void testSuccess_earliestOf() {
assertThat(earliestOf(START_OF_TIME, END_OF_TIME)).isEqualTo(START_OF_TIME);
assertThat(earliestOf(sampleDates)).isEqualTo(START_OF_TIME);
}
@Test
public void testSuccess_latestOf() {
assertThat(latestOf(START_OF_TIME, END_OF_TIME)).isEqualTo(END_OF_TIME);
assertThat(latestOf(sampleDates)).isEqualTo(END_OF_TIME);
}
@Test
public void testSuccess_isBeforeOrAt() {
assertThat(isBeforeOrAt(START_OF_TIME, START_OF_TIME.plusDays(1))).isTrue();
assertThat(isBeforeOrAt(START_OF_TIME, START_OF_TIME)).isTrue();
assertThat(isBeforeOrAt(START_OF_TIME.plusDays(1), START_OF_TIME)).isFalse();
}
@Test
public void testSuccess_isAtOrAfter() {
assertThat(isAtOrAfter(START_OF_TIME, START_OF_TIME.plusDays(1))).isFalse();
assertThat(isAtOrAfter(START_OF_TIME, START_OF_TIME)).isTrue();
assertThat(isAtOrAfter(START_OF_TIME.plusDays(1), START_OF_TIME)).isTrue();
}
@Test
public void testSuccess_leapSafeAddYears() {
DateTime startDate = DateTime.parse("2012-02-29T00:00:00Z");
assertThat(startDate.plusYears(4)).isEqualTo(DateTime.parse("2016-02-29T00:00:00Z"));
assertThat(leapSafeAddYears(startDate, 4)).isEqualTo(DateTime.parse("2016-02-28T00:00:00Z"));
}
@Test
public void testSuccess_leapSafeSubtractYears() {
DateTime startDate = DateTime.parse("2012-02-29T00:00:00Z");
assertThat(startDate.minusYears(4)).isEqualTo(DateTime.parse("2008-02-29T00:00:00Z"));
assertThat(leapSafeSubtractYears(startDate, 4))
.isEqualTo(DateTime.parse("2008-02-28T00:00:00Z"));
}
@Test
public void testSuccess_leapSafeSubtractYears_zeroYears() {
DateTime leapDay = DateTime.parse("2012-02-29T00:00:00Z");
assertThat(leapDay.minusYears(0)).isEqualTo(leapDay);
}
@Test
public void testFailure_earliestOfEmpty() {
assertThrows(IllegalArgumentException.class, () -> earliestOf(ImmutableList.of()));
}
@Test
public void testFailure_latestOfEmpty() {
assertThrows(IllegalArgumentException.class, () -> earliestOf(ImmutableList.of()));
}
}

View file

@ -0,0 +1,107 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.util.DiffUtils.prettyPrintEntityDeepDiff;
import static google.registry.util.DiffUtils.prettyPrintSetDiff;
import com.google.common.collect.ImmutableSet;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DiffUtils} */
@RunWith(JUnit4.class)
public class DiffUtilsTest {
@Test
public void test_prettyPrintSetDiff_emptySets() {
assertThat(prettyPrintSetDiff(ImmutableSet.of(), ImmutableSet.of()))
.isEqualTo("NO DIFFERENCES");
}
@Test
public void test_prettyPrintSetDiff_noDifferences() {
assertThat(prettyPrintSetDiff(ImmutableSet.of("c", "x", "m"), ImmutableSet.of("m", "x", "c")))
.isEqualTo("NO DIFFERENCES");
}
@Test
public void test_prettyPrintSetDiff_addedElements() {
assertThat(prettyPrintSetDiff(ImmutableSet.of("z"), ImmutableSet.of("a", "b", "z")))
.isEqualTo("\n ADDED: [a, b]\n FINAL CONTENTS: [a, b, z]");
}
@Test
public void test_prettyPrintSetDiff_removedElements() {
assertThat(prettyPrintSetDiff(ImmutableSet.of("x", "y", "z"), ImmutableSet.of("y")))
.isEqualTo("\n REMOVED: [x, z]\n FINAL CONTENTS: [y]");
}
@Test
public void test_prettyPrintSetDiff_addedAndRemovedElements() {
assertThat(prettyPrintSetDiff(
ImmutableSet.of("a", "b", "c"), ImmutableSet.of("a", "y", "z")))
.isEqualTo("\n ADDED: [y, z]\n REMOVED: [b, c]\n FINAL CONTENTS: [a, y, z]");
}
@Test
public void test_emptyToNullCollection_doesntDisplay() {
Map<String, Object> mapA = new HashMap<>();
mapA.put("a", "jim");
mapA.put("b", null);
Map<String, Object> mapB = new HashMap<>();
mapB.put("a", "tim");
mapB.put("b", ImmutableSet.of());
// This ensures that it is not outputting a diff of b: null -> [].
assertThat(prettyPrintEntityDeepDiff(mapA, mapB)).isEqualTo("a: jim -> tim\n");
}
@Test
public void test_prettyPrintSetDiff_addedAndRemovedElements_objects() {
DummyObject a = DummyObject.create("a");
DummyObject b = DummyObject.create("b");
DummyObject c = DummyObject.create("c");
assertThat(prettyPrintSetDiff(
ImmutableSet.of(a, b), ImmutableSet.of(a, c)))
.isEqualTo("\n"
+ " ADDED:\n"
+ " {c}\n"
+ " REMOVED:\n"
+ " {b}\n"
+ " FINAL CONTENTS:\n"
+ " {a},\n"
+ " {c}");
}
private static class DummyObject {
public String id;
public static DummyObject create(String id) {
DummyObject instance = new DummyObject();
instance.id = id;
return instance;
}
@Override
public String toString() {
return String.format("{%s}", id);
}
}
}

View file

@ -0,0 +1,73 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static google.registry.util.DomainNameUtils.getSecondLevelDomain;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link DomainNameUtils}. */
@RunWith(JUnit4.class)
public class DomainNameUtilsTest {
@Test
public void testCanonicalizeDomainName() {
assertThat(canonicalizeDomainName("foo")).isEqualTo("foo");
assertThat(canonicalizeDomainName("FOO")).isEqualTo("foo");
assertThat(canonicalizeDomainName("foo.tld")).isEqualTo("foo.tld");
assertThat(canonicalizeDomainName("xn--q9jyb4c")).isEqualTo("xn--q9jyb4c");
assertThat(canonicalizeDomainName("XN--Q9JYB4C")).isEqualTo("xn--q9jyb4c");
assertThat(canonicalizeDomainName("みんな")).isEqualTo("xn--q9jyb4c");
assertThat(canonicalizeDomainName("みんな.みんな")).isEqualTo("xn--q9jyb4c.xn--q9jyb4c");
assertThat(canonicalizeDomainName("みんな.foo")).isEqualTo("xn--q9jyb4c.foo");
assertThat(canonicalizeDomainName("foo.みんな")).isEqualTo("foo.xn--q9jyb4c");
assertThat(canonicalizeDomainName("ħ")).isEqualTo("xn--1ea");
}
@Test
public void testCanonicalizeDomainName_acePrefixUnicodeChars() {
assertThrows(IllegalArgumentException.class, () -> canonicalizeDomainName("xn--みんな"));
}
@Test
public void testGetSecondLevelDomain_returnsProperDomain() {
assertThat(getSecondLevelDomain("foo.bar", "bar")).isEqualTo("foo.bar");
assertThat(getSecondLevelDomain("ns1.foo.bar", "bar")).isEqualTo("foo.bar");
assertThat(getSecondLevelDomain("ns1.abc.foo.bar", "bar")).isEqualTo("foo.bar");
assertThat(getSecondLevelDomain("ns1.abc.foo.bar", "foo.bar")).isEqualTo("abc.foo.bar");
}
@Test
public void testGetSecondLevelDomain_insufficientDomainNameDepth() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class, () -> getSecondLevelDomain("bar", "bar"));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("hostName must be at least one level below the tld");
}
@Test
public void testGetSecondLevelDomain_domainNotUnderTld() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class, () -> getSecondLevelDomain("foo.bar", "abc"));
assertThat(thrown).hasMessageThat().isEqualTo("hostName must be under the tld");
}
}

View file

@ -0,0 +1,206 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.StringWriter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link HexDumper}. */
@RunWith(JUnit4.class)
public class HexDumperTest {
@Test
public void testEmpty() {
String input = "";
String output = "[0 bytes total]\n";
assertThat(input).isEmpty();
assertThat(HexDumper.dumpHex(input.getBytes(UTF_8))).isEqualTo(output);
}
@Test
public void testOneLine() {
String input = "hello world";
String output = "[11 bytes total]\n"
+ "00000000 68 65 6c 6c 6f 20 77 6f 72 6c 64 hello world \n";
assertThat(input).hasLength(11);
assertThat(HexDumper.dumpHex(input.getBytes(UTF_8))).isEqualTo(output);
}
@Test
public void testMultiLine() {
String input = ""
+ "\n"
+ "Maids heard the goblins cry:\n"
+ "\"Come buy our orchard fruits,\n"
+ "\"Come buy, come buy:\n";
String output = "[81 bytes total]\n"
+ "00000000 0a 4d 61 69 64 73 20 68 65 61 72 64 20 74 68 65 .Maids heard the\n"
+ "00000016 20 67 6f 62 6c 69 6e 73 20 63 72 79 3a 0a 22 43 goblins cry:.\"C\n"
+ "00000032 6f 6d 65 20 62 75 79 20 6f 75 72 20 6f 72 63 68 ome buy our orch\n"
+ "00000048 61 72 64 20 66 72 75 69 74 73 2c 0a 22 43 6f 6d ard fruits,.\"Com\n"
+ "00000064 65 20 62 75 79 2c 20 63 6f 6d 65 20 62 75 79 3a e buy, come buy:\n"
+ "00000080 0a . \n";
assertThat(input).hasLength(81);
assertThat(HexDumper.dumpHex(input.getBytes(UTF_8))).isEqualTo(output);
}
@Test
public void testFullLine() {
String input = "hello worldddddd";
String output = "[16 bytes total]\n"
+ "00000000 68 65 6c 6c 6f 20 77 6f 72 6c 64 64 64 64 64 64 hello worldddddd\n";
assertThat(input).hasLength(16);
assertThat(HexDumper.dumpHex(input.getBytes(UTF_8))).isEqualTo(output);
}
@Test
public void testUnicode() {
String input = "(◕‿◕)";
String output = "[11 bytes total]\n"
+ "00000000 28 e2 97 95 e2 80 bf e2 97 95 29 (.........) \n";
assertThat(input).hasLength(5);
assertThat(HexDumper.dumpHex(input.getBytes(UTF_8))).isEqualTo(output);
}
@Test
public void testRainbow() {
byte[] input = new byte[256];
for (int n = 0; n < 256; ++n) {
input[n] = (byte) n;
}
String output = "[256 bytes total]\n"
+ "00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................\n"
+ "00000016 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ................\n"
+ "00000032 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !\"#$%&'()*+,-./\n"
+ "00000048 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?\n"
+ "00000064 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO\n"
+ "00000080 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\\]^_\n"
+ "00000096 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno\n"
+ "00000112 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.\n"
+ "00000128 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f ................\n"
+ "00000144 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f ................\n"
+ "00000160 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af ................\n"
+ "00000176 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf ................\n"
+ "00000192 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf ................\n"
+ "00000208 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df ................\n"
+ "00000224 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef ................\n"
+ "00000240 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff ................\n";
assertThat(HexDumper.dumpHex(input)).isEqualTo(output);
}
@Test
public void testLineBuffering() throws Exception {
// Assume that we have some data that's N bytes long.
byte[] data = "Sweet to tongue and sound to eye; Come buy, come buy.".getBytes(UTF_8);
// And a streaming HexDumper that displays N+1 characters per row.
int perLine = data.length + 1;
try (StringWriter out = new StringWriter();
HexDumper dumper = new HexDumper(out, perLine, 0)) {
// Constructing the object does not cause it to write anything to our upstream device.
assertThat(out.toString()).isEmpty();
// And then we write N bytes to the hex dumper.
dumper.write(data);
// But it won't output any hex because it's buffering a line of output internally.
assertThat(out.toString()).isEmpty();
// But one more byte will bring the total to N+1, thereby flushing a line of hexdump output.
dumper.write(0);
assertThat(out.toString())
.isEqualTo(
"00000000 53 77 65 65 74 20 74 6f 20 74 6f 6e 67 75 65 20 61 6e 64 20 73 6f "
+ "75 6e 64 20 74 6f 20 65 79 65 3b 20 43 6f 6d 65 20 62 75 79 2c 20 63 6f 6d 65 "
+ "20 62 75 79 2e 00 Sweet to tongue and sound to eye; Come buy, come buy..\n");
// No additional data will need to be written upon close.
int oldTotal = out.toString().length();
assertThat(out.toString().length()).isEqualTo(oldTotal);
}
}
@Test
public void testFlush() throws Exception {
try (StringWriter out = new StringWriter();
HexDumper dumper = new HexDumper(out)) {
dumper.write("hello ".getBytes(UTF_8));
assertThat(out.toString()).isEmpty();
dumper.flush();
assertThat(out.toString()).isEqualTo("00000000 68 65 6c 6c 6f 20 ");
dumper.write("world".getBytes(UTF_8));
assertThat(out.toString()).isEqualTo("00000000 68 65 6c 6c 6f 20 ");
dumper.flush();
assertThat(out.toString()).isEqualTo("00000000 68 65 6c 6c 6f 20 77 6f 72 6c 64 ");
dumper.close();
assertThat(out.toString()).isEqualTo(
"00000000 68 65 6c 6c 6f 20 77 6f 72 6c 64 hello world \n");
}
}
@Test
public void testPerLineIsOne() {
String input = "hello";
String output = "[5 bytes total]\n"
+ "00000000 68 h\n"
+ "00000001 65 e\n"
+ "00000002 6c l\n"
+ "00000003 6c l\n"
+ "00000004 6f o\n";
assertThat(HexDumper.dumpHex(input.getBytes(UTF_8), 1, 0)).isEqualTo(output);
}
@Test
public void testBadArgumentPerLineZero() {
HexDumper.dumpHex(new byte[1], 1, 0);
assertThrows(IllegalArgumentException.class, () -> HexDumper.dumpHex(new byte[1], 0, 0));
}
@Test
public void testBadArgumentPerLineNegative() {
HexDumper.dumpHex(new byte[1], 1, 0);
assertThrows(IllegalArgumentException.class, () -> HexDumper.dumpHex(new byte[1], -1, 0));
}
@Test
public void testBadArgumentPerGroupNegative() {
HexDumper.dumpHex(new byte[1], 1, 0);
assertThrows(IllegalArgumentException.class, () -> HexDumper.dumpHex(new byte[1], 1, -1));
}
@Test
public void testBadArgumentPerGroupGreaterThanOrEqualToPerLine() {
HexDumper.dumpHex(new byte[1], 1, 0);
HexDumper.dumpHex(new byte[1], 2, 1);
assertThrows(IllegalArgumentException.class, () -> HexDumper.dumpHex(new byte[1], 1, 1));
}
@Test
public void testBadArgumentBytesIsNull() {
HexDumper.dumpHex(new byte[1]);
assertThrows(NullPointerException.class, () -> HexDumper.dumpHex(null));
}
@Test
public void testMultiClose() throws Exception {
try (StringWriter out = new StringWriter();
HexDumper dumper = new HexDumper(out)) {
dumper.close();
dumper.close();
out.close();
}
}
}

View file

@ -0,0 +1,263 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static google.registry.testing.SystemInfo.hasCommand;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assume.assumeTrue;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** System integration tests for {@link PosixTarHeader}. */
@RunWith(JUnit4.class)
public class PosixTarHeaderSystemTest {
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
@Test
@Ignore
public void testCreateSingleFileArchive() throws Exception {
assumeTrue(hasCommand("tar"));
// We have some data (in memory) that we'll call hello.txt.
String fileName = "hello.txt";
byte[] fileData = "hello world\n".getBytes(UTF_8);
// We're going to put it in a new tar archive (on the filesystem) named hello.tar.
String tarName = "hello.tar";
File tarFile = folder.newFile(tarName);
try (FileOutputStream output = new FileOutputStream(tarFile)) {
output.write(new PosixTarHeader.Builder()
.setName(fileName)
.setSize(fileData.length)
.build()
.getBytes());
output.write(fileData);
output.write(new byte[512 - fileData.length % 512]); // Align with 512-byte block size.
output.write(new byte[1024]); // Bunch of null bytes to indicate end of archive.
}
assertThat(tarFile.length() % 512).isEqualTo(0);
assertThat(tarFile.length() / 512).isEqualTo(2 + 2);
// Now we run the system's tar command to extract our file.
String[] cmd = {"tar", "-xf", tarName};
String[] env = {"PATH=" + System.getenv("PATH")};
File cwd = folder.getRoot();
Process pid = Runtime.getRuntime().exec(cmd, env, cwd);
String err = CharStreams.toString(new InputStreamReader(pid.getErrorStream(), UTF_8));
assertThat(pid.waitFor()).isEqualTo(0);
assertThat(err.trim()).isEmpty();
// And verify that hello.txt came out.
File dataFile = new File(cwd, fileName);
assertThat(dataFile.exists()).isTrue();
assertThat(dataFile.isFile()).isTrue();
assertThat(Files.asByteSource(dataFile).read()).isEqualTo(fileData);
// And that nothing else came out.
Set<String> expectedFiles = ImmutableSet.of(tarName, fileName);
assertThat(ImmutableSet.copyOf(folder.getRoot().list())).isEqualTo(expectedFiles);
}
@Test
@Ignore
public void testCreateMultiFileArchive() throws Exception {
assumeTrue(hasCommand("tar"));
Map<String, String> files = ImmutableMap.of(
"one.txt", ""
+ "There is data on line one\n"
+ "and on line two\n"
+ "and on line three\n",
"two.txt", ""
+ "There is even more data\n"
+ "in this second file\n"
+ "with its own three lines\n",
"subdir/three.txt", ""
+ "More data\n"
+ "but only two lines\n");
String tarName = "hello.tar";
File tarFile = folder.newFile(tarName);
try (FileOutputStream output = new FileOutputStream(tarFile)) {
for (String name : files.keySet()) {
byte[] data = files.get(name).getBytes(UTF_8);
output.write(new PosixTarHeader.Builder()
.setName(name)
.setSize(data.length)
.build()
.getBytes());
output.write(data);
output.write(new byte[512 - data.length % 512]);
}
output.write(new byte[1024]);
}
assertThat(tarFile.length() % 512).isEqualTo(0);
assertThat(tarFile.length() / 512).isEqualTo(files.size() * 2 + 2);
String[] cmd = {"tar", "-xf", tarName};
String[] env = {"PATH=" + System.getenv("PATH")};
File cwd = folder.getRoot();
Process pid = Runtime.getRuntime().exec(cmd, env, cwd);
String err = CharStreams.toString(new InputStreamReader(pid.getErrorStream(), UTF_8));
assertThat(pid.waitFor()).isEqualTo(0);
assertThat(err.trim()).isEmpty();
for (String name : files.keySet()) {
File file = new File(folder.getRoot(), name);
assertWithMessage(name + " exists").that(file.exists()).isTrue();
assertWithMessage(name + " is a file").that(file.isFile()).isTrue();
byte[] data = files.get(name).getBytes(UTF_8);
assertThat(Files.asByteSource(file).read()).isEqualTo(data);
}
}
@Test
@Ignore
public void testReadArchiveUstar() throws Exception {
assumeTrue(hasCommand("tar"));
String one = "the first line";
String two = "the second line";
File cwd = folder.getRoot();
Files.write(one.getBytes(UTF_8), new File(cwd, "one"));
Files.write(two.getBytes(UTF_8), new File(cwd, "two"));
String[] cmd = {"tar", "--format=ustar", "-cf", "lines.tar", "one", "two"};
String[] env = {"PATH=" + System.getenv("PATH")};
Process pid = Runtime.getRuntime().exec(cmd, env, cwd);
String err = CharStreams.toString(new InputStreamReader(pid.getErrorStream(), UTF_8));
assertThat(pid.waitFor()).isEqualTo(0);
assertThat(err.trim()).isEmpty();
PosixTarHeader header;
byte[] block = new byte[512];
try (FileInputStream input = new FileInputStream(new File(cwd, "lines.tar"))) {
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getType()).isEqualTo(PosixTarHeader.Type.REGULAR);
assertThat(header.getName()).isEqualTo("one");
assertThat(header.getSize()).isEqualTo(one.length());
assertThat(input.read(block)).isEqualTo(512);
assertThat(one).isEqualTo(new String(block, 0, one.length(), UTF_8));
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getType()).isEqualTo(PosixTarHeader.Type.REGULAR);
assertThat(header.getName()).isEqualTo("two");
assertThat(header.getSize()).isEqualTo(two.length());
assertThat(input.read(block)).isEqualTo(512);
assertThat(two).isEqualTo(new String(block, 0, two.length(), UTF_8));
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
}
}
@Test
@Ignore
public void testReadArchiveDefaultFormat() throws Exception {
assumeTrue(hasCommand("tar"));
String truth = "No one really knows\n";
Files.write(truth.getBytes(UTF_8), folder.newFile("truth.txt"));
String[] cmd = {"tar", "-cf", "steam.tar", "truth.txt"};
String[] env = {"PATH=" + System.getenv("PATH")};
File cwd = folder.getRoot();
Process pid = Runtime.getRuntime().exec(cmd, env, cwd);
String err = CharStreams.toString(new InputStreamReader(pid.getErrorStream(), UTF_8));
assertThat(pid.waitFor()).isEqualTo(0);
assertThat(err.trim()).isEmpty();
PosixTarHeader header;
byte[] block = new byte[512];
try (FileInputStream input = new FileInputStream(new File(cwd, "steam.tar"))) {
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getType()).isEqualTo(PosixTarHeader.Type.REGULAR);
assertThat(header.getName()).isEqualTo("truth.txt");
assertThat(header.getSize()).isEqualTo(truth.length());
assertThat(input.read(block)).isEqualTo(512);
assertThat(truth).isEqualTo(new String(block, 0, truth.length(), UTF_8));
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
}
}
@Test
@Ignore
public void testCreateBigWebScaleData() throws Exception {
assumeTrue(hasCommand("tar"));
String name = "rando_numberissian.mov";
byte[] data = new byte[4 * 1024 * 1024];
Random rand = new Random();
rand.nextBytes(data);
String tarName = "occupy.tar";
File tarFile = folder.newFile(tarName);
try (FileOutputStream output = new FileOutputStream(tarFile)) {
output.write(new PosixTarHeader.Builder()
.setName(name)
.setSize(data.length)
.build()
.getBytes());
output.write(data);
output.write(new byte[1024]);
}
assertThat(tarFile.length() % 512).isEqualTo(0);
String[] cmd = {"tar", "-xf", tarName};
String[] env = {"PATH=" + System.getenv("PATH")};
File cwd = folder.getRoot();
Process pid = Runtime.getRuntime().exec(cmd, env, cwd);
String err = CharStreams.toString(new InputStreamReader(pid.getErrorStream(), UTF_8));
assertThat(pid.waitFor()).isEqualTo(0);
assertThat(err.trim()).isEmpty();
File dataFile = new File(cwd, name);
assertThat(dataFile.exists()).isTrue();
assertThat(dataFile.isFile()).isTrue();
assertThat(Files.asByteSource(dataFile).read()).isEqualTo(data);
Set<String> expectedFiles = ImmutableSet.of(tarName, name);
assertThat(ImmutableSet.copyOf(folder.getRoot().list())).isEqualTo(expectedFiles);
}
}

View file

@ -0,0 +1,477 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.io.BaseEncoding.base64;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static google.registry.testing.JUnitBackports.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.testing.EqualsTester;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.zip.GZIPInputStream;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link PosixTarHeader}. */
@RunWith(JUnit4.class)
public class PosixTarHeaderTest {
@Test
public void testGnuTarBlob() throws Exception {
// This data was generated as follows:
//
// echo hello kitty >hello.xml
// tar --format=ustar -cf ~/hello.tar hello.xml
// head -c 1024 <hello.tar | base64
//
// As you can see, we're only going to bother with the first 1024 characters.
byte[] gnuTarGeneratedData =
base64()
.decode(
""
+ "aGVsbG8ueG1sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDA2NDAAMDU0MTI2"
+ "NgAwMDExNjEwADAwMDAwMDAwMDE0ADEyMjAyMzEwMzI0ADAxMjQ2MQAgMAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhcgAwMGphcnQAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAZW5nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwADAwMDAw"
+ "MDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABo"
+ "ZWxsbyBraXR0eQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==");
assertThat(gnuTarGeneratedData.length).isEqualTo(1024);
// Now we have to replicate it.
byte[] data = "hello kitty\n".getBytes(UTF_8);
PosixTarHeader header =
new PosixTarHeader.Builder()
.setType(PosixTarHeader.Type.REGULAR)
.setName("hello.xml")
.setSize(data.length)
.setMode(0640)
// This timestamp should have been midnight but I think GNU tar might not understand
// daylight savings time. Woe is me.
.setMtime(DateTime.parse("2013-08-13T01:50:12Z"))
.setUname("jart")
.setGname("eng")
.setUid(180918) // echo $UID
.setGid(5000) // import grp; print grp.getgrnam('eng').gr_gid
.build();
ByteArrayOutputStream output = new ByteArrayOutputStream();
output.write(header.getBytes());
output.write(data);
// Line up with the 512-byte block boundary.
if (data.length % 512 != 0) {
output.write(new byte[512 - data.length % 512]);
}
output.write(new byte[1024]); // Bunch of null bytes to indicate end of archive.
byte[] tarData = output.toByteArray();
assertThat(tarData.length % 512).isEqualTo(0);
data = Arrays.copyOf(tarData, 1024);
// From Wikipedia:
// The checksum is calculated by taking the sum of the unsigned byte values of the header
// record with the eight checksum bytes taken to be ascii spaces (decimal value 32). It is
// stored as a six digit octal number with leading zeroes followed by a NUL and then a space.
// Various implementations do not adhere to this format. For better compatibility, ignore
// leading and trailing whitespace, and get the first six digits. In addition, some historic
// tar implementations treated bytes as signed. Implementations typically calculate the
// checksum both ways, and treat it as good if either the signed or unsigned sum matches the
// included checksum.
data[155] = ' ';
// Compare everything in the arrays except for the checksum. That way we know what's causing
// the checksum to fail.
byte[] gnuTarGeneratedDataNoChksum = gnuTarGeneratedData.clone();
Arrays.fill(gnuTarGeneratedDataNoChksum, 148, 148 + 8, (byte) 0);
byte[] dataNoChksum = data.clone();
Arrays.fill(dataNoChksum, 148, 148 + 8, (byte) 0);
assertThat(dataNoChksum).isEqualTo(gnuTarGeneratedDataNoChksum);
// Now do it again with the checksum.
assertThat(data).isEqualTo(gnuTarGeneratedData);
}
@Test
public void testFields() {
PosixTarHeader header =
new PosixTarHeader.Builder()
.setType(PosixTarHeader.Type.REGULAR)
.setName("(◕‿◕).txt")
.setSize(666)
.setMode(0777)
.setMtime(DateTime.parse("1984-12-18T04:20:00Z"))
.setUname("everything i ever touched")
.setGname("everything i ever had, has died")
.setUid(180918)
.setGid(5000)
.build();
assertThat(header.getType()).isEqualTo(PosixTarHeader.Type.REGULAR);
assertThat(header.getName()).isEqualTo("(◕‿◕).txt");
assertThat(header.getSize()).isEqualTo(666);
assertThat(header.getMode()).isEqualTo(0777);
assertThat(header.getUname()).isEqualTo("everything i ever touched");
assertThat(header.getGname()).isEqualTo("everything i ever had, has died");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(5000);
assertThat(header.getMtime()).isEqualTo(DateTime.parse("1984-12-18T04:20:00Z"));
assertThat(header.getMagic()).isEqualTo("ustar");
assertThat(header.getVersion()).isEqualTo("00");
}
@Test
public void testFieldsSomeMoar() {
PosixTarHeader header =
new PosixTarHeader.Builder()
.setType(PosixTarHeader.Type.DIRECTORY)
.setName("Black lung full of fumes, choke on memories")
.setSize(1024 * 1024 * 1024)
.setMode(31337)
.setMtime(DateTime.parse("2020-12-18T04:20:00Z"))
.setUname("every street i ever walked")
.setGname("every home i ever had, is lost")
.setUid(0)
.setGid(31337)
.build();
assertThat(header.getType()).isEqualTo(PosixTarHeader.Type.DIRECTORY);
assertThat(header.getName()).isEqualTo("Black lung full of fumes, choke on memories");
assertThat(header.getSize()).isEqualTo(1024 * 1024 * 1024);
assertThat(header.getMode()).isEqualTo(31337);
assertThat(header.getUname()).isEqualTo("every street i ever walked");
assertThat(header.getGname()).isEqualTo("every home i ever had, is lost");
assertThat(header.getUid()).isEqualTo(0);
assertThat(header.getGid()).isEqualTo(31337);
assertThat(header.getMtime()).isEqualTo(DateTime.parse("2020-12-18T04:20:00Z"));
}
@Test
public void testLoad() {
PosixTarHeader header =
new PosixTarHeader.Builder()
.setType(PosixTarHeader.Type.REGULAR)
.setName("(◕‿◕).txt")
.setSize(31337)
.setMode(0777)
.setMtime(DateTime.parse("1984-12-18T04:20:00Z"))
.setUname("everything i ever touched")
.setGname("everything i ever had, has died")
.setUid(180918)
.setGid(5000)
.build();
header = PosixTarHeader.from(header.getBytes()); // <-- Pay attention to this line.
assertThat(header.getType()).isEqualTo(PosixTarHeader.Type.REGULAR);
assertThat(header.getName()).isEqualTo("(◕‿◕).txt");
assertThat(header.getSize()).isEqualTo(31337);
assertThat(header.getMode()).isEqualTo(0777);
assertThat(header.getUname()).isEqualTo("everything i ever touched");
assertThat(header.getGname()).isEqualTo("everything i ever had, has died");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(5000);
assertThat(header.getMtime()).isEqualTo(DateTime.parse("1984-12-18T04:20:00Z"));
assertThat(header.getMagic()).isEqualTo("ustar");
assertThat(header.getVersion()).isEqualTo("00");
}
@Test
public void testBadChecksum() {
PosixTarHeader header =
new PosixTarHeader.Builder().setName("(◕‿◕).txt").setSize(31337).build();
byte[] bytes = header.getBytes();
bytes[150] = '0';
bytes[151] = '0';
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () -> PosixTarHeader.from(bytes));
assertThat(thrown).hasMessageThat().contains("chksum invalid");
}
@Test
public void testHashEquals() {
new EqualsTester()
.addEqualityGroup(
new PosixTarHeader.Builder()
.setName("(◕‿◕).txt")
.setSize(123)
.setMtime(DateTime.parse("1984-12-18TZ"))
.setUid(1234)
.setGid(456)
.setUname("jart")
.setGname("wheel")
.build(),
new PosixTarHeader.Builder()
.setName("(◕‿◕).txt")
.setSize(123)
.setMtime(DateTime.parse("1984-12-18TZ"))
.setUid(1234)
.setGid(456)
.setUname("jart")
.setGname("wheel")
.build())
.addEqualityGroup(
new PosixTarHeader.Builder()
.setName("(•︵•).txt") // Awwwww! It looks so sad...
.setSize(123)
.build())
.testEquals();
}
@Test
public void testReadBsdTarFormatUstar() throws Exception {
// $ tar --version
// bsdtar 2.8.3 - libarchive 2.8.3
// echo no rain can wash away my tears >liketears
// echo no wind can soothe my pain >inrain
// chmod 0600 liketears inrain
// tar --format=ustar -c liketears inrain | gzip | base64
InputStream input =
new GZIPInputStream(
new ByteArrayInputStream(
base64()
.decode(
"H4sIAM17DVIAA+3T0QqCMBTGca97ivMIx03n84waaNkMNcS3T4OCbuymFcH/dzc22Dd2vrY5h"
+ "TH4fsjSUVWnKllZ5MY5Wde6rvXBVpIbo9ZUpnKFaG7VlZlowkxP12H0/RLl6Ptx69xUh"
+ "9Bu7L8+Sj4bMp3YSe+bKHsfZfJDLX7ys5xnuQ/F7tfxkFgT1+9Pe8f7/ttn/12hS/+NL"
+ "Sr6/w1L/6cmHu79H7purMNa/ssyE3QfAAAAAAAAAAAAAADgH9wAqAJg4gAoAAA=")));
PosixTarHeader header;
byte[] block = new byte[512];
String likeTears = "no rain can wash away my tears\n";
String inRain = "no wind can soothe my pain\n";
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getName()).isEqualTo("liketears");
assertThat(header.getSize()).isEqualTo(likeTears.length());
assertThat(header.getMode()).isEqualTo(0600);
assertThat(header.getUname()).isEqualTo("jart");
assertThat(header.getGname()).isEqualTo("wheel");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(0);
assertThat(header.getMtime().toString(ISODateTimeFormat.date())).isEqualTo("2013-08-16");
assertThat(input.read(block)).isEqualTo(512);
assertThat(new String(block, 0, likeTears.length(), UTF_8)).isEqualTo(likeTears);
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getName()).isEqualTo("inrain");
assertThat(header.getSize()).isEqualTo(inRain.length());
assertThat(header.getMode()).isEqualTo(0600);
assertThat(header.getUname()).isEqualTo("jart");
assertThat(header.getGname()).isEqualTo("wheel");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(0);
assertThat(header.getMtime().toString(ISODateTimeFormat.date())).isEqualTo("2013-08-16");
assertThat(input.read(block)).isEqualTo(512);
assertThat(new String(block, 0, inRain.length(), UTF_8)).isEqualTo(inRain);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
}
@Test
public void testReadBsdTarFormatDefault() throws Exception {
// $ tar --version
// bsdtar 2.8.3 - libarchive 2.8.3
// echo no rain can wash away my tears >liketears
// echo no wind can soothe my pain >inrain
// chmod 0600 liketears inrain
// tar -c liketears inrain | gzip | base64
InputStream input =
new GZIPInputStream(
new ByteArrayInputStream(
base64()
.decode(
"H4sIAM17DVIAA+3T0QqCMBTGca97ivMIx03n84waaNkMNcS3T4OCbuymFcH/dzc22Dd2vrY5h"
+ "TH4fsjSUVWnKllZ5MY5Wde6rvXBVpIbo9ZUpnKFaG7VlZlowkxP12H0/RLl6Ptx69xUh"
+ "9Bu7L8+Sj4bMp3YSe+bKHsfZfJDLX7ys5xnuQ/F7tfxkFgT1+9Pe8f7/ttn/12hS/+NL"
+ "Sr6/w1L/6cmHu79H7purMNa/ssyE3QfAAAAAAAAAAAAAADgH9wAqAJg4gAoAAA=")));
PosixTarHeader header;
byte[] block = new byte[512];
String likeTears = "no rain can wash away my tears\n";
String inRain = "no wind can soothe my pain\n";
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getName()).isEqualTo("liketears");
assertThat(header.getSize()).isEqualTo(likeTears.length());
assertThat(header.getMode()).isEqualTo(0600);
assertThat(header.getUname()).isEqualTo("jart");
assertThat(header.getGname()).isEqualTo("wheel");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(0);
assertThat(header.getMtime().toString(ISODateTimeFormat.date())).isEqualTo("2013-08-16");
assertThat(input.read(block)).isEqualTo(512);
assertThat(new String(block, 0, likeTears.length(), UTF_8)).isEqualTo(likeTears);
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getName()).isEqualTo("inrain");
assertThat(header.getSize()).isEqualTo(inRain.length());
assertThat(header.getMode()).isEqualTo(0600);
assertThat(header.getUname()).isEqualTo("jart");
assertThat(header.getGname()).isEqualTo("wheel");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(0);
assertThat(header.getMtime().toString(ISODateTimeFormat.date())).isEqualTo("2013-08-16");
assertThat(input.read(block)).isEqualTo(512);
assertThat(new String(block, 0, inRain.length(), UTF_8)).isEqualTo(inRain);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
}
@Test
public void testReadGnuTarFormatDefault() throws Exception {
// $ tar --version
// tar (GNU tar) 1.26
// echo no rain can wash away my tears >liketears
// echo no wind can soothe my pain >inrain
// chmod 0600 liketears inrain
// tar -c liketears inrain | gzip | base64
InputStream input =
new GZIPInputStream(
new ByteArrayInputStream(
base64()
.decode(
"H4sIAOB8DVIAA+3TTQ6DIBCGYdY9BUcYUPE8pDWV/mCjNsbbF01jurIr25i8z4ZACAxhvlu4V"
+ "n3l205tRxInoqTIjXUuzY1xRub1WVYqY61ktnSmyJUYWzhRWjasafHset+mUi6+7df2V"
+ "fG8es77Kcu4E7HRrQ9RH33Ug+9q7Qc/6vuo56Y4/Ls8bCzE6fu3veN7/rOP/Lsp/1Jk5"
+ "P8XUv6HEE9z/rum6etqCv8j9QTZBwAAAAAAAAAAAAAA2IMXm3pYMgAoAAA=")));
PosixTarHeader header;
byte[] block = new byte[512];
String likeTears = "no rain can wash away my tears\n";
String inRain = "no wind can soothe my pain\n";
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getName()).isEqualTo("liketears");
assertThat(header.getSize()).isEqualTo(likeTears.length());
assertThat(header.getMode()).isEqualTo(0600);
assertThat(header.getUname()).isEqualTo("jart");
assertThat(header.getGname()).isEqualTo("eng");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(5000);
assertThat(header.getMtime().toString(ISODateTimeFormat.date())).isEqualTo("2013-08-16");
assertThat(input.read(block)).isEqualTo(512);
assertThat(new String(block, 0, likeTears.length(), UTF_8)).isEqualTo(likeTears);
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getName()).isEqualTo("inrain");
assertThat(header.getSize()).isEqualTo(inRain.length());
assertThat(header.getMode()).isEqualTo(0600);
assertThat(header.getUname()).isEqualTo("jart");
assertThat(header.getGname()).isEqualTo("eng");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(5000);
assertThat(header.getMtime().toString(ISODateTimeFormat.date())).isEqualTo("2013-08-16");
assertThat(input.read(block)).isEqualTo(512);
assertThat(new String(block, 0, inRain.length(), UTF_8)).isEqualTo(inRain);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
}
@Test
public void testReadGnuTarFormatUstar() throws Exception {
// $ tar --version
// tar (GNU tar) 1.26
// echo no rain can wash away my tears >liketears
// echo no wind can soothe my pain >inrain
// chmod 0600 liketears inrain
// tar --format=ustar -c liketears inrain | gzip | base64
InputStream input =
new GZIPInputStream(
new ByteArrayInputStream(
base64()
.decode(
"H4sIAOB8DVIAA+3TTQ6DIBCGYdY9BUcYUPE8pDWV/mCjNsbbF01jurIr25i8z4ZACAxhvlu4V"
+ "n3l205tRxInoqTIjXUuzY1xRub1WVYqY61ktnSmyJUYWzhRWjasafHset+mUi6+7df2V"
+ "fG8es77Kcu4E7HRrQ9RH33Ug+9q7Qc/6vuo56Y4/Ls8bCzE6fu3veN7/rOP/Lsp/1Jk5"
+ "P8XUv6HEE9z/rum6etqCv8j9QTZBwAAAAAAAAAAAAAA2IMXm3pYMgAoAAA=")));
PosixTarHeader header;
byte[] block = new byte[512];
String likeTears = "no rain can wash away my tears\n";
String inRain = "no wind can soothe my pain\n";
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getType()).isEqualTo(PosixTarHeader.Type.REGULAR);
assertThat(header.getName()).isEqualTo("liketears");
assertThat(header.getSize()).isEqualTo(likeTears.length());
assertThat(header.getMode()).isEqualTo(0600);
assertThat(header.getUname()).isEqualTo("jart");
assertThat(header.getGname()).isEqualTo("eng");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(5000);
assertThat(header.getMtime().toString(ISODateTimeFormat.date())).isEqualTo("2013-08-16");
assertThat(input.read(block)).isEqualTo(512);
assertThat(new String(block, 0, likeTears.length(), UTF_8)).isEqualTo(likeTears);
assertThat(input.read(block)).isEqualTo(512);
header = PosixTarHeader.from(block);
assertThat(header.getType()).isEqualTo(PosixTarHeader.Type.REGULAR);
assertThat(header.getName()).isEqualTo("inrain");
assertThat(header.getSize()).isEqualTo(inRain.length());
assertThat(header.getMode()).isEqualTo(0600);
assertThat(header.getUname()).isEqualTo("jart");
assertThat(header.getGname()).isEqualTo("eng");
assertThat(header.getUid()).isEqualTo(180918);
assertThat(header.getGid()).isEqualTo(5000);
assertThat(header.getMtime().toString(ISODateTimeFormat.date())).isEqualTo("2013-08-16");
assertThat(input.read(block)).isEqualTo(512);
assertThat(new String(block, 0, inRain.length(), UTF_8)).isEqualTo(inRain);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
assertThat(input.read(block)).isEqualTo(512);
assertWithMessage("End of archive marker corrupt").that(block).isEqualTo(new byte[512]);
}
}

View file

@ -0,0 +1,36 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
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 RegistrarUtils}. */
@RunWith(JUnit4.class)
public class RegistrarUtilsTest {
@Test
public void testNormalizeRegistrarName_letterOrDigitOnly() {
assertThat(RegistrarUtils.normalizeRegistrarName("129abzAZ")).isEqualTo("129abzaz");
}
@Test
public void testNormalizeRegistrarName_hasSymbols() {
assertThat(RegistrarUtils.normalizeRegistrarName("^}129a(bzAZ/:")).isEqualTo("129abzaz");
}
}

View file

@ -0,0 +1,125 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.LogsSubject.assertAboutLogs;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.appengine.api.log.LogQuery;
import com.google.appengine.api.log.LogService;
import com.google.appengine.api.log.RequestLogs;
import com.google.apphosting.api.ApiProxy;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.LoggerConfig;
import com.google.common.testing.TestLogHandler;
import google.registry.testing.AppEngineRule;
import java.util.logging.Level;
import org.junit.After;
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 RequestStatusCheckerImpl}. */
@RunWith(JUnit4.class)
public final class RequestStatusCheckerImplTest {
private static final TestLogHandler logHandler = new TestLogHandler();
private static final RequestStatusChecker requestStatusChecker = new RequestStatusCheckerImpl();
/**
* Matcher for the expected LogQuery in {@link RequestStatusCheckerImpl#isRunning}.
*
* Because LogQuery doesn't have a .equals function, we have to create an actual matcher to make
* sure we have the right argument in our mocks.
*/
private static LogQuery expectedLogQuery(final String requestLogId) {
return argThat(
object -> {
assertThat(object).isInstanceOf(LogQuery.class);
assertThat(object.getRequestIds()).containsExactly(requestLogId);
assertThat(object.getIncludeAppLogs()).isFalse();
assertThat(object.getIncludeIncomplete()).isTrue();
return true;
});
}
@Rule
public AppEngineRule appEngineRule = AppEngineRule.builder().build();
@Before public void setUp() {
LoggerConfig.getConfig(RequestStatusCheckerImpl.class).addHandler(logHandler);
RequestStatusCheckerImpl.logService = mock(LogService.class);
}
@After public void tearDown() {
LoggerConfig.getConfig(RequestStatusCheckerImpl.class).removeHandler(logHandler);
}
// If a logId is unrecognized, it could be that the log hasn't been uploaded yet - so we assume
// it's a request that has just started running recently.
@Test public void testIsRunning_unrecognized() {
when(RequestStatusCheckerImpl.logService.fetch(expectedLogQuery("12345678")))
.thenReturn(ImmutableList.of());
assertThat(requestStatusChecker.isRunning("12345678")).isTrue();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "Queried an unrecognized requestLogId");
}
@Test public void testIsRunning_notFinished() {
RequestLogs requestLogs = new RequestLogs();
requestLogs.setFinished(false);
when(RequestStatusCheckerImpl.logService.fetch(expectedLogQuery("12345678")))
.thenReturn(ImmutableList.of(requestLogs));
assertThat(requestStatusChecker.isRunning("12345678")).isTrue();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "isFinished: false");
}
@Test public void testIsRunning_finished() {
RequestLogs requestLogs = new RequestLogs();
requestLogs.setFinished(true);
when(RequestStatusCheckerImpl.logService.fetch(expectedLogQuery("12345678")))
.thenReturn(ImmutableList.of(requestLogs));
assertThat(requestStatusChecker.isRunning("12345678")).isFalse();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "isFinished: true");
}
@Test public void testGetLogId_returnsRequestLogId() {
String expectedLogId = ApiProxy.getCurrentEnvironment().getAttributes().get(
"com.google.appengine.runtime.request_log_id").toString();
assertThat(requestStatusChecker.getLogId()).isEqualTo(expectedLogId);
}
@Test public void testGetLogId_createsLog() {
requestStatusChecker.getLogId();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "Current requestLogId: ");
}
}

View file

@ -0,0 +1,128 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeSleeper;
import google.registry.util.Retrier.FailureReporter;
import java.util.concurrent.Callable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link Retrier}. */
@RunWith(JUnit4.class)
public class RetrierTest {
Retrier retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
/** An exception to throw from {@link CountingThrower}. */
static class CountingException extends RuntimeException {
CountingException(int count) {
super("" + count);
}
}
/** Test object that always throws an exception with the current count. */
static class CountingThrower implements Callable<Integer> {
int count = 0;
final int numThrows;
CountingThrower(int numThrows) {
this.numThrows = numThrows;
}
@Override
public Integer call() {
if (count == numThrows) {
return numThrows;
}
count++;
throw new CountingException(count);
}
}
static class TestReporter implements FailureReporter {
int numBeforeRetry = 0;
@Override
public void beforeRetry(Throwable e, int failures, int maxAttempts) {
numBeforeRetry++;
assertThat(failures).isEqualTo(numBeforeRetry);
}
}
@Test
public void testRetryableException() {
CountingException thrown =
assertThrows(
CountingException.class,
() -> retrier.callWithRetry(new CountingThrower(3), CountingException.class));
assertThat(thrown).hasMessageThat().contains("3");
}
@Test
public void testUnretryableException() {
CountingException thrown =
assertThrows(
CountingException.class,
() -> retrier.callWithRetry(new CountingThrower(5), IllegalArgumentException.class));
assertThat(thrown).hasMessageThat().contains("1");
}
@Test
public void testRetrySucceeded() {
assertThat(retrier.callWithRetry(new CountingThrower(2), CountingException.class))
.isEqualTo(2);
}
@Test
public void testRetryFailed_withReporter() {
CountingException thrown =
assertThrows(
CountingException.class,
() -> {
TestReporter reporter = new TestReporter();
try {
retrier.callWithRetry(new CountingThrower(3), reporter, CountingException.class);
} catch (CountingException expected) {
assertThat(reporter.numBeforeRetry).isEqualTo(2);
throw expected;
}
});
assertThat(thrown).hasMessageThat().contains("3");
}
@Test
public void testRetrySucceeded_withReporter() {
TestReporter reporter = new TestReporter();
assertThat(retrier.callWithRetry(new CountingThrower(2), reporter, CountingException.class))
.isEqualTo(2);
assertThat(reporter.numBeforeRetry).isEqualTo(2);
}
@Test
public void testFirstTrySucceeded_withReporter() {
TestReporter reporter = new TestReporter();
assertThat(retrier.callWithRetry(new CountingThrower(0), reporter, CountingException.class))
.isEqualTo(0);
assertThat(reporter.numBeforeRetry).isEqualTo(0);
}
}

View file

@ -0,0 +1,162 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import com.google.common.net.MediaType;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeSleeper;
import google.registry.util.EmailMessage.Attachment;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMultipart;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
/** Unit tests for {@link SendEmailService}. */
@RunWith(JUnit4.class)
public class SendEmailServiceTest {
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
private final Retrier retrier = new Retrier(new FakeSleeper(new FakeClock()), 2);
private final TransportEmailSender wrapper = mock(TransportEmailSender.class);
private final SendEmailService sendEmailService = new SendEmailService(retrier, wrapper);
@Captor private ArgumentCaptor<Message> messageCaptor;
@Test
public void testSuccess_simple() throws Exception {
EmailMessage content = createBuilder().build();
sendEmailService.sendEmail(content);
Message message = getMessage();
assertThat(message.getAllRecipients())
.asList()
.containsExactly(new InternetAddress("fake@example.com"));
assertThat(message.getFrom())
.asList()
.containsExactly(new InternetAddress("registry@example.com"));
assertThat(message.getRecipients(RecipientType.BCC)).isNull();
assertThat(message.getSubject()).isEqualTo("Subject");
assertThat(message.getContentType()).startsWith("multipart/mixed");
assertThat(getInternalContent(message).getContent().toString()).isEqualTo("body");
assertThat(getInternalContent(message).getContentType()).isEqualTo("text/plain; charset=utf-8");
assertThat(((MimeMultipart) message.getContent()).getCount()).isEqualTo(1);
}
@Test
public void testSuccess_bcc() throws Exception {
EmailMessage content = createBuilder().setBcc(new InternetAddress("bcc@example.com")).build();
sendEmailService.sendEmail(content);
Message message = getMessage();
assertThat(message.getRecipients(RecipientType.BCC))
.asList()
.containsExactly(new InternetAddress("bcc@example.com"));
}
@Test
public void testSuccess_contentType() throws Exception {
EmailMessage content = createBuilder().setContentType(MediaType.HTML_UTF_8).build();
sendEmailService.sendEmail(content);
Message message = getMessage();
assertThat(getInternalContent(message).getContentType())
.isEqualTo("text/html; charset=utf-8");
}
@Test
public void testSuccess_attachment() throws Exception {
EmailMessage content =
createBuilder()
.setAttachment(
Attachment.newBuilder()
.setFilename("filename")
.setContent("foo,bar\nbaz,qux")
.setContentType(MediaType.CSV_UTF_8)
.build())
.build();
sendEmailService.sendEmail(content);
Message message = getMessage();
assertThat(((MimeMultipart) message.getContent()).getCount()).isEqualTo(2);
BodyPart attachment = ((MimeMultipart) message.getContent()).getBodyPart(1);
assertThat(attachment.getContent()).isEqualTo("foo,bar\nbaz,qux");
assertThat(attachment.getContentType()).endsWith("name=filename");
}
@Test
public void testSuccess_retry() throws Exception {
doThrow(new MessagingException("hi"))
.doNothing()
.when(wrapper)
.sendMessage(messageCaptor.capture());
EmailMessage content = createBuilder().build();
sendEmailService.sendEmail(content);
assertThat(messageCaptor.getValue().getSubject()).isEqualTo("Subject");
}
@Test
public void testFailure_wrongExceptionType() throws Exception {
doThrow(new RuntimeException("this is a runtime exception")).when(wrapper).sendMessage(any());
EmailMessage content = createBuilder().build();
RuntimeException thrown =
assertThrows(RuntimeException.class, () -> sendEmailService.sendEmail(content));
assertThat(thrown).hasMessageThat().isEqualTo("this is a runtime exception");
}
@Test
public void testFailure_tooManyTries() throws Exception {
doThrow(new MessagingException("hi"))
.doThrow(new MessagingException("second"))
.when(wrapper)
.sendMessage(any());
EmailMessage content = createBuilder().build();
RuntimeException thrown =
assertThrows(RuntimeException.class, () -> sendEmailService.sendEmail(content));
assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("second");
assertThat(thrown).hasCauseThat().isInstanceOf(MessagingException.class);
}
private EmailMessage.Builder createBuilder() throws Exception {
return EmailMessage.newBuilder()
.setFrom(new InternetAddress("registry@example.com"))
.addRecipient(new InternetAddress("fake@example.com"))
.setSubject("Subject")
.setBody("body");
}
private Message getMessage() throws MessagingException {
verify(wrapper).sendMessage(messageCaptor.capture());
return messageCaptor.getValue();
}
private BodyPart getInternalContent(Message message) throws Exception {
return ((MimeMultipart) message.getContent()).getBodyPart(0);
}
}

View file

@ -0,0 +1,66 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.util.SerializeUtils.deserialize;
import static google.registry.util.SerializeUtils.serialize;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link SerializeUtils}. */
@RunWith(JUnit4.class)
public class SerializeUtilsTest {
static class Lol {
@Override
public String toString() {
return "LOL_VALUE";
}
}
@Test
public void testSerialize_nullValue_returnsNull() {
assertThat(serialize(null)).isNull();
}
@Test
public void testDeserialize_nullValue_returnsNull() {
assertThat(deserialize(Object.class, null)).isNull();
}
@Test
public void testSerializeDeserialize_stringValue_maintainsValue() {
assertThat(deserialize(String.class, serialize("hello"))).isEqualTo("hello");
}
@Test
public void testSerialize_objectDoesntImplementSerialize_hasInformativeError() {
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () -> serialize(new Lol()));
assertThat(thrown).hasMessageThat().contains("Unable to serialize: LOL_VALUE");
}
@Test
public void testDeserialize_badValue_hasInformativeError() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> deserialize(String.class, new byte[] {(byte) 0xff}));
assertThat(thrown).hasMessageThat().contains("Unable to deserialize: objectBytes=FF");
}
}

View file

@ -0,0 +1,146 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link SqlTemplate}. */
@RunWith(JUnit4.class)
public class SqlTemplateTest {
@Test
public void testFillSqlTemplate() {
assertThat(
SqlTemplate.create("%TEST%")
.put("TEST", "hello world")
.build())
.isEqualTo("hello world");
assertThat(
SqlTemplate.create("one %TWO% three")
.put("TWO", "2")
.build())
.isEqualTo("one 2 three");
assertThat(
SqlTemplate.create("%ONE% %TWO% %THREE%")
.put("ONE", "1")
.put("TWO", "2")
.put("THREE", "3")
.build())
.isEqualTo("1 2 3");
}
@Test
public void testFillSqlTemplate_substitutionButNoVariables() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class, () -> SqlTemplate.create("").put("ONE", "1").build());
assertThat(thrown).hasMessageThat().contains("Not found in template: ONE");
}
@Test
public void testFillSqlTemplate_substitutionButMissingVariables() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> SqlTemplate.create("%ONE%").put("ONE", "1").put("TWO", "2").build());
assertThat(thrown).hasMessageThat().contains("Not found in template: TWO");
}
@Test
public void testFillSqlTemplate_sameKeyTwice_failsEarly() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> SqlTemplate.create("%ONE%").put("ONE", "1").put("ONE", "2"));
assertThat(thrown).hasMessageThat().contains("");
}
@Test
public void testFillSqlTemplate_variablesButNotEnoughSubstitutions() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> SqlTemplate.create("%ONE% %TWO%").put("ONE", "1").build());
assertThat(thrown).hasMessageThat().contains("%TWO% found in template but no substitution");
}
@Test
public void testFillSqlTemplate_mismatchedVariableAndSubstitution() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> SqlTemplate.create("%TWO%").put("TOO", "2").build());
assertThat(thrown).hasMessageThat().contains("%TWO% found in template but no substitution");
}
@Test
public void testFillSqlTemplate_missingKeyVals_whatsThePoint() {
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () -> SqlTemplate.create("%TWO%").build());
assertThat(thrown).hasMessageThat().contains("%TWO% found in template but no substitution");
}
@Test
public void testFillSqlTemplate_lowercaseKey_notAllowed() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> SqlTemplate.create("%test%").put("test", "hello world").build());
assertThat(thrown).hasMessageThat().contains("Bad substitution key: test");
}
@Test
public void testFillSqlTemplate_substitution_disallowsSingleQuotes() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> SqlTemplate.create("The words are '%LOS%' and baz").put("LOS", "foo'bar"));
assertThat(thrown).hasMessageThat().contains("Illegal characters in foo'bar");
}
@Test
public void testFillSqlTemplate_substitution_disallowsDoubleQuotes() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> SqlTemplate.create("The words are '%LOS%' and baz").put("LOS", "foo\"bar"));
assertThat(thrown).hasMessageThat().contains("Illegal characters in foo\"bar");
}
@Test
public void testFillSqlTemplate_quoteMismatch_throwsError() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
SqlTemplate.create("The words are \"%LOS%' and baz").put("LOS", "foobar").build());
assertThat(thrown).hasMessageThat().contains("Quote mismatch: \"%LOS%'");
}
@Test
public void testFillSqlTemplate_extendedQuote_throwsError() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
SqlTemplate.create("The words are '%LOS%-lol' and baz").put("LOS", "roid").build());
assertThat(thrown).hasMessageThat().contains("Quote mismatch: '%LOS%");
}
}

View file

@ -0,0 +1,145 @@
// Copyright 2018 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.testing.TaskQueueHelper.getQueueInfo;
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.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskHandle;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.taskqueue.TransientFailureException;
import com.google.common.collect.ImmutableList;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeSleeper;
import org.joda.time.DateTime;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link TaskQueueUtils}. */
@RunWith(JUnit4.class)
public final class TaskQueueUtilsTest {
private static final int MAX_RETRIES = 3;
@Rule
public final AppEngineRule appEngine =
AppEngineRule.builder().withDatastore().withTaskQueue().build();
private int origBatchSize;
private final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01TZ"));
private final FakeSleeper sleeper = new FakeSleeper(clock);
private final TaskQueueUtils taskQueueUtils =
new TaskQueueUtils(new Retrier(sleeper, MAX_RETRIES));
private final Queue queue = mock(Queue.class);
private final TaskOptions task = withUrl("url").taskName("name");
private final TaskHandle handle = new TaskHandle(task, "handle");
@Before
public void before() {
origBatchSize = TaskQueueUtils.BATCH_SIZE;
TaskQueueUtils.BATCH_SIZE = 2;
}
@After
public void after() {
TaskQueueUtils.BATCH_SIZE = origBatchSize;
}
@Test
public void testEnqueue_worksOnFirstTry_doesntSleep() {
when(queue.add(ImmutableList.of(task))).thenReturn(ImmutableList.of(handle));
assertThat(taskQueueUtils.enqueue(queue, task)).isSameInstanceAs(handle);
verify(queue).add(ImmutableList.of(task));
assertThat(clock.nowUtc()).isEqualTo(DateTime.parse("2000-01-01TZ"));
}
@Test
public void testEnqueue_twoTransientErrorsThenSuccess_stillWorksAfterSleeping() {
when(queue.add(ImmutableList.of(task)))
.thenThrow(new TransientFailureException(""))
.thenThrow(new TransientFailureException(""))
.thenReturn(ImmutableList.of(handle));
assertThat(taskQueueUtils.enqueue(queue, task)).isSameInstanceAs(handle);
verify(queue, times(3)).add(ImmutableList.of(task));
assertThat(clock.nowUtc()).isEqualTo(DateTime.parse("2000-01-01T00:00:00.6Z")); // 200 + 400ms
}
@Test
public void testEnqueue_multiple() {
TaskOptions taskA = withUrl("a").taskName("a");
TaskOptions taskB = withUrl("b").taskName("b");
ImmutableList<TaskHandle> handles =
ImmutableList.of(new TaskHandle(taskA, "a"), new TaskHandle(taskB, "b"));
when(queue.add(ImmutableList.of(taskA, taskB))).thenReturn(handles);
assertThat(taskQueueUtils.enqueue(queue, ImmutableList.of(taskA, taskB)))
.isSameInstanceAs(handles);
assertThat(clock.nowUtc()).isEqualTo(DateTime.parse("2000-01-01TZ"));
}
@Test
public void testEnqueue_maxRetries_givesUp() {
when(queue.add(ImmutableList.of(task)))
.thenThrow(new TransientFailureException("one"))
.thenThrow(new TransientFailureException("two"))
.thenThrow(new TransientFailureException("three"))
.thenThrow(new TransientFailureException("four"));
TransientFailureException thrown =
assertThrows(TransientFailureException.class, () -> taskQueueUtils.enqueue(queue, task));
assertThat(thrown).hasMessageThat().contains("three");
}
@Test
public void testEnqueue_transientErrorThenInterrupt_throwsTransientError() {
when(queue.add(ImmutableList.of(task))).thenThrow(new TransientFailureException(""));
try {
Thread.currentThread().interrupt();
assertThrows(TransientFailureException.class, () -> taskQueueUtils.enqueue(queue, task));
} finally {
Thread.interrupted(); // Clear interrupt state so it doesn't pwn other tests.
}
}
@Test
public void testDeleteTasks_usesMultipleBatches() {
Queue defaultQ = QueueFactory.getQueue("default");
TaskOptions taskOptA = withUrl("/a").taskName("a");
TaskOptions taskOptB = withUrl("/b").taskName("b");
TaskOptions taskOptC = withUrl("/c").taskName("c");
taskQueueUtils.enqueue(defaultQ, ImmutableList.of(taskOptA, taskOptB, taskOptC));
assertThat(getQueueInfo("default").getTaskInfo()).hasSize(3);
taskQueueUtils.deleteTasks(
defaultQ,
ImmutableList.of(
new TaskHandle(taskOptA, "default"),
new TaskHandle(taskOptB, "default"),
new TaskHandle(taskOptC, "default")));
assertThat(getQueueInfo("default").getTaskInfo()).hasSize(0);
}
}

View file

@ -0,0 +1,87 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link TeeOutputStream}. */
@RunWith(JUnit4.class)
public class TeeOutputStreamTest {
private final ByteArrayOutputStream outputA = new ByteArrayOutputStream();
private final ByteArrayOutputStream outputB = new ByteArrayOutputStream();
private final ByteArrayOutputStream outputC = new ByteArrayOutputStream();
@Test
public void testWrite_writesToMultipleStreams() throws Exception {
// Write shared data using the tee output stream.
try (OutputStream tee =
new TeeOutputStream(asList(outputA, outputB, outputC))) {
tee.write("hello ".getBytes(UTF_8));
tee.write("hello world!".getBytes(UTF_8), 6, 5);
tee.write('!');
}
// Write some more data to the different streams - they should not have been closed.
outputA.write("a".getBytes(UTF_8));
outputB.write("b".getBytes(UTF_8));
outputC.write("c".getBytes(UTF_8));
// Check the results.
assertThat(outputA.toString()).isEqualTo("hello world!a");
assertThat(outputB.toString()).isEqualTo("hello world!b");
assertThat(outputC.toString()).isEqualTo("hello world!c");
}
@Test
@SuppressWarnings("resource")
public void testConstructor_failsWithEmptyIterable() {
assertThrows(IllegalArgumentException.class, () -> new TeeOutputStream(ImmutableSet.of()));
}
@Test
public void testWriteInteger_failsAfterClose() throws Exception {
OutputStream tee = new TeeOutputStream(ImmutableList.of(outputA));
tee.close();
IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> tee.write(1));
assertThat(thrown).hasMessageThat().contains("outputstream closed");
}
@Test
public void testWriteByteArray_failsAfterClose() throws Exception {
OutputStream tee = new TeeOutputStream(ImmutableList.of(outputA));
tee.close();
IllegalStateException thrown =
assertThrows(IllegalStateException.class, () -> tee.write("hello".getBytes(UTF_8)));
assertThat(thrown).hasMessageThat().contains("outputstream closed");
}
@Test
public void testWriteByteSubarray_failsAfterClose() throws Exception {
OutputStream tee = new TeeOutputStream(ImmutableList.of(outputA));
tee.close();
IllegalStateException thrown =
assertThrows(IllegalStateException.class, () -> tee.write("hello".getBytes(UTF_8), 1, 3));
assertThat(thrown).hasMessageThat().contains("outputstream closed");
}
}

View file

@ -0,0 +1,73 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import java.io.Serializable;
import java.util.ArrayList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link TypeUtils}. */
@RunWith(JUnit4.class)
public class TypeUtilsTest {
@Test
public void test_getClassFromString_validClass() {
Class<? extends Serializable> clazz =
TypeUtils.getClassFromString("java.util.ArrayList", Serializable.class);
assertThat(clazz).isEqualTo(ArrayList.class);
}
@Test
public void test_getClassFromString_notAssignableFrom() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> TypeUtils.getClassFromString("java.util.ArrayList", Integer.class));
assertThat(thrown).hasMessageThat().contains("ArrayList does not implement/extend Integer");
}
@Test
public void test_getClassFromString_unknownClass() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> TypeUtils.getClassFromString("com.fake.company.nonexistent.Class", Object.class));
assertThat(thrown)
.hasMessageThat()
.contains("Failed to load class com.fake.company.nonexistent.Class");
}
public static class ExampleClass {
String val;
public ExampleClass(String val) {
this.val = val;
}
}
@Test
public void test_instantiateWithArg() {
Class<ExampleClass> clazz =
TypeUtils.getClassFromString(
"google.registry.util.TypeUtilsTest$ExampleClass", ExampleClass.class);
ExampleClass result = TypeUtils.instantiate(clazz, "test");
assertThat(result.val).isEqualTo("test");
}
}

View file

@ -0,0 +1,109 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
import static com.google.common.net.MediaType.CSV_UTF_8;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.util.UrlFetchUtils.setPayloadMultipart;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPRequest;
import google.registry.testing.AppEngineRule;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
/** Unit tests for {@link UrlFetchUtils}. */
@RunWith(JUnit4.class)
public class UrlFetchUtilsTest {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.build();
private final Random random = mock(Random.class);
@Before
public void setupRandomZeroes() {
doAnswer(
info -> {
Arrays.fill((byte[]) info.getArguments()[0], (byte) 0);
return null;
})
.when(random)
.nextBytes(any(byte[].class));
}
@Test
public void testSetPayloadMultipart() {
HTTPRequest request = mock(HTTPRequest.class);
setPayloadMultipart(
request,
"lol",
"cat",
CSV_UTF_8,
"The nice people at the store say hello. ヘ(◕。◕ヘ)",
random);
ArgumentCaptor<HTTPHeader> headerCaptor = ArgumentCaptor.forClass(HTTPHeader.class);
verify(request, times(2)).addHeader(headerCaptor.capture());
List<HTTPHeader> addedHeaders = headerCaptor.getAllValues();
assertThat(addedHeaders.get(0).getName()).isEqualTo(CONTENT_TYPE);
assertThat(addedHeaders.get(0).getValue())
.isEqualTo(
"multipart/form-data; "
+ "boundary=\"------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"");
assertThat(addedHeaders.get(1).getName()).isEqualTo(CONTENT_LENGTH);
assertThat(addedHeaders.get(1).getValue()).isEqualTo("294");
String payload = "--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n"
+ "Content-Disposition: form-data; name=\"lol\"; filename=\"cat\"\r\n"
+ "Content-Type: text/csv; charset=utf-8\r\n"
+ "\r\n"
+ "The nice people at the store say hello. ヘ(◕。◕ヘ)\r\n"
+ "--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA--\r\n";
verify(request).setPayload(payload.getBytes(UTF_8));
verifyNoMoreInteractions(request);
}
@Test
public void testSetPayloadMultipart_boundaryInPayload() {
HTTPRequest request = mock(HTTPRequest.class);
String payload = "I screamed------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHH";
IllegalStateException thrown =
assertThrows(
IllegalStateException.class,
() -> setPayloadMultipart(request, "lol", "cat", CSV_UTF_8, payload, random));
assertThat(thrown)
.hasMessageThat()
.contains(
"Multipart data contains autogenerated boundary: "
+ "------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
}
}

View file

@ -0,0 +1,69 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.util.YamlUtils.mergeYaml;
import com.google.common.base.Joiner;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link YamlUtils}. */
@RunWith(JUnit4.class)
public class YamlUtilsTest {
@Test
public void testSuccess_mergeSimpleMaps() {
String defaultYaml = join("one: ay", "two: bee", "three: sea");
String customYaml = join("two: dee", "four: ignored");
assertThat(mergeYaml(defaultYaml, customYaml)).isEqualTo("{one: ay, two: dee, three: sea}\n");
}
@Test
public void testSuccess_mergeNestedMaps() {
String defaultYaml = join("non: ay", "nested:", " blah: tim", " neat: 12");
String customYaml = join("nested:", " blah: max", " extra: none");
assertThat(mergeYaml(defaultYaml, customYaml))
.isEqualTo(join("non: ay", "nested: {blah: max, neat: 12}"));
}
@Test
public void testSuccess_listsAreOverridden() {
String defaultYaml = join("non: ay", "list:", " - foo", " - bar", " - baz");
String customYaml = join("list:", " - crackle", " - pop var");
assertThat(mergeYaml(defaultYaml, customYaml))
.isEqualTo(join("non: ay", "list: [crackle, pop var]"));
}
@Test
public void testSuccess_mergeEmptyMap_isNoop() {
String defaultYaml = join("one: ay", "two: bee", "three: sea");
assertThat(mergeYaml(defaultYaml, "# Just a comment\n"))
.isEqualTo("{one: ay, two: bee, three: sea}\n");
}
@Test
public void testSuccess_mergeNamedMap_overwritesEntirelyWithNewKey() {
String defaultYaml = join("one: ay", "two: bee", "threeMap:", " foo: bar", " baz: gak");
assertThat(mergeYaml(defaultYaml, "threeMap: {time: money}"))
.isEqualTo(join("one: ay", "two: bee", "threeMap: {time: money}"));
}
private static String join(CharSequence... strings) {
return Joiner.on('\n').join(strings) + "\n";
}
}