import { parseBacklinks, syncBacklinks, getBacklinksForNote, getOutgoingLinksForNote } from '@/lib/backlinks' // Mock prisma before importing backlinks module jest.mock('@/lib/prisma', () => ({ prisma: { backlink: { deleteMany: jest.fn(), createMany: jest.fn(), findMany: jest.fn(), }, note: { findMany: jest.fn(), }, }, })) import { prisma } from '@/lib/prisma' describe('backlinks.ts', () => { beforeEach(() => { jest.clearAllMocks() }) describe('parseBacklinks', () => { it('extracts single wiki-link', () => { const content = 'This is about [[Docker Commands]]' const result = parseBacklinks(content) expect(result).toEqual(['Docker Commands']) }) it('extracts multiple wiki-links', () => { const content = 'See [[Docker Commands]] and [[Git Commands]] for reference' const result = parseBacklinks(content) expect(result).toContain('Docker Commands') expect(result).toContain('Git Commands') }) it('extracts wiki-links with extra whitespace', () => { const content = 'Check [[ Docker Commands ]] for details' const result = parseBacklinks(content) expect(result).toEqual(['Docker Commands']) }) it('returns empty array when no wiki-links', () => { const content = 'This is a plain note without any links' const result = parseBacklinks(content) expect(result).toEqual([]) }) it('handles wiki-links at start of content', () => { const content = '[[First Note]] is the beginning' const result = parseBacklinks(content) expect(result).toEqual(['First Note']) }) it('handles wiki-links at end of content', () => { const content = 'The solution is [[Last Note]]' const result = parseBacklinks(content) expect(result).toEqual(['Last Note']) }) it('deduplicates repeated wiki-links', () => { const content = 'See [[Docker Commands]] and again [[Docker Commands]]' const result = parseBacklinks(content) expect(result).toEqual(['Docker Commands']) }) it('handles nested brackets gracefully', () => { const content = 'Check [[This]] and [[That]]' const result = parseBacklinks(content) expect(result).toHaveLength(2) }) }) describe('syncBacklinks', () => { it('deletes existing backlinks before creating new ones', async () => { ;(prisma.backlink.deleteMany as jest.Mock).mockResolvedValue({ count: 2 }) ;(prisma.note.findMany as jest.Mock).mockResolvedValue([]) ;(prisma.backlink.createMany as jest.Mock).mockResolvedValue({ count: 0 }) await syncBacklinks('note-1', 'No links here') expect(prisma.backlink.deleteMany).toHaveBeenCalledWith({ where: { sourceNoteId: 'note-1' }, }) }) it('creates backlinks for valid linked notes', async () => { ;(prisma.backlink.deleteMany as jest.Mock).mockResolvedValue({ count: 0 }) ;(prisma.note.findMany as jest.Mock).mockResolvedValue([ { id: 'note-2', title: 'Docker Commands' }, { id: 'note-3', title: 'Git Commands' }, ]) ;(prisma.backlink.createMany as jest.Mock).mockResolvedValue({ count: 2 }) await syncBacklinks('note-1', 'See [[Docker Commands]] and [[Git Commands]]') expect(prisma.backlink.createMany).toHaveBeenCalledWith({ data: [ { sourceNoteId: 'note-1', targetNoteId: 'note-2' }, { sourceNoteId: 'note-1', targetNoteId: 'note-3' }, ], }) }) it('does not create backlink to self', async () => { ;(prisma.backlink.deleteMany as jest.Mock).mockResolvedValue({ count: 0 }) ;(prisma.note.findMany as jest.Mock).mockResolvedValue([ { id: 'note-1', title: 'Docker Commands' }, ]) ;(prisma.backlink.createMany as jest.Mock).mockResolvedValue({ count: 0 }) await syncBacklinks('note-1', 'This is [[Docker Commands]]') // Should not create a backlink to itself expect(prisma.backlink.createMany).not.toHaveBeenCalled() }) it('handles case-insensitive title matching', async () => { ;(prisma.backlink.deleteMany as jest.Mock).mockResolvedValue({ count: 0 }) ;(prisma.note.findMany as jest.Mock).mockResolvedValue([ { id: 'note-2', title: 'Docker Commands' }, ]) ;(prisma.backlink.createMany as jest.Mock).mockResolvedValue({ count: 1 }) await syncBacklinks('note-1', 'See [[docker commands]]') expect(prisma.backlink.createMany).toHaveBeenCalled() }) it('does nothing when no wiki-links in content', async () => { ;(prisma.backlink.deleteMany as jest.Mock).mockResolvedValue({ count: 2 }) ;(prisma.note.findMany as jest.Mock).mockResolvedValue([]) await syncBacklinks('note-1', 'Plain content without links') expect(prisma.backlink.createMany).not.toHaveBeenCalled() }) }) describe('getBacklinksForNote', () => { it('returns backlinks with source note info', async () => { const mockBacklinks = [ { id: 'bl-1', sourceNoteId: 'note-2', targetNoteId: 'note-1', createdAt: new Date('2024-01-01'), sourceNote: { id: 'note-2', title: 'Related Note', type: 'command' }, }, ] ;(prisma.backlink.findMany as jest.Mock).mockResolvedValue(mockBacklinks) const result = await getBacklinksForNote('note-1') expect(result).toHaveLength(1) expect(result[0].sourceNote.title).toBe('Related Note') expect(result[0].sourceNote.type).toBe('command') }) it('returns empty array when no backlinks', async () => { ;(prisma.backlink.findMany as jest.Mock).mockResolvedValue([]) const result = await getBacklinksForNote('note-1') expect(result).toEqual([]) }) it('converts Date to ISO string', async () => { const mockBacklinks = [ { id: 'bl-1', sourceNoteId: 'note-2', targetNoteId: 'note-1', createdAt: new Date('2024-01-01T12:00:00Z'), sourceNote: { id: 'note-2', title: 'Related Note', type: 'command' }, }, ] ;(prisma.backlink.findMany as jest.Mock).mockResolvedValue(mockBacklinks) const result = await getBacklinksForNote('note-1') expect(result[0].createdAt).toBe('2024-01-01T12:00:00.000Z') }) }) describe('getOutgoingLinksForNote', () => { it('returns outgoing links with target note info', async () => { const mockBacklinks = [ { id: 'bl-1', sourceNoteId: 'note-1', targetNoteId: 'note-2', createdAt: new Date('2024-01-01'), targetNote: { id: 'note-2', title: 'Linked Note', type: 'snippet' }, }, ] ;(prisma.backlink.findMany as jest.Mock).mockResolvedValue(mockBacklinks) const result = await getOutgoingLinksForNote('note-1') expect(result).toHaveLength(1) expect(result[0].sourceNote.title).toBe('Linked Note') expect(result[0].sourceNote.type).toBe('snippet') }) it('returns empty array when no outgoing links', async () => { ;(prisma.backlink.findMany as jest.Mock).mockResolvedValue([]) const result = await getOutgoingLinksForNote('note-1') expect(result).toEqual([]) }) }) })