restructured tests
This commit is contained in:
@@ -3,8 +3,11 @@
|
||||
# npx tsx scripts/alex.ts
|
||||
filename=$1
|
||||
|
||||
# Check if the file path contains "/tests/"
|
||||
if [[ $filename == *"/tests/"* ]]; then
|
||||
|
||||
# Check if the file path contains "shell"
|
||||
if [[ $filename == *"shell"* ]]; then
|
||||
$filename
|
||||
elif [[ $filename == *"/tests/"* ]]; then
|
||||
# Extract everything after "/tests/"
|
||||
path_after_tests=$(echo "$filename" | sed 's/.*\/tests\///')
|
||||
# Remove .ts extension if present
|
||||
|
||||
4
server/shell/config.sh
Normal file
4
server/shell/config.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
MOCHA_SETUP="npx mocha tests/00_setup.ts"
|
||||
MOCHA_CMD="npx mocha --parallel --timeout 10000000 --ignore tests/00_setup.ts"
|
||||
10
server/shell/g1.sh
Executable file
10
server/shell/g1.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Source shared configuration
|
||||
source "$(dirname "$0")/config.sh"
|
||||
|
||||
MOCHA_PARALLEL=true $MOCHA_SETUP \
|
||||
&& $MOCHA_CMD \
|
||||
'tests/attach/basic/*.ts' \
|
||||
'tests/attach/upgrade/*.ts' \
|
||||
'tests/attach/downgrade/*.ts'
|
||||
14
server/shell/g2.sh
Executable file
14
server/shell/g2.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Source shared configuration
|
||||
source "$(dirname "$0")/config.sh"
|
||||
|
||||
MOCHA_PARALLEL=true $MOCHA_SETUP && $MOCHA_CMD \
|
||||
'tests/attach/upgradeOld/*.ts' \
|
||||
'tests/attach/entities/*.ts' \
|
||||
'tests/attach/migrations/*.ts' \
|
||||
'tests/attach/newVersion/*.ts' \
|
||||
'tests/attach/others/*.ts' \
|
||||
'tests/attach/updateEnts/*.ts' \
|
||||
|
||||
|
||||
12
server/shell/g3.sh
Executable file
12
server/shell/g3.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Source shared configuration
|
||||
source "$(dirname "$0")/config.sh"
|
||||
|
||||
MOCHA_PARALLEL=true $MOCHA_SETUP
|
||||
|
||||
$MOCHA_CMD 'tests/contUse/entities/*.ts'
|
||||
|
||||
$MOCHA_CMD 'tests/contUse/update/*.ts'
|
||||
|
||||
$MOCHA_CMD 'tests/contUse/track/*.ts'
|
||||
16
server/shell/g4.sh
Executable file
16
server/shell/g4.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Source shared configuration
|
||||
source "$(dirname "$0")/config.sh"
|
||||
|
||||
MOCHA_PARALLEL=true $MOCHA_SETUP
|
||||
|
||||
$MOCHA_CMD 'tests/advanced/multiFeature/*.ts' \
|
||||
'tests/advanced/coupons/*.ts' \
|
||||
'tests/attach/updateQuantity/*.ts' \
|
||||
'tests/advanced/referrals/*.ts'
|
||||
|
||||
# $MOCHA_CMD 'tests/attach/multiProduct/*.ts'
|
||||
|
||||
# $MOCHA_CMD 'tests/advanced/usage/*.ts'
|
||||
|
||||
4
server/shell/g5.sh
Executable file
4
server/shell/g5.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
MOCHA_PARALLEL=true npx mocha 'tests/alex/00_setup.ts' && npx mocha --parallel --timeout 10000000 \
|
||||
'tests/alex/01_free.ts' 'tests/alex/02_pro.ts' 'tests/alex/03_premium.ts' \
|
||||
'tests/alex/04_topups.ts' 'tests/alex/05_cancel.ts' 'tests/alex/06_switch.ts' \
|
||||
--ignore 'tests/alex/00_setup.ts'
|
||||
15
server/src/external/autumn/autumnCli.ts
vendored
15
server/src/external/autumn/autumnCli.ts
vendored
@@ -7,11 +7,13 @@ import {
|
||||
CusExpand,
|
||||
EntityExpand,
|
||||
ErrCode,
|
||||
Invoice,
|
||||
} from "@autumn/shared";
|
||||
import {
|
||||
CancelParams,
|
||||
CheckParams,
|
||||
CheckResult,
|
||||
Customer,
|
||||
TrackParams,
|
||||
UsageParams,
|
||||
} from "autumn-js";
|
||||
@@ -214,7 +216,11 @@ export class AutumnInt {
|
||||
params?: {
|
||||
expand?: CusExpand[];
|
||||
},
|
||||
) => {
|
||||
): Promise<
|
||||
Customer & {
|
||||
invoices: any[];
|
||||
}
|
||||
> => {
|
||||
const queryParams = new URLSearchParams();
|
||||
const defaultParams = {
|
||||
expand: [CusExpand.Invoices],
|
||||
@@ -335,6 +341,11 @@ export class AutumnInt {
|
||||
const data = await this.post(`/rewards`, reward);
|
||||
return data;
|
||||
},
|
||||
|
||||
delete: async (rewardId: string) => {
|
||||
const data = await this.delete(`/rewards/${rewardId}`);
|
||||
return data;
|
||||
},
|
||||
};
|
||||
|
||||
rewardPrograms = {
|
||||
@@ -422,7 +433,7 @@ export class AutumnInt {
|
||||
return data;
|
||||
};
|
||||
|
||||
cancel = async (params: CancelParams) => {
|
||||
cancel = async (params: CancelParams & { expire_immediately?: boolean }) => {
|
||||
const data = await this.post(`/cancel`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -60,6 +60,8 @@ export const priceToUsageInAdvance = ({
|
||||
let optionsQuantity = options?.quantity;
|
||||
let finalQuantity = optionsQuantity;
|
||||
|
||||
console.log("options", options);
|
||||
|
||||
// 1. If adjustable quantity is set, use that, else if quantity is undefined, adjustable is true, else false
|
||||
let adjustable = !nullish(options?.adjustable_quantity)
|
||||
? options!.adjustable_quantity
|
||||
|
||||
16
server/src/external/stripe/stripeProductUtils.ts
vendored
16
server/src/external/stripe/stripeProductUtils.ts
vendored
@@ -7,7 +7,7 @@ import { StatusCodes } from "http-status-codes";
|
||||
export const createStripeProduct = async (
|
||||
org: Organization,
|
||||
env: AppEnv,
|
||||
product: Product
|
||||
product: Product,
|
||||
) => {
|
||||
try {
|
||||
const stripe = createStripeCli({ org, env });
|
||||
@@ -33,7 +33,7 @@ export const createStripeProduct = async (
|
||||
export const deleteStripeProduct = async (
|
||||
org: Organization,
|
||||
env: AppEnv,
|
||||
product: Product
|
||||
product: Product,
|
||||
) => {
|
||||
const stripe = createStripeCli({ org, env });
|
||||
|
||||
@@ -85,14 +85,14 @@ export const deactivateStripeMeters = async ({
|
||||
}
|
||||
}
|
||||
|
||||
const batchSize = 10;
|
||||
const batchSize = 40;
|
||||
for (let i = 0; i < allStripeMeters.length; i += batchSize) {
|
||||
const batch = allStripeMeters.slice(i, i + batchSize);
|
||||
await Promise.all(
|
||||
batch.map((meter) => stripeCli.billing.meters.deactivate(meter.id))
|
||||
batch.map((meter) => stripeCli.billing.meters.deactivate(meter.id)),
|
||||
);
|
||||
console.log(
|
||||
`Deactivated ${i + batch.length}/${allStripeMeters.length} meters`
|
||||
`Deactivated ${i + batch.length}/${allStripeMeters.length} meters`,
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
@@ -125,7 +125,7 @@ export const deleteAllStripeProducts = async ({
|
||||
});
|
||||
}
|
||||
|
||||
let batchSize = 10;
|
||||
let batchSize = 50;
|
||||
for (let i = 0; i < stripeProducts.data.length; i += batchSize) {
|
||||
let batch = stripeProducts.data.slice(i, i + batchSize);
|
||||
await Promise.all(
|
||||
@@ -137,10 +137,10 @@ export const deleteAllStripeProducts = async ({
|
||||
active: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
console.log(
|
||||
`Deleted ${i + batch.length}/${stripeProducts.data.length} products`
|
||||
`Deleted ${i + batch.length}/${stripeProducts.data.length} products`,
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ export const handleCusProductDeleted = async ({
|
||||
prematurelyCanceled: boolean;
|
||||
}) => {
|
||||
const { org, env } = req;
|
||||
// const customerId = cusProduct.customer!.id;
|
||||
// const orgId = org.id;
|
||||
// const lockKey = `attach_${customerId}_${orgId}_${env}`;
|
||||
|
||||
const { scheduled_ids } = cusProduct;
|
||||
|
||||
const customer = await CusService.getFull({
|
||||
@@ -84,6 +88,7 @@ export const handleCusProductDeleted = async ({
|
||||
}
|
||||
|
||||
if (cusProduct.status === CusProductStatus.Expired) {
|
||||
// When attaching eg. main is trial, canceled in attach function, don't handle...
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import { AttachScenario } from "@autumn/shared";
|
||||
|
||||
import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js";
|
||||
|
||||
import { CusProductStatus, FullCusProduct } from "@autumn/shared";
|
||||
import Stripe from "stripe";
|
||||
import { AttachScenario } from "@autumn/shared";
|
||||
import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js";
|
||||
import { CusProductStatus, FullCusProduct } from "@autumn/shared";
|
||||
import { formatUnixToDateTime, nullish } from "@/utils/genUtils.js";
|
||||
import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
import { ProductService } from "@/internal/products/ProductService.js";
|
||||
import { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import {
|
||||
webhookToAttachParams,
|
||||
webhookToInsertParams,
|
||||
} from "../../webhookUtils/webhookUtils.js";
|
||||
import { attachToInsertParams } from "@/internal/products/productUtils.js";
|
||||
import { productToInsertParams } from "@/internal/customers/attach/attachUtils/attachParams/convertToParams.js";
|
||||
|
||||
export const handleSubCanceled = async ({
|
||||
req,
|
||||
@@ -36,7 +30,7 @@ export const handleSubCanceled = async ({
|
||||
|
||||
const canceledFromPortal = isCanceled && !isAutumnDowngrade;
|
||||
|
||||
const { db, org, env, features, logtail: logger } = req;
|
||||
const { db, org, env, logtail: logger } = req;
|
||||
|
||||
if (!canceledFromPortal || updatedCusProducts.length == 0) {
|
||||
return;
|
||||
@@ -86,10 +80,10 @@ export const handleSubCanceled = async ({
|
||||
continue;
|
||||
}
|
||||
|
||||
let insertParams = webhookToInsertParams({
|
||||
let insertParams = productToInsertParams({
|
||||
req,
|
||||
cusProduct: updatedCusProducts[0],
|
||||
fullCus,
|
||||
newProduct: product,
|
||||
entities,
|
||||
});
|
||||
|
||||
|
||||
@@ -48,35 +48,3 @@ export const webhookToAttachParams = ({
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
export const webhookToInsertParams = ({
|
||||
req,
|
||||
cusProduct,
|
||||
fullCus,
|
||||
entities,
|
||||
}: {
|
||||
req: ExtendedRequest;
|
||||
cusProduct: FullCusProduct;
|
||||
fullCus: FullCustomer;
|
||||
entities?: Entity[];
|
||||
}): InsertCusProductParams => {
|
||||
const fullProduct = cusProductToProduct({ cusProduct });
|
||||
|
||||
const params: InsertCusProductParams = {
|
||||
customer: fullCus,
|
||||
org: req.org,
|
||||
product: fullProduct,
|
||||
prices: cusProductToPrices({ cusProduct }),
|
||||
entitlements: cusProductToEnts({ cusProduct }),
|
||||
features: req.features,
|
||||
freeTrial: cusProduct.free_trial || null,
|
||||
optionsList: cusProduct.options,
|
||||
cusProducts: [cusProduct],
|
||||
|
||||
internalEntityId: cusProduct.internal_entity_id || undefined,
|
||||
entities: entities || [],
|
||||
replaceables: [],
|
||||
};
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ export class EventService {
|
||||
event_name: events.event_name,
|
||||
value: events.value,
|
||||
created_at: events.created_at,
|
||||
// timestamp: events.timestamp,
|
||||
timestamp: events.timestamp,
|
||||
idempotency_key: events.idempotency_key,
|
||||
properties: events.properties,
|
||||
set_usage: events.set_usage,
|
||||
|
||||
@@ -44,12 +44,16 @@ productApiRouter.post("/all/init_stripe", async (req: any, res) => {
|
||||
OrgService.getFromReq(req),
|
||||
]);
|
||||
|
||||
console.log(
|
||||
"fullProducts",
|
||||
fullProducts.map((p) => p.id),
|
||||
);
|
||||
|
||||
const stripeCli = createStripeCli({
|
||||
org,
|
||||
env,
|
||||
});
|
||||
|
||||
const batchProductInit: Promise<any>[] = [];
|
||||
const productBatchSize = 5;
|
||||
for (let i = 0; i < fullProducts.length; i += productBatchSize) {
|
||||
const batch = fullProducts.slice(i, i + productBatchSize);
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
|
||||
import { pricesContainRecurring } from "@/internal/products/prices/priceUtils.js";
|
||||
|
||||
import { createCheckoutMetadata } from "@/internal/metadata/metadataUtils.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
import {
|
||||
AttachParams,
|
||||
AttachResultSchema,
|
||||
} from "../cusProducts/AttachParams.js";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { pricesContainRecurring } from "@/internal/products/prices/priceUtils.js";
|
||||
import { createCheckoutMetadata } from "@/internal/metadata/metadataUtils.js";
|
||||
import { freeTrialToStripeTimestamp } from "@/internal/products/free-trials/freeTrialUtils.js";
|
||||
|
||||
import { getStripeSubItems } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js";
|
||||
import { ErrCode } from "@/errors/errCodes.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
import { getNextStartOfMonthUnix } from "@/internal/products/prices/billingIntervalUtils.js";
|
||||
import { APIVersion } from "@autumn/shared";
|
||||
import { SuccessCode } from "@autumn/shared";
|
||||
@@ -49,6 +46,7 @@ export const handleCreateCheckout = async ({
|
||||
|
||||
// Handle first item set
|
||||
const { items } = itemSets[0];
|
||||
|
||||
attachParams.itemSets = itemSets;
|
||||
|
||||
const isRecurring = pricesContainRecurring(attachParams.prices);
|
||||
|
||||
@@ -29,6 +29,7 @@ export const handleAddProduct = async ({
|
||||
const defaultConfig: AttachConfig = getDefaultAttachConfig();
|
||||
|
||||
// 1. If paid product
|
||||
|
||||
if (prices.length > 0) {
|
||||
await handlePaidProduct({
|
||||
req,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Stripe from "stripe";
|
||||
import { getStripeSubItems } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js";
|
||||
import { cancelCurSubs } from "@/internal/customers/change-product/handleDowngrade/cancelCurSubs.js";
|
||||
import { updateScheduledSubWithNewItems } from "@/internal/customers/change-product/scheduleUtils/updateScheduleWithNewItems.js";
|
||||
@@ -13,9 +14,7 @@ import {
|
||||
isFreeProduct,
|
||||
} from "@/internal/products/productUtils.js";
|
||||
import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js";
|
||||
|
||||
import { cusProductsToSchedules } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js";
|
||||
import Stripe from "stripe";
|
||||
import { attachParamToCusProducts } from "../../attachUtils/convertAttachParams.js";
|
||||
import { getStripeSubs } from "@/external/stripe/stripeSubUtils.js";
|
||||
|
||||
|
||||
@@ -71,10 +71,12 @@ export const scheduleStripeSub = async ({
|
||||
export const updateOtherCusProdsWithNewSchedule = async ({
|
||||
db,
|
||||
attachParams,
|
||||
newSchedule,
|
||||
otherSub,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
attachParams: AttachParams;
|
||||
newSchedule: Stripe.SubscriptionSchedule;
|
||||
otherSub: Stripe.Subscription;
|
||||
}) => {
|
||||
const otherCusProducts = getCusProductsWithStripeSubId({
|
||||
@@ -86,7 +88,7 @@ export const updateOtherCusProdsWithNewSchedule = async ({
|
||||
for (const otherCusProduct of otherCusProducts) {
|
||||
let newScheduledIds = [
|
||||
...(otherCusProduct.scheduled_ids || []),
|
||||
otherSub.id,
|
||||
newSchedule.id,
|
||||
];
|
||||
|
||||
await CusProductService.update({
|
||||
@@ -138,6 +140,7 @@ export const handleNewScheduleForItemSet = async ({
|
||||
await updateOtherCusProdsWithNewSchedule({
|
||||
db,
|
||||
attachParams,
|
||||
newSchedule: stripeSchedule,
|
||||
otherSub,
|
||||
});
|
||||
|
||||
|
||||
@@ -198,10 +198,7 @@ export const createStripePrices = async ({
|
||||
req: any;
|
||||
logger: any;
|
||||
}) => {
|
||||
const { prices, entitlements, products, org, internalEntityId } =
|
||||
attachParams;
|
||||
|
||||
const stripeCli = createStripeCli({ org, env: attachParams.customer.env });
|
||||
const { prices, entitlements, products, org, stripeCli } = attachParams;
|
||||
|
||||
const batchPriceUpdates = [];
|
||||
for (const price of prices) {
|
||||
|
||||
@@ -56,31 +56,28 @@ export const webhookToAttachParams = ({
|
||||
return params;
|
||||
};
|
||||
|
||||
export const webhookToInsertParams = ({
|
||||
export const productToInsertParams = ({
|
||||
req,
|
||||
cusProduct,
|
||||
fullCus,
|
||||
newProduct,
|
||||
entities,
|
||||
}: {
|
||||
req: ExtendedRequest;
|
||||
cusProduct: FullCusProduct;
|
||||
fullCus: FullCustomer;
|
||||
newProduct: FullProduct;
|
||||
entities?: Entity[];
|
||||
}): InsertCusProductParams => {
|
||||
const fullProduct = cusProductToProduct({ cusProduct });
|
||||
|
||||
const params: InsertCusProductParams = {
|
||||
customer: fullCus,
|
||||
org: req.org,
|
||||
product: fullProduct,
|
||||
prices: cusProductToPrices({ cusProduct }),
|
||||
entitlements: cusProductToEnts({ cusProduct }),
|
||||
product: newProduct,
|
||||
prices: newProduct.prices,
|
||||
entitlements: newProduct.entitlements,
|
||||
features: req.features,
|
||||
freeTrial: cusProduct.free_trial || null,
|
||||
optionsList: cusProduct.options,
|
||||
cusProducts: [cusProduct],
|
||||
|
||||
internalEntityId: cusProduct.internal_entity_id || undefined,
|
||||
cusProducts: fullCus.customer_products,
|
||||
freeTrial: null,
|
||||
optionsList: [],
|
||||
internalEntityId: undefined,
|
||||
entities: entities || [],
|
||||
replaceables: [],
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ import { getEntsWithFeature } from "@/internal/products/entitlements/entitlement
|
||||
import { isMainProduct } from "@/internal/products/productUtils/classifyProduct.js";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { getStripeCusData } from "./attachParamsUtils/getStripeCusData.js";
|
||||
import { isOneOff } from "@/internal/products/productUtils.js";
|
||||
|
||||
const getProductsForAttach = async ({
|
||||
req,
|
||||
@@ -60,7 +61,8 @@ const getProductsForAttach = async ({
|
||||
let otherProd = products.find(
|
||||
(p) => p.group === prod.group && !p.is_add_on && p.id !== prod.id,
|
||||
);
|
||||
if (otherProd && !otherProd.is_add_on) {
|
||||
|
||||
if (otherProd && !otherProd.is_add_on && !isOneOff(prod.prices)) {
|
||||
throw new RecaseError({
|
||||
message:
|
||||
"Can't attach multiple products from the same group that are not add-ons",
|
||||
@@ -145,8 +147,7 @@ const getPricesAndEnts = async ({
|
||||
optionsInput,
|
||||
features,
|
||||
prices,
|
||||
// to check if it fails for multi prod attach...
|
||||
curCusProduct: prodIsMain ? curMainProduct : curSameProduct,
|
||||
curCusProduct: curMainProduct,
|
||||
}),
|
||||
prices,
|
||||
entitlements,
|
||||
@@ -205,7 +206,7 @@ const getPricesAndEnts = async ({
|
||||
optionsInput,
|
||||
features,
|
||||
prices,
|
||||
curCusProduct: prodIsMain ? curMainProduct : curSameProduct,
|
||||
curCusProduct: curMainProduct,
|
||||
}),
|
||||
prices,
|
||||
entitlements: getEntsWithFeature({
|
||||
|
||||
@@ -229,21 +229,34 @@ const getChangeProductBranch = async ({
|
||||
return AttachBranch.MainIsFree;
|
||||
}
|
||||
|
||||
if (isTrialing(curMainProduct!)) {
|
||||
if (isFreeProduct(attachParams.prices)) {
|
||||
return AttachBranch.Downgrade;
|
||||
}
|
||||
|
||||
return AttachBranch.MainIsTrial;
|
||||
}
|
||||
|
||||
// 2. If main product is paid, check if upgrade or downgrade
|
||||
// Check if upgrade or downgrade
|
||||
let curPrices = cusProductToPrices({ cusProduct: curMainProduct! });
|
||||
let newPrices = attachParams.prices;
|
||||
|
||||
// if (isTrialing(curMainProduct!)) {
|
||||
// if (isFreeProduct(attachParams.prices)) {
|
||||
// return AttachBranch.Downgrade;
|
||||
// }
|
||||
|
||||
// let isUpgrade = isProductUpgrade({
|
||||
// prices1: curPrices,
|
||||
// prices2: newPrices,
|
||||
// });
|
||||
|
||||
// if (!isUpgrade) {
|
||||
// return AttachBranch.Downgrade;
|
||||
// }
|
||||
|
||||
// return AttachBranch.MainIsTrial;
|
||||
// }
|
||||
|
||||
let isUpgrade = isProductUpgrade({ prices1: curPrices, prices2: newPrices });
|
||||
if (isUpgrade) {
|
||||
if (isTrialing(curMainProduct!)) {
|
||||
return AttachBranch.MainIsTrial;
|
||||
}
|
||||
|
||||
return AttachBranch.Upgrade;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ProrationBehavior } from "@autumn/shared";
|
||||
import { attachParamsToProduct } from "./convertAttachParams.js";
|
||||
import { attachParamToCusProducts } from "./convertAttachParams.js";
|
||||
import { cusProductToPrices } from "../../cusProducts/cusProductUtils/convertCusProduct.js";
|
||||
import { insertInvoiceFromAttach } from "@/internal/invoices/invoiceUtils.js";
|
||||
|
||||
export const intervalsAreSame = ({
|
||||
attachParams,
|
||||
@@ -80,15 +81,21 @@ export const getAttachConfig = async ({
|
||||
|
||||
let sameIntervals = intervalsAreSame({ attachParams });
|
||||
|
||||
let disableMerge =
|
||||
branch == AttachBranch.MainIsTrial ||
|
||||
org.config.merge_billing_cycles === false;
|
||||
|
||||
const onlyCheckout =
|
||||
isPublic || forceCheckout || (noPaymentMethod && !invoiceOnly && !isFree);
|
||||
|
||||
let config: AttachConfig = {
|
||||
branch,
|
||||
onlyCheckout:
|
||||
(isPublic || forceCheckout || noPaymentMethod) && !invoiceOnly && !isFree,
|
||||
onlyCheckout,
|
||||
carryUsage,
|
||||
proration,
|
||||
disableTrial,
|
||||
invoiceOnly: flags.invoiceOnly,
|
||||
disableMerge: org.config.merge_billing_cycles === false,
|
||||
disableMerge,
|
||||
sameIntervals,
|
||||
carryTrial,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
AttachParams,
|
||||
AttachResultSchema,
|
||||
} from "../../cusProducts/AttachParams.js";
|
||||
import { AttachBranch, AttachFunction } from "@autumn/shared";
|
||||
import { AttachBranch, AttachFunction, CusProductStatus } from "@autumn/shared";
|
||||
import { handleUpgradeDiffInterval } from "../attachFunctions/upgradeDiffIntFlow/handleUpgradeDiffInt.js";
|
||||
import { handleCreateCheckout } from "../../add-product/handleCreateCheckout.js";
|
||||
import { handleAddProduct } from "../attachFunctions/addProductFlow/handleAddProduct.js";
|
||||
@@ -16,6 +16,7 @@ import { attachParamToCusProducts } from "./convertAttachParams.js";
|
||||
import { deleteCurrentScheduledProduct } from "./deleteCurrentScheduledProduct.js";
|
||||
import { handleOneOffFunction } from "../attachFunctions/addProductFlow/handleOneOffFunction.js";
|
||||
import { handleUpgradeSameInterval } from "../attachFunctions/upgradeSameIntFlow/handleUpgradeSameInt.js";
|
||||
import { CusProductService } from "../../cusProducts/CusProductService.js";
|
||||
|
||||
/*
|
||||
1. If from new version, free trial should just carry over
|
||||
@@ -103,7 +104,7 @@ export const runAttachFunction = async ({
|
||||
attachBody: AttachBody;
|
||||
config: AttachConfig;
|
||||
}) => {
|
||||
const { logtail: logger } = req;
|
||||
const { logtail: logger, db } = req;
|
||||
const { stripeCli } = attachParams;
|
||||
|
||||
const attachFunction = await getAttachFunction({
|
||||
@@ -160,8 +161,20 @@ export const runAttachFunction = async ({
|
||||
|
||||
// 2. If main is trial, cancel it...
|
||||
if (branch == AttachBranch.MainIsTrial) {
|
||||
await CusProductService.update({
|
||||
db,
|
||||
cusProductId: curMainProduct!.id,
|
||||
updates: {
|
||||
status: CusProductStatus.Expired,
|
||||
},
|
||||
});
|
||||
|
||||
for (const subId of curMainProduct?.subscription_ids || []) {
|
||||
await stripeCli.subscriptions.cancel(subId);
|
||||
await stripeCli.subscriptions.cancel(subId, {
|
||||
cancellation_details: {
|
||||
comment: "autumn_downgrade",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@ import RecaseError from "@/utils/errorUtils.js";
|
||||
import { ErrCode } from "@/errors/errCodes.js";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { AttachParams } from "../../cusProducts/AttachParams.js";
|
||||
import {
|
||||
AttachBranch,
|
||||
AttachErrCode,
|
||||
entitlements,
|
||||
UsagePriceConfig,
|
||||
} from "@autumn/shared";
|
||||
import { AttachBranch, AttachErrCode, UsagePriceConfig } from "@autumn/shared";
|
||||
import { AttachBody } from "../models/AttachBody.js";
|
||||
import { AttachConfig, AttachFlags } from "../models/AttachFlags.js";
|
||||
import {
|
||||
@@ -233,7 +228,7 @@ export const handleAttachErrors = async ({
|
||||
useCheckout: onlyCheckout,
|
||||
});
|
||||
|
||||
await handleUpdateQuantityErrors({
|
||||
attachParams,
|
||||
});
|
||||
// await handleUpdateQuantityErrors({
|
||||
// attachParams,
|
||||
// });
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
AttachBranch,
|
||||
BillingInterval,
|
||||
FreeTrial,
|
||||
PreviewLineItem,
|
||||
Price,
|
||||
UsageModel,
|
||||
} from "@autumn/shared";
|
||||
@@ -179,12 +180,23 @@ export const getUpgradeProductPreview = async ({
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
currency: attachParams.org.default_currency,
|
||||
due_today: {
|
||||
let dueToday:
|
||||
| {
|
||||
line_items: PreviewLineItem[];
|
||||
total: number;
|
||||
}
|
||||
| undefined = {
|
||||
line_items: items,
|
||||
total: dueTodayAmt,
|
||||
},
|
||||
};
|
||||
|
||||
if (branch == AttachBranch.SameCustomEnts) {
|
||||
dueToday = undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
currency: attachParams.org.default_currency,
|
||||
due_today: dueToday,
|
||||
due_next_cycle: {
|
||||
line_items: nextCycleItems,
|
||||
due_at: nextCycleAt.next_cycle_at,
|
||||
|
||||
@@ -3,14 +3,11 @@ import {
|
||||
AppEnv,
|
||||
AttachScenario,
|
||||
CusProductResponseSchema,
|
||||
CusProductSchema,
|
||||
CusProductStatus,
|
||||
Customer,
|
||||
Entity,
|
||||
FixedPriceConfig,
|
||||
FullCusProduct,
|
||||
FullCustomerEntitlement,
|
||||
FullCustomerPrice,
|
||||
Organization,
|
||||
PriceType,
|
||||
Subscription,
|
||||
@@ -31,7 +28,6 @@ import {
|
||||
getStripeSubs,
|
||||
subIsPrematurelyCanceled,
|
||||
} from "@/external/stripe/stripeSubUtils.js";
|
||||
import { sortCusEntsForDeduction } from "./cusEnts/cusEntUtils.js";
|
||||
import { getRelatedCusEnt } from "./cusPrices/cusPriceUtils.js";
|
||||
import { notNullish } from "@/utils/genUtils.js";
|
||||
import { BREAK_API_VERSION } from "@/utils/constants.js";
|
||||
@@ -39,7 +35,6 @@ import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/han
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { getExistingCusProducts } from "./cusProductUtils/getExistingCusProducts.js";
|
||||
import { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { webhookToInsertParams } from "@/external/stripe/webhookUtils/webhookUtils.js";
|
||||
|
||||
export const isActiveStatus = (status: CusProductStatus) => {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { CusProductStatus, FullCusProduct } from "@autumn/shared";
|
||||
import { CusProductService } from "../CusProductService.js";
|
||||
|
||||
export const findCusProductById = async ({
|
||||
db,
|
||||
internalCustomerId,
|
||||
productId,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
internalCustomerId: string;
|
||||
productId: string;
|
||||
}) => {
|
||||
let cusProducts = await CusProductService.list({
|
||||
db,
|
||||
internalCustomerId,
|
||||
});
|
||||
|
||||
return cusProducts.find(
|
||||
(cusProduct: FullCusProduct) => cusProduct.product.id === productId,
|
||||
);
|
||||
};
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
ErrCode,
|
||||
BillingInterval,
|
||||
AttachScenario,
|
||||
AttachBranch,
|
||||
} from "@autumn/shared";
|
||||
import { AppEnv, Customer } from "@autumn/shared";
|
||||
import { createFullCusProduct } from "../add-product/createFullCusProduct.js";
|
||||
@@ -21,8 +20,7 @@ import { handleAddProduct } from "../attach/attachFunctions/addProductFlow/handl
|
||||
import { CusService } from "../CusService.js";
|
||||
import { initStripeCusAndProducts } from "../handlers/handleCreateCustomer.js";
|
||||
import { generateId } from "@/utils/genUtils.js";
|
||||
import { newCusToFullCus } from "./cusUtils.js";
|
||||
import { webhookToInsertParams } from "@/external/stripe/webhookUtils/webhookUtils.js";
|
||||
|
||||
import {
|
||||
newCusToAttachParams,
|
||||
newCusToInsertParams,
|
||||
|
||||
@@ -323,7 +323,7 @@ export class ProductService {
|
||||
internalId: string;
|
||||
update: any;
|
||||
}) {
|
||||
const data = await db
|
||||
await db
|
||||
.update(products)
|
||||
.set(update)
|
||||
.where(eq(products.internal_id, internalId));
|
||||
|
||||
@@ -227,8 +227,6 @@ export const handleNewProductItems = async ({
|
||||
features,
|
||||
});
|
||||
|
||||
console.log("Updated price", updatedPrice);
|
||||
|
||||
if (newPrice) {
|
||||
newPrices.push(newPrice);
|
||||
}
|
||||
|
||||
@@ -220,7 +220,6 @@ export const toFeatureAndPrice = ({
|
||||
on_increase: onIncrease,
|
||||
on_decrease: onDecrease,
|
||||
};
|
||||
console.log("Proration config", prorationConfig);
|
||||
}
|
||||
|
||||
let price: Price = {
|
||||
|
||||
@@ -239,6 +239,7 @@ export const checkStripeProductExists = async ({
|
||||
let stripeProduct = await stripeCli.products.retrieve(
|
||||
product.processor!.id,
|
||||
);
|
||||
|
||||
if (!stripeProduct.active) {
|
||||
createNew = true;
|
||||
}
|
||||
@@ -261,6 +262,10 @@ export const checkStripeProductExists = async ({
|
||||
},
|
||||
});
|
||||
|
||||
console.log(
|
||||
`Updated product ${product.name} with stripe product ${stripeProduct.id}`,
|
||||
);
|
||||
|
||||
product.processor = {
|
||||
id: stripeProduct.id,
|
||||
type: ProcessorType.Stripe,
|
||||
|
||||
@@ -61,13 +61,25 @@ export const initFeature = ({
|
||||
}
|
||||
};
|
||||
|
||||
// enum TestItemType {
|
||||
// Prepaid = "prepaid",
|
||||
// Arrear = "arrear",
|
||||
// ArrearProrated = "arrear_prorated",
|
||||
// FixedPrice = "fixed_price",
|
||||
// }
|
||||
|
||||
export const constructRawProduct = ({
|
||||
id,
|
||||
isAddOn = false,
|
||||
items,
|
||||
}: {
|
||||
id: string;
|
||||
isAddOn?: boolean;
|
||||
items: ProductItem[];
|
||||
}) => {
|
||||
return {
|
||||
id,
|
||||
name: keyToTitle(id),
|
||||
items,
|
||||
is_add_on: isAddOn,
|
||||
is_default: false,
|
||||
version: 1,
|
||||
group: "",
|
||||
};
|
||||
};
|
||||
export const constructProduct = ({
|
||||
id,
|
||||
items,
|
||||
|
||||
@@ -90,12 +90,13 @@ export const initCustomer = async ({
|
||||
})) as Customer;
|
||||
|
||||
const stripeCli = createStripeCli({ org: org, env: env });
|
||||
// if (withTestClock) {
|
||||
let testClockId = "";
|
||||
if (withTestClock) {
|
||||
const testClock = await stripeCli.testHelpers.testClocks.create({
|
||||
frozen_time: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
testClockId = testClock.id;
|
||||
// }
|
||||
}
|
||||
|
||||
if (attachPm) {
|
||||
await attachPmToCus({
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
format,
|
||||
} from "date-fns";
|
||||
import { Stripe } from "stripe";
|
||||
import { timeout } from "../genUtils.js";
|
||||
|
||||
export const getStripeNow = async ({
|
||||
stripeCli,
|
||||
@@ -57,6 +58,7 @@ export const advanceTestClock = async ({
|
||||
numberOfHours,
|
||||
numberOfMonths,
|
||||
advanceTo,
|
||||
waitForSeconds,
|
||||
}: {
|
||||
stripeCli: Stripe;
|
||||
testClockId: string;
|
||||
@@ -66,6 +68,7 @@ export const advanceTestClock = async ({
|
||||
numberOfHours?: number;
|
||||
numberOfMonths?: number;
|
||||
advanceTo?: number;
|
||||
waitForSeconds?: number;
|
||||
}) => {
|
||||
if (!startingFrom) {
|
||||
startingFrom = new Date();
|
||||
@@ -96,6 +99,10 @@ export const advanceTestClock = async ({
|
||||
frozen_time: Math.floor(advanceTo / 1000),
|
||||
});
|
||||
|
||||
if (waitForSeconds) {
|
||||
await timeout(waitForSeconds * 1000);
|
||||
}
|
||||
|
||||
return advanceTo;
|
||||
|
||||
// await timeout(
|
||||
|
||||
@@ -2,25 +2,53 @@
|
||||
MOCHA_SETUP="npx mocha tests/00_setup.ts"
|
||||
MOCHA_CMD="npx mocha --parallel --timeout 10000000 --ignore tests/00_setup.ts"
|
||||
|
||||
# TEST PARALLEL
|
||||
if [ "$1" == "basic-parallel" ]; then
|
||||
MOCHA_PARALLEL=true $MOCHA_SETUP && $MOCHA_CMD \
|
||||
tests/basic/*.ts \
|
||||
tests/basic/multi-feature/*.ts \
|
||||
tests/basic/entities/*.ts \
|
||||
# && $MOCHA_CMD \
|
||||
# 'tests/basic/referrals/*.ts' 'tests/attach/**/*.ts' \
|
||||
# 'tests/basic/referrals/*.ts' 'tests/attach/**/*.ts' \
|
||||
|
||||
elif [ "$1" == "advanced-parallel" ]; then
|
||||
MOCHA_PARALLEL=true \
|
||||
# Group 1
|
||||
if [ "$1" == "group1" ]; then
|
||||
$MOCHA_SETUP \
|
||||
&& $MOCHA_CMD 'tests/advanced/usage/*.ts' \
|
||||
&& $MOCHA_CMD 'tests/advanced/arrear_prorated/*.ts' 'tests/advanced/coupons/*.ts'\
|
||||
# && $MOCHA_CMD 'tests/advanced/coupons/*.ts'\
|
||||
&& $MOCHA_CMD \
|
||||
'tests/attach/basic/*.ts' \
|
||||
'tests/attach/upgrade/*.ts' \
|
||||
'tests/attach/downgrade/*.ts'
|
||||
fi
|
||||
|
||||
# Group 2
|
||||
if [ "$1" == "g2" ]; then
|
||||
$MOCHA_SETUP && $MOCHA_CMD \
|
||||
'tests/attach/upgradeOld/*.ts' \
|
||||
'tests/attach/entities/*.ts' \
|
||||
'tests/attach/migrations/*.ts' \
|
||||
'tests/attach/newVersion/*.ts' \
|
||||
'tests/attach/others/*.ts' \
|
||||
'tests/attach/updateEnts/*.ts' \
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$1" == "g3" ]; then
|
||||
$MOCHA_SETUP \
|
||||
&& $MOCHA_CMD 'tests/contUse/entities/*.ts' \
|
||||
&& $MOCHA_CMD 'tests/contUse/update/*.ts'\
|
||||
&& $MOCHA_CMD 'tests/contUse/track/*.ts' \
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$1" == "g4" ]; then
|
||||
$MOCHA_SETUP && $MOCHA_CMD \
|
||||
'tests/attach/updateQuantity/*.ts' \
|
||||
'tests/attach/multiProduct/*.ts' \
|
||||
'tests/advanced/multiFeature/*.ts' \
|
||||
'tests/advanced/referrals/*.ts' \
|
||||
'tests/advanced/coupons/*.ts'
|
||||
fi
|
||||
|
||||
# Group 4
|
||||
if [ "$1" == "g4" ]; then
|
||||
$MOCHA_SETUP && $MOCHA_CMD \
|
||||
'tests/advanced/usage/*.ts'
|
||||
fi
|
||||
|
||||
|
||||
elif [ "$1" == "alex-parallel" ]; then
|
||||
|
||||
if [ "$1" == "alex-parallel" ]; then
|
||||
MOCHA_PARALLEL=true npx mocha 'tests/alex/00_setup.ts' && npx mocha --parallel --timeout 10000000 \
|
||||
'tests/alex/01_free.ts' 'tests/alex/02_pro.ts' 'tests/alex/03_premium.ts' \
|
||||
'tests/alex/04_topups.ts' 'tests/alex/05_cancel.ts' 'tests/alex/06_switch.ts' \
|
||||
@@ -54,3 +82,39 @@ fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# # TEST PARALLEL
|
||||
# if [ "$1" == "basic-parallel" ]; then
|
||||
# MOCHA_PARALLEL=true $MOCHA_SETUP \
|
||||
# && $MOCHA_CMD \
|
||||
# 'tests/attach/basic/*.ts' \
|
||||
# 'tests/attach/upgrade/*.ts' \
|
||||
# 'tests/attach/downgrade/*.ts' \
|
||||
# && $MOCHA_CMD \
|
||||
# 'tests/attach/upgradeOld/*.ts' \
|
||||
# 'tests/attach/entities/*.ts' \
|
||||
# 'tests/attach/migrations/*.ts' \
|
||||
# 'tests/attach/multiProduct/*.ts' \
|
||||
# 'tests/attach/newVersion/*.ts' \
|
||||
# 'tests/attach/others/*.ts' \
|
||||
# 'tests/attach/updateEnts/*.ts' \
|
||||
# 'tests/attach/updateQuantity/*.ts' \
|
||||
# 'tests/contUse/entities/*.ts' \
|
||||
# 'tests/contUse/track/*.ts' \
|
||||
# 'tests/contUse/update/*.ts' \
|
||||
# && $MOCHA_CMD \
|
||||
# 'tests/advanced/multiFeature/*.ts' \
|
||||
# 'tests/advanced/referrals/*.ts' \
|
||||
# 'tests/advanced/coupons/*.ts' \
|
||||
|
||||
|
||||
# elif [ "$1" == "advanced-parallel" ]; then
|
||||
# MOCHA_PARALLEL=true \
|
||||
# $MOCHA_SETUP \
|
||||
# && $MOCHA_CMD 'tests/advanced/arrear_prorated/*.ts' 'tests/advanced/coupons/*.ts'\
|
||||
# # && $MOCHA_CMD 'tests/advanced/usage/*.ts' \
|
||||
# # && $MOCHA_CMD 'tests/advanced/coupons/*.ts'\
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import chalk from "chalk";
|
||||
import Stripe from "stripe";
|
||||
import { Customer } from "@autumn/shared";
|
||||
import { APIVersion, AppEnv, Customer, Organization } from "@autumn/shared";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||
import { getPriceForOverage } from "@/internal/products/prices/priceUtils.js";
|
||||
import { expect } from "chai";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import { features, products, rewards } from "tests/global.js";
|
||||
import { initCustomerWithTestClock } from "tests/utils/testInitUtils.js";
|
||||
import { getFixedPriceAmount, timeout } from "tests/utils/genUtils.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
@@ -18,42 +17,161 @@ import {
|
||||
completeCheckoutForm,
|
||||
getDiscount,
|
||||
} from "tests/utils/stripeUtils.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import {
|
||||
addPrefixToProducts,
|
||||
getBasePrice,
|
||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
|
||||
const testCase = "coupon1";
|
||||
|
||||
const pro = constructProduct({
|
||||
type: "pro",
|
||||
items: [constructArrearItem({ featureId: TestFeature.Words })],
|
||||
});
|
||||
|
||||
const simulateOneCycle = async ({
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
stripeCli,
|
||||
autumn,
|
||||
testClockId,
|
||||
couponAmount,
|
||||
curUnix,
|
||||
}: {
|
||||
customerId: string;
|
||||
db: DrizzleCli;
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
stripeCli: Stripe;
|
||||
autumn: AutumnInt;
|
||||
testClockId: string;
|
||||
couponAmount: number;
|
||||
curUnix: number;
|
||||
}) => {
|
||||
const usage = Math.random() * 100000 + 10000;
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
feature_id: TestFeature.Words,
|
||||
value: usage,
|
||||
});
|
||||
|
||||
// Expected invoice total
|
||||
let expectedTotal = await getExpectedInvoiceTotal({
|
||||
usage: [{ featureId: TestFeature.Words, value: usage }],
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
stripeCli,
|
||||
});
|
||||
|
||||
couponAmount -= expectedTotal;
|
||||
|
||||
curUnix = await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addHours(
|
||||
addMonths(curUnix, 1),
|
||||
hoursToFinalizeInvoice,
|
||||
).getTime(),
|
||||
waitForSeconds: 30,
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expect(customer.invoices![0].total).to.equal(0);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
stripeId: customer.stripe_id!,
|
||||
});
|
||||
|
||||
expect(cusDiscount).to.exist;
|
||||
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(
|
||||
Math.round(couponAmount * 100),
|
||||
`Expected stripe cus to have coupon amount ${couponAmount * 100}`,
|
||||
);
|
||||
|
||||
return {
|
||||
couponAmount,
|
||||
curUnix,
|
||||
};
|
||||
};
|
||||
|
||||
describe(
|
||||
chalk.yellow("coupon1 -- Testing one-off rollover, apply to all"),
|
||||
chalk.yellow(
|
||||
`${testCase} - Testing invoice credits reward, apply to all product`,
|
||||
),
|
||||
() => {
|
||||
let customerId = "coupon1";
|
||||
let stripeCli: Stripe;
|
||||
let customer: Customer;
|
||||
let testClockId: string;
|
||||
let db: DrizzleCli;
|
||||
let org: Organization;
|
||||
let env: AppEnv;
|
||||
|
||||
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
|
||||
|
||||
let couponAmount = rewards.rolloverAll.discount_config.discount_value;
|
||||
let curUnix = new Date().getTime();
|
||||
|
||||
before(async function () {
|
||||
const { testClockId: testClockId1, customer: customer1 } =
|
||||
await initCustomerWithTestClock({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
});
|
||||
testClockId = testClockId1;
|
||||
customer = customer1;
|
||||
await setupBefore(this);
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
stripeCli = createStripeCli({
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
const res = await initCustomer({
|
||||
customerId,
|
||||
org,
|
||||
env,
|
||||
db,
|
||||
autumn: this.autumnJs,
|
||||
});
|
||||
|
||||
addPrefixToProducts({
|
||||
products: [pro],
|
||||
prefix: testCase,
|
||||
});
|
||||
|
||||
await createProducts({
|
||||
products: [pro],
|
||||
orgId: org.id,
|
||||
env,
|
||||
db,
|
||||
autumn,
|
||||
});
|
||||
|
||||
testClockId = res.testClockId;
|
||||
customer = res.customer;
|
||||
});
|
||||
|
||||
// CYCLE 0
|
||||
it("CYCLE 0: should attach pro with overage (through checkout)", async () => {
|
||||
couponAmount -= getFixedPriceAmount(products.proWithOverage);
|
||||
|
||||
const res = await AutumnCli.attach({
|
||||
customerId,
|
||||
productId: products.proWithOverage.id,
|
||||
forceCheckout: true,
|
||||
it("should attach pro", async () => {
|
||||
const res = await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(
|
||||
@@ -62,117 +180,57 @@ describe(
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
|
||||
await timeout(20000);
|
||||
await timeout(10000);
|
||||
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.proWithOverage,
|
||||
cusRes,
|
||||
});
|
||||
});
|
||||
couponAmount -= getBasePrice({ product: pro });
|
||||
|
||||
it("CYCLE 0: should have $0 invoice and correct remaining coupon amount", async () => {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
expect(cusRes.invoices[0].total).to.equal(0);
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expectProductAttached({ customer, product: pro });
|
||||
|
||||
expect(customer.invoices![0].total).to.equal(0);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
customer: cusRes.customer,
|
||||
stripeCli,
|
||||
stripeId: customer.stripe_id!,
|
||||
});
|
||||
|
||||
try {
|
||||
expect(cusDiscount).to.exist;
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
|
||||
// Expect amount to be original amount - pro price
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
||||
} catch (error) {
|
||||
console.error("--------------------------------");
|
||||
console.error(
|
||||
"Expected stripe cus to have coupon",
|
||||
rewards.rolloverAll,
|
||||
);
|
||||
console.error("Actual stripe cus discount", cusDiscount);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
it("should run one cycle and have correct invoice + coupon amount", async () => {
|
||||
const res = await simulateOneCycle({
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
stripeCli,
|
||||
autumn,
|
||||
testClockId,
|
||||
couponAmount,
|
||||
curUnix: new Date().getTime(),
|
||||
});
|
||||
|
||||
couponAmount = res.couponAmount;
|
||||
curUnix = res.curUnix;
|
||||
});
|
||||
|
||||
// CYCLE 1
|
||||
it("CYCLE 1: should set usage to -100 and advance clock by 1 month", async () => {
|
||||
const usage = 100;
|
||||
const res = await AutumnCli.usage({
|
||||
it("should run another cycle and have correct invoice + coupon amount", async () => {
|
||||
const res = await simulateOneCycle({
|
||||
customerId,
|
||||
featureId: features.metered1.id,
|
||||
value: usage,
|
||||
});
|
||||
|
||||
// Price
|
||||
const price = getPriceForOverage(
|
||||
products.proWithOverage.prices[1],
|
||||
-(products.proWithOverage.entitlements.metered1.allowance! - usage),
|
||||
);
|
||||
|
||||
couponAmount =
|
||||
couponAmount - (price + getFixedPriceAmount(products.proWithOverage));
|
||||
|
||||
await advanceClockForInvoice({
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
stripeCli,
|
||||
autumn,
|
||||
testClockId,
|
||||
waitForMeterUpdate: true,
|
||||
couponAmount,
|
||||
curUnix,
|
||||
});
|
||||
});
|
||||
|
||||
it("CYCLE 1: should have $0 invoice and correct new coupon amount", async () => {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
expect(cusRes.invoices[0].total).to.equal(0);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
customer,
|
||||
});
|
||||
|
||||
try {
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
||||
} catch (error) {
|
||||
console.log("--------------------------------");
|
||||
console.log("coupon1, cycle 1 failed");
|
||||
console.log("Expected stripe cus to have coupon", rewards.rolloverAll);
|
||||
console.log("Actual stripe cus discount", cusDiscount);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// CYCLE 2
|
||||
it("CYCLE 2: should have $0 invoice and correct new coupon amount after 2nd cycle", async () => {
|
||||
await timeout(20000);
|
||||
let advanceTo = addHours(addMonths(new Date(), 2), 2);
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: advanceTo.getTime(),
|
||||
});
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
customer,
|
||||
});
|
||||
|
||||
const newCouponAmount =
|
||||
couponAmount - getFixedPriceAmount(products.proWithOverage);
|
||||
|
||||
try {
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(newCouponAmount * 100);
|
||||
} catch (error) {
|
||||
console.log("--------------------------------");
|
||||
console.log("coupon1, cycle 2 failed");
|
||||
console.log("Expected coupon amount", newCouponAmount * 100);
|
||||
console.log("Stripe cus discount", cusDiscount);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,156 +1,206 @@
|
||||
import { createLogtailWithContext } from "@/external/logtail/logtailUtils.js";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||
import { getPriceForOverage } from "@/internal/products/prices/priceUtils.js";
|
||||
import { Customer } from "@autumn/shared";
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import Stripe from "stripe";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { features, products, rewards } from "tests/global.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { getFixedPriceAmount, timeout } from "tests/utils/genUtils.js";
|
||||
import {
|
||||
advanceClockForInvoice,
|
||||
completeCheckoutForm,
|
||||
getDiscount,
|
||||
} from "tests/utils/stripeUtils.js";
|
||||
import { initCustomerWithTestClock } from "tests/utils/testInitUtils.js";
|
||||
|
||||
import chalk from "chalk";
|
||||
import Stripe from "stripe";
|
||||
|
||||
import { expect } from "chai";
|
||||
import {
|
||||
APIVersion,
|
||||
AppEnv,
|
||||
CouponDurationType,
|
||||
CreateReward,
|
||||
Organization,
|
||||
RewardType,
|
||||
} from "@autumn/shared";
|
||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||
import { getPriceForOverage } from "@/internal/products/prices/priceUtils.js";
|
||||
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import {
|
||||
addPrefixToProducts,
|
||||
getBasePrice,
|
||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { advanceTestClock } from "@/utils/scriptUtils/testClockUtils.js";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
|
||||
const pro = constructProduct({
|
||||
type: "pro",
|
||||
items: [constructArrearItem({ featureId: TestFeature.Words })],
|
||||
});
|
||||
|
||||
const testCase = "coupon2";
|
||||
|
||||
// Create reward input
|
||||
const reward: CreateReward = {
|
||||
id: "usage",
|
||||
name: "usage",
|
||||
promo_codes: [{ code: "usage" }],
|
||||
type: RewardType.InvoiceCredits,
|
||||
discount_config: {
|
||||
discount_value: 10000,
|
||||
duration_type: CouponDurationType.Forever,
|
||||
duration_value: 1,
|
||||
should_rollover: true,
|
||||
apply_to_all: false,
|
||||
price_ids: [],
|
||||
},
|
||||
};
|
||||
|
||||
describe(
|
||||
chalk.yellow("coupon2 -- Testing one-off rollover, apply to usage only"),
|
||||
chalk.yellow(`${testCase} - Testing one-off rollover, apply to usage only`),
|
||||
() => {
|
||||
let logger: any;
|
||||
let customerId = "coupon2";
|
||||
let customerId = testCase;
|
||||
let stripeCli: Stripe;
|
||||
let customer: Customer;
|
||||
let testClockId: string;
|
||||
|
||||
let couponAmount = rewards.rolloverUsage.discount_config.discount_value;
|
||||
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
|
||||
let org: Organization;
|
||||
let env: AppEnv;
|
||||
let db: DrizzleCli;
|
||||
|
||||
let couponAmount = reward.discount_config!.discount_value;
|
||||
|
||||
before(async function () {
|
||||
const { testClockId: testClockId1, customer: customer1 } =
|
||||
await initCustomerWithTestClock({
|
||||
await setupBefore(this);
|
||||
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
db = this.db;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { testClockId: testClockId1 } = await initCustomer({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn: this.autumnJs,
|
||||
});
|
||||
testClockId = testClockId1;
|
||||
customer = customer1;
|
||||
|
||||
logger = createLogtailWithContext({
|
||||
test: "coupon2 -- Testing one-off rollover, apply to usage only",
|
||||
customerId,
|
||||
testClockId = testClockId1;
|
||||
|
||||
addPrefixToProducts({
|
||||
products: [pro],
|
||||
prefix: testCase,
|
||||
});
|
||||
stripeCli = createStripeCli({
|
||||
org: this.org,
|
||||
|
||||
await createProducts({
|
||||
orgId: this.org.id,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn,
|
||||
products: [pro],
|
||||
});
|
||||
|
||||
await createReward({
|
||||
orgId: org.id,
|
||||
env,
|
||||
db,
|
||||
autumn,
|
||||
reward,
|
||||
productId: pro.id,
|
||||
onlyUsage: true,
|
||||
});
|
||||
});
|
||||
|
||||
// CYCLE 0
|
||||
it("should attach pro with overage (through checkout)", async () => {
|
||||
const res = await AutumnCli.attach({
|
||||
customerId,
|
||||
productId: products.proWithOverage.id,
|
||||
forceCheckout: true,
|
||||
it("should attach pro with promo code", async () => {
|
||||
const res = await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(
|
||||
res.checkout_url,
|
||||
undefined,
|
||||
rewards.rolloverUsage.id,
|
||||
);
|
||||
await completeCheckoutForm(res.checkout_url, undefined, reward.id);
|
||||
|
||||
await timeout(10000);
|
||||
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.proWithOverage,
|
||||
cusRes,
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expectProductAttached({
|
||||
customer,
|
||||
product: pro,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have fixed price invoice and correct remaining coupon amount", async () => {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
const fixedPrice = getFixedPriceAmount(products.proWithOverage);
|
||||
expect(cusRes.invoices[0].total).to.equal(fixedPrice);
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
const fixedPrice = getBasePrice({ product: pro });
|
||||
expect(customer.invoices![0].total).to.equal(fixedPrice);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
customer: cusRes.customer,
|
||||
stripeCli,
|
||||
stripeId: customer.stripe_id!,
|
||||
});
|
||||
|
||||
try {
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverUsage.id,
|
||||
);
|
||||
|
||||
// Expect amount to be original amount - pro price
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(reward.id);
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
||||
} catch (error) {
|
||||
logger.error("--------------------------------");
|
||||
logger.error(
|
||||
"Expected stripe cus to have coupon",
|
||||
rewards.rolloverUsage,
|
||||
);
|
||||
logger.error("Actual stripe cus discount", cusDiscount);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// CYCLE 1
|
||||
it("should set usage to -100 and advance clock by 1 month", async () => {
|
||||
const usage = Math.min(Math.floor(Math.random() * 1000), 100);
|
||||
it("should track usage and have correct invoice amount", async () => {
|
||||
const usage = new Decimal(Math.random() * 1250120 + 10000)
|
||||
.toDecimalPlaces(2)
|
||||
.toNumber();
|
||||
|
||||
const res = await AutumnCli.usage({
|
||||
customerId,
|
||||
featureId: features.metered1.id,
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
feature_id: TestFeature.Words,
|
||||
value: usage,
|
||||
});
|
||||
|
||||
// Price
|
||||
const price = getPriceForOverage(
|
||||
products.proWithOverage.prices[1],
|
||||
-(products.proWithOverage.entitlements.metered1.allowance! - usage),
|
||||
);
|
||||
let usageTotal = await getExpectedInvoiceTotal({
|
||||
org,
|
||||
env,
|
||||
db,
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
usage: [{ featureId: TestFeature.Words, value: usage }],
|
||||
stripeCli,
|
||||
onlyIncludeUsage: true,
|
||||
});
|
||||
|
||||
couponAmount = couponAmount - price;
|
||||
let basePrice = getBasePrice({ product: pro });
|
||||
|
||||
await advanceClockForInvoice({
|
||||
couponAmount = couponAmount - usageTotal;
|
||||
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
waitForMeterUpdate: true,
|
||||
});
|
||||
advanceTo: addHours(
|
||||
addMonths(new Date(), 1),
|
||||
hoursToFinalizeInvoice,
|
||||
).getTime(),
|
||||
waitForSeconds: 20,
|
||||
});
|
||||
|
||||
it("should have $0 invoice and correct new coupon amount", async () => {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
expect(cusRes.invoices[0].total).to.equal(
|
||||
getFixedPriceAmount(products.proWithOverage),
|
||||
);
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expect(customer.invoices![0].total).to.equal(basePrice);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
customer: cusRes.customer,
|
||||
stripeCli,
|
||||
stripeId: customer.stripe_id!,
|
||||
});
|
||||
|
||||
try {
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverUsage.id,
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(reward.id);
|
||||
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(
|
||||
Math.round(couponAmount * 100),
|
||||
);
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
||||
} catch (error) {
|
||||
logger.error("--------------------------------");
|
||||
logger.error("coupon2, cycle 1 failed");
|
||||
logger.error(
|
||||
"Expected stripe cus to have coupon",
|
||||
rewards.rolloverUsage,
|
||||
);
|
||||
logger.error("Actual stripe cus discount", cusDiscount);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { Autumn } from "@/external/autumn/autumnCli.js";
|
||||
import { features } from "tests/global.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "tests/utils/init.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import {
|
||||
AppEnv,
|
||||
BillingInterval,
|
||||
ProductItemFeatureType,
|
||||
UsageModel,
|
||||
} from "@autumn/shared";
|
||||
import { createProduct } from "tests/utils/productUtils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { getUsageCusEnt } from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getPrepaidCusEnt } from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { constructFeaturePriceItem } from "@/internal/products/product-items/productItemUtils.js";
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
import { timeout } from "@/utils/genUtils.js";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
|
||||
// Scenario 1: prepaid + pay per use monthly -> prepaid + pay per use monthly
|
||||
let pro = {
|
||||
@@ -102,15 +101,15 @@ export const getPrepaidAndUsageCusEnts = async ({
|
||||
return { prepaidCusEnt, usageCusEnt };
|
||||
};
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
const testCase = "multiFeature1";
|
||||
describe(`${chalk.yellowBright(
|
||||
"multi-feature/multi_feature1: Testing prepaid + pay per use -> prepaid + pay per use",
|
||||
"multiFeature1: Testing prepaid + pay per use -> prepaid + pay per use",
|
||||
)}`, () => {
|
||||
let autumn: Autumn;
|
||||
let customerId = "multiFeature1Customer";
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let customerId = testCase;
|
||||
|
||||
let prepaidQuantity = 10;
|
||||
let prepaidAllowance = pro.items.prepaid.included_usage + prepaidQuantity;
|
||||
|
||||
let totalUsage = 0;
|
||||
|
||||
let premiumPrepaidAllowance =
|
||||
@@ -127,30 +126,29 @@ describe(`${chalk.yellowBright(
|
||||
await setupBefore(this);
|
||||
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
attachPm: true,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
autumn = this.autumn;
|
||||
|
||||
await createProduct({
|
||||
await createProducts({
|
||||
autumn,
|
||||
product: pro,
|
||||
});
|
||||
|
||||
await createProduct({
|
||||
autumn,
|
||||
product: premium,
|
||||
products: [pro, premium],
|
||||
db: this.db,
|
||||
orgId: this.org.id,
|
||||
env: this.env,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro product to customer", async function () {
|
||||
await autumn.attach({
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
options: optionsList,
|
||||
});
|
||||
|
||||
@@ -172,10 +170,10 @@ describe(`${chalk.yellowBright(
|
||||
it("should use prepaid allowance first", async function () {
|
||||
let value = 60;
|
||||
|
||||
await autumn.events.send({
|
||||
customerId,
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
value,
|
||||
featureId: features.metered1.id,
|
||||
feature_id: features.metered1.id,
|
||||
});
|
||||
|
||||
totalUsage += value;
|
||||
@@ -196,15 +194,15 @@ describe(`${chalk.yellowBright(
|
||||
|
||||
it("should have correct usage / invoice after upgrade", async function () {
|
||||
let value = 60;
|
||||
await autumn.events.send({
|
||||
customerId,
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
value,
|
||||
featureId: features.metered1.id,
|
||||
feature_id: features.metered1.id,
|
||||
});
|
||||
|
||||
totalUsage += value;
|
||||
|
||||
await timeout(3000);
|
||||
await timeout(5000);
|
||||
|
||||
let { usageCusEnt } = await getPrepaidAndUsageCusEnts({
|
||||
customerId,
|
||||
@@ -214,11 +212,9 @@ describe(`${chalk.yellowBright(
|
||||
featureId: features.metered1.id,
|
||||
});
|
||||
|
||||
// totalUsage = totalUsage + value - (usageCusEnt?.balance ?? 0);
|
||||
|
||||
await autumn.attach({
|
||||
customerId,
|
||||
productId: premium.id,
|
||||
customer_id: customerId,
|
||||
product_id: premium.id,
|
||||
options: optionsList,
|
||||
});
|
||||
|
||||
@@ -233,8 +229,6 @@ describe(`${chalk.yellowBright(
|
||||
|
||||
// Check invoice too
|
||||
let { invoices } = await autumn.customers.get(customerId);
|
||||
// 1. Let invoices[1] be 10 * premium prepaid price - pro prepaid price
|
||||
// 2. Let invoices[0] be value * pro pay per use price
|
||||
|
||||
let invoice1Amount =
|
||||
(premium.items.prepaid.price ?? 0) * prepaidQuantity -
|
||||
@@ -242,16 +236,12 @@ describe(`${chalk.yellowBright(
|
||||
|
||||
let invoice0Amount = value * (pro.items.payPerUse.price ?? 0);
|
||||
|
||||
expect(invoices[1].total).to.equal(invoice1Amount);
|
||||
expect(invoices[0].total).to.equal(invoice0Amount);
|
||||
let totalAmount = invoice1Amount + invoice0Amount;
|
||||
|
||||
// console.log("Total usage", totalUsage);
|
||||
// console.log("Premium prepaid allowance", premiumPrepaidAllowance);
|
||||
// console.log("Value", value);
|
||||
expect(invoices![0].total).to.equal(totalAmount);
|
||||
|
||||
let leftover = premiumPrepaidAllowance - totalUsage + value;
|
||||
expect(prepaidCusEnt?.balance).to.equal(Math.max(0, leftover));
|
||||
|
||||
expect(newUsageCusEnt?.balance).to.equal(0);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,8 @@
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { Autumn } from "@/external/autumn/autumnCli.js";
|
||||
import { features } from "tests/global.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "tests/utils/init.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import {
|
||||
AppEnv,
|
||||
BillingInterval,
|
||||
@@ -22,9 +21,10 @@ import {
|
||||
constructFeatureItem,
|
||||
constructFeaturePriceItem,
|
||||
} from "@/internal/products/product-items/productItemUtils.js";
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
|
||||
import { timeout } from "@/utils/genUtils.js";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
|
||||
// Scenario 1: prepaid + pay per use monthly -> prepaid + pay per use monthly
|
||||
let pro = {
|
||||
@@ -51,15 +51,6 @@ let premium = {
|
||||
id: "multiFeature2Premium",
|
||||
name: "Multi Feature 2 Premium",
|
||||
items: {
|
||||
// // Prepaid
|
||||
// prepaid: constructFeaturePriceItem({
|
||||
// feature_id: features.metered1.id,
|
||||
// included_usage: 100,
|
||||
// amount: 15,
|
||||
// interval: BillingInterval.Month,
|
||||
// usage_model: UsageModel.Prepaid,
|
||||
// }),
|
||||
|
||||
// Pay per use
|
||||
payPerUse: constructFeaturePriceItem({
|
||||
feature_id: features.metered1.id,
|
||||
@@ -86,7 +77,7 @@ export const getLifetimeAndUsageCusEnts = async ({
|
||||
featureId: string;
|
||||
}) => {
|
||||
let mainCusProduct = await getMainCusProduct({
|
||||
customerId,
|
||||
customerId: customerId,
|
||||
db,
|
||||
orgId,
|
||||
env,
|
||||
@@ -105,12 +96,12 @@ export const getLifetimeAndUsageCusEnts = async ({
|
||||
return { lifetimeCusEnt, usageCusEnt };
|
||||
};
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
const testCase = "multiFeature2";
|
||||
describe(`${chalk.yellowBright(
|
||||
"multi-feature/multi_feature2: Testing lifetime + pay per use -> pay per use",
|
||||
"multiFeature2: Testing lifetime + pay per use -> pay per use",
|
||||
)}`, () => {
|
||||
let autumn: Autumn;
|
||||
let customerId = "multiFeature2Customer";
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let customerId = testCase;
|
||||
|
||||
let totalUsage = 0;
|
||||
|
||||
@@ -118,11 +109,12 @@ describe(`${chalk.yellowBright(
|
||||
await setupBefore(this);
|
||||
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
attachPm: true,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
autumn = this.autumn;
|
||||
@@ -130,18 +122,24 @@ describe(`${chalk.yellowBright(
|
||||
await createProduct({
|
||||
autumn,
|
||||
product: pro,
|
||||
db: this.db,
|
||||
orgId: this.org.id,
|
||||
env: this.env,
|
||||
});
|
||||
|
||||
await createProduct({
|
||||
autumn,
|
||||
product: premium,
|
||||
db: this.db,
|
||||
orgId: this.org.id,
|
||||
env: this.env,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro product to customer", async function () {
|
||||
await autumn.attach({
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
});
|
||||
|
||||
let { lifetimeCusEnt, usageCusEnt } = await getLifetimeAndUsageCusEnts({
|
||||
@@ -187,16 +185,17 @@ describe(`${chalk.yellowBright(
|
||||
it("should have correct usage after upgrade", async function () {
|
||||
let value = 20;
|
||||
|
||||
await autumn.events.send({
|
||||
customerId,
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
value,
|
||||
featureId: features.metered1.id,
|
||||
feature_id: features.metered1.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
|
||||
await autumn.attach({
|
||||
customerId,
|
||||
productId: premium.id,
|
||||
customer_id: customerId,
|
||||
product_id: premium.id,
|
||||
});
|
||||
|
||||
let { lifetimeCusEnt, usageCusEnt: newUsageCusEnt } =
|
||||
@@ -208,18 +207,18 @@ describe(`${chalk.yellowBright(
|
||||
featureId: features.metered1.id,
|
||||
});
|
||||
|
||||
// Check invoice too
|
||||
let { invoices } = await autumn.customers.get(customerId);
|
||||
// 1. Let invoices[1] be 10 * premium prepaid price - pro prepaid price
|
||||
// 2. Let invoices[0] be value * pro pay per use price
|
||||
|
||||
let invoice0Amount = value * (pro.items.payPerUse.price ?? 0);
|
||||
expect(invoices[0].total).to.equal(invoice0Amount);
|
||||
|
||||
expect(lifetimeCusEnt).to.not.exist;
|
||||
|
||||
expect(newUsageCusEnt?.balance).to.equal(
|
||||
premium.items.payPerUse.included_usage,
|
||||
);
|
||||
|
||||
// Check invoice too
|
||||
let res = await autumn.customers.get(customerId);
|
||||
let invoices = res.invoices;
|
||||
let invoice0Amount = value * (pro.items.payPerUse.price ?? 0);
|
||||
expect(invoices![0].total).to.equal(
|
||||
invoice0Amount,
|
||||
"Invoice 0 should be 0",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { Autumn } from "@/external/autumn/autumnCli.js";
|
||||
import { expect } from "chai";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { features } from "tests/global.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
ProductItemFeatureType,
|
||||
UsageModel,
|
||||
} from "@autumn/shared";
|
||||
import { createProduct } from "tests/utils/productUtils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import {
|
||||
getLifetimeFreeCusEnt,
|
||||
@@ -22,11 +22,10 @@ import {
|
||||
constructFeatureItem,
|
||||
constructFeaturePriceItem,
|
||||
} from "@/internal/products/product-items/productItemUtils.js";
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
import { timeout } from "@/utils/genUtils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { initCustomerWithTestClock } from "tests/utils/testInitUtils.js";
|
||||
import { addDays, addMonths } from "date-fns";
|
||||
import { addMonths } from "date-fns";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
|
||||
// Scenario 1: prepaid + pay per use monthly -> prepaid + pay per use monthly
|
||||
@@ -87,7 +86,7 @@ export const getLifetimeAndUsageCusEnts = async ({
|
||||
describe(`${chalk.yellowBright(
|
||||
"multi-feature/multi_feature3: Testing lifetime + pay per use, advance test clock",
|
||||
)}`, () => {
|
||||
let autumn: Autumn;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let customerId = "multiFeature3Customer";
|
||||
|
||||
let totalUsage = 0;
|
||||
@@ -108,16 +107,19 @@ describe(`${chalk.yellowBright(
|
||||
|
||||
autumn = this.autumn;
|
||||
|
||||
await createProduct({
|
||||
await createProducts({
|
||||
autumn,
|
||||
product: pro,
|
||||
products: [pro],
|
||||
db: this.db,
|
||||
orgId: this.org.id,
|
||||
env: this.env,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro product to customer", async function () {
|
||||
await autumn.attach({
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
});
|
||||
|
||||
let { lifetimeCusEnt, usageCusEnt } = await getLifetimeAndUsageCusEnts({
|
||||
@@ -138,10 +140,10 @@ describe(`${chalk.yellowBright(
|
||||
let value = pro.items.lifetime.included_usage as number;
|
||||
value += overageValue;
|
||||
|
||||
await autumn.events.send({
|
||||
customerId,
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
value,
|
||||
featureId: features.metered1.id,
|
||||
feature_id: features.metered1.id,
|
||||
});
|
||||
|
||||
totalUsage += value;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { products, referralPrograms } from "../../global.js";
|
||||
import { assert } from "chai";
|
||||
import chalk from "chalk";
|
||||
import AutumnError, { Autumn } from "@/external/autumn/autumnCli.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import {
|
||||
Customer,
|
||||
@@ -23,7 +23,7 @@ describe(`${chalk.yellowBright(
|
||||
let mainCustomerId = "main-referral-1";
|
||||
let alternateCustomerId = "alternate-referral-1";
|
||||
let redeemers = ["referral1-r1", "referral1-r2", "referral1-r3"];
|
||||
let autumn: Autumn;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let stripeCli: Stripe;
|
||||
let testClockId: string;
|
||||
let referralCode: ReferralCode;
|
||||
@@ -33,7 +33,6 @@ describe(`${chalk.yellowBright(
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
autumn = this.autumn;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { testClockId: testClockId1, customer } =
|
||||
@@ -48,8 +47,8 @@ describe(`${chalk.yellowBright(
|
||||
mainCustomer = customer;
|
||||
|
||||
await autumn.attach({
|
||||
customerId: mainCustomerId,
|
||||
productId: products.proWithTrial.id,
|
||||
customer_id: mainCustomerId,
|
||||
product_id: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
let batchCreate = [];
|
||||
@@ -155,8 +154,8 @@ describe(`${chalk.yellowBright(
|
||||
let redeemer = redeemers[i];
|
||||
|
||||
await autumn.attach({
|
||||
customerId: redeemer,
|
||||
productId: products.pro.id,
|
||||
customer_id: redeemer,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { products, referralPrograms } from "../../global.js";
|
||||
import { assert } from "chai";
|
||||
import chalk from "chalk";
|
||||
import AutumnError, { Autumn } from "@/external/autumn/autumnCli.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import {
|
||||
Customer,
|
||||
@@ -22,7 +22,7 @@ describe(`${chalk.yellowBright(
|
||||
)}`, () => {
|
||||
let mainCustomerId = "main-referral-2";
|
||||
let redeemers = ["referral2-r1", "referral2-r2", "referral2-r3"];
|
||||
let autumn: Autumn;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let stripeCli: Stripe;
|
||||
let testClockId: string;
|
||||
let referralCode: ReferralCode;
|
||||
@@ -32,7 +32,6 @@ describe(`${chalk.yellowBright(
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
autumn = this.autumn;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { testClockId: testClockId1, customer } =
|
||||
@@ -118,8 +117,8 @@ describe(`${chalk.yellowBright(
|
||||
let curTime = new Date();
|
||||
it("customer should have discount for first purchase", async function () {
|
||||
await autumn.attach({
|
||||
customerId: mainCustomerId,
|
||||
productId: products.proWithTrial.id,
|
||||
customer_id: mainCustomerId,
|
||||
product_id: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
@@ -134,8 +133,8 @@ describe(`${chalk.yellowBright(
|
||||
// 1. Get invoice
|
||||
let { invoices } = await autumn.customers.get(mainCustomerId);
|
||||
|
||||
assert.equal(invoices.length, 2);
|
||||
assert.equal(invoices[0].total, 0);
|
||||
assert.equal(invoices!.length, 2);
|
||||
assert.equal(invoices![0].total, 0);
|
||||
});
|
||||
|
||||
it("customer should have discount for second purchase", async function () {
|
||||
@@ -165,7 +164,7 @@ describe(`${chalk.yellowBright(
|
||||
// // 3. Get invoice again
|
||||
let { invoices: invoices2 } = await autumn.customers.get(mainCustomerId);
|
||||
|
||||
assert.equal(invoices2.length, 3);
|
||||
assert.equal(invoices2[0].total, 0);
|
||||
assert.equal(invoices2!.length, 3);
|
||||
assert.equal(invoices2![0].total, 0);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { features, products, referralPrograms } from "../../global.js";
|
||||
import { assert } from "chai";
|
||||
import chalk from "chalk";
|
||||
import AutumnError, { Autumn } from "@/external/autumn/autumnCli.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import {
|
||||
Customer,
|
||||
@@ -21,7 +21,7 @@ describe(`${chalk.yellowBright(
|
||||
)}`, () => {
|
||||
let mainCustomerId = "main-referral-3";
|
||||
let redeemers = ["referral3-r1", "referral3-r2", "referral3-r3"];
|
||||
let autumn: Autumn;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let stripeCli: Stripe;
|
||||
let testClockId: string;
|
||||
let referralCode: ReferralCode;
|
||||
@@ -46,8 +46,8 @@ describe(`${chalk.yellowBright(
|
||||
mainCustomer = customer;
|
||||
|
||||
await autumn.attach({
|
||||
customerId: mainCustomerId,
|
||||
productId: products.proWithTrial.id,
|
||||
customer_id: mainCustomerId,
|
||||
product_id: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
let batchCreate = [];
|
||||
@@ -106,8 +106,8 @@ describe(`${chalk.yellowBright(
|
||||
let redeemer = redeemers[i];
|
||||
|
||||
await autumn.attach({
|
||||
customerId: redeemer,
|
||||
productId: products.pro.id,
|
||||
customer_id: redeemer,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
@@ -1,14 +1,8 @@
|
||||
import { features, products, referralPrograms } from "../../global.js";
|
||||
import { assert } from "chai";
|
||||
import chalk from "chalk";
|
||||
import AutumnError, { Autumn } from "@/external/autumn/autumnCli.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import {
|
||||
Customer,
|
||||
ErrCode,
|
||||
ReferralCode,
|
||||
RewardRedemption,
|
||||
} from "@autumn/shared";
|
||||
import { Customer, ReferralCode, RewardRedemption } from "@autumn/shared";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { initCustomerWithTestClock } from "tests/utils/testInitUtils.js";
|
||||
import { Stripe } from "stripe";
|
||||
@@ -17,6 +11,7 @@ import { compareProductEntitlements } from "tests/utils/compare.js";
|
||||
import { addDays, addHours } from "date-fns";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
describe(`${chalk.yellowBright(
|
||||
@@ -26,7 +21,7 @@ describe(`${chalk.yellowBright(
|
||||
// let redeemers = ["referral4-r1", "referral4-r2"];
|
||||
let redeemerId = "referral4-r1";
|
||||
|
||||
let autumn: Autumn;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let stripeCli: Stripe;
|
||||
let referralCode: ReferralCode;
|
||||
|
||||
@@ -49,8 +44,8 @@ describe(`${chalk.yellowBright(
|
||||
});
|
||||
|
||||
await autumn.attach({
|
||||
customerId: mainCustomerId,
|
||||
productId: products.proWithTrial.id,
|
||||
customer_id: mainCustomerId,
|
||||
product_id: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
let { testClockId: testClockId1, customer } =
|
||||
@@ -85,8 +80,8 @@ describe(`${chalk.yellowBright(
|
||||
|
||||
it("should not be triggered because of trial", async function () {
|
||||
await autumn.attach({
|
||||
customerId: redeemerId,
|
||||
productId: products.proWithTrial.id,
|
||||
customer_id: redeemerId,
|
||||
product_id: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
@@ -1,188 +0,0 @@
|
||||
import { expect } from "chai";
|
||||
import { initCustomer } from "../../utils/init.js";
|
||||
import { AutumnCli } from "../../cli/AutumnCli.js";
|
||||
import {
|
||||
advanceProducts,
|
||||
creditSystems,
|
||||
features,
|
||||
products,
|
||||
} from "../../global.js";
|
||||
import { compareMainProduct } from "../../utils/compare.js";
|
||||
import { timeout } from "../../utils/genUtils.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
import { sendGPUEvents } from "../../utils/advancedUsageUtils.js";
|
||||
import chalk from "chalk";
|
||||
|
||||
const PRECISION = 12;
|
||||
describe.skip(`${chalk.yellowBright(
|
||||
"Testing group by -- regular metered1 feature",
|
||||
)}`, () => {
|
||||
let customerId = "group-by-basic-metered";
|
||||
before(async function () {
|
||||
await initCustomer({
|
||||
customer_data: {
|
||||
id: customerId,
|
||||
name: "Group by basic metered",
|
||||
email: "group-by-basic-metered@example.com",
|
||||
},
|
||||
attachPm: true,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro product to customer", async () => {
|
||||
await AutumnCli.attach({
|
||||
customerId,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
it("should send events for three different groups", async () => {
|
||||
// const users = ["null", "123", "abc"];
|
||||
const users = ["null", "123", "abc"];
|
||||
|
||||
let results: any = {};
|
||||
let numEventsPerUser = 5;
|
||||
let groupProperty = features.metered1.config.group_by.property;
|
||||
let multiplier = 1.5;
|
||||
|
||||
for (const user of users) {
|
||||
let batchEvents = [];
|
||||
let totalValue = 0;
|
||||
for (let i = 0; i < numEventsPerUser; i++) {
|
||||
let randomVal = new Decimal(Math.random().toFixed(PRECISION))
|
||||
.mul(multiplier)
|
||||
.toNumber();
|
||||
totalValue = new Decimal(totalValue).plus(randomVal).toNumber();
|
||||
|
||||
batchEvents.push(
|
||||
AutumnCli.sendEvent({
|
||||
customerId,
|
||||
eventName: features.metered1.eventName,
|
||||
properties: {
|
||||
[groupProperty]: user == "null" ? null : user,
|
||||
value: randomVal,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(batchEvents);
|
||||
await timeout(3000);
|
||||
|
||||
results[user] = totalValue;
|
||||
}
|
||||
|
||||
let metered1Allowance = products.pro.entitlements.metered1.allowance!;
|
||||
|
||||
for (const user of users) {
|
||||
const { allowed, balanceObj }: any = await AutumnCli.entitled(
|
||||
customerId,
|
||||
features.metered1.id,
|
||||
true,
|
||||
user == "null" ? undefined : user,
|
||||
);
|
||||
|
||||
let expectedBalance = new Decimal(metered1Allowance)
|
||||
.minus(results[user])
|
||||
.toNumber();
|
||||
|
||||
if (expectedBalance < 1) {
|
||||
expect(allowed).to.be.false;
|
||||
} else {
|
||||
expect(allowed).to.be.true;
|
||||
}
|
||||
|
||||
expect(balanceObj!.balance).to.equal(expectedBalance);
|
||||
|
||||
// Check balance for GET /customers/:customerId
|
||||
const res = await AutumnCli.getCustomer(customerId, {
|
||||
[groupProperty]: user == "null" ? undefined : user,
|
||||
});
|
||||
const entitlements = res.entitlements;
|
||||
const metered1 = entitlements.find(
|
||||
(e: any) => e.feature_id === features.metered1.id,
|
||||
);
|
||||
expect(metered1.balance).to.equal(expectedBalance);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// TO ADD SUPPORT FOR IN THE FUTURE
|
||||
describe.skip(`${chalk.yellowBright(
|
||||
"Testing group by -- advanced GPU usage",
|
||||
)}`, () => {
|
||||
let customerId = "group-by-advanced-gpu-usage-metered";
|
||||
before(async function () {
|
||||
await initCustomer({
|
||||
customer_data: {
|
||||
id: customerId,
|
||||
name: "Group by advanced GPU usage",
|
||||
email: "group-by-advanced-gpu-usage@example.com",
|
||||
},
|
||||
attachPm: true,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach GPU system starter product to customer", async () => {
|
||||
await AutumnCli.attach({
|
||||
customerId,
|
||||
productId: advanceProducts.gpuSystemStarter.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuSystemStarter,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
it("should send events (advanced GPU usage) for three different groups", async () => {
|
||||
const users = ["null", "123", "abc"];
|
||||
|
||||
let numEventsPerUser = 20;
|
||||
let groupProperty = features.gpu1.config.group_by.property;
|
||||
|
||||
const results: any = {};
|
||||
for (const user of users) {
|
||||
let { creditsUsed } = await sendGPUEvents({
|
||||
customerId,
|
||||
eventCount: numEventsPerUser,
|
||||
groupObj: user == "null" ? undefined : { [groupProperty]: user },
|
||||
});
|
||||
results[user] = creditsUsed;
|
||||
}
|
||||
|
||||
let creditAllowance =
|
||||
advanceProducts.gpuSystemStarter.entitlements.gpuCredits.allowance!;
|
||||
|
||||
console.log("Results", results);
|
||||
console.log("Starting allowance", creditAllowance);
|
||||
|
||||
for (const user of users) {
|
||||
const { allowed, balanceObj }: any = await AutumnCli.entitled(
|
||||
customerId,
|
||||
creditSystems.gpuCredits.id,
|
||||
true,
|
||||
user == "null" ? undefined : user,
|
||||
);
|
||||
|
||||
let expectedCreditAllowance = new Decimal(creditAllowance)
|
||||
.minus(results[user])
|
||||
.toNumber();
|
||||
|
||||
expect(balanceObj.balance).to.equal(expectedCreditAllowance);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,187 +0,0 @@
|
||||
import { Customer } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { initCustomerWithTestClock } from "../../utils/testInitUtils.js";
|
||||
import { compareMainProduct } from "../../utils/compare.js";
|
||||
import { AutumnCli } from "../../cli/AutumnCli.js";
|
||||
import { advanceProducts, creditSystems } from "../../global.js";
|
||||
import { timeout } from "../../utils/genUtils.js";
|
||||
import { assert, expect } from "chai";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import {
|
||||
advanceClockForInvoice,
|
||||
advanceMonths,
|
||||
checkBillingMeterEventSummary,
|
||||
getUsageInArrearPrice,
|
||||
} from "../../utils/stripeUtils.js";
|
||||
import { addMonths } from "date-fns";
|
||||
import {
|
||||
sendGPUEvents,
|
||||
checkCreditBalance,
|
||||
checkUsageInvoiceAmount,
|
||||
} from "../../utils/advancedUsageUtils.js";
|
||||
|
||||
// THIRD, TEST GPU PRO ANNUAL
|
||||
describe(`${chalk.yellowBright("multi_interval1: GPU starter annual")}`, () => {
|
||||
const customerId = "advancedUsageAnnual";
|
||||
let testClockId = "";
|
||||
let totalCreditsUsed = 0;
|
||||
let customer: Customer;
|
||||
before(async function () {
|
||||
this.timeout(30000);
|
||||
|
||||
let { testClockId: insertedTestClockId, customer: insertedCustomer } =
|
||||
await initCustomerWithTestClock({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
});
|
||||
|
||||
testClockId = insertedTestClockId;
|
||||
customer = insertedCustomer;
|
||||
console.log("Testing multi interval 1");
|
||||
});
|
||||
|
||||
it("should attach GPU starter annual", async function () {
|
||||
this.timeout(30000);
|
||||
|
||||
const res = await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuStarterAnnual.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
});
|
||||
|
||||
it("should have GPU starter annual product", async function () {
|
||||
this.timeout(30000);
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuStarterAnnual,
|
||||
cusRes: res,
|
||||
});
|
||||
|
||||
// Should have 2 invoices (one for annual, one for monthly)
|
||||
expect(res!.invoices.length).to.equal(2);
|
||||
});
|
||||
|
||||
it("should send 20 events and have correct balance", async function () {
|
||||
this.timeout(30000);
|
||||
|
||||
let eventCount = 20;
|
||||
const { creditsUsed } = await sendGPUEvents({
|
||||
customerId,
|
||||
eventCount,
|
||||
});
|
||||
|
||||
totalCreditsUsed = creditsUsed;
|
||||
console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
await checkCreditBalance({
|
||||
customerId,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
totalCreditsUsed,
|
||||
originalAllowance:
|
||||
advanceProducts.gpuStarterAnnual.entitlements.gpuCredits.allowance!,
|
||||
});
|
||||
});
|
||||
|
||||
// Advance by a month and check if latest invoice is correct
|
||||
it("should have invoice after a month and correct balance", async function () {
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
await advanceClockForInvoice({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
waitForMeterUpdate: true,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const invoices = res!.invoices;
|
||||
|
||||
let invoiceIndex = invoices.findIndex((invoice: any) =>
|
||||
invoice.product_ids.includes(advanceProducts.gpuStarterAnnual.id),
|
||||
);
|
||||
|
||||
await checkUsageInvoiceAmount({
|
||||
invoices,
|
||||
totalUsage: totalCreditsUsed,
|
||||
product: advanceProducts.gpuStarterAnnual,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
invoiceIndex,
|
||||
includeBase: false,
|
||||
});
|
||||
|
||||
await checkCreditBalance({
|
||||
customerId,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
totalCreditsUsed: 0,
|
||||
originalAllowance:
|
||||
advanceProducts.gpuStarterAnnual.entitlements.gpuCredits.allowance!,
|
||||
});
|
||||
});
|
||||
|
||||
// Advance by 1 year and check if latest invoice is correct
|
||||
it.skip("should have correct invoice after 1 year", async function () {
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
|
||||
// 1. Advance by 11 months
|
||||
let numberOfMonths = 11;
|
||||
await advanceMonths({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
numberOfMonths,
|
||||
});
|
||||
|
||||
// 2. Send 20 events
|
||||
let eventCount = 20;
|
||||
const { creditsUsed } = await sendGPUEvents({
|
||||
customerId,
|
||||
eventCount,
|
||||
});
|
||||
|
||||
let totalCreditsUsed = creditsUsed;
|
||||
console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
|
||||
// Advance by a month and check for usage
|
||||
await advanceClockForInvoice({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
waitForMeterUpdate: true,
|
||||
startingFrom: addMonths(new Date(), numberOfMonths),
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const invoices = res!.invoices;
|
||||
|
||||
let usagePrice = await getUsageInArrearPrice({
|
||||
org: this.org,
|
||||
sb: this.sb,
|
||||
env: this.env,
|
||||
productId: advanceProducts.gpuStarterAnnual.id,
|
||||
});
|
||||
|
||||
// Get billing meter event summary
|
||||
let eventSummary = await checkBillingMeterEventSummary({
|
||||
stripeCli,
|
||||
startTime: addMonths(new Date(), 11),
|
||||
stripeMeterId: usagePrice?.config?.stripe_meter_id,
|
||||
stripeCustomerId: customer.processor.id,
|
||||
});
|
||||
|
||||
try {
|
||||
assert.exists(eventSummary);
|
||||
assert.equal(
|
||||
eventSummary?.aggregated_value,
|
||||
Math.round(totalCreditsUsed),
|
||||
);
|
||||
assert.equal(invoices.length, 13 + 2);
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.log(" - Event summary: ", eventSummary);
|
||||
console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
console.log(" - Last 3 invoices: ", invoices.slice(-3));
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,206 +0,0 @@
|
||||
import { Customer } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { initCustomerWithTestClock } from "../../utils/testInitUtils.js";
|
||||
import { compareMainProduct } from "../../utils/compare.js";
|
||||
import { AutumnCli } from "../../cli/AutumnCli.js";
|
||||
import { advanceProducts, creditSystems } from "../../global.js";
|
||||
import { timeout } from "../../utils/genUtils.js";
|
||||
import { assert, expect } from "chai";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import {
|
||||
advanceClockForInvoice,
|
||||
advanceMonths,
|
||||
advanceTestClock,
|
||||
checkBillingMeterEventSummary,
|
||||
getUsageInArrearPrice,
|
||||
} from "../../utils/stripeUtils.js";
|
||||
import { addMonths } from "date-fns";
|
||||
import {
|
||||
sendGPUEvents,
|
||||
checkUsageInvoiceAmount,
|
||||
} from "../../utils/advancedUsageUtils.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
|
||||
// FOURTH, TEST GPU STARTER ANNUAL UPGRADE TO GPU PRO
|
||||
describe(`${chalk.yellowBright(
|
||||
"multi_interval2: GPU starter annual upgrade to GPU pro annual",
|
||||
)}`, () => {
|
||||
const customerId = "multi_interval2";
|
||||
let testClockId = "";
|
||||
let totalCreditsUsed = 0;
|
||||
let customer: Customer;
|
||||
|
||||
let curTime = new Date();
|
||||
before(async function () {
|
||||
let { testClockId: insertedTestClockId, customer: insertedCustomer } =
|
||||
await initCustomerWithTestClock({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
});
|
||||
|
||||
testClockId = insertedTestClockId;
|
||||
customer = insertedCustomer;
|
||||
// console.log("MULTI INTERVAL 2, INITIALIZED CUSTOMER");
|
||||
});
|
||||
|
||||
it("should attach GPU starter annual", async function () {
|
||||
const res = await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuStarterAnnual.id,
|
||||
});
|
||||
|
||||
await timeout(5000);
|
||||
});
|
||||
|
||||
it("should have GPU starter annual product", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuStarterAnnual,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
let numberOfMonths = 0;
|
||||
it(`should advance ${numberOfMonths} months and upgrade to GPU pro monthly`, async function () {
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
await advanceMonths({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
numberOfMonths,
|
||||
});
|
||||
|
||||
curTime = addMonths(curTime, numberOfMonths);
|
||||
|
||||
// Send 20 events
|
||||
let eventCount = 20;
|
||||
const { creditsUsed } = await sendGPUEvents({
|
||||
customerId,
|
||||
eventCount,
|
||||
});
|
||||
|
||||
totalCreditsUsed = creditsUsed;
|
||||
console.log(" - Total credits used: ", creditsUsed);
|
||||
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuProAnnual.id,
|
||||
});
|
||||
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
numberOfDays: 10,
|
||||
startingFrom: curTime,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have GPU pro annual product and 2 Stripe subscriptions", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuProAnnual,
|
||||
cusRes: res,
|
||||
});
|
||||
|
||||
// Should have 2 subscriptions
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
const subs = await stripeCli.subscriptions.list({
|
||||
customer: customer.processor.id,
|
||||
});
|
||||
|
||||
await timeout(5000);
|
||||
|
||||
expect(subs.data.length).to.equal(2);
|
||||
});
|
||||
|
||||
it("should have correct invoice for GPU starter annual (bill for remaining usages)", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const invoices = res!.invoices;
|
||||
|
||||
let invoiceIndex = invoices.findIndex((invoice: any) =>
|
||||
invoice.product_ids.includes(advanceProducts.gpuStarterAnnual.id),
|
||||
);
|
||||
|
||||
await checkUsageInvoiceAmount({
|
||||
invoices,
|
||||
totalUsage: totalCreditsUsed,
|
||||
product: advanceProducts.gpuStarterAnnual,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
invoiceIndex,
|
||||
includeBase: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("should send 20 events (on GPU pro annual)", async function () {
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
// Send 20 events
|
||||
let eventCount = 20;
|
||||
const { creditsUsed } = await sendGPUEvents({
|
||||
customerId,
|
||||
eventCount,
|
||||
});
|
||||
|
||||
totalCreditsUsed = creditsUsed;
|
||||
|
||||
console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
|
||||
await advanceClockForInvoice({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
waitForMeterUpdate: true,
|
||||
startingFrom: curTime,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct billing meter event summary for GPU pro annual", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const invoices = res!.invoices;
|
||||
|
||||
// Think I have to use Stripe metered event summary to check this
|
||||
let usagePrice = await getUsageInArrearPrice({
|
||||
org: this.org,
|
||||
sb: this.sb,
|
||||
env: this.env,
|
||||
productId: advanceProducts.gpuProAnnual.id,
|
||||
});
|
||||
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
// console.log(" - Usage price: ", usagePrice);
|
||||
// console.log(" - Config: ", usagePrice?.config);
|
||||
let eventSummary = await checkBillingMeterEventSummary({
|
||||
stripeCli,
|
||||
startTime: curTime, // Wrong date?
|
||||
stripeMeterId: usagePrice?.config?.stripe_meter_id,
|
||||
stripeCustomerId: customer.processor.id,
|
||||
});
|
||||
|
||||
let roundedFirst = Math.ceil(
|
||||
new Decimal(totalCreditsUsed)
|
||||
.div(usagePrice?.config?.billing_units!)
|
||||
.toNumber(),
|
||||
);
|
||||
let roundedTotalCreditsUsed = new Decimal(roundedFirst)
|
||||
.mul(usagePrice?.config?.billing_units!)
|
||||
.toNumber();
|
||||
try {
|
||||
assert.exists(eventSummary);
|
||||
assert.equal(eventSummary?.aggregated_value, roundedTotalCreditsUsed);
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.log(" - Event summary: ", eventSummary);
|
||||
console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
|
||||
// await checkUsageInvoiceAmount({
|
||||
// invoices,
|
||||
// totalUsage: totalCreditsUsed,
|
||||
// product: advanceProducts.gpuProAnnual,
|
||||
// featureId: creditSystems.gpuCredits.id,
|
||||
// invoiceIndex: 0,
|
||||
// includeBase: false,
|
||||
// });
|
||||
});
|
||||
});
|
||||
@@ -1,50 +1,50 @@
|
||||
import { expect } from "chai";
|
||||
import { v1ProductToBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { AutumnCli } from "../../cli/AutumnCli.js";
|
||||
import { features, products } from "../../global.js";
|
||||
import { compareMainProduct } from "../../utils/compare.js";
|
||||
import { initCustomer } from "../../utils/init.js";
|
||||
import {
|
||||
advanceClockForInvoice,
|
||||
completeCheckoutForm,
|
||||
} from "../../utils/stripeUtils.js";
|
||||
import { advanceClockForInvoice } from "../../utils/stripeUtils.js";
|
||||
import { timeout } from "../../utils/genUtils.js";
|
||||
import {
|
||||
calculateMetered1Price,
|
||||
createStripeCli,
|
||||
} from "@/external/stripe/utils.js";
|
||||
import { calculateMetered1Price } from "@/external/stripe/utils.js";
|
||||
import chalk from "chalk";
|
||||
import { initCustomerWithTestClock } from "tests/utils/testInitUtils.js";
|
||||
import { Customer } from "@autumn/shared";
|
||||
describe(`${chalk.yellowBright("usage1: Pro with overage")}`, () => {
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import Stripe from "stripe";
|
||||
|
||||
const testCase = "usage1";
|
||||
|
||||
describe(`${chalk.yellowBright("usage1: Testing basic usage product")}`, () => {
|
||||
const NUM_EVENTS = 50;
|
||||
const customerId = "usage1";
|
||||
const customerId = testCase;
|
||||
let testClockId: string;
|
||||
let customer: Customer;
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { customer: customer_, testClockId: testClockId_ } =
|
||||
await initCustomerWithTestClock({
|
||||
await initCustomer({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
customer = customer_;
|
||||
testClockId = testClockId_;
|
||||
});
|
||||
|
||||
it("usage1: should create a usage based entitlement", async function () {
|
||||
const res = await AutumnCli.attach({
|
||||
it("should attach usage based product", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.proWithOverage.id,
|
||||
forceCheckout: true,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(res.checkout_url);
|
||||
await timeout(10000);
|
||||
});
|
||||
|
||||
it("usage1: should have correct product", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
compareMainProduct({
|
||||
@@ -68,7 +68,7 @@ describe(`${chalk.yellowBright("usage1: Pro with overage")}`, () => {
|
||||
await timeout(10000);
|
||||
});
|
||||
|
||||
it("usage1: should have correct metered1 balance after sending events", async function () {
|
||||
it("should have correct metered1 balance after sending events", async function () {
|
||||
const res: any = await AutumnCli.entitled(customerId, features.metered1.id);
|
||||
|
||||
expect(res!.allowed).to.be.true;
|
||||
@@ -80,23 +80,17 @@ describe(`${chalk.yellowBright("usage1: Pro with overage")}`, () => {
|
||||
const proOverageAmt =
|
||||
products.proWithOverage.entitlements.metered1.allowance;
|
||||
|
||||
try {
|
||||
expect(res!.allowed).to.be.true;
|
||||
expect(balance?.balance).to.equal(proOverageAmt! - NUM_EVENTS);
|
||||
expect(balance?.usage_allowed).to.be.true;
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.log("Entitled res", res);
|
||||
console.group();
|
||||
throw error;
|
||||
}
|
||||
expect(res!.allowed, "should be allowed").to.be.true;
|
||||
|
||||
expect(balance?.balance, "should have correct metered1 balance").to.equal(
|
||||
proOverageAmt! - NUM_EVENTS,
|
||||
);
|
||||
|
||||
expect(balance?.usage_allowed, "should have usage_allowed").to.be.true;
|
||||
});
|
||||
|
||||
// Check invoice
|
||||
it("usage1: advance stripe test clock and wait for event", async function () {
|
||||
// this.timeout(1000 * 60 * 10);
|
||||
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
it("should advance stripe test clock and wait for event", async function () {
|
||||
await advanceClockForInvoice({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
@@ -104,7 +98,7 @@ describe(`${chalk.yellowBright("usage1: Pro with overage")}`, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("usage1: should have correct invoice amount", async function () {
|
||||
it("should have correct invoice amount", async function () {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
const invoices = cusRes!.invoices;
|
||||
|
||||
@@ -115,22 +109,17 @@ describe(`${chalk.yellowBright("usage1: Pro with overage")}`, () => {
|
||||
metered1Feature: features.metered1,
|
||||
});
|
||||
|
||||
try {
|
||||
expect(invoices.length).to.equal(2);
|
||||
|
||||
const invoice2 = invoices[0];
|
||||
|
||||
const basePrice = v1ProductToBasePrice({
|
||||
prices: products.proWithOverage.prices,
|
||||
});
|
||||
|
||||
expect(invoice2.total).to.equal(
|
||||
price + products.proWithOverage.prices[0].config.amount,
|
||||
price + basePrice,
|
||||
"invoice total should be usage price + base price",
|
||||
);
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.log(
|
||||
"Expected invoices[0] to have total of: ",
|
||||
price + products.proWithOverage.prices[0].config.amount,
|
||||
);
|
||||
console.log("Invoices", invoices);
|
||||
console.group();
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,6 @@ import { assert, expect } from "chai";
|
||||
import { AutumnCli } from "../../cli/AutumnCli.js";
|
||||
import { advanceProducts, creditSystems, features } from "../../global.js";
|
||||
import { compareMainProduct } from "../../utils/compare.js";
|
||||
import { initCustomer } from "../../utils/init.js";
|
||||
import { advanceClockForInvoice } from "../../utils/stripeUtils.js";
|
||||
import { timeout } from "../../utils/genUtils.js";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
@@ -14,11 +13,15 @@ import {
|
||||
} from "../../utils/advancedUsageUtils.js";
|
||||
|
||||
import chalk from "chalk";
|
||||
import { initCustomerWithTestClock } from "tests/utils/testInitUtils.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import Stripe from "stripe";
|
||||
|
||||
// FIRST, REGULAR CHECK GPU STARTER MONTHLY
|
||||
describe(`${chalk.yellowBright("usage2: GPU starter monthly")}`, () => {
|
||||
const customerId = "usage2";
|
||||
|
||||
const testCase = "usage2";
|
||||
describe(`${chalk.yellowBright("usage2: Testing basic usage product")}`, () => {
|
||||
const customerId = testCase;
|
||||
const PRECISION = 10;
|
||||
const ASSERT_INVOICE_AMOUNT = true;
|
||||
const CREDIT_MULTIPLIER = 100000;
|
||||
@@ -26,34 +29,30 @@ describe(`${chalk.yellowBright("usage2: GPU starter monthly")}`, () => {
|
||||
let testClockId = "";
|
||||
let totalCreditsUsed = 0;
|
||||
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
const { testClockId: createdTestClockId } = await initCustomerWithTestClock(
|
||||
{
|
||||
await setupBefore(this);
|
||||
const { testClockId: createdTestClockId } = await initCustomer({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
},
|
||||
);
|
||||
|
||||
testClockId = createdTestClockId;
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
it("usage2: should attach monthly starter", async function () {
|
||||
this.timeout(30000);
|
||||
testClockId = createdTestClockId;
|
||||
|
||||
const res = await AutumnCli.attach({
|
||||
stripeCli = this.stripeCli;
|
||||
});
|
||||
|
||||
it("should attach gpu system starter", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuSystemStarter.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
console.log(" - Attached product");
|
||||
});
|
||||
|
||||
it("usage2: should have monthly starter product", async function () {
|
||||
this.timeout(30000);
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuSystemStarter,
|
||||
@@ -62,9 +61,7 @@ describe(`${chalk.yellowBright("usage2: GPU starter monthly")}`, () => {
|
||||
});
|
||||
|
||||
// Use up events
|
||||
it("usage2: should have correct balance after events (up to 10 DP)", async function () {
|
||||
this.timeout(30000);
|
||||
|
||||
it("should send events and have correct balance (up to 10 DP)", async function () {
|
||||
let eventCount = 20;
|
||||
|
||||
const batchEvents = [];
|
||||
@@ -107,28 +104,16 @@ describe(`${chalk.yellowBright("usage2: GPU starter monthly")}`, () => {
|
||||
let creditAllowance =
|
||||
advanceProducts.gpuSystemStarter.entitlements.gpuCredits.allowance!;
|
||||
|
||||
try {
|
||||
expect(allowed).to.be.true;
|
||||
expect(balanceObj!.balance).to.equal(
|
||||
new Decimal(creditAllowance).minus(totalCreditsUsed).toNumber(),
|
||||
);
|
||||
console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
console.log(" - Balance: ", balanceObj!.balance);
|
||||
} catch (error) {
|
||||
console.log("Total credits used: ", totalCreditsUsed);
|
||||
console.log("Credit allowance: ", creditAllowance);
|
||||
console.log("Expected balance: ", creditAllowance - totalCreditsUsed);
|
||||
console.log("Received balance: ", balanceObj!.balance);
|
||||
console.group();
|
||||
throw error;
|
||||
}
|
||||
// console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
// console.log(" - Balance: ", balanceObj!.balance);
|
||||
});
|
||||
|
||||
// Check invoice.created event
|
||||
it("usage2: should have correct invoice amount / updated meter balance", async function () {
|
||||
this.timeout(100 * 60 * 1000);
|
||||
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
it("should have correct invoice amount / updated meter balance", async function () {
|
||||
await advanceClockForInvoice({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import chalk from "chalk";
|
||||
import { addDays, addMonths, differenceInDays } from "date-fns";
|
||||
import { initCustomerWithTestClock } from "../../utils/testInitUtils.js";
|
||||
import { advanceProducts, creditSystems } from "../../global.js";
|
||||
import { AutumnCli } from "../../cli/AutumnCli.js";
|
||||
import {
|
||||
@@ -17,63 +15,62 @@ import { assert, expect } from "chai";
|
||||
import { Decimal } from "decimal.js";
|
||||
import { compareMainProduct } from "../../utils/compare.js";
|
||||
import { checkSubscriptionContainsProducts } from "tests/utils/scheduleCheckUtils.js";
|
||||
import { getPriceForOverage } from "@/internal/products/prices/priceUtils.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import Stripe from "stripe";
|
||||
import { priceToInvoiceAmount } from "@/internal/products/prices/priceUtils/priceToInvoiceAmount.js";
|
||||
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
||||
import { getSubsFromCusId } from "tests/utils/expectUtils/expectSubUtils.js";
|
||||
|
||||
const testCase = "usage3";
|
||||
const ASSERT_INVOICE_AMOUNT = true;
|
||||
|
||||
// SECOND, UPGRADE TO GPU PRO MONTHLY
|
||||
describe(`${chalk.yellowBright(
|
||||
"usage3: upgrade from GPU starter monthly to GPU pro monthly",
|
||||
)}`, () => {
|
||||
const customerId = "usage3";
|
||||
let testClockId = "";
|
||||
let totalCreditsUsed = 0;
|
||||
let daysAdd1 = 15;
|
||||
// let daysAdd2 = differenceInDays(
|
||||
// addMonths(new Date(), 1),
|
||||
// addDays(new Date(), daysAdd1)
|
||||
// );
|
||||
let stripeCli: Stripe;
|
||||
let curUnix = 0;
|
||||
|
||||
before(async function () {
|
||||
let { testClockId: insertedTestClockId } = await initCustomerWithTestClock({
|
||||
await setupBefore(this);
|
||||
let { testClockId: insertedTestClockId } = await initCustomer({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
testClockId = insertedTestClockId;
|
||||
stripeCli = this.stripeCli;
|
||||
});
|
||||
|
||||
// 1. Attach GPU starter monthly
|
||||
it("usage3: should attach GPU starter monthly", async function () {
|
||||
const res = await AutumnCli.attach({
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuSystemStarter.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
});
|
||||
|
||||
// 2. Send 20 events
|
||||
it("usage3: should send 20 events", async function () {
|
||||
this.timeout(30000);
|
||||
// console.log(" Sending 20 events");
|
||||
|
||||
let eventCount = 20;
|
||||
const { creditsUsed } = await sendGPUEvents({
|
||||
customerId,
|
||||
eventCount,
|
||||
});
|
||||
|
||||
console.log(" - Total credits used: ", creditsUsed);
|
||||
totalCreditsUsed = creditsUsed;
|
||||
});
|
||||
|
||||
// 3. Advance test clock by 15 days and upgrade
|
||||
it("usage3: should advance test clock by 15 days and upgrade to GPU pro monthly", async function () {
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
await advanceTestClock({
|
||||
it("should advance test clock by 15 days and upgrade to GPU pro monthly", async function () {
|
||||
curUnix = await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
numberOfDays: 15,
|
||||
@@ -84,12 +81,7 @@ describe(`${chalk.yellowBright(
|
||||
productId: advanceProducts.gpuSystemPro.id,
|
||||
});
|
||||
|
||||
await timeout(3000);
|
||||
});
|
||||
|
||||
// 4. Check product attached
|
||||
it("usage3: should have correct product attached (GPU pro monthly)", async function () {
|
||||
this.timeout(30000);
|
||||
// MAKE SURE STRIPE SUB ONLY HAS GPU PRO
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
@@ -97,10 +89,7 @@ describe(`${chalk.yellowBright(
|
||||
cusRes: res,
|
||||
});
|
||||
|
||||
// MAKE SURE STRIPE SUB ONLY HAS GPU PRO
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
let subscriptionId = res.products[0].subscription_ids![0]!;
|
||||
|
||||
await checkSubscriptionContainsProducts({
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
@@ -110,82 +99,48 @@ describe(`${chalk.yellowBright(
|
||||
});
|
||||
});
|
||||
|
||||
// 5. Check invoice for 15 days of starter usage
|
||||
it("usage3: should have invoice for 15 days of starter usage", async function () {
|
||||
// 4. Check invoice for 15 days of starter usage
|
||||
it("should have invoice for 15 days of starter usage", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const invoices = res!.invoices;
|
||||
|
||||
let invoiceIndex = invoices.findIndex((invoice: any) =>
|
||||
invoice.product_ids.includes(advanceProducts.gpuSystemStarter.id),
|
||||
);
|
||||
let basePrice1 = advanceProducts.gpuSystemStarter.prices[0].config.amount;
|
||||
let basePrice2 = advanceProducts.gpuSystemPro.prices[0].config.amount;
|
||||
|
||||
// console.log("Total usage: ", totalCreditsUsed);
|
||||
|
||||
await checkUsageInvoiceAmount({
|
||||
invoices,
|
||||
totalUsage: totalCreditsUsed,
|
||||
product: advanceProducts.gpuSystemStarter,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
invoiceIndex,
|
||||
includeBase: false,
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
// 6. Advance another 15 days and check invoice for pro usage
|
||||
it("usage3: should 20 send events (GPU pro monthly)", async function () {
|
||||
let eventCount = 20;
|
||||
const { creditsUsed } = await sendGPUEvents({
|
||||
let { subs } = await getSubsFromCusId({
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
customerId,
|
||||
eventCount,
|
||||
});
|
||||
|
||||
totalCreditsUsed = creditsUsed;
|
||||
console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
|
||||
// Check entitled
|
||||
const { allowed, balanceObj }: any = await AutumnCli.entitled(
|
||||
customerId,
|
||||
creditSystems.gpuCredits.id,
|
||||
true,
|
||||
);
|
||||
|
||||
let proAllowance =
|
||||
advanceProducts.gpuSystemPro.entitlements.gpuCredits.allowance!;
|
||||
try {
|
||||
assert.equal(allowed, true);
|
||||
assert.equal(
|
||||
balanceObj.balance,
|
||||
new Decimal(proAllowance).minus(totalCreditsUsed).toNumber(),
|
||||
);
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
console.log(" - Pro allowance: ", proAllowance);
|
||||
console.log(" - Balance: ", balanceObj.balance);
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 7. Advance another 15 days and check invoice for pro usage
|
||||
it("usage3: should have invoice for 15 days of pro usage", async function () {
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
await advanceClockForInvoice({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
waitForMeterUpdate: ASSERT_INVOICE_AMOUNT,
|
||||
productId: advanceProducts.gpuSystemPro.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const invoices = res!.invoices;
|
||||
let sub = subs[0];
|
||||
|
||||
await checkUsageInvoiceAmount({
|
||||
invoices,
|
||||
totalUsage: totalCreditsUsed,
|
||||
product: advanceProducts.gpuSystemPro,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
let baseDiff = calculateProrationAmount({
|
||||
periodStart: sub.current_period_start * 1000,
|
||||
periodEnd: sub.current_period_end * 1000,
|
||||
now: curUnix,
|
||||
amount: basePrice2 - basePrice1,
|
||||
allowNegative: true,
|
||||
});
|
||||
|
||||
let usagePrice = advanceProducts.gpuSystemStarter.prices[1];
|
||||
let overage =
|
||||
totalCreditsUsed -
|
||||
advanceProducts.gpuSystemStarter.entitlements.gpuCredits.allowance!;
|
||||
|
||||
let overagePrice = priceToInvoiceAmount({
|
||||
price: usagePrice,
|
||||
overage,
|
||||
});
|
||||
|
||||
let calculatedTotal = new Decimal(baseDiff)
|
||||
.plus(overagePrice)
|
||||
.toDecimalPlaces(2)
|
||||
.toNumber();
|
||||
|
||||
expect(invoices[0].total).to.equal(calculatedTotal);
|
||||
});
|
||||
});
|
||||
|
||||
175
server/tests/advanced/usage/usage4.ts
Normal file
175
server/tests/advanced/usage/usage4.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import { Customer } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { compareMainProduct } from "../../utils/compare.js";
|
||||
import { AutumnCli } from "../../cli/AutumnCli.js";
|
||||
import { advanceProducts, creditSystems } from "../../global.js";
|
||||
|
||||
import { expect } from "chai";
|
||||
import { advanceClockForInvoice } from "../../utils/stripeUtils.js";
|
||||
|
||||
import {
|
||||
sendGPUEvents,
|
||||
checkCreditBalance,
|
||||
checkUsageInvoiceAmount,
|
||||
} from "../../utils/advancedUsageUtils.js";
|
||||
import Stripe from "stripe";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
|
||||
// THIRD, TEST GPU PRO ANNUAL
|
||||
|
||||
const testCase = "usage4";
|
||||
|
||||
describe(`${chalk.yellowBright("usage4: GPU starter annual")}`, () => {
|
||||
const customerId = testCase;
|
||||
let totalCreditsUsed = 0;
|
||||
|
||||
let testClockId = "";
|
||||
let customer: Customer;
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
let res = await initCustomer({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
testClockId = res.testClockId;
|
||||
customer = res.customer;
|
||||
stripeCli = this.stripeCli;
|
||||
});
|
||||
|
||||
it("should attach GPU starter annual", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuStarterAnnual.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuStarterAnnual,
|
||||
cusRes: res,
|
||||
});
|
||||
|
||||
expect(res!.invoices.length).to.equal(2);
|
||||
});
|
||||
|
||||
it("should send 20 events and have correct balance", async function () {
|
||||
let eventCount = 20;
|
||||
const { creditsUsed } = await sendGPUEvents({
|
||||
customerId,
|
||||
eventCount,
|
||||
});
|
||||
|
||||
totalCreditsUsed = creditsUsed;
|
||||
await checkCreditBalance({
|
||||
customerId,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
totalCreditsUsed,
|
||||
originalAllowance:
|
||||
advanceProducts.gpuStarterAnnual.entitlements.gpuCredits.allowance!,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have invoice after a month and correct balance", async function () {
|
||||
await advanceClockForInvoice({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
waitForMeterUpdate: true,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const invoices = res!.invoices;
|
||||
|
||||
let invoiceIndex = invoices.findIndex((invoice: any) =>
|
||||
invoice.product_ids.includes(advanceProducts.gpuStarterAnnual.id),
|
||||
);
|
||||
|
||||
await checkUsageInvoiceAmount({
|
||||
invoices,
|
||||
totalUsage: totalCreditsUsed,
|
||||
product: advanceProducts.gpuStarterAnnual,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
invoiceIndex,
|
||||
includeBase: false,
|
||||
});
|
||||
|
||||
await checkCreditBalance({
|
||||
customerId,
|
||||
featureId: creditSystems.gpuCredits.id,
|
||||
totalCreditsUsed: 0,
|
||||
originalAllowance:
|
||||
advanceProducts.gpuStarterAnnual.entitlements.gpuCredits.allowance!,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// // Advance by 1 year and check if latest invoice is correct
|
||||
// it.skip("should have correct invoice after 1 year", async function () {
|
||||
// const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
|
||||
// // 1. Advance by 11 months
|
||||
// let numberOfMonths = 11;
|
||||
// await advanceMonths({
|
||||
// stripeCli,
|
||||
// testClockId,
|
||||
// numberOfMonths,
|
||||
// });
|
||||
|
||||
// // 2. Send 20 events
|
||||
// let eventCount = 20;
|
||||
// const { creditsUsed } = await sendGPUEvents({
|
||||
// customerId,
|
||||
// eventCount,
|
||||
// });
|
||||
|
||||
// let totalCreditsUsed = creditsUsed;
|
||||
// console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
|
||||
// // Advance by a month and check for usage
|
||||
// await advanceClockForInvoice({
|
||||
// stripeCli,
|
||||
// testClockId,
|
||||
// waitForMeterUpdate: true,
|
||||
// startingFrom: addMonths(new Date(), numberOfMonths),
|
||||
// });
|
||||
|
||||
// const res = await AutumnCli.getCustomer(customerId);
|
||||
// const invoices = res!.invoices;
|
||||
|
||||
// let usagePrice = await getUsageInArrearPrice({
|
||||
// org: this.org,
|
||||
// sb: this.sb,
|
||||
// env: this.env,
|
||||
// productId: advanceProducts.gpuStarterAnnual.id,
|
||||
// });
|
||||
|
||||
// // Get billing meter event summary
|
||||
// let eventSummary = await checkBillingMeterEventSummary({
|
||||
// stripeCli,
|
||||
// startTime: addMonths(new Date(), 11),
|
||||
// stripeMeterId: usagePrice?.config?.stripe_meter_id,
|
||||
// stripeCustomerId: customer.processor.id,
|
||||
// });
|
||||
|
||||
// try {
|
||||
// assert.exists(eventSummary);
|
||||
// assert.equal(
|
||||
// eventSummary?.aggregated_value,
|
||||
// Math.round(totalCreditsUsed),
|
||||
// );
|
||||
// assert.equal(invoices.length, 13 + 2);
|
||||
// } catch (error) {
|
||||
// console.group();
|
||||
// console.log(" - Event summary: ", eventSummary);
|
||||
// console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
// console.log(" - Last 3 invoices: ", invoices.slice(-3));
|
||||
// console.groupEnd();
|
||||
// throw error;
|
||||
// }
|
||||
// });
|
||||
@@ -173,35 +173,35 @@ describe(chalk.yellowBright("05_cancel"), () => {
|
||||
await timeout(5000);
|
||||
});
|
||||
|
||||
// TODO: Edit so that it doesn't auto cancel for unit test org
|
||||
it("should have expired / past_dued pro product", async function () {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
// // TODO: Edit so that it doesn't auto cancel for unit test org
|
||||
// it("should have expired / past_dued pro product", async function () {
|
||||
// const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
let org = this.org;
|
||||
// let org = this.org;
|
||||
|
||||
console.log("Cancel on past due:", org.config.cancel_on_past_due);
|
||||
if (org.config.cancel_on_past_due) {
|
||||
const proProduct = getProductFromCusRes({
|
||||
cusRes,
|
||||
productId: alexProducts.pro.id,
|
||||
});
|
||||
// console.log("Cancel on past due:", org.config.cancel_on_past_due);
|
||||
// if (org.config.cancel_on_past_due) {
|
||||
// const proProduct = getProductFromCusRes({
|
||||
// cusRes,
|
||||
// productId: alexProducts.pro.id,
|
||||
// });
|
||||
|
||||
expect(proProduct).to.not.exist;
|
||||
// expect(proProduct).to.not.exist;
|
||||
|
||||
const freeProduct = getProductFromCusRes({
|
||||
cusRes,
|
||||
productId: alexProducts.free.id,
|
||||
});
|
||||
// const freeProduct = getProductFromCusRes({
|
||||
// cusRes,
|
||||
// productId: alexProducts.free.id,
|
||||
// });
|
||||
|
||||
expect(freeProduct).to.exist;
|
||||
expect(freeProduct.status).to.equal(CusProductStatus.Active);
|
||||
} else {
|
||||
compareMainProduct({
|
||||
sent: alexProducts.pro,
|
||||
cusRes,
|
||||
status: CusProductStatus.PastDue,
|
||||
});
|
||||
}
|
||||
});
|
||||
// expect(freeProduct).to.exist;
|
||||
// expect(freeProduct.status).to.equal(CusProductStatus.Active);
|
||||
// } else {
|
||||
// compareMainProduct({
|
||||
// sent: alexProducts.pro,
|
||||
// cusRes,
|
||||
// status: CusProductStatus.PastDue,
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
||||
210
server/tests/archives/03_cancel.ts
Normal file
210
server/tests/archives/03_cancel.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
// import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
// import { AutumnCli } from "../cli/AutumnCli.js";
|
||||
// import { features, products } from "../global.js";
|
||||
// import { initCustomer } from "../utils/init.js";
|
||||
// import { timeout } from "../utils/genUtils.js";
|
||||
// import chalk from "chalk";
|
||||
// import {
|
||||
// checkFeatureHasCorrectBalance,
|
||||
// compareMainProduct,
|
||||
// } from "../utils/compare.js";
|
||||
// import { expect } from "chai";
|
||||
// import {
|
||||
// advanceTestClock,
|
||||
// completeCheckoutForm,
|
||||
// } from "../utils/stripeUtils.js";
|
||||
// import { CusProductStatus } from "@autumn/shared";
|
||||
// import { attachFailedPaymentMethod } from "@/external/stripe/stripeCusUtils.js";
|
||||
// import { addDays, addMonths } from "date-fns";
|
||||
|
||||
// describe(`${chalk.yellowBright(
|
||||
// "03_cancel: Testing cancel (at period end and now)",
|
||||
// )}`, () => {
|
||||
// const customerId = "cancelCustomer";
|
||||
|
||||
// before(async function () {
|
||||
// this.timeout(30000);
|
||||
|
||||
// await initCustomer({
|
||||
// customer_data: {
|
||||
// id: customerId,
|
||||
// name: "Test Customer",
|
||||
// email: "test@test.com",
|
||||
// },
|
||||
// db: this.db,
|
||||
// org: this.org,
|
||||
// env: this.env,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should attach pro product", async function () {
|
||||
// this.timeout(30000);
|
||||
|
||||
// const res: any = await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: products.pro.id,
|
||||
// });
|
||||
|
||||
// await completeCheckoutForm(res.checkout_url);
|
||||
// await timeout(5000);
|
||||
// console.log(` ${chalk.greenBright("Attached pro product")}`);
|
||||
// });
|
||||
|
||||
// // 1. Cancel pro product
|
||||
// it("should cancel pro product (at period end)", async function () {
|
||||
// this.timeout(10000);
|
||||
|
||||
// const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
|
||||
// // 1. Cancel pro product
|
||||
// const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
// const proProduct = cusRes.products.find(
|
||||
// (p: any) => p.id === products.pro.id,
|
||||
// );
|
||||
|
||||
// for (const subId of proProduct.subscription_ids) {
|
||||
// await stripeCli.subscriptions.update(subId, {
|
||||
// cancel_at_period_end: true,
|
||||
// });
|
||||
// }
|
||||
|
||||
// await timeout(3000);
|
||||
// console.log(` ${chalk.greenBright("Cancelled pro product")}`);
|
||||
// });
|
||||
|
||||
// it("should have pro product active, and canceled_at != null", async function () {
|
||||
// this.timeout(10000);
|
||||
|
||||
// const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
// compareMainProduct({
|
||||
// sent: products.pro,
|
||||
// cusRes: cusRes,
|
||||
// });
|
||||
|
||||
// const proProduct = cusRes.products.find(
|
||||
// (p: any) => p.id === products.pro.id,
|
||||
// );
|
||||
// expect(proProduct.canceled_at).to.not.equal(null);
|
||||
// expect(proProduct.status).to.equal(CusProductStatus.Active);
|
||||
// });
|
||||
|
||||
// // CANCEL SUB NOW, SUBSCRIPTION.DELETED WEBHOOK
|
||||
// it("should cancel pro product (now)", async function () {
|
||||
// this.timeout(10000);
|
||||
|
||||
// const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
|
||||
// const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
// const proProduct = cusRes.products.find(
|
||||
// (p: any) => p.id === products.pro.id,
|
||||
// );
|
||||
|
||||
// for (const subId of proProduct.subscription_ids) {
|
||||
// await stripeCli.subscriptions.cancel(subId);
|
||||
// }
|
||||
|
||||
// await timeout(3000);
|
||||
// console.log(` ${chalk.greenBright("Cancelled pro product now")}`);
|
||||
// });
|
||||
|
||||
// it("should have free product active, and pro product not returned", async function () {
|
||||
// this.timeout(10000);
|
||||
|
||||
// const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
// compareMainProduct({
|
||||
// sent: products.free,
|
||||
// cusRes: cusRes,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should have correct entitlements (for free)", async function () {
|
||||
// for (const entitlement of Object.values(products.free.entitlements)) {
|
||||
// let feature = features[entitlement.feature_id!];
|
||||
// await checkFeatureHasCorrectBalance({
|
||||
// customerId,
|
||||
// feature: feature,
|
||||
// entitlement,
|
||||
// expectedBalance: entitlement.allowance || 0,
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe(`${chalk.yellowBright(
|
||||
// "03_cancel: Testing subscription past_due",
|
||||
// )}`, () => {
|
||||
// const customerId = "03_cancel_past_due";
|
||||
|
||||
// before(async function () {
|
||||
// this.timeout(30000);
|
||||
|
||||
// const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
|
||||
// const testClock = await stripeCli.testHelpers.testClocks.create({
|
||||
// frozen_time: Math.round(Date.now() / 1000),
|
||||
// });
|
||||
|
||||
// this.testClockId = testClock.id;
|
||||
|
||||
// await initCustomer({
|
||||
// customer_data: {
|
||||
// id: customerId,
|
||||
// name: "Test Customer",
|
||||
// email: "test@test.com",
|
||||
// },
|
||||
// db: this.db,
|
||||
// org: this.org,
|
||||
// env: this.env,
|
||||
// attachPm: true,
|
||||
// testClockId: testClock.id,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should attach pro product", async function () {
|
||||
// this.timeout(10000);
|
||||
|
||||
// await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: products.pro.id,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should attach failed payment method and advance to next billing date", async function () {
|
||||
// // 1. Swap customer's card
|
||||
// const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
|
||||
// const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
// await attachFailedPaymentMethod({
|
||||
// stripeCli,
|
||||
// customer: cusRes.customer,
|
||||
// });
|
||||
|
||||
// // const advanceDate = addDays(addMonths(new Date(), 1), 1);
|
||||
// // await stripeCli.testHelpers.testClocks.advance(this.testClockId, {
|
||||
// // frozen_time: Math.round(advanceDate.getTime() / 1000),
|
||||
// // });
|
||||
|
||||
// await advanceTestClock({
|
||||
// stripeCli,
|
||||
// testClockId: this.testClockId,
|
||||
// advanceTo: addDays(addMonths(new Date(), 1), 1).getTime(),
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should have free product active and correct entitlements", async function () {
|
||||
// const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
// // compareMainProduct({
|
||||
// // sent: products.free,
|
||||
// // cusRes: cusRes,
|
||||
// // });
|
||||
|
||||
// // TODO: Check why this line messes up the test
|
||||
// // compareProductEntitlements({
|
||||
// // customerId,
|
||||
// // product: products.free,
|
||||
// // features,
|
||||
// // });
|
||||
// });
|
||||
// });
|
||||
@@ -9,7 +9,7 @@ import { compareMainProduct } from "../utils/compare.js";
|
||||
import { AutumnCli } from "../cli/AutumnCli.js";
|
||||
import chalk from "chalk";
|
||||
|
||||
describe(`${chalk.yellowBright("Testing pkey")}`, () => {
|
||||
describe(`${chalk.yellowBright("08_pkey: Testing publishable key")}`, () => {
|
||||
// 1. Initialize customer with card
|
||||
let customerId = "pkeyTestCustomer";
|
||||
const bearerPublicAxios = getPublicAxiosInstance({
|
||||
@@ -99,8 +99,6 @@ describe(`${chalk.yellowBright("Testing pkey")}`, () => {
|
||||
});
|
||||
|
||||
it("should return error if try to downgrade to free", async function () {
|
||||
this.timeout(30000);
|
||||
|
||||
try {
|
||||
await bearerPublicAxios.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
@@ -116,7 +114,6 @@ describe(`${chalk.yellowBright("Testing pkey")}`, () => {
|
||||
|
||||
// Next, check entitled for pro
|
||||
it("should return correct metered1 amount for pro", async function () {
|
||||
this.timeout(30000);
|
||||
const { data } = await bearerPublicAxios.post("/v1/entitled", {
|
||||
customer_id: customerId,
|
||||
feature_id: features.metered1.id,
|
||||
@@ -133,7 +130,6 @@ describe(`${chalk.yellowBright("Testing pkey")}`, () => {
|
||||
});
|
||||
|
||||
it("should return same balance for entitled with bearer and x-publishable-key", async function () {
|
||||
this.timeout(30000);
|
||||
const { data } = await bearerPublicAxios.post("/v1/entitled", {
|
||||
customer_id: customerId,
|
||||
feature_id: features.metered1.id,
|
||||
@@ -150,7 +146,6 @@ describe(`${chalk.yellowBright("Testing pkey")}`, () => {
|
||||
});
|
||||
|
||||
it("should return error when try to send event", async function () {
|
||||
this.timeout(30000);
|
||||
try {
|
||||
await bearerPublicAxios.post("/v1/events", {
|
||||
customer_id: customerId,
|
||||
@@ -275,7 +275,4 @@ describe(`${chalk.yellowBright(
|
||||
startingBalance: balance,
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Test reset at for in arrear prorated with Ent Interval = Lifetime
|
||||
// TODO: Test in arrear prorated for entitlements with billing units > 1
|
||||
});
|
||||
188
server/tests/archives/coupon1 copy.ts
Normal file
188
server/tests/archives/coupon1 copy.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import chalk from "chalk";
|
||||
import Stripe from "stripe";
|
||||
import { APIVersion, Customer } from "@autumn/shared";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||
import { getPriceForOverage } from "@/internal/products/prices/priceUtils.js";
|
||||
import { expect } from "chai";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import { features, products, rewards } from "tests/global.js";
|
||||
import { getFixedPriceAmount, timeout } from "tests/utils/genUtils.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
|
||||
import {
|
||||
advanceClockForInvoice,
|
||||
advanceTestClock,
|
||||
completeCheckoutForm,
|
||||
getDiscount,
|
||||
} from "tests/utils/stripeUtils.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
|
||||
const testCase = "coupon1";
|
||||
|
||||
describe(
|
||||
chalk.yellow(`${testCase} -- Testing one-off rollover, apply to all`),
|
||||
() => {
|
||||
let customerId = "coupon1";
|
||||
let stripeCli: Stripe;
|
||||
let customer: Customer;
|
||||
let testClockId: string;
|
||||
let db, org, env;
|
||||
|
||||
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
|
||||
|
||||
let couponAmount = rewards.rolloverAll.discount_config.discount_value;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
autumn = this.autumnJs;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const res = await initCustomer({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn: this.autumnJs,
|
||||
});
|
||||
|
||||
testClockId = res.testClockId;
|
||||
customer = res.customer;
|
||||
});
|
||||
|
||||
// CYCLE 0
|
||||
it("CYCLE 0: should attach pro with overage (through checkout)", async () => {
|
||||
couponAmount -= getFixedPriceAmount(products.proWithOverage);
|
||||
|
||||
const res = await AutumnCli.attach({
|
||||
customerId,
|
||||
productId: products.proWithOverage.id,
|
||||
forceCheckout: true,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(
|
||||
res.checkout_url,
|
||||
undefined,
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
|
||||
await timeout(20000);
|
||||
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.proWithOverage,
|
||||
cusRes,
|
||||
});
|
||||
});
|
||||
|
||||
it("CYCLE 0: should have $0 invoice and correct remaining coupon amount", async () => {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
expect(cusRes.invoices[0].total).to.equal(0);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
customer: cusRes.customer,
|
||||
});
|
||||
|
||||
try {
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
|
||||
// Expect amount to be original amount - pro price
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
||||
} catch (error) {
|
||||
console.error("--------------------------------");
|
||||
console.error(
|
||||
"Expected stripe cus to have coupon",
|
||||
rewards.rolloverAll,
|
||||
);
|
||||
console.error("Actual stripe cus discount", cusDiscount);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// CYCLE 1
|
||||
it("CYCLE 1: should set usage to -100 and advance clock by 1 month", async () => {
|
||||
const usage = 100;
|
||||
const res = await AutumnCli.usage({
|
||||
customerId,
|
||||
featureId: features.metered1.id,
|
||||
value: usage,
|
||||
});
|
||||
|
||||
// Price
|
||||
const price = getPriceForOverage(
|
||||
products.proWithOverage.prices[1],
|
||||
-(products.proWithOverage.entitlements.metered1.allowance! - usage),
|
||||
);
|
||||
|
||||
couponAmount =
|
||||
couponAmount - (price + getFixedPriceAmount(products.proWithOverage));
|
||||
|
||||
await advanceClockForInvoice({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
waitForMeterUpdate: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("CYCLE 1: should have $0 invoice and correct new coupon amount", async () => {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
expect(cusRes.invoices[0].total).to.equal(0);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
customer,
|
||||
});
|
||||
|
||||
try {
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
||||
} catch (error) {
|
||||
console.log("--------------------------------");
|
||||
console.log("coupon1, cycle 1 failed");
|
||||
console.log("Expected stripe cus to have coupon", rewards.rolloverAll);
|
||||
console.log("Actual stripe cus discount", cusDiscount);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// CYCLE 2
|
||||
it("CYCLE 2: should have $0 invoice and correct new coupon amount after 2nd cycle", async () => {
|
||||
await timeout(20000);
|
||||
let advanceTo = addHours(addMonths(new Date(), 2), 2);
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: advanceTo.getTime(),
|
||||
});
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
customer,
|
||||
});
|
||||
|
||||
const newCouponAmount =
|
||||
couponAmount - getFixedPriceAmount(products.proWithOverage);
|
||||
|
||||
try {
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(newCouponAmount * 100);
|
||||
} catch (error) {
|
||||
console.log("--------------------------------");
|
||||
console.log("coupon1, cycle 2 failed");
|
||||
console.log("Expected coupon amount", newCouponAmount * 100);
|
||||
console.log("Stripe cus discount", cusDiscount);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
381
server/tests/archives/entities1.ts
Normal file
381
server/tests/archives/entities1.ts
Normal file
@@ -0,0 +1,381 @@
|
||||
// import { Autumn } from "@/external/autumn/autumnCli.js";
|
||||
// import { setupBefore } from "tests/before.js";
|
||||
// import { CusProductStatus, organizations } from "@autumn/shared";
|
||||
// import { getFeaturePrice, getUsagePriceTiers } from "tests/utils/genUtils.js";
|
||||
// import { entityProducts, features } from "../global.js";
|
||||
// import { Stripe } from "stripe";
|
||||
// import { checkBalance } from "tests/utils/autumnUtils.js";
|
||||
// import { initCustomerWithTestClock } from "tests/utils/testInitUtils.js";
|
||||
// import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
// import { addDays, addHours, addMonths } from "date-fns";
|
||||
// import { CacheManager } from "@/external/caching/CacheManager.js";
|
||||
// import { CacheType } from "@/external/caching/cacheActions.js";
|
||||
// import { hashApiKey } from "@/internal/dev/api-keys/apiKeyUtils.js";
|
||||
// import { compareMainProduct } from "../utils/compare.js";
|
||||
// import { assert, expect } from "chai";
|
||||
|
||||
// import chalk from "chalk";
|
||||
// import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
// import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
|
||||
// import { eq } from "drizzle-orm";
|
||||
|
||||
// // Check balance and stripe quantity
|
||||
// const checkEntAndStripeQuantity = async ({
|
||||
// db,
|
||||
// autumn,
|
||||
// stripeCli,
|
||||
// featureId,
|
||||
// customerId,
|
||||
// expectedBalance,
|
||||
// expectedUsage,
|
||||
// expectedStripeQuantity,
|
||||
// }: {
|
||||
// db: DrizzleCli;
|
||||
// autumn: Autumn;
|
||||
// stripeCli: Stripe;
|
||||
// featureId: string;
|
||||
// customerId: string;
|
||||
// expectedBalance: number;
|
||||
// expectedUsage?: number;
|
||||
// expectedStripeQuantity: number;
|
||||
// }) => {
|
||||
// let { customer, entitlements, products } =
|
||||
// await autumn.customers.get(customerId);
|
||||
|
||||
// let cusProducts = await CusProductService.list({
|
||||
// db,
|
||||
// internalCustomerId: customer.internal_id,
|
||||
// inStatuses: [CusProductStatus.Active],
|
||||
// });
|
||||
|
||||
// let entitlement = entitlements.find((e: any) => e.feature_id == featureId);
|
||||
|
||||
// expect(entitlement.balance).to.equal(expectedBalance);
|
||||
// if (expectedUsage) {
|
||||
// expect(entitlement.used).to.equal(
|
||||
// expectedUsage,
|
||||
// `Get customer ${customerId} returned incorrect "used" for feature ${featureId}`,
|
||||
// );
|
||||
// }
|
||||
|
||||
// if (products.length == 0) {
|
||||
// assert.fail(`Get customer ${customerId} returned no products`);
|
||||
// }
|
||||
|
||||
// // 2. Get stripe quantity
|
||||
// let mainProduct = products[0];
|
||||
|
||||
// if (mainProduct.subscription_ids.length == 0) {
|
||||
// assert.fail(`Get customer ${customerId} returned no subscriptions`);
|
||||
// }
|
||||
|
||||
// let price = getFeaturePrice({
|
||||
// product: mainProduct,
|
||||
// featureId: featureId,
|
||||
// cusProducts,
|
||||
// });
|
||||
|
||||
// if (!price) {
|
||||
// assert.fail(
|
||||
// `Get customer ${customerId} returned no price for feature ${featureId}`,
|
||||
// );
|
||||
// }
|
||||
|
||||
// let stripeSub = await stripeCli.subscriptions.retrieve(
|
||||
// mainProduct.subscription_ids[0],
|
||||
// );
|
||||
// let subItem = stripeSub.items.data.find(
|
||||
// (item: any) => item.price.id == price.config!.stripe_price_id,
|
||||
// );
|
||||
|
||||
// if (!subItem) {
|
||||
// assert.fail(
|
||||
// `Get customer ${customerId} returned no sub item for feature ${featureId}`,
|
||||
// );
|
||||
// }
|
||||
|
||||
// expect(subItem.quantity).to.equal(
|
||||
// expectedStripeQuantity,
|
||||
// `Get customer ${customerId} returned incorrect stripe quantity for feature ${featureId}`,
|
||||
// );
|
||||
// };
|
||||
|
||||
// // UNCOMMENT FROM HERE
|
||||
// describe(`${chalk.yellowBright("entities1: Testing entities")}`, () => {
|
||||
// let customerId = "entity1";
|
||||
// let autumn: Autumn;
|
||||
// let stripeCli: Stripe;
|
||||
// let usageTiers = getUsagePriceTiers({
|
||||
// product: entityProducts.entityPro,
|
||||
// featureId: features.seats.id,
|
||||
// });
|
||||
// let testClockId: string;
|
||||
|
||||
// before(async function () {
|
||||
// await setupBefore(this);
|
||||
// autumn = this.autumn;
|
||||
// stripeCli = this.stripeCli;
|
||||
|
||||
// const { testClockId: testClockId1 } = await initCustomerWithTestClock({
|
||||
// customerId,
|
||||
// db: this.db,
|
||||
// org: this.org,
|
||||
// env: this.env,
|
||||
// });
|
||||
|
||||
// testClockId = testClockId1;
|
||||
|
||||
// // Update org config
|
||||
// await this.db
|
||||
// .update(organizations)
|
||||
// .set({
|
||||
// config: {
|
||||
// ...this.org.config,
|
||||
// prorate_unused: false,
|
||||
// },
|
||||
// })
|
||||
// .where(eq(organizations.id, this.org.id));
|
||||
|
||||
// await CacheManager.invalidate({
|
||||
// action: CacheType.SecretKey,
|
||||
// value: hashApiKey(process.env.UNIT_TEST_AUTUMN_SECRET_KEY!),
|
||||
// });
|
||||
// await CacheManager.disconnect();
|
||||
// });
|
||||
|
||||
// let firstEntityId = "1";
|
||||
|
||||
// it("should attach entityFree product", async function () {
|
||||
// await this.autumn.attach({
|
||||
// customerId,
|
||||
// productId: entityProducts.entityFree.id,
|
||||
// });
|
||||
|
||||
// await this.autumn.entities.create(customerId, {
|
||||
// id: firstEntityId,
|
||||
// name: "1@gmail.com",
|
||||
// featureId: features.seats.id,
|
||||
// });
|
||||
|
||||
// // Check if entity is created
|
||||
// let res = await this.autumn.entities.list(customerId);
|
||||
// let entities = res.data;
|
||||
|
||||
// expect(entities).to.have.lengthOf(1);
|
||||
// expect(entities[0].id).to.equal(firstEntityId);
|
||||
// });
|
||||
|
||||
// it("should successfully remove created entity", async function () {
|
||||
// await this.autumn.entities.delete(customerId, firstEntityId);
|
||||
|
||||
// let res = await this.autumn.entities.list(customerId);
|
||||
// let entities = res.data;
|
||||
// expect(entities).to.have.lengthOf(0);
|
||||
// });
|
||||
|
||||
// it("should create first entity, then attach entityPro product", async function () {
|
||||
// await this.autumn.entities.create(customerId, {
|
||||
// id: firstEntityId,
|
||||
// name: "1@gmail.com",
|
||||
// featureId: features.seats.id,
|
||||
// });
|
||||
|
||||
// await this.autumn.attach({
|
||||
// customerId,
|
||||
// productId: entityProducts.entityPro.id,
|
||||
// });
|
||||
|
||||
// // Check product is attached correctly
|
||||
// let cusRes = await autumn.customers.get(customerId);
|
||||
// compareMainProduct({
|
||||
// sent: entityProducts.entityPro,
|
||||
// cusRes: cusRes,
|
||||
// });
|
||||
|
||||
// let { invoices } = cusRes;
|
||||
// let usageTiers = getUsagePriceTiers({
|
||||
// product: entityProducts.entityPro,
|
||||
// featureId: features.seats.id,
|
||||
// });
|
||||
|
||||
// // Check invoice is created correctly
|
||||
// expect(invoices).to.have.lengthOf(1);
|
||||
// expect(invoices[0].total).to.equal(usageTiers[0].amount);
|
||||
|
||||
// // Check balance and stripe quantity
|
||||
// await checkEntAndStripeQuantity({
|
||||
// db: this.db,
|
||||
// autumn,
|
||||
// stripeCli,
|
||||
// featureId: features.seats.id,
|
||||
// customerId,
|
||||
// expectedBalance: -1,
|
||||
// expectedStripeQuantity: 1,
|
||||
// });
|
||||
|
||||
// await checkBalance({
|
||||
// autumn,
|
||||
// featureId: features.metered1.id,
|
||||
// customerId,
|
||||
// expectedBalance:
|
||||
// entityProducts.entityPro.entitlements.metered1.allowance!,
|
||||
// });
|
||||
// });
|
||||
|
||||
// let newEntities = [
|
||||
// {
|
||||
// id: "2",
|
||||
// name: "2@gmail.com",
|
||||
// featureId: features.seats.id,
|
||||
// },
|
||||
// {
|
||||
// id: "3",
|
||||
// name: "3@gmail.com",
|
||||
// featureId: features.seats.id,
|
||||
// },
|
||||
// {
|
||||
// id: "4",
|
||||
// name: "4@gmail.com",
|
||||
// featureId: features.seats.id,
|
||||
// },
|
||||
// ];
|
||||
|
||||
// it("should create 3 additional entities and be charged immediately", async function () {
|
||||
// let advanceToDay = addDays(new Date(), 1).getTime();
|
||||
// await advanceTestClock({
|
||||
// stripeCli,
|
||||
// testClockId,
|
||||
// advanceTo: advanceToDay,
|
||||
// waitForSeconds: 20,
|
||||
// });
|
||||
|
||||
// await this.autumn.entities.create(customerId, newEntities);
|
||||
|
||||
// let entitiesRes = await this.autumn.entities.list(customerId);
|
||||
// let entities = entitiesRes.data;
|
||||
// expect(entities).to.have.lengthOf(newEntities.length + 1);
|
||||
|
||||
// let cusRes = await autumn.customers.get(customerId);
|
||||
|
||||
// let { invoices } = cusRes;
|
||||
// expect(invoices[0].total).to.equal(
|
||||
// usageTiers[0].amount * newEntities.length,
|
||||
// );
|
||||
// });
|
||||
|
||||
// it("should remove 2 entities, and have correct balance / stripe quantity", async function () {
|
||||
// await this.autumn.entities.delete(customerId, newEntities[0].id);
|
||||
|
||||
// await checkEntAndStripeQuantity({
|
||||
// db: this.db,
|
||||
// autumn,
|
||||
// stripeCli,
|
||||
// featureId: features.seats.id,
|
||||
// customerId,
|
||||
// expectedBalance: -(newEntities.length + 1),
|
||||
// expectedStripeQuantity: newEntities.length + 1 - 1,
|
||||
// expectedUsage: newEntities.length + 1 - 1,
|
||||
// });
|
||||
|
||||
// await this.autumn.entities.delete(customerId, newEntities[1].id);
|
||||
// await checkEntAndStripeQuantity({
|
||||
// db: this.db,
|
||||
// autumn,
|
||||
// stripeCli,
|
||||
// featureId: features.seats.id,
|
||||
// customerId,
|
||||
// expectedBalance: -(newEntities.length + 1),
|
||||
// expectedStripeQuantity: newEntities.length + 1 - 2,
|
||||
// expectedUsage: newEntities.length + 1 - 2,
|
||||
// });
|
||||
// });
|
||||
|
||||
// let newEntities2 = [
|
||||
// {
|
||||
// id: "5",
|
||||
// name: "5@gmail.com",
|
||||
// featureId: features.seats.id,
|
||||
// },
|
||||
// {
|
||||
// id: "6",
|
||||
// name: "6@gmail.com",
|
||||
// featureId: features.seats.id,
|
||||
// },
|
||||
// {
|
||||
// id: "7",
|
||||
// name: "7@gmail.com",
|
||||
// featureId: features.seats.id,
|
||||
// },
|
||||
// ];
|
||||
|
||||
// it("should create three additional seats, and be charged for only one", async function () {
|
||||
// await this.autumn.entities.create(customerId, newEntities2);
|
||||
|
||||
// let totalSeats = newEntities2.length + 2;
|
||||
// await checkEntAndStripeQuantity({
|
||||
// db: this.db,
|
||||
// autumn,
|
||||
// stripeCli,
|
||||
// featureId: features.seats.id,
|
||||
// customerId,
|
||||
// expectedBalance: -totalSeats,
|
||||
// expectedStripeQuantity: totalSeats,
|
||||
// expectedUsage: totalSeats,
|
||||
// });
|
||||
|
||||
// let cusRes = await autumn.customers.get(customerId);
|
||||
// let { invoices } = cusRes;
|
||||
// expect(invoices[0].total).to.equal(usageTiers[0].amount);
|
||||
// });
|
||||
|
||||
// // return;
|
||||
|
||||
// it("should remove one entity, and have correct balance / stripe quantity after advancing test clock", async function () {
|
||||
// await this.autumn.entities.delete(customerId, newEntities2[0].id);
|
||||
|
||||
// let totalSeats = newEntities2.length + 1;
|
||||
|
||||
// let advanceTo = addHours(addMonths(new Date(), 1), 4).getTime();
|
||||
// await advanceTestClock({ stripeCli, testClockId, advanceTo });
|
||||
|
||||
// // Get entities
|
||||
// let { data: entities } = await this.autumn.entities.list(customerId);
|
||||
// expect(entities).to.have.lengthOf(totalSeats);
|
||||
|
||||
// await checkEntAndStripeQuantity({
|
||||
// db: this.db,
|
||||
// autumn,
|
||||
// stripeCli,
|
||||
// featureId: features.seats.id,
|
||||
// customerId,
|
||||
// expectedBalance: -totalSeats,
|
||||
// expectedStripeQuantity: totalSeats,
|
||||
// expectedUsage: totalSeats,
|
||||
// });
|
||||
|
||||
// await checkBalance({
|
||||
// autumn,
|
||||
// featureId: features.metered1.id,
|
||||
// customerId,
|
||||
// expectedBalance:
|
||||
// totalSeats * entityProducts.entityPro.entitlements.metered1.allowance!,
|
||||
// });
|
||||
// });
|
||||
|
||||
// after(async function () {
|
||||
// await this.db
|
||||
// .update(organizations)
|
||||
// .set({
|
||||
// config: {
|
||||
// ...this.org.config,
|
||||
// prorate_unused: true,
|
||||
// },
|
||||
// })
|
||||
// .where(eq(organizations.id, this.org.id));
|
||||
|
||||
// void CacheManager.invalidate({
|
||||
// action: CacheType.SecretKey,
|
||||
// value: hashApiKey(process.env.UNIT_TEST_AUTUMN_SECRET_KEY!),
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
199
server/tests/archives/multi_interval2.ts
Normal file
199
server/tests/archives/multi_interval2.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
// THIS TEST CASE IS COVERED UNDER UPGRADE2.TS
|
||||
|
||||
// import { Customer } from "@autumn/shared";
|
||||
// import chalk from "chalk";
|
||||
// import { compareMainProduct } from "../../utils/compare.js";
|
||||
// import { AutumnCli } from "../../cli/AutumnCli.js";
|
||||
// import { advanceProducts, creditSystems } from "../../global.js";
|
||||
// import { timeout } from "../../utils/genUtils.js";
|
||||
// import { assert, expect } from "chai";
|
||||
// import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
// import {
|
||||
// advanceClockForInvoice,
|
||||
// advanceMonths,
|
||||
// advanceTestClock,
|
||||
// checkBillingMeterEventSummary,
|
||||
// getUsageInArrearPrice,
|
||||
// } from "../../utils/stripeUtils.js";
|
||||
// import { addMonths } from "date-fns";
|
||||
// import {
|
||||
// sendGPUEvents,
|
||||
// checkUsageInvoiceAmount,
|
||||
// } from "../../utils/advancedUsageUtils.js";
|
||||
// import { Decimal } from "decimal.js";
|
||||
// import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
// import { setupBefore } from "tests/before.js";
|
||||
// import Stripe from "stripe";
|
||||
|
||||
// // FOURTH, TEST GPU STARTER ANNUAL UPGRADE TO GPU PRO
|
||||
|
||||
// const testCase = "usage5";
|
||||
// describe(`${chalk.yellowBright("usage5: multi interval upgrade, GPU starter annual -> GPU pro annual")}`, () => {
|
||||
// const customerId = testCase;
|
||||
// let testClockId = "";
|
||||
// let totalCreditsUsed = 0;
|
||||
// let customer: Customer;
|
||||
// let stripeCli: Stripe;
|
||||
|
||||
// let curTime = new Date();
|
||||
// before(async function () {
|
||||
// await setupBefore(this);
|
||||
// let res = await initCustomer({
|
||||
// customerId,
|
||||
// org: this.org,
|
||||
// env: this.env,
|
||||
// db: this.db,
|
||||
// autumn: this.autumnJs,
|
||||
// attachPm: "success",
|
||||
// });
|
||||
|
||||
// testClockId = res.testClockId;
|
||||
// customer = res.customer;
|
||||
// stripeCli = this.stripeCli;
|
||||
// });
|
||||
|
||||
// it("should attach GPU starter annual", async function () {
|
||||
// await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: advanceProducts.gpuStarterAnnual.id,
|
||||
// });
|
||||
|
||||
// const res = await AutumnCli.getCustomer(customerId);
|
||||
// compareMainProduct({
|
||||
// sent: advanceProducts.gpuStarterAnnual,
|
||||
// cusRes: res,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it(`should advance 1 month and upgrade to GPU pro monthly`, async function () {
|
||||
// let numberOfMonths = 1;
|
||||
// await advanceMonths({
|
||||
// stripeCli,
|
||||
// testClockId,
|
||||
// numberOfMonths,
|
||||
// });
|
||||
|
||||
// curTime = addMonths(curTime, numberOfMonths);
|
||||
|
||||
// // Send 20 events
|
||||
// let eventCount = 20;
|
||||
// const { creditsUsed } = await sendGPUEvents({
|
||||
// customerId,
|
||||
// eventCount,
|
||||
// });
|
||||
|
||||
// totalCreditsUsed = creditsUsed;
|
||||
|
||||
// await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: advanceProducts.gpuProAnnual.id,
|
||||
// });
|
||||
|
||||
// await advanceTestClock({
|
||||
// stripeCli,
|
||||
// testClockId,
|
||||
// numberOfDays: 10,
|
||||
// startingFrom: curTime,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should have GPU pro annual product and 2 Stripe subscriptions", async function () {
|
||||
// const res = await AutumnCli.getCustomer(customerId);
|
||||
// compareMainProduct({
|
||||
// sent: advanceProducts.gpuProAnnual,
|
||||
// cusRes: res,
|
||||
// });
|
||||
|
||||
// // Should have 2 subscriptions
|
||||
// const subs = await stripeCli.subscriptions.list({
|
||||
// customer: customer.processor.id,
|
||||
// });
|
||||
|
||||
// expect(subs.data.length).to.equal(2);
|
||||
// });
|
||||
|
||||
// it("should have correct invoice for GPU starter annual (bill for remaining usages)", async function () {
|
||||
// const res = await AutumnCli.getCustomer(customerId);
|
||||
// const invoices = res!.invoices;
|
||||
|
||||
// let invoiceIndex = invoices.findIndex((invoice: any) =>
|
||||
// invoice.product_ids.includes(advanceProducts.gpuStarterAnnual.id),
|
||||
// );
|
||||
|
||||
// await checkUsageInvoiceAmount({
|
||||
// invoices,
|
||||
// totalUsage: totalCreditsUsed,
|
||||
// product: advanceProducts.gpuStarterAnnual,
|
||||
// featureId: creditSystems.gpuCredits.id,
|
||||
// invoiceIndex,
|
||||
// includeBase: false,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should send 20 events (on GPU pro annual)", async function () {
|
||||
// const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
// // Send 20 events
|
||||
// let eventCount = 20;
|
||||
// const { creditsUsed } = await sendGPUEvents({
|
||||
// customerId,
|
||||
// eventCount,
|
||||
// });
|
||||
|
||||
// totalCreditsUsed = creditsUsed;
|
||||
|
||||
// await advanceClockForInvoice({
|
||||
// stripeCli,
|
||||
// testClockId,
|
||||
// waitForMeterUpdate: true,
|
||||
// startingFrom: curTime,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should have correct billing meter event summary for GPU pro annual", async function () {
|
||||
// const res = await AutumnCli.getCustomer(customerId);
|
||||
// const invoices = res!.invoices;
|
||||
|
||||
// // Think I have to use Stripe metered event summary to check this
|
||||
// let usagePrice = await getUsageInArrearPrice({
|
||||
// org: this.org,
|
||||
// sb: this.sb,
|
||||
// env: this.env,
|
||||
// productId: advanceProducts.gpuProAnnual.id,
|
||||
// });
|
||||
|
||||
// let eventSummary = await checkBillingMeterEventSummary({
|
||||
// stripeCli,
|
||||
// startTime: curTime, // Wrong date?
|
||||
// stripeMeterId: usagePrice?.config?.stripe_meter_id,
|
||||
// stripeCustomerId: customer.processor.id,
|
||||
// });
|
||||
|
||||
// let roundedFirst = Math.ceil(
|
||||
// new Decimal(totalCreditsUsed)
|
||||
// .div(usagePrice?.config?.billing_units!)
|
||||
// .toNumber(),
|
||||
// );
|
||||
// let roundedTotalCreditsUsed = new Decimal(roundedFirst)
|
||||
// .mul(usagePrice?.config?.billing_units!)
|
||||
// .toNumber();
|
||||
// try {
|
||||
// assert.exists(eventSummary);
|
||||
// assert.equal(eventSummary?.aggregated_value, roundedTotalCreditsUsed);
|
||||
// } catch (error) {
|
||||
// console.group();
|
||||
// console.log(" - Event summary: ", eventSummary);
|
||||
// console.log(" - Total credits used: ", totalCreditsUsed);
|
||||
// console.groupEnd();
|
||||
// throw error;
|
||||
// }
|
||||
|
||||
// // await checkUsageInvoiceAmount({
|
||||
// // invoices,
|
||||
// // totalUsage: totalCreditsUsed,
|
||||
// // product: advanceProducts.gpuProAnnual,
|
||||
// // featureId: creditSystems.gpuCredits.id,
|
||||
// // invoiceIndex: 0,
|
||||
// // includeBase: false,
|
||||
// // });
|
||||
// });
|
||||
// });
|
||||
332
server/tests/attach/basic/basic1.ts
Normal file
332
server/tests/attach/basic/basic1.ts
Normal file
@@ -0,0 +1,332 @@
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { assert, expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { features, products } from "tests/global.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { completeCheckoutForm } from "tests/utils/stripeUtils.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
|
||||
const oneTimeQuantity = 2;
|
||||
const oneTimePurchaseCount = 2;
|
||||
const oneTimeOverrideQuantity = 4;
|
||||
const monthlyQuantity = 2;
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
const testCase = "basic1";
|
||||
describe(`${chalk.yellowBright(
|
||||
"basic1: Testing attach -- free, pro & one-time / monthly add on",
|
||||
)}`, () => {
|
||||
let customerId = testCase;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let db, org, env;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
fingerprint: "test",
|
||||
});
|
||||
});
|
||||
|
||||
it("should create customer and have default free active", async function () {
|
||||
const data = await autumn.customers.get(customerId);
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.free,
|
||||
cusRes: data,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct entitlements", async function () {
|
||||
const data = await autumn.customers.get(customerId);
|
||||
const expectedEntitlement = products.free.entitlements.metered1;
|
||||
|
||||
const entitled = (await autumn.check({
|
||||
customer_id: customerId,
|
||||
feature_id: features.metered1.id,
|
||||
})) as any;
|
||||
|
||||
const metered1Balance = entitled.balances.find(
|
||||
(balance: any) => balance.feature_id === features.metered1.id,
|
||||
);
|
||||
|
||||
expect(entitled.allowed).to.be.true;
|
||||
expect(metered1Balance).to.exist;
|
||||
expect(metered1Balance.balance).to.equal(expectedEntitlement.allowance);
|
||||
expect(metered1Balance.unlimited).to.not.exist;
|
||||
});
|
||||
|
||||
it("should have correct boolean1 entitlement", async function () {
|
||||
const entitled = await AutumnCli.entitled(customerId, features.boolean1.id);
|
||||
expect(entitled!.allowed).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
// describe("Attach pro -- check products & entitlements", () => {
|
||||
// it("POST /attach -- attaching pro (force checkout)", async function () {
|
||||
// const { checkout_url } = await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: products.pro.id,
|
||||
// });
|
||||
|
||||
// await completeCheckoutForm(checkout_url);
|
||||
// await timeout(10000); // for webhook to be processed
|
||||
// console.log(` ${chalk.greenBright("Attached pro")}`);
|
||||
// });
|
||||
|
||||
// it("GET /customers/:id -- checking product & entitlements (pro)", async function () {
|
||||
// const res = await AutumnCli.getCustomer(customerId);
|
||||
// // console.log("Res: ", res);
|
||||
// compareMainProduct({
|
||||
// sent: products.pro,
|
||||
// cusRes: res,
|
||||
// });
|
||||
// expect(res.invoices.length).to.be.greaterThan(0);
|
||||
// });
|
||||
|
||||
// // return;
|
||||
|
||||
// it("GET /entitled -- checking entitlements for metered1 && boolean1", async function () {
|
||||
// const proEntitlements = products.pro.entitlements;
|
||||
|
||||
// for (const entitlement of Object.values(proEntitlements)) {
|
||||
// const allowance = entitlement.allowance;
|
||||
|
||||
// const res: any = await AutumnCli.entitled(
|
||||
// customerId,
|
||||
// entitlement.feature_id!,
|
||||
// );
|
||||
|
||||
// const entBalance = res!.balances.find(
|
||||
// (b: any) => b.feature_id === entitlement.feature_id,
|
||||
// );
|
||||
|
||||
// try {
|
||||
// expect(res!.allowed).to.be.true;
|
||||
// expect(entBalance).to.exist;
|
||||
// if (entitlement.allowance) {
|
||||
// expect(entBalance!.balance).to.equal(allowance);
|
||||
// }
|
||||
// // console.log(` - ${entitlement.feature_id} -- Passed`);
|
||||
// } catch (error) {
|
||||
// console.group();
|
||||
// console.group();
|
||||
// console.log("Looking for: ", entitlement);
|
||||
// console.log("Received: ", res);
|
||||
// console.groupEnd();
|
||||
// console.groupEnd();
|
||||
// throw error;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// const oneTimeBillingUnits =
|
||||
// products.oneTimeAddOnMetered1.prices[0].config.billing_units;
|
||||
// const monthlyBillingUnits =
|
||||
// products.monthlyAddOnMetered1.prices[0].config.billing_units;
|
||||
|
||||
// describe("One time add on (force checkout)", () => {
|
||||
// // PURCHASE ONE TIME ADD ON
|
||||
|
||||
// it("POST /attach -- attaching one time add on (force checkout) [no quantity passed in]", async function () {
|
||||
// try {
|
||||
// for (let i = 0; i < oneTimePurchaseCount; i++) {
|
||||
// const res = await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: products.oneTimeAddOnMetered1.id,
|
||||
// forceCheckout: true,
|
||||
// });
|
||||
|
||||
// await completeCheckoutForm(res.checkout_url, oneTimeOverrideQuantity);
|
||||
// await timeout(10000); // for webhook to be processed
|
||||
// console.log(` ${chalk.greenBright("Attached one time add on")}`);
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.group();
|
||||
// console.group();
|
||||
// console.log("Failed to attach one time add on");
|
||||
// console.log("Error data:", error);
|
||||
// console.groupEnd();
|
||||
// console.groupEnd();
|
||||
// process.exit(1);
|
||||
// }
|
||||
// });
|
||||
|
||||
// // TODO: Attach one time add on again (with quantity?)
|
||||
|
||||
// it("GET /customers/:id -- checking product & entitlements (one time add on)", async function () {
|
||||
// const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
// // 1. Metered1 balance should be pro + one time add on
|
||||
|
||||
// // Fetch balance
|
||||
// const addOnBalance = cusRes.entitlements.find(
|
||||
// (e: any) =>
|
||||
// e.feature_id === features.metered1.id &&
|
||||
// e.interval ==
|
||||
// products.oneTimeAddOnMetered1.entitlements.metered1.interval,
|
||||
// );
|
||||
|
||||
// const expectedAmt =
|
||||
// (oneTimeOverrideQuantity || oneTimeQuantity) *
|
||||
// oneTimeBillingUnits *
|
||||
// oneTimePurchaseCount;
|
||||
|
||||
// try {
|
||||
// assert.equal(addOnBalance!.balance, expectedAmt);
|
||||
// assert.equal(cusRes.add_ons.length, 1);
|
||||
// assert.equal(cusRes.add_ons[0].id, products.oneTimeAddOnMetered1.id);
|
||||
// assert.equal(cusRes.invoices.length, 1 + oneTimePurchaseCount);
|
||||
// } catch (error) {
|
||||
// console.group();
|
||||
// console.group();
|
||||
// console.log("GET customer, balances failed");
|
||||
// console.log(
|
||||
// "Add on entitlement:",
|
||||
// products.oneTimeAddOnMetered1.entitlements.metered1,
|
||||
// );
|
||||
// console.log("Customer entitlements:", cusRes.entitlements);
|
||||
|
||||
// console.groupEnd();
|
||||
// console.groupEnd();
|
||||
// throw error;
|
||||
// }
|
||||
// });
|
||||
|
||||
// it("GET /entitled -- checking entitled for metered1", async function () {
|
||||
// const res: any = await AutumnCli.entitled(
|
||||
// customerId,
|
||||
// features.metered1.id,
|
||||
// );
|
||||
|
||||
// expect(res!.allowed).to.be.true;
|
||||
|
||||
// // pro metered1
|
||||
// const proMetered1Amt = products.pro.entitlements.metered1.allowance;
|
||||
|
||||
// const addOnBalance = res!.balances.find(
|
||||
// (b: any) => b.feature_id === features.metered1.id,
|
||||
// );
|
||||
|
||||
// expect(res!.allowed).to.be.true;
|
||||
// expect(addOnBalance!.balance).to.equal(
|
||||
// proMetered1Amt! +
|
||||
// (oneTimeOverrideQuantity || oneTimeQuantity) *
|
||||
// oneTimeBillingUnits *
|
||||
// oneTimePurchaseCount,
|
||||
// );
|
||||
// });
|
||||
// });
|
||||
|
||||
// // PURCHASE MONTHLY ADD ON
|
||||
// describe("Monthly add on", () => {
|
||||
// it("POST /attach -- attaching monthly add on", async function () {
|
||||
// await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: products.monthlyAddOnMetered1.id,
|
||||
// forceCheckout: false,
|
||||
// options: [
|
||||
// {
|
||||
// feature_id: features.metered1.id,
|
||||
// quantity: monthlyQuantity * monthlyBillingUnits,
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
// await timeout(10000);
|
||||
|
||||
// console.log(` ${chalk.greenBright("Attached monthly top up")}`);
|
||||
// });
|
||||
|
||||
// it("GET /customers/:id -- checking product & entitlements (monthly add on)", async function () {
|
||||
// const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
// // 1. Metered1 balance should be pro + one time add on
|
||||
// const proMetered1 = products.pro.entitlements.metered1.allowance;
|
||||
|
||||
// // Fetch balance
|
||||
// const monthlyMetered1Balance = cusRes.entitlements.find(
|
||||
// (e: any) =>
|
||||
// e.feature_id === features.metered1.id &&
|
||||
// e.interval ==
|
||||
// products.monthlyAddOnMetered1.entitlements.metered1.interval,
|
||||
// );
|
||||
|
||||
// try {
|
||||
// assert.equal(
|
||||
// monthlyMetered1Balance!.balance,
|
||||
// proMetered1! + monthlyQuantity * monthlyBillingUnits,
|
||||
// );
|
||||
|
||||
// assert.equal(cusRes.add_ons.length, 2);
|
||||
// const monthlyAddOnId = cusRes.add_ons.find(
|
||||
// (a: any) => a.id === products.monthlyAddOnMetered1.id,
|
||||
// );
|
||||
|
||||
// assert.exists(monthlyAddOnId);
|
||||
// expect(cusRes.invoices.length).to.equal(2 + oneTimePurchaseCount);
|
||||
// } catch (error) {
|
||||
// console.group();
|
||||
// console.group();
|
||||
// console.log("GET customer, balances failed");
|
||||
// console.log(
|
||||
// "Add on entitlement:",
|
||||
// products.monthlyAddOnMetered1.entitlements.metered1,
|
||||
// );
|
||||
// console.log("Customer entitlements:", cusRes.entitlements);
|
||||
|
||||
// console.groupEnd();
|
||||
// console.groupEnd();
|
||||
// throw error;
|
||||
// }
|
||||
// });
|
||||
|
||||
// it("GET /entitled -- checking entitlements (monthly add on)", async function () {
|
||||
// const res: any = await AutumnCli.entitled(
|
||||
// customerId,
|
||||
// features.metered1.id,
|
||||
// );
|
||||
|
||||
// const metered1Balance = res!.balances.find(
|
||||
// (b: any) => b.feature_id === features.metered1.id,
|
||||
// );
|
||||
|
||||
// const proMetered1Amt = products.pro.entitlements.metered1.allowance;
|
||||
// const monthlyAddOnMetered1Amt = monthlyQuantity * monthlyBillingUnits;
|
||||
|
||||
// const oneTimeAddOnMetered1Amt =
|
||||
// (oneTimeOverrideQuantity || oneTimeQuantity) *
|
||||
// oneTimeBillingUnits *
|
||||
// oneTimePurchaseCount;
|
||||
|
||||
// try {
|
||||
// expect(metered1Balance!.balance).to.equal(
|
||||
// proMetered1Amt! + monthlyAddOnMetered1Amt + oneTimeAddOnMetered1Amt,
|
||||
// );
|
||||
// } catch (error) {
|
||||
// console.group();
|
||||
// console.group();
|
||||
// console.log("GET entitled, balances failed");
|
||||
|
||||
// console.log("/entitled response:", res);
|
||||
// console.log("Pro metered1 amt:", proMetered1Amt);
|
||||
// console.log("Monthly add on metered1 amt:", monthlyAddOnMetered1Amt);
|
||||
// console.log("One time add on metered1 amt:", oneTimeAddOnMetered1Amt);
|
||||
|
||||
// console.groupEnd();
|
||||
// console.groupEnd();
|
||||
// throw error;
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
@@ -14,14 +14,11 @@ import { completeCheckoutForm } from "tests/utils/stripeUtils.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
import { expect } from "chai";
|
||||
|
||||
describe(`${chalk.yellowBright("attach3: Multi attach, all one off")}`, () => {
|
||||
let customerId = "attach3";
|
||||
const testCase = "basic10";
|
||||
|
||||
// let billingUnits =
|
||||
// oneTimeProducts.oneTimeMetered2.prices[0].config.billing_units;
|
||||
// let quantity = 1000 * billingUnits;
|
||||
describe(`${chalk.yellowBright("basic10: Multi attach, all one off")}`, () => {
|
||||
let customerId = testCase;
|
||||
let quantity = 1000;
|
||||
|
||||
let options = [
|
||||
{
|
||||
feature_id: features.metered2.id,
|
||||
@@ -48,7 +45,7 @@ describe(`${chalk.yellowBright("attach3: Multi attach, all one off")}`, () => {
|
||||
});
|
||||
|
||||
await completeCheckoutForm(res.checkout_url);
|
||||
await timeout(15000);
|
||||
await timeout(20000);
|
||||
});
|
||||
|
||||
it("should have correct main product and entitlements", async function () {
|
||||
@@ -65,7 +62,6 @@ describe(`${chalk.yellowBright("attach3: Multi attach, all one off")}`, () => {
|
||||
optionsList: options,
|
||||
});
|
||||
|
||||
// Check invoices
|
||||
const invoices = cusRes.invoices;
|
||||
const metered1Amount = getFixedPriceAmount(oneTimeProducts.oneTimeMetered1);
|
||||
const metered2Tiers = getUsagePriceTiers({
|
||||
@@ -79,13 +75,11 @@ describe(`${chalk.yellowBright("attach3: Multi attach, all one off")}`, () => {
|
||||
oneTimeProducts.oneTimeMetered2.prices[0].config.billing_units,
|
||||
);
|
||||
|
||||
// console.log("Num billing units: ", numBillingUnits);
|
||||
|
||||
expect(invoices[0].total).to.equal(
|
||||
new Decimal(metered2Amount)
|
||||
const expectedTotal = new Decimal(metered2Amount)
|
||||
.mul(numBillingUnits)
|
||||
.add(metered1Amount)
|
||||
.toNumber(),
|
||||
);
|
||||
.toNumber();
|
||||
|
||||
expect(invoices[0].total).to.equal(expectedTotal);
|
||||
});
|
||||
});
|
||||
295
server/tests/attach/basic/basic2.ts
Normal file
295
server/tests/attach/basic/basic2.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { assert, expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { features, products } from "tests/global.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { completeCheckoutForm } from "tests/utils/stripeUtils.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
|
||||
const oneTimeQuantity = 2;
|
||||
const oneTimePurchaseCount = 2;
|
||||
const oneTimeOverrideQuantity = 4;
|
||||
const monthlyQuantity = 2;
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
const testCase = "basic2";
|
||||
describe(`${chalk.yellowBright("basic2: Testing attach pro")}`, () => {
|
||||
let customerId = testCase;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let db, org, env;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
fingerprint: "test",
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro through checkout", async function () {
|
||||
const { checkout_url } = await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(checkout_url);
|
||||
await timeout(10000);
|
||||
});
|
||||
|
||||
it("should have correct product & entitlements", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: res,
|
||||
});
|
||||
expect(res.invoices.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
// return;
|
||||
|
||||
it("should have correct result when calling /check", async function () {
|
||||
const proEntitlements = products.pro.entitlements;
|
||||
|
||||
for (const entitlement of Object.values(proEntitlements)) {
|
||||
const allowance = entitlement.allowance;
|
||||
|
||||
const res: any = await AutumnCli.entitled(
|
||||
customerId,
|
||||
entitlement.feature_id!,
|
||||
);
|
||||
|
||||
const entBalance = res!.balances.find(
|
||||
(b: any) => b.feature_id === entitlement.feature_id,
|
||||
);
|
||||
|
||||
try {
|
||||
expect(res!.allowed).to.be.true;
|
||||
expect(entBalance).to.exist;
|
||||
if (entitlement.allowance) {
|
||||
expect(entBalance!.balance).to.equal(allowance);
|
||||
}
|
||||
// console.log(` - ${entitlement.feature_id} -- Passed`);
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.group();
|
||||
console.log("Looking for: ", entitlement);
|
||||
console.log("Received: ", res);
|
||||
console.groupEnd();
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
const oneTimeBillingUnits =
|
||||
products.oneTimeAddOnMetered1.prices[0].config.billing_units;
|
||||
const monthlyBillingUnits =
|
||||
products.monthlyAddOnMetered1.prices[0].config.billing_units;
|
||||
|
||||
describe("One time add on (force checkout)", () => {
|
||||
// PURCHASE ONE TIME ADD ON
|
||||
|
||||
it("POST /attach -- attaching one time add on (force checkout) [no quantity passed in]", async function () {
|
||||
try {
|
||||
for (let i = 0; i < oneTimePurchaseCount; i++) {
|
||||
const res = await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.oneTimeAddOnMetered1.id,
|
||||
forceCheckout: true,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(res.checkout_url, oneTimeOverrideQuantity);
|
||||
await timeout(10000); // for webhook to be processed
|
||||
console.log(` ${chalk.greenBright("Attached one time add on")}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.group();
|
||||
console.log("Failed to attach one time add on");
|
||||
console.log("Error data:", error);
|
||||
console.groupEnd();
|
||||
console.groupEnd();
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Attach one time add on again (with quantity?)
|
||||
|
||||
it("GET /customers/:id -- checking product & entitlements (one time add on)", async function () {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
// 1. Metered1 balance should be pro + one time add on
|
||||
|
||||
// Fetch balance
|
||||
const addOnBalance = cusRes.entitlements.find(
|
||||
(e: any) =>
|
||||
e.feature_id === features.metered1.id &&
|
||||
e.interval ==
|
||||
products.oneTimeAddOnMetered1.entitlements.metered1.interval,
|
||||
);
|
||||
|
||||
const expectedAmt =
|
||||
(oneTimeOverrideQuantity || oneTimeQuantity) *
|
||||
oneTimeBillingUnits *
|
||||
oneTimePurchaseCount;
|
||||
|
||||
try {
|
||||
assert.equal(addOnBalance!.balance, expectedAmt);
|
||||
assert.equal(cusRes.add_ons.length, 1);
|
||||
assert.equal(cusRes.add_ons[0].id, products.oneTimeAddOnMetered1.id);
|
||||
assert.equal(cusRes.invoices.length, 1 + oneTimePurchaseCount);
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.group();
|
||||
console.log("GET customer, balances failed");
|
||||
console.log(
|
||||
"Add on entitlement:",
|
||||
products.oneTimeAddOnMetered1.entitlements.metered1,
|
||||
);
|
||||
console.log("Customer entitlements:", cusRes.entitlements);
|
||||
|
||||
console.groupEnd();
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /entitled -- checking entitled for metered1", async function () {
|
||||
const res: any = await AutumnCli.entitled(
|
||||
customerId,
|
||||
features.metered1.id,
|
||||
);
|
||||
|
||||
expect(res!.allowed).to.be.true;
|
||||
|
||||
// pro metered1
|
||||
const proMetered1Amt = products.pro.entitlements.metered1.allowance;
|
||||
|
||||
const addOnBalance = res!.balances.find(
|
||||
(b: any) => b.feature_id === features.metered1.id,
|
||||
);
|
||||
|
||||
expect(res!.allowed).to.be.true;
|
||||
expect(addOnBalance!.balance).to.equal(
|
||||
proMetered1Amt! +
|
||||
(oneTimeOverrideQuantity || oneTimeQuantity) *
|
||||
oneTimeBillingUnits *
|
||||
oneTimePurchaseCount,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// PURCHASE MONTHLY ADD ON
|
||||
describe("Monthly add on", () => {
|
||||
it("POST /attach -- attaching monthly add on", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.monthlyAddOnMetered1.id,
|
||||
forceCheckout: false,
|
||||
options: [
|
||||
{
|
||||
feature_id: features.metered1.id,
|
||||
quantity: monthlyQuantity * monthlyBillingUnits,
|
||||
},
|
||||
],
|
||||
});
|
||||
await timeout(10000);
|
||||
|
||||
console.log(` ${chalk.greenBright("Attached monthly top up")}`);
|
||||
});
|
||||
|
||||
it("GET /customers/:id -- checking product & entitlements (monthly add on)", async function () {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
// 1. Metered1 balance should be pro + one time add on
|
||||
const proMetered1 = products.pro.entitlements.metered1.allowance;
|
||||
|
||||
// Fetch balance
|
||||
const monthlyMetered1Balance = cusRes.entitlements.find(
|
||||
(e: any) =>
|
||||
e.feature_id === features.metered1.id &&
|
||||
e.interval ==
|
||||
products.monthlyAddOnMetered1.entitlements.metered1.interval,
|
||||
);
|
||||
|
||||
try {
|
||||
assert.equal(
|
||||
monthlyMetered1Balance!.balance,
|
||||
proMetered1! + monthlyQuantity * monthlyBillingUnits,
|
||||
);
|
||||
|
||||
assert.equal(cusRes.add_ons.length, 2);
|
||||
const monthlyAddOnId = cusRes.add_ons.find(
|
||||
(a: any) => a.id === products.monthlyAddOnMetered1.id,
|
||||
);
|
||||
|
||||
assert.exists(monthlyAddOnId);
|
||||
expect(cusRes.invoices.length).to.equal(2 + oneTimePurchaseCount);
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.group();
|
||||
console.log("GET customer, balances failed");
|
||||
console.log(
|
||||
"Add on entitlement:",
|
||||
products.monthlyAddOnMetered1.entitlements.metered1,
|
||||
);
|
||||
console.log("Customer entitlements:", cusRes.entitlements);
|
||||
|
||||
console.groupEnd();
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /entitled -- checking entitlements (monthly add on)", async function () {
|
||||
const res: any = await AutumnCli.entitled(
|
||||
customerId,
|
||||
features.metered1.id,
|
||||
);
|
||||
|
||||
const metered1Balance = res!.balances.find(
|
||||
(b: any) => b.feature_id === features.metered1.id,
|
||||
);
|
||||
|
||||
const proMetered1Amt = products.pro.entitlements.metered1.allowance;
|
||||
const monthlyAddOnMetered1Amt = monthlyQuantity * monthlyBillingUnits;
|
||||
|
||||
const oneTimeAddOnMetered1Amt =
|
||||
(oneTimeOverrideQuantity || oneTimeQuantity) *
|
||||
oneTimeBillingUnits *
|
||||
oneTimePurchaseCount;
|
||||
|
||||
try {
|
||||
expect(metered1Balance!.balance).to.equal(
|
||||
proMetered1Amt! + monthlyAddOnMetered1Amt + oneTimeAddOnMetered1Amt,
|
||||
);
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.group();
|
||||
console.log("GET entitled, balances failed");
|
||||
|
||||
console.log("/entitled response:", res);
|
||||
console.log("Pro metered1 amt:", proMetered1Amt);
|
||||
console.log("Monthly add on metered1 amt:", monthlyAddOnMetered1Amt);
|
||||
console.log("One time add on metered1 amt:", oneTimeAddOnMetered1Amt);
|
||||
|
||||
console.groupEnd();
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
161
server/tests/attach/basic/basic3.ts
Normal file
161
server/tests/attach/basic/basic3.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { assert, expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { features, products } from "tests/global.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { completeCheckoutForm } from "tests/utils/stripeUtils.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import {
|
||||
constructProduct,
|
||||
constructRawProduct,
|
||||
} from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { constructPrepaidItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
|
||||
// const oneTimeQuantity = 2;
|
||||
// const oneTimePurchaseCount = 2;
|
||||
// const oneTimeOverrideQuantity = 4;
|
||||
// const monthlyQuantity = 2;
|
||||
let oneTimeItem = constructPrepaidItem({
|
||||
featureId: features.metered1.id,
|
||||
price: 9,
|
||||
billingUnits: 250,
|
||||
isOneOff: true,
|
||||
});
|
||||
|
||||
let oneTime = constructRawProduct({
|
||||
id: "basic3_one_off",
|
||||
items: [oneTimeItem],
|
||||
isAddOn: true,
|
||||
});
|
||||
|
||||
let monthlyItem = constructPrepaidItem({
|
||||
featureId: features.metered1.id,
|
||||
price: 9,
|
||||
billingUnits: 250,
|
||||
});
|
||||
|
||||
let monthly = constructRawProduct({
|
||||
id: "basic3_monthly",
|
||||
items: [
|
||||
constructPrepaidItem({
|
||||
featureId: features.metered1.id,
|
||||
price: 9,
|
||||
billingUnits: 250,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
const testCase = "basic3";
|
||||
describe(`${chalk.yellowBright("basic3: Testing attach one time / monthly add ons")}`, () => {
|
||||
let customerId = testCase;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let db, org, env;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
await createProducts({
|
||||
autumn: this.autumnJs,
|
||||
db,
|
||||
orgId: org.id,
|
||||
env,
|
||||
products: [oneTime, monthly],
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro", async function () {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
const oneTimeQuantity = 500;
|
||||
const oneTimeBillingUnits = oneTimeItem.billing_units;
|
||||
const oneTimePurchaseCount = 2;
|
||||
|
||||
it("should attach one time add on twice, force checkout", async function () {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const res = await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: oneTime.id,
|
||||
force_checkout: true,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(
|
||||
res.checkout_url,
|
||||
oneTimeQuantity / oneTimeBillingUnits!,
|
||||
);
|
||||
await timeout(20000);
|
||||
}
|
||||
});
|
||||
|
||||
it("should have correct product & entitlements", async function () {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
const addOnBalance = cusRes.entitlements.find(
|
||||
(e: any) =>
|
||||
e.feature_id === features.metered1.id &&
|
||||
e.interval ==
|
||||
products.oneTimeAddOnMetered1.entitlements.metered1.interval,
|
||||
);
|
||||
|
||||
const expectedAmt = oneTimeQuantity * oneTimePurchaseCount;
|
||||
|
||||
expect(addOnBalance!.balance).to.equal(
|
||||
expectedAmt,
|
||||
"add on balance should be correct",
|
||||
);
|
||||
expect(cusRes.add_ons).to.have.lengthOf(
|
||||
1,
|
||||
"should only have one add on product after two purchases (since they combine)",
|
||||
);
|
||||
expect(cusRes.add_ons[0].id).to.equal(
|
||||
oneTime.id,
|
||||
"add on product should exist",
|
||||
);
|
||||
expect(cusRes.invoices.length).to.equal(
|
||||
1 + oneTimePurchaseCount,
|
||||
"invoices should be correct",
|
||||
);
|
||||
});
|
||||
|
||||
it("should have correct /check result for metered1", async function () {
|
||||
const res: any = await AutumnCli.entitled(customerId, features.metered1.id);
|
||||
|
||||
expect(res!.allowed).to.be.true;
|
||||
|
||||
const proMetered1Amt = products.pro.entitlements.metered1.allowance;
|
||||
const addOnBalance = res!.balances.find(
|
||||
(b: any) => b.feature_id === features.metered1.id,
|
||||
);
|
||||
|
||||
expect(res!.allowed).to.be.true;
|
||||
expect(addOnBalance!.balance).to.equal(
|
||||
proMetered1Amt! + oneTimeQuantity * oneTimePurchaseCount,
|
||||
);
|
||||
});
|
||||
});
|
||||
121
server/tests/attach/basic/basic4.ts
Normal file
121
server/tests/attach/basic/basic4.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import chalk from "chalk";
|
||||
import { expect } from "chai";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { features, products } from "tests/global.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { constructRawProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { constructPrepaidItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
|
||||
let monthlyItem = constructPrepaidItem({
|
||||
featureId: features.metered1.id,
|
||||
price: 9,
|
||||
billingUnits: 250,
|
||||
});
|
||||
|
||||
let monthly = constructRawProduct({
|
||||
id: "basic4_monthly",
|
||||
items: [monthlyItem],
|
||||
});
|
||||
|
||||
const testCase = "basic4";
|
||||
describe(`${chalk.yellowBright("basic4: Testing attach monthly add on")}`, () => {
|
||||
let customerId = testCase;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let db, org, env;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
await createProducts({
|
||||
autumn: this.autumnJs,
|
||||
db,
|
||||
orgId: org.id,
|
||||
env,
|
||||
products: [monthly],
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro", async function () {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
const monthlyQuantity = 500;
|
||||
|
||||
it("should attach monthly add on", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.monthlyAddOnMetered1.id,
|
||||
forceCheckout: false,
|
||||
options: [
|
||||
{
|
||||
feature_id: features.metered1.id,
|
||||
quantity: monthlyQuantity,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct product & entitlements", async function () {
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
const proMetered1 = products.pro.entitlements.metered1.allowance;
|
||||
|
||||
const monthlyMetered1Balance = cusRes.entitlements.find(
|
||||
(e: any) =>
|
||||
e.feature_id === features.metered1.id &&
|
||||
e.interval ==
|
||||
products.monthlyAddOnMetered1.entitlements.metered1.interval,
|
||||
);
|
||||
|
||||
expect(monthlyMetered1Balance!.balance).to.equal(
|
||||
proMetered1! + monthlyQuantity,
|
||||
);
|
||||
|
||||
expect(cusRes.add_ons).to.have.lengthOf(1);
|
||||
const monthlyAddOnId = cusRes.add_ons.find(
|
||||
(a: any) => a.id === products.monthlyAddOnMetered1.id,
|
||||
);
|
||||
|
||||
expect(monthlyAddOnId).to.exist;
|
||||
expect(cusRes.invoices.length).to.equal(2);
|
||||
});
|
||||
|
||||
it("should have correct /check result for metered1", async function () {
|
||||
const res: any = await AutumnCli.entitled(customerId, features.metered1.id);
|
||||
|
||||
const metered1Balance = res!.balances.find(
|
||||
(b: any) => b.feature_id === features.metered1.id,
|
||||
);
|
||||
|
||||
const proMetered1Amt = products.pro.entitlements.metered1.allowance;
|
||||
const monthlyAddOnMetered1Amt = monthlyQuantity;
|
||||
|
||||
expect(metered1Balance!.balance).to.equal(
|
||||
proMetered1Amt! + monthlyAddOnMetered1Amt,
|
||||
);
|
||||
});
|
||||
});
|
||||
98
server/tests/attach/basic/basic5.ts
Normal file
98
server/tests/attach/basic/basic5.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { features, products } from "tests/global.js";
|
||||
import chalk from "chalk";
|
||||
import {
|
||||
checkFeatureHasCorrectBalance,
|
||||
compareMainProduct,
|
||||
} from "tests/utils/compare.js";
|
||||
import { expect } from "chai";
|
||||
import { CusProductStatus } from "@autumn/shared";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import Stripe from "stripe";
|
||||
import { timeout } from "@/utils/genUtils.js";
|
||||
|
||||
const testCase = "basic5";
|
||||
describe(`${chalk.yellowBright(
|
||||
"basic5: Testing cancel through Stripe at period end and now",
|
||||
)}`, () => {
|
||||
const customerId = testCase;
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
stripeCli = this.stripeCli;
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro product", async function () {
|
||||
const res: any = await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should cancel pro product (at period end)", async function () {
|
||||
const stripeCli = createStripeCli({ org: this.org, env: this.env });
|
||||
const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
const proProduct = cusRes.products.find(
|
||||
(p: any) => p.id === products.pro.id,
|
||||
);
|
||||
|
||||
for (const subId of proProduct.subscription_ids) {
|
||||
await stripeCli.subscriptions.update(subId, {
|
||||
cancel_at_period_end: true,
|
||||
});
|
||||
}
|
||||
await timeout(5000);
|
||||
});
|
||||
|
||||
it("should have pro product active, and canceled_at != null, and free scheduled", async function () {
|
||||
const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: cusRes,
|
||||
});
|
||||
|
||||
const proProduct = cusRes.products.find(
|
||||
(p: any) => p.id === products.pro.id,
|
||||
);
|
||||
expect(proProduct.canceled_at).to.not.equal(null);
|
||||
expect(proProduct.status).to.equal(CusProductStatus.Active);
|
||||
|
||||
const freeProduct = cusRes.products.find(
|
||||
(p: any) => p.id === products.free.id,
|
||||
);
|
||||
expect(freeProduct).to.exist;
|
||||
expect(freeProduct.status).to.equal(CusProductStatus.Scheduled);
|
||||
});
|
||||
|
||||
it("should cancel pro product (now)", async function () {
|
||||
const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
const proProduct = cusRes.products.find(
|
||||
(p: any) => p.id === products.pro.id,
|
||||
);
|
||||
|
||||
for (const subId of proProduct.subscription_ids) {
|
||||
await stripeCli.subscriptions.cancel(subId);
|
||||
}
|
||||
await timeout(5000);
|
||||
});
|
||||
|
||||
it("should have free product active, and no pro product", async function () {
|
||||
const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.free,
|
||||
cusRes: cusRes,
|
||||
});
|
||||
});
|
||||
});
|
||||
74
server/tests/attach/basic/basic6.ts
Normal file
74
server/tests/attach/basic/basic6.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import Stripe from "stripe";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { products } from "tests/global.js";
|
||||
import { attachFailedPaymentMethod } from "@/external/stripe/stripeCusUtils.js";
|
||||
import { CusProductStatus, Customer } from "@autumn/shared";
|
||||
import { addDays, addHours, addMonths } from "date-fns";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { expect } from "chai";
|
||||
|
||||
const testCase = "basic6";
|
||||
describe(`${chalk.yellowBright(
|
||||
"basic6: Testing subscription past_due",
|
||||
)}`, () => {
|
||||
const customerId = testCase;
|
||||
let stripeCli: Stripe;
|
||||
let testClockId: string;
|
||||
let customer: Customer;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { testClockId: testClockId_, customer: customer_ } =
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
testClockId = testClockId_;
|
||||
customer = customer_;
|
||||
});
|
||||
|
||||
it("should attach pro product and switch to failed payment method", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
|
||||
await attachFailedPaymentMethod({
|
||||
stripeCli,
|
||||
customer,
|
||||
});
|
||||
});
|
||||
|
||||
it("should advance to next cycle", async function () {
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addHours(
|
||||
addMonths(new Date(), 1),
|
||||
hoursToFinalizeInvoice,
|
||||
).getTime(),
|
||||
waitForSeconds: 30,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have pro product in past due status", async function () {
|
||||
const cusRes: any = await AutumnCli.getCustomer(customerId);
|
||||
const proProduct = cusRes.products.find(
|
||||
(p: any) => p.id === products.pro.id,
|
||||
);
|
||||
expect(proProduct).to.exist;
|
||||
expect(proProduct.status).to.equal(CusProductStatus.PastDue);
|
||||
});
|
||||
});
|
||||
80
server/tests/attach/basic/basic7.ts
Normal file
80
server/tests/attach/basic/basic7.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import chalk from "chalk";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { products } from "tests/global.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { assert, expect } from "chai";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { CusProductStatus } from "@autumn/shared";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
|
||||
const testCase = "basic7";
|
||||
|
||||
describe(`${chalk.yellowBright("basic7: Testing trial duplicates (same customer)")}`, () => {
|
||||
const customerId = testCase;
|
||||
let customerId2 = testCase + "2";
|
||||
const autumn = new AutumnInt();
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro with trial and have correct product & invoice", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
const customer = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.proWithTrial,
|
||||
cusRes: customer,
|
||||
status: CusProductStatus.Trialing,
|
||||
});
|
||||
|
||||
const invoices = customer.invoices;
|
||||
expect(invoices.length).to.equal(1, "Invoice length should be 1");
|
||||
expect(invoices[0].total).to.equal(0, "Invoice total should be 0");
|
||||
});
|
||||
|
||||
it("should cancel pro with trial", async function () {
|
||||
await autumn.cancel({
|
||||
customer_id: customerId,
|
||||
product_id: products.proWithTrial.id,
|
||||
expire_immediately: true,
|
||||
});
|
||||
await timeout(5000); // for webhook to be processed
|
||||
});
|
||||
|
||||
it("should be able to attach pro with trial again (renewal flow)", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
const customer = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.proWithTrial,
|
||||
cusRes: customer,
|
||||
status: CusProductStatus.Trialing,
|
||||
});
|
||||
|
||||
const invoices = customer.invoices;
|
||||
expect(invoices.length).to.equal(1, "Invoice length should be 1");
|
||||
expect(invoices[0].amount).to.equal(
|
||||
products.proWithTrial.prices[0].amount,
|
||||
"should have paid full amount (trial already used once)",
|
||||
);
|
||||
});
|
||||
});
|
||||
85
server/tests/attach/basic/basic8.ts
Normal file
85
server/tests/attach/basic/basic8.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { products } from "tests/global.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { CusProductStatus } from "@autumn/shared";
|
||||
import { expect } from "chai";
|
||||
import { timeout } from "@/utils/genUtils.js";
|
||||
|
||||
const testCase = "basic8";
|
||||
|
||||
describe(`${chalk.yellowBright("basic8: Testing trial duplicates (same fingerprint)")}`, () => {
|
||||
const customerId = testCase;
|
||||
let customerId2 = testCase + "2";
|
||||
const autumn = new AutumnInt();
|
||||
|
||||
before(async function () {
|
||||
const randFingerprint = Math.random().toString(36).substring(2, 15);
|
||||
await setupBefore(this);
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
fingerprint: randFingerprint,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
await initCustomer({
|
||||
customerId: customerId2,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
fingerprint: randFingerprint,
|
||||
attachPm: "success",
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro with trial and have correct product & invoice", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
const customer = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.proWithTrial,
|
||||
cusRes: customer,
|
||||
status: CusProductStatus.Trialing,
|
||||
});
|
||||
|
||||
const invoices = customer.invoices;
|
||||
expect(invoices.length).to.equal(1, "Invoice length should be 1");
|
||||
expect(invoices[0].total).to.equal(0, "Invoice total should be 0");
|
||||
});
|
||||
|
||||
it("should attach pro with trial to second customer and have correct product & invoice (pro with trial, full price)", async function () {
|
||||
await autumn.attach({
|
||||
customer_id: customerId2,
|
||||
product_id: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
// await timeout(5000); // for webhook to be processed
|
||||
const customer = await AutumnCli.getCustomer(customerId2);
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.proWithTrial,
|
||||
cusRes: customer,
|
||||
status: CusProductStatus.Active,
|
||||
});
|
||||
|
||||
// Check invoice is equal monthly price
|
||||
const invoices = customer.invoices;
|
||||
expect(invoices.length).to.equal(1, "Invoice length should be 1");
|
||||
expect(invoices[0].total).to.equal(
|
||||
10,
|
||||
"Invoice total should be full price",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -7,10 +7,11 @@ import { initCustomer } from "tests/utils/init.js";
|
||||
import { completeCheckoutForm } from "tests/utils/stripeUtils.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
|
||||
const testCase = "basic9";
|
||||
describe(`${chalk.yellowBright(
|
||||
"attach2: Testing monthly with one time prepaid, quantity = 0",
|
||||
"basic9: attach monthly with one time prepaid, and quantity = 0",
|
||||
)}`, () => {
|
||||
let customerId = "attach2";
|
||||
let customerId = testCase;
|
||||
|
||||
let options = [
|
||||
{
|
||||
@@ -12,7 +12,6 @@ import { addPrefixToProducts, runAttachTest } from "../utils.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { constructFeatureItem } from "@/internal/products/product-items/productItemUtils.js";
|
||||
import { expectDowngradeCorrect } from "tests/utils/expectUtils/expectScheduleUtils.js";
|
||||
import { expectNextCycleCorrect } from "tests/utils/expectUtils/expectScheduleUtils.js";
|
||||
|
||||
|
||||
120
server/tests/attach/downgrade/downgrade5.ts
Normal file
120
server/tests/attach/downgrade/downgrade5.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import chalk from "chalk";
|
||||
import Stripe from "stripe";
|
||||
|
||||
import { CusProductStatus } from "@autumn/shared";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { products } from "tests/global.js";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { advanceTestClock } from "@/utils/scriptUtils/testClockUtils.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
|
||||
const testCase = "downgrade5";
|
||||
describe(`${chalk.yellowBright(
|
||||
"downgrade5: testing basic downgrade (paid to paid)",
|
||||
)}`, () => {
|
||||
let customerId = testCase;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let testClockId: string;
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
const { autumnJs } = this;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { customer: customer_, testClockId: testClockId_ } =
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
attachPm: "success",
|
||||
autumn: autumnJs,
|
||||
});
|
||||
|
||||
testClockId = testClockId_;
|
||||
});
|
||||
|
||||
it("should attach premium", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.premium.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct product and entitlements for scheduled pro", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.premium,
|
||||
cusRes: res,
|
||||
});
|
||||
|
||||
const { products: resProducts } = res;
|
||||
|
||||
const resPro = resProducts.find(
|
||||
(p: any) =>
|
||||
p.id === products.pro.id && p.status === CusProductStatus.Scheduled,
|
||||
);
|
||||
|
||||
expect(resPro).to.exist;
|
||||
});
|
||||
|
||||
it("should attach premium and remove scheduled pro", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.premium.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const resPro = res.products.find(
|
||||
(p: any) =>
|
||||
p.id === products.pro.id && p.status === CusProductStatus.Scheduled,
|
||||
);
|
||||
|
||||
expect(resPro).to.not.exist;
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.premium,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
// Advance time 1 month
|
||||
it("should attach pro, advance stripe clock and have pro is attached", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addHours(
|
||||
addMonths(new Date(), 1),
|
||||
hoursToFinalizeInvoice,
|
||||
).getTime(),
|
||||
waitForSeconds: 15,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
});
|
||||
114
server/tests/attach/downgrade/downgrade6.ts
Normal file
114
server/tests/attach/downgrade/downgrade6.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Customer } from "@autumn/shared";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { products } from "tests/global.js";
|
||||
import chalk from "chalk";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { getMainCusProduct } from "@/internal/customers/cusProducts/cusProductUtils.js";
|
||||
|
||||
const testCase = "downgrade6";
|
||||
describe(`${chalk.yellowBright("downgrade6: testing expire button")}`, () => {
|
||||
let customerId = testCase;
|
||||
let testClockId: string;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let customer: Customer;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
|
||||
const { testClockId: testClockId_, customer: customer_ } =
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
});
|
||||
|
||||
customer = customer_;
|
||||
testClockId = testClockId_;
|
||||
});
|
||||
|
||||
it("should attach premium", async function () {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.premium.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should expire premium", async function () {
|
||||
const cusProduct = await getMainCusProduct({
|
||||
db: this.db,
|
||||
internalCustomerId: customer.internal_id,
|
||||
});
|
||||
|
||||
await AutumnCli.expire(cusProduct!.id);
|
||||
});
|
||||
|
||||
it("should have correct product and entitlements after expiration", async function () {
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.free,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
// // 2. Get premium
|
||||
// it("POST /attach -- attaching premium, then attach pro", async function () {
|
||||
// this.timeout(30000);
|
||||
// await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: products.premium.id,
|
||||
// });
|
||||
|
||||
// await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: products.pro.id,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("Expiring pro product (should re-attach premium)", async function () {
|
||||
// this.timeout(30000);
|
||||
|
||||
// // Expire pro product
|
||||
// const customerProduct = await getCusProduct(
|
||||
// this.sb,
|
||||
// customer.internal_id,
|
||||
// products.pro.id,
|
||||
// );
|
||||
// await AutumnCli.expire(customerProduct.id);
|
||||
// await timeout(5000);
|
||||
// });
|
||||
|
||||
// it("GET /customers/:customer_id -- checking product and ents", async function () {
|
||||
// this.timeout(30000);
|
||||
// // Check that free is attached
|
||||
// const res = await AutumnCli.getCustomer(customerId);
|
||||
// compareMainProduct({
|
||||
// sent: products.premium,
|
||||
// cusRes: res,
|
||||
// });
|
||||
|
||||
// // Get stripe subscription (ensure canceled is null)
|
||||
// const stripeCli = createStripeCli({
|
||||
// org: this.org,
|
||||
// env: this.env,
|
||||
// });
|
||||
|
||||
// const premiumCusProduct = await getCusProduct(
|
||||
// this.sb,
|
||||
// customer.internal_id,
|
||||
// products.premium.id,
|
||||
// );
|
||||
|
||||
// const stripeSub = await stripeCli.subscriptions.retrieve(
|
||||
// premiumCusProduct.processor.subscription_id,
|
||||
// );
|
||||
|
||||
// // Check that canceled is null
|
||||
// assert.isNull(stripeSub.canceled_at);
|
||||
// });
|
||||
});
|
||||
80
server/tests/attach/downgrade/downgrade7.ts
Normal file
80
server/tests/attach/downgrade/downgrade7.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Customer } from "@autumn/shared";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { products } from "tests/global.js";
|
||||
import chalk from "chalk";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { findCusProductById } from "@/internal/customers/cusProducts/cusProductUtils/findCusProduct.js";
|
||||
import { expect } from "chai";
|
||||
import { getSubsFromCusId } from "tests/utils/expectUtils/expectSubUtils.js";
|
||||
|
||||
const testCase = "downgrade7";
|
||||
describe(`${chalk.yellowBright("downgrade7: testing expire scheduled product")}`, () => {
|
||||
let customerId = testCase;
|
||||
let testClockId: string;
|
||||
let customer: Customer;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
|
||||
const { testClockId: testClockId_, customer: customer_ } =
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
customer = customer_;
|
||||
testClockId = testClockId_;
|
||||
});
|
||||
|
||||
// 2. Get premium
|
||||
it("should attach premium, then attach pro", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.premium.id,
|
||||
});
|
||||
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should expire scheduled product (pro)", async function () {
|
||||
const cusProduct = await findCusProductById({
|
||||
db: this.db,
|
||||
internalCustomerId: customer.internal_id,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
|
||||
expect(cusProduct).to.exist;
|
||||
await AutumnCli.expire(cusProduct!.id);
|
||||
});
|
||||
|
||||
it("should have correct product and entitlements (premium)", async function () {
|
||||
this.timeout(30000);
|
||||
// Check that free is attached
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.premium,
|
||||
cusRes: res,
|
||||
});
|
||||
|
||||
const { subs } = await getSubsFromCusId({
|
||||
stripeCli: this.stripeCli,
|
||||
customerId: customerId,
|
||||
productId: products.premium.id,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
});
|
||||
expect(subs).to.have.lengthOf(1);
|
||||
expect(subs[0].canceled_at).to.be.null;
|
||||
});
|
||||
});
|
||||
118
server/tests/attach/downgrade/downgrade9.ts
Normal file
118
server/tests/attach/downgrade/downgrade9.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import chalk from "chalk";
|
||||
import Stripe from "stripe";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { advanceProducts } from "tests/global.js";
|
||||
import {
|
||||
checkProductIsScheduled,
|
||||
compareMainProduct,
|
||||
} from "tests/utils/compare.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
|
||||
// TEST MULTI INTERVAL DOWNGRADE
|
||||
//
|
||||
/*
|
||||
CASE 1: Annual pro -> Annual starter
|
||||
- If attach annual starter, should schedule correctly [DONE]
|
||||
- If advance test clock, should downgrade correctly (to monthly starter) [DONE]
|
||||
- If cancel active subscription (on Stripe), should remove scheduled correctly [DONE]
|
||||
- If cancel scheduled subscription (on Stripe), should remove scheduled correctly [DONE]
|
||||
- If expire on dashboard, should remove scheduled correctly
|
||||
|
||||
- If upgrade back to annual pro, should remove scheduled correctly [DONE]
|
||||
- If downgrade to monthly pro (switch downgrade), should be correct [DONE]
|
||||
- If downgrade to free (switch downgrade), should be correct
|
||||
*/
|
||||
|
||||
const testCase = "downgrade9";
|
||||
|
||||
describe(`${chalk.yellowBright("downgrade9: Multi interval downgrade -- Annual pro -> Annual starter")}`, () => {
|
||||
let customerId = testCase;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
withTestClock: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach annual pro", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuProAnnual.id,
|
||||
});
|
||||
|
||||
let cusRes = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuProAnnual,
|
||||
cusRes,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach downgrade to annual starter", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuStarterAnnual.id,
|
||||
});
|
||||
|
||||
let cusRes = await AutumnCli.getCustomer(customerId);
|
||||
checkProductIsScheduled({
|
||||
cusRes,
|
||||
product: advanceProducts.gpuStarterAnnual,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// describe(`${chalk.yellowBright("downgrade9: Multi interval downgrade -- Quarterly pro -> Monthly pro")}`, () => {
|
||||
// let customerId = testCase;
|
||||
// let stripeCli: Stripe;
|
||||
// let testClockId: string;
|
||||
|
||||
// before(async function () {
|
||||
// const { testClockId: insertedTestClockId } =
|
||||
// await initCustomerWithTestClock({
|
||||
// customerId,
|
||||
// org: this.org,
|
||||
// env: this.env,
|
||||
// db: this.db,
|
||||
// });
|
||||
// testClockId = insertedTestClockId;
|
||||
// stripeCli = createStripeCli({
|
||||
// org: this.org,
|
||||
// env: this.env,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should attach quarterly pro", async function () {
|
||||
// let res = await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: advanceProducts.gpuProQuarter.id,
|
||||
// });
|
||||
|
||||
// let cusRes = await AutumnCli.getCustomer(customerId);
|
||||
// compareMainProduct({
|
||||
// sent: advanceProducts.gpuProQuarter,
|
||||
// cusRes,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("should attach downgrade to monthly pro", async function () {
|
||||
// let res = await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: advanceProducts.gpuSystemPro.id,
|
||||
// });
|
||||
|
||||
// let cusRes = await AutumnCli.getCustomer(customerId);
|
||||
// checkProductIsScheduled({
|
||||
// cusRes,
|
||||
// product: advanceProducts.gpuSystemPro,
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
@@ -147,38 +147,4 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to
|
||||
env,
|
||||
});
|
||||
});
|
||||
|
||||
// let nextUsage = 123123912;
|
||||
// it("should cancel and have a final invoice", async function () {
|
||||
// await autumn.cancel({
|
||||
// customer_id: customerId,
|
||||
// product_id: proAnnual.id,
|
||||
// entity_id: entityId,
|
||||
// });
|
||||
|
||||
// await timeout(5000);
|
||||
|
||||
// curUnix = await advanceTestClock({
|
||||
// stripeCli,
|
||||
// testClockId,
|
||||
// advanceTo: addHours(
|
||||
// addMonths(curUnix, 1),
|
||||
// hoursToFinalizeInvoice,
|
||||
// ).getTime(),
|
||||
// });
|
||||
|
||||
// await expectInvoiceAfterUsage({
|
||||
// autumn,
|
||||
// customerId,
|
||||
// entityId,
|
||||
// featureId: TestFeature.Words,
|
||||
// product: proAnnual,
|
||||
// usage: nextUsage,
|
||||
// stripeCli,
|
||||
// db,
|
||||
// org,
|
||||
// env,
|
||||
// numInvoices: 3,
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { attachProducts } from "tests/global.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { initCustomer } from "tests/utils/init.js";
|
||||
import chalk from "chalk";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { Customer } from "@autumn/shared";
|
||||
|
||||
/*
|
||||
FLOW:
|
||||
@@ -10,24 +12,24 @@ FLOW:
|
||||
2. Upgrade pro group 1 -> premium group 1
|
||||
3. Upgrade pro group 2 -> premium group 2
|
||||
*/
|
||||
|
||||
const testCase = "multiProduct1";
|
||||
describe(
|
||||
chalk.yellowBright(
|
||||
"01_multi_product1: Testing multi product attach, and upgrade",
|
||||
),
|
||||
chalk.yellowBright(`${testCase}: Testing multi product attach, and upgrade`),
|
||||
() => {
|
||||
let customerId = "multi-product-attach-upgrade";
|
||||
let customerId = testCase;
|
||||
let customer: Customer;
|
||||
before(async function () {
|
||||
this.customer = await initCustomer({
|
||||
await setupBefore(this);
|
||||
const res = await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
customer_data: {
|
||||
id: customerId,
|
||||
name: customerId,
|
||||
email: "multi-product-attach-upgrade@example.com",
|
||||
},
|
||||
env: this.env,
|
||||
attachPm: true,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
customer = res.customer;
|
||||
});
|
||||
|
||||
it("should attach pro group 1 and pro group 2", async function () {
|
||||
@@ -50,8 +52,6 @@ describe(
|
||||
// 1. Compare main product
|
||||
const cusRes = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({ sent: attachProducts.premiumGroup1, cusRes });
|
||||
|
||||
// 2. Check latest invoice
|
||||
});
|
||||
|
||||
it("should upgrade to premium group 2", async function () {
|
||||
@@ -1,17 +1,20 @@
|
||||
import chalk from "chalk";
|
||||
|
||||
import { Stripe } from "stripe";
|
||||
import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
|
||||
import { CusProductStatus, Customer } from "@autumn/shared";
|
||||
import { expect } from "chai";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { advanceProducts, attachProducts } from "tests/global.js";
|
||||
import { attachProducts } from "tests/global.js";
|
||||
import {
|
||||
checkProductIsScheduled,
|
||||
compareMainProduct,
|
||||
} from "tests/utils/compare.js";
|
||||
import { initCustomer } from "tests/utils/init.js";
|
||||
import { searchCusProducts, timeout } from "tests/utils/genUtils.js";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { Stripe } from "stripe";
|
||||
|
||||
import { searchCusProducts } from "tests/utils/genUtils.js";
|
||||
import { checkScheduleContainsProducts } from "tests/utils/scheduleCheckUtils.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
|
||||
/*
|
||||
FLOW:
|
||||
@@ -20,26 +23,27 @@ FLOW:
|
||||
3. Downgrade to starter group 2
|
||||
4. Change downgrade to pro group 2
|
||||
*/
|
||||
describe("Multi Product 3: premium1->starter1, premium2->starter2, then premium2->pro2, then premium2->free", () => {
|
||||
let customerId = "multi-side-free-downgrade";
|
||||
|
||||
const testCase = "multiProduct2";
|
||||
describe(`${chalk.yellowBright(
|
||||
"multiProduct2: premium1->starter1, premium2->starter2, then premium2->pro2, then premium2->free",
|
||||
)}`, () => {
|
||||
let customerId = testCase;
|
||||
let customer: Customer;
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
customer = await initCustomer({
|
||||
await setupBefore(this);
|
||||
stripeCli = this.stripeCli;
|
||||
const res = await initCustomer({
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
customer_data: {
|
||||
id: customerId,
|
||||
name: customerId,
|
||||
email: "multi-side-free-downgrade@example.com",
|
||||
},
|
||||
env: this.env,
|
||||
attachPm: true,
|
||||
});
|
||||
stripeCli = createStripeCli({
|
||||
org: this.org,
|
||||
customerId,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
customer = res.customer;
|
||||
});
|
||||
|
||||
it("should attach premium group 1 and premium group 2", async function () {
|
||||
@@ -121,14 +125,6 @@ describe("Multi Product 3: premium1->starter1, premium2->starter2, then premium2
|
||||
});
|
||||
});
|
||||
|
||||
// it("should upgrade to premium group 1 again, then downgrade back to starter group 1", async function () {
|
||||
// await AutumnCli.attach({
|
||||
// customerId: customerId,
|
||||
// productId: attachProducts.premiumGroup1.id,
|
||||
// });
|
||||
|
||||
// });
|
||||
|
||||
it("should downgrade to free", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
@@ -160,15 +156,3 @@ describe("Multi Product 3: premium1->starter1, premium2->starter2, then premium2
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Advance test clock
|
||||
// 1. Premium 1 -> Starter 1, Premium 2 -> Starter 2, Advance Test Clock [OK]
|
||||
// 2. Premium 1 -> Starter 1, Premium 2, Advance Test Clock
|
||||
// 3. Premium 1 -> Starter 1, Premium 2 -> Free, Advance Test Clock
|
||||
|
||||
// EXPIRE BUTTON
|
||||
// 1. Premium 1 -> Starter 1, Premium 2 -> Starter 2, Expire Starter 1, Expire Starter 2
|
||||
// 2. Premium 1 -> Starter 1, Premium 2 -> Starter 2, Cancel Premium 1, or 2 immediately...
|
||||
|
||||
// UPGRADES
|
||||
// 1. Premium 1 -> Starter 1, Premium 2 -> Starter 2, Upgrade to Starter 1
|
||||
@@ -1,51 +1,47 @@
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { getStripeSubs } from "@/external/stripe/stripeSubUtils.js";
|
||||
|
||||
import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
|
||||
import chalk from "chalk";
|
||||
|
||||
import { expect } from "chai";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { attachProducts } from "tests/global.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { initCustomer } from "tests/utils/init.js";
|
||||
import { searchCusProducts, timeout } from "tests/utils/genUtils.js";
|
||||
import chalk from "chalk";
|
||||
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { getStripeSubs } from "@/external/stripe/stripeSubUtils.js";
|
||||
import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import Stripe from "stripe";
|
||||
|
||||
// TESTING DOWNGRADE DOWNGRADE THEN
|
||||
// 1. UPGRADE FIRST PRODUCT BACK -- SHOULD REPLACE SCHEDULE WITH OLD FIRST PRODUCT
|
||||
// 2. UPGRADE SECOND PRODUCT BACK -- SHOULD CANCEL SCHEDULE
|
||||
|
||||
const testCase = "multiProduct3";
|
||||
describe(
|
||||
chalk.yellowBright(
|
||||
"Multi Product 4: double downgrade, double upgrade (back)",
|
||||
),
|
||||
chalk.yellowBright(`${testCase}: double downgrade, double upgrade (back)`),
|
||||
() => {
|
||||
let customerId = "multi-double-downgrade-upgrade";
|
||||
let customerId = testCase;
|
||||
let customer;
|
||||
|
||||
let stripeCli;
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
customer = await initCustomer({
|
||||
await setupBefore(this);
|
||||
stripeCli = this.stripeCli;
|
||||
const res = await initCustomer({
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
customer_data: {
|
||||
id: customerId,
|
||||
name: customerId,
|
||||
email: "multi-product-upgrade-test@example.com",
|
||||
},
|
||||
attachPm: true,
|
||||
customerId,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
stripeCli = createStripeCli({
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
});
|
||||
customer = res.customer;
|
||||
});
|
||||
|
||||
it("should attach premium group 1 and premium group 2", async function () {
|
||||
let res = await AutumnCli.attach({
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productIds: [
|
||||
attachProducts.premiumGroup1.id,
|
||||
@@ -59,7 +55,7 @@ describe(
|
||||
});
|
||||
|
||||
it("should attach starter group 1, then starter group 2", async function () {
|
||||
let res = await AutumnCli.attach({
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: attachProducts.starterGroup1.id,
|
||||
});
|
||||
@@ -71,20 +67,18 @@ describe(
|
||||
});
|
||||
|
||||
it("should reattach premium group 1", async function () {
|
||||
await timeout(3000);
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: attachProducts.premiumGroup1.id,
|
||||
});
|
||||
|
||||
// Check that schedule contains premium group 1, and starter group 2...
|
||||
await timeout(10000);
|
||||
|
||||
const cusProducts = await CusProductService.list({
|
||||
db: this.db,
|
||||
internalCustomerId: customer!.internal_id,
|
||||
});
|
||||
|
||||
// 1. Check that premium group 1 is active, and had scheduled_ids
|
||||
let premiumGroup1 = searchCusProducts({
|
||||
cusProducts,
|
||||
productId: attachProducts.premiumGroup1.id,
|
||||
@@ -119,7 +113,7 @@ describe(
|
||||
productId: attachProducts.premiumGroup2.id,
|
||||
});
|
||||
|
||||
await timeout(5000);
|
||||
await timeout(10000);
|
||||
|
||||
const cusProducts = await CusProductService.list({
|
||||
db: this.db,
|
||||
@@ -157,12 +151,5 @@ describe(
|
||||
expect(sub.cancel_at).to.equal(null);
|
||||
expect(sub.status).to.equal("active");
|
||||
});
|
||||
|
||||
// it("should reattach premium group 2", async function () {
|
||||
// let res = await AutumnCli.attach({
|
||||
// customerId: this.customer.id,
|
||||
// productId: attachProducts.premiumGroup2.id,
|
||||
// });
|
||||
// });
|
||||
},
|
||||
);
|
||||
161
server/tests/attach/others/others4.ts
Normal file
161
server/tests/attach/others/others4.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { assert } from "chai";
|
||||
import { features, products } from "tests/global.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { getPublicAxiosInstance } from "tests/utils/setup.js";
|
||||
import { completeCheckoutForm } from "tests/utils/stripeUtils.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { ErrCode } from "@autumn/shared";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
|
||||
const testCase = "others4";
|
||||
describe(`${chalk.yellowBright("others4: Testing publishable key")}`, () => {
|
||||
// 1. Initialize customer with card
|
||||
let customerId = testCase;
|
||||
const bearerPublicAxios = getPublicAxiosInstance({
|
||||
withBearer: true,
|
||||
});
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
});
|
||||
});
|
||||
|
||||
it("should return a 401 if the pkey is invalid", async function () {
|
||||
this.timeout(30000);
|
||||
const axiosInstance = getPublicAxiosInstance({
|
||||
withBearer: true,
|
||||
pkey: "am_pk_test_invalid",
|
||||
});
|
||||
|
||||
try {
|
||||
const { data } = await axiosInstance.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
|
||||
throw new Error("Should not be able to attach");
|
||||
} catch (error: any) {
|
||||
assert.equal(error.response.status, 401);
|
||||
}
|
||||
});
|
||||
|
||||
it("should return checkout URL for both bearer key", async function () {
|
||||
this.timeout(30000);
|
||||
const axiosInstanceBearer = getPublicAxiosInstance({
|
||||
withBearer: true,
|
||||
});
|
||||
|
||||
// 1. Should be able to upgrade to pro
|
||||
const { data } = await axiosInstanceBearer.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
|
||||
assert.exists(data.checkout_url);
|
||||
|
||||
await completeCheckoutForm(data.checkout_url);
|
||||
|
||||
await timeout(5000);
|
||||
});
|
||||
|
||||
it("should have customer with product", async function () {
|
||||
this.timeout(30000);
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
it("should return error if try to upgrade or downgrade without pkey", async function () {
|
||||
this.timeout(30000);
|
||||
const axiosInstance = getPublicAxiosInstance({
|
||||
withBearer: true,
|
||||
});
|
||||
|
||||
try {
|
||||
await axiosInstance.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
product_id: products.premium.id,
|
||||
});
|
||||
|
||||
throw new Error("Should not be able to attach");
|
||||
} catch (error: any) {
|
||||
assert.equal(error.response.status, 400);
|
||||
assert.equal(error.response.data.code, ErrCode.InvalidRequest);
|
||||
}
|
||||
});
|
||||
|
||||
it("should return error if try to downgrade to free", async function () {
|
||||
try {
|
||||
await bearerPublicAxios.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
product_id: products.free.id,
|
||||
});
|
||||
|
||||
throw new Error("Should not be able to attach");
|
||||
} catch (error: any) {
|
||||
assert.equal(error.response.status, 400);
|
||||
assert.equal(error.response.data.code, ErrCode.InvalidRequest);
|
||||
}
|
||||
});
|
||||
|
||||
// Next, check entitled for pro
|
||||
it("should return correct metered1 amount for pro", async function () {
|
||||
const { data } = await bearerPublicAxios.post("/v1/entitled", {
|
||||
customer_id: customerId,
|
||||
feature_id: features.metered1.id,
|
||||
});
|
||||
|
||||
assert.equal(data.allowed, true);
|
||||
const metered1Balance = data.balances.find(
|
||||
(b: any) => b.feature_id === features.metered1.id,
|
||||
);
|
||||
assert.equal(
|
||||
metered1Balance.balance,
|
||||
products.pro.entitlements.metered1.allowance,
|
||||
);
|
||||
});
|
||||
|
||||
it("should return same balance for entitled with bearer and x-publishable-key", async function () {
|
||||
const { data } = await bearerPublicAxios.post("/v1/entitled", {
|
||||
customer_id: customerId,
|
||||
feature_id: features.metered1.id,
|
||||
});
|
||||
|
||||
assert.equal(data.allowed, true);
|
||||
const metered1Balance = data.balances.find(
|
||||
(b: any) => b.feature_id === features.metered1.id,
|
||||
);
|
||||
assert.equal(
|
||||
metered1Balance.balance,
|
||||
products.pro.entitlements.metered1.allowance,
|
||||
);
|
||||
});
|
||||
|
||||
it("should return error when try to send event", async function () {
|
||||
try {
|
||||
await bearerPublicAxios.post("/v1/events", {
|
||||
customer_id: customerId,
|
||||
event_name: features.metered1.id,
|
||||
properties: {
|
||||
value: 10,
|
||||
},
|
||||
});
|
||||
|
||||
throw new Error("Should not be able to send event");
|
||||
} catch (error: any) {
|
||||
assert.equal(error.response.status, 401);
|
||||
assert.equal(error.response.data.code, ErrCode.EndpointNotPublic);
|
||||
}
|
||||
});
|
||||
});
|
||||
243
server/tests/attach/others/others5.ts
Normal file
243
server/tests/attach/others/others5.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
import chalk from "chalk";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { features, products } from "tests/global.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { timeout } from "../../utils/genUtils.js";
|
||||
import { expect } from "chai";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
|
||||
const checkEntitledOnProduct = async ({
|
||||
customerId,
|
||||
product,
|
||||
totalAllowance,
|
||||
finish = false,
|
||||
usageBased = false,
|
||||
}: {
|
||||
customerId: string;
|
||||
product: any;
|
||||
totalAllowance?: number;
|
||||
finish?: boolean;
|
||||
usageBased?: boolean;
|
||||
}) => {
|
||||
// 1. Send events
|
||||
const allowance = totalAllowance || product.entitlements.metered1.allowance;
|
||||
// const randomNum = Math.floor(Math.random() * (allowance - 1));
|
||||
const randomNum = 3;
|
||||
|
||||
const batchUpdates = [];
|
||||
for (let i = 0; i < randomNum; i++) {
|
||||
batchUpdates.push(
|
||||
AutumnCli.sendEvent({
|
||||
customerId: customerId,
|
||||
eventName: features.metered1.eventName,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(batchUpdates);
|
||||
await timeout(8000);
|
||||
let used = randomNum;
|
||||
|
||||
// 2. Check entitled
|
||||
const { allowed, balanceObj }: any = await AutumnCli.entitled(
|
||||
customerId,
|
||||
features.metered1.id,
|
||||
true,
|
||||
);
|
||||
|
||||
try {
|
||||
expect(allowed).to.be.true;
|
||||
expect(balanceObj!.balance).to.equal(allowance - randomNum);
|
||||
|
||||
if (!finish) {
|
||||
return used;
|
||||
}
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.group();
|
||||
console.log("Allowance: ", allowance, "Random num: ", randomNum);
|
||||
console.log("Expected balance to be: ", allowance - randomNum);
|
||||
console.log("Entitled res: ", { allowed, balanceObj });
|
||||
console.groupEnd();
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Finish up
|
||||
const batchUpdates2 = [];
|
||||
for (let i = 0; i < allowance - randomNum; i++) {
|
||||
batchUpdates2.push(
|
||||
AutumnCli.sendEvent({
|
||||
customerId: customerId,
|
||||
eventName: features.metered1.eventName,
|
||||
}),
|
||||
);
|
||||
}
|
||||
await Promise.all(batchUpdates2);
|
||||
await timeout(8000);
|
||||
used += allowance - randomNum;
|
||||
|
||||
// 3. Check entitled again
|
||||
const { allowed: allowed2, balanceObj: balanceObj2 }: any =
|
||||
await AutumnCli.entitled(customerId, features.metered1.id, true);
|
||||
try {
|
||||
if (usageBased) {
|
||||
expect(allowed2).to.be.true;
|
||||
} else {
|
||||
expect(allowed2).to.be.false;
|
||||
}
|
||||
expect(balanceObj2!.balance).to.equal(0);
|
||||
return used;
|
||||
} catch (error) {
|
||||
console.group();
|
||||
console.group();
|
||||
console.log("Expected balance to be: ", 0);
|
||||
console.log("Entitled res: ", { allowed2, balanceObj2 });
|
||||
console.groupEnd();
|
||||
console.groupEnd();
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Add test case for unlimited feature
|
||||
|
||||
const testCase = "others5";
|
||||
describe(`${chalk.yellowBright(
|
||||
"others5: Testing /events and /entitled, for pro, one time top up",
|
||||
)}`, () => {
|
||||
const customerId = testCase;
|
||||
|
||||
let curAllowance = 0;
|
||||
const oneTimeBillingUnits =
|
||||
products.oneTimeAddOnMetered1.prices[0].config.billing_units!;
|
||||
let oneTimeQuantity = 2 * oneTimeBillingUnits;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct entitlements (free)", async function () {
|
||||
await checkEntitledOnProduct({
|
||||
customerId: customerId,
|
||||
product: products.free,
|
||||
finish: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach pro", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct entitlements (pro)", async function () {
|
||||
const used = await checkEntitledOnProduct({
|
||||
customerId: customerId,
|
||||
product: products.pro,
|
||||
finish: false,
|
||||
});
|
||||
|
||||
curAllowance = products.pro.entitlements.metered1.allowance! - used;
|
||||
});
|
||||
|
||||
it("should attach one time top up", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.oneTimeAddOnMetered1.id,
|
||||
options: [
|
||||
{
|
||||
feature_id: features.metered1.id,
|
||||
quantity: oneTimeQuantity,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct entitlements (one time top up)", async function () {
|
||||
// const oneTimeAmt = oneTimeBillingUnits * oneTimeQuantity;
|
||||
|
||||
await checkEntitledOnProduct({
|
||||
customerId: customerId,
|
||||
product: products.oneTimeAddOnMetered1,
|
||||
finish: true,
|
||||
totalAllowance: curAllowance + oneTimeQuantity,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`${chalk.yellowBright(
|
||||
"others5: Testing /entitled & /events, for pro with overage",
|
||||
)}`, () => {
|
||||
const customerId = testCase;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
});
|
||||
|
||||
// PRO WITH OVERAGE
|
||||
it("should attach pro (with overage)", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.proWithOverage.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct entitlements (pro with overage)", async function () {
|
||||
await checkEntitledOnProduct({
|
||||
customerId: customerId,
|
||||
product: products.proWithOverage,
|
||||
finish: true,
|
||||
totalAllowance: products.proWithOverage.entitlements.metered1.allowance!,
|
||||
usageBased: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct usage-based balance (balance < 0)", async function () {
|
||||
const { allowed, balanceObj }: any = await AutumnCli.entitled(
|
||||
customerId,
|
||||
features.metered1.id,
|
||||
true,
|
||||
);
|
||||
|
||||
expect(allowed).to.be.true;
|
||||
expect(balanceObj!.balance).to.equal(0);
|
||||
|
||||
// Sent 5 events
|
||||
const batchUpdates = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
batchUpdates.push(
|
||||
AutumnCli.sendEvent({
|
||||
customerId: customerId,
|
||||
eventName: features.metered1.eventName,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(batchUpdates);
|
||||
await timeout(5000);
|
||||
|
||||
const { allowed: allowed2, balanceObj: balanceObj2 }: any =
|
||||
await AutumnCli.entitled(customerId, features.metered1.id, true);
|
||||
|
||||
expect(allowed2).to.be.true;
|
||||
expect(balanceObj2!.balance).to.equal(-5);
|
||||
expect(balanceObj2!.usage_allowed).to.be.true;
|
||||
});
|
||||
});
|
||||
@@ -65,6 +65,7 @@ const runUpdateEntsTest = async ({
|
||||
expect(preview.branch).to.equal(AttachBranch.NewVersion);
|
||||
} else {
|
||||
expect(preview.branch).to.equal(AttachBranch.SameCustomEnts);
|
||||
expect(preview.due_today).to.be.undefined;
|
||||
}
|
||||
|
||||
await autumn.attach({
|
||||
|
||||
@@ -30,17 +30,6 @@ export let pro = constructProduct({
|
||||
type: "pro",
|
||||
});
|
||||
|
||||
/**
|
||||
* upgrade3:
|
||||
* Testing upgrades for arrear prorated
|
||||
* 1. Start with pro monthly plan (usage-based)
|
||||
* 2. Upgrade to pro annual plan (usage-based)
|
||||
* 3. Upgrade to premium annual plan (usage-based)
|
||||
*
|
||||
* Verifies subscription items and anchors are correct after each upgrade
|
||||
* with arrear prorated billing
|
||||
*/
|
||||
|
||||
describe(`${chalk.yellowBright(`${testCase}: Testing update ents (changing included usage)`)}`, () => {
|
||||
let customerId = testCase;
|
||||
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
|
||||
@@ -60,15 +49,6 @@ describe(`${chalk.yellowBright(`${testCase}: Testing update ents (changing inclu
|
||||
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { testClockId: testClockId1 } = await initCustomer({
|
||||
autumn: autumnJs,
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
addPrefixToProducts({
|
||||
products: [pro],
|
||||
prefix: testCase,
|
||||
@@ -83,10 +63,19 @@ describe(`${chalk.yellowBright(`${testCase}: Testing update ents (changing inclu
|
||||
customerId,
|
||||
});
|
||||
|
||||
const { testClockId: testClockId1 } = await initCustomer({
|
||||
autumn: autumnJs,
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
testClockId = testClockId1!;
|
||||
});
|
||||
|
||||
it("should attach pro product (prepaid single use)", async function () {
|
||||
it("should attach pro product", async function () {
|
||||
await runAttachTest({
|
||||
autumn,
|
||||
customerId,
|
||||
@@ -112,7 +101,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing update ents (changing inclu
|
||||
let usage = 50000;
|
||||
let overage = 50000 - (newItem.included_usage as number);
|
||||
|
||||
it("should attach custom pro product", async function () {
|
||||
it("should update overage item to have new included usage", async function () {
|
||||
const customProduct = {
|
||||
...pro,
|
||||
items: customItems,
|
||||
@@ -144,7 +133,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing update ents (changing inclu
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct invoice usage next cycle", async function () {
|
||||
it("should have correct invoice next cycle", async function () {
|
||||
const invoiceTotal = await getExpectedInvoiceTotal({
|
||||
org,
|
||||
env,
|
||||
@@ -175,7 +164,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing update ents (changing inclu
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
const invoice = customer.invoices[0];
|
||||
const invoice = customer.invoices![0];
|
||||
expect(invoice.total).to.equal(
|
||||
invoiceTotal,
|
||||
"invoice total after 1 cycle should be correct",
|
||||
|
||||
@@ -183,7 +183,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing update ents (changing inclu
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
const invoice = customer.invoices[0];
|
||||
const invoice = customer.invoices![0];
|
||||
expect(invoice.total).to.equal(
|
||||
invoiceTotal,
|
||||
"invoice total after 1 cycle should be correct",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
|
||||
import {
|
||||
APIVersion,
|
||||
AppEnv,
|
||||
@@ -19,6 +19,7 @@ import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { addWeeks } from "date-fns";
|
||||
import { expectAutumnError } from "tests/utils/expectUtils/expectErrUtils.js";
|
||||
import { timeout } from "@/utils/genUtils.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
|
||||
const testCase = "updateQuantity1";
|
||||
|
||||
@@ -33,17 +34,6 @@ export let pro = constructProduct({
|
||||
type: "pro",
|
||||
});
|
||||
|
||||
/**
|
||||
* upgrade3:
|
||||
* Testing upgrades for arrear prorated
|
||||
* 1. Start with pro monthly plan (usage-based)
|
||||
* 2. Upgrade to pro annual plan (usage-based)
|
||||
* 3. Upgrade to premium annual plan (usage-based)
|
||||
*
|
||||
* Verifies subscription items and anchors are correct after each upgrade
|
||||
* with arrear prorated billing
|
||||
*/
|
||||
|
||||
describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid single use`)}`, () => {
|
||||
let customerId = testCase;
|
||||
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
|
||||
@@ -80,6 +70,9 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid singl
|
||||
await createProducts({
|
||||
autumn,
|
||||
products: [pro],
|
||||
db,
|
||||
orgId: org.id,
|
||||
env,
|
||||
});
|
||||
|
||||
testClockId = testClockId1!;
|
||||
@@ -110,40 +103,40 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid singl
|
||||
errCode: AttachErrCode.ProductAlreadyAttached,
|
||||
func: async () => {
|
||||
await autumn.attach({
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
options: proOpts,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const newOpts = [
|
||||
{
|
||||
feature_id: TestFeature.Users,
|
||||
quantity: 1,
|
||||
},
|
||||
];
|
||||
it("should throw error if try to reduce seats to less than current usage", async function () {
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
feature_id: TestFeature.Users,
|
||||
value: 2,
|
||||
});
|
||||
// const newOpts = [
|
||||
// {
|
||||
// feature_id: TestFeature.Users,
|
||||
// quantity: 1,
|
||||
// },
|
||||
// ];
|
||||
// it("should throw error if try to reduce seats to less than current usage", async function () {
|
||||
// await autumn.track({
|
||||
// customer_id: customerId,
|
||||
// feature_id: TestFeature.Users,
|
||||
// value: 2,
|
||||
// });
|
||||
|
||||
await timeout(1000);
|
||||
// await timeout(1000);
|
||||
|
||||
await expectAutumnError({
|
||||
errCode: AttachErrCode.InvalidOptions,
|
||||
func: async () => {
|
||||
await autumn.attach({
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
options: newOpts,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
// await expectAutumnError({
|
||||
// errCode: AttachErrCode.InvalidOptions,
|
||||
// func: async () => {
|
||||
// await autumn.attach({
|
||||
// customer_id: customerId,
|
||||
// product_id: pro.id,
|
||||
// options: newOpts,
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
|
||||
const updatedOpts = [
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ let growth = constructProduct({
|
||||
type: "growth",
|
||||
});
|
||||
|
||||
describe(`${chalk.yellowBright("attach/upgrade1: Testing usage upgrades")}`, () => {
|
||||
describe(`${chalk.yellowBright("upgrade1: Testing usage upgrades")}`, () => {
|
||||
let customerId = "upgrade1";
|
||||
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
|
||||
|
||||
|
||||
@@ -6,17 +6,11 @@ import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import {
|
||||
APIVersion,
|
||||
AppEnv,
|
||||
FullCusProduct,
|
||||
Organization,
|
||||
} from "@autumn/shared";
|
||||
import { APIVersion, AppEnv, Organization } from "@autumn/shared";
|
||||
import { addPrefixToProducts, runAttachTest } from "../utils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { addWeeks, getDate } from "date-fns";
|
||||
import { addWeeks } from "date-fns";
|
||||
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
|
||||
@@ -51,7 +45,7 @@ export let premiumAnnual = constructProduct({
|
||||
* Verifies subscription items and anchors are correct after each upgrade
|
||||
*/
|
||||
|
||||
describe(`${chalk.yellowBright("attach/upgrade2: Testing usage upgrades with monthly -> annual")}`, () => {
|
||||
describe(`${chalk.yellowBright("upgrade2: Testing usage upgrades with monthly -> annual")}`, () => {
|
||||
let customerId = "upgrade2";
|
||||
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
|
||||
let stripeCli: Stripe;
|
||||
@@ -153,6 +147,7 @@ describe(`${chalk.yellowBright("attach/upgrade2: Testing usage upgrades with mon
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
singleInvoice: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ export let pro = constructProduct({
|
||||
export let premium = constructProduct({
|
||||
items: [
|
||||
constructPrepaidItem({
|
||||
featureId: TestFeature.Users,
|
||||
featureId: TestFeature.Messages,
|
||||
price: 8,
|
||||
billingUnits: 100,
|
||||
}),
|
||||
@@ -37,29 +37,6 @@ export let premium = constructProduct({
|
||||
type: "premium",
|
||||
});
|
||||
|
||||
export let proAnnual = constructProduct({
|
||||
items: [
|
||||
constructPrepaidItem({
|
||||
featureId: TestFeature.Users,
|
||||
price: 12,
|
||||
billingUnits: 1,
|
||||
}),
|
||||
],
|
||||
type: "pro",
|
||||
isAnnual: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* upgrade3:
|
||||
* Testing upgrades for arrear prorated
|
||||
* 1. Start with pro monthly plan (usage-based)
|
||||
* 2. Upgrade to pro annual plan (usage-based)
|
||||
* 3. Upgrade to premium annual plan (usage-based)
|
||||
*
|
||||
* Verifies subscription items and anchors are correct after each upgrade
|
||||
* with arrear prorated billing
|
||||
*/
|
||||
|
||||
describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid single use`)}`, () => {
|
||||
let customerId = testCase;
|
||||
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
|
||||
@@ -89,13 +66,13 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid singl
|
||||
});
|
||||
|
||||
addPrefixToProducts({
|
||||
products: [pro, premium, proAnnual],
|
||||
products: [pro, premium],
|
||||
prefix: testCase,
|
||||
});
|
||||
|
||||
await createProducts({
|
||||
autumn,
|
||||
products: [pro, premium, proAnnual],
|
||||
products: [pro, premium],
|
||||
db,
|
||||
orgId: org.id,
|
||||
env,
|
||||
@@ -136,7 +113,9 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid singl
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addWeeks(curUnix, 1).getTime(),
|
||||
waitForSeconds: 20,
|
||||
});
|
||||
|
||||
await runAttachTest({
|
||||
autumn,
|
||||
customerId,
|
||||
|
||||
@@ -10,12 +10,10 @@ import { addPrefixToProducts, runAttachTest } from "../utils.js";
|
||||
import {
|
||||
constructArrearItem,
|
||||
constructArrearProratedItem,
|
||||
constructPrepaidItem,
|
||||
} from "@/utils/scriptUtils/constructItem.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { addWeeks } from "date-fns";
|
||||
|
||||
import { expectAutumnError } from "tests/utils/expectUtils/expectErrUtils.js";
|
||||
import { attachFailedPaymentMethod } from "@/external/stripe/stripeCusUtils.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
@@ -55,9 +53,6 @@ describe(`${chalk.yellowBright(`${testCase}: Testing failed upgrades`)}`, () =>
|
||||
let db: DrizzleCli, org: Organization, env: AppEnv;
|
||||
let stripeCli: Stripe;
|
||||
|
||||
let curUnix = new Date().getTime();
|
||||
let numUsers = 0;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
const { autumnJs } = this;
|
||||
@@ -121,6 +116,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing failed upgrades`)}`, () =>
|
||||
});
|
||||
|
||||
await attachFailedPaymentMethod({ stripeCli, customer: cus! });
|
||||
await timeout(2000);
|
||||
|
||||
await expectAutumnError({
|
||||
func: async () => {
|
||||
|
||||
75
server/tests/attach/upgradeOld/upgradeOld1.ts
Normal file
75
server/tests/attach/upgradeOld/upgradeOld1.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import chalk from "chalk";
|
||||
import { products } from "tests/global.js";
|
||||
import { assert } from "chai";
|
||||
import { Customer } from "@autumn/shared";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { addDays } from "date-fns";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { advanceTestClock } from "@/utils/scriptUtils/testClockUtils.js";
|
||||
import Stripe from "stripe";
|
||||
|
||||
describe(`${chalk.yellowBright(
|
||||
"upgradeOld1: Testing upgrade (trial to paid)",
|
||||
)}`, () => {
|
||||
const customerId = "upgradeOld1";
|
||||
let testClockId: string;
|
||||
let customer: Customer;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
stripeCli = this.stripeCli;
|
||||
const { customer: customer_, testClockId: testClockId_ } =
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
customer = customer_;
|
||||
testClockId = testClockId_;
|
||||
});
|
||||
|
||||
it("should attach pro with trial", async function () {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.proWithTrial.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach premium", async function () {
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addDays(new Date(), 3).getTime(),
|
||||
waitForSeconds: 10,
|
||||
});
|
||||
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.premium.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should check product, ents and invoices", async function () {
|
||||
const res = await autumn.customers.get(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.premium,
|
||||
cusRes: res,
|
||||
});
|
||||
|
||||
const invoices = await res.invoices;
|
||||
|
||||
assert.equal(
|
||||
invoices[0].total,
|
||||
products.premium.prices[0].config.amount,
|
||||
"Invoice should be for 50.00",
|
||||
);
|
||||
});
|
||||
});
|
||||
52
server/tests/attach/upgradeOld/upgradeOld2.ts
Normal file
52
server/tests/attach/upgradeOld/upgradeOld2.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import Stripe from "stripe";
|
||||
import chalk from "chalk";
|
||||
import { Customer } from "@autumn/shared";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { products } from "tests/global.js";
|
||||
import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
|
||||
|
||||
describe(`${chalk.yellowBright(
|
||||
"upgradeOld2: Testing upgrade (paid to trial)",
|
||||
)}`, () => {
|
||||
const customerId = "upgradeOld2";
|
||||
let testClockId: string;
|
||||
let customer: Customer;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let stripeCli: Stripe;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
stripeCli = this.stripeCli;
|
||||
const { customer: customer_, testClockId: testClockId_ } =
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
customer = customer_;
|
||||
testClockId = testClockId_;
|
||||
});
|
||||
|
||||
it("should attach pro", async function () {
|
||||
this.timeout(30000);
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach premium with trial and have trial", async function () {
|
||||
this.timeout(30000);
|
||||
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.premiumWithTrial.id,
|
||||
});
|
||||
});
|
||||
});
|
||||
72
server/tests/attach/upgradeOld/upgradeOld3.ts
Normal file
72
server/tests/attach/upgradeOld/upgradeOld3.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import assert from "assert";
|
||||
import chalk from "chalk";
|
||||
import { CusProductStatus } from "@autumn/shared";
|
||||
import { addDays } from "date-fns";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { products } from "tests/global.js";
|
||||
import Stripe from "stripe";
|
||||
import { advanceTestClock } from "@/utils/scriptUtils/testClockUtils.js";
|
||||
|
||||
describe(`${chalk.yellowBright("upgradeOld3: Testing upgrade (trial to trial)")}`, () => {
|
||||
const customerId = "upgradeOld3";
|
||||
let testClockId: string;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
let stripeCli: Stripe;
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
stripeCli = this.stripeCli;
|
||||
const { customer: customer_, testClockId: testClockId_ } =
|
||||
await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
testClockId = testClockId_;
|
||||
});
|
||||
|
||||
it("should attach pro with trial", async function () {
|
||||
this.timeout(30000);
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.proWithTrial.id,
|
||||
});
|
||||
|
||||
console.log(` ${chalk.greenBright("Attached pro with trial")}`);
|
||||
});
|
||||
|
||||
it("should attach premium with trial", async function () {
|
||||
const advanceTo = addDays(new Date(), 3).getTime();
|
||||
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo,
|
||||
waitForSeconds: 10,
|
||||
});
|
||||
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.premiumWithTrial.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("should check product and ents", async function () {
|
||||
const res = await autumn.customers.get(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.premiumWithTrial,
|
||||
cusRes: res,
|
||||
status: CusProductStatus.Trialing,
|
||||
});
|
||||
|
||||
const invoices = res.invoices;
|
||||
|
||||
assert.equal(invoices![0].total, 0, "Invoice should be 0");
|
||||
});
|
||||
});
|
||||
116
server/tests/attach/upgradeOld/upgradeOld4.ts
Normal file
116
server/tests/attach/upgradeOld/upgradeOld4.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
// TESTING UPGRADES
|
||||
|
||||
import chalk from "chalk";
|
||||
import { AppEnv, Organization } from "@autumn/shared";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { products } from "tests/global.js";
|
||||
import {
|
||||
attachFailedPaymentMethod,
|
||||
attachPmToCus,
|
||||
} from "@/external/stripe/stripeCusUtils.js";
|
||||
import { Customer } from "@autumn/shared";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { expectAutumnError } from "tests/utils/expectUtils/expectErrUtils.js";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import Stripe from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
|
||||
const testCase = "upgradeOld4";
|
||||
describe(`${chalk.yellowBright("upgradeOld4: Testing upgrade from pro -> premium")}`, () => {
|
||||
let customer: Customer;
|
||||
let customerId = testCase;
|
||||
|
||||
let stripeCli: Stripe;
|
||||
let db: DrizzleCli;
|
||||
let org: Organization;
|
||||
let env: AppEnv;
|
||||
let autumn: AutumnInt = new AutumnInt();
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
|
||||
stripeCli = this.stripeCli;
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
|
||||
let { customer: customer_ } = await initCustomer({
|
||||
autumn: this.autumnJs,
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
customer = customer_;
|
||||
});
|
||||
|
||||
it("should attach pro (trial)", async function () {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
|
||||
let res = await autumn.customers.get(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
// 1. Try force checkout...
|
||||
it("should attach premium and not be able to force checkout", async function () {
|
||||
expectAutumnError({
|
||||
func: async () => {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.premium.id,
|
||||
force_checkout: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach premium and not be able to upgrade (without payment method)", async function () {
|
||||
await attachFailedPaymentMethod({
|
||||
stripeCli: stripeCli,
|
||||
customer: customer,
|
||||
});
|
||||
|
||||
await expectAutumnError({
|
||||
func: async () => {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: products.premium.id,
|
||||
force_checkout: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Attach payment method
|
||||
it("should attach successful payment method", async function () {
|
||||
await attachPmToCus({
|
||||
db: this.db,
|
||||
customer: customer,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach premium and have correct product and entitlements", async function () {
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.premium.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.premium,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -44,6 +44,7 @@ export const runAttachTest = async ({
|
||||
waitForInvoice = 0,
|
||||
isCanceled = false,
|
||||
skipFeatureCheck = false,
|
||||
singleInvoice = false,
|
||||
}: {
|
||||
autumn: AutumnInt;
|
||||
customerId: string;
|
||||
@@ -61,6 +62,7 @@ export const runAttachTest = async ({
|
||||
waitForInvoice?: number;
|
||||
isCanceled?: boolean;
|
||||
skipFeatureCheck?: boolean;
|
||||
singleInvoice?: boolean;
|
||||
}) => {
|
||||
const preview = await autumn.attachPreview({
|
||||
customer_id: customerId,
|
||||
@@ -110,10 +112,11 @@ export const runAttachTest = async ({
|
||||
|
||||
const freeProduct = isFreeProductV2({ product });
|
||||
if (!freeProduct) {
|
||||
let multiInvoice = !singleInvoice && multiInterval;
|
||||
expectInvoicesCorrect({
|
||||
customer,
|
||||
first: multiInterval ? undefined : { productId: product.id, total },
|
||||
second: multiInterval ? { productId: product.id, total } : undefined,
|
||||
first: multiInvoice ? undefined : { productId: product.id, total },
|
||||
second: multiInvoice ? { productId: product.id, total } : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -141,7 +144,7 @@ export const runAttachTest = async ({
|
||||
});
|
||||
|
||||
const stripeSubs = await stripeCli.subscriptions.list({
|
||||
customer: customer.stripe_id,
|
||||
customer: customer.stripe_id!,
|
||||
});
|
||||
if (multiInterval) {
|
||||
expect(stripeSubs.data.length).to.equal(2, "should have 2 subscriptions");
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { addMonths } from "date-fns";
|
||||
import Stripe from "stripe";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import { advanceProducts } from "tests/global.js";
|
||||
import {
|
||||
checkProductIsScheduled,
|
||||
compareMainProduct,
|
||||
} from "tests/utils/compare.js";
|
||||
import { initCustomer } from "tests/utils/init.js";
|
||||
import { advanceClockForInvoice } from "tests/utils/stripeUtils.js";
|
||||
import { advanceMonths } from "tests/utils/stripeUtils.js";
|
||||
import { initCustomerWithTestClock } from "tests/utils/testInitUtils.js";
|
||||
|
||||
// TEST MULTI INTERVAL DOWNGRADE
|
||||
//
|
||||
/*
|
||||
CASE 1: Annual pro -> Annual starter
|
||||
- If attach annual starter, should schedule correctly [DONE]
|
||||
- If advance test clock, should downgrade correctly (to monthly starter) [DONE]
|
||||
- If cancel active subscription (on Stripe), should remove scheduled correctly [DONE]
|
||||
- If cancel scheduled subscription (on Stripe), should remove scheduled correctly [DONE]
|
||||
- If expire on dashboard, should remove scheduled correctly
|
||||
|
||||
- If upgrade back to annual pro, should remove scheduled correctly [DONE]
|
||||
- If downgrade to monthly pro (switch downgrade), should be correct [DONE]
|
||||
- If downgrade to free (switch downgrade), should be correct
|
||||
*/
|
||||
|
||||
describe.skip("Multi interval downgrade -- Quarterly pro -> Monthly pro", () => {
|
||||
let customerId = "multi-interval-downgrade";
|
||||
let stripeCli: Stripe;
|
||||
let testClockId: string;
|
||||
|
||||
before(async function () {
|
||||
const { testClockId: insertedTestClockId } =
|
||||
await initCustomerWithTestClock({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
});
|
||||
testClockId = insertedTestClockId;
|
||||
stripeCli = createStripeCli({
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach quarterly pro", async function () {
|
||||
let res = await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuProQuarter.id,
|
||||
});
|
||||
|
||||
let cusRes = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuProQuarter,
|
||||
cusRes,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach downgrade to monthly pro", async function () {
|
||||
let res = await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuSystemPro.id,
|
||||
});
|
||||
|
||||
let cusRes = await AutumnCli.getCustomer(customerId);
|
||||
checkProductIsScheduled({
|
||||
cusRes,
|
||||
product: advanceProducts.gpuSystemPro,
|
||||
});
|
||||
});
|
||||
|
||||
it("should advance clock by a year", async function () {
|
||||
// let numberOfMonths = 2;
|
||||
// await advanceMonths({
|
||||
// stripeCli,
|
||||
// testClockId,
|
||||
// numberOfMonths,
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
||||
describe("Multi interval downgrade -- Annual pro -> Annual starter", () => {
|
||||
let customerId = "multi-interval-downgrade";
|
||||
let customer;
|
||||
let stripeCli;
|
||||
let testClockId: string;
|
||||
|
||||
before(async function () {
|
||||
await initCustomer({
|
||||
customer_data: {
|
||||
id: customerId,
|
||||
name: customerId,
|
||||
email: "multi-interval-downgrade@example.com",
|
||||
},
|
||||
attachPm: true,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach annual pro", async function () {
|
||||
let res = await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuProAnnual.id,
|
||||
});
|
||||
|
||||
let cusRes = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: advanceProducts.gpuProAnnual,
|
||||
cusRes,
|
||||
});
|
||||
});
|
||||
|
||||
it("should attach downgrade to annual starter", async function () {
|
||||
let res = await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: advanceProducts.gpuStarterAnnual.id,
|
||||
});
|
||||
|
||||
let cusRes = await AutumnCli.getCustomer(customerId);
|
||||
checkProductIsScheduled({
|
||||
cusRes,
|
||||
product: advanceProducts.gpuStarterAnnual,
|
||||
});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user