7.5 KiB
7.5 KiB
Test Migration Guide: Global State → Parallel-Ready Tests
Status
- ✅ Phase 1: Migration guide created
- ✅ Phase 2: Conversion utilities completed
- ✅ Phase 3: Validation process documented
- ✅ Phase 4: Initial test migrations validated (basic1, basic2)
See VALIDATION_RESULTS.md for detailed validation analysis.
Validation Process
CRITICAL: Before replacing any test file, you MUST validate the migration preserves test logic.
Step-by-Step Validation
-
Create New File - Don't modify original
# Create basic1.new.test.ts (not basic1.test.ts) -
Run Original Test - Uses global state
cd server bun test tests/attach/basic/basic1.test.ts- Note all assertions and expected values
- Save output for comparison
-
Run Migrated Test - Uses inline products
cd server bun parallel-tests # Or configure config.ts to point to basic1.new.test.ts -
Compare Test Logic - NOT output values
- ✅ Same test structure (beforeAll, test blocks)
- ✅ Same assertions (expect calls)
- ✅ Same logic flow
- ❌ Don't compare feature IDs (metered1 → Messages is OK)
- ❌ Don't compare product names (different orgs)
-
Critical Checks
- Both tests pass ✅
- Same number of assertions
- Same expected behavior (e.g., "balance should be 5")
- No logic lost or added
-
Only After Validation - Replace original
mv basic1.new.test.ts basic1.test.ts
Example: basic1.test.ts Migration
Original (uses global state):
test("should have correct entitlements", async () => {
const expectedEntitlement = products.free.entitlements.metered1;
const entitled = await AutumnCli.entitled(customerId, features.metered1.id);
const balance = entitled.balances.find(b => b.feature_id === features.metered1.id);
expect(entitled.allowed).toBe(true);
expect(balance).toBeDefined();
expect(balance.balance).toBe(expectedEntitlement.allowance); // 5
expect(balance.unlimited).toBeUndefined();
});
Migrated (uses inline products):
test("should have correct entitlements", async () => {
// Expected: 5 allowance for Messages feature (same as metered1)
const entitled = await AutumnCli.entitled(customerId, TestFeature.Messages);
const balance = entitled.balances.find(b => b.feature_id === TestFeature.Messages);
expect(entitled.allowed).toBe(true);
expect(balance).toBeDefined();
expect(balance.balance).toBe(5); // Same expected value
expect(balance.unlimited).toBeUndefined();
});
Key Differences (ALLOWED):
- Feature ID:
features.metered1.id→TestFeature.Messages - Source:
products.free.entitlements.metered1→ inlinefreeProddefinition - Expected value: Hardcoded
5instead ofexpectedEntitlement.allowance
What Must Stay Same:
- Number of expects: 4
- Expected values: balance = 5, allowed = true, unlimited = undefined
- Test logic: Check balance, verify allowed, ensure not unlimited
Quick Start
Is Your Test Already Migrated?
✅ Already Done - Your test uses:
- Inline product definitions (
constructProduct) - V1.2+ API (
AutumnIntwithLegacyVersion.v1_2or higher) - Modern assertions (
expectProductAttached,expectFeaturesCorrect)
❌ Needs Migration - Your test uses:
products.*fromtests/global.ts- V0.1 API (
AutumnCli.getCustomer()) - Legacy assertions (
compareMainProduct)
Migration Steps
1. Define Products Inline
Before:
import { products } from "tests/global.js";
// Uses products.pro
After:
import { TestFeature } from "tests/setup/v2Features.js";
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
const pro = constructProduct({
type: "pro",
items: [
constructFeatureItem({
featureId: TestFeature.Messages,
includedUsage: 10,
}),
],
});
2. Initialize Products in beforeAll
beforeAll(async () => {
await initProductsV0({
ctx,
products: [pro], // Pass inline products
prefix: testCase,
});
});
3. Update Assertions
For V0.1 API (AutumnCli):
Before:
import { compareMainProduct } from "tests/utils/compare.js";
const res = await AutumnCli.getCustomer(customerId);
compareMainProduct({ sent: products.pro, cusRes: res });
After:
import { expectCustomerV0Correct } from "tests/utils/expectUtils/expectCustomerV0Correct.js";
const res = await AutumnCli.getCustomer(customerId);
await expectCustomerV0Correct({ sent: pro, cusRes: res });
For V1.2+ API (AutumnInt):
Already correct:
const customer = await autumn.customers.get(customerId);
expectProductAttached({ customer, product: pro });
expectFeaturesCorrect({ customer, product: pro });
Product Mapping Reference
Free Product
// global.ts: products.free
const free = constructProduct({
type: "free",
items: [
constructFeatureItem({
featureId: TestFeature.Messages,
includedUsage: 5,
interval: ProductItemInterval.Month,
}),
],
});
Pro Product
// global.ts: products.pro
const pro = constructProduct({
type: "pro",
items: [
constructFeatureItem({
featureId: TestFeature.Messages,
includedUsage: 10,
interval: ProductItemInterval.Month,
}),
],
});
Pro with Overage
// global.ts: products.proWithOverage
const proWithOverage = constructProduct({
type: "pro",
items: [
constructFeatureItem({
featureId: TestFeature.Messages,
includedUsage: 10,
}),
constructArrearItem({
featureId: TestFeature.Messages,
}),
],
});
Common Patterns
Multiple Features
const product = constructProduct({
type: "pro",
items: [
constructFeatureItem({
featureId: TestFeature.Messages,
includedUsage: 100,
}),
constructFeatureItem({
featureId: TestFeature.Dashboard,
isBoolean: true,
}),
],
});
Prepaid/Arrear Pricing
const product = constructProduct({
type: "pro",
items: [
constructPrepaidItem({
featureId: TestFeature.Messages,
price: 9,
billingUnits: 100,
}),
// OR
constructArrearItem({
featureId: TestFeature.Words,
price: 0.1,
billingUnits: 1000,
}),
],
});
Files to Migrate
✅ Completed
tests/attach/basic/basic1.test.ts- Migrated to basic1.new.test.tstests/attach/basic/basic2.test.ts- Migrated to basic2.new.test.ts
Priority 1 (Simple)
tests/attach/basic/basic3.test.ts- Uses products.premium
Priority 2 (Medium)
tests/attach/upgrade/*.test.tstests/attach/downgrade/*.test.ts
Priority 3 (Complex)
tests/attach/entities/*.test.tstests/core/cancel/*.test.ts
Utilities Reference
constructProduct()- Create product with itemsconstructFeatureItem()- Create feature entitlementconstructPrepaidItem()- Create prepaid feature priceconstructArrearItem()- Create pay-per-use (single_use) [eg. credits, messages, tokens] feature priceconstructArrearProratedItem()- Create pay-per-use (continuous_use) [eg. seats, users, admins] feature priceconstructFixedPrice()- Create fixed priceexpectCustomerV0Correct()- Compare V2 product with V0.1 customer responseexpectProductAttached()- V1.2+ API product checkexpectFeaturesCorrect()- V1.2+ API feature balance check