import { describe, expect, it } from 'vitest'; import { BillingPlan } from './billing.js'; import { isCreditsPricing, isSeatlessOrg, isSeatsPricing, PricingModelEnum, resolvePricingModel } from './pricingModel.js'; describe('PricingModelEnum', () => { it('uses stable string values for the billing.pricing_model column', () => { expect(PricingModelEnum.SEATS).toBe('seats'); expect(PricingModelEnum.CREDITS).toBe('credits'); }); }); describe('isCreditsPricing', () => { it('is true only for credits-based orgs', () => { expect(isCreditsPricing(PricingModelEnum.CREDITS)).toBe(true); }); it('is false for seat-based orgs', () => { expect(isCreditsPricing(PricingModelEnum.SEATS)).toBe(false); }); it('defaults to false (seat-based) when the model is absent, so existing orgs are grandfathered', () => { expect(isCreditsPricing(undefined)).toBe(false); expect(isCreditsPricing(null)).toBe(false); }); }); describe('isSeatsPricing', () => { it('is true for seat-based orgs', () => { expect(isSeatsPricing(PricingModelEnum.SEATS)).toBe(true); }); it('is false for credits-based orgs', () => { expect(isSeatsPricing(PricingModelEnum.CREDITS)).toBe(false); }); it('treats an absent model as seat-based, so existing orgs are grandfathered', () => { expect(isSeatsPricing(undefined)).toBe(true); expect(isSeatsPricing(null)).toBe(true); }); }); describe('isSeatlessOrg', () => { // `pricing_model` is stamped once at TRIAL signup and grandfathered forever // (organization.ts: "Only TRIAL signups reach here"). A PLG org that later // signs an enterprise contract therefore keeps `credits` — while its contract // is seat-bearing. Gating seat surfaces on the raw stamp stranded exactly // those orgs with no way to assign builder licenses. it('is true for a PLG org on credits pricing', () => { expect(isSeatlessOrg(PricingModelEnum.CREDITS, BillingPlan.TEAMS)).toBe(true); expect(isSeatlessOrg(PricingModelEnum.CREDITS, BillingPlan.TRIAL)).toBe(true); }); it('is false for every enterprise-like plan, whatever the stamp says', () => { for (const plan of [BillingPlan.ENTERPRISE, BillingPlan.POC, BillingPlan.STARTER, BillingPlan.PRO]) { expect(isSeatlessOrg(PricingModelEnum.CREDITS, plan)).toBe(false); } }); it('is false for seat-pricing and for an absent stamp', () => { expect(isSeatlessOrg(PricingModelEnum.SEATS, BillingPlan.TEAMS)).toBe(false); expect(isSeatlessOrg(undefined, BillingPlan.TEAMS)).toBe(false); expect(isSeatlessOrg(null, BillingPlan.TEAMS)).toBe(false); }); it('is false when the plan is unknown (fail closed to showing seats)', () => { expect(isSeatlessOrg(PricingModelEnum.CREDITS, undefined)).toBe(false); expect(isSeatlessOrg(PricingModelEnum.CREDITS, null)).toBe(false); }); }); describe('resolvePricingModel', () => { it('passes through recognized model values from the LD flag', () => { expect(resolvePricingModel('credits')).toBe(PricingModelEnum.CREDITS); expect(resolvePricingModel('seats')).toBe(PricingModelEnum.SEATS); }); it('fails closed to seats for an unrecognized flag value (LD typo / unknown variation)', () => { expect(resolvePricingModel('gau')).toBe(PricingModelEnum.SEATS); expect(resolvePricingModel('Credits')).toBe(PricingModelEnum.SEATS); expect(resolvePricingModel('')).toBe(PricingModelEnum.SEATS); }); it('fails closed to seats when the flag is missing or the wrong type', () => { expect(resolvePricingModel(undefined)).toBe(PricingModelEnum.SEATS); expect(resolvePricingModel(null)).toBe(PricingModelEnum.SEATS); expect(resolvePricingModel(true)).toBe(PricingModelEnum.SEATS); expect(resolvePricingModel(42)).toBe(PricingModelEnum.SEATS); }); });