import { describe, it, expect, beforeEach } from 'vitest'; import { adaptSlots, adaptServiceSlots, hasAvailable } from '@/lib/appointmentSlots'; import { getAccessToken, setAccessToken, clearAccessToken } from '@/lib/tokenStore'; import { sanitizeHtml, safeJsonParse } from '@/lib/sanitize'; import { defineAbilitiesFor } from '@/lib/ability'; import { setRefreshCookie, clearRefreshCookie, COOKIE_NAME } from '@/lib/refreshCookie'; import { buildPatientUser } from '@/lib/representationAdapters'; describe('appointmentSlots', () => { // مرزِ شیفت را بک‌اند می‌دهد؛ فرانت تقسیم صبح/عصر نمی‌سازد. سه تست قبلی روی // قرارداد قدیمیِ `{ morning, evening }` نوشته شده بودند و از زمانی که خروجی به // آرایهٔ session تغییر کرد قرمز مانده بودند. const resp = { sessions: [ { start_time: '09:00', end_time: '12:00', slots: [ { start_time: '09:00', is_available: true }, { start_time: '11:30', is_available: false }, ], }, { start_time: '16:00', end_time: '20:00', slots: [{ start_time: '16:00', is_available: true }], }, ], }; it('adaptSlots هر شیفتِ بک‌اند را یک session با برچسب بازه می‌کند', () => { const sessions = adaptSlots(resp); expect(sessions).toHaveLength(2); expect(sessions[0].label).toBe('09:00 - 12:00'); expect(sessions[1].label).toBe('16:00 - 20:00'); expect(sessions[0].slots).toHaveLength(2); }); it('adaptSlots ساختار data.sessions را هم می‌پذیرد', () => { expect(adaptSlots({ data: resp })).toHaveLength(2); }); it('adaptSlots شیفتِ بدون اسلات را حذف می‌کند', () => { const withEmpty = { sessions: [...resp.sessions, { start_time: '21:00', end_time: '22:00', slots: [] }] }; expect(adaptSlots(withEmpty)).toHaveLength(2); }); it('adaptSlots ورودی خالی → آرایهٔ خالی', () => { expect(adaptSlots(null)).toEqual([]); }); it('hasAvailable', () => { expect(hasAvailable([{ is_available: false }, { is_available: true }])).toBe(true); expect(hasAvailable([{ is_available: false }])).toBe(false); expect(hasAvailable([])).toBe(false); expect(hasAvailable(null)).toBe(false); }); }); describe('adaptServiceSlots', () => { // پاسخ واقعی: شیفت ۰۹:۰۰–۱۲:۰۰، دو سرویس ۲۰+۱۵، بافر ۱۰ ⇒ گام ۴۵ دقیقه. const serviceResp = { data: { total_duration_minutes: 35, buffer_minutes: 10, start_times: [ { start: 1785562200, end: 1785564300, start_time: '09:00', end_time: '09:35' }, { start: 1785564900, end: 1785567000, start_time: '09:45', end_time: '10:20' }, { start: 1785567600, end: 1785569700, start_time: '10:30', end_time: '11:05' }, { start: 1785570300, end: 1785572400, start_time: '11:15', end_time: '11:50' }, ], }, }; it('یک session با بازهٔ واقعی می‌سازد', () => { const [session] = adaptServiceSlots(serviceResp); expect(session.start_time).toBe('09:00'); // پایانِ آخرین نوبت، نه زمانِ شروعش. expect(session.end_time).toBe('11:50'); expect(session.label).toBe('09:00 - 11:50'); expect(session.slots).toHaveLength(4); }); it('همهٔ زمان‌های پیشنهادی available علامت می‌خورند', () => { const [session] = adaptServiceSlots(serviceResp); expect(session.slots.every((s) => s.is_available)).toBe(true); expect(hasAvailable(session.slots)).toBe(true); }); it('ساختار بدون data را هم می‌پذیرد', () => { expect(adaptServiceSlots(serviceResp.data)).toHaveLength(1); }); it('نبودِ end_time در پاسخ → از total_duration_minutes ساخته می‌شود', () => { const withoutEnd = { data: { total_duration_minutes: 35, start_times: [{ start_time: '09:00' }], }, }; expect(adaptServiceSlots(withoutEnd)[0].end_time).toBe('09:35'); }); it('start_times خالی → آرایهٔ خالی', () => { expect(adaptServiceSlots({ data: { start_times: [] } })).toEqual([]); expect(adaptServiceSlots(null)).toEqual([]); }); it('یک اسلات تنها → بازهٔ همان اسلات', () => { const single = { data: { start_times: [{ start_time: '15:00', end_time: '15:30' }] } }; expect(adaptServiceSlots(single)[0].label).toBe('15:00 - 15:30'); }); }); describe('tokenStore', () => { beforeEach(() => clearAccessToken()); it('set/get/clear', () => { expect(getAccessToken()).toBeNull(); setAccessToken('tok'); expect(getAccessToken()).toBe('tok'); clearAccessToken(); expect(getAccessToken()).toBeNull(); }); it('set با مقدار falsy → null', () => { setAccessToken(''); expect(getAccessToken()).toBeNull(); }); }); describe('sanitize', () => { it('غیررشته → رشته خالی', () => { expect(sanitizeHtml(null)).toBe(''); expect(sanitizeHtml(123)).toBe(''); }); it('تگ مجاز حفظ، اسکریپت حذف', () => { const out = sanitizeHtml('

سلام

'); expect(out).toContain('

سلام

'); expect(out).not.toContain('