Use DomainPricingLogic in ExpandRecurringBillingEventsAction (#1687)

* Inject DomainPricingLogic to action file

* Remove attempt to inject

* Merge conflict

* Fix non static to static issue

* Merge branch 'master' into at_internal/Expand
This commit is contained in:
Ben McIlwain 2022-06-29 17:39:15 -04:00 committed by GitHub
parent cd4770dd5b
commit 9665ddbe70
5 changed files with 315 additions and 17 deletions

View file

@ -26,7 +26,6 @@ import static google.registry.persistence.transaction.QueryComposer.Comparator.E
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.earliestOf;
@ -38,6 +37,7 @@ import com.google.common.collect.Range;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
import google.registry.flows.domain.DomainPricingLogic;
import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
@ -61,7 +61,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.joda.money.Money;
import org.joda.time.DateTime;
/**
@ -89,6 +88,7 @@ public class ExpandRecurringBillingEventsAction implements Runnable {
@Inject @Parameter(PARAM_DRY_RUN) boolean isDryRun;
@Inject @Parameter(PARAM_CURSOR_TIME) Optional<DateTime> cursorTimeParam;
@Inject DomainPricingLogic domainPricingLogic;
@Inject Response response;
@Inject ExpandRecurringBillingEventsAction() {}
@ -156,7 +156,8 @@ public class ExpandRecurringBillingEventsAction implements Runnable {
continue;
}
int billingEventsSaved =
expandBillingEvent(recurring, executeTime, cursorTime, isDryRun);
expandBillingEvent(
recurring, executeTime, cursorTime, isDryRun, domainPricingLogic);
batchBillingEventsSaved += billingEventsSaved;
if (billingEventsSaved > 0) {
expandedDomains.add(recurring.getTargetId());
@ -231,7 +232,11 @@ public class ExpandRecurringBillingEventsAction implements Runnable {
}
private static int expandBillingEvent(
Recurring recurring, DateTime executeTime, DateTime cursorTime, boolean isDryRun) {
Recurring recurring,
DateTime executeTime,
DateTime cursorTime,
boolean isDryRun,
DomainPricingLogic domainPricingLogic) {
ImmutableSet.Builder<OneTime> syntheticOneTimesBuilder = new ImmutableSet.Builder<>();
final Registry tld = Registry.get(getTldFromDomainName(recurring.getTargetId()));
@ -295,13 +300,16 @@ public class ExpandRecurringBillingEventsAction implements Runnable {
historyEntriesBuilder.add(historyEntry);
DateTime eventTime = billingTime.minus(tld.getAutoRenewGracePeriodLength());
// Determine the cost for a one-year renewal.
Money renewCost = getDomainRenewCost(recurring.getTargetId(), eventTime, 1);
syntheticOneTimesBuilder.add(
new OneTime.Builder()
.setBillingTime(billingTime)
.setRegistrarId(recurring.getRegistrarId())
.setCost(renewCost)
// Determine the cost for a one-year renewal.
.setCost(
domainPricingLogic
.getRenewPrice(tld, recurring.getTargetId(), eventTime, 1, recurring)
.getRenewCost())
.setEventTime(eventTime)
.setFlags(union(recurring.getFlags(), Flag.SYNTHETIC))
.setParent(historyEntry)

View file

@ -50,8 +50,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
/** A hook that customizes the renew price. */
@SuppressWarnings("unused")
public FeesAndCredits customizeRenewPrice(RenewPriceParameters priceParameters)
throws EppException {
public FeesAndCredits customizeRenewPrice(RenewPriceParameters priceParameters) {
return priceParameters.feesAndCredits();
}

View file

@ -51,10 +51,12 @@ import org.joda.time.DateTime;
*/
public final class DomainPricingLogic {
@Inject DomainPricingCustomLogic customLogic;
private final DomainPricingCustomLogic customLogic;
@Inject
DomainPricingLogic() {}
public DomainPricingLogic(DomainPricingCustomLogic customLogic) {
this.customLogic = customLogic;
}
/**
* Returns a new create price for the pricer.
@ -105,13 +107,12 @@ public final class DomainPricingLogic {
}
/** Returns a new renewal cost for the pricer. */
FeesAndCredits getRenewPrice(
public FeesAndCredits getRenewPrice(
Registry registry,
String domainName,
DateTime dateTime,
int years,
@Nullable Recurring recurringBillingEvent)
throws EppException {
@Nullable Recurring recurringBillingEvent) {
checkArgument(years > 0, "Number of years must be positive");
Money renewCost;
boolean isRenewCostPremiumPrice;

View file

@ -15,6 +15,8 @@
package google.registry.batch;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.common.Cursor.CursorType.RECURRING_BILLING;
import static google.registry.model.domain.Period.Unit.YEARS;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_AUTORENEW;
@ -38,6 +40,8 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.googlecode.objectify.Key;
import google.registry.flows.custom.DomainPricingCustomLogic;
import google.registry.flows.domain.DomainPricingLogic;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.OneTime;
@ -89,6 +93,8 @@ public class ExpandRecurringBillingEventsActionTest {
action.clock = clock;
action.cursorTimeParam = Optional.empty();
action.batchSize = 2;
action.domainPricingLogic =
new DomainPricingLogic(new DomainPricingCustomLogic(null, null, null));
createTld("tld");
domain =
persistResource(
@ -729,6 +735,112 @@ public class ExpandRecurringBillingEventsActionTest {
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_expandMultipleEvents_anchorTenant() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld2", USD, "example,USD 100"))
.build());
recurring = persistResource(recurring.asBuilder().setRenewalPriceBehavior(NONPREMIUM).build());
BillingEvent.Recurring recurring2 =
persistResource(
recurring
.asBuilder()
.setEventTime(recurring.getEventTime().plusMonths(3))
.setId(3L)
.build());
action.cursorTimeParam = Optional.of(START_OF_TIME);
runAction();
List<DomainHistory> persistedEntries =
getHistoryEntriesOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
assertThat(persistedEntries).hasSize(2);
assertHistoryEntryMatches(
domain,
persistedEntries.get(0),
"TheRegistrar",
DateTime.parse("2000-02-19T00:00:00Z"),
true);
BillingEvent.OneTime expected =
defaultOneTimeBuilder()
.setParent(persistedEntries.get(0))
.setCancellationMatchingBillingEvent(recurring.createVKey())
.build();
assertHistoryEntryMatches(
domain,
persistedEntries.get(1),
"TheRegistrar",
DateTime.parse("2000-05-20T00:00:00Z"),
true);
BillingEvent.OneTime expected2 =
defaultOneTimeBuilder()
.setBillingTime(DateTime.parse("2000-05-20T00:00:00Z"))
.setEventTime(DateTime.parse("2000-04-05T00:00:00Z"))
.setParent(persistedEntries.get(1))
.setCancellationMatchingBillingEvent(recurring2.createVKey())
.build();
assertBillingEventsForResource(domain, expected, expected2, recurring, recurring2);
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_expandMultipleEvents_premiumDomain_internalRegistration() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld2", USD, "example,USD 100"))
.build());
recurring =
persistResource(
recurring
.asBuilder()
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 4))
.build());
BillingEvent.Recurring recurring2 =
persistResource(
recurring
.asBuilder()
.setEventTime(recurring.getEventTime().plusMonths(3))
.setId(3L)
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 4))
.build());
action.cursorTimeParam = Optional.of(START_OF_TIME);
runAction();
List<DomainHistory> persistedEntries =
getHistoryEntriesOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
assertThat(persistedEntries).hasSize(2);
assertHistoryEntryMatches(
domain,
persistedEntries.get(0),
"TheRegistrar",
DateTime.parse("2000-02-19T00:00:00Z"),
true);
BillingEvent.OneTime expected =
defaultOneTimeBuilder()
.setCost(Money.of(USD, 4))
.setParent(persistedEntries.get(0))
.setCancellationMatchingBillingEvent(recurring.createVKey())
.build();
assertHistoryEntryMatches(
domain,
persistedEntries.get(1),
"TheRegistrar",
DateTime.parse("2000-05-20T00:00:00Z"),
true);
BillingEvent.OneTime expected2 =
defaultOneTimeBuilder()
.setCost(Money.of(USD, 4))
.setBillingTime(DateTime.parse("2000-05-20T00:00:00Z"))
.setEventTime(DateTime.parse("2000-04-05T00:00:00Z"))
.setParent(persistedEntries.get(1))
.setCancellationMatchingBillingEvent(recurring2.createVKey())
.build();
assertBillingEventsForResource(domain, expected, expected2, recurring, recurring2);
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_premiumDomain() throws Exception {
persistResource(
@ -749,6 +861,87 @@ public class ExpandRecurringBillingEventsActionTest {
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_premiumDomain_forAnchorTenant() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld2", USD, "example,USD 100"))
.build());
recurring = persistResource(recurring.asBuilder().setRenewalPriceBehavior(NONPREMIUM).build());
action.cursorTimeParam = Optional.of(START_OF_TIME);
runAction();
DomainHistory persistedEntry =
getOnlyHistoryEntryOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
assertHistoryEntryMatches(
domain, persistedEntry, "TheRegistrar", DateTime.parse("2000-02-19T00:00:00Z"), true);
BillingEvent.OneTime expected =
defaultOneTimeBuilder().setParent(persistedEntry).setCost(Money.of(USD, 11)).build();
assertBillingEventsForResource(domain, expected, recurring);
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_standardDomain_forAnchorTenant() throws Exception {
recurring = persistResource(recurring.asBuilder().setRenewalPriceBehavior(NONPREMIUM).build());
action.cursorTimeParam = Optional.of(START_OF_TIME);
runAction();
DomainHistory persistedEntry =
getOnlyHistoryEntryOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
assertHistoryEntryMatches(
domain, persistedEntry, "TheRegistrar", DateTime.parse("2000-02-19T00:00:00Z"), true);
BillingEvent.OneTime expected = defaultOneTimeBuilder().setParent(persistedEntry).build();
assertBillingEventsForResource(domain, expected, recurring);
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_premiumDomain_forInternalRegistration() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld2", USD, "example,USD 100"))
.build());
recurring =
persistResource(
recurring
.asBuilder()
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 20))
.build());
action.cursorTimeParam = Optional.of(START_OF_TIME);
runAction();
DomainHistory persistedEntry =
getOnlyHistoryEntryOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
assertHistoryEntryMatches(
domain, persistedEntry, "TheRegistrar", DateTime.parse("2000-02-19T00:00:00Z"), true);
BillingEvent.OneTime expected =
defaultOneTimeBuilder().setParent(persistedEntry).setCost(Money.of(USD, 20)).build();
assertBillingEventsForResource(domain, expected, recurring);
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_standardDomain_forInternalRegistration() throws Exception {
recurring =
persistResource(
recurring
.asBuilder()
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 2))
.build());
action.cursorTimeParam = Optional.of(START_OF_TIME);
runAction();
DomainHistory persistedEntry =
getOnlyHistoryEntryOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
assertHistoryEntryMatches(
domain, persistedEntry, "TheRegistrar", DateTime.parse("2000-02-19T00:00:00Z"), true);
BillingEvent.OneTime expected =
defaultOneTimeBuilder().setParent(persistedEntry).setCost(Money.of(USD, 2)).build();
assertBillingEventsForResource(domain, expected, recurring);
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_varyingRenewPrices() throws Exception {
clock.setTo(currentTestTime);
@ -793,6 +986,102 @@ public class ExpandRecurringBillingEventsActionTest {
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_varyingRenewPrices_anchorTenant() throws Exception {
clock.setTo(currentTestTime);
persistResource(
Registry.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld2", USD, "example,USD 100"))
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
Money.of(USD, 8),
DateTime.parse("2000-06-01T00:00:00Z"),
Money.of(USD, 10)))
.build());
clock.setTo(DateTime.parse("2001-10-02T00:00:00Z"));
recurring = persistResource(recurring.asBuilder().setRenewalPriceBehavior(NONPREMIUM).build());
action.cursorTimeParam = Optional.of(START_OF_TIME);
runAction();
List<DomainHistory> persistedEntries =
getHistoryEntriesOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
assertThat(persistedEntries).hasSize(2);
DateTime eventDate = DateTime.parse("2000-01-05T00:00:00Z");
DateTime billingDate = DateTime.parse("2000-02-19T00:00:00Z");
assertHistoryEntryMatches(domain, persistedEntries.get(0), "TheRegistrar", billingDate, true);
BillingEvent.OneTime cheaper =
defaultOneTimeBuilder()
.setBillingTime(billingDate)
.setEventTime(eventDate)
.setParent(persistedEntries.get(0))
.setCost(Money.of(USD, 8))
.build();
assertHistoryEntryMatches(
domain, persistedEntries.get(1), "TheRegistrar", billingDate.plusYears(1), true);
BillingEvent.OneTime expensive =
cheaper
.asBuilder()
.setCost(Money.of(USD, 10))
.setBillingTime(billingDate.plusYears(1))
.setEventTime(eventDate.plusYears(1))
.setParent(persistedEntries.get(1))
.build();
assertBillingEventsForResource(domain, recurring, cheaper, expensive);
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testSuccess_varyingRenewPrices_internalRegistration() throws Exception {
clock.setTo(currentTestTime);
persistResource(
Registry.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld2", USD, "example,USD 100"))
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
Money.of(USD, 8),
DateTime.parse("2000-06-01T00:00:00Z"),
Money.of(USD, 10)))
.build());
clock.setTo(DateTime.parse("2001-10-02T00:00:00Z"));
recurring =
persistResource(
recurring
.asBuilder()
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 5))
.build());
action.cursorTimeParam = Optional.of(START_OF_TIME);
runAction();
List<DomainHistory> persistedEntries =
getHistoryEntriesOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
assertThat(persistedEntries).hasSize(2);
DateTime eventDate = DateTime.parse("2000-01-05T00:00:00Z");
DateTime billingDate = DateTime.parse("2000-02-19T00:00:00Z");
assertHistoryEntryMatches(domain, persistedEntries.get(0), "TheRegistrar", billingDate, true);
BillingEvent.OneTime cheaper =
defaultOneTimeBuilder()
.setBillingTime(billingDate)
.setEventTime(eventDate)
.setParent(persistedEntries.get(0))
.setCost(Money.of(USD, 5))
.build();
assertHistoryEntryMatches(
domain, persistedEntries.get(1), "TheRegistrar", billingDate.plusYears(1), true);
BillingEvent.OneTime expensive =
cheaper
.asBuilder()
.setCost(Money.of(USD, 5))
.setBillingTime(billingDate.plusYears(1))
.setEventTime(eventDate.plusYears(1))
.setParent(persistedEntries.get(1))
.build();
assertBillingEventsForResource(domain, recurring, cheaper, expensive);
assertCursorAt(currentTestTime);
}
@TestOfyAndSql
void testFailure_cursorAfterExecutionTime() {
action.cursorTimeParam = Optional.of(clock.nowUtc().plusYears(1));

View file

@ -64,7 +64,7 @@ import org.mockito.Mock;
/** Unit tests for {@link DomainPricingLogic}. */
@DualDatabaseTest
public class DomainPricingLogicTest {
DomainPricingLogic domainPricingLogic = new DomainPricingLogic();
DomainPricingLogic domainPricingLogic;
@RegisterExtension
public final AppEngineExtension appEngine =
@ -81,8 +81,9 @@ public class DomainPricingLogicTest {
void beforeEach() throws Exception {
createTld("example");
sessionMetadata = new HttpSessionMetadata(new FakeHttpSession());
domainPricingLogic.customLogic =
new DomainPricingCustomLogic(eppInput, sessionMetadata, flowMetadata);
domainPricingLogic =
new DomainPricingLogic(
new DomainPricingCustomLogic(eppInput, sessionMetadata, flowMetadata));
registry =
persistResource(
Registry.get("example")