mirror of
https://github.com/google/nomulus.git
synced 2025-04-29 19:47:51 +02:00
Add a command to update Recurrence objects' behavior (#1987)
We want to basically be able to change the renewal behavior, either setting the behavior type (e.g. NONPREMIUM) or the specified renewal price.
This commit is contained in:
parent
dcc7e65813
commit
f985cfd749
3 changed files with 418 additions and 0 deletions
|
@ -108,6 +108,7 @@ public final class RegistryTool {
|
||||||
.put("update_keyring_secret", UpdateKeyringSecretCommand.class)
|
.put("update_keyring_secret", UpdateKeyringSecretCommand.class)
|
||||||
.put("update_package_promotion", UpdatePackagePromotionCommand.class)
|
.put("update_package_promotion", UpdatePackagePromotionCommand.class)
|
||||||
.put("update_premium_list", UpdatePremiumListCommand.class)
|
.put("update_premium_list", UpdatePremiumListCommand.class)
|
||||||
|
.put("update_recurrence", UpdateRecurrenceCommand.class)
|
||||||
.put("update_registrar", UpdateRegistrarCommand.class)
|
.put("update_registrar", UpdateRegistrarCommand.class)
|
||||||
.put("update_reserved_list", UpdateReservedListCommand.class)
|
.put("update_reserved_list", UpdateReservedListCommand.class)
|
||||||
.put("update_server_locks", UpdateServerLocksCommand.class)
|
.put("update_server_locks", UpdateServerLocksCommand.class)
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package google.registry.tools;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static google.registry.model.IdService.allocateId;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||||
|
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import google.registry.model.EppResourceUtils;
|
||||||
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
|
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||||
|
import google.registry.model.domain.Domain;
|
||||||
|
import google.registry.model.domain.DomainHistory;
|
||||||
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
|
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import org.joda.money.Money;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to update {@link Recurring} billing events with a new behavior and/or price.
|
||||||
|
*
|
||||||
|
* <p>More specifically, this closes the existing recurrence object and creates a new, similar,
|
||||||
|
* object as well as a corresponding synthetic {@link DomainHistory} object. This is done to
|
||||||
|
* preserve the recurrence's history.
|
||||||
|
*/
|
||||||
|
@Parameters(separators = " =", commandDescription = "Update a billing recurrence")
|
||||||
|
public class UpdateRecurrenceCommand extends ConfirmingCommand {
|
||||||
|
|
||||||
|
private static final String HISTORY_REASON =
|
||||||
|
"Administrative update of billing recurrence behavior";
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
description = "Domain name(s) for which we wish to update the recurrence(s)",
|
||||||
|
required = true)
|
||||||
|
private List<String> mainParameters;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Parameter(
|
||||||
|
names = "--renewal_price_behavior",
|
||||||
|
description = "New RenewalPriceBehavior value to use with this recurrence")
|
||||||
|
RenewalPriceBehavior renewalPriceBehavior;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Parameter(
|
||||||
|
names = "--specified_renewal_price",
|
||||||
|
description = "Exact renewal price to use if the behavior is SPECIFIED")
|
||||||
|
Money specifiedRenewalPrice;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String prompt() throws Exception {
|
||||||
|
checkArgument(
|
||||||
|
renewalPriceBehavior != null || specifiedRenewalPrice != null,
|
||||||
|
"Must specify a behavior and/or a price");
|
||||||
|
|
||||||
|
if (renewalPriceBehavior != null) {
|
||||||
|
if (renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED)) {
|
||||||
|
checkArgument(
|
||||||
|
specifiedRenewalPrice != null,
|
||||||
|
"Renewal price must be set when using SPECIFIED behavior");
|
||||||
|
} else {
|
||||||
|
checkArgument(
|
||||||
|
specifiedRenewalPrice == null,
|
||||||
|
"Renewal price can have a value if and only if the renewal price behavior is"
|
||||||
|
+ " SPECIFIED");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImmutableMap<Domain, Recurring> domainsAndRecurrings =
|
||||||
|
tm().transact(this::loadDomainsAndRecurrings);
|
||||||
|
if (renewalPriceBehavior == null) {
|
||||||
|
// Allow users to specify only a price only if all renewals are already SPECIFIED
|
||||||
|
domainsAndRecurrings.forEach(
|
||||||
|
(d, r) ->
|
||||||
|
checkArgument(
|
||||||
|
r.getRenewalPriceBehavior().equals(RenewalPriceBehavior.SPECIFIED),
|
||||||
|
"When specifying only a price, all domains must have SPECIFIED behavior. Domain"
|
||||||
|
+ " %s does not",
|
||||||
|
d.getDomainName()));
|
||||||
|
}
|
||||||
|
String specifiedPriceString =
|
||||||
|
specifiedRenewalPrice == null ? "" : String.format(" and price %s", specifiedRenewalPrice);
|
||||||
|
return String.format(
|
||||||
|
"Update the following with behavior %s%s?\n%s",
|
||||||
|
renewalPriceBehavior,
|
||||||
|
specifiedPriceString,
|
||||||
|
Joiner.on('\n').withKeyValueSeparator(':').join(domainsAndRecurrings));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String execute() throws Exception {
|
||||||
|
ImmutableList<Recurring> newRecurrings = tm().transact(this::internalExecute);
|
||||||
|
return "Updated new recurring(s): " + newRecurrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<Recurring> internalExecute() {
|
||||||
|
ImmutableMap<Domain, Recurring> domainsAndRecurrings = loadDomainsAndRecurrings();
|
||||||
|
DateTime now = tm().getTransactionTime();
|
||||||
|
ImmutableList.Builder<Recurring> resultBuilder = new ImmutableList.Builder<>();
|
||||||
|
domainsAndRecurrings.forEach(
|
||||||
|
(domain, existingRecurring) -> {
|
||||||
|
// Make a new history ID to break the (recurring, history, domain) circular dep chain
|
||||||
|
long newHistoryId = allocateId();
|
||||||
|
HistoryEntryId newDomainHistoryId = new HistoryEntryId(domain.getRepoId(), newHistoryId);
|
||||||
|
Recurring endingNow = existingRecurring.asBuilder().setRecurrenceEndTime(now).build();
|
||||||
|
Recurring.Builder newRecurringBuilder =
|
||||||
|
existingRecurring
|
||||||
|
.asBuilder()
|
||||||
|
// set the ID to be 0 (null) to create a new object
|
||||||
|
.setId(0)
|
||||||
|
.setDomainHistoryId(newDomainHistoryId);
|
||||||
|
if (renewalPriceBehavior != null) {
|
||||||
|
newRecurringBuilder.setRenewalPriceBehavior(renewalPriceBehavior);
|
||||||
|
newRecurringBuilder.setRenewalPrice(null);
|
||||||
|
}
|
||||||
|
if (specifiedRenewalPrice != null) {
|
||||||
|
newRecurringBuilder.setRenewalPrice(specifiedRenewalPrice);
|
||||||
|
}
|
||||||
|
Recurring newRecurring = newRecurringBuilder.build();
|
||||||
|
Domain newDomain =
|
||||||
|
domain.asBuilder().setAutorenewBillingEvent(newRecurring.createVKey()).build();
|
||||||
|
DomainHistory newDomainHistory =
|
||||||
|
new DomainHistory.Builder()
|
||||||
|
.setRevisionId(newDomainHistoryId.getRevisionId())
|
||||||
|
.setDomain(newDomain)
|
||||||
|
.setReason(HISTORY_REASON)
|
||||||
|
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
|
||||||
|
.setBySuperuser(true)
|
||||||
|
.setRequestedByRegistrar(false)
|
||||||
|
.setType(HistoryEntry.Type.SYNTHETIC)
|
||||||
|
.setModificationTime(now)
|
||||||
|
.build();
|
||||||
|
tm().putAll(endingNow, newRecurring, newDomain, newDomainHistory);
|
||||||
|
resultBuilder.add(newRecurring);
|
||||||
|
});
|
||||||
|
return resultBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableMap<Domain, Recurring> loadDomainsAndRecurrings() {
|
||||||
|
ImmutableMap.Builder<Domain, Recurring> result = new ImmutableMap.Builder<>();
|
||||||
|
DateTime now = tm().getTransactionTime();
|
||||||
|
for (String domainName : mainParameters) {
|
||||||
|
Domain domain =
|
||||||
|
EppResourceUtils.loadByForeignKey(Domain.class, domainName, now)
|
||||||
|
.orElseThrow(
|
||||||
|
() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Domain %s does not exist or has been deleted", domainName)));
|
||||||
|
checkArgument(
|
||||||
|
domain.getDeletionTime().equals(END_OF_TIME),
|
||||||
|
"Domain %s has already had a deletion time set",
|
||||||
|
domainName);
|
||||||
|
checkArgument(
|
||||||
|
domain.getTransferData().isEmpty(),
|
||||||
|
"Domain %s has a pending transfer: %s",
|
||||||
|
domainName,
|
||||||
|
domain.getTransferData());
|
||||||
|
Optional<DateTime> domainAutorenewEndTime = domain.getAutorenewEndTime();
|
||||||
|
domainAutorenewEndTime.ifPresent(
|
||||||
|
endTime ->
|
||||||
|
checkArgument(
|
||||||
|
endTime.isAfter(now),
|
||||||
|
"Domain %s autorenew ended prior to now at %s",
|
||||||
|
domainName,
|
||||||
|
endTime));
|
||||||
|
Recurring recurring = tm().loadByKey(domain.getAutorenewBillingEvent());
|
||||||
|
checkArgument(
|
||||||
|
recurring.getRecurrenceEndTime().equals(END_OF_TIME),
|
||||||
|
"Domain %s's recurrence's end date is not END_OF_TIME",
|
||||||
|
domainName);
|
||||||
|
result.put(domain, recurring);
|
||||||
|
}
|
||||||
|
return result.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package google.registry.tools;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static com.google.common.truth.Truth8.assertThat;
|
||||||
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
|
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||||
|
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||||
|
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||||
|
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||||
|
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
|
||||||
|
import static google.registry.testing.DatabaseHelper.persistDomainWithPendingTransfer;
|
||||||
|
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
|
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import google.registry.model.billing.BillingEvent.Reason;
|
||||||
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
|
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||||
|
import google.registry.model.domain.Domain;
|
||||||
|
import google.registry.model.domain.DomainHistory;
|
||||||
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import org.joda.money.CurrencyUnit;
|
||||||
|
import org.joda.money.Money;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/** Tests for {@link UpdateRecurrenceCommand}. */
|
||||||
|
public class UpdateRecurrenceCommandTest extends CommandTestCase<UpdateRecurrenceCommand> {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach() {
|
||||||
|
createTld("tld");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_setsSpecified() throws Exception {
|
||||||
|
persistDomain();
|
||||||
|
Recurring existingRecurring = Iterables.getOnlyElement(loadAllOf(Recurring.class));
|
||||||
|
assertThat(existingRecurring.getRecurrenceEndTime()).isEqualTo(END_OF_TIME);
|
||||||
|
assertThat(existingRecurring.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||||
|
runCommandForced(
|
||||||
|
"domain.tld",
|
||||||
|
"--renewal_price_behavior",
|
||||||
|
"SPECIFIED",
|
||||||
|
"--specified_renewal_price",
|
||||||
|
"USD 9001");
|
||||||
|
assertThat(loadByEntity(existingRecurring).getRecurrenceEndTime())
|
||||||
|
.isEqualTo(fakeClock.nowUtc());
|
||||||
|
assertNewBillingEventAndHistory(
|
||||||
|
existingRecurring.getId(),
|
||||||
|
RenewalPriceBehavior.SPECIFIED,
|
||||||
|
Money.of(CurrencyUnit.USD, 9001));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_setsNonPremium() throws Exception {
|
||||||
|
persistDomain();
|
||||||
|
Recurring existingRecurring = Iterables.getOnlyElement(loadAllOf(Recurring.class));
|
||||||
|
assertThat(existingRecurring.getRecurrenceEndTime()).isEqualTo(END_OF_TIME);
|
||||||
|
assertThat(existingRecurring.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||||
|
runCommandForced("domain.tld", "--renewal_price_behavior", "NONPREMIUM");
|
||||||
|
assertThat(loadByEntity(existingRecurring).getRecurrenceEndTime())
|
||||||
|
.isEqualTo(fakeClock.nowUtc());
|
||||||
|
assertNewBillingEventAndHistory(
|
||||||
|
existingRecurring.getId(), RenewalPriceBehavior.NONPREMIUM, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_setsDefault() throws Exception {
|
||||||
|
persistDomain();
|
||||||
|
Recurring existingRecurring = Iterables.getOnlyElement(loadAllOf(Recurring.class));
|
||||||
|
persistResource(
|
||||||
|
existingRecurring
|
||||||
|
.asBuilder()
|
||||||
|
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||||
|
.setRenewalPrice(Money.of(CurrencyUnit.USD, 100))
|
||||||
|
.build());
|
||||||
|
assertThat(existingRecurring.getRecurrenceEndTime()).isEqualTo(END_OF_TIME);
|
||||||
|
runCommandForced("domain.tld", "--renewal_price_behavior", "DEFAULT");
|
||||||
|
assertThat(loadByEntity(existingRecurring).getRecurrenceEndTime())
|
||||||
|
.isEqualTo(fakeClock.nowUtc());
|
||||||
|
assertNewBillingEventAndHistory(existingRecurring.getId(), RenewalPriceBehavior.DEFAULT, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_setsPrice_whenSpecifiedAlready() throws Exception {
|
||||||
|
Domain domain = persistDomain();
|
||||||
|
Recurring recurring = loadByKey(domain.getAutorenewBillingEvent());
|
||||||
|
persistResource(
|
||||||
|
recurring
|
||||||
|
.asBuilder()
|
||||||
|
.setRenewalPrice(Money.of(CurrencyUnit.USD, 20))
|
||||||
|
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||||
|
.build());
|
||||||
|
runCommandForced("domain.tld", "--specified_renewal_price", "USD 9001");
|
||||||
|
assertNewBillingEventAndHistory(
|
||||||
|
recurring.getId(), RenewalPriceBehavior.SPECIFIED, Money.of(CurrencyUnit.USD, 9001));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_nonexistentDomain() {
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> runCommandForced("nonexistent.tld", "--renewal_price_behavior", "NONPREMIUM"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_invalidInputs() throws Exception {
|
||||||
|
persistDomain();
|
||||||
|
assertThat(assertThrows(IllegalArgumentException.class, () -> runCommandForced("domain.tld")))
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo("Must specify a behavior and/or a price");
|
||||||
|
command = newCommandInstance();
|
||||||
|
assertThat(
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() ->
|
||||||
|
runCommandForced(
|
||||||
|
"domain.tld",
|
||||||
|
"--renewal_price_behavior",
|
||||||
|
"DEFAULT",
|
||||||
|
"--specified_renewal_price",
|
||||||
|
"USD 50")))
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo(
|
||||||
|
"Renewal price can have a value if and only if the renewal price behavior is"
|
||||||
|
+ " SPECIFIED");
|
||||||
|
command = newCommandInstance();
|
||||||
|
assertThat(
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> runCommandForced("domain.tld", "--renewal_price_behavior", "SPECIFIED")))
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo("Renewal price must be set when using SPECIFIED behavior");
|
||||||
|
command = newCommandInstance();
|
||||||
|
assertThat(
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> runCommandForced("domain.tld", "--specified_renewal_price", "USD 50")))
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo(
|
||||||
|
"When specifying only a price, all domains must have SPECIFIED behavior. Domain"
|
||||||
|
+ " domain.tld does not");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_billingAlreadyClosed() {
|
||||||
|
Domain domain = persistDomain();
|
||||||
|
Recurring recurring = loadByKey(domain.getAutorenewBillingEvent());
|
||||||
|
persistResource(recurring.asBuilder().setRecurrenceEndTime(fakeClock.nowUtc()).build());
|
||||||
|
assertThat(
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> runCommandForced("domain.tld", "--renewal_price_behavior", "NONPREMIUM")))
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo("Domain domain.tld's recurrence's end date is not END_OF_TIME");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_pendingTransfer() {
|
||||||
|
persistDomainWithPendingTransfer(
|
||||||
|
persistDomain(),
|
||||||
|
fakeClock.nowUtc().minusMillis(1),
|
||||||
|
fakeClock.nowUtc().plusMonths(1),
|
||||||
|
END_OF_TIME);
|
||||||
|
assertThat(
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> runCommandForced("domain.tld", "--renewal_price_behavior", "NONPREMIUM")))
|
||||||
|
.hasMessageThat()
|
||||||
|
.startsWith("Domain domain.tld has a pending transfer: DomainTransferData: {");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNewBillingEventAndHistory(
|
||||||
|
long previousId, RenewalPriceBehavior expectedBehavior, @Nullable Money expectedPrice) {
|
||||||
|
Recurring newRecurring =
|
||||||
|
loadAllOf(Recurring.class).stream().filter(r -> r.getId() != previousId).findFirst().get();
|
||||||
|
assertThat(newRecurring.getRecurrenceEndTime()).isEqualTo(END_OF_TIME);
|
||||||
|
assertThat(newRecurring.getRenewalPriceBehavior()).isEqualTo(expectedBehavior);
|
||||||
|
assertThat(newRecurring.getRenewalPrice()).isEqualTo(Optional.ofNullable(expectedPrice));
|
||||||
|
assertThat(newRecurring.getReason()).isEqualTo(Reason.RENEW);
|
||||||
|
|
||||||
|
DomainHistory newHistory =
|
||||||
|
loadAllOf(DomainHistory.class).stream()
|
||||||
|
.filter(dh -> !dh.getType().equals(HistoryEntry.Type.DOMAIN_CREATE))
|
||||||
|
.findFirst()
|
||||||
|
.get();
|
||||||
|
assertThat(newHistory.getType()).isEqualTo(HistoryEntry.Type.SYNTHETIC);
|
||||||
|
assertThat(newHistory.getReason())
|
||||||
|
.isEqualTo("Administrative update of billing recurrence behavior");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Domain persistDomain() {
|
||||||
|
Domain domain =
|
||||||
|
persistDomainWithDependentResources(
|
||||||
|
"domain",
|
||||||
|
"tld",
|
||||||
|
persistActiveContact("contact1234"),
|
||||||
|
fakeClock.nowUtc(),
|
||||||
|
fakeClock.nowUtc(),
|
||||||
|
END_OF_TIME);
|
||||||
|
fakeClock.advanceOneMilli();
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue