mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 425ecdcd87 | |||
| 77ee124374 | |||
| b9742adc0b | |||
| d4cd25c4ae | |||
| 8b7e938ed6 | |||
| c216c874b4 | |||
| 0ab9471c8d |
@@ -103,6 +103,7 @@ explodeWar.doLast {
|
||||
file("${it.explodedAppDirectory}/WEB-INF/lib/tools.jar").setWritable(true)
|
||||
}
|
||||
|
||||
appengineDeployAll.finalizedBy ':cloudSchedulerDeployer'
|
||||
rootProject.deploy.dependsOn appengineDeployAll
|
||||
rootProject.stage.dependsOn appengineStage
|
||||
tasks['war'].dependsOn ':console-webapp:buildConsoleWebappProd'
|
||||
|
||||
@@ -558,6 +558,21 @@ task coreDev {
|
||||
|
||||
javadocDependentTasks.each { tasks.javadoc.dependsOn(it) }
|
||||
|
||||
// Runs the script, which deploys cloud scheduler tasks based on the config
|
||||
task cloudSchedulerDeployer {
|
||||
doLast {
|
||||
def env = environment
|
||||
if (!prodOrSandboxEnv) {
|
||||
exec {
|
||||
commandLine 'go', 'run',
|
||||
"${rootDir}/release/builder/cloudSchedulerDeployer.go",
|
||||
"${rootDir}/core/src/main/java/google/registry/env/${env}/default/WEB-INF/cloud-scheduler-tasks.xml",
|
||||
"domain-registry-${env}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disable javadoc in subprojects, these will break because they don't have
|
||||
// the correct classpath (see above).
|
||||
gradle.taskGraph.whenReady { graph ->
|
||||
|
||||
@@ -1028,6 +1028,7 @@ test {
|
||||
// TODO(weiminyu): Remove dependency on sqlIntegrationTest
|
||||
}.dependsOn(fragileTest, outcastTest, standardTest, registryToolIntegrationTest, sqlIntegrationTest)
|
||||
|
||||
|
||||
// When we override tests, we also break the cleanTest command.
|
||||
cleanTest.dependsOn(cleanFragileTest, cleanOutcastTest, cleanStandardTest,
|
||||
cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest)
|
||||
|
||||
+27
-18
@@ -36,6 +36,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.custom.CustomLogicModule;
|
||||
import google.registry.flows.domain.DomainPricingLogic;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent.Cancellation;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
@@ -53,6 +54,7 @@ import google.registry.util.Clock;
|
||||
import google.registry.util.SystemClock;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
@@ -383,24 +385,31 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
// It is OK to always create a OneTime, even though the domain might be deleted or transferred
|
||||
// later during autorenew grace period, as a cancellation will always be written out in those
|
||||
// instances.
|
||||
OneTime oneTime =
|
||||
new OneTime.Builder()
|
||||
.setBillingTime(billingTime)
|
||||
.setRegistrarId(recurring.getRegistrarId())
|
||||
// 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))
|
||||
.setDomainHistory(historyEntry)
|
||||
.setPeriodYears(1)
|
||||
.setReason(recurring.getReason())
|
||||
.setSyntheticCreationTime(endTime)
|
||||
.setCancellationMatchingBillingEvent(recurring)
|
||||
.setTargetId(recurring.getTargetId())
|
||||
.build();
|
||||
OneTime oneTime = null;
|
||||
try {
|
||||
oneTime =
|
||||
new OneTime.Builder()
|
||||
.setBillingTime(billingTime)
|
||||
.setRegistrarId(recurring.getRegistrarId())
|
||||
// Determine the cost for a one-year renewal.
|
||||
.setCost(
|
||||
domainPricingLogic
|
||||
.getRenewPrice(
|
||||
tld, recurring.getTargetId(), eventTime, 1, recurring, Optional.empty())
|
||||
.getRenewCost())
|
||||
.setEventTime(eventTime)
|
||||
.setFlags(union(recurring.getFlags(), Flag.SYNTHETIC))
|
||||
.setDomainHistory(historyEntry)
|
||||
.setPeriodYears(1)
|
||||
.setReason(recurring.getReason())
|
||||
.setSyntheticCreationTime(endTime)
|
||||
.setCancellationMatchingBillingEvent(recurring)
|
||||
.setTargetId(recurring.getTargetId())
|
||||
.build();
|
||||
} catch (AllocationTokenInvalidForPremiumNameException e) {
|
||||
// This should not be reached since we are not using an allocation token
|
||||
return;
|
||||
}
|
||||
results.add(oneTime);
|
||||
}
|
||||
results.add(
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
</cronentries>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
</cronentries>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
</cronentries>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
</cronentries>
|
||||
-3
@@ -11,9 +11,6 @@
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date eventually.
|
||||
|
||||
See <a href="../../../production/default/WEB-INF/cron.xml">production config</a> for an
|
||||
explanation of job starting times.
|
||||
-->
|
||||
<schedule>7 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
@@ -265,6 +265,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
validateLaunchCreateNotice(launchCreate.get().getNotice(), domainLabel, isSuperuser, now);
|
||||
}
|
||||
boolean isSunriseCreate = hasSignedMarks && (tldState == START_DATE_SUNRISE);
|
||||
// TODO(sarahbot@): Add check for valid EPP actions on the token
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenCreateIfPresent(
|
||||
command,
|
||||
|
||||
@@ -674,6 +674,8 @@ public class DomainFlowUtils {
|
||||
String feeClass = null;
|
||||
ImmutableList<Fee> fees = ImmutableList.of();
|
||||
switch (feeRequest.getCommandName()) {
|
||||
// TODO(sarahbot@): Add check of valid EPP actions on token before passing the token to the
|
||||
// fee request.
|
||||
case CREATE:
|
||||
// Don't return a create price for reserved names.
|
||||
if (isReserved(domainName, isSunrise) && !isAvailable) {
|
||||
@@ -698,7 +700,8 @@ public class DomainFlowUtils {
|
||||
builder.setAvailIfSupported(true);
|
||||
fees =
|
||||
pricingLogic
|
||||
.getRenewPrice(registry, domainNameString, now, years, recurringBillingEvent)
|
||||
.getRenewPrice(
|
||||
registry, domainNameString, now, years, recurringBillingEvent, allocationToken)
|
||||
.getFees();
|
||||
break;
|
||||
case RESTORE:
|
||||
|
||||
@@ -34,6 +34,7 @@ import google.registry.model.domain.fee.BaseFee;
|
||||
import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenBehavior;
|
||||
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
|
||||
import google.registry.model.tld.Registry;
|
||||
import java.math.RoundingMode;
|
||||
@@ -112,21 +113,22 @@ public final class DomainPricingLogic {
|
||||
String domainName,
|
||||
DateTime dateTime,
|
||||
int years,
|
||||
@Nullable Recurring recurringBillingEvent) {
|
||||
@Nullable Recurring recurringBillingEvent,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
checkArgument(years > 0, "Number of years must be positive");
|
||||
Money renewCost;
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
boolean isRenewCostPremiumPrice;
|
||||
// recurring billing event is null if the domain is still available. Billing events are created
|
||||
// in the process of domain creation.
|
||||
if (recurringBillingEvent == null) {
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
renewCost = domainPrices.getRenewCost().multipliedBy(years);
|
||||
renewCost = getDomainRenewCostWithDiscount(domainPrices, years, allocationToken);
|
||||
isRenewCostPremiumPrice = domainPrices.isPremium();
|
||||
} else {
|
||||
switch (recurringBillingEvent.getRenewalPriceBehavior()) {
|
||||
case DEFAULT:
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
renewCost = domainPrices.getRenewCost().multipliedBy(years);
|
||||
renewCost = getDomainRenewCostWithDiscount(domainPrices, years, allocationToken);
|
||||
isRenewCostPremiumPrice = domainPrices.isPremium();
|
||||
break;
|
||||
// if the renewal price behavior is specified, then the renewal price should be the same
|
||||
@@ -136,6 +138,7 @@ public final class DomainPricingLogic {
|
||||
recurringBillingEvent.getRenewalPrice(),
|
||||
"Unexpected behavior: renewal price cannot be null when renewal behavior is"
|
||||
+ " SPECIFIED");
|
||||
// Don't apply allocation token to renewal price when SPECIFIED
|
||||
renewCost = recurringBillingEvent.getRenewalPrice().get().multipliedBy(years);
|
||||
isRenewCostPremiumPrice = false;
|
||||
break;
|
||||
@@ -143,9 +146,11 @@ public final class DomainPricingLogic {
|
||||
// at standard price of domains at the time, even if the domain is premium
|
||||
case NONPREMIUM:
|
||||
renewCost =
|
||||
Registry.get(getTldFromDomainName(domainName))
|
||||
.getStandardRenewCost(dateTime)
|
||||
.multipliedBy(years);
|
||||
getDomainCostWithDiscount(
|
||||
false,
|
||||
years,
|
||||
allocationToken,
|
||||
Registry.get(getTldFromDomainName(domainName)).getStandardRenewCost(dateTime));
|
||||
isRenewCostPremiumPrice = false;
|
||||
break;
|
||||
default:
|
||||
@@ -202,7 +207,7 @@ public final class DomainPricingLogic {
|
||||
@Nullable Recurring recurringBillingEvent)
|
||||
throws EppException {
|
||||
FeesAndCredits renewPrice =
|
||||
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent);
|
||||
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent, Optional.empty());
|
||||
return customLogic.customizeTransferPrice(
|
||||
TransferPriceParameters.newBuilder()
|
||||
.setFeesAndCredits(
|
||||
@@ -242,25 +247,40 @@ public final class DomainPricingLogic {
|
||||
private Money getDomainCreateCostWithDiscount(
|
||||
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken)
|
||||
throws EppException {
|
||||
return getDomainCostWithDiscount(
|
||||
domainPrices.isPremium(), years, allocationToken, domainPrices.getCreateCost());
|
||||
}
|
||||
|
||||
/** Returns the domain renew cost with allocation-token-related discounts applied. */
|
||||
private Money getDomainRenewCostWithDiscount(
|
||||
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
return getDomainCostWithDiscount(
|
||||
domainPrices.isPremium(), years, allocationToken, domainPrices.getRenewCost());
|
||||
}
|
||||
|
||||
private Money getDomainCostWithDiscount(
|
||||
boolean isPremium, int years, Optional<AllocationToken> allocationToken, Money oneYearCost)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getDiscountFraction() != 0.0
|
||||
&& domainPrices.isPremium()
|
||||
&& isPremium
|
||||
&& !allocationToken.get().shouldDiscountPremiums()) {
|
||||
throw new AllocationTokenInvalidForPremiumNameException();
|
||||
}
|
||||
Money oneYearCreateCost = domainPrices.getCreateCost();
|
||||
Money totalDomainCreateCost = oneYearCreateCost.multipliedBy(years);
|
||||
Money totalDomainFlowCost = oneYearCost.multipliedBy(years);
|
||||
|
||||
// Apply the allocation token discount, if applicable.
|
||||
if (allocationToken.isPresent()) {
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getTokenBehavior().equals(TokenBehavior.DEFAULT)) {
|
||||
int discountedYears = Math.min(years, allocationToken.get().getDiscountYears());
|
||||
Money discount =
|
||||
oneYearCreateCost.multipliedBy(
|
||||
oneYearCost.multipliedBy(
|
||||
discountedYears * allocationToken.get().getDiscountFraction(),
|
||||
RoundingMode.HALF_EVEN);
|
||||
totalDomainCreateCost = totalDomainCreateCost.minus(discount);
|
||||
totalDomainFlowCost = totalDomainFlowCost.minus(discount);
|
||||
}
|
||||
return totalDomainCreateCost;
|
||||
return totalDomainFlowCost;
|
||||
}
|
||||
|
||||
/** An allocation token was provided that is invalid for premium domains. */
|
||||
|
||||
@@ -172,6 +172,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
Renew command = (Renew) resourceCommand;
|
||||
// Loads the target resource if it exists
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
// TODO(sarahbot@): Add check for valid EPP actions on the token
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
@@ -198,7 +199,8 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
targetId,
|
||||
now,
|
||||
years,
|
||||
existingRecurringBillingEvent);
|
||||
existingRecurringBillingEvent,
|
||||
allocationToken);
|
||||
validateFeeChallenge(feeRenew, feesAndCredits, false);
|
||||
flowCustomLogic.afterValidation(
|
||||
AfterValidationParameters.newBuilder()
|
||||
|
||||
@@ -275,6 +275,26 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The time to live for DNS A and AAAA records.
|
||||
*
|
||||
* <p>When this field is null, the "dnsDefaultATtl" value from the config file will be used.
|
||||
*/
|
||||
Duration dnsAPlusAaaaTtl;
|
||||
|
||||
/**
|
||||
* The time to live for DNS NS records.
|
||||
*
|
||||
* <p>When this field is null, the "dnsDefaultNsTtl" value from the config file will be used.
|
||||
*/
|
||||
Duration dnsNsTtl;
|
||||
|
||||
/**
|
||||
* The time to live for DNS DS records.
|
||||
*
|
||||
* <p>When this field is null, the "dnsDefaultDsTtl" value from the config file will be used.
|
||||
*/
|
||||
Duration dnsDsTtl;
|
||||
/**
|
||||
* The unicode-aware representation of the TLD associated with this {@link Registry}.
|
||||
*
|
||||
@@ -647,6 +667,21 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
return numDnsPublishLocks;
|
||||
}
|
||||
|
||||
/** Returns the time to live for A and AAAA records. */
|
||||
public Duration getDnsAPlusAaaaTtl() {
|
||||
return dnsAPlusAaaaTtl;
|
||||
}
|
||||
|
||||
/** Returns the time to live for NS records. */
|
||||
public Duration getDnsNsTtl() {
|
||||
return dnsNsTtl;
|
||||
}
|
||||
|
||||
/** Returns the time to live for DS records. */
|
||||
public Duration getDnsDsTtl() {
|
||||
return dnsDsTtl;
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getAllowedRegistrantContactIds() {
|
||||
return nullToEmptyImmutableCopy(allowedRegistrantContactIds);
|
||||
}
|
||||
@@ -737,6 +772,21 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDnsAPlusAaaaTtl(Duration dnsAPlusAaaaTtl) {
|
||||
getInstance().dnsAPlusAaaaTtl = dnsAPlusAaaaTtl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDnsNsAtl(Duration dnsNsAtl) {
|
||||
getInstance().dnsNsTtl = dnsNsAtl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDnsDsAtl(Duration dnsDsAtl) {
|
||||
getInstance().dnsDsTtl = dnsDsAtl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAddGracePeriodLength(Duration addGracePeriodLength) {
|
||||
checkArgument(
|
||||
addGracePeriodLength.isLongerThan(Duration.ZERO),
|
||||
|
||||
@@ -1577,7 +1577,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, BigDecimal.valueOf(19.5)));
|
||||
}
|
||||
@@ -1627,7 +1627,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.put("EXDATE", "2004-04-03T22:00:00.0Z")
|
||||
.build()));
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getCost()).isEqualTo(expectedPrice);
|
||||
}
|
||||
@@ -1660,7 +1660,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"domain_create_response_premium.xml",
|
||||
ImmutableMap.of("EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "104.00")));
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("rich.example");
|
||||
// 1yr @ $100 + 2yrs @ $100 * (1 - 0.98) = $104
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 104.00));
|
||||
@@ -1693,7 +1693,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"domain_create_response_premium.xml",
|
||||
ImmutableMap.of("EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "204.44")));
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("rich.example");
|
||||
// 2yrs @ $100 + 1yr @ $100 * (1 - 0.95555) = $204.44
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 204.44));
|
||||
@@ -1862,7 +1862,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, BigDecimal.valueOf(19.5)));
|
||||
assertThat(billingEvent.getAllocationToken().get().getKey()).isEqualTo("abc123");
|
||||
@@ -2058,7 +2058,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.put("EXDATE", "2001-04-03T22:00:00.0Z")
|
||||
.build()));
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getAllocationToken().get().getKey()).isEqualTo(token);
|
||||
return billingEvent;
|
||||
|
||||
@@ -20,6 +20,7 @@ import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.DE
|
||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
|
||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.domain.fee.BaseFee.FeeType.RENEW;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
@@ -32,11 +33,13 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowMetadata;
|
||||
import google.registry.flows.HttpSessionMetadata;
|
||||
import google.registry.flows.SessionMetadata;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
@@ -44,6 +47,7 @@ import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
@@ -133,7 +137,8 @@ public class DomainPricingLogicTest {
|
||||
void testGetDomainRenewPrice_oneYear_standardDomain_noBilling_isStandardPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(registry, "standard.example", clock.nowUtc(), 1, null))
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "standard.example", clock.nowUtc(), 1, null, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -145,7 +150,8 @@ public class DomainPricingLogicTest {
|
||||
void testGetDomainRenewPrice_multiYear_standardDomain_noBilling_isStandardPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(registry, "standard.example", clock.nowUtc(), 5, null))
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "standard.example", clock.nowUtc(), 5, null, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -157,7 +163,8 @@ public class DomainPricingLogicTest {
|
||||
void testGetDomainRenewPrice_oneYear_premiumDomain_noBilling_isPremiumPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(registry, "premium.example", clock.nowUtc(), 1, null))
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "premium.example", clock.nowUtc(), 1, null, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -169,7 +176,8 @@ public class DomainPricingLogicTest {
|
||||
void testGetDomainRenewPrice_multiYear_premiumDomain_noBilling_isPremiumPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(registry, "premium.example", clock.nowUtc(), 5, null))
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "premium.example", clock.nowUtc(), 5, null, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -186,7 +194,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", DEFAULT, Optional.empty())))
|
||||
"premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -194,6 +203,58 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_oneYear_premiumDomain_default_withToken_isPremiumPrice()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(true)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 50).getAmount(), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_oneYear_premiumDomain_default_withTokenNotValidForPremiums_throwsException()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
assertThrows(
|
||||
AllocationTokenInvalidForPremiumNameException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_multiYear_premiumDomain_default_isPremiumCost() throws EppException {
|
||||
assertThat(
|
||||
@@ -203,7 +264,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", DEFAULT, Optional.empty())))
|
||||
"premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -211,6 +273,60 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_multiYear_premiumDomain_default_withToken_isPremiumPrice()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(true)
|
||||
.setDiscountYears(2)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 400).getAmount(), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_multiYear_premiumDomain_default_withTokenNotValidForPremiums_throwsException()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.setDiscountYears(2)
|
||||
.build());
|
||||
assertThrows(
|
||||
AllocationTokenInvalidForPremiumNameException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_oneYear_standardDomain_default_isNonPremiumPrice()
|
||||
throws EppException {
|
||||
@@ -221,7 +337,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", DEFAULT, Optional.empty())))
|
||||
"standard.example", DEFAULT, Optional.empty()),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -229,6 +346,33 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_oneYear_standardDomain_default_withToken_isDiscountedPrice()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_multiYear_standardDomain_default_isNonPremiumCost()
|
||||
throws EppException {
|
||||
@@ -239,7 +383,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", DEFAULT, Optional.empty())))
|
||||
"standard.example", DEFAULT, Optional.empty()),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -247,6 +392,34 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_multiYear_standardDomain_default_withToken_isDiscountedPrice()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.setDiscountYears(2)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 40).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_oneYear_premiumDomain_anchorTenant_isNonPremiumPrice()
|
||||
throws EppException {
|
||||
@@ -257,7 +430,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", NONPREMIUM, Optional.empty())))
|
||||
"premium.example", NONPREMIUM, Optional.empty()),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -265,6 +439,34 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_oneYear_premiumDomain_anchorTenant__withToken_isDiscountedNonPremiumPrice()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", NONPREMIUM, Optional.empty()),
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_multiYear_premiumDomain_anchorTenant_isNonPremiumCost()
|
||||
throws EppException {
|
||||
@@ -275,7 +477,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", NONPREMIUM, Optional.empty())))
|
||||
"premium.example", NONPREMIUM, Optional.empty()),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -283,6 +486,35 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_multiYear_premiumDomain_anchorTenant__withToken_isDiscountedNonPremiumPrice()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.setDiscountYears(2)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", NONPREMIUM, Optional.empty()),
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 40).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_oneYear_standardDomain_anchorTenant_isNonPremiumPrice()
|
||||
throws EppException {
|
||||
@@ -293,7 +525,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", NONPREMIUM, Optional.empty())))
|
||||
"standard.example", NONPREMIUM, Optional.empty()),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -311,7 +544,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", NONPREMIUM, Optional.empty())))
|
||||
"standard.example", NONPREMIUM, Optional.empty()),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -329,7 +563,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1)))))
|
||||
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -337,6 +572,71 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_oneYear_standardDomain_internalRegistration_withToken_isSpecifiedPrice()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
|
||||
Optional.of(allocationToken)))
|
||||
|
||||
// The allocation token should not discount the speicifed price
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_oneYear_standardDomain_internalRegistration_withToken_doesNotChangePriceBehavior()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setRenewalPriceBehavior(DEFAULT)
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
|
||||
Optional.of(allocationToken)))
|
||||
|
||||
// The allocation token should not discount the speicifed price
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1).getAmount(), RENEW, false))
|
||||
.build());
|
||||
assertThat(
|
||||
Iterables.getLast(DatabaseHelper.loadAllOf(BillingEvent.Recurring.class))
|
||||
.getRenewalPriceBehavior())
|
||||
.isEqualTo(SPECIFIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_multiYear_standardDomain_internalRegistration_isSpecifiedPrice()
|
||||
throws EppException {
|
||||
@@ -347,7 +647,36 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1)))))
|
||||
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void
|
||||
testGetDomainRenewPrice_multiYear_standardDomain_internalRegistration_withToken_isSpecifiedPrice()
|
||||
throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountPremiums(false)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -365,7 +694,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 17)))))
|
||||
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 17))),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -383,7 +713,8 @@ public class DomainPricingLogicTest {
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 17)))))
|
||||
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 17))),
|
||||
Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -398,7 +729,7 @@ public class DomainPricingLogicTest {
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "standard.example", clock.nowUtc(), -1, null));
|
||||
registry, "standard.example", clock.nowUtc(), -1, null, Optional.empty()));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Number of years must be positive");
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.truth.Truth8;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppRequestSource;
|
||||
@@ -604,12 +605,21 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("example.tld")
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountYears(1)
|
||||
.build());
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_renew_response.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
|
||||
assertThat(DatabaseHelper.loadByEntity(allocationToken).getRedemptionHistoryId()).isPresent();
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getAllocationToken().get().getKey())
|
||||
.isEqualTo(allocationToken.getToken());
|
||||
// Price is 50% off the first year only. Non-discounted price is $11.
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 16.5));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -619,11 +629,20 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
|
||||
persistDomain();
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("abc123").setTokenType(UNLIMITED_USE).build());
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.build());
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_renew_response.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getAllocationToken().get().getKey()).isEqualTo("abc123");
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 16.5));
|
||||
clock.advanceOneMilli();
|
||||
setEppInput(
|
||||
"domain_renew_allocationtoken.xml",
|
||||
@@ -633,6 +652,38 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
loadFile(
|
||||
"domain_renew_response.xml",
|
||||
ImmutableMap.of("DOMAIN", "other-example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
|
||||
billingEvent = Iterables.getLast(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("other-example.tld");
|
||||
assertThat(billingEvent.getAllocationToken().get().getKey()).isEqualTo("abc123");
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 16.5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_allocationTokenMultiYearDiscount() throws Exception {
|
||||
setEppInput(
|
||||
"domain_renew_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
|
||||
persistDomain();
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("example.tld")
|
||||
.setDiscountFraction(0.5)
|
||||
.setDiscountYears(10)
|
||||
.build());
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_renew_response.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
|
||||
assertThat(DatabaseHelper.loadByEntity(allocationToken).getRedemptionHistoryId()).isPresent();
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getAllocationToken().get().getKey())
|
||||
.isEqualTo(allocationToken.getToken());
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 11));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -750,6 +801,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryId(historyEntryId)
|
||||
.setDiscountFraction(0.5)
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
EppException thrown =
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>renew</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="renew">11.00</fee:fee>
|
||||
<fee:fee description="renew">5.50</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:name>example1.tld</fee:name>
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
</fee:object>
|
||||
<fee:command name="renew">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="renew">11.00</fee:fee>
|
||||
<fee:fee description="renew">5.50</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
|
||||
@@ -261,11 +261,11 @@ td.section {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2023-03-16 20:10:49.374346</td>
|
||||
<td class="property_value">2023-03-28 19:28:38.164363</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
<td id="lastFlywayFile" class="property_value">V141__add_ttl_columns_to_tld.sql</td>
|
||||
<td id="lastFlywayFile" class="property_value">V143__idn_per_tld.sql</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -284,7 +284,7 @@ td.section {
|
||||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="3835.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2023-03-16 20:10:49.374346
|
||||
2023-03-28 19:28:38.164363
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="3748,-4 3748,-44 4013,-44 4013,-4 3748,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -139,3 +139,5 @@ V138__drop_dns_refresh_request_time_column.sql
|
||||
V139__add_allowed_epp_actions_column.sql
|
||||
V140__rename_process_time_column_in_dns_refresh_request_table.sql
|
||||
V141__add_ttl_columns_to_tld.sql
|
||||
V142__drop_request_log_id.sql
|
||||
V143__idn_per_tld.sql
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Copyright 2022 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.
|
||||
|
||||
ALTER TABLE "Lock" DROP COLUMN request_log_id;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Copyright 2023 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.
|
||||
|
||||
ALTER TABLE "Tld" ADD COLUMN IF NOT EXISTS idn_tables text[];
|
||||
@@ -716,6 +716,9 @@
|
||||
creation_time timestamptz not null,
|
||||
currency text not null,
|
||||
default_promo_tokens text[],
|
||||
dns_a_plus_aaaa_ttl interval,
|
||||
dns_ds_ttl interval,
|
||||
dns_ns_ttl interval,
|
||||
dns_paused boolean not null,
|
||||
dns_writers text[] not null,
|
||||
drive_folder_id text,
|
||||
|
||||
@@ -674,8 +674,7 @@ CREATE TABLE public."Lock" (
|
||||
resource_name text NOT NULL,
|
||||
scope text NOT NULL,
|
||||
acquired_time timestamp with time zone NOT NULL,
|
||||
expiration_time timestamp with time zone NOT NULL,
|
||||
request_log_id text
|
||||
expiration_time timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@@ -1095,7 +1094,8 @@ CREATE TABLE public."Tld" (
|
||||
default_promo_tokens text[],
|
||||
dns_a_plus_aaaa_ttl interval,
|
||||
dns_ds_ttl interval,
|
||||
dns_ns_ttl interval
|
||||
dns_ns_ttl interval,
|
||||
idn_tables text[]
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ steps:
|
||||
else
|
||||
project_id="domain-registry-${_ENV}"
|
||||
fi
|
||||
for filename in cron dispatch queue; do
|
||||
for filename in dispatch queue; do
|
||||
gcloud -q --project $project_id app deploy \
|
||||
default/WEB-INF/appengine-generated/$filename.yaml
|
||||
done
|
||||
|
||||
@@ -75,5 +75,6 @@ while (( "$#" > 0 )); do
|
||||
--metadata-file "./core/src/main/resources/${metadata_pathname}" \
|
||||
--jar "./core/build/libs/${uberjar_name}.jar" \
|
||||
--env FLEX_TEMPLATE_JAVA_MAIN_CLASS="${main_class}" \
|
||||
--additional-experiments=disable_runner_v2 \
|
||||
--project "${dev_project}"
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user