mirror of
https://github.com/google/nomulus.git
synced 2025-07-21 18:26:12 +02:00
Use a potential discount in the AllocationToken when determining domain create price
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=245458027
This commit is contained in:
parent
1a1ff94bc5
commit
70c7e6c224
14 changed files with 388 additions and 96 deletions
|
@ -15,6 +15,7 @@
|
|||
package google.registry.flows.domain;
|
||||
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.model.eppoutput.CheckData.DomainCheck.create;
|
||||
import static google.registry.model.registry.Registry.TldState.PREDELEGATION;
|
||||
import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE;
|
||||
|
@ -63,6 +64,7 @@ import google.registry.flows.domain.DomainFlowUtils.UnknownFeeCommandException;
|
|||
import google.registry.flows.exceptions.TooManyResourceChecksException;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.registry.label.ReservedList;
|
||||
|
@ -182,6 +184,24 @@ public class DomainCheckFlowTest
|
|||
create(false, "premiumcollision.tld", "Cannot be delegated"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_allocationTokenPromotion() throws Exception {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().plusMillis(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_check_allocationtoken_fee.xml");
|
||||
runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_oneReservedInSunrise() throws Exception {
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
|
|
|
@ -65,6 +65,7 @@ import com.google.common.collect.ImmutableList;
|
|||
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.googlecode.objectify.Key;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.flows.EppException;
|
||||
|
@ -140,6 +141,8 @@ import google.registry.model.domain.launch.LaunchNotice;
|
|||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
|
@ -151,6 +154,7 @@ import google.registry.model.reporting.DomainTransactionRecord.TransactionReport
|
|||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
|
@ -1101,6 +1105,57 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
|
|||
.isEqualTo(Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_allocationTokenPromotion() throws Exception {
|
||||
// A discount of 0.5 means that the first-year cost (13) is cut in half, so a discount of 6.5
|
||||
// Note: we're asking to register it for two years so the total cost should be 13 + (13/2)
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().plusMillis(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
BillingEvent.OneTime billingEvent =
|
||||
Iterables.getOnlyElement(ofy().load().type(BillingEvent.OneTime.class));
|
||||
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
|
||||
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, BigDecimal.valueOf(19.5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_promotionDoesNotApplyToPremiumPrice() {
|
||||
// At the moment, discounts cannot apply to premium domains
|
||||
createTld("example");
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().plusMillis(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
setEppInput("domain_create_premium_allocationtoken.xml");
|
||||
assertThat(assertThrows(IllegalArgumentException.class, this::runFlow))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("A nonzero discount code cannot be applied to premium domains");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_superuserReserved() throws Exception {
|
||||
setEppInput("domain_create_reserved.xml");
|
||||
|
|
41
javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee.xml
vendored
Normal file
41
javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee.xml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example1.tld</domain:name>
|
||||
<domain:name>example2.tld</domain:name>
|
||||
<domain:name>reserved.tld</domain:name>
|
||||
</domain:check>
|
||||
</check>
|
||||
<extension>
|
||||
<allocationToken:allocationToken
|
||||
xmlns:allocationToken=
|
||||
"urn:ietf:params:xml:ns:allocationToken-1.0">
|
||||
abc123
|
||||
</allocationToken:allocationToken>
|
||||
<fee:check
|
||||
xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:domain>
|
||||
<fee:name>example1.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
<fee:domain>
|
||||
<fee:name>example2.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
<fee:domain>
|
||||
<fee:name>reserved.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
</fee:check>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
51
javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee_response.xml
vendored
Normal file
51
javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee_response.xml
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1" xmlns:host="urn:ietf:params:xml:ns:host-1.0" xmlns:fee11="urn:ietf:params:xml:ns:fee-0.11" xmlns:fee12="urn:ietf:params:xml:ns:fee-0.12" xmlns:fee="urn:ietf:params:xml:ns:fee-0.6" xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0" xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:domain="urn:ietf:params:xml:ns:domain-1.0" xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:chkData>
|
||||
<domain:cd>
|
||||
<domain:name avail="true">example1.tld</domain:name>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="true">example2.tld</domain:name>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="false">reserved.tld</domain:name>
|
||||
<domain:reason>Reserved</domain:reason>
|
||||
</domain:cd>
|
||||
</domain:chkData>
|
||||
</resData>
|
||||
<extension>
|
||||
<fee:chkData>
|
||||
<fee:cd>
|
||||
<fee:name>example2.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">6.50</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>example1.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">6.50</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
<fee:name>reserved.tld</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:class>reserved</fee:class>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
</extension>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
33
javatests/google/registry/flows/domain/testdata/domain_create_premium_allocationtoken.xml
vendored
Normal file
33
javatests/google/registry/flows/domain/testdata/domain_create_premium_allocationtoken.xml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>rich.example</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns1.example.net</domain:hostObj>
|
||||
<domain:hostObj>ns2.example.net</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:registrant>jd1234</domain:registrant>
|
||||
<domain:contact type="admin">sh8013</domain:contact>
|
||||
<domain:contact type="tech">sh8013</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
</create>
|
||||
<extension>
|
||||
<allocationToken:allocationToken
|
||||
xmlns:allocationToken=
|
||||
"urn:ietf:params:xml:ns:allocationToken-1.0">
|
||||
abc123
|
||||
</allocationToken:allocationToken>
|
||||
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-0.12">
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:fee>193.5</fee:fee>
|
||||
</fee:create>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -36,7 +36,7 @@ import google.registry.model.registry.Registry;
|
|||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.testing.ShardableTestCase;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
|
@ -63,7 +63,7 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
|
||||
assertThat(
|
||||
flowUtils.verifyToken(
|
||||
flowUtils.loadAndVerifyToken(
|
||||
createCommand("blah.tld"),
|
||||
"tokeN",
|
||||
Registry.get("tld"),
|
||||
|
@ -80,7 +80,7 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
assertThrows(
|
||||
InvalidAllocationTokenException.class,
|
||||
() ->
|
||||
flowUtils.verifyToken(
|
||||
flowUtils.loadAndVerifyToken(
|
||||
createCommand("blah.tld"),
|
||||
"tokeN",
|
||||
Registry.get("tld"),
|
||||
|
@ -99,7 +99,7 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
flowUtils.verifyToken(
|
||||
flowUtils.loadAndVerifyToken(
|
||||
createCommand("blah.tld"),
|
||||
"tokeN",
|
||||
Registry.get("tld"),
|
||||
|
@ -115,12 +115,14 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
|
||||
assertThat(
|
||||
flowUtils.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")),
|
||||
"tokeN",
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC)))
|
||||
flowUtils
|
||||
.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")),
|
||||
"tokeN",
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC))
|
||||
.domainCheckResults())
|
||||
.containsExactlyEntriesIn(
|
||||
ImmutableMap.of(
|
||||
InternetDomainName.from("blah.tld"), "", InternetDomainName.from("blah2.tld"), ""))
|
||||
|
@ -138,12 +140,14 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
|
||||
assertThat(
|
||||
flowUtils.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")),
|
||||
"tokeN",
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC)))
|
||||
flowUtils
|
||||
.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")),
|
||||
"tokeN",
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC))
|
||||
.domainCheckResults())
|
||||
.containsExactlyEntriesIn(
|
||||
ImmutableMap.of(
|
||||
InternetDomainName.from("blah.tld"),
|
||||
|
@ -179,12 +183,14 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new CustomResultAllocationTokenCustomLogic());
|
||||
assertThat(
|
||||
flowUtils.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("bunny.tld")),
|
||||
"tokeN",
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC)))
|
||||
flowUtils
|
||||
.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("bunny.tld")),
|
||||
"tokeN",
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC))
|
||||
.domainCheckResults())
|
||||
.containsExactlyEntriesIn(
|
||||
ImmutableMap.of(
|
||||
InternetDomainName.from("blah.tld"),
|
||||
|
@ -215,7 +221,7 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
|
||||
@Override
|
||||
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
|
||||
ImmutableMap<InternetDomainName, String> checkResults,
|
||||
ImmutableList<InternetDomainName> domainNames,
|
||||
AllocationToken tokenEntity,
|
||||
String clientId,
|
||||
DateTime now) {
|
||||
|
@ -228,18 +234,15 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
|
||||
@Override
|
||||
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
|
||||
ImmutableMap<InternetDomainName, String> checkResults,
|
||||
ImmutableList<InternetDomainName> domainNames,
|
||||
AllocationToken tokenEntity,
|
||||
String clientId,
|
||||
DateTime now) {
|
||||
return checkResults
|
||||
.entrySet()
|
||||
.stream()
|
||||
return domainNames.stream()
|
||||
.collect(
|
||||
ImmutableMap.toImmutableMap(
|
||||
Map.Entry::getKey,
|
||||
entry ->
|
||||
entry.getKey().toString().contains("bunny") ? "fufu" : entry.getValue()));
|
||||
Function.identity(),
|
||||
domainName -> domainName.toString().contains("bunny") ? "fufu" : ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue