// Mock CouchDB service for testing const mockCouchdbService = { findUserById: jest.fn(), getById: jest.fn(), createDocument: jest.fn(), updateDocument: jest.fn(), findByType: jest.fn(), find: jest.fn(), deleteDocument: jest.fn(), updatePost: jest.fn(), initialize: jest.fn(), isReady: jest.fn().mockReturnValue(true), shutdown: jest.fn() }; // Mock the service module jest.mock('../../services/couchdbService', () => mockCouchdbService); const Comment = require('../../models/Comment'); describe('Comment Model', () => { beforeEach(() => { jest.clearAllMocks(); // Reset all mocks to ensure clean state mockCouchdbService.findUserById.mockReset(); mockCouchdbService.getById.mockReset(); mockCouchdbService.createDocument.mockReset(); mockCouchdbService.updateDocument.mockReset(); mockCouchdbService.findByType.mockReset(); mockCouchdbService.find.mockReset(); mockCouchdbService.deleteDocument.mockReset(); mockCouchdbService.updatePost.mockReset(); }); describe('Schema Validation', () => { it('should create a valid comment', async () => { const commentData = { user: 'user_123', post: 'post_123', content: 'This is a great post!', }; const mockUser = { _id: 'user_123', name: 'Test User', profilePicture: '' }; const mockPost = { _id: 'post_123', content: 'Test post content', user: { userId: 'user_123' }, commentsCount: 0 }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User', profilePicture: '' }, post: { postId: 'post_123', content: 'Test post content', userId: 'user_123' }, content: 'This is a great post!', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment._id).toBeDefined(); expect(comment.user.userId).toBe('user_123'); expect(comment.post.postId).toBe('post_123'); expect(comment.content).toBe('This is a great post!'); }); it('should require post field', async () => { const commentData = { user: { userId: 'user_123' }, content: 'Comment without post', }; expect(() => new Comment(commentData)).toThrow(); }); it('should require user field', async () => { const commentData = { post: { postId: 'post_123' }, content: 'Comment without user', }; expect(() => new Comment(commentData)).toThrow(); }); it('should require content field', async () => { const commentData = { post: { postId: 'post_123' }, user: { userId: 'user_123' }, }; expect(() => new Comment(commentData)).toThrow(); }); }); describe('Content Validation', () => { it('should trim content', async () => { const commentData = { post: 'post_123', user: 'user_123', content: ' This comment has spaces ', }; const mockUser = { _id: 'user_123', name: 'Test User' }; const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'This comment has spaces', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment.content).toBe('This comment has spaces'); }); it('should allow long comments', async () => { const longContent = 'a'.repeat(400); // Within 500 character limit const commentData = { post: 'post_123', user: 'user_123', content: longContent, }; const mockUser = { _id: 'user_123', name: 'Test User' }; const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: longContent, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment.content).toBe(longContent); }); it('should reject empty content after trimming', async () => { const commentData = { post: { postId: 'post_123' }, user: { userId: 'user_123' }, content: ' ', // Only spaces }; expect(() => new Comment(commentData)).toThrow(); }); }); describe('Basic Properties', () => { it('should create comment with basic properties', async () => { const commentData = { post: 'post_123', user: 'user_123', content: 'Basic comment', }; const mockUser = { _id: 'user_123', name: 'Test User' }; const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'Basic comment', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment._id).toBeDefined(); expect(comment.type).toBe('comment'); expect(comment.content).toBe('Basic comment'); }); }); describe('Relationships', () => { it('should reference post ID', async () => { const commentData = { post: 'post_123', user: 'user_123', content: 'Comment on specific post', }; const mockUser = { _id: 'user_123', name: 'Test User' }; const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'Comment on specific post', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment.post.postId).toBe('post_123'); }); it('should reference user ID', async () => { const commentData = { post: 'post_123', user: 'user_123', content: 'Comment by specific user', }; const mockUser = { _id: 'user_123', name: 'Test User' }; const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'Comment by specific user', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment.user.userId).toBe('user_123'); }); }); describe('Timestamps', () => { it('should automatically set createdAt and updatedAt', async () => { const commentData = { post: 'post_123', user: 'user_123', content: 'Timestamp test comment', }; const mockUser = { _id: 'user_123', name: 'Test User' }; const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'Timestamp test comment', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment.createdAt).toBeDefined(); expect(comment.updatedAt).toBeDefined(); expect(typeof comment.createdAt).toBe('string'); expect(typeof comment.updatedAt).toBe('string'); }); it('should update updatedAt on modification', async () => { const mockComment = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'Update test comment', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.getById.mockResolvedValue(mockComment); mockCouchdbService.updateDocument.mockResolvedValue({ ...mockComment, content: 'Updated comment content', _rev: '2-def', updatedAt: '2023-01-01T00:00:01.000Z' }); const comment = await Comment.findById('comment_123'); const originalUpdatedAt = comment.updatedAt; comment.content = 'Updated comment content'; await comment.save(); expect(comment.updatedAt).not.toBe(originalUpdatedAt); }); }); describe('Content Edge Cases', () => { it('should handle special characters in content', async () => { const commentData = { post: 'post_123', user: 'user_123', content: 'This comment has émojis 🎉 and spëcial charactërs!', }; const mockUser = { _id: 'user_123', name: 'Test User' }; const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'This comment has émojis 🎉 and spëcial charactërs!', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment.content).toBe('This comment has émojis 🎉 and spëcial charactërs!'); }); it('should handle newlines in content', async () => { const commentData = { post: 'post_123', user: 'user_123', content: 'This comment\nhas\nmultiple\nlines', }; const mockUser = { _id: 'user_123', name: 'Test User' }; const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } }; const mockCreated = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'This comment\nhas\nmultiple\nlines', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.findUserById.mockResolvedValue(mockUser); mockCouchdbService.getById.mockResolvedValue(mockPost); mockCouchdbService.createDocument.mockResolvedValue(mockCreated); mockCouchdbService.updatePost.mockResolvedValue(mockPost); const comment = await Comment.create(commentData); expect(comment.content).toBe('This comment\nhas\nmultiple\nlines'); }); }); describe('Static Methods', () => { it('should find comment by ID', async () => { const mockComment = { _id: 'comment_123', _rev: '1-abc', type: 'comment', user: { userId: 'user_123', name: 'Test User' }, post: { postId: 'post_123', content: 'Test post', userId: 'user_123' }, content: 'Test comment', createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-01-01T00:00:00.000Z' }; mockCouchdbService.getById.mockResolvedValue(mockComment); const comment = await Comment.findById('comment_123'); expect(comment).toBeDefined(); expect(comment._id).toBe('comment_123'); expect(comment.content).toBe('Test comment'); }); it('should return null when comment not found', async () => { mockCouchdbService.getById.mockResolvedValue(null); const comment = await Comment.findById('nonexistent'); expect(comment).toBeNull(); }); }); });