Files
zen-kanji/server/tests/services/controllers.test.js
2025-12-24 06:54:37 +01:00

172 lines
6.5 KiB
JavaScript

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);
});
it('submitLesson should succeed', async () => {
const reply = mockReply();
ReviewService.processLesson.mockResolvedValue({ success: true });
await ReviewController.submitLesson(mockReq({ subjectId: 100 }), reply);
expect(reply.send).toHaveBeenCalledWith({ success: true });
});
it('submitLesson should handle error', async () => {
const reply = mockReply();
ReviewService.processLesson.mockRejectedValue(new Error('Lesson error'));
await ReviewController.submitLesson(mockReq({ subjectId: 100 }), reply);
expect(reply.code).toHaveBeenCalledWith(404);
expect(reply.send).toHaveBeenCalledWith({ error: 'Lesson error' });
});
});
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('getLessonQueue should call service with explicit limit', async () => {
const reply = mockReply();
ReviewService.getLessonQueue.mockResolvedValue([]);
await CollectionController.getLessonQueue(mockReq({}, {}, { limit: '10' }), reply);
expect(ReviewService.getLessonQueue).toHaveBeenCalledWith(expect.anything(), 10);
});
it('getLessonQueue should use default limit when none provided', async () => {
const reply = mockReply();
ReviewService.getLessonQueue.mockResolvedValue([]);
await CollectionController.getLessonQueue(mockReq({}, {}, {}), reply);
expect(ReviewService.getLessonQueue).toHaveBeenCalledWith(expect.anything(), 10);
});
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();
});
});
});