Make billing catalog dynamic for all runtimes and bill optional service resources.

Derive admin tabs and addon rows from enums, and add Redis/RabbitMQ/ES CPU/RAM/disk to deploy cost using the app runtime unit rates.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-05-15 18:22:35 +03:30
parent 35235fe0fc
commit bb27c90ae4
6 changed files with 292 additions and 109 deletions
@@ -7,8 +7,10 @@ import {
AppRuntime,
BillingCycle,
DatabaseType,
OptionalService,
PricingResourceType,
} from '../common/enums';
import { OPTIONAL_SERVICE_DEPLOY_SPECS } from './pricing-catalog.constants';
import { CalculateCostDto } from './dto/billing.dto';
describe('PricingCatalogService', () => {
@@ -115,6 +117,38 @@ describe('PricingCatalogService', () => {
expect(result.yearly).not.toBe(result.monthly * 12);
});
it('bills optional service CPU/RAM/storage with same runtime unit rates', () => {
const rates = [
{
runtime: AppRuntime.NODEJS,
resourceType: PricingResourceType.CPU_PER_CORE,
hourlyPrice: 1000,
monthlyPrice: 0,
yearlyPrice: 0,
isActive: true,
},
{
runtime: AppRuntime.NODEJS,
resourceType: PricingResourceType.MEMORY_PER_GB,
hourlyPrice: 0,
monthlyPrice: 0,
yearlyPrice: 0,
isActive: true,
},
] as PricingRate[];
const redisCpu = parseFloat(OPTIONAL_SERVICE_DEPLOY_SPECS[OptionalService.REDIS].cpuLimit) / 1000;
const without = service.computeTotalsWithRates(baseDto(), rates, []);
const withRedis = service.computeTotalsWithRates(
{ ...baseDto(), enableRedis: true },
rates,
[],
);
expect(withRedis.hourly - without.hourly).toBe(Math.round(redisCpu * 1000));
});
it('amountForCycleFromLine picks the correct column', () => {
const line = { label: 'Test', hourly: 1, monthly: 2, yearly: 3 };
expect(service.amountForCycleFromLine(line, BillingCycle.HOURLY)).toBe(1);