Add managed databases and services with billing-aligned upgrades.
Introduce product types for managed PostgreSQL, Redis, and RabbitMQ with a dedicated dashboard, Helm-only deploy pipeline, external access, snapshots with progress, and prorated resource or storage upgrades matching application billing rules. PVCs use an expandable StorageClass with automatic migration when legacy disks cannot resize in place. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
DatabaseType,
|
||||
OptionalService,
|
||||
PricingResourceType,
|
||||
ProductType,
|
||||
} from '../common/enums';
|
||||
import { OPTIONAL_SERVICE_DEPLOY_SPECS } from './pricing-catalog.constants';
|
||||
import { CalculateCostDto } from './dto/billing.dto';
|
||||
@@ -207,4 +208,92 @@ describe('PricingCatalogService', () => {
|
||||
expect(service.amountForCycleFromLine(line, BillingCycle.MONTHLY)).toBe(2);
|
||||
expect(service.amountForCycleFromLine(line, BillingCycle.YEARLY)).toBe(3);
|
||||
});
|
||||
|
||||
it('managed_database bills database addon and resources without app base fee', () => {
|
||||
const rates = [
|
||||
{
|
||||
runtime: AppRuntime.NODEJS,
|
||||
resourceType: PricingResourceType.BASE_FEE,
|
||||
hourlyPrice: 999,
|
||||
monthlyPrice: 999,
|
||||
yearlyPrice: 999,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
runtime: AppRuntime.NODEJS,
|
||||
resourceType: PricingResourceType.DATABASE_ADDON,
|
||||
hourlyPrice: 0,
|
||||
monthlyPrice: 500,
|
||||
yearlyPrice: 0,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
runtime: AppRuntime.NODEJS,
|
||||
resourceType: PricingResourceType.STORAGE_PER_GB,
|
||||
hourlyPrice: 0,
|
||||
monthlyPrice: 100,
|
||||
yearlyPrice: 0,
|
||||
isActive: true,
|
||||
},
|
||||
] as PricingRate[];
|
||||
|
||||
const result = service.computeTotalsWithRates(
|
||||
{
|
||||
...baseDto(),
|
||||
productType: ProductType.MANAGED_DATABASE,
|
||||
databaseType: DatabaseType.POSTGRESQL,
|
||||
replicas: 0,
|
||||
dbStorageSize: '2Gi',
|
||||
cpuLimit: '500m',
|
||||
memoryLimit: '512Mi',
|
||||
},
|
||||
rates,
|
||||
emptyOptional(),
|
||||
);
|
||||
expect(result.monthly).toBe(700);
|
||||
expect(result.breakdown.some((l) => l.label.includes('Base fee'))).toBe(false);
|
||||
});
|
||||
|
||||
it('managed_redis bills only optional redis lines', () => {
|
||||
const profile = {
|
||||
service: OptionalService.REDIS,
|
||||
cpuLimit: OPTIONAL_SERVICE_DEPLOY_SPECS[OptionalService.REDIS].cpuLimit,
|
||||
memoryLimit: '256Mi',
|
||||
storageGi: 0,
|
||||
} as OptionalServiceProfile;
|
||||
|
||||
const rates = [
|
||||
{
|
||||
service: OptionalService.REDIS,
|
||||
resourceType: PricingResourceType.BASE_FEE,
|
||||
hourlyPrice: 0,
|
||||
monthlyPrice: 250,
|
||||
yearlyPrice: 0,
|
||||
isActive: true,
|
||||
},
|
||||
] as OptionalServiceRate[];
|
||||
|
||||
const appRates = [
|
||||
{
|
||||
runtime: AppRuntime.NODEJS,
|
||||
resourceType: PricingResourceType.BASE_FEE,
|
||||
hourlyPrice: 999,
|
||||
monthlyPrice: 999,
|
||||
yearlyPrice: 999,
|
||||
isActive: true,
|
||||
},
|
||||
] as PricingRate[];
|
||||
|
||||
const result = service.computeTotalsWithRates(
|
||||
{
|
||||
...baseDto(),
|
||||
productType: ProductType.MANAGED_REDIS,
|
||||
replicas: 0,
|
||||
enableRedis: true,
|
||||
},
|
||||
appRates,
|
||||
{ profiles: [profile], rates, customDomain: null },
|
||||
);
|
||||
expect(result.monthly).toBe(250);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user