f2e6783ae8
Клиент: карточка имени отправителя (статусы pending/active/suspended/rejected, диалог заказа с согласием и платой, отключение) + вкладка «Авто-СМС» (тумблер+текст). Админ: экран «СМС: тарифы и имена» — правка ступеней, платы за имя/грейса, очередь подтверждения имён (approve/reject/disable); роут /admin/sms + пункт AdminLayout. API-обёртки в client-sms.ts и admin.ts (cookie-сессия). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
141 lines
5.1 KiB
TypeScript
141 lines
5.1 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||
|
||
vi.mock('../../resources/js/api/client', () => ({
|
||
apiClient: {
|
||
get: vi.fn(),
|
||
post: vi.fn(),
|
||
put: vi.fn(),
|
||
patch: vi.fn(),
|
||
delete: vi.fn(),
|
||
},
|
||
ensureCsrfCookie: vi.fn(),
|
||
}));
|
||
|
||
import {
|
||
fetchSmsTariffs,
|
||
updateSmsTariffs,
|
||
updateSmsSettings,
|
||
fetchSmsSenders,
|
||
approveSmsSender,
|
||
rejectSmsSender,
|
||
disableSmsSender,
|
||
} from '../../resources/js/api/admin';
|
||
import { apiClient, ensureCsrfCookie } from '../../resources/js/api/client';
|
||
|
||
describe('api/admin — СМС: тарифы и имена (Task 16b)', () => {
|
||
beforeEach(() => vi.clearAllMocks());
|
||
|
||
it('fetchSmsTariffs() GETs /api/admin/sms/tariffs', async () => {
|
||
const payload = {
|
||
tariffs: [{ id: 1, min_qty: 1, price_rub: '3.50' }],
|
||
settings: { name_fee_rub_per_operator: '500.00', name_debt_grace_days: 5 },
|
||
};
|
||
vi.mocked(apiClient.get).mockResolvedValue({ data: payload });
|
||
|
||
const r = await fetchSmsTariffs();
|
||
|
||
expect(apiClient.get).toHaveBeenCalledWith('/api/admin/sms/tariffs');
|
||
expect(r).toEqual(payload);
|
||
});
|
||
|
||
it('updateSmsTariffs(rows) ensureCsrfCookie ПЕРЕД PUT /api/admin/sms/tariffs с {rows}', async () => {
|
||
const rows = [
|
||
{ min_qty: 1, price_rub: '3.50' },
|
||
{ min_qty: 1000, price_rub: '3.00' },
|
||
];
|
||
vi.mocked(apiClient.put).mockResolvedValue({
|
||
data: { tariffs: [{ id: 1, min_qty: 1, price_rub: '3.50' }] },
|
||
});
|
||
|
||
const r = await updateSmsTariffs(rows);
|
||
|
||
expect(ensureCsrfCookie).toHaveBeenCalledOnce();
|
||
expect(apiClient.put).toHaveBeenCalledWith('/api/admin/sms/tariffs', { rows });
|
||
const csrfOrder = vi.mocked(ensureCsrfCookie).mock.invocationCallOrder[0];
|
||
const putOrder = vi.mocked(apiClient.put).mock.invocationCallOrder[0];
|
||
expect(csrfOrder).toBeLessThan(putOrder);
|
||
expect(r.tariffs).toHaveLength(1);
|
||
});
|
||
|
||
it('updateSmsSettings(fee, graceDays) PUTs /api/admin/sms/settings с телом', async () => {
|
||
vi.mocked(apiClient.put).mockResolvedValue({
|
||
data: { name_fee_rub_per_operator: '600.00', name_debt_grace_days: 7 },
|
||
});
|
||
|
||
const r = await updateSmsSettings('600.00', 7);
|
||
|
||
expect(ensureCsrfCookie).toHaveBeenCalledOnce();
|
||
expect(apiClient.put).toHaveBeenCalledWith('/api/admin/sms/settings', {
|
||
name_fee_rub_per_operator: '600.00',
|
||
name_debt_grace_days: 7,
|
||
});
|
||
expect(r.name_debt_grace_days).toBe(7);
|
||
});
|
||
|
||
it('fetchSmsSenders(status) GETs /api/admin/sms/senders с params.status', async () => {
|
||
const senders = [
|
||
{
|
||
id: 1,
|
||
tenant_id: 5,
|
||
name: 'ОКНА МСК',
|
||
name_type: 'company',
|
||
status: 'pending',
|
||
monthly_fee_rub: '500.00',
|
||
note: null,
|
||
},
|
||
];
|
||
vi.mocked(apiClient.get).mockResolvedValue({ data: { senders } });
|
||
|
||
const r = await fetchSmsSenders('pending');
|
||
|
||
expect(apiClient.get).toHaveBeenCalledWith('/api/admin/sms/senders', { params: { status: 'pending' } });
|
||
expect(r).toEqual(senders);
|
||
});
|
||
|
||
it('fetchSmsSenders() без статуса — params пустой', async () => {
|
||
vi.mocked(apiClient.get).mockResolvedValue({ data: { senders: [] } });
|
||
|
||
await fetchSmsSenders();
|
||
|
||
expect(apiClient.get).toHaveBeenCalledWith('/api/admin/sms/senders', { params: {} });
|
||
});
|
||
|
||
it('approveSmsSender(id) POSTs /api/admin/sms/senders/{id}/approve', async () => {
|
||
vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1, status: 'active' } });
|
||
|
||
const r = await approveSmsSender(1);
|
||
|
||
expect(ensureCsrfCookie).toHaveBeenCalledOnce();
|
||
expect(apiClient.post).toHaveBeenCalledWith('/api/admin/sms/senders/1/approve');
|
||
expect(r.status).toBe('active');
|
||
});
|
||
|
||
it('rejectSmsSender(id, note) POSTs /api/admin/sms/senders/{id}/reject с note', async () => {
|
||
vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1, status: 'rejected' } });
|
||
|
||
const r = await rejectSmsSender(1, 'Похоже на чужой бренд');
|
||
|
||
expect(apiClient.post).toHaveBeenCalledWith('/api/admin/sms/senders/1/reject', {
|
||
note: 'Похоже на чужой бренд',
|
||
});
|
||
expect(r.status).toBe('rejected');
|
||
});
|
||
|
||
it('rejectSmsSender(id) без note шлёт пустое тело', async () => {
|
||
vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1, status: 'rejected' } });
|
||
|
||
await rejectSmsSender(1);
|
||
|
||
expect(apiClient.post).toHaveBeenCalledWith('/api/admin/sms/senders/1/reject', {});
|
||
});
|
||
|
||
it('disableSmsSender(id) POSTs /api/admin/sms/senders/{id}/disable', async () => {
|
||
vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1, status: 'suspended' } });
|
||
|
||
const r = await disableSmsSender(1);
|
||
|
||
expect(apiClient.post).toHaveBeenCalledWith('/api/admin/sms/senders/1/disable');
|
||
expect(r.status).toBe('suspended');
|
||
});
|
||
});
|