import { describe, it, expect, vi, beforeEach } from 'vitest'; import * as AuthController from '../../src/controllers/auth.controller.js'; import * as ReviewController from '../../src/controllers/review.controller.js'; import * as SyncController from '../../src/controllers/sync.controller.js'; import * as CollectionController from '../../src/controllers/collection.controller.js'; import * as AuthService from '../../src/services/auth.service.js'; import * as ReviewService from '../../src/services/review.service.js'; import * as SyncService from '../../src/services/sync.service.js'; import * as StatsService from '../../src/services/stats.service.js'; import { StudyItem } from '../../src/models/StudyItem.js'; vi.mock('../../src/services/auth.service.js'); vi.mock('../../src/services/review.service.js'); vi.mock('../../src/services/sync.service.js'); vi.mock('../../src/services/stats.service.js'); vi.mock('../../src/models/StudyItem.js'); const mockReq = (body = {}, user = {}, query = {}) => ({ body, user, query }); const mockReply = () => { const res = {}; res.code = vi.fn().mockReturnValue(res); res.send = vi.fn().mockReturnValue(res); res.jwtSign = vi.fn().mockResolvedValue('token'); return res; }; describe('Controllers', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('Auth Controller', () => { it('login should fail without apiKey', async () => { const reply = mockReply(); await AuthController.login(mockReq({}), reply); expect(reply.code).toHaveBeenCalledWith(400); }); it('login should succeed', async () => { const reply = mockReply(); AuthService.loginUser.mockResolvedValue({ _id: 1, tokenVersion: 1 }); await AuthController.login(mockReq({ apiKey: 'key' }), reply); expect(reply.jwtSign).toHaveBeenCalled(); expect(reply.code).not.toHaveBeenCalledWith(401); }); it('login should catch errors', async () => { const reply = mockReply(); AuthService.loginUser.mockRejectedValue(new Error('fail')); await AuthController.login(mockReq({ apiKey: 'k' }), reply); expect(reply.code).toHaveBeenCalledWith(401); }); it('logout should succeed', async () => { const reply = mockReply(); await AuthController.logout(mockReq({}, { _id: 1 }), reply); expect(AuthService.logoutUser).toHaveBeenCalled(); }); }); describe('Review Controller', () => { it('submitReview should succeed', async () => { const reply = mockReply(); ReviewService.processReview.mockResolvedValue({}); await ReviewController.submitReview(mockReq({}), reply); expect(reply.send).toHaveBeenCalled(); }); it('submitReview should handle error', async () => { const reply = mockReply(); ReviewService.processReview.mockRejectedValue(new Error('err')); await ReviewController.submitReview(mockReq({}), reply); expect(reply.code).toHaveBeenCalledWith(404); }); }); describe('Sync Controller', () => { it('sync should succeed', async () => { const reply = mockReply(); SyncService.syncWithWaniKani.mockResolvedValue({}); await SyncController.sync(mockReq({}, {}), reply); expect(reply.send).toHaveBeenCalled(); }); it('sync should handle error', async () => { const reply = mockReply(); SyncService.syncWithWaniKani.mockRejectedValue(new Error('err')); await SyncController.sync(mockReq({}, {}), reply); expect(reply.code).toHaveBeenCalledWith(500); }); }); describe('Collection Controller', () => { it('getCollection should return items', async () => { const reply = mockReply(); StudyItem.find.mockResolvedValue([]); await CollectionController.getCollection(mockReq({}, { _id: 1 }), reply); expect(reply.send).toHaveBeenCalledWith([]); }); it('getQueue should call service with default limit', async () => { const reply = mockReply(); ReviewService.getQueue.mockResolvedValue([]); await CollectionController.getQueue(mockReq({}, {}, {}), reply); expect(ReviewService.getQueue).toHaveBeenCalledWith(expect.anything(), 20, undefined); }); it('getQueue should call service with provided limit', async () => { const reply = mockReply(); ReviewService.getQueue.mockResolvedValue([]); await CollectionController.getQueue(mockReq({}, {}, { limit: '50' }), reply); expect(ReviewService.getQueue).toHaveBeenCalledWith(expect.anything(), 50, undefined); }); it('getStats should call service', async () => { const reply = mockReply(); StatsService.getUserStats.mockResolvedValue({}); await CollectionController.getStats(mockReq({}, {}), reply); expect(StatsService.getUserStats).toHaveBeenCalled(); }); it('updateSettings should initialize settings if missing', async () => { const reply = mockReply(); const save = vi.fn(); const user = { save }; await CollectionController.updateSettings(mockReq({ batchSize: 50 }, user), reply); expect(user.settings).toBeDefined(); expect(user.settings.batchSize).toBe(50); expect(save).toHaveBeenCalled(); }); it('updateSettings should update drawingAccuracy', async () => { const reply = mockReply(); const save = vi.fn(); const user = { settings: {}, save }; await CollectionController.updateSettings(mockReq({ drawingAccuracy: 5 }, user), reply); expect(user.settings.drawingAccuracy).toBe(5); expect(save).toHaveBeenCalled(); }); }); });