diff --git a/__tests__/db/couchdb-note-repository.error.test.js b/__tests__/db/couchdb-note-repository.error.test.js index 01355a9..5b489a3 100644 --- a/__tests__/db/couchdb-note-repository.error.test.js +++ b/__tests__/db/couchdb-note-repository.error.test.js @@ -516,4 +516,81 @@ describe('CouchDbNoteRepository Error Scenarios', () => { expect(result).toEqual([]); }); }); + + describe('Additional Coverage', () => { + test('init should log when updating existing design document with missing views', async () => { + const existingDoc = { + _id: '_design/notes', + _rev: '1-abc', + views: { + all: { map: 'function(doc) { emit(doc._id, null); }' } + } + }; + + const mockDb = { + get: jest.fn().mockResolvedValue(existingDoc), + insert: jest.fn().mockResolvedValue({ ok: true }) + }; + const mockClient = { + db: { + list: jest.fn().mockResolvedValue(['test_notes_error_db']) + }, + use: jest.fn().mockReturnValue(mockDb) + }; + + repository.client = mockClient; + + const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + await repository.init(); + + expect(consoleSpy).toHaveBeenCalledWith('Updated design document with all views'); + consoleSpy.mockRestore(); + }); + + test('moveToRecycleBin should return false when insert throws 404', async () => { + const mockDb = { + get: jest.fn().mockResolvedValue({ _id: 'note1', _rev: '1-rev' }), + insert: jest.fn().mockRejectedValue({ statusCode: 404 }) + }; + repository.db = mockDb; + + const result = await repository.moveToRecycleBin('note1'); + expect(result).toBe(false); + }); + + test('restore should return false when insert throws 404', async () => { + const mockDb = { + get: jest.fn().mockResolvedValue({ _id: 'note1', _rev: '1-rev' }), + insert: jest.fn().mockRejectedValue({ statusCode: 404 }) + }; + repository.db = mockDb; + + const result = await repository.restore('note1'); + expect(result).toBe(false); + }); + + test('permanentDelete should return false when destroy throws 404', async () => { + const mockDb = { + get: jest.fn().mockResolvedValue({ _id: 'note1', _rev: '1-rev' }), + destroy: jest.fn().mockRejectedValue({ statusCode: 404 }) + }; + repository.db = mockDb; + + const result = await repository.permanentDelete('note1'); + expect(result).toBe(false); + }); + + test('restoreAll should handle errors', async () => { + repository.findDeleted = jest.fn().mockRejectedValue(new Error('findDeleted failed')); + await expect(repository.restoreAll()).rejects.toThrow('findDeleted failed'); + }); + + test('countDeleted should handle errors', async () => { + const mockDb = { + view: jest.fn().mockRejectedValue(new Error('view failed')) + }; + repository.db = mockDb; + await expect(repository.countDeleted()).rejects.toThrow('view failed'); + }); + }); }); diff --git a/__tests__/db/extra-coverage.test.js b/__tests__/db/extra-coverage.test.js new file mode 100644 index 0000000..39ec09b --- /dev/null +++ b/__tests__/db/extra-coverage.test.js @@ -0,0 +1,46 @@ +import { jest } from '@jest/globals'; +import { NoteRepository } from '../../src/db/note-repository.js'; +import * as apiServer from '../../src/notes-api-server.js'; + +describe('Extra Coverage Tests', () => { + describe('NoteRepository Base Class', () => { + const repo = new NoteRepository(); + const methods = [ + 'findAll', 'findDeleted', 'findAllIncludingDeleted', 'findById', + 'create', 'update', 'moveToRecycleBin', 'restore', 'permanentDelete', + 'emptyRecycleBin', 'restoreAll', 'countDeleted' + ]; + + test.each(methods)('%s should throw Method not implemented', async (method) => { + await expect(repo[method]()).rejects.toThrow('Method not implemented'); + }); + }); + + describe('API Server Backward Compatibility Exports', () => { + test('should export app', () => { + expect(apiServer.app).toBeDefined(); + }); + + test('should export createNoteRepository', () => { + expect(typeof apiServer.createNoteRepository).toBe('function'); + }); + + test('should export gracefulShutdown', () => { + expect(typeof apiServer.gracefulShutdown).toBe('function'); + }); + + test('should export initializeApp', async () => { + expect(typeof apiServer.initializeApp).toBe('function'); + // Mock repository to avoid real DB connection + const mockRepo = { + init: jest.fn().mockResolvedValue() + }; + const result = await apiServer.initializeApp(mockRepo); + expect(result.repository).toBe(mockRepo); + }); + + test('should export startServer', () => { + expect(typeof apiServer.startServer).toBe('function'); + }); + }); +}); diff --git a/__tests__/db/mongodb-note-repository.error.test.js b/__tests__/db/mongodb-note-repository.error.test.js index 94323f2..9f3f484 100644 --- a/__tests__/db/mongodb-note-repository.error.test.js +++ b/__tests__/db/mongodb-note-repository.error.test.js @@ -417,5 +417,36 @@ describe('MongoDbNoteRepository Error Handling Tests', () => { await expect(repository.countDeleted()).rejects.toThrow('No connection to database'); expect(console.error).toHaveBeenCalledWith('Failed to count deleted notes:', expect.any(Error)); }); + + test('should handle restoreAll database errors', async () => { + repository.NoteModel.updateMany = jest.fn().mockRejectedValue(new Error('Update failed')); + await expect(repository.restoreAll()).rejects.toThrow('Update failed'); + expect(console.error).toHaveBeenCalledWith('Failed to restore all notes from recycle bin:', expect.any(Error)); + }); + + test('should handle emptyRecycleBin database errors', async () => { + repository.NoteModel.deleteMany = jest.fn().mockRejectedValue(new Error('Delete failed')); + await expect(repository.emptyRecycleBin()).rejects.toThrow('Delete failed'); + expect(console.error).toHaveBeenCalledWith('Failed to empty recycle bin:', expect.any(Error)); + }); + + test('init should not re-create model if it exists', async () => { + mongoose.connect = jest.fn().mockResolvedValue(); + repository.NoteModel = { some: 'model' }; + await repository.init(); + expect(repository.NoteModel).toEqual({ some: 'model' }); + }); + + test('restoreAll should return 0 if no notes modified', async () => { + repository.NoteModel.updateMany = jest.fn().mockResolvedValue({ modifiedCount: 0 }); + const result = await repository.restoreAll(); + expect(result).toBe(0); + }); + + test('emptyRecycleBin should return 0 if no notes deleted', async () => { + repository.NoteModel.deleteMany = jest.fn().mockResolvedValue({ deletedCount: 0 }); + const result = await repository.emptyRecycleBin(); + expect(result).toBe(0); + }); }); -}); \ No newline at end of file +}); \ No newline at end of file