import { describe, it, expect } from 'vitest'; import { mergeNotes, hasChanges, diffNotes } from './crdt.js'; import type { Note } from '../types/index.js'; function makeNote(overrides: Partial = {}): Note { return { id: 'test-id', title: 'Test', content: 'Content', color: 'default', pinned: false, archived: false, trashed: false, trashedAt: null, checklistMode: false, sortOrder: 0, createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01'), version: 1, ...overrides }; } describe('mergeNotes (LWW)', () => { it('should prefer remote when remote is newer', () => { const local = makeNote({ title: 'Local', updatedAt: new Date('2024-01-01') }); const remote = makeNote({ title: 'Remote', updatedAt: new Date('2024-01-02') }); const result = mergeNotes(local, remote); expect(result.title).toBe('Remote'); }); it('should prefer local when local is newer', () => { const local = makeNote({ title: 'Local', updatedAt: new Date('2024-01-02') }); const remote = makeNote({ title: 'Remote', updatedAt: new Date('2024-01-01') }); const result = mergeNotes(local, remote); expect(result.title).toBe('Local'); }); it('should prefer higher version when timestamps are equal', () => { const ts = new Date('2024-01-01'); const local = makeNote({ title: 'Local', updatedAt: ts, version: 1 }); const remote = makeNote({ title: 'Remote', updatedAt: ts, version: 2 }); const result = mergeNotes(local, remote); expect(result.title).toBe('Remote'); }); it('should prefer local when timestamps and versions are equal', () => { const ts = new Date('2024-01-01'); const local = makeNote({ title: 'Local', updatedAt: ts, version: 1 }); const remote = makeNote({ title: 'Remote', updatedAt: ts, version: 1 }); const result = mergeNotes(local, remote); expect(result.title).toBe('Local'); }); }); describe('hasChanges', () => { it('should detect title change', () => { const a = makeNote({ title: 'A' }); const b = makeNote({ title: 'B' }); expect(hasChanges(a, b)).toBe(true); }); it('should detect content change', () => { const a = makeNote({ content: 'old' }); const b = makeNote({ content: 'new' }); expect(hasChanges(a, b)).toBe(true); }); it('should detect color change', () => { const a = makeNote({ color: 'default' }); const b = makeNote({ color: 'coral' }); expect(hasChanges(a, b)).toBe(true); }); it('should detect pinned change', () => { const a = makeNote({ pinned: false }); const b = makeNote({ pinned: true }); expect(hasChanges(a, b)).toBe(true); }); it('should return false when notes are identical', () => { const a = makeNote(); const b = makeNote(); expect(hasChanges(a, b)).toBe(false); }); }); describe('diffNotes', () => { it('should return only changed fields', () => { const old = makeNote({ title: 'Old', content: 'Same' }); const updated = makeNote({ title: 'New', content: 'Same' }); const diff = diffNotes(old, updated); expect(diff.title).toBe('New'); expect(diff.content).toBeUndefined(); }); it('should return empty object when no changes', () => { const a = makeNote(); const b = makeNote(); const diff = diffNotes(a, b); expect(Object.keys(diff)).toHaveLength(0); }); it('should track multiple changed fields', () => { const old = makeNote({ title: 'Old', color: 'default', pinned: false }); const updated = makeNote({ title: 'New', color: 'coral', pinned: true }); const diff = diffNotes(old, updated); expect(diff.title).toBe('New'); expect(diff.color).toBe('coral'); expect(diff.pinned).toBe(true); }); });