fix preprod
Some checks failed
CD Preprod / Backend — Lint (push) Successful in 10m27s
CD Preprod / Frontend — Lint & Type-check (push) Successful in 10m55s
CD Preprod / Backend — Unit Tests (push) Successful in 10m10s
CD Preprod / Frontend — Unit Tests (push) Successful in 10m33s
CD Preprod / Backend — Integration Tests (push) Successful in 10m6s
CD Preprod / Build Backend (push) Successful in 16m5s
CD Preprod / Build Frontend (push) Successful in 35m0s
CD Preprod / Deploy to Preprod (push) Failing after 1s
CD Preprod / Smoke Tests (push) Has been skipped
CD Preprod / Notify Success (push) Has been skipped
CD Preprod / Notify Failure (push) Has been skipped

This commit is contained in:
David 2026-04-04 17:58:36 +02:00
parent fe7cd1f792
commit 72141c5f68
3 changed files with 178 additions and 157 deletions

View File

@ -21,12 +21,12 @@ describe('Subscription Entity', () => {
}; };
describe('create', () => { describe('create', () => {
it('should create a subscription with default FREE plan', () => { it('should create a subscription with default BRONZE plan', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription();
expect(subscription.id).toBe('sub-123'); expect(subscription.id).toBe('sub-123');
expect(subscription.organizationId).toBe('org-123'); expect(subscription.organizationId).toBe('org-123');
expect(subscription.plan.value).toBe('FREE'); expect(subscription.plan.value).toBe('BRONZE');
expect(subscription.status.value).toBe('ACTIVE'); expect(subscription.status.value).toBe('ACTIVE');
expect(subscription.cancelAtPeriodEnd).toBe(false); expect(subscription.cancelAtPeriodEnd).toBe(false);
}); });
@ -35,10 +35,10 @@ describe('Subscription Entity', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.starter(), plan: SubscriptionPlan.silver(),
}); });
expect(subscription.plan.value).toBe('STARTER'); expect(subscription.plan.value).toBe('SILVER');
}); });
it('should create a subscription with Stripe IDs', () => { it('should create a subscription with Stripe IDs', () => {
@ -59,7 +59,7 @@ describe('Subscription Entity', () => {
const subscription = Subscription.fromPersistence({ const subscription = Subscription.fromPersistence({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: 'PRO', plan: 'GOLD',
status: 'ACTIVE', status: 'ACTIVE',
stripeCustomerId: 'cus_123', stripeCustomerId: 'cus_123',
stripeSubscriptionId: 'sub_stripe_123', stripeSubscriptionId: 'sub_stripe_123',
@ -71,57 +71,57 @@ describe('Subscription Entity', () => {
}); });
expect(subscription.id).toBe('sub-123'); expect(subscription.id).toBe('sub-123');
expect(subscription.plan.value).toBe('PRO'); expect(subscription.plan.value).toBe('GOLD');
expect(subscription.status.value).toBe('ACTIVE'); expect(subscription.status.value).toBe('ACTIVE');
expect(subscription.cancelAtPeriodEnd).toBe(true); expect(subscription.cancelAtPeriodEnd).toBe(true);
}); });
}); });
describe('maxLicenses', () => { describe('maxLicenses', () => {
it('should return correct limits for FREE plan', () => { it('should return correct limits for BRONZE plan', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription();
expect(subscription.maxLicenses).toBe(2); expect(subscription.maxLicenses).toBe(1);
}); });
it('should return correct limits for STARTER plan', () => { it('should return correct limits for SILVER plan', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.starter(), plan: SubscriptionPlan.silver(),
}); });
expect(subscription.maxLicenses).toBe(5); expect(subscription.maxLicenses).toBe(5);
}); });
it('should return correct limits for PRO plan', () => { it('should return correct limits for GOLD plan', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.pro(), plan: SubscriptionPlan.gold(),
}); });
expect(subscription.maxLicenses).toBe(20); expect(subscription.maxLicenses).toBe(20);
}); });
it('should return -1 for ENTERPRISE plan (unlimited)', () => { it('should return -1 for PLATINIUM plan (unlimited)', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.enterprise(), plan: SubscriptionPlan.platinium(),
}); });
expect(subscription.maxLicenses).toBe(-1); expect(subscription.maxLicenses).toBe(-1);
}); });
}); });
describe('isUnlimited', () => { describe('isUnlimited', () => {
it('should return false for FREE plan', () => { it('should return false for BRONZE plan', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription();
expect(subscription.isUnlimited()).toBe(false); expect(subscription.isUnlimited()).toBe(false);
}); });
it('should return true for ENTERPRISE plan', () => { it('should return true for PLATINIUM plan', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.enterprise(), plan: SubscriptionPlan.platinium(),
}); });
expect(subscription.isUnlimited()).toBe(true); expect(subscription.isUnlimited()).toBe(true);
}); });
@ -137,7 +137,7 @@ describe('Subscription Entity', () => {
const subscription = Subscription.fromPersistence({ const subscription = Subscription.fromPersistence({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: 'FREE', plan: 'BRONZE',
status: 'TRIALING', status: 'TRIALING',
stripeCustomerId: null, stripeCustomerId: null,
stripeSubscriptionId: null, stripeSubscriptionId: null,
@ -154,7 +154,7 @@ describe('Subscription Entity', () => {
const subscription = Subscription.fromPersistence({ const subscription = Subscription.fromPersistence({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: 'FREE', plan: 'BRONZE',
status: 'CANCELED', status: 'CANCELED',
stripeCustomerId: null, stripeCustomerId: null,
stripeSubscriptionId: null, stripeSubscriptionId: null,
@ -170,21 +170,20 @@ describe('Subscription Entity', () => {
describe('canAllocateLicenses', () => { describe('canAllocateLicenses', () => {
it('should return true when licenses are available', () => { it('should return true when licenses are available', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription(); // BRONZE = 1 license
expect(subscription.canAllocateLicenses(0, 1)).toBe(true); expect(subscription.canAllocateLicenses(0, 1)).toBe(true);
expect(subscription.canAllocateLicenses(1, 1)).toBe(true);
}); });
it('should return false when no licenses available', () => { it('should return false when no licenses available', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription();
expect(subscription.canAllocateLicenses(2, 1)).toBe(false); // FREE has 2 licenses expect(subscription.canAllocateLicenses(1, 1)).toBe(false); // BRONZE has 1 license max
}); });
it('should always return true for ENTERPRISE plan', () => { it('should always return true for PLATINIUM plan', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.enterprise(), plan: SubscriptionPlan.platinium(),
}); });
expect(subscription.canAllocateLicenses(1000, 100)).toBe(true); expect(subscription.canAllocateLicenses(1000, 100)).toBe(true);
}); });
@ -193,7 +192,7 @@ describe('Subscription Entity', () => {
const subscription = Subscription.fromPersistence({ const subscription = Subscription.fromPersistence({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: 'FREE', plan: 'BRONZE',
status: 'CANCELED', status: 'CANCELED',
stripeCustomerId: null, stripeCustomerId: null,
stripeSubscriptionId: null, stripeSubscriptionId: null,
@ -208,23 +207,23 @@ describe('Subscription Entity', () => {
}); });
describe('canUpgradeTo', () => { describe('canUpgradeTo', () => {
it('should allow upgrade from FREE to STARTER', () => { it('should allow upgrade from BRONZE to SILVER', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription();
expect(subscription.canUpgradeTo(SubscriptionPlan.starter())).toBe(true); expect(subscription.canUpgradeTo(SubscriptionPlan.silver())).toBe(true);
}); });
it('should allow upgrade from FREE to PRO', () => { it('should allow upgrade from BRONZE to GOLD', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription();
expect(subscription.canUpgradeTo(SubscriptionPlan.pro())).toBe(true); expect(subscription.canUpgradeTo(SubscriptionPlan.gold())).toBe(true);
}); });
it('should not allow downgrade via canUpgradeTo', () => { it('should not allow downgrade via canUpgradeTo', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.starter(), plan: SubscriptionPlan.silver(),
}); });
expect(subscription.canUpgradeTo(SubscriptionPlan.free())).toBe(false); expect(subscription.canUpgradeTo(SubscriptionPlan.bronze())).toBe(false);
}); });
}); });
@ -233,34 +232,34 @@ describe('Subscription Entity', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.starter(), plan: SubscriptionPlan.silver(),
}); });
expect(subscription.canDowngradeTo(SubscriptionPlan.free(), 1)).toBe(true); expect(subscription.canDowngradeTo(SubscriptionPlan.bronze(), 1)).toBe(true);
}); });
it('should prevent downgrade when user count exceeds new plan', () => { it('should prevent downgrade when user count exceeds new plan', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.starter(), plan: SubscriptionPlan.silver(),
}); });
expect(subscription.canDowngradeTo(SubscriptionPlan.free(), 5)).toBe(false); expect(subscription.canDowngradeTo(SubscriptionPlan.bronze(), 5)).toBe(false);
}); });
}); });
describe('updatePlan', () => { describe('updatePlan', () => {
it('should update to new plan when valid', () => { it('should update to new plan when valid', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription();
const updated = subscription.updatePlan(SubscriptionPlan.starter(), 1); const updated = subscription.updatePlan(SubscriptionPlan.silver(), 1);
expect(updated.plan.value).toBe('STARTER'); expect(updated.plan.value).toBe('SILVER');
}); });
it('should throw when subscription is not active', () => { it('should throw when subscription is not active', () => {
const subscription = Subscription.fromPersistence({ const subscription = Subscription.fromPersistence({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: 'FREE', plan: 'BRONZE',
status: 'CANCELED', status: 'CANCELED',
stripeCustomerId: null, stripeCustomerId: null,
stripeSubscriptionId: null, stripeSubscriptionId: null,
@ -271,7 +270,7 @@ describe('Subscription Entity', () => {
updatedAt: new Date(), updatedAt: new Date(),
}); });
expect(() => subscription.updatePlan(SubscriptionPlan.starter(), 0)).toThrow( expect(() => subscription.updatePlan(SubscriptionPlan.silver(), 0)).toThrow(
SubscriptionNotActiveException SubscriptionNotActiveException
); );
}); });
@ -280,10 +279,10 @@ describe('Subscription Entity', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.pro(), plan: SubscriptionPlan.gold(),
}); });
expect(() => subscription.updatePlan(SubscriptionPlan.starter(), 10)).toThrow( expect(() => subscription.updatePlan(SubscriptionPlan.silver(), 10)).toThrow(
InvalidSubscriptionDowngradeException InvalidSubscriptionDowngradeException
); );
}); });
@ -341,7 +340,7 @@ describe('Subscription Entity', () => {
const subscription = Subscription.fromPersistence({ const subscription = Subscription.fromPersistence({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: 'STARTER', plan: 'SILVER',
status: 'ACTIVE', status: 'ACTIVE',
stripeCustomerId: 'cus_123', stripeCustomerId: 'cus_123',
stripeSubscriptionId: 'sub_123', stripeSubscriptionId: 'sub_123',
@ -368,17 +367,17 @@ describe('Subscription Entity', () => {
}); });
describe('isFree and isPaid', () => { describe('isFree and isPaid', () => {
it('should return true for isFree when FREE plan', () => { it('should return true for isFree when BRONZE plan', () => {
const subscription = createValidSubscription(); const subscription = createValidSubscription();
expect(subscription.isFree()).toBe(true); expect(subscription.isFree()).toBe(true);
expect(subscription.isPaid()).toBe(false); expect(subscription.isPaid()).toBe(false);
}); });
it('should return true for isPaid when STARTER plan', () => { it('should return true for isPaid when SILVER plan', () => {
const subscription = Subscription.create({ const subscription = Subscription.create({
id: 'sub-123', id: 'sub-123',
organizationId: 'org-123', organizationId: 'org-123',
plan: SubscriptionPlan.starter(), plan: SubscriptionPlan.silver(),
}); });
expect(subscription.isFree()).toBe(false); expect(subscription.isFree()).toBe(false);
expect(subscription.isPaid()).toBe(true); expect(subscription.isPaid()).toBe(true);
@ -397,7 +396,7 @@ describe('Subscription Entity', () => {
expect(obj.id).toBe('sub-123'); expect(obj.id).toBe('sub-123');
expect(obj.organizationId).toBe('org-123'); expect(obj.organizationId).toBe('org-123');
expect(obj.plan).toBe('FREE'); expect(obj.plan).toBe('BRONZE');
expect(obj.status).toBe('ACTIVE'); expect(obj.status).toBe('ACTIVE');
expect(obj.stripeCustomerId).toBe('cus_123'); expect(obj.stripeCustomerId).toBe('cus_123');
}); });

View File

@ -8,31 +8,56 @@ import { SubscriptionPlan } from './subscription-plan.vo';
describe('SubscriptionPlan Value Object', () => { describe('SubscriptionPlan Value Object', () => {
describe('static factory methods', () => { describe('static factory methods', () => {
it('should create FREE plan', () => { it('should create BRONZE plan via bronze()', () => {
const plan = SubscriptionPlan.bronze();
expect(plan.value).toBe('BRONZE');
});
it('should create SILVER plan via silver()', () => {
const plan = SubscriptionPlan.silver();
expect(plan.value).toBe('SILVER');
});
it('should create GOLD plan via gold()', () => {
const plan = SubscriptionPlan.gold();
expect(plan.value).toBe('GOLD');
});
it('should create PLATINIUM plan via platinium()', () => {
const plan = SubscriptionPlan.platinium();
expect(plan.value).toBe('PLATINIUM');
});
it('should create BRONZE plan via free() alias', () => {
const plan = SubscriptionPlan.free(); const plan = SubscriptionPlan.free();
expect(plan.value).toBe('FREE'); expect(plan.value).toBe('BRONZE');
}); });
it('should create STARTER plan', () => { it('should create SILVER plan via starter() alias', () => {
const plan = SubscriptionPlan.starter(); const plan = SubscriptionPlan.starter();
expect(plan.value).toBe('STARTER'); expect(plan.value).toBe('SILVER');
}); });
it('should create PRO plan', () => { it('should create GOLD plan via pro() alias', () => {
const plan = SubscriptionPlan.pro(); const plan = SubscriptionPlan.pro();
expect(plan.value).toBe('PRO'); expect(plan.value).toBe('GOLD');
}); });
it('should create ENTERPRISE plan', () => { it('should create PLATINIUM plan via enterprise() alias', () => {
const plan = SubscriptionPlan.enterprise(); const plan = SubscriptionPlan.enterprise();
expect(plan.value).toBe('ENTERPRISE'); expect(plan.value).toBe('PLATINIUM');
}); });
}); });
describe('create', () => { describe('create', () => {
it('should create plan from valid type', () => { it('should create plan from valid type SILVER', () => {
const plan = SubscriptionPlan.create('STARTER'); const plan = SubscriptionPlan.create('SILVER');
expect(plan.value).toBe('STARTER'); expect(plan.value).toBe('SILVER');
});
it('should create plan from valid type BRONZE', () => {
const plan = SubscriptionPlan.create('BRONZE');
expect(plan.value).toBe('BRONZE');
}); });
it('should throw for invalid plan type', () => { it('should throw for invalid plan type', () => {
@ -41,9 +66,29 @@ describe('SubscriptionPlan Value Object', () => {
}); });
describe('fromString', () => { describe('fromString', () => {
it('should create plan from lowercase string', () => { it('should create SILVER from lowercase "silver"', () => {
const plan = SubscriptionPlan.fromString('silver');
expect(plan.value).toBe('SILVER');
});
it('should map legacy "starter" to SILVER', () => {
const plan = SubscriptionPlan.fromString('starter'); const plan = SubscriptionPlan.fromString('starter');
expect(plan.value).toBe('STARTER'); expect(plan.value).toBe('SILVER');
});
it('should map legacy "free" to BRONZE', () => {
const plan = SubscriptionPlan.fromString('free');
expect(plan.value).toBe('BRONZE');
});
it('should map legacy "pro" to GOLD', () => {
const plan = SubscriptionPlan.fromString('pro');
expect(plan.value).toBe('GOLD');
});
it('should map legacy "enterprise" to PLATINIUM', () => {
const plan = SubscriptionPlan.fromString('enterprise');
expect(plan.value).toBe('PLATINIUM');
}); });
it('should throw for invalid string', () => { it('should throw for invalid string', () => {
@ -52,146 +97,150 @@ describe('SubscriptionPlan Value Object', () => {
}); });
describe('maxLicenses', () => { describe('maxLicenses', () => {
it('should return 2 for FREE plan', () => { it('should return 1 for BRONZE plan', () => {
const plan = SubscriptionPlan.free(); const plan = SubscriptionPlan.bronze();
expect(plan.maxLicenses).toBe(2); expect(plan.maxLicenses).toBe(1);
}); });
it('should return 5 for STARTER plan', () => { it('should return 5 for SILVER plan', () => {
const plan = SubscriptionPlan.starter(); const plan = SubscriptionPlan.silver();
expect(plan.maxLicenses).toBe(5); expect(plan.maxLicenses).toBe(5);
}); });
it('should return 20 for PRO plan', () => { it('should return 20 for GOLD plan', () => {
const plan = SubscriptionPlan.pro(); const plan = SubscriptionPlan.gold();
expect(plan.maxLicenses).toBe(20); expect(plan.maxLicenses).toBe(20);
}); });
it('should return -1 (unlimited) for ENTERPRISE plan', () => { it('should return -1 (unlimited) for PLATINIUM plan', () => {
const plan = SubscriptionPlan.enterprise(); const plan = SubscriptionPlan.platinium();
expect(plan.maxLicenses).toBe(-1); expect(plan.maxLicenses).toBe(-1);
}); });
}); });
describe('isUnlimited', () => { describe('isUnlimited', () => {
it('should return false for FREE plan', () => { it('should return false for BRONZE plan', () => {
expect(SubscriptionPlan.free().isUnlimited()).toBe(false); expect(SubscriptionPlan.bronze().isUnlimited()).toBe(false);
}); });
it('should return false for STARTER plan', () => { it('should return false for SILVER plan', () => {
expect(SubscriptionPlan.starter().isUnlimited()).toBe(false); expect(SubscriptionPlan.silver().isUnlimited()).toBe(false);
}); });
it('should return false for PRO plan', () => { it('should return false for GOLD plan', () => {
expect(SubscriptionPlan.pro().isUnlimited()).toBe(false); expect(SubscriptionPlan.gold().isUnlimited()).toBe(false);
}); });
it('should return true for ENTERPRISE plan', () => { it('should return true for PLATINIUM plan', () => {
expect(SubscriptionPlan.enterprise().isUnlimited()).toBe(true); expect(SubscriptionPlan.platinium().isUnlimited()).toBe(true);
}); });
}); });
describe('isPaid', () => { describe('isPaid', () => {
it('should return false for FREE plan', () => { it('should return false for BRONZE plan', () => {
expect(SubscriptionPlan.free().isPaid()).toBe(false); expect(SubscriptionPlan.bronze().isPaid()).toBe(false);
}); });
it('should return true for STARTER plan', () => { it('should return true for SILVER plan', () => {
expect(SubscriptionPlan.starter().isPaid()).toBe(true); expect(SubscriptionPlan.silver().isPaid()).toBe(true);
}); });
it('should return true for PRO plan', () => { it('should return true for GOLD plan', () => {
expect(SubscriptionPlan.pro().isPaid()).toBe(true); expect(SubscriptionPlan.gold().isPaid()).toBe(true);
}); });
it('should return true for ENTERPRISE plan', () => { it('should return true for PLATINIUM plan', () => {
expect(SubscriptionPlan.enterprise().isPaid()).toBe(true); expect(SubscriptionPlan.platinium().isPaid()).toBe(true);
}); });
}); });
describe('isFree', () => { describe('isFree', () => {
it('should return true for FREE plan', () => { it('should return true for BRONZE plan', () => {
expect(SubscriptionPlan.free().isFree()).toBe(true); expect(SubscriptionPlan.bronze().isFree()).toBe(true);
}); });
it('should return false for STARTER plan', () => { it('should return false for SILVER plan', () => {
expect(SubscriptionPlan.starter().isFree()).toBe(false); expect(SubscriptionPlan.silver().isFree()).toBe(false);
}); });
}); });
describe('canAccommodateUsers', () => { describe('canAccommodateUsers', () => {
it('should return true for FREE plan with 2 users', () => { it('should return true for BRONZE plan with 1 user', () => {
expect(SubscriptionPlan.free().canAccommodateUsers(2)).toBe(true); expect(SubscriptionPlan.bronze().canAccommodateUsers(1)).toBe(true);
}); });
it('should return false for FREE plan with 3 users', () => { it('should return false for BRONZE plan with 2 users', () => {
expect(SubscriptionPlan.free().canAccommodateUsers(3)).toBe(false); expect(SubscriptionPlan.bronze().canAccommodateUsers(2)).toBe(false);
}); });
it('should return true for STARTER plan with 5 users', () => { it('should return true for SILVER plan with 5 users', () => {
expect(SubscriptionPlan.starter().canAccommodateUsers(5)).toBe(true); expect(SubscriptionPlan.silver().canAccommodateUsers(5)).toBe(true);
}); });
it('should always return true for ENTERPRISE plan', () => { it('should always return true for PLATINIUM plan', () => {
expect(SubscriptionPlan.enterprise().canAccommodateUsers(1000)).toBe(true); expect(SubscriptionPlan.platinium().canAccommodateUsers(1000)).toBe(true);
}); });
}); });
describe('canUpgradeTo', () => { describe('canUpgradeTo', () => {
it('should allow upgrade from FREE to STARTER', () => { it('should allow upgrade from BRONZE to SILVER', () => {
expect(SubscriptionPlan.free().canUpgradeTo(SubscriptionPlan.starter())).toBe(true); expect(SubscriptionPlan.bronze().canUpgradeTo(SubscriptionPlan.silver())).toBe(true);
}); });
it('should allow upgrade from FREE to PRO', () => { it('should allow upgrade from BRONZE to GOLD', () => {
expect(SubscriptionPlan.free().canUpgradeTo(SubscriptionPlan.pro())).toBe(true); expect(SubscriptionPlan.bronze().canUpgradeTo(SubscriptionPlan.gold())).toBe(true);
}); });
it('should allow upgrade from FREE to ENTERPRISE', () => { it('should allow upgrade from BRONZE to PLATINIUM', () => {
expect(SubscriptionPlan.free().canUpgradeTo(SubscriptionPlan.enterprise())).toBe(true); expect(SubscriptionPlan.bronze().canUpgradeTo(SubscriptionPlan.platinium())).toBe(true);
}); });
it('should allow upgrade from STARTER to PRO', () => { it('should allow upgrade from SILVER to GOLD', () => {
expect(SubscriptionPlan.starter().canUpgradeTo(SubscriptionPlan.pro())).toBe(true); expect(SubscriptionPlan.silver().canUpgradeTo(SubscriptionPlan.gold())).toBe(true);
}); });
it('should not allow downgrade from STARTER to FREE', () => { it('should not allow downgrade from SILVER to BRONZE', () => {
expect(SubscriptionPlan.starter().canUpgradeTo(SubscriptionPlan.free())).toBe(false); expect(SubscriptionPlan.silver().canUpgradeTo(SubscriptionPlan.bronze())).toBe(false);
}); });
it('should not allow same plan upgrade', () => { it('should not allow same plan upgrade', () => {
expect(SubscriptionPlan.pro().canUpgradeTo(SubscriptionPlan.pro())).toBe(false); expect(SubscriptionPlan.gold().canUpgradeTo(SubscriptionPlan.gold())).toBe(false);
}); });
}); });
describe('canDowngradeTo', () => { describe('canDowngradeTo', () => {
it('should allow downgrade from STARTER to FREE when users fit', () => { it('should allow downgrade from SILVER to BRONZE when users fit', () => {
expect(SubscriptionPlan.starter().canDowngradeTo(SubscriptionPlan.free(), 1)).toBe(true); expect(SubscriptionPlan.silver().canDowngradeTo(SubscriptionPlan.bronze(), 1)).toBe(true);
}); });
it('should not allow downgrade from STARTER to FREE when users exceed', () => { it('should not allow downgrade from SILVER to BRONZE when users exceed', () => {
expect(SubscriptionPlan.starter().canDowngradeTo(SubscriptionPlan.free(), 5)).toBe(false); expect(SubscriptionPlan.silver().canDowngradeTo(SubscriptionPlan.bronze(), 5)).toBe(false);
}); });
it('should not allow upgrade via canDowngradeTo', () => { it('should not allow upgrade via canDowngradeTo', () => {
expect(SubscriptionPlan.free().canDowngradeTo(SubscriptionPlan.starter(), 1)).toBe(false); expect(SubscriptionPlan.bronze().canDowngradeTo(SubscriptionPlan.silver(), 1)).toBe(false);
}); });
}); });
describe('plan details', () => { describe('plan details', () => {
it('should return correct name for FREE plan', () => { it('should return correct name for BRONZE plan', () => {
expect(SubscriptionPlan.free().name).toBe('Free'); expect(SubscriptionPlan.bronze().name).toBe('Bronze');
}); });
it('should return correct prices for STARTER plan', () => { it('should return correct name for SILVER plan', () => {
const plan = SubscriptionPlan.starter(); expect(SubscriptionPlan.silver().name).toBe('Silver');
expect(plan.monthlyPriceEur).toBe(49);
expect(plan.yearlyPriceEur).toBe(470);
}); });
it('should return features for PRO plan', () => { it('should return correct prices for SILVER plan', () => {
const plan = SubscriptionPlan.pro(); const plan = SubscriptionPlan.silver();
expect(plan.features).toContain('Up to 20 users'); expect(plan.monthlyPriceEur).toBe(249);
expect(plan.features).toContain('API access'); expect(plan.yearlyPriceEur).toBe(2739);
});
it('should return features for GOLD plan', () => {
const plan = SubscriptionPlan.gold();
expect(plan.features).toContain("Jusqu'à 20 utilisateurs");
expect(plan.features).toContain('Intégration API');
}); });
}); });
@ -200,24 +249,24 @@ describe('SubscriptionPlan Value Object', () => {
const plans = SubscriptionPlan.getAllPlans(); const plans = SubscriptionPlan.getAllPlans();
expect(plans).toHaveLength(4); expect(plans).toHaveLength(4);
expect(plans.map(p => p.value)).toEqual(['FREE', 'STARTER', 'PRO', 'ENTERPRISE']); expect(plans.map(p => p.value)).toEqual(['BRONZE', 'SILVER', 'GOLD', 'PLATINIUM']);
}); });
}); });
describe('equals', () => { describe('equals', () => {
it('should return true for same plan', () => { it('should return true for same plan', () => {
expect(SubscriptionPlan.free().equals(SubscriptionPlan.free())).toBe(true); expect(SubscriptionPlan.bronze().equals(SubscriptionPlan.bronze())).toBe(true);
}); });
it('should return false for different plans', () => { it('should return false for different plans', () => {
expect(SubscriptionPlan.free().equals(SubscriptionPlan.starter())).toBe(false); expect(SubscriptionPlan.bronze().equals(SubscriptionPlan.silver())).toBe(false);
}); });
}); });
describe('toString', () => { describe('toString', () => {
it('should return plan value as string', () => { it('should return plan value as string', () => {
expect(SubscriptionPlan.free().toString()).toBe('FREE'); expect(SubscriptionPlan.bronze().toString()).toBe('BRONZE');
expect(SubscriptionPlan.starter().toString()).toBe('STARTER'); expect(SubscriptionPlan.silver().toString()).toBe('SILVER');
}); });
}); });
}); });

View File

@ -55,7 +55,7 @@ const PLAN_DETAILS: Record<SubscriptionPlanType, PlanDetails> = {
name: 'Silver', name: 'Silver',
maxLicenses: 5, maxLicenses: 5,
monthlyPriceEur: 249, monthlyPriceEur: 249,
yearlyPriceEur: 2739, // 249 * 11 months yearlyPriceEur: 2739,
maxShipmentsPerYear: -1, maxShipmentsPerYear: -1,
commissionRatePercent: 3, commissionRatePercent: 3,
statusBadge: 'silver', statusBadge: 'silver',
@ -75,7 +75,7 @@ const PLAN_DETAILS: Record<SubscriptionPlanType, PlanDetails> = {
name: 'Gold', name: 'Gold',
maxLicenses: 20, maxLicenses: 20,
monthlyPriceEur: 899, monthlyPriceEur: 899,
yearlyPriceEur: 9889, // 899 * 11 months yearlyPriceEur: 9889,
maxShipmentsPerYear: -1, maxShipmentsPerYear: -1,
commissionRatePercent: 2, commissionRatePercent: 2,
statusBadge: 'gold', statusBadge: 'gold',
@ -225,59 +225,35 @@ export class SubscriptionPlan {
return PLAN_DETAILS[this.plan].planFeatures; return PLAN_DETAILS[this.plan].planFeatures;
} }
/**
* Check if this plan includes a specific feature
*/
hasFeature(feature: PlanFeature): boolean { hasFeature(feature: PlanFeature): boolean {
return this.planFeatures.includes(feature); return this.planFeatures.includes(feature);
} }
/**
* Returns true if this plan has unlimited licenses
*/
isUnlimited(): boolean { isUnlimited(): boolean {
return this.maxLicenses === -1; return this.maxLicenses === -1;
} }
/**
* Returns true if this plan has unlimited shipments
*/
hasUnlimitedShipments(): boolean { hasUnlimitedShipments(): boolean {
return this.maxShipmentsPerYear === -1; return this.maxShipmentsPerYear === -1;
} }
/**
* Returns true if this is a paid plan
*/
isPaid(): boolean { isPaid(): boolean {
return this.plan !== 'BRONZE'; return this.plan !== 'BRONZE';
} }
/**
* Returns true if this is the free (Bronze) plan
*/
isFree(): boolean { isFree(): boolean {
return this.plan === 'BRONZE'; return this.plan === 'BRONZE';
} }
/**
* Returns true if this plan has custom pricing (Platinium)
*/
isCustomPricing(): boolean { isCustomPricing(): boolean {
return this.plan === 'PLATINIUM'; return this.plan === 'PLATINIUM';
} }
/**
* Check if a given number of users can be accommodated by this plan
*/
canAccommodateUsers(userCount: number): boolean { canAccommodateUsers(userCount: number): boolean {
if (this.isUnlimited()) return true; if (this.isUnlimited()) return true;
return userCount <= this.maxLicenses; return userCount <= this.maxLicenses;
} }
/**
* Check if upgrade to target plan is allowed
*/
canUpgradeTo(targetPlan: SubscriptionPlan): boolean { canUpgradeTo(targetPlan: SubscriptionPlan): boolean {
const planOrder: SubscriptionPlanType[] = ['BRONZE', 'SILVER', 'GOLD', 'PLATINIUM']; const planOrder: SubscriptionPlanType[] = ['BRONZE', 'SILVER', 'GOLD', 'PLATINIUM'];
const currentIndex = planOrder.indexOf(this.plan); const currentIndex = planOrder.indexOf(this.plan);
@ -285,15 +261,12 @@ export class SubscriptionPlan {
return targetIndex > currentIndex; return targetIndex > currentIndex;
} }
/**
* Check if downgrade to target plan is allowed given current user count
*/
canDowngradeTo(targetPlan: SubscriptionPlan, currentUserCount: number): boolean { canDowngradeTo(targetPlan: SubscriptionPlan, currentUserCount: number): boolean {
const planOrder: SubscriptionPlanType[] = ['BRONZE', 'SILVER', 'GOLD', 'PLATINIUM']; const planOrder: SubscriptionPlanType[] = ['BRONZE', 'SILVER', 'GOLD', 'PLATINIUM'];
const currentIndex = planOrder.indexOf(this.plan); const currentIndex = planOrder.indexOf(this.plan);
const targetIndex = planOrder.indexOf(targetPlan.value); const targetIndex = planOrder.indexOf(targetPlan.value);
if (targetIndex >= currentIndex) return false; // Not a downgrade if (targetIndex >= currentIndex) return false;
return targetPlan.canAccommodateUsers(currentUserCount); return targetPlan.canAccommodateUsers(currentUserCount);
} }