/** * Subscription Entity Tests * * Unit tests for the Subscription domain entity */ import { Subscription } from './subscription.entity'; import { SubscriptionPlan } from '../value-objects/subscription-plan.vo'; import { SubscriptionStatus } from '../value-objects/subscription-status.vo'; import { InvalidSubscriptionDowngradeException, SubscriptionNotActiveException, } from '../exceptions/subscription.exceptions'; describe('Subscription Entity', () => { const createValidSubscription = () => { return Subscription.create({ id: 'sub-123', organizationId: 'org-123', }); }; describe('create', () => { it('should create a subscription with default BRONZE plan', () => { const subscription = createValidSubscription(); expect(subscription.id).toBe('sub-123'); expect(subscription.organizationId).toBe('org-123'); expect(subscription.plan.value).toBe('BRONZE'); expect(subscription.status.value).toBe('ACTIVE'); expect(subscription.cancelAtPeriodEnd).toBe(false); }); it('should create a subscription with custom plan', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.silver(), }); expect(subscription.plan.value).toBe('SILVER'); }); it('should create a subscription with Stripe IDs', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', stripeCustomerId: 'cus_123', stripeSubscriptionId: 'sub_stripe_123', }); expect(subscription.stripeCustomerId).toBe('cus_123'); expect(subscription.stripeSubscriptionId).toBe('sub_stripe_123'); }); }); describe('fromPersistence', () => { it('should reconstitute a subscription from persistence data', () => { const subscription = Subscription.fromPersistence({ id: 'sub-123', organizationId: 'org-123', plan: 'GOLD', status: 'ACTIVE', stripeCustomerId: 'cus_123', stripeSubscriptionId: 'sub_stripe_123', currentPeriodStart: new Date('2024-01-01'), currentPeriodEnd: new Date('2024-02-01'), cancelAtPeriodEnd: true, createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-15'), }); expect(subscription.id).toBe('sub-123'); expect(subscription.plan.value).toBe('GOLD'); expect(subscription.status.value).toBe('ACTIVE'); expect(subscription.cancelAtPeriodEnd).toBe(true); }); }); describe('maxLicenses', () => { it('should return correct limits for BRONZE plan', () => { const subscription = createValidSubscription(); expect(subscription.maxLicenses).toBe(1); }); it('should return correct limits for SILVER plan', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.silver(), }); expect(subscription.maxLicenses).toBe(5); }); it('should return correct limits for GOLD plan', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.gold(), }); expect(subscription.maxLicenses).toBe(20); }); it('should return -1 for PLATINIUM plan (unlimited)', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.platinium(), }); expect(subscription.maxLicenses).toBe(-1); }); }); describe('isUnlimited', () => { it('should return false for BRONZE plan', () => { const subscription = createValidSubscription(); expect(subscription.isUnlimited()).toBe(false); }); it('should return true for PLATINIUM plan', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.platinium(), }); expect(subscription.isUnlimited()).toBe(true); }); }); describe('isActive', () => { it('should return true for ACTIVE status', () => { const subscription = createValidSubscription(); expect(subscription.isActive()).toBe(true); }); it('should return true for TRIALING status', () => { const subscription = Subscription.fromPersistence({ id: 'sub-123', organizationId: 'org-123', plan: 'BRONZE', status: 'TRIALING', stripeCustomerId: null, stripeSubscriptionId: null, currentPeriodStart: null, currentPeriodEnd: null, cancelAtPeriodEnd: false, createdAt: new Date(), updatedAt: new Date(), }); expect(subscription.isActive()).toBe(true); }); it('should return false for CANCELED status', () => { const subscription = Subscription.fromPersistence({ id: 'sub-123', organizationId: 'org-123', plan: 'BRONZE', status: 'CANCELED', stripeCustomerId: null, stripeSubscriptionId: null, currentPeriodStart: null, currentPeriodEnd: null, cancelAtPeriodEnd: false, createdAt: new Date(), updatedAt: new Date(), }); expect(subscription.isActive()).toBe(false); }); }); describe('canAllocateLicenses', () => { it('should return true when licenses are available', () => { const subscription = createValidSubscription(); // BRONZE = 1 license expect(subscription.canAllocateLicenses(0, 1)).toBe(true); }); it('should return false when no licenses available', () => { const subscription = createValidSubscription(); expect(subscription.canAllocateLicenses(1, 1)).toBe(false); // BRONZE has 1 license max }); it('should always return true for PLATINIUM plan', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.platinium(), }); expect(subscription.canAllocateLicenses(1000, 100)).toBe(true); }); it('should return false when subscription is not active', () => { const subscription = Subscription.fromPersistence({ id: 'sub-123', organizationId: 'org-123', plan: 'BRONZE', status: 'CANCELED', stripeCustomerId: null, stripeSubscriptionId: null, currentPeriodStart: null, currentPeriodEnd: null, cancelAtPeriodEnd: false, createdAt: new Date(), updatedAt: new Date(), }); expect(subscription.canAllocateLicenses(0, 1)).toBe(false); }); }); describe('canUpgradeTo', () => { it('should allow upgrade from BRONZE to SILVER', () => { const subscription = createValidSubscription(); expect(subscription.canUpgradeTo(SubscriptionPlan.silver())).toBe(true); }); it('should allow upgrade from BRONZE to GOLD', () => { const subscription = createValidSubscription(); expect(subscription.canUpgradeTo(SubscriptionPlan.gold())).toBe(true); }); it('should not allow downgrade via canUpgradeTo', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.silver(), }); expect(subscription.canUpgradeTo(SubscriptionPlan.bronze())).toBe(false); }); }); describe('canDowngradeTo', () => { it('should allow downgrade when user count fits', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.silver(), }); expect(subscription.canDowngradeTo(SubscriptionPlan.bronze(), 1)).toBe(true); }); it('should prevent downgrade when user count exceeds new plan', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.silver(), }); expect(subscription.canDowngradeTo(SubscriptionPlan.bronze(), 5)).toBe(false); }); }); describe('updatePlan', () => { it('should update to new plan when valid', () => { const subscription = createValidSubscription(); const updated = subscription.updatePlan(SubscriptionPlan.silver(), 1); expect(updated.plan.value).toBe('SILVER'); }); it('should throw when subscription is not active', () => { const subscription = Subscription.fromPersistence({ id: 'sub-123', organizationId: 'org-123', plan: 'BRONZE', status: 'CANCELED', stripeCustomerId: null, stripeSubscriptionId: null, currentPeriodStart: null, currentPeriodEnd: null, cancelAtPeriodEnd: false, createdAt: new Date(), updatedAt: new Date(), }); expect(() => subscription.updatePlan(SubscriptionPlan.silver(), 0)).toThrow( SubscriptionNotActiveException ); }); it('should throw when downgrading with too many users', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.gold(), }); expect(() => subscription.updatePlan(SubscriptionPlan.silver(), 10)).toThrow( InvalidSubscriptionDowngradeException ); }); }); describe('updateStatus', () => { it('should update subscription status', () => { const subscription = createValidSubscription(); const updated = subscription.updateStatus(SubscriptionStatus.pastDue()); expect(updated.status.value).toBe('PAST_DUE'); }); }); describe('updateStripeCustomerId', () => { it('should update Stripe customer ID', () => { const subscription = createValidSubscription(); const updated = subscription.updateStripeCustomerId('cus_new_123'); expect(updated.stripeCustomerId).toBe('cus_new_123'); }); }); describe('updateStripeSubscription', () => { it('should update Stripe subscription details', () => { const subscription = createValidSubscription(); const periodStart = new Date('2024-02-01'); const periodEnd = new Date('2024-03-01'); const updated = subscription.updateStripeSubscription({ stripeSubscriptionId: 'sub_new_123', currentPeriodStart: periodStart, currentPeriodEnd: periodEnd, cancelAtPeriodEnd: true, }); expect(updated.stripeSubscriptionId).toBe('sub_new_123'); expect(updated.currentPeriodStart).toEqual(periodStart); expect(updated.currentPeriodEnd).toEqual(periodEnd); expect(updated.cancelAtPeriodEnd).toBe(true); }); }); describe('scheduleCancellation', () => { it('should mark subscription for cancellation', () => { const subscription = createValidSubscription(); const updated = subscription.scheduleCancellation(); expect(updated.cancelAtPeriodEnd).toBe(true); }); }); describe('unscheduleCancellation', () => { it('should unmark subscription for cancellation', () => { const subscription = Subscription.fromPersistence({ id: 'sub-123', organizationId: 'org-123', plan: 'SILVER', status: 'ACTIVE', stripeCustomerId: 'cus_123', stripeSubscriptionId: 'sub_123', currentPeriodStart: new Date(), currentPeriodEnd: new Date(), cancelAtPeriodEnd: true, createdAt: new Date(), updatedAt: new Date(), }); const updated = subscription.unscheduleCancellation(); expect(updated.cancelAtPeriodEnd).toBe(false); }); }); describe('cancel', () => { it('should cancel the subscription immediately', () => { const subscription = createValidSubscription(); const updated = subscription.cancel(); expect(updated.status.value).toBe('CANCELED'); expect(updated.cancelAtPeriodEnd).toBe(false); }); }); describe('isFree and isPaid', () => { it('should return true for isFree when BRONZE plan', () => { const subscription = createValidSubscription(); expect(subscription.isFree()).toBe(true); expect(subscription.isPaid()).toBe(false); }); it('should return true for isPaid when SILVER plan', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', plan: SubscriptionPlan.silver(), }); expect(subscription.isFree()).toBe(false); expect(subscription.isPaid()).toBe(true); }); }); describe('toObject', () => { it('should convert to plain object for persistence', () => { const subscription = Subscription.create({ id: 'sub-123', organizationId: 'org-123', stripeCustomerId: 'cus_123', }); const obj = subscription.toObject(); expect(obj.id).toBe('sub-123'); expect(obj.organizationId).toBe('org-123'); expect(obj.plan).toBe('BRONZE'); expect(obj.status).toBe('ACTIVE'); expect(obj.stripeCustomerId).toBe('cus_123'); }); }); });