import { describe, it, expect } from 'vitest'; import { DURATION_OPTIONS, calculatePricing, durationByDays, pluralize, round2, sumAddonPrices, devicesLabel, formatMoney, } from './format'; import type { AddonData, PricingPlans } from '../types'; const pricing: PricingPlans = { device_price: 100, addons: [ { id: 'a', name: 'A', price: 50, free_threshold: -1, is_enabled: true }, { id: 'b', name: 'B', price: 30, free_threshold: -1, is_enabled: true }, ], }; describe('round2', () => { it('rounds to two decimals', () => { expect(round2(1.005)).toBe(1.01); expect(round2(2.345)).toBe(2.35); expect(round2(10)).toBe(10); }); }); describe('sumAddonPrices', () => { const addons: AddonData[] = pricing.addons; it('sums selected addon prices', () => { expect(sumAddonPrices(addons, ['a', 'b'], 6)).toBe(80); expect(sumAddonPrices(addons, ['a'], 6)).toBe(50); expect(sumAddonPrices(addons, ['unknown'], 6)).toBe(0); expect(sumAddonPrices(addons, [], 6)).toBe(0); }); it('makes addons free at their device threshold but keeps -1 paid', () => { const thresholdAddons: AddonData[] = [ { id: 'threshold', name: 'Threshold', price: 50, free_threshold: 6, is_enabled: true }, { id: 'paid', name: 'Paid', price: 30, free_threshold: -1, is_enabled: true }, ]; expect(sumAddonPrices(thresholdAddons, ['threshold', 'paid'], 5)).toBe(80); expect(sumAddonPrices(thresholdAddons, ['threshold', 'paid'], 6)).toBe(30); }); }); describe('calculatePricing', () => { it('computes linear device cost + addons + duration total', () => { const r = calculatePricing({ pricing, devices: 6, addonIds: ['a'], duration: DURATION_OPTIONS[1]!, // 90 days }); // monthly = 6*100 + 50 = 650 // total = 650 * 90 / 30 = 1950 expect(r.deviceCost).toBe(600); expect(r.addonCost).toBe(50); expect(r.monthly).toBe(650); expect(r.periodTotal).toBe(1950); }); it('handles 1 month / 30 days', () => { const r = calculatePricing({ pricing, devices: 3, addonIds: [], duration: DURATION_OPTIONS[0]!, }); expect(r.monthly).toBe(300); expect(r.periodTotal).toBe(300); }); it('12 months / 365 days yields near 12x', () => { const r = calculatePricing({ pricing, devices: 1, addonIds: [], duration: DURATION_OPTIONS[3]!, }); // monthly=100, total = 100 * 365/30 = 1216.67 expect(r.periodTotal).toBeCloseTo(1216.67, 1); }); }); describe('durationByDays', () => { it('finds duration by days', () => { expect(durationByDays(30).months).toBe(1); expect(durationByDays(90).months).toBe(3); expect(durationByDays(180).months).toBe(6); expect(durationByDays(365).months).toBe(12); }); it('falls back to first option for unknown days', () => { expect(durationByDays(7).months).toBe(1); }); }); describe('pluralize / devicesLabel', () => { it('picks correct form', () => { expect(pluralize(1, 'устройство', 'устройства', 'устройств')).toBe('устройство'); expect(pluralize(2, 'устройство', 'устройства', 'устройств')).toBe('устройства'); expect(pluralize(5, 'устройство', 'устройства', 'устройств')).toBe('устройств'); expect(pluralize(11, 'устройство', 'устройства', 'устройств')).toBe('устройств'); }); it('devicesLabel combines number and noun', () => { expect(devicesLabel(3)).toBe('3 устройства'); expect(devicesLabel(6)).toBe('6 устройств'); }); }); describe('formatMoney', () => { it('formats rubles', () => { expect(formatMoney(100)).toMatch(/100/); expect(formatMoney(1950)).toMatch(/1\s950/); }); });