added .vscode
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -100,7 +100,7 @@ supabase/
|
||||
# 2. Load files
|
||||
|
||||
.claude/
|
||||
.vscode/
|
||||
|
||||
|
||||
migration.sh
|
||||
stat.sh
|
||||
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"postman.settings.dotenv-detection-notification-visibility": false,
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||
}
|
||||
30
.vscode/tasks.json
vendored
Normal file
30
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Run Test Pattern",
|
||||
"type": "shell",
|
||||
"command": "bun test ${relativeFile} -t \"${input:testPattern}\"",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Run Describe at Cursor",
|
||||
"type": "shell",
|
||||
"command": "bun test ${relativeFile} --timeout 0 -t \"$(bun scripts/testScripts/getDescribeAtCursor.ts ${file} ${lineNumber})\"",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Run Current Test File",
|
||||
"type": "shell",
|
||||
"command": "bun test ${relativeFile}",
|
||||
"problemMatcher": []
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "testPattern",
|
||||
"type": "promptString",
|
||||
"description": "Test name pattern"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { expectCustomerFeatureCorrect } from "@tests/billing/utils/expectCustomerFeatureCorrect";
|
||||
import { expectCustomerInvoiceCorrect } from "@tests/billing/utils/expectCustomerInvoiceCorrect";
|
||||
import { expectProductActive } from "@tests/billing/utils/expectCustomerProductCorrect";
|
||||
import { expectSubToBeCorrect } from "@tests/merged/mergeUtils/expectSubCorrect";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { items } from "@tests/utils/fixtures/items.js";
|
||||
@@ -55,9 +56,10 @@ test.concurrent(`${chalk.yellowBright("multi-entity-free-to-paid: entity 2 upgra
|
||||
items: [messagesItem, priceItem],
|
||||
});
|
||||
|
||||
// Verify entity 2's feature
|
||||
// Verify entity 2 has the updated product
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
|
||||
await expectProductActive({ customer: entity2Data, productId: free.id });
|
||||
expectCustomerFeatureCorrect({
|
||||
customer: entity2Data,
|
||||
featureId: TestFeature.Messages,
|
||||
@@ -149,9 +151,10 @@ test.concurrent(`${chalk.yellowBright("multi-entity-free-to-paid: base + consuma
|
||||
],
|
||||
});
|
||||
|
||||
// Verify entity 2's features
|
||||
// Verify entity 2 has the updated product
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
|
||||
await expectProductActive({ customer: entity2Data, productId: free.id });
|
||||
expectCustomerFeatureCorrect({
|
||||
customer: entity2Data,
|
||||
featureId: TestFeature.Messages,
|
||||
@@ -226,9 +229,10 @@ test.concurrent(`${chalk.yellowBright("multi-entity-free-to-paid: annual price")
|
||||
items: [messagesItem, annualPriceItem],
|
||||
});
|
||||
|
||||
// Verify entity 2's feature
|
||||
// Verify entity 2 has the updated product
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
|
||||
await expectProductActive({ customer: entity2Data, productId: free.id });
|
||||
expectCustomerFeatureCorrect({
|
||||
customer: entity2Data,
|
||||
featureId: TestFeature.Messages,
|
||||
@@ -305,9 +309,10 @@ test.concurrent(`${chalk.yellowBright("multi-entity-free-to-paid: annual mid-cyc
|
||||
items: [messagesItem, annualPriceItem],
|
||||
});
|
||||
|
||||
// Verify entity 2's feature
|
||||
// Verify entity 2 has the updated product
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
|
||||
await expectProductActive({ customer: entity2Data, productId: free.id });
|
||||
expectCustomerFeatureCorrect({
|
||||
customer: entity2Data,
|
||||
featureId: TestFeature.Messages,
|
||||
@@ -384,9 +389,10 @@ test.concurrent(`${chalk.yellowBright("multi-entity-free-to-paid: monthly mid-cy
|
||||
items: [messagesItem, priceItem],
|
||||
});
|
||||
|
||||
// Verify entity 2's feature
|
||||
// Verify entity 2 has the updated product
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
|
||||
await expectProductActive({ customer: entity2Data, productId: free.id });
|
||||
expectCustomerFeatureCorrect({
|
||||
customer: entity2Data,
|
||||
featureId: TestFeature.Messages,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { test } from "bun:test";
|
||||
import {
|
||||
expectProductActive,
|
||||
expectProductCanceled,
|
||||
expectProductNotPresent,
|
||||
} from "@tests/billing/utils/expectCustomerProductCorrect";
|
||||
import { expectSubToBeCorrect } from "@tests/merged/mergeUtils/expectSubCorrect";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { items } from "@tests/utils/fixtures/items.js";
|
||||
@@ -43,10 +48,10 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: cancel entity 1, update en
|
||||
customerId,
|
||||
entities[0].id,
|
||||
);
|
||||
const entity1Product = entity1AfterCancel.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
);
|
||||
expect(entity1Product?.canceled_at).toBeDefined();
|
||||
await expectProductCanceled({
|
||||
customer: entity1AfterCancel,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
|
||||
// Entity 2 updates pro's items (change price)
|
||||
const newPriceItem = items.monthlyPrice({ price: 30 }); // $30/mo instead of $20
|
||||
@@ -60,10 +65,10 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: cancel entity 1, update en
|
||||
|
||||
// Verify entity 2 has updated items
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
const entity2Product = entity2Data.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
);
|
||||
expect(entity2Product).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity2Data,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
@@ -89,15 +94,14 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: cancel entity 1, update en
|
||||
entities[1].id,
|
||||
);
|
||||
|
||||
const entity1ProAfter = entity1AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
);
|
||||
const entity2ProAfter = entity2AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
);
|
||||
|
||||
expect(entity1ProAfter).toBeUndefined();
|
||||
expect(entity2ProAfter).toBeDefined();
|
||||
await expectProductNotPresent({
|
||||
customer: entity1AfterCycle,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
await expectProductActive({
|
||||
customer: entity2AfterCycle,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
@@ -166,10 +170,10 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: downgrade entity 1, update
|
||||
|
||||
// Verify entity 2's premium still active with new price
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
const entity2Premium = entity2Data.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
expect(entity2Premium).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity2Data,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
@@ -196,19 +200,18 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: downgrade entity 1, update
|
||||
entities[1].id,
|
||||
);
|
||||
|
||||
const entity1Premium = entity1AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
const entity1Pro = entity1AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
);
|
||||
const entity2PremiumAfter = entity2AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
|
||||
expect(entity1Premium).toBeUndefined();
|
||||
expect(entity1Pro).toBeDefined();
|
||||
expect(entity2PremiumAfter).toBeDefined();
|
||||
await expectProductNotPresent({
|
||||
customer: entity1AfterCycle,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
await expectProductActive({
|
||||
customer: entity1AfterCycle,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
await expectProductActive({
|
||||
customer: entity2AfterCycle,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
@@ -287,10 +290,10 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: annual + monthly downgrade
|
||||
|
||||
// Verify entity 3's premium still active
|
||||
const entity3Data = await autumnV1.entities.get(customerId, entities[2].id);
|
||||
const entity3Premium = entity3Data.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
expect(entity3Premium).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity3Data,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
@@ -323,26 +326,26 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: annual + monthly downgrade
|
||||
);
|
||||
|
||||
// Entity 1 should still have premium-annual (annual cycle not over)
|
||||
const entity1PremiumAnnual = entity1AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium-annual`,
|
||||
);
|
||||
expect(entity1PremiumAnnual).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity1AfterCycle,
|
||||
productId: `${customerId}_premium-annual`,
|
||||
});
|
||||
|
||||
// Entity 2 should be on pro now
|
||||
const entity2Premium = entity2AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
const entity2Pro = entity2AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
);
|
||||
expect(entity2Premium).toBeUndefined();
|
||||
expect(entity2Pro).toBeDefined();
|
||||
await expectProductNotPresent({
|
||||
customer: entity2AfterCycle,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
await expectProductActive({
|
||||
customer: entity2AfterCycle,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
|
||||
// Entity 3 should still have premium
|
||||
const entity3PremiumAfter = entity3AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
expect(entity3PremiumAfter).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity3AfterCycle,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
@@ -377,16 +380,14 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: update to free item remove
|
||||
// Verify both entities have pro
|
||||
const entity1Before = await autumnV1.entities.get(customerId, entities[0].id);
|
||||
const entity2Before = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
expect(
|
||||
entity1Before.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
entity2Before.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
),
|
||||
).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity1Before,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
await expectProductActive({
|
||||
customer: entity2Before,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
|
||||
// Entity 2 updates Pro's items to be free (no price items, only feature)
|
||||
await autumnV1.subscriptions.update({
|
||||
@@ -398,10 +399,10 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: update to free item remove
|
||||
|
||||
// Entity 2's subscription should now be free (removed from paid sub)
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
const entity2Product = entity2Data.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
);
|
||||
expect(entity2Product).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity2Data,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
|
||||
// Should only have 1 subscription (entity 1's pro)
|
||||
await expectSubToBeCorrect({
|
||||
@@ -429,16 +430,14 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: update to free item remove
|
||||
entities[1].id,
|
||||
);
|
||||
|
||||
expect(
|
||||
entity1AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
entity2AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
),
|
||||
).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity1AfterCycle,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
await expectProductActive({
|
||||
customer: entity2AfterCycle,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
@@ -507,10 +506,10 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: downgrade + update to free
|
||||
|
||||
// Entity 2's subscription should now be free
|
||||
const entity2Data = await autumnV1.entities.get(customerId, entities[1].id);
|
||||
const entity2Premium = entity2Data.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
expect(entity2Premium).toBeDefined();
|
||||
await expectProductActive({
|
||||
customer: entity2Data,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
@@ -538,19 +537,18 @@ test.concurrent(`${chalk.yellowBright("schedules-p2p: downgrade + update to free
|
||||
entities[1].id,
|
||||
);
|
||||
|
||||
const entity1Premium = entity1AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
const entity1Pro = entity1AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_pro`,
|
||||
);
|
||||
const entity2PremiumAfter = entity2AfterCycle.products?.find(
|
||||
(p: { id?: string }) => p.id === `${customerId}_premium`,
|
||||
);
|
||||
|
||||
expect(entity1Premium).toBeUndefined();
|
||||
expect(entity1Pro).toBeDefined();
|
||||
expect(entity2PremiumAfter).toBeDefined();
|
||||
await expectProductNotPresent({
|
||||
customer: entity1AfterCycle,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
await expectProductActive({
|
||||
customer: entity1AfterCycle,
|
||||
productId: `${customerId}_pro`,
|
||||
});
|
||||
await expectProductActive({
|
||||
customer: entity2AfterCycle,
|
||||
productId: `${customerId}_premium`,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { applyProration, priceToLineAmount } from "@autumn/shared";
|
||||
import { expect, test } from "bun:test";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { applyProration, type Price, priceToLineAmount } from "@autumn/shared";
|
||||
import { items } from "@tests/utils/fixtures/items.js";
|
||||
import { products } from "@tests/utils/fixtures/products.js";
|
||||
import { initScenario, s } from "@tests/utils/testInitUtils/initScenario.js";
|
||||
@@ -34,7 +33,10 @@ test.concurrent(`${chalk.yellowBright("preview-total: mid-cycle free to paid pro
|
||||
const customer = await autumnV1.customers.get(customerId);
|
||||
const subscription = customer.products?.[0];
|
||||
|
||||
if (!subscription?.current_period_start || !subscription?.current_period_end) {
|
||||
if (
|
||||
!subscription?.current_period_start ||
|
||||
!subscription?.current_period_end
|
||||
) {
|
||||
throw new Error("Missing billing period on subscription");
|
||||
}
|
||||
|
||||
@@ -54,7 +56,7 @@ test.concurrent(`${chalk.yellowBright("preview-total: mid-cycle free to paid pro
|
||||
|
||||
// Calculate expected amount manually
|
||||
const baseAmount = priceToLineAmount({
|
||||
price: priceItem.price,
|
||||
price: priceItem.price as unknown as Price,
|
||||
multiplier: 1,
|
||||
});
|
||||
|
||||
@@ -74,4 +76,3 @@ test.concurrent(`${chalk.yellowBright("preview-total: mid-cycle free to paid pro
|
||||
|
||||
expect(preview.total).toBe(expectedAmount);
|
||||
});
|
||||
|
||||
|
||||
@@ -38,7 +38,9 @@ export const expectCustomerProductCorrect = async ({
|
||||
}
|
||||
|
||||
if (!product) {
|
||||
throw new Error(`Product ${productId} not found but expected state: ${state}`);
|
||||
throw new Error(
|
||||
`Product ${productId} not found but expected state: ${state}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (state === "active") {
|
||||
|
||||
Reference in New Issue
Block a user