/
Nspection
/
rules-manager-mcp
Обзор
Документация
Войти
/
Nspection
/
rules-manager-mcp
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
dev
src/__tests__/utils/data-loader.test.ts
262 строки
9 KB
Buglov Evgeny
Покрытие unit тестами
08 мар 2026, 22:16
08 мар 2026, 22:16
5c01609
Код
Авторство
О чём код?
import { describe, it, expect, beforeEach, jest } from '@jest/globals'; import { loadAllData, saveRule, createBackup, deleteRuleWithBackup } from '../../data-loader.js'; import { mkdir, writeFile, readFile, unlink, readdir } from 'fs/promises'; import { join } from 'path'; // Моки для fs/promises jest.mock('fs/promises', () => ({ mkdir: jest.fn(), writeFile: jest.fn(), readFile: jest.fn(), unlink: jest.fn(), readdir: jest.fn(), rename: jest.fn(), })); const mockMkdir = mkdir as jest.MockedFunction<typeof mkdir>; const mockWriteFile = writeFile as jest.MockedFunction<typeof writeFile>; const mockReadFile = readFile as jest.MockedFunction<typeof readFile>; const mockUnlink = unlink as jest.MockedFunction<typeof unlink>; const mockReaddir = readdir as jest.MockedFunction<typeof readdir>; describe('data-loader', () => { const testRulesDir = '/test/rules'; const testBackupDir = '/test/backups'; beforeEach(() => { jest.clearAllMocks(); mockMkdir.mockResolvedValue(undefined); }); describe('loadAllData', () => { const mockIndexContent = { version: '1.0', last_updated: '2024-01-01T00:00:00.000Z', rules: { test_rule: { uid: 'test-uid', name: 'Test Rule', description: 'Test description', tags: ['test'], critical: false, file: 'test-uid_test_rule.md', }, }, }; const mockRuleContent = '# Test Rule\n\n## Описание\nTest content'; beforeEach(() => { // Сброс всех моков jest.clearAllMocks(); }); it('должен создавать директории при загрузке', async () => { mockReadFile.mockResolvedValue(JSON.stringify(mockIndexContent)); mockReaddir.mockResolvedValue([] as any); await loadAllData(testRulesDir, testBackupDir); expect(mockMkdir).toHaveBeenCalledWith(testRulesDir, { recursive: true }); expect(mockMkdir).toHaveBeenCalledWith(testBackupDir, { recursive: true }); }); it('должен загружать индекс правил', async () => { mockReadFile.mockResolvedValue(JSON.stringify(mockIndexContent)); mockReaddir.mockResolvedValue([] as any); const result = await loadAllData(testRulesDir, testBackupDir); expect(result.rulesIndex).toEqual(mockIndexContent); }); it('должен загружать файлы правил', async () => { const ruleFiles = ['test-uid_test_rule.md']; mockReadFile .mockResolvedValueOnce(JSON.stringify(mockIndexContent)) // Индекс .mockResolvedValueOnce(mockRuleContent); // Файл правила mockReaddir.mockResolvedValue(ruleFiles as any); const result = await loadAllData(testRulesDir, testBackupDir); expect(result.projectRules['test-uid_test_rule.md']).toBe(mockRuleContent); }); it('должен фильтровать файлы, загружая только .md файлы', async () => { const allFiles = [ 'test-uid_test_rule.md', '_index.json', '_TEMPLATE.md', 'other.txt', ]; mockReadFile .mockResolvedValueOnce(JSON.stringify(mockIndexContent)) // Индекс .mockResolvedValueOnce(mockRuleContent); // Правило mockReaddir.mockResolvedValue(allFiles as any); const result = await loadAllData(testRulesDir, testBackupDir); // Должен загрузить только test-uid_test_rule.md (не _TEMPLATE.md) expect(Object.keys(result.projectRules)).toHaveLength(1); }); it('должен исключать _TEMPLATE.md из загрузки', async () => { const ruleFiles = ['_TEMPLATE.md', 'test-uid_test_rule.md']; mockReadFile .mockResolvedValueOnce(JSON.stringify(mockIndexContent)) .mockResolvedValueOnce(mockRuleContent); mockReaddir.mockResolvedValue(ruleFiles as any); const result = await loadAllData(testRulesDir, testBackupDir); expect(result.projectRules['_TEMPLATE.md']).toBeUndefined(); }); it('должен возвращать пустой объект projectRules если нет файлов', async () => { mockReadFile.mockResolvedValue(JSON.stringify(mockIndexContent)); mockReaddir.mockResolvedValue([] as any); const result = await loadAllData(testRulesDir, testBackupDir); expect(result.projectRules).toEqual({}); }); it('должен создавать пустой индекс если файл индекса не найден', async () => { mockReadFile .mockRejectedValueOnce(new Error('File not found')) // Индекс не найден .mockResolvedValue('[]'); // readdir mockReaddir.mockResolvedValue([] as any); const result = await loadAllData(testRulesDir, testBackupDir); expect(result.rulesIndex).toHaveProperty('version', '1.0'); expect(result.rulesIndex.rules).toEqual({}); }); }); describe('saveRule', () => { const testContent = '# Test Rule\n\nСодержимое правила'; it('должен сохранять правило в файл', async () => { const uid = 'abc-123'; const key = 'test_rule'; const expectedFileName = `${uid}_${key}.md`; await saveRule(testRulesDir, uid, key, testContent); expect(mockWriteFile).toHaveBeenCalledWith( join(testRulesDir, expectedFileName), testContent, 'utf-8' ); }); it('должен возвращать имя файла', async () => { const uid = 'abc-123'; const key = 'test_rule'; const expectedFileName = `${uid}_${key}.md`; const result = await saveRule(testRulesDir, uid, key, testContent); expect(result).toBe(expectedFileName); }); it('должен работать с ключами, содержащими подчёркивания', async () => { const uid = 'xyz-789'; const key = 'my_test_rule'; const expectedFileName = `${uid}_${key}.md`; const result = await saveRule(testRulesDir, uid, key, testContent); expect(result).toBe(expectedFileName); }); }); describe('createBackup', () => { const testFileName = 'test-uid_test_rule.md'; const testContent = '# Original Content'; beforeEach(() => { jest.useFakeTimers(); jest.setSystemTime(new Date('2024-06-15T10:30:45.000Z')); }); afterEach(() => { jest.useRealTimers(); }); it('должен создавать бэкап файла', async () => { await createBackup(testBackupDir, testFileName, testContent); expect(mockWriteFile).toHaveBeenCalledWith( expect.stringContaining(join(testBackupDir)), testContent, 'utf-8' ); }); it('должен добавлять timestamp к имени файла бэкапа', async () => { const result = await createBackup(testBackupDir, testFileName, testContent); // Ожидаем формат: test-uid_test_rule.md.2024-06-15T10-30-45-000Z.md expect(result).toMatch(/test-uid_test_rule\.md\.\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z\.md/); }); it('должен возвращать имя файла бэкапа', async () => { const result = await createBackup(testBackupDir, testFileName, testContent); expect(result).toBeDefined(); expect(typeof result).toBe('string'); }); it('должен заменять точки и двоеточия в timestamp', async () => { const result = await createBackup(testBackupDir, testFileName, testContent); // Проверяем, что в имени нет точек кроме расширения и двоеточий const parts = result.split('.'); expect(parts).toHaveLength(4); // filename, md, timestamp, md }); }); describe('deleteRuleWithBackup', () => { const testFileName = 'test-uid_test_rule.md'; const testContent = '# Rule Content'; beforeEach(() => { jest.useFakeTimers(); jest.setSystemTime(new Date('2024-06-15T10:30:45.000Z')); }); afterEach(() => { jest.useRealTimers(); }); it('должен создавать бэкап перед удалением', async () => { await deleteRuleWithBackup(testRulesDir, testBackupDir, testFileName, testContent); expect(mockWriteFile).toHaveBeenCalledWith( expect.stringContaining(join(testBackupDir)), testContent, 'utf-8' ); }); it('должен удалять файл правила', async () => { await deleteRuleWithBackup(testRulesDir, testBackupDir, testFileName, testContent); expect(mockUnlink).toHaveBeenCalledWith( join(testRulesDir, testFileName) ); }); it('должен сначала создавать бэкап, затем удалять файл', async () => { // Порядок вызовов: сначала writeFile (бэкап), потом unlink (удаление) await deleteRuleWithBackup(testRulesDir, testBackupDir, testFileName, testContent); // Проверяем порядок вызовов через индексы const writeFileCallIndex = (mockWriteFile as any).mock.invocationCallOrder[0]; const unlinkCallIndex = (mockUnlink as any).mock.invocationCallOrder[0]; expect(writeFileCallIndex).toBeLessThan(unlinkCallIndex); }); }); });