Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package com.maxio.advancedbilling.controllers.subscriptiongroupstatus;

import com.maxio.advancedbilling.AdvancedBillingClient;
import com.maxio.advancedbilling.TestClientProvider;
import com.maxio.advancedbilling.controllers.InvoicesController;
import com.maxio.advancedbilling.controllers.SubscriptionComponentsController;
import com.maxio.advancedbilling.controllers.SubscriptionGroupStatusController;
import com.maxio.advancedbilling.controllers.SubscriptionGroupsController;
import com.maxio.advancedbilling.controllers.SubscriptionsController;
import com.maxio.advancedbilling.exceptions.ApiException;
import com.maxio.advancedbilling.models.Component;
import com.maxio.advancedbilling.models.CreateSubscription;
import com.maxio.advancedbilling.models.CreateSubscriptionComponent;
import com.maxio.advancedbilling.models.CreateSubscriptionGroup;
import com.maxio.advancedbilling.models.CreateSubscriptionGroupRequest;
import com.maxio.advancedbilling.models.Customer;
import com.maxio.advancedbilling.models.FullSubscriptionGroupResponse;
import com.maxio.advancedbilling.models.Product;
import com.maxio.advancedbilling.models.ProductFamily;
import com.maxio.advancedbilling.models.ReactivateSubscriptionGroupRequest;
import com.maxio.advancedbilling.models.Subscription;
import com.maxio.advancedbilling.models.SubscriptionState;
import com.maxio.advancedbilling.models.containers.CreateSubscriptionComponentComponentId;
import com.maxio.advancedbilling.utils.TestSetup;
import com.maxio.advancedbilling.utils.TestTeardown;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class BaseSubscriptionGroupStatusControllerTest {
protected final TestSetup testSetup = new TestSetup();
protected final AdvancedBillingClient client = TestClientProvider.getClient();
protected SubscriptionGroupStatusController subscriptionGroupStatusController = client.getSubscriptionGroupStatusController();
protected SubscriptionGroupsController subscriptionGroupsController = client.getSubscriptionGroupsController();
protected SubscriptionsController subscriptionsController = client.getSubscriptionsController();
protected InvoicesController invoicesController = client.getInvoicesController();
protected SubscriptionComponentsController subscriptionComponentsController = client.getSubscriptionComponentsController();

protected Customer customer;
protected ProductFamily productFamily;
protected Product product;
protected Component meteredComponent;
protected SubscriptionGroupData subscriptionGroupData;

@BeforeAll
void setUp() throws IOException, ApiException {
customer = testSetup.createCustomer();
productFamily = testSetup.createProductFamily();
product = testSetup.createProduct(productFamily);
meteredComponent = testSetup.createMeteredComponent(productFamily, 10.0);
}

@AfterEach
void teardown() throws IOException, ApiException {
if (subscriptionGroupData != null) {
subscriptionGroupData.cleanup();
}
}

@AfterAll
void cleanupCustomer() throws IOException, ApiException {
new TestTeardown().deleteCustomer(customer);
}

protected SubscriptionGroupData createGroup() throws IOException, ApiException {
return createGroup(b -> {
});
}

protected SubscriptionGroupData createGroup(Consumer<CreateSubscription.Builder> subscriptionCustomizer) throws IOException, ApiException {
Consumer<CreateSubscription.Builder> customizer = subscriptionCustomizer.andThen(builder ->
builder.components(List.of(
new CreateSubscriptionComponent.Builder()
.componentId(CreateSubscriptionComponentComponentId.fromNumber(meteredComponent.getId()))
.unitBalance(10)
.build()
)));

Subscription subscription1 = testSetup.createSubscription(customer, product, customizer);
Subscription subscription2 = testSetup.createSubscription(customer, product, customizer);

// when
subscriptionGroupsController
.createSubscriptionGroup(new CreateSubscriptionGroupRequest(new CreateSubscriptionGroup.Builder()
.subscriptionId(subscription1.getId())
.memberIds(List.of(subscription2.getId()))
.build()
));
FullSubscriptionGroupResponse subscriptionGroup = subscriptionGroupsController
.findSubscriptionGroup(subscription1.getId().toString());
subscriptionGroupData = new SubscriptionGroupData(subscriptionGroup, subscription1, List.of(subscription2), customer);
return subscriptionGroupData;
}

protected class SubscriptionGroupData {
private final Customer customer;
private final List<Subscription> members;
private FullSubscriptionGroupResponse subscriptionGroup;
private Subscription primarySubscription;
private boolean cleaned = false;

public SubscriptionGroupData(FullSubscriptionGroupResponse subscriptionGroup, Subscription primarySubscription, List<Subscription> members, Customer customer) {
this.subscriptionGroup = subscriptionGroup;
this.primarySubscription = primarySubscription;
this.members = new ArrayList<>(members);
this.customer = customer;
}

public FullSubscriptionGroupResponse getSubscriptionGroup() {
return subscriptionGroup;
}

public Subscription getPrimarySubscription() {
return primarySubscription;
}

public Subscription getMember(int i) {
return members.get(i);
}

public List<Subscription> getMembers() {
return members;
}

public List<Integer> getMemeberIds() {
return getMembers().stream().map(Subscription::getId).toList();
}

void refresh() throws IOException, ApiException {
subscriptionGroup = subscriptionGroupsController.readSubscriptionGroup(subscriptionGroup.getUid(), List.of());
primarySubscription = subscriptionsController.readSubscription(primarySubscription.getId(), List.of()).getSubscription();
for (int i = 0; i < members.size(); ++i) {
Subscription refreshedMember = subscriptionsController.readSubscription(members.get(i).getId(), List.of()).getSubscription();
members.set(i, refreshedMember);
}
}

void cleanup() throws IOException, ApiException {
if (!cleaned) {
Subscription primary = subscriptionsController.readSubscription(primarySubscription.getId(), List.of()).getSubscription();
if (primary.getState() != SubscriptionState.ACTIVE) {
subscriptionGroupStatusController.reactivateSubscriptionGroup(subscriptionGroup.getUid(), new ReactivateSubscriptionGroupRequest());
}
new TestTeardown().deleteSubscriptionGroup(primarySubscription.getId(), getMemeberIds(), customer.getId(), false);
cleaned = true;
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.maxio.advancedbilling.controllers.subscriptiongroupstatus;

import com.maxio.advancedbilling.TestClientProvider;
import com.maxio.advancedbilling.exceptions.ApiException;
import com.maxio.advancedbilling.models.CancelGroupedSubscriptionsRequest;
import com.maxio.advancedbilling.models.FullSubscriptionGroupResponse;
import com.maxio.advancedbilling.models.Subscription;
import com.maxio.advancedbilling.models.SubscriptionState;
import org.junit.jupiter.api.Test;

import java.io.IOException;

import static com.maxio.advancedbilling.utils.assertions.CommonAssertions.assertNotFound;
import static com.maxio.advancedbilling.utils.assertions.CommonAssertions.assertThatErrorListResponse;
import static com.maxio.advancedbilling.utils.assertions.CommonAssertions.assertUnauthorized;
import static org.assertj.core.api.Assertions.assertThat;

public class SubscriptionGroupStatusControllerCancelDelayedCancellationTest extends BaseSubscriptionGroupStatusControllerTest {

@Test
void shouldCancelDelayedCancellation() throws IOException, ApiException {
// given
SubscriptionGroupData group = createGroup();
subscriptionGroupStatusController.initiateDelayedCancellationForGroup(group.getSubscriptionGroup().getUid());

// when
subscriptionGroupStatusController.cancelDelayedCancellationForGroup(group.getSubscriptionGroup().getUid());

// then
group.refresh();

FullSubscriptionGroupResponse subscriptionGroup = group.getSubscriptionGroup();
assertThat(subscriptionGroup.getState()).isEqualTo(SubscriptionState.ACTIVE);
assertThat(subscriptionGroup.getCancelAtEndOfPeriod()).isFalse();

Subscription primarySub = group.getPrimarySubscription();
Subscription secondarySub = group.getMembers().get(0);

assertThat(primarySub.getState()).isEqualTo(SubscriptionState.ACTIVE);
assertThat(primarySub.getCanceledAt()).isNull();
assertThat(primarySub.getDelayedCancelAt()).isNull();
assertThat(primarySub.getCancelAtEndOfPeriod()).isNull();

assertThat(secondarySub.getState()).isEqualTo(SubscriptionState.ACTIVE);
assertThat(secondarySub.getCanceledAt()).isNull();
assertThat(secondarySub.getDelayedCancelAt()).isNull();
assertThat(secondarySub.getCancelAtEndOfPeriod()).isNull();
}

@Test
void shouldThrowExceptionIfAlreadyCancelled() throws IOException, ApiException {
// given
SubscriptionGroupData group = createGroup();
subscriptionGroupStatusController.cancelSubscriptionsInGroup(group.getSubscriptionGroup().getUid(),
new CancelGroupedSubscriptionsRequest(false));

// when then
assertThatErrorListResponse(() ->
subscriptionGroupStatusController.cancelDelayedCancellationForGroup(group.getSubscriptionGroup().getUid()))
.isUnprocessableEntity()
.hasMessage("HTTP Response Not OK. Status code: 422. Response: '{errors:[Primary Subscription is already canceled. " +
"Changes to schedule cancellation are blocked.,Subscriptions group does not have a pending delayed cancellation]}'.")
.hasErrors("Primary Subscription is already canceled. Changes to schedule cancellation are blocked.",
"Subscriptions group does not have a pending delayed cancellation");
}

@Test
void shouldReturn404IfGroupDoesNotExist() {
// when - then
assertNotFound(() -> subscriptionGroupStatusController.cancelDelayedCancellationForGroup("not_existing"));
}

@Test
void shouldReturn401WhenProvidingInvalidCredentials() throws IOException, ApiException {
// given
SubscriptionGroupData group = createGroup();

// when - then
assertUnauthorized(() -> TestClientProvider.createInvalidCredentialsClient().getSubscriptionGroupStatusController()
.cancelDelayedCancellationForGroup(group.getSubscriptionGroup().getUid()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.maxio.advancedbilling.controllers.subscriptiongroupstatus;

import com.maxio.advancedbilling.TestClientProvider;
import com.maxio.advancedbilling.exceptions.ApiException;
import com.maxio.advancedbilling.models.CancelGroupedSubscriptionsRequest;
import com.maxio.advancedbilling.models.CreateUsage;
import com.maxio.advancedbilling.models.CreateUsageRequest;
import com.maxio.advancedbilling.models.FullSubscriptionGroupResponse;
import com.maxio.advancedbilling.models.Invoice;
import com.maxio.advancedbilling.models.ListInvoicesInput;
import com.maxio.advancedbilling.models.Subscription;
import com.maxio.advancedbilling.models.SubscriptionState;
import com.maxio.advancedbilling.models.containers.CreateUsageComponentId;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;

import static com.maxio.advancedbilling.utils.assertions.CommonAssertions.assertNotFound;
import static com.maxio.advancedbilling.utils.assertions.CommonAssertions.assertUnauthorized;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.byLessThan;

public class SubscriptionGroupStatusControllerCancelTest extends BaseSubscriptionGroupStatusControllerTest {

@Test
void shouldCancelGroupWithoutCharge() throws IOException, ApiException {
// given
ZonedDateTime before = ZonedDateTime.now().truncatedTo(ChronoUnit.SECONDS);

SubscriptionGroupData group = createGroup();
CreateUsageComponentId componentId = CreateUsageComponentId.fromNumber(meteredComponent.getId());
CreateUsageRequest usageRequest = new CreateUsageRequest(
new CreateUsage.Builder()
.quantity(10.0)
.memo("testing")
.build()
);

subscriptionComponentsController.createUsage(group.getPrimarySubscription().getId(), componentId, usageRequest);
subscriptionComponentsController.createUsage(group.getMember(0).getId(), componentId, usageRequest);

// when
subscriptionGroupStatusController.cancelSubscriptionsInGroup(group.getSubscriptionGroup().getUid(),
new CancelGroupedSubscriptionsRequest(false));

// then
group.refresh();

FullSubscriptionGroupResponse subscriptionGroup = group.getSubscriptionGroup();
assertThat(subscriptionGroup.getCancelAtEndOfPeriod()).isFalse();
assertThat(subscriptionGroup.getState()).isEqualTo(SubscriptionState.CANCELED);

Subscription primarySub = group.getPrimarySubscription();
assertThat(primarySub.getState()).isEqualTo(SubscriptionState.CANCELED);
assertThat(primarySub.getCanceledAt()).isCloseTo(before, byLessThan(60, ChronoUnit.SECONDS));

Subscription secondarySub = group.getMember(0);
assertThat(secondarySub.getState()).isEqualTo(SubscriptionState.CANCELED);
assertThat(secondarySub.getCanceledAt()).isCloseTo(before, byLessThan(60, ChronoUnit.SECONDS));

// no charge at cancellation, only one invoice from signup
assertThat(getInvoices(primarySub)).hasSize(1);
assertThat(getInvoices(secondarySub)).hasSize(1);
}

@Test
void shouldCancelGroupWithCharge() throws IOException, ApiException {
// given
ZonedDateTime before = ZonedDateTime.now().truncatedTo(ChronoUnit.SECONDS);

SubscriptionGroupData group = createGroup();
CreateUsageComponentId componentId = CreateUsageComponentId.fromNumber(meteredComponent.getId());
CreateUsageRequest usageRequest = new CreateUsageRequest(
new CreateUsage.Builder()
.quantity(10.0)
.memo("testing")
.build()
);

subscriptionComponentsController.createUsage(group.getPrimarySubscription().getId(), componentId, usageRequest);
subscriptionComponentsController.createUsage(group.getMember(0).getId(), componentId, usageRequest);

// when
subscriptionGroupStatusController.cancelSubscriptionsInGroup(group.getSubscriptionGroup().getUid(),
new CancelGroupedSubscriptionsRequest(true));

// then
group.refresh();

FullSubscriptionGroupResponse subscriptionGroup = group.getSubscriptionGroup();
assertThat(subscriptionGroup.getCancelAtEndOfPeriod()).isFalse();
assertThat(subscriptionGroup.getState()).isEqualTo(SubscriptionState.CANCELED);

Subscription primarySub = group.getPrimarySubscription();
assertThat(primarySub.getState()).isEqualTo(SubscriptionState.CANCELED);
assertThat(primarySub.getCanceledAt()).isCloseTo(before, byLessThan(60, ChronoUnit.SECONDS));

Subscription secondarySub = group.getMember(0);
assertThat(secondarySub.getState()).isEqualTo(SubscriptionState.CANCELED);
assertThat(secondarySub.getCanceledAt()).isCloseTo(before, byLessThan(60, ChronoUnit.SECONDS));

// additional charge, second invoice
assertThat(getInvoices(primarySub)).hasSize(2);
assertThat(getInvoices(secondarySub)).hasSize(2);
}

@Test
void shouldReturn404IfGroupDoesNotExist() {
// when - then
assertNotFound(() -> subscriptionGroupStatusController
.cancelSubscriptionsInGroup("not_existing", new CancelGroupedSubscriptionsRequest(false)));
}

@Test
void shouldReturn401WhenProvidingInvalidCredentials() throws IOException, ApiException {
// given
SubscriptionGroupData group = createGroup();

// when - then
assertUnauthorized(() -> TestClientProvider.createInvalidCredentialsClient().getSubscriptionGroupStatusController()
.cancelSubscriptionsInGroup(group.getSubscriptionGroup().getUid(), new CancelGroupedSubscriptionsRequest(false)));
}

private List<Invoice> getInvoices(Subscription subscription) throws ApiException, IOException {
return invoicesController.listInvoices(new ListInvoicesInput.Builder()
.subscriptionId(subscription.getId())
.build())
.getInvoices();
}
}
Loading
Loading