120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
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/);
|
|
});
|
|
});
|