Files
portal/app/tests/Frontend/adminTenantDetailMapper.spec.ts
T

275 lines
10 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { mapAdminTenantDetail } from '../../resources/js/composables/adminTenantDetailMapper';
import type { AdminTenantDetailResponse } from '../../resources/js/api/admin';
function makeApi(overrides: Partial<AdminTenantDetailResponse> = {}): AdminTenantDetailResponse {
return {
tenant: {
id: 1,
subdomain: 'test-co',
organization_name: 'Test ООО',
contact_email: 'admin@test-co.ru',
status: 'active',
balance_rub: '10000.00',
balance_leads: 5,
is_trial: false,
last_activity_at: null,
tariff_id: 1,
tariff_name: 'Команда',
mrr_rub: '990.00',
desired_daily_numbers: 10,
chargeback_unrecovered_rub: '0.00',
created_at: '2025-01-01T00:00:00+00:00',
},
users: [],
projects: [],
balance_history: [],
activity: [],
metrics: {
leads_today: 5,
leads_this_week: 30,
leads_this_month: 120,
avg_lead_cost_rub: 250,
runway_days: 40,
},
...overrides,
};
}
describe('adminTenantDetailMapper', () => {
it('маппит code = subdomain', () => {
const ui = mapAdminTenantDetail(makeApi());
expect(ui.code).toBe('test-co');
});
it('маппит name = organization_name', () => {
expect(mapAdminTenantDetail(makeApi()).name).toBe('Test ООО');
});
it('inn/contact_phone/legal_address пустые (нет в schema)', () => {
const ui = mapAdminTenantDetail(makeApi());
expect(ui.inn).toBe('');
expect(ui.contact_phone).toBe('');
expect(ui.legal_address).toBe('');
});
it('balanceRub парсится из string в number', () => {
expect(mapAdminTenantDetail(makeApi()).balanceRub).toBe(10000);
});
it('mrrRub: парсится в число для не-trial; null для trial', () => {
expect(mapAdminTenantDetail(makeApi()).mrrRub).toBe(990);
const trial = mapAdminTenantDetail(makeApi({ tenant: { ...makeApi().tenant, is_trial: true, mrr_rub: null } }));
expect(trial.mrrRub).toBeNull();
});
it('status derive: trial при is_trial=true', () => {
const ui = mapAdminTenantDetail(makeApi({ tenant: { ...makeApi().tenant, is_trial: true, mrr_rub: null } }));
expect(ui.status).toBe('trial');
expect(ui.statusText).toBe('Trial');
});
it('status derive: overdue при balance<0 или chargeback>0', () => {
const a = mapAdminTenantDetail(makeApi({ tenant: { ...makeApi().tenant, balance_rub: '-100.00' } }));
expect(a.status).toBe('overdue');
const b = mapAdminTenantDetail(
makeApi({ tenant: { ...makeApi().tenant, chargeback_unrecovered_rub: '500.00' } }),
);
expect(b.status).toBe('overdue');
});
it('status derive: suspended → suspended', () => {
const ui = mapAdminTenantDetail(makeApi({ tenant: { ...makeApi().tenant, status: 'suspended' } }));
expect(ui.status).toBe('suspended');
});
it('tariff: из tariff_name (или Trial если is_trial)', () => {
expect(mapAdminTenantDetail(makeApi()).tariff).toBe('Команда');
const trial = mapAdminTenantDetail(makeApi({ tenant: { ...makeApi().tenant, is_trial: true, mrr_rub: null } }));
expect(trial.tariff).toBe('Trial');
});
it('tariff fallback Trial если name не из known list', () => {
const ui = mapAdminTenantDetail(makeApi({ tenant: { ...makeApi().tenant, tariff_name: 'NotInList' } }));
expect(ui.tariff).toBe('Trial');
});
it('todayActual = metrics.leads_today; todayDesired = desired_daily_numbers', () => {
const ui = mapAdminTenantDetail(makeApi());
expect(ui.todayActual).toBe(5);
expect(ui.todayDesired).toBe(10);
});
it('users mapping: fullName из first+last; fallback на email', () => {
const ui = mapAdminTenantDetail(
makeApi({
users: [
{
id: 1,
email: 'a@b.ru',
first_name: 'Иван',
last_name: 'Петров',
is_active: true,
totp_enabled: false,
last_active_at: null,
last_login_at: null,
},
{
id: 2,
email: 'c@d.ru',
first_name: null,
last_name: null,
is_active: false,
totp_enabled: false,
last_active_at: null,
last_login_at: null,
},
],
}),
);
expect(ui.users[0]!.fullName).toBe('Иван Петров');
expect(ui.users[1]!.fullName).toBe('c@d.ru');
expect(ui.users[0]!.role).toBe('manager');
});
it('projects mapping: slug=tag, suppliers=suppliers_count, leadsToday/desiredToday', () => {
const ui = mapAdminTenantDetail(
makeApi({
projects: [
{
id: 1,
name: 'P1',
tag: 'p1-slug',
is_active: true,
daily_limit_target: 8,
suppliers_count: 3,
leads_today: 5,
},
],
}),
);
expect(ui.projects[0]!.slug).toBe('p1-slug');
expect(ui.projects[0]!.suppliers).toBe(3);
expect(ui.projects[0]!.leadsToday).toBe(5);
expect(ui.projects[0]!.desiredToday).toBe(8);
});
it('balanceHistory: id префиксуется TX-, amount парсится в number', () => {
const ui = mapAdminTenantDetail(
makeApi({
balance_history: [
{
id: 347,
type: 'topup',
amount_rub: '5000.00',
amount_leads: 0,
balance_rub_after: '15000.00',
description: 'X',
created_at: '2026-01-01T00:00:00Z',
},
],
}),
);
expect(ui.balanceHistory[0]!.id).toBe('TX-347');
expect(ui.balanceHistory[0]!.amount).toBe(5000);
expect(ui.balanceHistory[0]!.type).toBe('topup');
});
it('balanceHistory: chargeback_writedown → manual_adjustment, chargeback_repayment → topup', () => {
const ui = mapAdminTenantDetail(
makeApi({
balance_history: [
{
id: 1,
type: 'chargeback_writedown',
amount_rub: '-500',
amount_leads: 0,
balance_rub_after: null,
description: '',
created_at: '2026-01-01T00:00:00Z',
},
{
id: 2,
type: 'chargeback_repayment',
amount_rub: '500',
amount_leads: 0,
balance_rub_after: null,
description: '',
created_at: '2026-01-01T00:00:00Z',
},
],
}),
);
expect(ui.balanceHistory[0]!.type).toBe('manual_adjustment');
expect(ui.balanceHistory[1]!.type).toBe('topup');
});
it('activity: actor=actor_email; null → system', () => {
const ui = mapAdminTenantDetail(
makeApi({
activity: [
{
id: 1,
event: 'webhook.received',
deal_id: 100,
actor_email: null,
context: null,
created_at: '2026-01-01T00:00:00Z',
},
{
id: 2,
event: 'deal.status_changed',
deal_id: 200,
actor_email: 'user@test.io',
context: { from: 'new', to: 'in_progress' },
created_at: '2026-01-01T00:00:00Z',
},
],
}),
);
expect(ui.activity[0]!.actor).toBe('system');
expect(ui.activity[1]!.actor).toBe('user@test.io');
expect(ui.activity[1]!.summary).toBe('Сделка #200: new → in_progress');
});
it('metrics: leadsThisMonth/Week/avgLeadCost/runwayDays', () => {
const ui = mapAdminTenantDetail(makeApi());
expect(ui.leadsThisMonth).toBe(120);
expect(ui.leadsThisWeek).toBe(30);
expect(ui.avgLeadCost).toBe(250);
expect(ui.runwayDays).toBe(40);
});
it('avgLeadCost / runwayDays default 0 если null', () => {
const ui = mapAdminTenantDetail(
makeApi({
metrics: {
leads_today: 0,
leads_this_week: 0,
leads_this_month: 0,
avg_lead_cost_rub: null,
runway_days: null,
},
}),
);
expect(ui.avgLeadCost).toBe(0);
expect(ui.runwayDays).toBe(0);
});
it('activitySince: «N мин назад» / «не активен» если null', () => {
const now = new Date('2026-05-09T12:00:00Z');
const ui1 = mapAdminTenantDetail(
makeApi({
tenant: {
...makeApi().tenant,
last_activity_at: '2026-05-09T11:30:00Z',
},
}),
now,
);
expect(ui1.activitySince).toBe('30 мин назад');
const ui2 = mapAdminTenantDetail(makeApi(), now);
expect(ui2.activitySince).toBe('не активен');
});
});