import { describe, it, expect, vi, beforeEach } from 'vitest'; vi.mock('./api.js', () => ({ api: { get: vi.fn(), post: vi.fn(), }, ApiError: class extends Error { constructor(s, b) { super(); this.status = s; this.body = b; } }, })); import { contentApi } from './content-api.js'; import { api } from './api.js'; describe('contentApi', () => { beforeEach(() => { vi.restoreAllMocks(); }); it('listArticles calls GET /api/articles', async () => { api.get.mockResolvedValue({ items: [{ slug: 'test' }], total: 1 }); const res = await contentApi.listArticles({ limit: 10 }); expect(api.get).toHaveBeenCalledWith('/api/articles?limit=10'); expect(res.items).toEqual([{ slug: 'test' }]); }); it('getArticle calls GET /api/articles/:slug', async () => { api.get.mockResolvedValue({ article: { slug: 'test', title: 'Test' } }); const res = await contentApi.getArticle('test-unique-slug-1'); expect(api.get).toHaveBeenCalledWith('/api/articles/test-unique-slug-1'); expect(res.article.slug).toBe('test'); }); it('getArticle caches results', async () => { api.get.mockClear(); api.get.mockResolvedValue({ article: { slug: 'cache-test-slug-999' } }); await contentApi.getArticle('cache-test-slug-999'); const callCount = api.get.mock.calls.length; await contentApi.getArticle('cache-test-slug-999'); expect(api.get.mock.calls.length).toBe(callCount); }); it('search calls GET /api/search with query', async () => { api.get.mockResolvedValue({ items: [] }); await contentApi.search({ q: 'هوش مصنوعی' }); expect(api.get).toHaveBeenCalledWith('/api/search?q=%D9%87%D9%88%D8%B4+%D9%85%D8%B5%D9%86%D9%88%D8%B9%DB%8C'); }); it('listCategories calls GET /api/categories', async () => { api.get.mockResolvedValue({ items: [{ slug: 'ai', name: 'هوش مصنوعی' }] }); const res = await contentApi.listCategories(); expect(res[0].slug).toBe('ai'); }); it('toggleReaction calls POST with article ID and type', async () => { api.post.mockResolvedValue({ ok: true }); await contentApi.toggleReaction('art123', 'insightful'); expect(api.post).toHaveBeenCalledWith('/api/articles/art123/reactions', { type: 'insightful' }); }); it('subscribeNewsletter sends email and captcha data', async () => { api.post.mockResolvedValue({ ok: true }); await contentApi.subscribeNewsletter('test@example.com', 'cap-id', 'ABC'); expect(api.post).toHaveBeenCalledWith('/api/newsletter', { email: 'test@example.com', captchaId: 'cap-id', userSolution: 'ABC', }); }); it('fallbackArticles returns array from TeknavData', () => { const result = contentApi.fallbackArticles(); expect(Array.isArray(result)).toBe(true); }); it('fallbackAuthors returns array from TeknavData', () => { const result = contentApi.fallbackAuthors(); expect(Array.isArray(result)).toBe(true); }); });