import { describe, expect, it } from 'vitest'; import { getAllFeatureAccess, getChildAppLimit, getEntitlementContext, getRequiredPlanForAdditionalChildApp, PRO_CHILD_APP_LIMIT, resolveSubscriptionProduct, SCALE_CHILD_APP_LIMIT, } from '../entitlements'; import { distributionPlan, products, proPlan, scalePlan } from '../plans'; import type { IApp } from '../toDesktop'; const makeApp = ({ id, name = id, parent = null, subscription, }: Partial & Pick): IApp => ({ appModelId: `com.todesktop.${id}`, appType: 'electron', id, name, parent, subscription, url: 'https://example.com', windowOptions: {} as IApp['windowOptions'], }) as IApp; describe('entitlements', () => { it('uses the leader app subscription for child apps', () => { const leader = makeApp({ id: 'leader', subscription: { itemId: 'leader-item', planId: products.prod.cli.performance.prices.monthly_375.id, productId: products.prod.cli.performance.id, status: 'active', subscriptionId: 'leader-sub', }, }); const child = makeApp({ id: 'child', parent: { id: leader.id, relationshipToParent: 'Staging' }, }); const context = getEntitlementContext(child, [leader, child]); const access = getAllFeatureAccess(context); expect(context.leaderApp.id).toBe(leader.id); expect(context.effectivePlan?.tier).toBe('legacy_pro'); expect(access.find(({ id }) => id === 'analytics')?.hasAccess).toBe(true); }); it('computes child app limits from the effective plan', () => { expect( getChildAppLimit({ itemId: 'pro-item', planId: products.prod.cli.performance.prices.monthly_375.id, productId: products.prod.cli.performance.id, status: 'active', subscriptionId: 'pro-sub', }), ).toBe(PRO_CHILD_APP_LIMIT); expect( getChildAppLimit({ itemId: 'scale-item', planId: products.prod.cli.scale.prices.monthly_1500.id, productId: products.prod.cli.scale.id, status: 'active', subscriptionId: 'scale-sub', }), ).toBe(SCALE_CHILD_APP_LIMIT); }); it('computes the required plan for the next child app slot', () => { expect(getRequiredPlanForAdditionalChildApp(0).tier).toBe(proPlan.tier); expect(getRequiredPlanForAdditionalChildApp(1).tier).toBe(proPlan.tier); expect(getRequiredPlanForAdditionalChildApp(2).tier).toBe(scalePlan.tier); }); it('resolves the normalized product label from subscription ids', () => { expect( resolveSubscriptionProduct({ planId: products.prod.cli.performance.prices.yearly_3600.id, }), ).toMatchObject({ catalog: 'cli', key: 'performance', label: 'CLI Performance', priceKey: 'yearly_3600', stage: 'prod', }); expect( resolveSubscriptionProduct({ planId: products.prod.legacy.professional.prices.monthly_199.id, }), ).toMatchObject({ catalog: 'legacy', key: 'professional', label: 'Legacy Professional', stage: 'prod', }); }); it('omits the price key when a plan id maps to multiple aliases', () => { const match = resolveSubscriptionProduct({ planId: products.dev.builder.professional.prices.monthly_240.id, }); expect(match).toMatchObject({ catalog: 'builder', key: 'professional', label: 'Builder Professional', stage: 'dev', }); expect(match?.priceKey).toBeUndefined(); }); it('keeps feature access aligned with shared plan definitions', () => { const app = makeApp({ id: 'legacy-pro', subscription: { itemId: 'legacy-item', planId: products.prod.legacy.professional.prices.monthly_199.id, productId: products.prod.legacy.professional.id, status: 'active', subscriptionId: 'legacy-sub', }, }); const access = getAllFeatureAccess(getEntitlementContext(app, [app])); const featureRecord = Object.fromEntries( access.map(({ hasAccess, id, plan }) => [id, { hasAccess, plan }]), ); expect(featureRecord.analytics.plan.tier).toBe('pro'); expect(featureRecord.analytics.hasAccess).toBe(true); expect(featureRecord.custom_domain.plan.tier).toBe(distributionPlan.tier); expect(featureRecord.custom_domain.hasAccess).toBe(true); }); });