Files
MyTube/backend/src/__tests__/controllers/settingsController.test.ts
2025-11-25 18:48:44 -05:00

143 lines
4.8 KiB
TypeScript

import bcrypt from 'bcryptjs';
import { Request, Response } from 'express';
import fs from 'fs-extra';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { deleteLegacyData, getSettings, migrateData, updateSettings, verifyPassword } from '../../controllers/settingsController';
import downloadManager from '../../services/downloadManager';
import * as storageService from '../../services/storageService';
vi.mock('../../services/storageService');
vi.mock('../../services/downloadManager');
vi.mock('bcryptjs');
vi.mock('fs-extra');
vi.mock('../../services/migrationService', () => ({
runMigration: vi.fn(),
}));
describe('SettingsController', () => {
let req: Partial<Request>;
let res: Partial<Response>;
let json: any;
let status: any;
beforeEach(() => {
vi.clearAllMocks();
json = vi.fn();
status = vi.fn().mockReturnValue({ json });
req = {};
res = {
json,
status,
};
});
describe('getSettings', () => {
it('should return settings', async () => {
(storageService.getSettings as any).mockReturnValue({ theme: 'dark' });
await getSettings(req as Request, res as Response);
expect(json).toHaveBeenCalledWith(expect.objectContaining({ theme: 'dark' }));
});
it('should save defaults if empty', async () => {
(storageService.getSettings as any).mockReturnValue({});
await getSettings(req as Request, res as Response);
expect(storageService.saveSettings).toHaveBeenCalled();
expect(json).toHaveBeenCalled();
});
});
describe('updateSettings', () => {
it('should update settings', async () => {
req.body = { theme: 'light', maxConcurrentDownloads: 5 };
(storageService.getSettings as any).mockReturnValue({});
await updateSettings(req as Request, res as Response);
expect(storageService.saveSettings).toHaveBeenCalled();
expect(downloadManager.setMaxConcurrentDownloads).toHaveBeenCalledWith(5);
expect(json).toHaveBeenCalledWith(expect.objectContaining({ success: true }));
});
it('should hash password if provided', async () => {
req.body = { password: 'pass' };
(storageService.getSettings as any).mockReturnValue({});
(bcrypt.genSalt as any).mockResolvedValue('salt');
(bcrypt.hash as any).mockResolvedValue('hashed');
await updateSettings(req as Request, res as Response);
expect(bcrypt.hash).toHaveBeenCalledWith('pass', 'salt');
});
});
describe('verifyPassword', () => {
it('should verify correct password', async () => {
req.body = { password: 'pass' };
(storageService.getSettings as any).mockReturnValue({ loginEnabled: true, password: 'hashed' });
(bcrypt.compare as any).mockResolvedValue(true);
await verifyPassword(req as Request, res as Response);
expect(json).toHaveBeenCalledWith({ success: true });
});
it('should reject incorrect password', async () => {
req.body = { password: 'wrong' };
(storageService.getSettings as any).mockReturnValue({ loginEnabled: true, password: 'hashed' });
(bcrypt.compare as any).mockResolvedValue(false);
await verifyPassword(req as Request, res as Response);
expect(status).toHaveBeenCalledWith(401);
});
});
describe('migrateData', () => {
it('should run migration', async () => {
const migrationService = await import('../../services/migrationService');
(migrationService.runMigration as any).mockResolvedValue({ success: true });
await migrateData(req as Request, res as Response);
expect(json).toHaveBeenCalledWith(expect.objectContaining({ success: true }));
});
it('should handle errors', async () => {
const migrationService = await import('../../services/migrationService');
(migrationService.runMigration as any).mockRejectedValue(new Error('Migration failed'));
await migrateData(req as Request, res as Response);
expect(status).toHaveBeenCalledWith(500);
});
});
describe('deleteLegacyData', () => {
it('should delete legacy files', async () => {
(fs.existsSync as any).mockReturnValue(true);
(fs.unlinkSync as any).mockImplementation(() => {});
await deleteLegacyData(req as Request, res as Response);
expect(fs.unlinkSync).toHaveBeenCalledTimes(4);
expect(json).toHaveBeenCalledWith(expect.objectContaining({ success: true }));
});
it('should handle errors during deletion', async () => {
(fs.existsSync as any).mockReturnValue(true);
(fs.unlinkSync as any).mockImplementation(() => {
throw new Error('Delete failed');
});
await deleteLegacyData(req as Request, res as Response);
expect(json).toHaveBeenCalledWith(expect.objectContaining({ success: true }));
// It returns success but with failed list
});
});
});