@@ -9,6 +9,7 @@ import bubble.model.bill.AccountPlan; | |||
import bubble.model.cloud.CloudService; | |||
import bubble.server.BubbleConfiguration; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.hibernate.criterion.Order; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Repository; | |||
@@ -24,6 +25,8 @@ public class AccountPaymentMethodDAO extends AccountOwnedEntityDAO<AccountPaymen | |||
@Autowired private CloudServiceDAO cloudDAO; | |||
@Autowired private BubbleConfiguration configuration; | |||
@Override public Order getDefaultSortOrder() { return ORDER_CTIME_DESC; } | |||
public AccountPaymentMethod findByAccountAndPaymentInfo(String account, String paymentInfo) { | |||
return findByAccount(account).stream() | |||
.filter(apm -> apm.getPaymentInfo().equals(paymentInfo)) | |||
@@ -26,16 +26,16 @@ public class PromotionDAO extends AbstractCRUDDAO<Promotion> { | |||
} | |||
public Promotion findEnabledAndActiveWithCode(String code) { | |||
return filterActive(findByUniqueFields("enabled", true, "code", code, "referral", false)); | |||
return filterActive(findByUniqueFields("enabled", true, "code", code, "referral", false, "adminAssignOnly", false)); | |||
} | |||
public List<Promotion> findEnabledAndActiveWithNoCode() { | |||
return filterActive(findByFields("enabled", true, "code", null, "referral", false)); | |||
return filterActive(findByFields("enabled", true, "code", null, "referral", false, "adminAssignOnly", false)); | |||
} | |||
public List<Promotion> findVisibleAndEnabledAndActiveWithNoCodeOrWithCode(String code) { | |||
if (empty(code)) { | |||
return filterActive(findByFields("enabled", true, "code", null, "visible", true)); | |||
return filterActive(findByFields("enabled", true, "code", null, "visible", true, "adminAssignOnly", false)); | |||
} else { | |||
return filterActive(list(criteria().add(and( | |||
eq("enabled", true), | |||
@@ -45,7 +45,7 @@ public class PromotionDAO extends AbstractCRUDDAO<Promotion> { | |||
} | |||
public List<Promotion> findEnabledAndActiveWithReferral() { | |||
return filterActive(findByFields("enabled", true, "referral", true)); | |||
return filterActive(findByFields("enabled", true, "referral", true, "adminAssignOnly", false)); | |||
} | |||
public Promotion filterActive(Promotion promo) { return promo != null && promo.active() ? promo : null; } | |||
@@ -30,7 +30,9 @@ import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | |||
public class Promotion extends IdentifiableBase | |||
implements NamedEntity, HasPriority, SqlViewSearchResult, Comparable<Promotion> { | |||
public static final String[] UPDATE_FIELDS = {"priority", "enabled", "validFrom", "validTo"}; | |||
public static final String[] UPDATE_FIELDS = { | |||
"priority", "enabled", "visible", "adminAssignOnly", "validFrom", "validTo" | |||
}; | |||
public static final String[] CREATE_FIELDS = ArrayUtil.append(UPDATE_FIELDS, | |||
"name", "code", "referral", "currency", "maxValue"); | |||
@@ -87,30 +89,36 @@ public class Promotion extends IdentifiableBase | |||
public boolean invisible() { return !visible(); } | |||
@ECSearchable @ECField(index=70) | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean adminAssignOnly = false; | |||
public boolean adminAssignOnly() { return adminAssignOnly != null && adminAssignOnly; } | |||
public boolean notAdminAssignOnly() { return !adminAssignOnly(); } | |||
@ECSearchable @ECField(index=80) | |||
@ECIndex @Getter @Setter private Long validFrom; | |||
public boolean hasStarted () { return validFrom == null || validFrom > now(); } | |||
@ECSearchable @ECField(index=80) | |||
@ECSearchable @ECField(index=90) | |||
@ECIndex @Getter @Setter private Long validTo; | |||
public boolean hasEnded () { return validTo != null && validTo > now(); } | |||
public boolean active () { return enabled() && hasStarted() && !hasEnded(); } | |||
public boolean inactive () { return !active(); } | |||
@ECSearchable @ECField(index=90) | |||
@ECSearchable @ECField(index=100) | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean referral = false; | |||
public boolean referral () { return referral != null && referral; } | |||
@ECSearchable @ECField(index=100) | |||
@ECSearchable @ECField(index=110) | |||
@ECIndex @Column(nullable=false, updatable=false, length=10) | |||
@Getter @Setter private String currency; | |||
@ECSearchable @ECField(index=110) | |||
@ECSearchable @ECField(index=120) | |||
@ECIndex @Column(nullable=false, updatable=false) | |||
@Getter @Setter private Integer minValue = 100; | |||
@ECSearchable @ECField(index=120) | |||
@ECSearchable @ECField(index=130) | |||
@ECIndex @Column(nullable=false, updatable=false) | |||
@Getter @Setter private Integer maxValue; | |||
@@ -157,8 +157,11 @@ public class PromotionService { | |||
return chargeAmount; | |||
} | |||
promos.sort(Promotion::compareTo); | |||
// find the payment cloud associated with the promo, defer to that | |||
final String accountPlanUuid = accountPlan.getUuid(); | |||
final Set<Promotion> used = new HashSet<>(); | |||
for (Promotion promo : promos) { | |||
final AccountPaymentMethod apm = promo.getPaymentMethod(); | |||
final CloudService promoCloud = cloudDAO.findByUuid(promo.getCloud()); | |||
@@ -175,19 +178,18 @@ public class PromotionService { | |||
try { | |||
final PaymentServiceDriver promoPaymentDriver = promoCloud.getPaymentDriver(configuration); | |||
final PromotionalPaymentServiceDriver promoDriver = (PromotionalPaymentServiceDriver) promoPaymentDriver; | |||
final Set<Promotion> usable = new HashSet<>(); | |||
if (!promoDriver.canUseNow(bill, promo, promoDriver, promos, usable, accountPlan, paymentMethod)) { | |||
if (!promoDriver.canUseNow(bill, promo, promoDriver, promos, used, accountPlan, paymentMethod)) { | |||
log.warn("purchase: Promotion "+promo.getName()+" cannot currently be used for accountPlan "+ accountPlanUuid); | |||
continue; | |||
} | |||
usable.add(promo); | |||
promoDriver.purchase(accountPlanUuid, apm.getUuid(), bill.getUuid()); | |||
used.add(promo); | |||
// verify AccountPayments exists for new payment with promo | |||
final List<AccountPayment> creditsApplied = accountPaymentDAO.findByAccountAndAccountPlanAndBillAndCreditAppliedSuccess(accountPlan.getAccount(), accountPlanUuid, bill.getUuid()); | |||
final List<AccountPayment> creditsByThisPromo = creditsApplied.stream() | |||
.filter(c -> c.getPaymentMethod().equals(apm.getUuid())) | |||
.collect(Collectors.toList());; | |||
.collect(Collectors.toList()); | |||
if (empty(creditsByThisPromo)) { | |||
log.warn("purchase: applying promotion did not result in an AccountPayment to Bill "+bill.getUuid()); | |||
continue; | |||
@@ -1,16 +0,0 @@ | |||
package bubble.test.promo; | |||
import bubble.test.payment.PaymentTestBase; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.junit.Test; | |||
@Slf4j | |||
public class FirstMonthAndReferralMonthPromotionTest extends PaymentTestBase { | |||
@Override protected String getManifest() { return "promo/1mo_and_referral/manifest_1mo_and_referral"; } | |||
@Test public void testFirstMonthAndMultipleReferralMonthsFree () throws Exception { | |||
modelTest("promo/first_month_and_multiple_referral_months_free"); | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
package bubble.test.promo; | |||
import bubble.test.payment.PaymentTestBase; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.junit.Test; | |||
@Slf4j | |||
public class MultiplePromotionsTest extends PaymentTestBase { | |||
@Override protected String getManifest() { return "promo/multi/manifest_multi"; } | |||
@Test public void testMultiplePromotions() throws Exception { modelTest("promo/multi_promo"); } | |||
} |
@@ -0,0 +1,228 @@ | |||
[ | |||
{ | |||
"comment": "declare defaults for referral_signup test part", | |||
"include": "_defaults", | |||
"params": { | |||
"referralCode": "_required", | |||
"referredName": "_required", | |||
"referredUserSessionName": "referredUserSession", | |||
"referredUserVar": "referredUser", | |||
"referredPaymentMethodsVar": "referredPaymentMethods", | |||
"referredAccountPlanVar": "referredAccountPlan", | |||
"rootSessionName": "rootSession", | |||
"plansVar": "plans" | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: register an account for the referred user, using one of the referral codes", | |||
"request": { | |||
"session": "new", | |||
"uri": "auth/register", | |||
"entity": { | |||
"name": "<<referredName>>", | |||
"password": "password1!", | |||
"contact": {"type": "email", "info": "<<referredName>>@example.com"}, | |||
"promoCode": "<<referralCode>>" | |||
} | |||
}, | |||
"response": { | |||
"store": "<<referredUserVar>>", | |||
"sessionName": "<<referredUserSessionName>>", | |||
"session": "token" | |||
} | |||
}, | |||
{ | |||
"before": "sleep 5s", // wait for account objects to be created | |||
"comment": "as root, check email inbox for verification message", | |||
"request": { | |||
"session": "<<rootSessionName>>", | |||
"uri": "debug/inbox/email/<<referredName>>@example.com?type=request&action=verify&target=account" | |||
}, | |||
"response": { | |||
"store": "emailInbox", | |||
"check": [ | |||
{"condition": "'{{json.[0].ctx.message.messageType}}' == 'request'"}, | |||
{"condition": "'{{json.[0].ctx.message.action}}' == 'verify'"}, | |||
{"condition": "'{{json.[0].ctx.message.target}}' == 'account'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: approve email verification request", | |||
"request": { | |||
"session": "<<referredUserSessionName>>", | |||
"uri": "auth/approve/{{emailInbox.[0].ctx.confirmationToken}}", | |||
"method": "post" | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: lookup payment methods, ensure both FirstMonthFree and ReferralMonthFree are present, tokenize a credit card", | |||
"request": { "uri": "me/paymentMethods" }, | |||
"response": { | |||
"store": "<<referredPaymentMethodsVar>>", | |||
"check": [ | |||
{"condition": "json.length === 2"}, | |||
{"condition": "json[0].getPaymentMethodType().name() === 'promotional_credit'"}, | |||
{"condition": "json[0].deleted() === false"}, | |||
{"condition": "json[1].getPaymentMethodType().name() === 'promotional_credit'"}, | |||
{"condition": "json[1].deleted() === false"}, | |||
{"condition": "_find(json, function (p) { return p.getMaskedPaymentInfo() === 'FirstMonthFree'; }) !== null"}, | |||
{"condition": "_find(json, function (p) { return p.getMaskedPaymentInfo() === 'ReferralMonthFree'; }) !== null"} | |||
] | |||
}, | |||
"after": "stripe_tokenize_card" | |||
}, | |||
{ | |||
"comment": "<<referredName>>: add plan, using 'credit' payment method, applied first-month free promo", | |||
"request": { | |||
"uri": "me/plans", | |||
"method": "put", | |||
"entity": { | |||
"name": "test-net-{{rand 5}}", | |||
"domain": "{{defaultDomain}}", | |||
"locale": "en_US", | |||
"timezone": "EST", | |||
"plan": "{{<<plansVar>>.[0].name}}", | |||
"footprint": "US", | |||
"paymentMethodObject": { | |||
"paymentMethodType": "credit", | |||
"paymentInfo": "{{stripeToken}}" | |||
} | |||
} | |||
}, | |||
"response": { | |||
"store": "<<referredAccountPlanVar>>" | |||
} | |||
}, | |||
{ | |||
"before": "sleep 10s", | |||
"comment": "<<referredName>>: start the network", | |||
"request": { | |||
"uri": "me/networks/{{<<referredAccountPlanVar>>.network}}/actions/start?cloud=MockCompute®ion=nyc_mock", | |||
"method": "post" | |||
} | |||
}, | |||
{ | |||
"before": "sleep 3s", | |||
"comment": "<<referredName>>: verify the network is running", | |||
"request": { "uri": "me/networks/{{<<referredAccountPlanVar>>.network}}" }, | |||
"response": { | |||
"check": [ {"condition": "json.getState().name() == 'running'"} ] | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: list all account payment methods, should be three, with the FirstMonthFree credit used up", | |||
"request": { "uri": "me/paymentMethods?all=true" }, | |||
"response": { | |||
"store": "<<referredPaymentMethodsVar>>", | |||
"check": [ | |||
{"condition": "json.length === 3"}, | |||
{"condition": "_find(json, function(p) { return p.getPaymentMethodType().name() === 'credit'; }) !== null"}, | |||
{"condition": "_find(json, function(p) { return p.getPaymentMethodType().name() === 'credit'; }).deleted() === false"}, | |||
{"condition": "_find(json, function(p) { return p.getMaskedPaymentInfo() === 'ReferralMonthFree'; }).deleted() === false"}, | |||
{"condition": "_find(json, function(p) { return p.getMaskedPaymentInfo() === 'FirstMonthFree'; }).deleted() === true"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: verify account plans, should be one, verify enabled", | |||
"request": { "uri": "me/plans" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getName() === <<referredAccountPlanVar>>.getName()"}, | |||
{"condition": "json[0].enabled()"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: verify account plan payment info", | |||
"request": { "uri": "me/plans/{{<<referredAccountPlanVar>>.uuid}}/paymentMethod" }, | |||
"response": { | |||
"store": "referredCreditPaymentMethod", | |||
"check": [ | |||
{"condition": "json.getPaymentMethodType().name() === 'credit'"}, | |||
{"condition": "json.getMaskedPaymentInfo() == 'XXXX-XXXX-XXXX-4242'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: verify bill exists and was paid", | |||
"request": { "uri": "me/bills" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{<<plansVar>>.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{<<referredAccountPlanVar>>.uuid}}'"}, | |||
{"condition": "json[0].getQuantity() === 1"}, | |||
{"condition": "json[0].getPrice() === {{<<plansVar>>.[0].price}}"}, | |||
{"condition": "json[0].getTotal() === {{<<plansVar>>.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'paid'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: verify bill exists via plan and is paid", | |||
"request": { "uri": "me/plans/{{<<referredAccountPlanVar>>.uuid}}/bills" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{<<plansVar>>.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{<<referredAccountPlanVar>>.uuid}}'"}, | |||
{"condition": "json[0].getQuantity() === 1"}, | |||
{"condition": "json[0].getPrice() === {{<<plansVar>>.[0].price}}"}, | |||
{"condition": "json[0].getTotal() === {{<<plansVar>>.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'paid'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: verify payment exists and is successful and used a promotion", | |||
"request": { "uri": "me/payments" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{<<plansVar>>.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{<<referredAccountPlanVar>>.uuid}}'"}, | |||
{"condition": "json[0].getAmount() === {{<<plansVar>>.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'success'"}, | |||
{"condition": "json[0].getType().name() === 'credit_applied'"}, | |||
{"condition": "json[0].getPaymentMethod() === _find(<<referredPaymentMethodsVar>>, function(p) { return p.getMaskedPaymentInfo() === 'FirstMonthFree'; }).getUuid()"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "<<referredName>>: verify payment exists via plan and is successful and used a promotion", | |||
"request": { "uri": "me/plans/{{<<referredAccountPlanVar>>.uuid}}/payments" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{<<plansVar>>.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{<<referredAccountPlanVar>>.uuid}}'"}, | |||
{"condition": "json[0].getAmount() === {{<<plansVar>>.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'success'"}, | |||
{"condition": "json[0].getType().name() === 'credit_applied'"}, | |||
{"condition": "json[0].getPaymentMethod() === _find(<<referredPaymentMethodsVar>>, function(p) { return p.getMaskedPaymentInfo() === 'FirstMonthFree'; }).getUuid()"}, | |||
{"condition": "json[0].getBillObject().getPlan() === '{{<<plansVar>>.[0].uuid}}'"}, | |||
{"condition": "json[0].getBillObject().getAccountPlan() === '{{<<referredAccountPlanVar>>.uuid}}'"}, | |||
{"condition": "json[0].getBillObject().getQuantity() === 1"}, | |||
{"condition": "json[0].getBillObject().getPrice() === {{<<plansVar>>.[0].price}}"}, | |||
{"condition": "json[0].getBillObject().getTotal() === {{<<plansVar>>.[0].price}}"}, | |||
{"condition": "json[0].getBillObject().getStatus().name() === 'paid'"} | |||
] | |||
} | |||
} | |||
] |
@@ -2,7 +2,7 @@ | |||
{ | |||
"name": "FirstMonthFree", | |||
"cloud": "FirstMonthFree", | |||
"priority": 1, | |||
"priority": -10000, | |||
"currency": "USD", | |||
"maxValue": 1200 | |||
} |
@@ -1,5 +0,0 @@ | |||
[ | |||
"manifest-test", | |||
"promo/1mo/manifest_1mo", | |||
"promo/referral/manifest_referral" | |||
] |
@@ -2,25 +2,28 @@ | |||
{ | |||
"name": "AccountCredit1", | |||
"cloud": "AccountCredit1", | |||
"priority": 1, | |||
"priority": 10000, | |||
"currency": "USD", | |||
"maxValue": 100, | |||
"visible": false | |||
"visible": false, | |||
"adminAssignOnly": true | |||
}, | |||
{ | |||
"name": "AccountCredit5", | |||
"cloud": "AccountCredit5", | |||
"priority": 1, | |||
"priority": 10000, | |||
"currency": "USD", | |||
"maxValue": 500, | |||
"visible": false | |||
"visible": false, | |||
"adminAssignOnly": true | |||
}, | |||
{ | |||
"name": "AccountCreditBill", | |||
"cloud": "AccountCreditBill", | |||
"priority": 1, | |||
"priority": 10000, | |||
"currency": "USD", | |||
"maxValue": 10000, | |||
"visible": false | |||
"visible": false, | |||
"adminAssignOnly": true | |||
} | |||
] |
@@ -0,0 +1,6 @@ | |||
[ | |||
"manifest-test", | |||
"promo/1mo/manifest_1mo", | |||
"promo/referral/manifest_referral", | |||
"promo/credit/manifest_credit" | |||
] |
@@ -2,9 +2,9 @@ | |||
{ | |||
"name": "ReferralMonthFree", | |||
"cloud": "ReferralMonthFree", | |||
"priority": 1, | |||
"priority": -1000, | |||
"currency": "USD", | |||
"maxValue": 1200, | |||
"maxValue": 2500, | |||
"referral": true | |||
} | |||
] |
@@ -1 +0,0 @@ | |||
[] |
@@ -0,0 +1,347 @@ | |||
[ | |||
{ | |||
"comment": "root: create a user account for the referring user", | |||
"request": { | |||
"session": "new", | |||
"uri": "auth/register", | |||
"entity": { | |||
"name": "test_user_referring_multi", | |||
"password": "password1!", | |||
"contact": {"type": "email", "info": "test_user_referring_multi@example.com"} | |||
} | |||
}, | |||
"response": { | |||
"store": "referringUser", | |||
"sessionName": "referringUserSession", | |||
"session": "token" | |||
} | |||
}, | |||
{ | |||
"before": "sleep 5s", | |||
"comment": "referring: lookup payment methods, should have 1mo free", | |||
"request": { | |||
"session": "referringUserSession", | |||
"uri": "me/paymentMethods" | |||
}, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].hasPromotion()"}, | |||
{"condition": "json[0].getMaskedPaymentInfo() === 'FirstMonthFree'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: list promos, should have 1mo free", | |||
"request": { | |||
"uri": "me/promos" | |||
}, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getName() === 'FirstMonthFree'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "root: check email inbox for verification message for referring user", | |||
"request": { | |||
"session": "rootSession", | |||
"uri": "debug/inbox/email/test_user_referring_multi@example.com?type=request&action=verify&target=account" | |||
}, | |||
"response": { | |||
"store": "emailInbox", | |||
"check": [ | |||
{"condition": "'{{json.[0].ctx.message.messageType}}' == 'request'"}, | |||
{"condition": "'{{json.[0].ctx.message.action}}' == 'verify'"}, | |||
{"condition": "'{{json.[0].ctx.message.target}}' == 'account'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "as root, grant some referral codes to the referring user", | |||
"request": { | |||
"uri": "users/test_user_referring_multi/referralCodes", | |||
"method": "put", | |||
"entity": { "count": 3 } | |||
}, | |||
"response": { | |||
"store": "referralCodes", | |||
"check": [ {"condition": "json.length === 3"} ] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: approve email verification request for referring user", | |||
"request": { | |||
"session": "referringUserSession", | |||
"uri": "auth/approve/{{emailInbox.[0].ctx.confirmationToken}}", | |||
"method": "post" | |||
} | |||
}, | |||
{ | |||
"comment": "referring: list referral codes, verify all codes are unused", | |||
"request": { "uri": "me/referralCodes" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 3"}, | |||
{"condition": "json[0].getClaimedBy() === null"}, | |||
{"condition": "json[1].getClaimedBy() === null"}, | |||
{"condition": "json[2].getClaimedBy() === null"}, | |||
{"condition": "json[0].getClaimedByUuid() === null"}, | |||
{"condition": "json[1].getClaimedByUuid() === null"}, | |||
{"condition": "json[2].getClaimedByUuid() === null"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: get plans", | |||
"request": { "uri": "plans" }, | |||
"response": { | |||
"store": "plans", | |||
"check": [{"condition": "json.length >= 1"}] | |||
} | |||
}, | |||
{ | |||
"comment": "sign up as first referred user", | |||
"include": "referral_signup", | |||
"params": { | |||
"referralCode": "{{referralCodes.[0].name}}", | |||
"referredName": "referred_multi_1" | |||
} | |||
}, | |||
{ | |||
"comment": "sign up as second referred user", | |||
"include": "referral_signup", | |||
"params": { | |||
"referralCode": "{{referralCodes.[1].name}}", | |||
"referredName": "referred_multi_2" | |||
} | |||
}, | |||
{ | |||
"comment": "sign up as third referred user", | |||
"include": "referral_signup", | |||
"params": { | |||
"referralCode": "{{referralCodes.[2].name}}", | |||
"referredName": "referred_multi_3" | |||
} | |||
}, | |||
{ | |||
"comment": "referring: list referral codes after 3 referrals, verify all codes have been claimed, tokenize a card", | |||
"request": { | |||
"session": "referringUserSession", | |||
"uri": "me/referralCodes?show=all" | |||
}, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 3"}, | |||
{"condition": "json[0].getClaimedBy() !== null"}, | |||
{"condition": "json[1].getClaimedBy() !== null"}, | |||
{"condition": "json[2].getClaimedBy() !== null"}, | |||
{"condition": "json[0].getClaimedByUuid() !== null"}, | |||
{"condition": "json[1].getClaimedByUuid() !== null"}, | |||
{"condition": "json[2].getClaimedByUuid() !== null"} | |||
] | |||
}, | |||
"after": "stripe_tokenize_card" | |||
}, | |||
{ | |||
"comment": "referring: add plan, using 'credit' payment method, applies 1mo promo", | |||
"request": { | |||
"uri": "me/plans", | |||
"method": "put", | |||
"entity": { | |||
"name": "test-net-{{rand 5}}", | |||
"domain": "{{defaultDomain}}", | |||
"locale": "en_US", | |||
"timezone": "EST", | |||
"plan": "{{plans.[0].name}}", | |||
"footprint": "US", | |||
"paymentMethodObject": { | |||
"paymentMethodType": "credit", | |||
"paymentInfo": "{{stripeToken}}" | |||
} | |||
} | |||
}, | |||
"response": { | |||
"store": "referringAccountPlan" | |||
} | |||
}, | |||
{ | |||
"before": "sleep 15s", | |||
"comment": "referring: start the network", | |||
"request": { | |||
"uri": "me/networks/{{referringAccountPlan.network}}/actions/start?cloud=MockCompute®ion=nyc_mock", | |||
"method": "post" | |||
} | |||
}, | |||
{ | |||
"before": "sleep 5s", | |||
"comment": "referring: verify the network is running", | |||
"request": { "uri": "me/networks/{{referringAccountPlan.network}}" }, | |||
"response": { | |||
"check": [ {"condition": "json.getState().name() == 'running'"} ] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: list active account payment methods, should be five, FirstMonthFree used, ReferralMonthFree promos all unused", | |||
"request": { "uri": "me/paymentMethods?all=true" }, | |||
"response": { | |||
"store": "referringPaymentMethods", | |||
"check": [ | |||
{"condition": "json.length === 5"}, | |||
{"condition": "_find(json, function(p) { return p.getPaymentMethodType().name() === 'credit'; }) !== null"}, | |||
{"condition": "_find(json, function(p) { return p.getPaymentMethodType().name() === 'credit'; }).deleted() === false"}, | |||
{"condition": "_find(json, function(p) { return p.getMaskedPaymentInfo() === 'ReferralMonthFree' && p.deleted(); }) === null"}, | |||
{"condition": "_find(json, function(p) { return p.getMaskedPaymentInfo() === 'FirstMonthFree' && p.deleted(); }) !== null"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: verify account plans, should be one, verify enabled", | |||
"request": { "uri": "me/plans" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getName() === referringAccountPlan.getName()"}, | |||
{"condition": "json[0].enabled()"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: verify account plan payment info", | |||
"request": { "uri": "me/plans/{{referringAccountPlan.uuid}}/paymentMethod" }, | |||
"response": { | |||
"store": "referringCreditPaymentMethod", | |||
"check": [ | |||
{"condition": "json.getPaymentMethodType().name() === 'credit'"}, | |||
{"condition": "json.getMaskedPaymentInfo() == 'XXXX-XXXX-XXXX-4242'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: verify bill exists and was paid", | |||
"request": { "uri": "me/bills" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{referringAccountPlan.uuid}}'"}, | |||
{"condition": "json[0].getQuantity() === 1"}, | |||
{"condition": "json[0].getPrice() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getTotal() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'paid'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: verify bill exists via plan and is paid", | |||
"request": { "uri": "me/plans/{{referringAccountPlan.uuid}}/bills" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{referringAccountPlan.uuid}}'"}, | |||
{"condition": "json[0].getQuantity() === 1"}, | |||
{"condition": "json[0].getPrice() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getTotal() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'paid'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "referring: verify payment exists and is successful via 1mo free", | |||
"request": { "uri": "me/payments" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{referringAccountPlan.uuid}}'"}, | |||
{"condition": "json[0].getAmount() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'success'"}, | |||
{"condition": "json[0].getType().name() === 'credit_applied'"}, | |||
{"condition": "_find(referringPaymentMethods, function(p) { return p.getUuid() === json[0].getPaymentMethod() && p.getMaskedPaymentInfo() === 'FirstMonthFree' }) !== null"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "verify payment exists via plan and is successful via 1mo free", | |||
"request": { "uri": "me/plans/{{referringAccountPlan.uuid}}/payments" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{referringAccountPlan.uuid}}'"}, | |||
{"condition": "json[0].getAmount() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'success'"}, | |||
{"condition": "json[0].getType().name() === 'credit_applied'"}, | |||
{"condition": "_find(referringPaymentMethods, function(p) { return p.getUuid() === json[0].getPaymentMethod() && p.getMaskedPaymentInfo() === 'FirstMonthFree' }) !== null"}, | |||
{"condition": "json[0].getBillObject().getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json[0].getBillObject().getAccountPlan() === '{{referringAccountPlan.uuid}}'"}, | |||
{"condition": "json[0].getBillObject().getQuantity() === 1"}, | |||
{"condition": "json[0].getBillObject().getPrice() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getBillObject().getTotal() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getBillObject().getStatus().name() === 'paid'"} | |||
] | |||
} | |||
} | |||
// todo: fast-forward 1month | |||
// referred accounts have all paid via referral credit | |||
// referring account paid via credit card | |||
// todo: ff 1 month | |||
// referred accounts have paid via credit card | |||
// referring account used first referral credit | |||
// root: apply credit to referring account | |||
// ff 1 month | |||
// referred accounts have paid via credit card | |||
// referring account used second referral credit | |||
// ff 1 month | |||
// referred accounts have paid via credit card | |||
// referring account used third referral credit | |||
// ff 1 month | |||
// referred accounts have paid via credit card | |||
// referring account used partial regular account credit | |||
// ff 1 month | |||
// referred accounts have paid via credit card | |||
// referring account paid via credit card | |||
] |
@@ -72,7 +72,7 @@ This code is available under the GNU Affero General Public License, version 3: h | |||
<include>bubble.test.promo.FirstMonthFreePromotionTest</include> | |||
<include>bubble.test.promo.ReferralMonthFreePromotionTest</include> | |||
<include>bubble.test.promo.AccountCreditTest</include> | |||
<include>bubble.test.promo.FirstMonthAndReferralMonthPromotionTest</include> | |||
<include>bubble.test.promo.MultiplePromotionsTest</include> | |||
<include>bubble.test.system.DriverTest</include> | |||
<include>bubble.test.filter.ProxyTest</include> | |||
<include>bubble.test.filter.TrafficAnalyticsTest</include> | |||