From ee31f1fd95752357f5ff064141106c49c74d924b Mon Sep 17 00:00:00 2001 From: gbrodman Date: Tue, 20 Apr 2021 11:52:53 -0400 Subject: [PATCH] Update various tests to work with SQL as well (#1078) * Update various tests to work with SQL as well The main weird bit here is adding a method in DatabaseHelper to retrieve and initialize all objects in either database. The initialization is necessary since it's used post-command-dry-run to make sure that no changes were actually made. --- .../JpaTransactionManagerImpl.java | 2 + .../google/registry/flows/EppTestCase.java | 3 +- .../google/registry/flows/FlowTestCase.java | 12 +- .../model/CreateAutoTimestampTest.java | 40 ++-- .../registry/testing/DatabaseHelper.java | 56 +++++- .../registry/tmch/LordnTaskUtilsTest.java | 34 ++-- .../tools/CreateRegistrarCommandTest.java | 173 +++++++++--------- .../tools/UpdateCursorsCommandTest.java | 54 +++--- 8 files changed, 220 insertions(+), 154 deletions(-) diff --git a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java index b2e492da5..d75b80804 100644 --- a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java +++ b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java @@ -37,6 +37,7 @@ import google.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex; import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex; import google.registry.model.ofy.DatastoreTransactionManager; import google.registry.model.server.KmsSecret; +import google.registry.model.tmch.ClaimsListShard.ClaimsListSingleton; import google.registry.persistence.JpaRetries; import google.registry.persistence.VKey; import google.registry.util.Clock; @@ -73,6 +74,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager { // TODO(b/176108270): Remove this property after database migration. private static final ImmutableSet> IGNORED_ENTITY_CLASSES = ImmutableSet.of( + ClaimsListSingleton.class, EppResourceIndex.class, ForeignKeyContactIndex.class, ForeignKeyDomainIndex.class, diff --git a/core/src/test/java/google/registry/flows/EppTestCase.java b/core/src/test/java/google/registry/flows/EppTestCase.java index 42537c4ee..38cad6d36 100644 --- a/core/src/test/java/google/registry/flows/EppTestCase.java +++ b/core/src/test/java/google/registry/flows/EppTestCase.java @@ -16,6 +16,7 @@ package google.registry.flows; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType; import static google.registry.testing.DatabaseHelper.stripBillingEventId; import static google.registry.testing.TestDataHelper.loadFile; @@ -216,7 +217,7 @@ public class EppTestCase { "Running " + inputFilename + " => " + outputFilename, "epp.response.resData.infData.roid", "epp.response.trID.svTRID"); - ofy().clearSessionCache(); // Clear the cache like OfyFilter would. + tm().clearSessionCache(); // Clear the cache like OfyFilter would. return actualOutput; } diff --git a/core/src/test/java/google/registry/flows/FlowTestCase.java b/core/src/test/java/google/registry/flows/FlowTestCase.java index 6680940ea..33a8447b4 100644 --- a/core/src/test/java/google/registry/flows/FlowTestCase.java +++ b/core/src/test/java/google/registry/flows/FlowTestCase.java @@ -20,10 +20,11 @@ import static com.google.common.collect.Sets.difference; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static google.registry.model.eppcommon.EppXmlTransformer.marshal; -import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm; import static google.registry.testing.DatabaseHelper.stripBillingEventId; +import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.xml.XmlTestUtils.assertXmlEquals; import static java.nio.charset.StandardCharsets.UTF_8; import static org.joda.time.DateTimeZone.UTC; @@ -41,9 +42,10 @@ import google.registry.model.eppcommon.ProtocolDefinition; import google.registry.model.eppinput.EppInput; import google.registry.model.eppoutput.EppOutput; import google.registry.model.ofy.Ofy; -import google.registry.model.reporting.HistoryEntry; +import google.registry.model.reporting.HistoryEntryDao; import google.registry.monitoring.whitebox.EppMetric; import google.registry.testing.AppEngineExtension; +import google.registry.testing.DatabaseHelper; import google.registry.testing.EppLoader; import google.registry.testing.FakeClock; import google.registry.testing.FakeHttpSession; @@ -172,7 +174,7 @@ public abstract class FlowTestCase { } protected void assertNoHistory() { - assertThat(ofy().load().type(HistoryEntry.class)).isEmpty(); + assertThat(HistoryEntryDao.loadAllHistoryObjects(START_OF_TIME, END_OF_TIME)).isEmpty(); } /** @@ -301,10 +303,10 @@ public abstract class FlowTestCase { private TmchCaMode tmchCaMode = TmchCaMode.PILOT; public EppOutput dryRunFlowAssertResponse(String xml, String... ignoredPaths) throws Exception { - List beforeEntities = ofy().load().list(); + List beforeEntities = DatabaseHelper.loadAllEntities(); EppOutput output = runFlowAssertResponse(CommitMode.DRY_RUN, UserPrivileges.NORMAL, xml, ignoredPaths); - assertThat(ofy().load()).containsExactlyElementsIn(beforeEntities); + assertThat(DatabaseHelper.loadAllEntities()).containsExactlyElementsIn(beforeEntities); return output; } diff --git a/core/src/test/java/google/registry/model/CreateAutoTimestampTest.java b/core/src/test/java/google/registry/model/CreateAutoTimestampTest.java index c3bd3c7fe..48fdfd7db 100644 --- a/core/src/test/java/google/registry/model/CreateAutoTimestampTest.java +++ b/core/src/test/java/google/registry/model/CreateAutoTimestampTest.java @@ -15,65 +15,69 @@ package google.registry.model; import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.testing.DatabaseHelper.loadByEntity; import static org.joda.time.DateTimeZone.UTC; import com.googlecode.objectify.annotation.Entity; +import com.googlecode.objectify.annotation.Ignore; import google.registry.model.common.CrossTldSingleton; import google.registry.schema.replay.EntityTest.EntityForTesting; import google.registry.testing.AppEngineExtension; +import google.registry.testing.DualDatabaseTest; +import google.registry.testing.TestOfyAndSql; import org.joda.time.DateTime; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** Unit tests for {@link CreateAutoTimestamp}. */ +@DualDatabaseTest public class CreateAutoTimestampTest { @RegisterExtension public final AppEngineExtension appEngine = AppEngineExtension.builder() .withDatastoreAndCloudSql() - .withOfyTestEntities(TestObject.class) + .withOfyTestEntities(CreateAutoTimestampTestObject.class) + .withJpaUnitTestEntities(CreateAutoTimestampTestObject.class) .build(); /** Timestamped class. */ @Entity(name = "CatTestEntity") @EntityForTesting - public static class TestObject extends CrossTldSingleton { + @javax.persistence.Entity + public static class CreateAutoTimestampTestObject extends CrossTldSingleton { + @Ignore @javax.persistence.Id long id = SINGLETON_ID; CreateAutoTimestamp createTime = CreateAutoTimestamp.create(null); } - private TestObject reload() { - return ofy().load().entity(new TestObject()).now(); + private CreateAutoTimestampTestObject reload() { + return loadByEntity(new CreateAutoTimestampTestObject()); } - @Test + @TestOfyAndSql void testSaveSetsTime() { DateTime transactionTime = - tm() - .transact( + tm().transact( () -> { - TestObject object = new TestObject(); + CreateAutoTimestampTestObject object = new CreateAutoTimestampTestObject(); assertThat(object.createTime.getTimestamp()).isNull(); - ofy().save().entity(object); + tm().put(object); return tm().getTransactionTime(); }); - ofy().clearSessionCache(); + tm().clearSessionCache(); assertThat(reload().createTime.timestamp).isEqualTo(transactionTime); } - @Test + @TestOfyAndSql void testResavingRespectsOriginalTime() { final DateTime oldCreateTime = DateTime.now(UTC).minusDays(1); - tm() - .transact( + tm().transact( () -> { - TestObject object = new TestObject(); + CreateAutoTimestampTestObject object = new CreateAutoTimestampTestObject(); object.createTime = CreateAutoTimestamp.create(oldCreateTime); - ofy().save().entity(object); + tm().put(object); }); - ofy().clearSessionCache(); + tm().clearSessionCache(); assertThat(reload().createTime.timestamp).isEqualTo(oldCreateTime); } } diff --git a/core/src/test/java/google/registry/testing/DatabaseHelper.java b/core/src/test/java/google/registry/testing/DatabaseHelper.java index 867e2afbd..138cb37ef 100644 --- a/core/src/test/java/google/registry/testing/DatabaseHelper.java +++ b/core/src/test/java/google/registry/testing/DatabaseHelper.java @@ -116,6 +116,7 @@ import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferStatus; import google.registry.persistence.VKey; import google.registry.tmch.LordnTaskUtils; +import java.lang.reflect.Field; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -124,6 +125,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; import javax.annotation.Nullable; +import org.hibernate.Hibernate; import org.joda.money.CurrencyUnit; import org.joda.money.Money; import org.joda.time.DateTime; @@ -339,8 +341,7 @@ public class DatabaseHelper { /** Persists a domain and enqueues a LORDN task of the appropriate type for it. */ public static DomainBase persistDomainAndEnqueueLordn(final DomainBase domain) { final DomainBase persistedDomain = persistResource(domain); - // Calls {@link LordnTaskUtils#enqueueDomainBaseTask} wrapped in an ofy transaction so that - // the + // Calls {@link LordnTaskUtils#enqueueDomainBaseTask} wrapped in a transaction so that the // transaction time is set correctly. tm().transactNew(() -> LordnTaskUtils.enqueueDomainBaseTask(persistedDomain)); maybeAdvanceClock(); @@ -995,8 +996,8 @@ public class DatabaseHelper { * Persists a test resource to Datastore and returns it. * *

Tests should always use this method (or the shortcut persist methods in this class) to - * persist test data, to avoid potentially subtle bugs related to race conditions and a stale - * ofy() session cache. Specifically, this method calls .now() on the save to force the write to + * persist test data, to avoid potentially subtle bugs related to race conditions and a stale ofy + * session cache. Specifically, this method calls .now() on the save to force the write to * actually get sent to Datastore (although it does not force it to be applied) and clears the * session cache. If necessary, this method also updates the relevant {@link EppResourceIndex}, * {@link ForeignKeyIndex}. @@ -1281,6 +1282,53 @@ public class DatabaseHelper { clientId); } + /** + * Loads all entities from all classes stored in the database. + * + *

This is not performant (it requires initializing and detaching all Hibernate entities so + * that they can be used outside of the transaction in which they're loaded) and it should only be + * used in situations where we need to verify that, for instance, a dry run flow hasn't affected + * the database at all. + */ + public static List loadAllEntities() { + if (tm().isOfy()) { + return ofy().load().list(); + } else { + return jpaTm() + .transact( + () -> { + ImmutableList> entityClasses = + jpaTm().getEntityManager().getMetamodel().getEntities().stream() + .map(javax.persistence.metamodel.Type::getJavaType) + .collect(toImmutableList()); + ImmutableList.Builder result = new ImmutableList.Builder<>(); + for (Class entityClass : entityClasses) { + for (Object object : jpaTm().loadAllOf(entityClass)) { + // Initialize and detach the objects so they can be used for comparison later + initializeHibernateObject(object); + jpaTm().getEntityManager().detach(object); + result.add(object); + } + } + return result.build(); + }); + } + } + + /** + * Initializes all fields in a Hibernate proxy object so it can be used outside of a transaction. + */ + private static void initializeHibernateObject(Object object) { + for (Field field : object.getClass().getDeclaredFields()) { + field.setAccessible(true); + try { + Hibernate.initialize(field.get(object)); + } catch (IllegalAccessException e) { + // shouldn't happen since we set the field to be accessible + } + } + } + /** * Loads (i.e. reloads) the specified entity from the DB. * diff --git a/core/src/test/java/google/registry/tmch/LordnTaskUtilsTest.java b/core/src/test/java/google/registry/tmch/LordnTaskUtilsTest.java index 5bb571aff..94a826286 100644 --- a/core/src/test/java/google/registry/tmch/LordnTaskUtilsTest.java +++ b/core/src/test/java/google/registry/tmch/LordnTaskUtilsTest.java @@ -15,7 +15,6 @@ package google.registry.tmch; import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.loadRegistrar; @@ -29,23 +28,30 @@ import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.ofy.Ofy; import google.registry.model.registrar.Registrar.Type; import google.registry.testing.AppEngineExtension; +import google.registry.testing.DualDatabaseTest; import google.registry.testing.FakeClock; import google.registry.testing.InjectExtension; import google.registry.testing.TaskQueueHelper.TaskMatcher; +import google.registry.testing.TestOfyAndSql; +import google.registry.testing.TestOfyOnly; import google.registry.util.Clock; import org.joda.time.DateTime; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** Unit tests for {@link LordnTaskUtils}. */ +@DualDatabaseTest public class LordnTaskUtilsTest { private static final Clock clock = new FakeClock(DateTime.parse("2010-05-01T10:11:12Z")); @RegisterExtension public final AppEngineExtension appEngine = - AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build(); + AppEngineExtension.builder() + .withDatastoreAndCloudSql() + .withClock(clock) + .withTaskQueue() + .build(); @RegisterExtension public final InjectExtension inject = new InjectExtension(); @@ -58,12 +64,14 @@ public class LordnTaskUtilsTest { private DomainBase.Builder newDomainBuilder() { return new DomainBase.Builder() .setDomainName("fleece.example") + .setPersistedCurrentSponsorClientId("TheRegistrar") + .setCreationClientId("TheRegistrar") .setRegistrant(persistActiveContact("jd1234").createVKey()) .setSmdId("smdzzzz") .setCreationClientId("TheRegistrar"); } - @Test + @TestOfyAndSql void test_enqueueDomainBaseTask_sunrise() { persistDomainAndEnqueueLordn(newDomainBuilder().setRepoId("A-EXAMPLE").build()); String expectedPayload = @@ -72,7 +80,7 @@ public class LordnTaskUtilsTest { "lordn-sunrise", new TaskMatcher().payload(expectedPayload).tag("example")); } - @Test + @TestOfyAndSql void test_enqueueDomainBaseTask_claims() { DomainBase domain = newDomainBuilder() @@ -84,18 +92,14 @@ public class LordnTaskUtilsTest { persistDomainAndEnqueueLordn(domain); String expectedPayload = "11-EXAMPLE,fleece.example,landrush1tcn,1,2010-05-01T10:11:12.000Z," + "2010-05-01T09:11:12.000Z"; - assertTasksEnqueued( - "lordn-claims", new TaskMatcher().payload(expectedPayload).tag("example")); + assertTasksEnqueued("lordn-claims", new TaskMatcher().payload(expectedPayload).tag("example")); } - @Test + @TestOfyAndSql void test_oteRegistrarWithNullIanaId() { - tm() - .transact( + tm().transact( () -> - ofy() - .save() - .entity( + tm().put( loadRegistrar("TheRegistrar") .asBuilder() .setType(Type.OTE) @@ -107,7 +111,7 @@ public class LordnTaskUtilsTest { "lordn-sunrise", new TaskMatcher().payload(expectedPayload).tag("example")); } - @Test + @TestOfyOnly // moot in SQL since the domain creation fails the registrar foreign key check void test_enqueueDomainBaseTask_throwsExceptionOnInvalidRegistrar() { DomainBase domain = newDomainBuilder() @@ -121,7 +125,7 @@ public class LordnTaskUtilsTest { .contains("No registrar found for client id: nonexistentRegistrar"); } - @Test + @TestOfyAndSql void test_enqueueDomainBaseTask_throwsNpeOnNullDomain() { assertThrows( NullPointerException.class, diff --git a/core/src/test/java/google/registry/tools/CreateRegistrarCommandTest.java b/core/src/test/java/google/registry/tools/CreateRegistrarCommandTest.java index 3f6b59537..ad2c08ded 100644 --- a/core/src/test/java/google/registry/tools/CreateRegistrarCommandTest.java +++ b/core/src/test/java/google/registry/tools/CreateRegistrarCommandTest.java @@ -16,14 +16,13 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; -import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.testing.CertificateSamples.SAMPLE_CERT; import static google.registry.testing.CertificateSamples.SAMPLE_CERT3; import static google.registry.testing.CertificateSamples.SAMPLE_CERT3_HASH; import static google.registry.testing.DatabaseHelper.createTlds; import static google.registry.testing.DatabaseHelper.persistNewRegistrar; import static google.registry.util.DateTimeUtils.START_OF_TIME; -import static org.joda.time.DateTimeZone.UTC; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; @@ -38,23 +37,31 @@ import com.google.common.collect.Range; import com.google.common.net.MediaType; import google.registry.flows.certs.CertificateChecker; import google.registry.flows.certs.CertificateChecker.InsecureCertificateException; +import google.registry.model.ofy.Ofy; import google.registry.model.registrar.Registrar; +import google.registry.testing.DualDatabaseTest; +import google.registry.testing.InjectExtension; +import google.registry.testing.TestOfyAndSql; import java.io.IOException; import java.util.Optional; import org.joda.money.CurrencyUnit; import org.joda.time.DateTime; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentMatchers; import org.mockito.Mock; /** Unit tests for {@link CreateRegistrarCommand}. */ +@DualDatabaseTest class CreateRegistrarCommandTest extends CommandTestCase { @Mock private AppEngineConnection connection; + @RegisterExtension final InjectExtension inject = new InjectExtension(); + @BeforeEach void beforeEach() { + inject.setStaticField(Ofy.class, "clock", fakeClock); command.setConnection(connection); command.certificateChecker = new CertificateChecker( @@ -65,9 +72,9 @@ class CreateRegistrarCommandTest extends CommandTestCase fakeClock); } - @Test + @TestOfyAndSql void testSuccess() throws Exception { - DateTime before = DateTime.now(UTC); + DateTime before = fakeClock.nowUtc(); runCommandForced( "--name=blobio", "--password=some_password", @@ -81,10 +88,10 @@ class CreateRegistrarCommandTest extends CommandTestCase "--zip 00351", "--cc US", "clientz"); - DateTime after = DateTime.now(UTC); + DateTime after = fakeClock.nowUtc(); // Clear the cache so that the CreateAutoTimestamp field gets reloaded. - ofy().clearSessionCache(); + tm().clearSessionCache(); Optional registrarOptional = Registrar.loadByClientId("clientz"); assertThat(registrarOptional).isPresent(); @@ -112,7 +119,7 @@ class CreateRegistrarCommandTest extends CommandTestCase eq(new byte[0])); } - @Test + @TestOfyAndSql void testSuccess_quotedPassword() throws Exception { runCommandForced( "--name=blobio", @@ -133,7 +140,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().verifyPassword("some_password")).isTrue(); } - @Test + @TestOfyAndSql void testSuccess_registrarTypeFlag() throws Exception { runCommandForced( "--name=blobio", @@ -153,7 +160,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getType()).isEqualTo(Registrar.Type.TEST); } - @Test + @TestOfyAndSql void testSuccess_registrarStateFlag() throws Exception { runCommandForced( "--name=blobio", @@ -175,7 +182,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getState()).isEqualTo(Registrar.State.SUSPENDED); } - @Test + @TestOfyAndSql void testSuccess_allowedTldsInNonProductionEnvironment() throws Exception { createTlds("xn--q9jyb4c", "foobar"); @@ -202,7 +209,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getAllowedTlds()).containsExactly("xn--q9jyb4c", "foobar"); } - @Test + @TestOfyAndSql void testSuccess_allowedTldsInPDT() throws Exception { createTlds("xn--q9jyb4c", "foobar"); @@ -229,7 +236,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getAllowedTlds()).containsExactly("xn--q9jyb4c", "foobar"); } - @Test + @TestOfyAndSql void testSuccess_groupCreationCanBeDisabled() throws Exception { runCommandForced( "--name=blobio", @@ -248,7 +255,7 @@ class CreateRegistrarCommandTest extends CommandTestCase verifyNoInteractions(connection); } - @Test + @TestOfyAndSql void testFailure_groupCreationFails() throws Exception { when(connection.sendPostRequest( ArgumentMatchers.anyString(), @@ -275,7 +282,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertInStdout("BAD ROBOT NO COOKIE"); } - @Test + @TestOfyAndSql void testSuccess_groupCreationDoesntOccurOnAlphaEnv() throws Exception { runCommandInEnvironment( RegistryToolEnvironment.ALPHA, @@ -295,7 +302,7 @@ class CreateRegistrarCommandTest extends CommandTestCase verifyNoInteractions(connection); } - @Test + @TestOfyAndSql void testSuccess_ipAllowListFlag() throws Exception { runCommandForced( "--name=blobio", @@ -319,7 +326,7 @@ class CreateRegistrarCommandTest extends CommandTestCase .inOrder(); } - @Test + @TestOfyAndSql void testSuccess_ipAllowListFlagNull() throws Exception { runCommandForced( "--name=blobio", @@ -341,7 +348,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getIpAddressAllowList()).isEmpty(); } - @Test + @TestOfyAndSql void testSuccess_clientCertFileFlag() throws Exception { fakeClock.setTo(DateTime.parse("2020-11-01T00:00:00Z")); runCommandForced( @@ -364,7 +371,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getClientCertificateHash()).hasValue(SAMPLE_CERT3_HASH); } - @Test + @TestOfyAndSql void testFail_clientCertFileFlagWithViolation() { fakeClock.setTo(DateTime.parse("2020-10-01T00:00:00Z")); InsecureCertificateException thrown = @@ -394,7 +401,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar).isEmpty(); } - @Test + @TestOfyAndSql void testFail_clientCertFileFlagWithMultipleViolations() { fakeClock.setTo(DateTime.parse("2055-10-01T00:00:00Z")); InsecureCertificateException thrown = @@ -424,7 +431,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar).isEmpty(); } - @Test + @TestOfyAndSql void testSuccess_failoverClientCertFileFlag() throws Exception { fakeClock.setTo(DateTime.parse("2020-11-01T00:00:00Z")); runCommandForced( @@ -451,7 +458,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.getFailoverClientCertificateHash()).hasValue(SAMPLE_CERT3_HASH); } - @Test + @TestOfyAndSql void testFail_failoverClientCertFileFlagWithViolations() { fakeClock.setTo(DateTime.parse("2020-11-01T00:00:00Z")); InsecureCertificateException thrown = @@ -481,7 +488,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar).isEmpty(); } - @Test + @TestOfyAndSql void testFail_failoverClientCertFileFlagWithMultipleViolations() { fakeClock.setTo(DateTime.parse("2055-11-01T00:00:00Z")); InsecureCertificateException thrown = @@ -511,7 +518,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar).isEmpty(); } - @Test + @TestOfyAndSql void testSuccess_ianaId() throws Exception { runCommandForced( "--name=blobio", @@ -532,7 +539,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getIanaIdentifier()).isEqualTo(12345); } - @Test + @TestOfyAndSql void testSuccess_billingId() throws Exception { runCommandForced( "--name=blobio", @@ -554,7 +561,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getBillingIdentifier()).isEqualTo(12345); } - @Test + @TestOfyAndSql void testSuccess_poNumber() throws Exception { runCommandForced( "--name=blobio", @@ -576,7 +583,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getPoNumber()).hasValue("AA55G"); } - @Test + @TestOfyAndSql void testSuccess_billingAccountMap() throws Exception { runCommandForced( "--name=blobio", @@ -599,7 +606,7 @@ class CreateRegistrarCommandTest extends CommandTestCase .containsExactly(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz"); } - @Test + @TestOfyAndSql void testFailure_billingAccountMap_doesNotContainEntryForTldAllowed() { createTlds("foo"); @@ -627,7 +634,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("USD"); } - @Test + @TestOfyAndSql void testSuccess_billingAccountMap_onlyAppliesToRealRegistrar() throws Exception { createTlds("foo"); @@ -651,7 +658,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getBillingAccountMap()).containsExactly(CurrencyUnit.JPY, "789xyz"); } - @Test + @TestOfyAndSql void testSuccess_streetAddress() throws Exception { runCommandForced( "--name=blobio", @@ -683,7 +690,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.getLocalizedAddress().getCountryCode()).isEqualTo("US"); } - @Test + @TestOfyAndSql void testSuccess_email() throws Exception { runCommandForced( "--name=blobio", @@ -705,7 +712,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getEmailAddress()).isEqualTo("foo@foo.foo"); } - @Test + @TestOfyAndSql void testSuccess_fallBackToIcannReferralEmail() throws Exception { runCommandForced( "--name=blobio", @@ -726,7 +733,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getEmailAddress()).isEqualTo("foo@bar.test"); } - @Test + @TestOfyAndSql void testSuccess_url() throws Exception { runCommandForced( "--name=blobio", @@ -748,7 +755,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getUrl()).isEqualTo("http://foo.foo"); } - @Test + @TestOfyAndSql void testSuccess_phone() throws Exception { runCommandForced( "--name=blobio", @@ -770,7 +777,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getPhoneNumber()).isEqualTo("+1.2125556342"); } - @Test + @TestOfyAndSql void testSuccess_optionalParamsAsNull() throws Exception { runCommandForced( "--name=blobio", @@ -801,7 +808,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.getDriveFolderId()).isNull(); } - @Test + @TestOfyAndSql void testSuccess_optionalParamsAsEmptyString() throws Exception { runCommandForced( "--name=blobio", @@ -832,7 +839,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.getDriveFolderId()).isNull(); } - @Test + @TestOfyAndSql void testSuccess_blockPremiumNames() throws Exception { runCommandForced( "--name=blobio", @@ -854,7 +861,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getBlockPremiumNames()).isTrue(); } - @Test + @TestOfyAndSql void testSuccess_noBlockPremiumNames() throws Exception { runCommandForced( "--name=blobio", @@ -876,7 +883,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getBlockPremiumNames()).isFalse(); } - @Test + @TestOfyAndSql void testSuccess_registryLockAllowed() throws Exception { runCommandForced( "--name=blobio", @@ -898,7 +905,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().isRegistryLockAllowed()).isTrue(); } - @Test + @TestOfyAndSql void testSuccess_registryLockDisallowed() throws Exception { runCommandForced( "--name=blobio", @@ -920,7 +927,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().isRegistryLockAllowed()).isFalse(); } - @Test + @TestOfyAndSql void testFailure_badPhoneNumber() { ParameterException thrown = assertThrows( @@ -943,7 +950,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("phone"); } - @Test + @TestOfyAndSql void testFailure_badPhoneNumber2() { ParameterException thrown = assertThrows( @@ -966,7 +973,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("phone"); } - @Test + @TestOfyAndSql void testSuccess_fax() throws Exception { runCommandForced( "--name=blobio", @@ -988,7 +995,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(registrar.get().getFaxNumber()).isEqualTo("+1.2125556342"); } - @Test + @TestOfyAndSql void testFailure_missingRegistrarType() { IllegalArgumentException thrown = assertThrows( @@ -1008,7 +1015,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("Registrar type cannot be null"); } - @Test + @TestOfyAndSql void testFailure_invalidRegistrarType() { assertThrows( ParameterException.class, @@ -1026,7 +1033,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_invalidRegistrarState() { assertThrows( ParameterException.class, @@ -1047,7 +1054,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_allowedTldDoesNotExist() { assertThrows( IllegalArgumentException.class, @@ -1068,7 +1075,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_allowedTldsInRealWithoutAbuseContact() { createTlds("xn--q9jyb4c", "foobar"); IllegalArgumentException thrown = @@ -1094,7 +1101,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().startsWith("Cannot add allowed TLDs"); } - @Test + @TestOfyAndSql void testFailure_invalidIpAllowListFlag() { assertThrows( IllegalArgumentException.class, @@ -1115,7 +1122,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testSuccess_ipAllowListFlagWithNull() { assertThrows( IllegalArgumentException.class, @@ -1136,7 +1143,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_missingName() { IllegalArgumentException thrown = assertThrows( @@ -1157,7 +1164,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("--name is a required field"); } - @Test + @TestOfyAndSql void testFailure_missingPassword() { IllegalArgumentException thrown = assertThrows( @@ -1178,7 +1185,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("--password is a required field"); } - @Test + @TestOfyAndSql void testFailure_emptyPassword() { IllegalArgumentException thrown = assertThrows( @@ -1200,7 +1207,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("--password is a required field"); } - @Test + @TestOfyAndSql void testFailure_clientIdTooShort() { assertThrows( IllegalArgumentException.class, @@ -1220,7 +1227,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "ab")); } - @Test + @TestOfyAndSql void testFailure_clientIdTooLong() { assertThrows( IllegalArgumentException.class, @@ -1240,7 +1247,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientabcdefghijk")); } - @Test + @TestOfyAndSql void testFailure_missingClientId() { assertThrows( ParameterException.class, @@ -1260,7 +1267,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "--force")); } - @Test + @TestOfyAndSql void testFailure_missingStreetLines() { assertThrows( IllegalArgumentException.class, @@ -1279,7 +1286,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_missingCity() { assertThrows( IllegalArgumentException.class, @@ -1300,7 +1307,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_missingState() { assertThrows( IllegalArgumentException.class, @@ -1321,7 +1328,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_missingZip() { assertThrows( IllegalArgumentException.class, @@ -1342,7 +1349,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_missingCc() { assertThrows( IllegalArgumentException.class, @@ -1363,7 +1370,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_invalidCc() { assertThrows( IllegalArgumentException.class, @@ -1385,7 +1392,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_tooManyStreetLines() { assertThrows( IllegalArgumentException.class, @@ -1408,7 +1415,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_tooFewStreetLines() { assertThrows( IllegalArgumentException.class, @@ -1428,7 +1435,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_missingIanaIdForRealRegistrar() { assertThrows( IllegalArgumentException.class, @@ -1447,7 +1454,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_negativeIanaId() { assertThrows( IllegalArgumentException.class, @@ -1467,7 +1474,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_nonIntegerIanaId() { assertThrows( ParameterException.class, @@ -1487,7 +1494,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_negativeBillingId() { assertThrows( IllegalArgumentException.class, @@ -1508,7 +1515,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_nonIntegerBillingId() { assertThrows( ParameterException.class, @@ -1529,7 +1536,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_missingPhonePasscode() { assertThrows( IllegalArgumentException.class, @@ -1548,7 +1555,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_missingIcannReferralEmail() { IllegalArgumentException thrown = assertThrows( @@ -1569,7 +1576,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("--icann_referral_email"); } - @Test + @TestOfyAndSql void testFailure_passcodeTooShort() { assertThrows( IllegalArgumentException.class, @@ -1589,7 +1596,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_passcodeTooLong() { assertThrows( IllegalArgumentException.class, @@ -1609,7 +1616,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_invalidPasscode() { assertThrows( IllegalArgumentException.class, @@ -1629,7 +1636,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_twoClientsSpecified() { assertThrows( IllegalArgumentException.class, @@ -1650,7 +1657,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_unknownFlag() { assertThrows( ParameterException.class, @@ -1671,7 +1678,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_alreadyExists() { persistNewRegistrar("existing", "Existing Registrar", Registrar.Type.REAL, 1L); IllegalStateException thrown = @@ -1694,7 +1701,7 @@ class CreateRegistrarCommandTest extends CommandTestCase assertThat(thrown).hasMessageThat().contains("Registrar existing already exists"); } - @Test + @TestOfyAndSql void testFailure_registrarNameSimilarToExisting() { // Note that "tHeRe GiStRaR" normalizes identically to "The Registrar", which is created by // AppEngineRule. @@ -1722,7 +1729,7 @@ class CreateRegistrarCommandTest extends CommandTestCase + "identically to existing registrar name The Registrar"); } - @Test + @TestOfyAndSql void testFailure_clientIdNormalizesToExisting() { IllegalArgumentException thrown = assertThrows( @@ -1748,7 +1755,7 @@ class CreateRegistrarCommandTest extends CommandTestCase + "normalizes identically to existing registrar TheRegistrar"); } - @Test + @TestOfyAndSql void testFailure_clientIdIsInvalidFormat() { IllegalArgumentException thrown = assertThrows( @@ -1773,7 +1780,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "Client identifier (.L33T) can only contain lowercase letters, numbers, and hyphens"); } - @Test + @TestOfyAndSql void testFailure_phone() { assertThrows( ParameterException.class, @@ -1794,7 +1801,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_fax() { assertThrows( ParameterException.class, @@ -1815,7 +1822,7 @@ class CreateRegistrarCommandTest extends CommandTestCase "clientz")); } - @Test + @TestOfyAndSql void testFailure_badEmail() { IllegalArgumentException thrown = assertThrows( diff --git a/core/src/test/java/google/registry/tools/UpdateCursorsCommandTest.java b/core/src/test/java/google/registry/tools/UpdateCursorsCommandTest.java index 86ef01f36..17185ac05 100644 --- a/core/src/test/java/google/registry/tools/UpdateCursorsCommandTest.java +++ b/core/src/test/java/google/registry/tools/UpdateCursorsCommandTest.java @@ -15,8 +15,10 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.ofy.ObjectifyService.ofy; +import static com.google.common.truth.Truth8.assertThat; import static google.registry.testing.DatabaseHelper.createTld; +import static google.registry.testing.DatabaseHelper.loadByKey; +import static google.registry.testing.DatabaseHelper.loadByKeyIfPresent; import static google.registry.testing.DatabaseHelper.persistResource; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -25,11 +27,13 @@ import google.registry.model.common.Cursor; import google.registry.model.common.Cursor.CursorType; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.RegistryNotFoundException; +import google.registry.testing.DualDatabaseTest; +import google.registry.testing.TestOfyAndSql; import org.joda.time.DateTime; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; /** Unit tests for {@link UpdateCursorsCommand}. */ +@DualDatabaseTest class UpdateCursorsCommandTest extends CommandTestCase { private Registry registry; @@ -42,7 +46,7 @@ class UpdateCursorsCommandTest extends CommandTestCase { void doUpdateTest() throws Exception { runCommandForced("--type=brda", "--timestamp=1984-12-18T00:00:00Z", "foo"); - assertThat(ofy().load().key(Cursor.createKey(CursorType.BRDA, registry)).now().getCursorTime()) + assertThat(loadByKey(Cursor.createVKey(CursorType.BRDA, "foo")).getCursorTime()) .isEqualTo(DateTime.parse("1984-12-18TZ")); String changes = command.prompt(); assertThat(changes) @@ -51,12 +55,7 @@ class UpdateCursorsCommandTest extends CommandTestCase { void doGlobalUpdateTest() throws Exception { runCommandForced("--type=recurring_billing", "--timestamp=1984-12-18T00:00:00Z"); - assertThat( - ofy() - .load() - .key(Cursor.createGlobalKey(CursorType.RECURRING_BILLING)) - .now() - .getCursorTime()) + assertThat(loadByKey(Cursor.createGlobalVKey(CursorType.RECURRING_BILLING)).getCursorTime()) .isEqualTo(DateTime.parse("1984-12-18TZ")); String changes = command.prompt(); assertThat(changes) @@ -65,42 +64,42 @@ class UpdateCursorsCommandTest extends CommandTestCase { + " 1984-12-18T00:00:00.000Z\n"); } - @Test + @TestOfyAndSql void testSuccess_oldValueisEmpty() throws Exception { - assertThat(ofy().load().key(Cursor.createKey(CursorType.BRDA, registry)).now()).isNull(); + assertThat(loadByKeyIfPresent(Cursor.createVKey(CursorType.BRDA, registry.getTldStr()))) + .isEmpty(); doUpdateTest(); } - @Test + @TestOfyAndSql void testSuccess_hasOldValue() throws Exception { persistResource(Cursor.create(CursorType.BRDA, DateTime.parse("1950-12-18TZ"), registry)); doUpdateTest(); } - @Test + @TestOfyAndSql void testSuccess_global_hasOldValue() throws Exception { persistResource( Cursor.createGlobal(CursorType.RECURRING_BILLING, DateTime.parse("1950-12-18TZ"))); doGlobalUpdateTest(); } - @Test - void testSuccess_global_oldValueisEmpty() throws Exception { - assertThat(ofy().load().key(Cursor.createGlobalKey(CursorType.RECURRING_BILLING)).now()) - .isNull(); + @TestOfyAndSql + void testSuccess_global_oldValueIsEmpty() throws Exception { + assertThat(loadByKeyIfPresent(Cursor.createGlobalVKey(CursorType.RECURRING_BILLING))).isEmpty(); doGlobalUpdateTest(); } - @Test + @TestOfyAndSql void testSuccess_multipleTlds_hasOldValue() throws Exception { createTld("bar"); Registry registry2 = Registry.get("bar"); persistResource(Cursor.create(CursorType.BRDA, DateTime.parse("1950-12-18TZ"), registry)); persistResource(Cursor.create(CursorType.BRDA, DateTime.parse("1950-12-18TZ"), registry2)); runCommandForced("--type=brda", "--timestamp=1984-12-18T00:00:00Z", "foo", "bar"); - assertThat(ofy().load().key(Cursor.createKey(CursorType.BRDA, registry)).now().getCursorTime()) + assertThat(loadByKey(Cursor.createVKey(CursorType.BRDA, "foo")).getCursorTime()) .isEqualTo(DateTime.parse("1984-12-18TZ")); - assertThat(ofy().load().key(Cursor.createKey(CursorType.BRDA, registry2)).now().getCursorTime()) + assertThat(loadByKey(Cursor.createVKey(CursorType.BRDA, "bar")).getCursorTime()) .isEqualTo(DateTime.parse("1984-12-18TZ")); String changes = command.prompt(); assertThat(changes) @@ -109,16 +108,15 @@ class UpdateCursorsCommandTest extends CommandTestCase { + "Change cursorTime of BRDA for Scope:bar to 1984-12-18T00:00:00.000Z\n"); } - @Test + @TestOfyAndSql void testSuccess_multipleTlds_oldValueisEmpty() throws Exception { createTld("bar"); - Registry registry2 = Registry.get("bar"); - assertThat(ofy().load().key(Cursor.createKey(CursorType.BRDA, registry)).now()).isNull(); - assertThat(ofy().load().key(Cursor.createKey(CursorType.BRDA, registry2)).now()).isNull(); + assertThat(loadByKeyIfPresent(Cursor.createVKey(CursorType.BRDA, "foo"))).isEmpty(); + assertThat(loadByKeyIfPresent(Cursor.createVKey(CursorType.BRDA, "bar"))).isEmpty(); runCommandForced("--type=brda", "--timestamp=1984-12-18T00:00:00Z", "foo", "bar"); - assertThat(ofy().load().key(Cursor.createKey(CursorType.BRDA, registry)).now().getCursorTime()) + assertThat(loadByKey(Cursor.createVKey(CursorType.BRDA, "foo")).getCursorTime()) .isEqualTo(DateTime.parse("1984-12-18TZ")); - assertThat(ofy().load().key(Cursor.createKey(CursorType.BRDA, registry2)).now().getCursorTime()) + assertThat(loadByKey(Cursor.createVKey(CursorType.BRDA, "bar")).getCursorTime()) .isEqualTo(DateTime.parse("1984-12-18TZ")); String changes = command.prompt(); assertThat(changes) @@ -127,14 +125,14 @@ class UpdateCursorsCommandTest extends CommandTestCase { + "Change cursorTime of BRDA for Scope:bar to 1984-12-18T00:00:00.000Z\n"); } - @Test + @TestOfyAndSql void testFailure_badTld() { assertThrows( RegistryNotFoundException.class, () -> runCommandForced("--type=brda", "--timestamp=1984-12-18T00:00:00Z", "bar")); } - @Test + @TestOfyAndSql void testFailure_badCursorType() { ParameterException thrown = assertThrows(