added .vscode

This commit is contained in:
John Yeo
2026-01-07 17:46:05 +00:00
parent 850dd0775a
commit ef5e44fb84
7 changed files with 149 additions and 108 deletions

2
.gitignore vendored
View File

@@ -100,7 +100,7 @@ supabase/
# 2. Load files
.claude/
.vscode/
migration.sh
stat.sh

4
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"postman.settings.dotenv-detection-notification-visibility": false,
"typescript.preferences.importModuleSpecifier": "non-relative"
}

30
.vscode/tasks.json vendored Normal file
View 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"
}
]
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);
});

View File

@@ -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") {