diff --git a/java/google/registry/model/billing/BillingEvent.java b/java/google/registry/model/billing/BillingEvent.java index e37bd408f..d0cfcfeb6 100644 --- a/java/google/registry/model/billing/BillingEvent.java +++ b/java/google/registry/model/billing/BillingEvent.java @@ -189,11 +189,11 @@ public abstract class BillingEvent extends ImmutableObject @Override public T build() { T instance = getInstance(); - checkNotNull(instance.reason); - checkNotNull(instance.clientId); - checkNotNull(instance.eventTime); - checkNotNull(instance.targetId); - checkNotNull(instance.parent); + checkNotNull(instance.reason, "Reason must be set"); + checkNotNull(instance.clientId, "Client ID must be set"); + checkNotNull(instance.eventTime, "Event time must be set"); + checkNotNull(instance.targetId, "Target ID must be set"); + checkNotNull(instance.parent, "Parent must be set"); return super.build(); } } @@ -502,8 +502,8 @@ public abstract class BillingEvent extends ImmutableObject @Override public Cancellation build() { Cancellation instance = getInstance(); - checkNotNull(instance.billingTime); - checkNotNull(instance.reason); + checkNotNull(instance.billingTime, "Must set billing time"); + checkNotNull(instance.reason, "Must set reason"); checkState((instance.refOneTime == null) != (instance.refRecurring == null), "Cancellations must have exactly one billing event key set"); return super.build(); diff --git a/javatests/google/registry/flows/EppLifecycleDomainTest.java b/javatests/google/registry/flows/EppLifecycleDomainTest.java index 8167e8d0f..70bf3d738 100644 --- a/javatests/google/registry/flows/EppLifecycleDomainTest.java +++ b/javatests/google/registry/flows/EppLifecycleDomainTest.java @@ -14,20 +14,40 @@ package google.registry.flows; +import static com.google.common.truth.Truth8.assertThat; +import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.eppoutput.Result.Code.SUCCESS_AND_CLOSE; import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING; +import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.testing.DatastoreHelper.assertBillingEventsForResource; import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTlds; +import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType; +import static google.registry.testing.DatastoreHelper.persistResource; +import static google.registry.testing.DatastoreHelper.stripBillingEventId; import static google.registry.testing.EppMetricSubject.assertThat; import static google.registry.util.DateTimeUtils.START_OF_TIME; +import static org.joda.money.CurrencyUnit.USD; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import com.google.re2j.Matcher; import com.google.re2j.Pattern; +import com.googlecode.objectify.Key; +import google.registry.model.billing.BillingEvent; +import google.registry.model.billing.BillingEvent.Flag; +import google.registry.model.billing.BillingEvent.OneTime; +import google.registry.model.billing.BillingEvent.Reason; +import google.registry.model.domain.DomainResource; +import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; +import google.registry.model.reporting.HistoryEntry.Type; import google.registry.testing.AppEngineRule; +import java.util.Objects; +import java.util.Optional; +import org.joda.money.Money; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Rule; @@ -50,31 +70,37 @@ public class EppLifecycleDomainTest extends EppTestCase { /** Create the two administrative contacts and two hosts. */ void createContactsAndHosts() throws Exception { - DateTime startTime = DateTime.parse("2000-06-01T00:00:00Z"); - assertCommandAndResponse( - "contact_create_sh8013.xml", - ImmutableMap.of(), - "contact_create_response_sh8013.xml", - ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"), - startTime); - assertCommandAndResponse( - "contact_create_jd1234.xml", - "contact_create_response_jd1234.xml", - startTime.plusMinutes(1)); + DateTime createTime = DateTime.parse("2000-06-01T00:00:00Z"); + createContacts(createTime); assertCommandAndResponse( "host_create.xml", ImmutableMap.of("HOSTNAME", "ns1.example.external"), "host_create_response.xml", ImmutableMap.of( - "HOSTNAME", "ns1.example.external", "CRDATE", startTime.plusMinutes(2).toString()), - startTime.plusMinutes(2)); + "HOSTNAME", "ns1.example.external", "CRDATE", createTime.plusMinutes(2).toString()), + createTime.plusMinutes(2)); assertCommandAndResponse( "host_create.xml", ImmutableMap.of("HOSTNAME", "ns2.example.external"), "host_create_response.xml", ImmutableMap.of( - "HOSTNAME", "ns2.example.external", "CRDATE", startTime.plusMinutes(3).toString()), - startTime.plusMinutes(3)); + "HOSTNAME", "ns2.example.external", "CRDATE", createTime.plusMinutes(3).toString()), + createTime.plusMinutes(3)); + } + + private void createContacts(DateTime createTime) throws Exception { + assertCommandAndResponse( + "contact_create_sh8013.xml", + ImmutableMap.of(), + "contact_create_response_sh8013.xml", + ImmutableMap.of("CRDATE", createTime.toString()), + createTime); + assertCommandAndResponse( + "contact_create_jd1234.xml", + ImmutableMap.of(), + "contact_create_response_jd1234.xml", + ImmutableMap.of("CRDATE", createTime.plusMinutes(1).toString()), + createTime.plusMinutes(1)); } /** Creates the domain fakesite.example with two nameservers on it. */ @@ -117,18 +143,7 @@ public class EppLifecycleDomainTest extends EppTestCase { @Test public void testDomainDeleteRestore() throws Exception { assertCommandAndResponse("login_valid.xml", "login_response.xml"); - - // Create contacts sh8013 and jd1234. - assertCommandAndResponse( - "contact_create_sh8013.xml", - null, - "contact_create_response_sh8013.xml", - ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"), - DateTime.parse("2000-06-01T00:00:00Z")); - assertCommandAndResponse( - "contact_create_jd1234.xml", - "contact_create_response_jd1234.xml", - DateTime.parse("2000-06-01T00:01:00Z")); + createContacts(DateTime.parse("2000-06-01T00:00:00Z")); // Create domain example.tld assertCommandAndResponse( @@ -152,42 +167,237 @@ public class EppLifecycleDomainTest extends EppTestCase { } @Test - public void testDomainDeletion_withinAddGracePeriod() throws Exception { + public void testDomainDeletion_withinAddGracePeriod_deletesImmediately() throws Exception { assertCommandAndResponse("login_valid.xml", "login_response.xml"); + createContacts(DateTime.parse("2000-06-01T00:00:00Z")); - // Create contacts sh8013 and jd1234. + // Create domain example.tld + DateTime createTime = DateTime.parse("2000-06-01T00:02:00Z"); assertCommandAndResponse( - "contact_create_sh8013.xml", - null, - "contact_create_response_sh8013.xml", - ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"), - DateTime.parse("2000-06-01T00:00:00Z")); - assertCommandAndResponse( - "contact_create_jd1234.xml", - "contact_create_response_jd1234.xml", - DateTime.parse("2000-06-01T00:01:00Z")); + "domain_create_no_hosts_or_dsdata.xml", + "domain_create_response.xml", + createTime); + DomainResource domain = + loadByForeignKey(DomainResource.class, "example.tld", createTime.plusHours(1)); + + // Delete domain example.tld within the add grace period. + DateTime deleteTime = createTime.plusDays(1); + assertCommandAndResponse( + "domain_delete.xml", + "generic_success_response.xml", + deleteTime); + + // Verify that it is immediately non-existent. + assertCommandAndResponse( + "domain_info.xml", + "domain_info_response_nonexistent.xml", + deleteTime.plusSeconds(1)); + + // The expected one-time billing event, that should have an associated Cancellation. + OneTime oneTimeCreateBillingEvent = makeOneTimeCreateBillingEvent(domain, createTime); + // Verify that the OneTime billing event associated with the domain creation is canceled. + assertBillingEventsForResource( + domain, + // Check the existence of the expected create one-time billing event. + oneTimeCreateBillingEvent, + makeRecurringCreateBillingEvent(domain, createTime, deleteTime), + // Check for the existence of a cancellation for the given one-time billing event. + makeCancellationBillingEventFor( + domain, oneTimeCreateBillingEvent, createTime, deleteTime)); + + assertCommandAndResponse("logout.xml", "logout_response.xml"); + } + + @Test + public void testDomainDeletion_outsideAddGracePeriod_showsRedemptionPeriod() throws Exception { + assertCommandAndResponse("login_valid.xml", "login_response.xml"); + createContacts(DateTime.parse("2000-06-01T00:00:00Z")); + + DateTime createTime = DateTime.parse("2000-06-01T00:02:00Z"); // Create domain example.tld assertCommandAndResponse( "domain_create_no_hosts_or_dsdata.xml", "domain_create_response.xml", - DateTime.parse("2000-06-01T00:02:00Z")); + createTime); + DateTime deleteTime = DateTime.parse("2000-07-07T00:02:00Z"); // 1 month and 6 days after // Delete domain example.tld after its add grace period has expired. assertCommandAndResponse( "domain_delete.xml", "generic_success_action_pending_response.xml", - DateTime.parse("2000-07-01T00:02:00Z")); + deleteTime); - // Poke the domain a little at various times to see its status + // Verify that domain shows redemptionPeriod soon after deletion. assertCommandAndResponse( "domain_info.xml", - "domain_info_response_pendingdelete.xml", - DateTime.parse("2000-08-01T00:02:00Z")); // 1 day out. + ImmutableMap.of(), + "domain_info_response_wildcard.xml", + ImmutableMap.of("STATUS", "redemptionPeriod"), + DateTime.parse("2000-07-08T00:00:00Z")); + + // Verify that the domain shows pendingDelete next. + assertCommandAndResponse( + "domain_info.xml", + ImmutableMap.of(), + "domain_info_response_wildcard.xml", + ImmutableMap.of("STATUS", "pendingDelete"), + DateTime.parse("2000-08-08T00:00:00Z")); + + // Verify that the domain is non-existent (available for registration) later. + assertCommandAndResponse( + "domain_info.xml", + "domain_info_response_nonexistent.xml", + DateTime.parse("2000-09-01T00:00:00Z")); + + DomainResource domain = + loadByForeignKey( + DomainResource.class, "example.tld", DateTime.parse("2000-08-01T00:02:00Z")); + // Verify that the autorenew was ended and that the one-time billing event is not canceled. + assertBillingEventsForResource( + domain, + makeOneTimeCreateBillingEvent(domain, createTime), + makeRecurringCreateBillingEvent(domain, createTime, deleteTime)); assertCommandAndResponse("logout.xml", "logout_response.xml"); } + @Test + public void testEapDomainDeletion_withinAddGracePeriod_eapFeeIsNotRefunded() throws Exception { + assertCommandAndResponse("login_valid_fee_extension.xml", "login_response.xml"); + createContacts(DateTime.parse("2000-06-01T00:00:00Z")); + + // Set the EAP schedule. + persistResource( + Registry.get("tld") + .asBuilder() + .setEapFeeSchedule( + ImmutableSortedMap.of( + START_OF_TIME, Money.of(USD, 0), + DateTime.parse("2000-06-01T00:00:00Z"), Money.of(USD, 100), + DateTime.parse("2000-06-02T00:00:00Z"), Money.of(USD, 0))) + .build()); + + // Create domain example.tld, which should have an EAP fee of USD 100. + DateTime createTime = DateTime.parse("2000-06-01T00:02:00Z"); + assertCommandAndResponse( + "domain_create_eap_fee.xml", + "domain_create_response_eap_fee.xml", + createTime); + + DomainResource domain = + loadByForeignKey( + DomainResource.class, "example.tld", DateTime.parse("2000-06-01T00:03:00Z")); + + // Delete domain example.tld within the add grade period. + DateTime deleteTime = createTime.plusDays(1); + assertCommandAndResponse( + "domain_delete.xml", + "domain_delete_response_fee.xml", + deleteTime); + + // Verify that the OneTime billing event associated with the base fee of domain registration and + // is canceled and the autorenew is ended, but that the EAP fee is not canceled. + OneTime expectedCreateEapBillingEvent = + new BillingEvent.OneTime.Builder() + .setReason(Reason.FEE_EARLY_ACCESS) + .setTargetId("example.tld") + .setClientId("NewRegistrar") + .setCost(Money.parse("USD 100.00")) + .setEventTime(createTime) + .setBillingTime(createTime.plus(Registry.get("tld").getRenewGracePeriodLength())) + .setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE)) + .build(); + + // The expected one-time billing event, that should have an associated Cancellation. + OneTime expectedOneTimeCreateBillingEvent = makeOneTimeCreateBillingEvent(domain, createTime); + assertBillingEventsForResource( + domain, + // Check for the expected create one-time billing event ... + expectedOneTimeCreateBillingEvent, + // ... and the expected one-time EAP fee billing event ... + expectedCreateEapBillingEvent, + makeRecurringCreateBillingEvent(domain, createTime, deleteTime), + // ... and verify that the create one-time billing event was canceled ... + makeCancellationBillingEventFor( + domain, expectedOneTimeCreateBillingEvent, createTime, deleteTime)); + // ... but there was NOT a Cancellation for the EAP fee, as this would fail if additional + // billing events were present. + + assertCommandAndResponse("logout.xml", "logout_response.xml"); + } + + /** Makes a one-time billing event corresponding to the given domain's creation. */ + private static BillingEvent.OneTime makeOneTimeCreateBillingEvent( + DomainResource domain, DateTime createTime) { + return new BillingEvent.OneTime.Builder() + .setReason(Reason.CREATE) + .setTargetId(domain.getFullyQualifiedDomainName()) + .setClientId(domain.getCurrentSponsorClientId()) + .setCost(Money.parse("USD 26.00")) + .setPeriodYears(2) + .setEventTime(createTime) + .setBillingTime(createTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength())) + .setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE)) + .build(); + } + + /** Makes a recurring billing event corresponding to the given domain's creation. */ + private static BillingEvent.Recurring makeRecurringCreateBillingEvent( + DomainResource domain, DateTime createTime, DateTime endTime) { + return new BillingEvent.Recurring.Builder() + .setReason(Reason.RENEW) + .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) + .setTargetId(domain.getFullyQualifiedDomainName()) + .setClientId(domain.getCurrentSponsorClientId()) + .setEventTime(createTime.plusYears(2)) + .setRecurrenceEndTime(endTime) + .setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE)) + .build(); + } + + /** Makes a cancellation billing event cancelling out the given domain create billing event. */ + private static BillingEvent.Cancellation makeCancellationBillingEventFor( + DomainResource domain, + OneTime billingEventToCancel, + DateTime createTime, + DateTime deleteTime) { + return new BillingEvent.Cancellation.Builder() + .setTargetId(domain.getFullyQualifiedDomainName()) + .setClientId(domain.getCurrentSponsorClientId()) + .setEventTime(deleteTime) + .setOneTimeEventKey(findKeyToActualOneTimeBillingEvent(billingEventToCancel)) + .setBillingTime(createTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength())) + .setReason(Reason.CREATE) + .setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_DELETE)) + .build(); + } + + /** + * Finds the Key to the actual one-time create billing event associated with a domain's creation. + * + *

This is used in the situation where we have created an expected billing event associated + * with the domain's creation (which is passed as the parameter here), then need to locate the key + * to the actual billing event in Datastore that would be seen on a Cancellation billing event. + * This is necessary because the ID will be different even though all the rest of the fields are + * the same. + */ + private static Key findKeyToActualOneTimeBillingEvent(OneTime expectedBillingEvent) { + Optional actualCreateBillingEvent = + ofy() + .load() + .type(BillingEvent.OneTime.class) + .list() + .stream() + .filter( + b -> + Objects.equals( + stripBillingEventId(b), stripBillingEventId(expectedBillingEvent))) + .findFirst(); + assertThat(actualCreateBillingEvent).isPresent(); + return Key.create(actualCreateBillingEvent.get()); + } + @Test public void testDomainDeletionWithSubordinateHost_fails() throws Exception { assertCommandAndResponse("login_valid.xml", "login_response.xml"); @@ -751,17 +961,7 @@ public class EppLifecycleDomainTest extends EppTestCase { createTlds("bar.foo.tld", "foo.tld"); assertCommandAndResponse("login_valid.xml", "login_response.xml"); - - assertCommandAndResponse( - "contact_create_sh8013.xml", - null, - "contact_create_response_sh8013.xml", - ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"), - DateTime.parse("2000-06-01T00:00:00Z")); - assertCommandAndResponse( - "contact_create_jd1234.xml", - "contact_create_response_jd1234.xml", - DateTime.parse("2000-06-01T00:01:00Z")); + createContacts(DateTime.parse("2000-06-01T00:00:00Z")); // Create domain example.bar.foo.tld assertCommandAndResponse( @@ -795,17 +995,7 @@ public class EppLifecycleDomainTest extends EppTestCase { createTld("tld.foo"); assertCommandAndResponse("login_valid.xml", "login_response.xml"); - - assertCommandAndResponse( - "contact_create_sh8013.xml", - null, - "contact_create_response_sh8013.xml", - ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"), - DateTime.parse("2000-06-01T00:00:00Z")); - assertCommandAndResponse( - "contact_create_jd1234.xml", - "contact_create_response_jd1234.xml", - DateTime.parse("2000-06-01T00:01:00Z")); + createContacts(DateTime.parse("2000-06-01T00:00:00Z")); // Create domain example.tld.foo assertCommandAndResponse( diff --git a/javatests/google/registry/flows/FlowTestCase.java b/javatests/google/registry/flows/FlowTestCase.java index f9f27f99e..7acc28150 100644 --- a/javatests/google/registry/flows/FlowTestCase.java +++ b/javatests/google/registry/flows/FlowTestCase.java @@ -21,9 +21,9 @@ import static com.google.common.collect.Sets.difference; import static com.google.common.truth.Truth.assertThat; import static google.registry.flows.EppXmlTransformer.marshal; import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.testing.DatastoreHelper.BILLING_EVENT_ID_STRIPPER; import static google.registry.testing.DatastoreHelper.POLL_MESSAGE_ID_STRIPPER; import static google.registry.testing.DatastoreHelper.getPollMessages; +import static google.registry.testing.DatastoreHelper.stripBillingEventId; import static google.registry.xml.XmlTestUtils.assertXmlEquals; import static java.nio.charset.StandardCharsets.UTF_8; import static org.joda.time.DateTimeZone.UTC; @@ -215,7 +215,7 @@ public abstract class FlowTestCase extends ShardableTestCase { gracePeriod.getClientId(), null)) .apply(entry.getKey()), - BILLING_EVENT_ID_STRIPPER.apply(entry.getValue())); + stripBillingEventId(entry.getValue())); } return builder.build(); } diff --git a/javatests/google/registry/flows/testdata/domain_create_eap_fee.xml b/javatests/google/registry/flows/testdata/domain_create_eap_fee.xml new file mode 100644 index 000000000..ee41134ac --- /dev/null +++ b/javatests/google/registry/flows/testdata/domain_create_eap_fee.xml @@ -0,0 +1,24 @@ + + + + + example.tld + 2 + jd1234 + sh8013 + sh8013 + + 2fooBAR + + + + + + USD + 26.00 + 100.00 + + + ABC-12345 + + diff --git a/javatests/google/registry/flows/testdata/domain_create_response_eap_fee.xml b/javatests/google/registry/flows/testdata/domain_create_response_eap_fee.xml new file mode 100644 index 000000000..a80bc2292 --- /dev/null +++ b/javatests/google/registry/flows/testdata/domain_create_response_eap_fee.xml @@ -0,0 +1,25 @@ + + + + Command completed successfully + + + + example.tld + 2000-06-01T00:02:00Z + 2002-06-01T00:02:00Z + + + + + USD + 26.00 + 100.00 + + + + ABC-12345 + server-trid + + + diff --git a/javatests/google/registry/flows/testdata/domain_delete_response_fee.xml b/javatests/google/registry/flows/testdata/domain_delete_response_fee.xml new file mode 100644 index 000000000..d1498878d --- /dev/null +++ b/javatests/google/registry/flows/testdata/domain_delete_response_fee.xml @@ -0,0 +1,18 @@ + + + + Command completed successfully + + + + USD + -26.00 + + + + ABC-12345 + server-trid + + + diff --git a/javatests/google/registry/flows/testdata/domain_info_response_nonexistent.xml b/javatests/google/registry/flows/testdata/domain_info_response_nonexistent.xml new file mode 100644 index 000000000..e2672146e --- /dev/null +++ b/javatests/google/registry/flows/testdata/domain_info_response_nonexistent.xml @@ -0,0 +1,12 @@ + + + + The domain with given ID (example.tld) doesn't exist. + + + ABC-12345 + server-trid + + + diff --git a/javatests/google/registry/flows/testdata/domain_info_response_pendingdelete.xml b/javatests/google/registry/flows/testdata/domain_info_response_wildcard.xml similarity index 96% rename from javatests/google/registry/flows/testdata/domain_info_response_pendingdelete.xml rename to javatests/google/registry/flows/testdata/domain_info_response_wildcard.xml index 8471fda0b..c10ba4b5e 100644 --- a/javatests/google/registry/flows/testdata/domain_info_response_pendingdelete.xml +++ b/javatests/google/registry/flows/testdata/domain_info_response_wildcard.xml @@ -25,7 +25,7 @@ - + diff --git a/javatests/google/registry/testing/DatastoreHelper.java b/javatests/google/registry/testing/DatastoreHelper.java index 81cde7d83..8f79ef987 100644 --- a/javatests/google/registry/testing/DatastoreHelper.java +++ b/javatests/google/registry/testing/DatastoreHelper.java @@ -660,16 +660,20 @@ public class DatastoreHelper { /** Assert that the actual billing event matches the expected one, ignoring IDs. */ public static void assertBillingEventsEqual(BillingEvent actual, BillingEvent expected) { - assertThat(BILLING_EVENT_ID_STRIPPER.apply(actual)) - .isEqualTo(BILLING_EVENT_ID_STRIPPER.apply(expected)); + assertThat(stripBillingEventId(actual)).isEqualTo(stripBillingEventId(expected)); } /** Assert that the actual billing events match the expected ones, ignoring IDs and order. */ public static void assertBillingEventsEqual( Iterable actual, Iterable expected) { - assertThat(Streams.stream(actual).map(BILLING_EVENT_ID_STRIPPER).collect(toImmutableList())) + assertThat( + Streams.stream(actual) + .map(DatastoreHelper::stripBillingEventId) + .collect(toImmutableList())) .containsExactlyElementsIn( - Streams.stream(expected).map(BILLING_EVENT_ID_STRIPPER).collect(toImmutableList())); + Streams.stream(expected) + .map(DatastoreHelper::stripBillingEventId) + .collect(toImmutableList())); } /** Assert that the expected billing events are exactly the ones found in the fake Datastore. */ @@ -689,10 +693,12 @@ public class DatastoreHelper { EppResource resource, BillingEvent... expected) throws Exception { assertThat( Streams.stream(getBillingEvents(resource)) - .map(BILLING_EVENT_ID_STRIPPER) + .map(DatastoreHelper::stripBillingEventId) .collect(toImmutableList())) .containsExactlyElementsIn( - Arrays.stream(expected).map(BILLING_EVENT_ID_STRIPPER).collect(toImmutableList())); + Arrays.stream(expected) + .map(DatastoreHelper::stripBillingEventId) + .collect(toImmutableList())); } /** Assert that there are no billing events. */ @@ -700,9 +706,10 @@ public class DatastoreHelper { assertThat(getBillingEvents()).isEmpty(); } - /** Helper to effectively erase the billing event ID to facilitate comparison. */ - public static final Function BILLING_EVENT_ID_STRIPPER = - billingEvent -> billingEvent.asBuilder().setId(1L).build(); + /** Strips the billing event ID (really, sets it to a constant value) to facilitate comparison. */ + public static BillingEvent stripBillingEventId(BillingEvent billingEvent) { + return billingEvent.asBuilder().setId(1L).build(); + } /** Assert that the actual poll message matches the expected one, ignoring IDs. */ public static void assertPollMessagesEqual(PollMessage actual, PollMessage expected) {