From 943baf9ff888fb243c990e8c191d4aecf9936f37 Mon Sep 17 00:00:00 2001 From: John Yeo Date: Tue, 17 Jun 2025 12:26:58 +0100 Subject: [PATCH] fix: added tests for attach and fixed contUse updates --- docker-compose.dev.yml | 5 +- pnpm-lock.yaml | 6 + run.sh | 6 +- server/shell/g3.sh | 4 +- server/shell/g4.sh | 4 +- server/src/db/initDrizzle.ts | 2 +- server/src/external/autumn/autumnCli.ts | 10 +- server/src/external/stripe/stripeSubUtils.ts | 4 + .../handleCusDiscountDeleted.ts | 110 +++--- .../handlers/handleProductsUpdated.ts | 6 +- .../scheduleFlow/scheduleStripeSub.ts | 2 + .../attach/attachUtils/getAttachConfig.ts | 7 +- .../attach/attachUtils/getAttachFunction.ts | 7 +- .../attach/attachUtils/handleAttachErrors.ts | 2 +- .../rewards/RewardRedemptionService.ts | 6 +- server/src/utils/scriptUtils/constructItem.ts | 13 + server/src/utils/scriptUtils/initCustomer.ts | 2 +- server/tests/advanced/referrals/referrals1.ts | 50 +-- server/tests/advanced/referrals/referrals2.ts | 50 +-- server/tests/attach/basic/basic1.ts | 342 ++++------------ server/tests/attach/downgrade/downgrade4.ts | 24 +- server/tests/attach/downgrade/downgrade5.ts | 4 +- server/tests/attach/downgrade/downgrade6.ts | 2 +- server/tests/attach/downgrade/downgrade7.ts | 5 +- server/tests/attach/downgrade/downgrade9.ts | 2 - server/tests/attach/others/others6.ts | 133 +++++++ server/tests/attach/others/others7.ts | 80 ++++ server/tests/attach/others/others8.ts | 0 server/tests/contUse/roles/role2.ts | 9 - server/tests/contUse/update/updateContUse1.ts | 10 +- server/tests/contUse/update/updateContUse2.ts | 6 +- server/tests/contUse/update/updateContUse4.ts | 4 +- server/tests/contUse/update/updateContUse5.ts | 6 +- .../tests/utils/expectUtils/expectAttach.ts | 22 ++ .../utils/expectUtils/expectInvoiceUtils.ts | 2 +- shared/drizzle.config.ts | 2 - shared/package.json | 2 + .../{create-entity.tsx => CreateEntity.tsx} | 12 +- .../customer-sidebar/select-entity.tsx | 5 +- .../product/components/AttachModal.tsx | 2 +- vite/src/views/demo/MintDemo.tsx | 369 ------------------ 41 files changed, 519 insertions(+), 820 deletions(-) create mode 100644 server/tests/attach/others/others6.ts create mode 100644 server/tests/attach/others/others7.ts create mode 100644 server/tests/attach/others/others8.ts rename vite/src/views/customers/customer/customer-sidebar/create-entity/{create-entity.tsx => CreateEntity.tsx} (99%) delete mode 100644 vite/src/views/demo/MintDemo.tsx diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 82d23189c..154f91282 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,4 +1,5 @@ services: + # Run valkey valkey: image: docker.io/bitnami/valkey:8.0 environment: @@ -11,8 +12,8 @@ services: interval: 10s timeout: 5s retries: 5 - # ports: - # - "6379:6379" + ports: + - "6379:6379" restart: unless-stopped shared: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f3ad97a1..3d49e5c55 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -244,6 +244,12 @@ importers: decimal.js: specifier: ^10.5.0 version: 10.5.0 + dotenv: + specifier: ^16.5.0 + version: 16.5.0 + drizzle-kit: + specifier: ^0.31.1 + version: 0.31.1 drizzle-orm: specifier: ^0.43.1 version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(kysely@0.28.2)(pg@8.16.0)(postgres@3.4.7) diff --git a/run.sh b/run.sh index c9f7f6edf..1325c82d2 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,7 @@ if [[ $1 == *"docker-compose"* ]]; then - docker compose -f "$1" up + if [[ $2 == "down" ]]; then + docker compose -f "$1" down $3 + else + docker compose -f "$1" up $3 + fi fi diff --git a/server/shell/g3.sh b/server/shell/g3.sh index 61d9a5803..7b6a2f1d3 100755 --- a/server/shell/g3.sh +++ b/server/shell/g3.sh @@ -9,4 +9,6 @@ $MOCHA_CMD 'tests/contUse/entities/*.ts' $MOCHA_CMD 'tests/contUse/update/*.ts' -$MOCHA_CMD 'tests/contUse/track/*.ts' \ No newline at end of file +$MOCHA_CMD 'tests/contUse/track/*.ts' + +$MOCHA_CMD 'tests/contUse/roles/*.ts' \ No newline at end of file diff --git a/server/shell/g4.sh b/server/shell/g4.sh index 3ef7c5133..e6168e94c 100755 --- a/server/shell/g4.sh +++ b/server/shell/g4.sh @@ -10,7 +10,7 @@ $MOCHA_CMD 'tests/advanced/multiFeature/*.ts' \ 'tests/attach/updateQuantity/*.ts' \ 'tests/advanced/referrals/*.ts' -# $MOCHA_CMD 'tests/attach/multiProduct/*.ts' +$MOCHA_CMD 'tests/attach/multiProduct/*.ts' -# $MOCHA_CMD 'tests/advanced/usage/*.ts' +$MOCHA_CMD 'tests/advanced/usage/*.ts' \ No newline at end of file diff --git a/server/src/db/initDrizzle.ts b/server/src/db/initDrizzle.ts index 7be274da9..a5b023f6a 100644 --- a/server/src/db/initDrizzle.ts +++ b/server/src/db/initDrizzle.ts @@ -16,7 +16,7 @@ export const initDrizzle = (params?: { maxConnections?: number }) => { const db = drizzle(client, { schema, - logger: process.env.NODE_ENV === "development", + // logger: process.env.NODE_ENV === "development", }); return { db, client }; diff --git a/server/src/external/autumn/autumnCli.ts b/server/src/external/autumn/autumnCli.ts index 77d5e5c31..c4e6e78c1 100644 --- a/server/src/external/autumn/autumnCli.ts +++ b/server/src/external/autumn/autumnCli.ts @@ -238,8 +238,8 @@ export class AutumnInt { return data; }, - create: async (customer: { id: string; email: string; name: string }) => { - const data = await this.post(`/customers`, customer); + create: async (customer: { id: string; email: string; name?: string }) => { + const data = await this.post(`/customers?with_autumn_id=true`, customer); return data; }, delete: async ( @@ -269,10 +269,10 @@ export class AutumnInt { customerId: string, entity: CreateEntity | CreateEntity[], ) => { - let entities = Array.isArray(entity) ? entity : [entity]; + // let entities = Array.isArray(entity) ? entity : [entity]; const data = await this.post( - `/customers/${customerId}/entities`, - entities, + `/customers/${customerId}/entities?with_autumn_id=true`, + entity, ); return data; diff --git a/server/src/external/stripe/stripeSubUtils.ts b/server/src/external/stripe/stripeSubUtils.ts index c88388ea5..1fa8390e8 100644 --- a/server/src/external/stripe/stripeSubUtils.ts +++ b/server/src/external/stripe/stripeSubUtils.ts @@ -206,6 +206,10 @@ export const getStripeSchedules = async ({ const schedule = await stripeCli.subscriptionSchedules.retrieve(scheduleId); + if (schedule.status == "canceled") { + return null; + } + const batchPricesGet = []; for (const item of schedule.phases[0].items) { batchPricesGet.push(stripeCli.prices.retrieve(item.price as string)); diff --git a/server/src/external/stripe/webhookHandlers/handleCusDiscountDeleted.ts b/server/src/external/stripe/webhookHandlers/handleCusDiscountDeleted.ts index 39e377c27..265c5147a 100644 --- a/server/src/external/stripe/webhookHandlers/handleCusDiscountDeleted.ts +++ b/server/src/external/stripe/webhookHandlers/handleCusDiscountDeleted.ts @@ -4,6 +4,7 @@ import { createStripeCli } from "../utils.js"; import Stripe from "stripe"; import { notNullish, timeout } from "@/utils/genUtils.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; +import { RewardService } from "@/internal/rewards/RewardService.js"; export async function handleCusDiscountDeleted({ db, @@ -35,61 +36,68 @@ export async function handleCusDiscountDeleted({ return; } + // Check if any redemptions available, and apply to customer if so + let redemptions = await RewardRedemptionService.getUnappliedRedemptions({ + db, + internalCustomerId: customer.internal_id, + }); + + logger.info(`discount.deleted: found ${redemptions.length} redemptions`); + + if (redemptions.length == 0) { + return; + } + + let redemption = redemptions[0]; + + // Apply redemption to customer + let stripeCli = createStripeCli({ + org, + env, + }); + + let stripeCus = (await stripeCli.customers.retrieve( + discount.customer, + )) as Stripe.Customer; + + if (stripeCus && notNullish(stripeCus.discount)) { + logger.info( + `discount.deleted: stripe customer ${discount.customer} already has a discount`, + ); + return; + } + + // Send response first...? res.status(200).json({ message: "OK" }); - return; - // // Check if any redemptions available, and apply to customer if so - // let redemptions = await RewardRedemptionService.getUnappliedRedemptions({ - // db, - // internalCustomerId: customer.internal_id, - // }); + const reward = await RewardService.get({ + db, + orgId: org.id, + env, + idOrInternalId: redemption.reward_program.internal_reward_id!, + }); - // if (redemptions.length == 0) { - // return; - // } + if (!reward) { + logger.warn( + `discount.deleted: reward ${redemption.reward_program.internal_id} not found`, + ); + return; + } - // let redemption = redemptions[0]; - // let reward = redemption.reward_program.reward; + await stripeCli.customers.update(discount.customer, { + coupon: reward.internal_id, + }); - // // Apply redemption to customer - // let stripeCli = createStripeCli({ - // org, - // env, - // }); + await RewardRedemptionService.update({ + db, + id: redemption.id, + updates: { + applied: true, + }, + }); - // let stripeCus = (await stripeCli.customers.retrieve( - // discount.customer, - // )) as Stripe.Customer; - - // if (stripeCus && notNullish(stripeCus.discount)) { - // logger.info( - // `discount.deleted: stripe customer ${discount.customer} already has a discount`, - // ); - // return; - // } - - // // Send response first...? - // res.status(200).json({ message: "OK" }); - - // if (notNullish(stripeCus.test_clock)) { - // // Time out for test clock to complete - // await timeout(5000); - // } - - // await stripeCli.customers.update(discount.customer, { - // coupon: reward.internal_id, - // }); - - // await RewardRedemptionService.update({ - // db, - // id: redemption.id, - // updates: { - // applied: true, - // }, - // }); - - // logger.info( - // `discount.deleted: applied reward ${reward.name} on customer ${customer.name} (${customer.id})`, - // ); - // logger.info(`Redemption ID: ${redemption.id}`); + logger.info( + `discount.deleted: applied reward ${reward.name} on customer ${customer.name} (${customer.id})`, + ); + logger.info(`Redemption ID: ${redemption.id}`); } diff --git a/server/src/internal/analytics/handlers/handleProductsUpdated.ts b/server/src/internal/analytics/handlers/handleProductsUpdated.ts index b9a6b0c73..bc437d564 100644 --- a/server/src/internal/analytics/handlers/handleProductsUpdated.ts +++ b/server/src/internal/analytics/handlers/handleProductsUpdated.ts @@ -119,7 +119,11 @@ export const handleProductsUpdated = async ({ if (!req) { logger.warn("products.updated, no req object found, skipping", { - data, + ...data, + org: { + id: org.id, + slug: org.slug, + }, }); return; } diff --git a/server/src/internal/customers/attach/attachFunctions/scheduleFlow/scheduleStripeSub.ts b/server/src/internal/customers/attach/attachFunctions/scheduleFlow/scheduleStripeSub.ts index 7e275adc6..933f29142 100644 --- a/server/src/internal/customers/attach/attachFunctions/scheduleFlow/scheduleStripeSub.ts +++ b/server/src/internal/customers/attach/attachFunctions/scheduleFlow/scheduleStripeSub.ts @@ -120,6 +120,8 @@ export const handleNewScheduleForItemSet = async ({ let otherSub = otherSubObj?.otherSub || null; let otherSubItems = otherSubObj?.otherSubItems || []; + // console.log("Interval:", itemSet.interval, "Other sub: ", otherSub?.id); + // 2. Add to item set to create new schedule itemSet.items.push( ...otherSubItems.map((sub: any) => ({ diff --git a/server/src/internal/customers/attach/attachUtils/getAttachConfig.ts b/server/src/internal/customers/attach/attachUtils/getAttachConfig.ts index 7e038e33b..709724860 100644 --- a/server/src/internal/customers/attach/attachUtils/getAttachConfig.ts +++ b/server/src/internal/customers/attach/attachUtils/getAttachConfig.ts @@ -8,7 +8,6 @@ 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, @@ -85,8 +84,10 @@ export const getAttachConfig = async ({ branch == AttachBranch.MainIsTrial || org.config.merge_billing_cycles === false; - const onlyCheckout = - isPublic || forceCheckout || (noPaymentMethod && !invoiceOnly && !isFree); + const checkoutFlow = + isPublic || forceCheckout || (noPaymentMethod && !invoiceOnly); + + const onlyCheckout = !isFree && checkoutFlow; let config: AttachConfig = { branch, diff --git a/server/src/internal/customers/attach/attachUtils/getAttachFunction.ts b/server/src/internal/customers/attach/attachUtils/getAttachFunction.ts index c1348feca..8082f386c 100644 --- a/server/src/internal/customers/attach/attachUtils/getAttachFunction.ts +++ b/server/src/internal/customers/attach/attachUtils/getAttachFunction.ts @@ -3,12 +3,7 @@ import { AttachParams, AttachResultSchema, } from "../../cusProducts/AttachParams.js"; -import { - AttachBranch, - AttachFunction, - CusProductStatus, - ProrationBehavior, -} 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"; diff --git a/server/src/internal/customers/attach/attachUtils/handleAttachErrors.ts b/server/src/internal/customers/attach/attachUtils/handleAttachErrors.ts index 6b3f90bdf..5730a2464 100644 --- a/server/src/internal/customers/attach/attachUtils/handleAttachErrors.ts +++ b/server/src/internal/customers/attach/attachUtils/handleAttachErrors.ts @@ -185,7 +185,7 @@ export const handleAttachErrors = async ({ }) => { const { onlyCheckout } = config; - if (onlyCheckout) { + if (onlyCheckout || flags.isPublic) { let upgradeDowngradeFlows = [ AttachBranch.Upgrade, AttachBranch.Downgrade, diff --git a/server/src/internal/rewards/RewardRedemptionService.ts b/server/src/internal/rewards/RewardRedemptionService.ts index 184832c9c..64f9d6db1 100644 --- a/server/src/internal/rewards/RewardRedemptionService.ts +++ b/server/src/internal/rewards/RewardRedemptionService.ts @@ -178,10 +178,6 @@ export class RewardRedemptionService { rewardPrograms.internal_id, ), ) - .innerJoin( - rewards, - eq(rewards.internal_id, rewardPrograms.internal_reward_id), - ) .where( and( eq(referralCodes.internal_customer_id, internalCustomerId), @@ -197,7 +193,7 @@ export class RewardRedemptionService { referral_code: d.referral_codes, reward_program: { ...d.reward_programs, - reward: d.rewards, + // reward: d.rewards, }, })); diff --git a/server/src/utils/scriptUtils/constructItem.ts b/server/src/utils/scriptUtils/constructItem.ts index a53fdb42d..e48fdc685 100644 --- a/server/src/utils/scriptUtils/constructItem.ts +++ b/server/src/utils/scriptUtils/constructItem.ts @@ -123,3 +123,16 @@ export const constructArrearProratedItem = ({ return item; }; + +export const constructFixedPrice = ({ + price, + interval = ProductItemInterval.Month, +}: { + price: number; + interval?: ProductItemInterval; +}) => { + return { + price, + interval, + }; +}; diff --git a/server/src/utils/scriptUtils/initCustomer.ts b/server/src/utils/scriptUtils/initCustomer.ts index 7b8a22c8a..e16af4da0 100644 --- a/server/src/utils/scriptUtils/initCustomer.ts +++ b/server/src/utils/scriptUtils/initCustomer.ts @@ -113,7 +113,7 @@ export const initCustomer = async ({ org, env, db, - testClockId, + testClockId: testClockId || undefined, }); } diff --git a/server/tests/advanced/referrals/referrals1.ts b/server/tests/advanced/referrals/referrals1.ts index cff8a140b..8318400f8 100644 --- a/server/tests/advanced/referrals/referrals1.ts +++ b/server/tests/advanced/referrals/referrals1.ts @@ -199,34 +199,34 @@ describe(`${chalk.yellowBright( assert.equal(invoices[0].total, 0); }); - it("customer should have discount for second purchase", async function () { - // 2. Check that customer has another discount - let stripeCus = (await stripeCli.customers.retrieve( - mainCustomer.processor?.id, - )) as Stripe.Customer; + // it("customer should have discount for second purchase", async function () { + // // 2. Check that customer has another discount + // let stripeCus = (await stripeCli.customers.retrieve( + // mainCustomer.processor?.id, + // )) as Stripe.Customer; - assert.notEqual(stripeCus.discount, null); + // assert.notEqual(stripeCus.discount, null); - // 2. Advance test clock to 1 month from start (trigger discount.deleted event) - curTime = addHours(addMonths(new Date(), 1), 2); - await advanceTestClock({ - testClockId, - advanceTo: curTime.getTime(), - stripeCli, - }); + // // 2. Advance test clock to 1 month from start (trigger discount.deleted event) + // curTime = addHours(addMonths(new Date(), 1), 2); + // await advanceTestClock({ + // testClockId, + // advanceTo: curTime.getTime(), + // stripeCli, + // }); - // 3. Advance test clock to 1 month + 12 days from start (trigger new invoice) - curTime = addDays(curTime, 12); - await advanceTestClock({ - testClockId, - advanceTo: curTime.getTime(), - stripeCli, - }); + // // 3. Advance test clock to 1 month + 12 days from start (trigger new invoice) + // curTime = addDays(curTime, 12); + // await advanceTestClock({ + // testClockId, + // advanceTo: curTime.getTime(), + // stripeCli, + // }); - // // 3. Get invoice again - let { invoices: invoices2 } = await autumn.customers.get(mainCustomerId); + // // // 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); + // }); }); diff --git a/server/tests/advanced/referrals/referrals2.ts b/server/tests/advanced/referrals/referrals2.ts index 93afa103c..035bd493c 100644 --- a/server/tests/advanced/referrals/referrals2.ts +++ b/server/tests/advanced/referrals/referrals2.ts @@ -137,34 +137,34 @@ describe(`${chalk.yellowBright( assert.equal(invoices![0].total, 0); }); - it("customer should have discount for second purchase", async function () { - // 2. Check that customer has another discount - let stripeCus = (await stripeCli.customers.retrieve( - mainCustomer.processor?.id, - )) as Stripe.Customer; + // it("customer should have discount for second purchase", async function () { + // // 2. Check that customer has another discount + // let stripeCus = (await stripeCli.customers.retrieve( + // mainCustomer.processor?.id, + // )) as Stripe.Customer; - assert.notEqual(stripeCus.discount, null); + // assert.notEqual(stripeCus.discount, null); - // 2. Advance test clock to 1 month from start (trigger discount.deleted event) - curTime = addHours(addMonths(new Date(), 1), 2); - await advanceTestClock({ - testClockId, - advanceTo: curTime.getTime(), - stripeCli, - }); + // // 2. Advance test clock to 1 month from start (trigger discount.deleted event) + // curTime = addHours(addMonths(new Date(), 1), 2); + // await advanceTestClock({ + // testClockId, + // advanceTo: curTime.getTime(), + // stripeCli, + // }); - // 3. Advance test clock to 1 month + 7 days from start (trigger new invoice) - curTime = addDays(curTime, 8); - await advanceTestClock({ - testClockId, - advanceTo: curTime.getTime(), - stripeCli, - }); + // // 3. Advance test clock to 1 month + 7 days from start (trigger new invoice) + // curTime = addDays(curTime, 8); + // await advanceTestClock({ + // testClockId, + // advanceTo: curTime.getTime(), + // stripeCli, + // }); - // // 3. Get invoice again - let { invoices: invoices2 } = await autumn.customers.get(mainCustomerId); + // // // 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); + // }); }); diff --git a/server/tests/attach/basic/basic1.ts b/server/tests/attach/basic/basic1.ts index 51ec532df..641bca051 100644 --- a/server/tests/attach/basic/basic1.ts +++ b/server/tests/attach/basic/basic1.ts @@ -1,26 +1,43 @@ +import chalk from "chalk"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { + constructFeatureItem, + constructFixedPrice, +} from "@/utils/scriptUtils/constructItem.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.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 { createProducts } from "tests/utils/productUtils.js"; +import { addPrefixToProducts } from "../utils.js"; +import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; +import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; +import { TestFeature } from "tests/setup/v2Features.js"; +import { APIVersion } from "@autumn/shared"; -const oneTimeQuantity = 2; -const oneTimePurchaseCount = 2; -const oneTimeOverrideQuantity = 4; -const monthlyQuantity = 2; +const freeProd = constructProduct({ + type: "free", + isDefault: false, + items: [ + constructFeatureItem({ + featureId: TestFeature.Messages, + includedUsage: 1000, + }), + // constructFixedPrice({ + // price: 0, + // }), + ], +}); // UNCOMMENT FROM HERE const testCase = "basic1"; -describe(`${chalk.yellowBright( - "basic1: Testing attach -- free, pro & one-time / monthly add on", -)}`, () => { +describe(`${chalk.yellowBright("basic1: Testing attach free product")}`, () => { let customerId = testCase; - let autumn: AutumnInt = new AutumnInt(); + let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_2 }); let db, org, env; before(async function () { @@ -36,11 +53,25 @@ describe(`${chalk.yellowBright( org, env, fingerprint: "test", + withTestClock: false, + }); + + addPrefixToProducts({ + products: [freeProd], + prefix: testCase, + }); + + await createProducts({ + db, + orgId: org.id, + env, + autumn, + products: [freeProd], }); }); it("should create customer and have default free active", async function () { - const data = await autumn.customers.get(customerId); + const data = await AutumnCli.getCustomer(customerId); compareMainProduct({ sent: products.free, @@ -49,13 +80,12 @@ describe(`${chalk.yellowBright( }); 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 entitled = (await AutumnCli.entitled( + customerId, + features.metered1.id, + )) as any; const metered1Balance = entitled.balances.find( (balance: any) => balance.feature_id === features.metered1.id, @@ -71,262 +101,24 @@ describe(`${chalk.yellowBright( const entitled = await AutumnCli.entitled(customerId, features.boolean1.id); expect(entitled!.allowed).to.be.false; }); + + it("should attach free (with $0 price) and force checkout and succeed", async function () { + await autumn.attach({ + customer_id: customerId, + product_id: freeProd.id, + force_checkout: true, + }); + + const customer = await autumn.customers.get(customerId); + + expectProductAttached({ + customer, + product: freeProd, + }); + + expectFeaturesCorrect({ + customer, + product: freeProd, + }); + }); }); - -// 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; -// } -// }); -// }); diff --git a/server/tests/attach/downgrade/downgrade4.ts b/server/tests/attach/downgrade/downgrade4.ts index 9422b008c..5774ad8da 100644 --- a/server/tests/attach/downgrade/downgrade4.ts +++ b/server/tests/attach/downgrade/downgrade4.ts @@ -38,7 +38,12 @@ let pro = constructProduct({ type: "pro", }); -describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: premium -> pro -> free -> pro -> premium`)}`, () => { +let premium = constructProduct({ + items: [constructArrearItem({ featureId: TestFeature.Words })], + type: "premium", +}); + +describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: pro-quarter -> premium -> pro`)}`, () => { let customerId = testCase; let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 }); let customer: Customer; @@ -58,13 +63,13 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: premium -> pro - stripeCli = this.stripeCli; addPrefixToProducts({ - products: [proQuarter, pro], + products: [proQuarter, pro, premium], prefix: testCase, }); await createProducts({ autumn, - products: [proQuarter, pro], + products: [proQuarter, pro, premium], customerId, db, orgId: org.id, @@ -97,6 +102,19 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: premium -> pro - }); }); + it("should downgrade to premium", async function () { + await expectDowngradeCorrect({ + autumn, + customerId, + curProduct: proQuarter, + newProduct: premium, + stripeCli, + db, + org, + env, + }); + }); + let preview = null; it("should downgrade to pro", async function () { diff --git a/server/tests/attach/downgrade/downgrade5.ts b/server/tests/attach/downgrade/downgrade5.ts index 1e7afa98f..5d6477634 100644 --- a/server/tests/attach/downgrade/downgrade5.ts +++ b/server/tests/attach/downgrade/downgrade5.ts @@ -16,9 +16,7 @@ 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)", -)}`, () => { +describe(`${chalk.yellowBright(`${testCase}: testing basic downgrade (paid to paid)`)}`, () => { let customerId = testCase; let autumn: AutumnInt = new AutumnInt(); let testClockId: string; diff --git a/server/tests/attach/downgrade/downgrade6.ts b/server/tests/attach/downgrade/downgrade6.ts index 577367d56..6a03fbfdf 100644 --- a/server/tests/attach/downgrade/downgrade6.ts +++ b/server/tests/attach/downgrade/downgrade6.ts @@ -9,7 +9,7 @@ import { setupBefore } from "tests/before.js"; import { getMainCusProduct } from "@/internal/customers/cusProducts/cusProductUtils.js"; const testCase = "downgrade6"; -describe(`${chalk.yellowBright("downgrade6: testing expire button")}`, () => { +describe(`${chalk.yellowBright(`${testCase}: testing expire button`)}`, () => { let customerId = testCase; let testClockId: string; let autumn: AutumnInt = new AutumnInt(); diff --git a/server/tests/attach/downgrade/downgrade7.ts b/server/tests/attach/downgrade/downgrade7.ts index 21ec10db5..7975fc8ec 100644 --- a/server/tests/attach/downgrade/downgrade7.ts +++ b/server/tests/attach/downgrade/downgrade7.ts @@ -1,17 +1,16 @@ +import chalk from "chalk"; 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")}`, () => { +describe(`${chalk.yellowBright(`${testCase}: testing expire scheduled product`)}`, () => { let customerId = testCase; let testClockId: string; let customer: Customer; diff --git a/server/tests/attach/downgrade/downgrade9.ts b/server/tests/attach/downgrade/downgrade9.ts index 045fc9789..67944835c 100644 --- a/server/tests/attach/downgrade/downgrade9.ts +++ b/server/tests/attach/downgrade/downgrade9.ts @@ -1,6 +1,4 @@ -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 { diff --git a/server/tests/attach/others/others6.ts b/server/tests/attach/others/others6.ts new file mode 100644 index 000000000..afc3d3b26 --- /dev/null +++ b/server/tests/attach/others/others6.ts @@ -0,0 +1,133 @@ +import { expect } from "chai"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { APIVersion, AppEnv, Organization } from "@autumn/shared"; +import chalk from "chalk"; +import Stripe from "stripe"; +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { setupBefore } from "tests/before.js"; +import { createProducts } from "tests/utils/productUtils.js"; +import { addPrefixToProducts } from "../utils.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; +import { TestFeature } from "tests/setup/v2Features.js"; +import { expectAttachCorrect } from "tests/utils/expectUtils/expectAttach.js"; +import { CusService } from "@/internal/customers/CusService.js"; + +export let pro = constructProduct({ + items: [constructArrearItem({ featureId: TestFeature.Words })], + type: "pro", +}); + +const testCase = "others6"; + +describe(`${chalk.yellowBright(`${testCase}: Testing attach with customer ID and entity ID null`)}`, () => { + let customerId = testCase; + let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 }); + let testClockId: string; + let db: DrizzleCli, org: Organization, env: AppEnv; + let stripeCli: Stripe; + + const email = `${customerId}@test.com`; + before(async function () { + await setupBefore(this); + const { autumnJs } = this; + db = this.db; + org = this.org; + env = this.env; + + stripeCli = this.stripeCli; + + let customer = await CusService.getByEmail({ + db, + orgId: org.id, + env, + email, + }); + + if (customer.length > 0) { + await autumn.customers.delete(customer[0].internal_id); + } + + addPrefixToProducts({ + products: [pro], + prefix: testCase, + }); + + await createProducts({ + db, + orgId: org.id, + env, + autumn, + products: [pro], + }); + }); + + let internalCustomerId = ""; + let internalEntityId = ""; + let entityId = "1"; + it("should attach create customer with no ID", async function () { + let customer = await autumn.customers.create({ + // @ts-ignore + id: null, + email: `${customerId}@test.com`, + name: customerId, + }); + + expect(customer.autumn_id, "Customer ID should exist").to.exist; + + internalCustomerId = customer.autumn_id; + + let data = await autumn.entities.create(internalCustomerId, { + // @ts-ignore + id: null, + feature_id: TestFeature.Users, + }); + + internalEntityId = data.autumn_id; + + expect(internalEntityId, "Entity ID should exist").to.exist; + }); + + it("should be able to attach pro product, invoice only", async function () { + await autumn.attach({ + customer_id: internalCustomerId, + entity_id: internalEntityId, + product_id: pro.id, + invoice_only: true, + }); + + const customer = await autumn.customers.get(internalCustomerId); + + expectAttachCorrect({ + customer, + product: pro, + }); + + expect(customer.invoices.length).to.equal(1); + expect(customer.invoices[0].status).to.equal("draft"); + }); + + it("should create customer with ID, and attach pro product", async function () { + let customer = await autumn.customers.create({ + id: customerId, + email: `${customerId}@test.com`, + }); + + expect(customer.autumn_id).to.equal(internalCustomerId); + + let entity = await autumn.entities.create(customer.autumn_id, { + id: entityId, + feature_id: TestFeature.Users, + }); + + internalEntityId = entity.autumn_id; + + const customer2 = await autumn.customers.get(customerId); + + expectAttachCorrect({ + customer: customer2, + product: pro, + entityId, + }); + }); +}); diff --git a/server/tests/attach/others/others7.ts b/server/tests/attach/others/others7.ts new file mode 100644 index 000000000..a52346f51 --- /dev/null +++ b/server/tests/attach/others/others7.ts @@ -0,0 +1,80 @@ +import chalk from "chalk"; +import Stripe from "stripe"; +import { expect } from "chai"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { APIVersion, AppEnv, Organization } from "@autumn/shared"; +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { setupBefore } from "tests/before.js"; +import { createProducts } from "tests/utils/productUtils.js"; +import { addPrefixToProducts } from "../utils.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; +import { TestFeature } from "tests/setup/v2Features.js"; +import { expectAttachCorrect } from "tests/utils/expectUtils/expectAttach.js"; +import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js"; + +export let pro = constructProduct({ + items: [constructArrearItem({ featureId: TestFeature.Words })], + type: "pro", + trial: true, +}); + +const testCase = "others7"; + +describe(`${chalk.yellowBright(`${testCase}: Testing attach with free_trial=False`)}`, () => { + let customerId = testCase; + let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 }); + let db: DrizzleCli, org: Organization, env: AppEnv; + let stripeCli: Stripe; + + before(async function () { + await setupBefore(this); + db = this.db; + org = this.org; + env = this.env; + + stripeCli = this.stripeCli; + + await initCustomer({ + db, + org, + env, + autumn: this.autumnJs, + customerId, + fingerprint: "test", + attachPm: "success", + }); + + addPrefixToProducts({ + products: [pro], + prefix: testCase, + }); + + await createProducts({ + db, + orgId: org.id, + env, + autumn, + products: [pro], + }); + }); + + it("should attach pro product with free_trial=False", async function () { + await autumn.attach({ + customer_id: customerId, + product_id: pro.id, + free_trial: false, + }); + + const customer = await autumn.customers.get(customerId); + + expectAttachCorrect({ + customer, + product: pro, + }); + + expect(customer.invoices.length).to.equal(1); + expect(customer.invoices[0].total).to.equal(getBasePrice({ product: pro })); + }); +}); diff --git a/server/tests/attach/others/others8.ts b/server/tests/attach/others/others8.ts new file mode 100644 index 000000000..e69de29bb diff --git a/server/tests/contUse/roles/role2.ts b/server/tests/contUse/roles/role2.ts index e2b0a725f..072322029 100644 --- a/server/tests/contUse/roles/role2.ts +++ b/server/tests/contUse/roles/role2.ts @@ -7,7 +7,6 @@ import { CreateEntity, LimitedItem, Organization, - ProductItem, } from "@autumn/shared"; import { TestFeature } from "tests/setup/v2Features.js"; @@ -21,7 +20,6 @@ import { createProducts } from "tests/utils/productUtils.js"; import { addPrefixToProducts, runAttachTest } from "../../attach/utils.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; -import { Decimal } from "decimal.js"; import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; import { advanceTestClock } from "@/utils/scriptUtils/testClockUtils.js"; import { addHours, addMonths } from "date-fns"; @@ -37,13 +35,6 @@ let userMessages = constructArrearItem({ entityFeatureId: user, }) as LimitedItem; -// let adminMessages = constructArrearItem({ -// featureId: TestFeature.Messages, -// includedUsage: 0, -// price: 0.1, -// entityFeatureId: admin, -// }) as LimitedItem; - export let pro = constructProduct({ items: [userMessages], type: "pro", diff --git a/server/tests/contUse/update/updateContUse1.ts b/server/tests/contUse/update/updateContUse1.ts index 4c6cec033..0e8de7004 100644 --- a/server/tests/contUse/update/updateContUse1.ts +++ b/server/tests/contUse/update/updateContUse1.ts @@ -87,17 +87,17 @@ describe(`${chalk.yellowBright(`attach/entities/${testCase}: Testing update cont { id: "1", name: "test", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, { id: "2", name: "test2", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, { id: "3", name: "test3", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, ]; @@ -167,12 +167,12 @@ describe(`${chalk.yellowBright(`attach/entities/${testCase}: Testing update cont { id: "4", name: "test4", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, { id: "5", name: "test5", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, ]; diff --git a/server/tests/contUse/update/updateContUse2.ts b/server/tests/contUse/update/updateContUse2.ts index a0ca62769..f07e18987 100644 --- a/server/tests/contUse/update/updateContUse2.ts +++ b/server/tests/contUse/update/updateContUse2.ts @@ -87,17 +87,17 @@ describe(`${chalk.yellowBright(`contUse/update/${testCase}: Testing update cont { id: "1", name: "test", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, { id: "2", name: "test2", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, { id: "3", name: "test3", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, ]; diff --git a/server/tests/contUse/update/updateContUse4.ts b/server/tests/contUse/update/updateContUse4.ts index c55d475b6..ebf4f14d8 100644 --- a/server/tests/contUse/update/updateContUse4.ts +++ b/server/tests/contUse/update/updateContUse4.ts @@ -88,12 +88,12 @@ describe(`${chalk.yellowBright(`contUse/${testCase}: Testing update contUse incl { id: "1", name: "entity1", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, { id: "2", name: "entity2", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, ]; diff --git a/server/tests/contUse/update/updateContUse5.ts b/server/tests/contUse/update/updateContUse5.ts index df8cfe83e..c8abe2737 100644 --- a/server/tests/contUse/update/updateContUse5.ts +++ b/server/tests/contUse/update/updateContUse5.ts @@ -99,17 +99,17 @@ describe(`${chalk.yellowBright(`contUse/${testCase}: Testing update contUse incl { id: "1", name: "entity1", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, { id: "2", name: "entity2", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, { id: "3", name: "entity3", - featureId: TestFeature.Users, + feature_id: TestFeature.Users, }, ]; diff --git a/server/tests/utils/expectUtils/expectAttach.ts b/server/tests/utils/expectUtils/expectAttach.ts index 51d92c527..9d374fd00 100644 --- a/server/tests/utils/expectUtils/expectAttach.ts +++ b/server/tests/utils/expectUtils/expectAttach.ts @@ -18,6 +18,7 @@ import { DrizzleCli } from "@/db/initDrizzle.js"; import { expect } from "chai"; import { completeCheckoutForm } from "../stripeUtils.js"; +import { Customer } from "autumn-js"; export const attachAndExpectCorrect = async ({ autumn, @@ -158,3 +159,24 @@ export const attachAndExpectCorrect = async ({ ); } }; + +export const expectAttachCorrect = async ({ + customer, + product, + entityId, +}: { + customer: Customer; + product: ProductV2; + entityId?: string; +}) => { + expectProductAttached({ + customer, + product, + entityId, + }); + + expectFeaturesCorrect({ + customer, + product, + }); +}; diff --git a/server/tests/utils/expectUtils/expectInvoiceUtils.ts b/server/tests/utils/expectUtils/expectInvoiceUtils.ts index 29e3c42fc..c867b9e0b 100644 --- a/server/tests/utils/expectUtils/expectInvoiceUtils.ts +++ b/server/tests/utils/expectUtils/expectInvoiceUtils.ts @@ -71,7 +71,7 @@ export const getExpectedInvoiceTotal = async ({ const usageAmount = usage.find( (u) => u.featureId == featureId && - (u.featureId ? u.entityFeatureId == ent.entity_feature_id : true), + (u.entityFeatureId ? u.entityFeatureId == ent.entity_feature_id : true), )?.value; const overage = diff --git a/shared/drizzle.config.ts b/shared/drizzle.config.ts index 6c759e188..d34bc8ca0 100644 --- a/shared/drizzle.config.ts +++ b/shared/drizzle.config.ts @@ -2,8 +2,6 @@ import { config } from "dotenv"; config({ path: "../server/.env" }); import { defineConfig } from "drizzle-kit"; -console.log("DATABASE_URL", process.env.DATABASE_URL); - export default defineConfig({ dialect: "postgresql", out: "./drizzle", diff --git a/shared/package.json b/shared/package.json index ca36f5d51..b8c1a856f 100644 --- a/shared/package.json +++ b/shared/package.json @@ -21,6 +21,8 @@ }, "dependencies": { "decimal.js": "^10.5.0", + "dotenv": "^16.5.0", + "drizzle-kit": "^0.31.1", "drizzle-orm": "^0.43.1", "drizzle-zod": "^0.8.2", "zod": "^3.25.23" diff --git a/vite/src/views/customers/customer/customer-sidebar/create-entity/create-entity.tsx b/vite/src/views/customers/customer/customer-sidebar/create-entity/CreateEntity.tsx similarity index 99% rename from vite/src/views/customers/customer/customer-sidebar/create-entity/create-entity.tsx rename to vite/src/views/customers/customer/customer-sidebar/create-entity/CreateEntity.tsx index 5b74cf983..918702f92 100644 --- a/vite/src/views/customers/customer/customer-sidebar/create-entity/create-entity.tsx +++ b/vite/src/views/customers/customer/customer-sidebar/create-entity/CreateEntity.tsx @@ -25,11 +25,6 @@ export const CreateEntity = ({ const cusContext = useCustomerContext(); const navigate = useNavigate(); - if (!cusContext) { - return null; - } - - const { customer, cusMutate } = cusContext; const [isLoading, setIsLoading] = useState(false); const [entity, setEntity] = useState({ id: "", @@ -39,6 +34,11 @@ export const CreateEntity = ({ const env = useEnv(); const axiosInstance = useAxiosInstance({ env }); + if (!cusContext) { + return null; + } + const { customer, cusMutate } = cusContext; + const handleCreateClicked = async () => { setIsLoading(true); try { @@ -51,7 +51,7 @@ export const CreateEntity = ({ name: entity.name || null, feature_id: entity.feature_id, customer_id: customer.id, - } + }, ); await cusMutate(); diff --git a/vite/src/views/customers/customer/customer-sidebar/select-entity.tsx b/vite/src/views/customers/customer/customer-sidebar/select-entity.tsx index 1b29f8437..5fda426bf 100644 --- a/vite/src/views/customers/customer/customer-sidebar/select-entity.tsx +++ b/vite/src/views/customers/customer/customer-sidebar/select-entity.tsx @@ -9,7 +9,7 @@ import { import { Entity } from "@autumn/shared"; import { useLocation, useNavigate } from "react-router"; -import { CreateEntity } from "./create-entity/create-entity"; +import { CreateEntity } from "./create-entity/CreateEntity"; import { useState } from "react"; import { Button } from "@/components/ui/button"; import { PlusIcon } from "lucide-react"; @@ -46,7 +46,8 @@ export const SelectEntity = ({ } const entity = entities.find( - (entity: Entity) => entity.id === entityId || entity.internal_id == entityId + (entity: Entity) => + entity.id === entityId || entity.internal_id == entityId, ); return ( diff --git a/vite/src/views/customers/customer/product/components/AttachModal.tsx b/vite/src/views/customers/customer/product/components/AttachModal.tsx index 7631ce064..172679baf 100644 --- a/vite/src/views/customers/customer/product/components/AttachModal.tsx +++ b/vite/src/views/customers/customer/product/components/AttachModal.tsx @@ -132,7 +132,7 @@ export const AttachModal = ({ const redirectUrl = getRedirectUrl(`/customers/${cusId}`, env); const attachBody = getAttachBody({ - customerId: customer.id, + customerId: customer.id || customer.internal_id, entityId, product, optionsInput: options, diff --git a/vite/src/views/demo/MintDemo.tsx b/vite/src/views/demo/MintDemo.tsx deleted file mode 100644 index 5156c989b..000000000 --- a/vite/src/views/demo/MintDemo.tsx +++ /dev/null @@ -1,369 +0,0 @@ -"use client"; - -import axios from "axios"; -import { toast } from "sonner"; -import { useState, useEffect } from "react"; -import { Button } from "@/components/ui/button"; -import { LoaderCircle } from "lucide-react"; - -import { useDemoSWR } from "@/services/useAxiosSwr"; -import CustomerBalances from "./CustomerBalances"; -import { Input } from "@/components/ui/input"; -import { APIPlayground } from "./APIPlayground"; -const endpoint = "https://api.useautumn.com"; -// const endpoint = "http://localhost:8080"; - -export default function MintDemo({ - publishableKey, - secretKey, -}: { - publishableKey: string; - secretKey: string; -}) { - const [eventName, setEventName] = useState("chat-responses"); - const customerId = "hahnbee"; - - const axiosInstance = axios.create({ - baseURL: endpoint + "/v1", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${secretKey}`, - }, - }); - - const { - data: customer, - error, - isLoading: customerLoading, - mutate: cusMutate, - } = useDemoSWR({ - url: `/public/customers/${customerId}`, - publishableKey: publishableKey || "", - }); - - const hasAccessRequest = { - customer_id: customerId, - feature_id: eventName, - }; - - const sendEventRequestChat = { - customer_id: customerId, - event_name: "chat-responses", - }; - - const sendEventRequestEditorsPlus = { - customer_id: customerId, - event_name: "editors", - properties: { - value: 1, - }, - }; - - const sendEventRequestEditorsMinus = { - customer_id: customerId, - event_name: "editors", - properties: { - value: -1, - }, - }; - - const getCustomerRequest = { - customer_id: customerId, - }; - - const [hasAccessLoading, setHasAccessLoading] = useState(false); - const [hasAccessResponse, setHasAccessResponse] = useState(null); - const [sendEventRequest, setSendEventRequest] = - useState(sendEventRequestChat); - const [getCustomerResponse, setGetCustomerResponse] = useState(null); - const [sendEventLoading, setSendEventLoading] = useState(false); - const [sendEventResponse, setSendEventResponse] = useState(null); - const [loading, setLoading] = useState(false); - const [message, setMessage] = useState(""); - - useEffect(() => { - setGetCustomerResponse(customer?.entitlements); - }, [customer]); - - //Check access to Pro features and email balance - const checkAccess = async (featureId: string) => { - const { data } = await axiosInstance.post("/entitled", { - customer_id: customerId, - feature_id: featureId, - }); - return data; - }; - - //Send usage event for - const sendUsage = async (featureId: string) => { - const { data } = await axiosInstance.post("/events", { - customer_id: customerId, - event_name: featureId, - properties: { - value: 1, - }, - }); - - return data; - }; - - const handleClicked = async () => { - setHasAccessLoading(true); - const data = await checkAccess("chat-responses"); - setHasAccessResponse(data); - await setSendEventRequest(sendEventRequestChat); - setHasAccessLoading(false); - - if (!data.allowed) { - toast.error("You're out of AI chat responses"); - return; - } - - setSendEventLoading(true); - const eventData = await sendUsage("chat-responses"); - setSendEventResponse(eventData); - setSendEventLoading(false); - await cusMutate(); - }; - - return ( -
-
-
-
-
- autumn -
- -
- - - - - -
-
-
- Products -
- - - - -
-
- - - -
-
-
-
- -
- {loading ? ( -
- -
- ) : ( - <> -
-
- Mintlify -
-
- -

- Start building modern documentation in under five minutes -

-
- setMessage(e.target.value)} - /> - - - - {/* */} -
- -
-

Good evening, Autumn

-

- Welcome back to your documentation portal -

- -
- {/* Dashboard Preview */} - -
-
-
- Live -
- - Last updated 1 week ago by Ayush - -
- -
- - - -
-
-
-
- -
Billing
-

- {customer?.name ? customer?.name : customerId}, you have access - to: -

- -
Pricing
- -
- -

- Make a test purchase to see how Autumn handles it. Use the - Stripe test card{" "} - 4242 4242 4242 4242 with any - expiration date, CVC and cardholder details. -

- - )} -
-
- {/* Autumn Logo */} - - - -
-
-
- ); -} - -function SidebarItem({ - icon, - text, - active = false, -}: { - icon: string; - text: string; - active?: boolean; -}) { - return ( -
- {icon} - {text} -
- ); -}