Files
vps-tracker/apps/api/src/services/userapi/mappers.test.ts
T
DenozordecandCursor b15e10f117
Docker / build (push) Has been cancelled
fix(userapi): корректно определять суточные ставки Macloud/VDSina при синке
Учитываются server-group, full_cost при нулевом cost и неактивные тарифные планы из индекса.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 10:04:42 +07:00

252 lines
6.4 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { mapOperationToPayment, mapServerToVps } from './mappers.js'
import type { UserApiOperation, UserApiServerDetail } from './operations.js'
const server: UserApiServerDetail = {
id: 12345,
name: 'Server #12345',
full_name: 'Server 2 RAM / 1 CPU / 40 NVMe #12345',
created: '2022-02-24',
end: '2029-02-20',
status: 'active',
host: 'super.server.host',
ip: { id: 1, ip: '91.84.101.78', type: '4' },
template: { id: 23, name: 'Ubuntu 24.04' },
datacenter: { id: 1, name: 'Amsterdam 1', country: 'nl' },
'server-plan': { id: 1, name: '2 RAM / 1 CPU / 40 NVMe' },
data: {
cpu: { value: 1 },
ram: { value: 2 },
disk: { value: 40 },
traff: { value: 32, for: 'Tb' },
},
}
describe('mapServerToVps', () => {
it('maps macloud server with apiType prefix in notes', () => {
const planIndex = new Map([
[
'1',
{
id: 1,
name: '2 RAM / 1 CPU / 40 NVMe',
cost: 1.55,
period: 'day',
},
],
])
const vps = mapServerToVps(server, 'macloud', 'prov-1', 'acc-1', planIndex)
expect(vps.externalId).toBe('12345')
expect(vps.ip).toBe('91.84.101.78')
expect(vps.os).toBe('Ubuntu 24.04')
expect(vps.vcpu).toBe(1)
expect(vps.ramGb).toBe(2)
expect(vps.diskGb).toBe(40)
expect(vps.bandwidthTb).toBe(32)
expect(vps.country).toBe('NL')
expect(vps.paidUntil).toBe('2029-02-20')
expect(vps.notes).toContain('macloud-12345')
expect(vps.tariffType).toBe('daily')
expect(vps.dailyRate).toBe(1.55)
expect(vps.monthlyRate).toBe(46.5)
expect(vps.currency).toBe('RUB')
})
it('resolves plan by server-group scoped index key', () => {
const planIndex = new Map([
[
'11:1',
{
id: 1,
name: 'Wrong group plan',
cost: 0,
full_cost: 9.99,
period: 'day',
'server-group': 11,
},
],
[
'5:1',
{
id: 1,
name: '2 RAM / 1 CPU / 40 NVMe',
cost: 2.5,
period: 'day',
'server-group': 5,
},
],
])
const vps = mapServerToVps(
{ ...server, 'server-group': { id: 5, name: 'VDS' } },
'vdsina',
'prov-1',
'acc-1',
planIndex,
'USD',
)
expect(vps.dailyRate).toBe(2.5)
})
it('uses full_cost when discounted cost is zero', () => {
const planIndex = new Map([
[
'1',
{
id: 1,
name: '2 RAM / 1 CPU / 40 NVMe',
cost: 0,
full_cost: 1.55,
period: 'day',
},
],
])
const vps = mapServerToVps(server, 'macloud', 'prov-1', 'acc-1', planIndex)
expect(vps.dailyRate).toBe(1.55)
expect(vps.monthlyRate).toBe(46.5)
})
it('parses string plan cost from index', () => {
const planIndex = new Map([
[
'1',
{
id: 1,
name: 'Plan',
cost: '2.50',
period: 'day',
},
],
])
const vps = mapServerToVps(server, 'vdsina', 'prov-1', 'acc-1', planIndex, 'USD')
expect(vps.dailyRate).toBe(2.5)
expect(vps.currency).toBe('USD')
})
it('falls back to tariff item price when plan index misses', () => {
const tariffByPlanId = new Map([
[
'1',
{
externalId: '1',
datacenterKey: '11',
datacenterName: 'Cloud',
name: 'Plan',
desc: '',
vcpu: 1,
ramGb: 1,
diskGb: 10,
diskType: 'NVMe',
virtualization: 'KVM',
channel: '',
location: 'Cloud',
country: '',
cpuModel: '',
orderAvailable: true,
price: '3 USD/day',
},
],
])
const vps = mapServerToVps(server, 'vdsina', 'prov-1', 'acc-1', undefined, 'USD', tariffByPlanId)
expect(vps.dailyRate).toBe(3)
expect(vps.monthlyRate).toBe(90)
expect(vps.currency).toBe('USD')
})
it('maps monthly plan cost from plan index', () => {
const planIndex = new Map([
[
'1',
{
id: 1,
name: '2 RAM / 1 CPU / 40 NVMe',
cost: 500,
period: 'month',
},
],
])
const vps = mapServerToVps(server, 'vdsina', 'prov-1', 'acc-1', planIndex)
expect(vps.tariffType).toBe('monthly')
expect(vps.monthlyRate).toBe(500)
expect(vps.dailyRate).toBeNull()
})
it('adds constructor plan extra cost from server totals', () => {
const planIndex = new Map([
[
'1',
{
id: 1,
name: 'Constructor',
cost: 10,
period: 'day',
has_params: true,
params: {
cpu: { cost: 1 },
ram: { cost: 0.5 },
disk: { cost: 0.1 },
},
data: { cpu: { value: 1 }, ram: { value: 1 }, disk: { value: 1 } },
},
],
])
const vps = mapServerToVps(
{
...server,
data: {
cpu: { value: 1, total: 4 },
ram: { value: 1, total: 8 },
disk: { value: 1, total: 10 },
},
},
'vdsina',
'prov-1',
'acc-1',
planIndex,
)
// 10 + 3*1 + 7*0.5 + 9*0.1 = 10 + 3 + 3.5 + 0.9 = 17.4
expect(vps.dailyRate).toBe(17.4)
})
it('maps vdsina server with vdsina prefix in notes', () => {
const vps = mapServerToVps(server, 'vdsina', 'prov-2', 'acc-2')
expect(vps.notes).toContain('vdsina-12345')
})
it('maps paused status for notpaid', () => {
const vps = mapServerToVps({ ...server, status: 'notpaid' }, 'macloud', 'p', 'a')
expect(vps.status).toBe('paused')
})
})
describe('mapOperationToPayment', () => {
const topup: UserApiOperation = {
id: 4290676,
purse: 'real',
type: 1,
status: 1,
summ: '100',
created: '2025-02-22 12:28:23',
comment: 'Balance replenishment',
}
it('maps paid credit to provider_balance_topup', () => {
const payment = mapOperationToPayment(topup, 'macloud', 'acc-1')
expect(payment).toMatchObject({
externalId: '4290676',
type: 'provider_balance_topup',
amount: 100,
date: '2025-02-22',
note: 'Balance replenishment',
})
})
it('returns null for debit operations', () => {
expect(mapOperationToPayment({ ...topup, type: -1 }, 'vdsina', 'acc-1')).toBeNull()
})
it('returns null for unpaid operations', () => {
expect(mapOperationToPayment({ ...topup, status: 0 }, 'macloud', 'acc-1')).toBeNull()
})
})