Allow allocation token discounts on premiums and for multiple years (#744)

* Allow allocation token discounts on premiums and for multiple years

* Add domain check flow tests

* Address code review comments

* Update schema file
This commit is contained in:
Ben McIlwain 2020-08-05 17:54:47 -04:00 committed by GitHub
parent 6f56451412
commit b89af91bda
25 changed files with 606 additions and 111 deletions

View file

@ -282,13 +282,14 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
}
@Test
void testSuccess_allocationTokenPromotion() throws Exception {
void testSuccess_allocationTokenPromotion_singleYear() throws Exception {
createTld("example");
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(UNLIMITED_USE)
.setDiscountFraction(0.5)
.setDiscountYears(2)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
@ -300,6 +301,69 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response.xml"));
}
@Test
void testSuccess_allocationTokenPromotion_multiYearAndPremiums() throws Exception {
createTld("example");
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("rich.example")
.setDiscountFraction(0.9)
.setDiscountYears(3)
.setDiscountPremiums(true)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
.build())
.build());
setEppInput(
"domain_check_allocationtoken_promotion.xml", ImmutableMap.of("DOMAIN", "rich.example"));
runFlowAssertResponse(
loadFile(
"domain_check_allocationtoken_promotion_response.xml",
new ImmutableMap.Builder<String, String>()
.put("DOMAIN", "rich.example")
.put("COST_1YR", "10.00")
.put("COST_2YR", "20.00")
.put("COST_5YR", "230.00")
.put("FEE_CLASS", "<fee:class>premium</fee:class>")
.build()));
}
@Test
void testSuccess_allocationTokenPromotion_multiYear() throws Exception {
createTld("tld");
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("single.tld")
.setDiscountFraction(0.444)
.setDiscountYears(2)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
.build())
.build());
setEppInput(
"domain_check_allocationtoken_promotion.xml", ImmutableMap.of("DOMAIN", "single.tld"));
runFlowAssertResponse(
loadFile(
"domain_check_allocationtoken_promotion_response.xml",
new ImmutableMap.Builder<String, String>()
.put("DOMAIN", "single.tld")
.put("COST_1YR", "7.23")
.put("COST_2YR", "14.46")
.put("COST_5YR", "53.46")
.put("FEE_CLASS", "")
.build()));
}
@Test
void testSuccess_promotionNotActive() throws Exception {
createTld("example");

View file

@ -68,6 +68,7 @@ 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.collect.Ordering;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig;
import google.registry.flows.EppException;
@ -150,12 +151,12 @@ 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;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.Registry.TldType;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
@ -435,7 +436,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFailure_invalidAllocationToken() {
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts();
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@ -445,7 +448,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
void testFailure_reservedDomainCreate_allocationTokenIsForADifferentDomain() {
// Try to register a reserved domain name with an allocation token valid for a different domain
// name.
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "resdom.tld"));
setEppInput(
"domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "resdom.tld", "YEARS", "2"));
persistContactsAndHosts();
persistResource(
new AllocationToken.Builder()
@ -464,7 +468,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
void testFailure_nonreservedDomainCreate_allocationTokenIsForADifferentDomain() {
// Try to register a non-reserved domain name with an allocation token valid for a different
// domain name.
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts();
persistResource(
new AllocationToken.Builder()
@ -481,7 +487,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFailure_alreadyRedemeedAllocationToken() {
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts();
persistResource(
new AllocationToken.Builder()
@ -497,7 +505,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testSuccess_validAllocationToken_isRedeemed() throws Exception {
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts();
AllocationToken token =
persistResource(
@ -508,12 +518,14 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
HistoryEntry historyEntry =
ofy().load().type(HistoryEntry.class).ancestor(reloadResourceByForeignKey()).first().now();
assertThat(ofy().load().entity(token).now().getRedemptionHistoryEntry())
.isEqualTo(Key.create(historyEntry));
.hasValue(Key.create(historyEntry));
}
@Test
void testSuccess_validAllocationToken_multiUse() throws Exception {
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts();
allocationToken =
persistResource(
@ -531,7 +543,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(), allocationToken);
clock.advanceOneMilli();
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "otherexample.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "otherexample.tld", "YEARS", "2"));
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "otherexample.tld")));
}
@ -543,8 +557,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistContactsAndHosts("foo.tld");
assertTransactionalFlow(true);
String expectedResponseXml =
loadFile(
"domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.foo.tld"));
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.foo.tld"));
runFlowAssertResponse(CommitMode.LIVE, UserPrivileges.NORMAL, expectedResponseXml);
assertSuccessfulCreate("foo.tld", ImmutableSet.of());
assertNoLordn();
@ -576,8 +589,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_registration_encoded_signed_mark.xml",
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "open"));
persistContactsAndHosts();
EppException thrown =
assertThrows(SignedMarksOnlyDuringSunriseException.class, this::runFlow);
EppException thrown = assertThrows(SignedMarksOnlyDuringSunriseException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@ -1184,13 +1196,14 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
Registry.get("tld")
.asBuilder()
.setTldStateTransitions(
ImmutableSortedMap.of(
START_OF_TIME, PREDELEGATION,
DateTime.parse("1999-01-01T00:00:00Z"), QUIET_PERIOD,
// The anchor tenant is created here, on 1999-04-03
DateTime.parse("1999-07-01T00:00:00Z"), START_DATE_SUNRISE,
DateTime.parse("2000-01-01T00:00:00Z"), GENERAL_AVAILABILITY))
new ImmutableSortedMap.Builder<DateTime, TldState>(Ordering.natural())
.put(START_OF_TIME, PREDELEGATION)
.put(DateTime.parse("1999-01-01T00:00:00Z"), QUIET_PERIOD)
.put(DateTime.parse("1999-07-01T00:00:00Z"), START_DATE_SUNRISE)
.put(DateTime.parse("2000-01-01T00:00:00Z"), GENERAL_AVAILABILITY)
.build())
.build());
// The anchor tenant is created during the quiet period, on 1999-04-03.
setEppInput("domain_create_anchor_allocationtoken.xml");
persistContactsAndHosts();
runFlowAssertResponse(loadFile("domain_create_anchor_response.xml"));
@ -1210,7 +1223,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.build());
// Despite the domain being FULLY_BLOCKED, the non-superuser create succeeds the domain is also
// RESERVED_FOR_SPECIFIC_USE and the correct allocation token is passed.
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "resdom.tld"));
setEppInput(
"domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "resdom.tld", "YEARS", "2"));
persistContactsAndHosts();
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "resdom.tld")));
@ -1233,7 +1247,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setTokenType(SINGLE_USE)
.setDomainName("resdom.tld")
.build());
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "resdom.tld"));
setEppInput(
"domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "resdom.tld", "YEARS", "2"));
persistContactsAndHosts();
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "resdom.tld")));
@ -1247,7 +1262,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
ofy().load().key(Key.create(AllocationToken.class, token)).now();
assertThat(reloadedToken.isRedeemed()).isTrue();
assertThat(reloadedToken.getRedemptionHistoryEntry())
.isEqualTo(Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0)));
.hasValue(Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0)));
}
private void assertAllocationTokenWasNotRedeemed(String token) {
@ -1264,7 +1279,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(TokenType.UNLIMITED_USE)
.setTokenType(UNLIMITED_USE)
.setDiscountFraction(0.5)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
@ -1274,7 +1289,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.build())
.build());
clock.advanceOneMilli();
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
BillingEvent.OneTime billingEvent =
@ -1284,14 +1301,131 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_promotionDoesNotApplyToPremiumPrice() {
// At the moment, discounts cannot apply to premium domains
void testSuccess_allocationToken_multiYearDiscount_maxesAtTokenDiscountYears() throws Exception {
// 2yrs @ $13 + 3yrs @ $13 * (1 - 0.73) = $36.53
runTest_allocationToken_multiYearDiscount(false, 0.73, 3, Money.of(USD, 36.53));
}
@Test
void testSuccess_allocationToken_multiYearDiscount_maxesAtNumRegistrationYears()
throws Exception {
// 5yrs @ $13 * (1 - 0.276) = $47.06
runTest_allocationToken_multiYearDiscount(false, 0.276, 10, Money.of(USD, 47.06));
}
void runTest_allocationToken_multiYearDiscount(
boolean discountPremiums, double discountFraction, int discountYears, Money expectedPrice)
throws Exception {
persistContactsAndHosts();
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("example.tld")
.setDiscountFraction(discountFraction)
.setDiscountYears(discountYears)
.setDiscountPremiums(discountPremiums)
.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", "YEARS", "5"));
runFlowAssertResponse(
loadFile(
"domain_create_response_wildcard.xml",
new ImmutableMap.Builder<String, String>()
.put("DOMAIN", "example.tld")
.put("CRDATE", "1999-04-03T22:00:00.0Z")
.put("EXDATE", "2004-04-03T22:00:00.0Z")
.build()));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(ofy().load().type(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getCost()).isEqualTo(expectedPrice);
}
@Test
void testSuccess_allocationToken_multiYearDiscount_worksForPremiums() throws Exception {
createTld("example");
persistContactsAndHosts();
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(TokenType.UNLIMITED_USE)
.setTokenType(SINGLE_USE)
.setDomainName("rich.example")
.setDiscountFraction(0.98)
.setDiscountYears(2)
.setDiscountPremiums(true)
.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",
ImmutableMap.of("YEARS", "3", "FEE", "104.00"));
runFlowAssertResponse(
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "104.00")));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(ofy().load().type(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));
}
@Test
void testSuccess_allocationToken_singleYearDiscount_worksForPremiums() throws Exception {
createTld("example");
persistContactsAndHosts();
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("rich.example")
.setDiscountFraction(0.95555)
.setDiscountPremiums(true)
.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",
ImmutableMap.of("YEARS", "3", "FEE", "204.44"));
runFlowAssertResponse(
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "204.44")));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(ofy().load().type(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));
}
@Test
void testSuccess_promotionDoesNotApplyToPremiumPrice() {
// Discounts only apply to premium domains if the token is explicitly configured to allow it.
createTld("example");
persistContactsAndHosts();
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(UNLIMITED_USE)
.setDiscountFraction(0.5)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
@ -1301,7 +1435,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.build())
.build());
clock.advanceOneMilli();
setEppInput("domain_create_premium_allocationtoken.xml");
setEppInput(
"domain_create_premium_allocationtoken.xml",
ImmutableMap.of("YEARS", "2", "FEE", "193.50"));
assertAboutEppExceptions()
.that(assertThrows(AllocationTokenInvalidForPremiumNameException.class, this::runFlow))
.marshalsToXml();
@ -1313,7 +1449,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(TokenType.UNLIMITED_USE)
.setTokenType(UNLIMITED_USE)
.setDiscountFraction(0.5)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
@ -1322,7 +1458,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.put(clock.nowUtc().plusDays(60), TokenStatus.ENDED)
.build())
.build());
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
assertAboutEppExceptions()
.that(assertThrows(AllocationTokenNotInPromotionException.class, this::runFlow))
.marshalsToXml();
@ -1334,7 +1472,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(TokenType.UNLIMITED_USE)
.setTokenType(UNLIMITED_USE)
.setAllowedTlds(ImmutableSet.of("example"))
.setDiscountFraction(0.5)
.setTokenStatusTransitions(
@ -1344,7 +1482,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
.build())
.build());
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
assertAboutEppExceptions()
.that(assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow))
.marshalsToXml();
@ -1356,7 +1496,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(TokenType.UNLIMITED_USE)
.setTokenType(UNLIMITED_USE)
.setAllowedClientIds(ImmutableSet.of("someClientId"))
.setDiscountFraction(0.5)
.setTokenStatusTransitions(
@ -1366,7 +1506,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
.build())
.build());
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
assertAboutEppExceptions()
.that(assertThrows(AllocationTokenNotValidForRegistrarException.class, this::runFlow))
.marshalsToXml();
@ -1562,7 +1704,11 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
// Modify the Registrar to block premium names.
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
runFlowAssertResponse(
CommitMode.LIVE, SUPERUSER, loadFile("domain_create_response_premium.xml"));
CommitMode.LIVE,
SUPERUSER,
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00")));
assertSuccessfulCreate("example", ImmutableSet.of());
}

View file

@ -44,7 +44,7 @@ import org.junit.jupiter.api.Test;
class AllocationTokenTest extends EntityTestCase {
@BeforeEach
void setup() {
void beforeEach() {
createTld("foo");
}
@ -59,6 +59,8 @@ class AllocationTokenTest extends EntityTestCase {
.setAllowedTlds(ImmutableSet.of("dev", "app"))
.setAllowedClientIds(ImmutableSet.of("TheRegistrar, NewRegistrar"))
.setDiscountFraction(0.5)
.setDiscountPremiums(true)
.setDiscountYears(3)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, NOT_STARTED)
@ -155,7 +157,7 @@ class AllocationTokenTest extends EntityTestCase {
}
@Test
void testBuild_invalidTLD() {
void testBuild_invalidTld() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
@ -268,6 +270,50 @@ class AllocationTokenTest extends EntityTestCase {
assertTerminal(CANCELLED);
}
@Test
void testSetDiscountFractionTooHigh() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> new AllocationToken.Builder().setDiscountFraction(1.1));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Discount fraction must be between 0 and 1 inclusive");
}
@Test
void testSetDiscountFractionTooLow() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> new AllocationToken.Builder().setDiscountFraction(-.0001));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Discount fraction must be between 0 and 1 inclusive");
}
@Test
void testSetDiscountYearsTooHigh() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> new AllocationToken.Builder().setDiscountYears(11));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Discount years must be between 1 and 10 inclusive");
}
@Test
void testSetDiscountYearsTooLow() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> new AllocationToken.Builder().setDiscountYears(0));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Discount years must be between 1 and 10 inclusive");
}
@Test
void testBuild_noTokenType() {
IllegalArgumentException thrown =
@ -295,6 +341,38 @@ class AllocationTokenTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().isEqualTo("Token must not be blank");
}
@Test
void testBuild_discountPremiumsRequiresDiscountFraction() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
new AllocationToken.Builder()
.setToken("abc")
.setTokenType(SINGLE_USE)
.setDiscountPremiums(true)
.build());
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Discount premiums can only be specified along with a discount fraction");
}
@Test
void testBuild_discountYearsRequiresDiscountFraction() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
new AllocationToken.Builder()
.setToken("abc")
.setTokenType(SINGLE_USE)
.setDiscountYears(2)
.build());
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Discount years can only be specified along with a discount fraction");
}
private void assertBadInitialTransition(TokenStatus status) {
assertBadTransition(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()

View file

@ -69,9 +69,11 @@ public class ContactHistoryTest extends EntityTestCase {
}
static void assertContactHistoriesEqual(ContactHistory one, ContactHistory two) {
assertAboutImmutableObjects().that(one)
assertAboutImmutableObjects()
.that(one)
.isEqualExceptFields(two, "contactBase", "contactRepoId", "parent");
assertAboutImmutableObjects().that(one.getContactBase())
assertAboutImmutableObjects()
.that(one.getContactBase())
.isEqualExceptFields(two.getContactBase(), "repoId");
}
}

View file

@ -83,7 +83,8 @@ public class DomainHistoryTest extends EntityTestCase {
}
static void assertDomainHistoriesEqual(DomainHistory one, DomainHistory two) {
assertAboutImmutableObjects().that(one)
assertAboutImmutableObjects()
.that(one)
.isEqualExceptFields(two, "domainContent", "domainRepoId", "parent");
}
}

View file

@ -26,6 +26,7 @@ import static google.registry.config.RegistryConfig.getContactAndHostRoidSuffix;
import static google.registry.config.RegistryConfig.getContactAutomaticTransferLength;
import static google.registry.model.EppResourceUtils.createDomainRepoId;
import static google.registry.model.EppResourceUtils.createRepoId;
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
import static google.registry.model.ResourceTransferUtils.createTransferResponse;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY;
@ -71,6 +72,7 @@ import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
@ -832,6 +834,12 @@ public class DatastoreHelper {
.collect(onlyElement());
}
public static void assertAllocationTokens(AllocationToken... expectedTokens) {
assertThat(ofy().load().type(AllocationToken.class).list())
.comparingElementsUsing(immutableObjectCorrespondence("updateTimestamp", "creationTime"))
.containsExactlyElementsIn(expectedTokens);
}
/** Returns a newly allocated, globally unique domain repoId of the format HEX-TLD. */
public static String generateNewDomainRoid(String tld) {
return createDomainRepoId(ObjectifyService.allocateId(), tld);

View file

@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
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.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.assertAllocationTokens;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
@ -32,11 +33,9 @@ import static org.mockito.Mockito.verify;
import com.beust.jcommander.ParameterException;
import com.google.appengine.tools.remoteapi.RemoteApiException;
import com.google.common.base.Joiner;
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.collect.Maps;
import com.google.common.io.Files;
import com.googlecode.objectify.Key;
import google.registry.model.domain.token.AllocationToken;
@ -160,6 +159,8 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
"--allowed_client_ids", "TheRegistrar,NewRegistrar",
"--allowed_tlds", "tld,example",
"--discount_fraction", "0.5",
"--discount_premiums", "true",
"--discount_years", "6",
"--token_status_transitions",
String.format(
"\"%s=NOT_STARTED,%s=VALID,%s=ENDED\"", START_OF_TIME, promoStart, promoEnd));
@ -170,6 +171,8 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
.setAllowedClientIds(ImmutableSet.of("TheRegistrar", "NewRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld", "example"))
.setDiscountFraction(0.5)
.setDiscountPremiums(true)
.setDiscountYears(6)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
@ -309,26 +312,6 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
.isEqualTo("For UNLIMITED_USE tokens, must specify --token_status_transitions");
}
private void assertAllocationTokens(AllocationToken... expectedTokens) {
// Using ImmutableObject comparison here is tricky because the creation/updated timestamps are
// neither easy nor valuable to test here.
ImmutableMap<String, AllocationToken> actualTokens =
Maps.uniqueIndex(ofy().load().type(AllocationToken.class), AllocationToken::getToken);
assertThat(actualTokens).hasSize(expectedTokens.length);
for (AllocationToken expectedToken : expectedTokens) {
AllocationToken match = actualTokens.get(expectedToken.getToken());
assertThat(match).isNotNull();
assertThat(match.getRedemptionHistoryEntry())
.isEqualTo(expectedToken.getRedemptionHistoryEntry());
assertThat(match.getAllowedClientIds()).isEqualTo(expectedToken.getAllowedClientIds());
assertThat(match.getAllowedTlds()).isEqualTo(expectedToken.getAllowedTlds());
assertThat(match.getDiscountFraction()).isEqualTo(expectedToken.getDiscountFraction());
assertThat(match.getTokenStatusTransitions())
.isEqualTo(expectedToken.getTokenStatusTransitions());
assertThat(match.getTokenType()).isEqualTo(expectedToken.getTokenType());
}
}
private AllocationToken createToken(
String token,
@Nullable Key<HistoryEntry> redemptionHistoryEntry,

View file

@ -77,6 +77,24 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
assertThat(reloadResource(token).getDiscountFraction()).isEqualTo(0.15);
}
@Test
void testUpdateDiscountPremiums() throws Exception {
AllocationToken token =
persistResource(
builderWithPromo().setDiscountFraction(0.5).setDiscountPremiums(false).build());
runCommandForced("--prefix", "token", "--discount_premiums", "true");
assertThat(reloadResource(token).shouldDiscountPremiums()).isTrue();
runCommandForced("--prefix", "token", "--discount_premiums", "false");
assertThat(reloadResource(token).shouldDiscountPremiums()).isFalse();
}
@Test
void testUpdateDiscountYears() throws Exception {
AllocationToken token = persistResource(builderWithPromo().setDiscountFraction(0.5).build());
runCommandForced("--prefix", "token", "--discount_years", "4");
assertThat(reloadResource(token).getDiscountYears()).isEqualTo(4);
}
@Test
void testUpdateStatusTransitions() throws Exception {
DateTime now = DateTime.now(UTC);