diff --git a/__tests__/api.integration.test.ts b/__tests__/api.integration.test.ts index 1a3727f..ad7a9cc 100644 --- a/__tests__/api.integration.test.ts +++ b/__tests__/api.integration.test.ts @@ -38,6 +38,11 @@ const mockPrisma = { create: jest.fn(), createMany: jest.fn(), }, + noteVersion: { + create: jest.fn(), + findMany: jest.fn(), + findUnique: jest.fn(), + }, $transaction: jest.fn((callback) => callback(mockPrisma)), } diff --git a/__tests__/versions.test.ts b/__tests__/versions.test.ts new file mode 100644 index 0000000..f6a0482 --- /dev/null +++ b/__tests__/versions.test.ts @@ -0,0 +1,167 @@ +import { createVersion, getVersions, getVersion, restoreVersion } from '@/lib/versions' + +jest.mock('@/lib/prisma', () => ({ + prisma: { + note: { findUnique: jest.fn(), update: jest.fn() }, + noteVersion: { create: jest.fn(), findMany: jest.fn(), findUnique: jest.fn() }, + }, +})) + +import { prisma } from '@/lib/prisma' +import { NotFoundError } from '@/lib/errors' + +describe('versions.ts', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + describe('createVersion', () => { + it('creates a version with correct noteId, title, content', async () => { + const mockNote = { id: 'note-1', title: 'Test Title', content: 'Test Content' } + const mockVersion = { id: 'version-1', noteId: 'note-1', title: 'Test Title', content: 'Test Content', createdAt: new Date() } + + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue(mockNote) + ;(prisma.noteVersion.create as jest.Mock).mockResolvedValue(mockVersion) + + const result = await createVersion('note-1') + + expect(result).toEqual(mockVersion) + expect(prisma.note.findUnique).toHaveBeenCalledWith({ + where: { id: 'note-1' }, + select: { id: true, title: true, content: true }, + }) + expect(prisma.noteVersion.create).toHaveBeenCalledWith({ + data: { + noteId: 'note-1', + title: 'Test Title', + content: 'Test Content', + }, + }) + }) + + it('throws NotFoundError if note does not exist', async () => { + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue(null) + + await expect(createVersion('note-1')).rejects.toThrow(NotFoundError) + }) + }) + + describe('getVersions', () => { + it('returns all versions for a note ordered by createdAt desc', async () => { + const mockNote = { id: 'note-1' } + const mockVersions = [ + { id: 'version-2', noteId: 'note-1', title: 'Title 2', content: 'Content 2', createdAt: new Date('2024-01-02') }, + { id: 'version-1', noteId: 'note-1', title: 'Title 1', content: 'Content 1', createdAt: new Date('2024-01-01') }, + ] + + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue(mockNote) + ;(prisma.noteVersion.findMany as jest.Mock).mockResolvedValue(mockVersions) + + const result = await getVersions('note-1') + + expect(result).toEqual(mockVersions) + expect(prisma.note.findUnique).toHaveBeenCalledWith({ where: { id: 'note-1' } }) + expect(prisma.noteVersion.findMany).toHaveBeenCalledWith({ + where: { noteId: 'note-1' }, + orderBy: { createdAt: 'desc' }, + select: { + id: true, + noteId: true, + title: true, + content: true, + createdAt: true, + }, + }) + }) + + it('throws NotFoundError if note does not exist', async () => { + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue(null) + + await expect(getVersions('note-1')).rejects.toThrow(NotFoundError) + }) + }) + + describe('getVersion', () => { + it('returns version by ID', async () => { + const mockVersion = { id: 'version-1', noteId: 'note-1', title: 'Title', content: 'Content', createdAt: new Date() } + + ;(prisma.noteVersion.findUnique as jest.Mock).mockResolvedValue(mockVersion) + + const result = await getVersion('version-1') + + expect(result).toEqual(mockVersion) + expect(prisma.noteVersion.findUnique).toHaveBeenCalledWith({ where: { id: 'version-1' } }) + }) + + it('throws NotFoundError if version does not exist', async () => { + ;(prisma.noteVersion.findUnique as jest.Mock).mockResolvedValue(null) + + await expect(getVersion('version-1')).rejects.toThrow(NotFoundError) + }) + }) + + describe('restoreVersion', () => { + it('updates note title and content from version', async () => { + const mockNote = { id: 'note-1', title: 'Old Title', content: 'Old Content' } + const mockVersion = { id: 'version-1', noteId: 'note-1', title: 'Old Title', content: 'Old Content', createdAt: new Date() } + const updatedNote = { id: 'note-1', title: 'Old Title', content: 'Old Content', updatedAt: new Date() } + + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue(mockNote) + ;(prisma.noteVersion.findUnique as jest.Mock).mockResolvedValue(mockVersion) + ;(prisma.note.update as jest.Mock).mockResolvedValue(updatedNote) + + const result = await restoreVersion('note-1', 'version-1') + + expect(result).toEqual(updatedNote) + expect(prisma.note.update).toHaveBeenCalledWith({ + where: { id: 'note-1' }, + data: { + title: 'Old Title', + content: 'Old Content', + updatedAt: expect.any(Date), + }, + select: { + id: true, + title: true, + content: true, + updatedAt: true, + }, + }) + }) + + it('updates note updatedAt timestamp', async () => { + const mockNote = { id: 'note-1', title: 'Title', content: 'Content' } + const mockVersion = { id: 'version-1', noteId: 'note-1', title: 'Title', content: 'Content', createdAt: new Date() } + const beforeUpdate = new Date('2024-01-01') + const afterUpdate = new Date('2024-01-02') + + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue(mockNote) + ;(prisma.noteVersion.findUnique as jest.Mock).mockResolvedValue(mockVersion) + ;(prisma.note.update as jest.Mock).mockResolvedValue({ id: 'note-1', title: 'Title', content: 'Content', updatedAt: afterUpdate }) + + const result = await restoreVersion('note-1', 'version-1') + + expect(result.updatedAt).toEqual(afterUpdate) + }) + + it('throws NotFoundError if note does not exist', async () => { + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue(null) + + await expect(restoreVersion('note-1', 'version-1')).rejects.toThrow(NotFoundError) + }) + + it('throws NotFoundError if version does not exist', async () => { + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue({ id: 'note-1' }) + ;(prisma.noteVersion.findUnique as jest.Mock).mockResolvedValue(null) + + await expect(restoreVersion('note-1', 'version-1')).rejects.toThrow(NotFoundError) + }) + + it('throws NotFoundError if version does not belong to note', async () => { + ;(prisma.note.findUnique as jest.Mock).mockResolvedValue({ id: 'note-1' }) + ;(prisma.noteVersion.findUnique as jest.Mock).mockResolvedValue({ id: 'version-1', noteId: 'note-2' }) + + await expect(restoreVersion('note-1', 'version-1')).rejects.toThrow(NotFoundError) + }) + }) +})