// Tests fuer NotesApi (Vite-Plugin-Logik) // Phase 4.3 import { describe, it, expect, afterEach } from 'vitest'; import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readFileSync, existsSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { NotesApi } from '../src/notes-api-server.js'; const vaults: string[] = []; afterEach(() => { for (const v of vaults) rmSync(v, { recursive: true, force: true }); }); function setupVault(files: Record = {}): string { const vault = mkdtempSync(join(tmpdir(), 'mindomat-notes-api-')); for (const d of [ '00_Inbox', '00_Inbox/Verarbeitet', '00_Inbox/Problemfaelle', '01_Daily', '10_Wiki', '10_Wiki/Seiten', '20_Projekte', '90_Templates', '99_System', '99_System/Skripte', '99_System/Logs', '99_System/Cache', ]) { mkdirSync(join(vault, d), { recursive: true }); } for (const [rel, content] of Object.entries(files)) { const full = join(vault, rel); mkdirSync(join(full, '..'), { recursive: true }); writeFileSync(full, content); } return vault; } describe('NotesApi.listNotes', () => { it('leerer Vault: leere Liste', () => { const vault = setupVault(); vaults.push(vault); const api = new NotesApi(vault); expect(api.listNotes()).toEqual([]); }); it('Inbox-Notizen werden gelistet', () => { const vault = setupVault({ '00_Inbox/2026-09-05_A.md': '# A', '00_Inbox/2026-09-05_B.md': '# B', }); vaults.push(vault); const api = new NotesApi(vault); const notes = api.listNotes(); expect(notes).toContain('00_Inbox/2026-09-05_A.md'); expect(notes).toContain('00_Inbox/2026-09-05_B.md'); expect(notes.length).toBe(2); }); it('Verarbeitet/-Problemfaelle werden NICHT gelistet (Pfad-Filter)', () => { const vault = setupVault({ '00_Inbox/2026-09-05_A.md': '# A', '00_Inbox/Verarbeitet/2026-09-05_B.md': '# B', '00_Inbox/Problemfaelle/2026-09-05_C.md': '# C', }); vaults.push(vault); const api = new NotesApi(vault); const notes = api.listNotes(); // B ist in einem Unterordner, der NICHT in der Top-Level-Liste ist // Die Implementation listet nur Top-Level-Files in den 4 Verzeichnissen expect(notes).toContain('00_Inbox/2026-09-05_A.md'); expect(notes.length).toBe(1); }); it('Forward-Slashes in Pfaden (Windows-Kompatibilitaet)', () => { const vault = setupVault({ '10_Wiki/Seiten/Wiki_A.md': '# Wiki', }); vaults.push(vault); const api = new NotesApi(vault); const notes = api.listNotes(); expect(notes[0]).toBe('10_Wiki/Seiten/Wiki_A.md'); expect(notes[0]).not.toContain('\\'); }); }); describe('NotesApi.getNote', () => { it('gibt null zurueck fuer nicht-existente Notiz', () => { const vault = setupVault(); vaults.push(vault); const api = new NotesApi(vault); expect(api.getNote('00_Inbox/nicht-da.md')).toBeNull(); }); it('liest existierende Notiz mit Frontmatter', () => { const vault = setupVault({ '00_Inbox/2026-09-05_Test.md': `--- title: Test created: 2026-09-05 tags: [test] --- # Inhalt`, }); vaults.push(vault); const api = new NotesApi(vault); const note = api.getNote('00_Inbox/2026-09-05_Test.md'); expect(note).not.toBeNull(); expect(note!.content).toContain('# Inhalt'); expect(note!.frontmatter.title).toBe('Test'); expect(note!.frontmatter.tags).toEqual(['test']); expect(note!.lastModified).toBeTruthy(); }); }); describe('NotesApi.saveNote', () => { it('schreibt neue Notiz und liest sie zurueck', () => { const vault = setupVault(); vaults.push(vault); const api = new NotesApi(vault); const id = '01_Daily/2026-09-05_New.md'; const content = `--- title: Neu created: 2026-09-05 --- # Inhalt`; const result = api.saveNote(id, content); expect(result.id).toBe(id); expect(result.frontmatter.title).toBe('Neu'); expect(existsSync(join(vault, id))).toBe(true); }); it('ueberschreibt existierende Notiz', () => { const vault = setupVault({ '00_Inbox/2026-09-05_Update.md': '# alt', }); vaults.push(vault); const api = new NotesApi(vault); const id = '00_Inbox/2026-09-05_Update.md'; const newContent = '# neu'; api.saveNote(id, newContent); const readBack = readFileSync(join(vault, id), 'utf-8'); expect(readBack).toBe(newContent); }); it('erstellt Parent-Verzeichnisse automatisch', () => { const vault = setupVault(); vaults.push(vault); const api = new NotesApi(vault); const id = '20_Projekte/Sub/New.md'; api.saveNote(id, '# Inhalt'); expect(existsSync(join(vault, id))).toBe(true); }); }); describe('NotesApi End-to-End', () => { it('list -> get -> save -> get roundtrip', () => { const vault = setupVault(); vaults.push(vault); const api = new NotesApi(vault); // 1. Liste ist leer expect(api.listNotes()).toEqual([]); // 2. Speichere Notiz const id = '10_Wiki/Seiten/Wiki_X.md'; api.saveNote(id, '---\ntitle: Wiki X\n---\n\n# Wiki X'); // 3. Liste enthaelt die Notiz expect(api.listNotes()).toContain(id); // 4. getNote liefert die Notiz const note = api.getNote(id); expect(note).not.toBeNull(); expect(note!.frontmatter.title).toBe('Wiki X'); // 5. Update Notiz api.saveNote(id, '---\ntitle: Wiki X (updated)\n---\n\n# Updated'); const updated = api.getNote(id); expect(updated!.frontmatter.title).toBe('Wiki X (updated)'); expect(updated!.content).toContain('# Updated'); }); it('listNotesWithMetadata liefert Notizen mit Titeln, Tags und Ordnern', () => { const vault = setupVault({ '00_Inbox/2026-09-08_Idee.md': '---\ntitle: Coole Idee\ntags: [app, mobile]\n---\n\n# Coole Idee\n\nDas ist der Snippet-Text.', }); vaults.push(vault); const api = new NotesApi(vault); const items = api.listNotesWithMetadata(); expect(items.length).toBe(1); expect(items[0].title).toBe('Coole Idee'); expect(items[0].folder).toBe('00_Inbox'); expect(items[0].tags).toEqual(['app', 'mobile']); expect(items[0].snippet).toContain('Das ist der Snippet-Text.'); }); it('captureNote legt Notiz in 00_Inbox mit Frontmatter an', () => { const vault = setupVault(); vaults.push(vault); const api = new NotesApi(vault); const result = api.captureNote({ text: 'Meeting mit Anna morgen um 10 Uhr', title: 'Meeting Anna', tags: ['meeting', 'arbeit'], }); expect(result.ok).toBe(true); expect(result.noteId).toMatch(/^00_Inbox\/\d{4}-\d{2}-\d{2}_Meeting_Anna_\d+\.md$/); expect(existsSync(join(vault, result.noteId))).toBe(true); const saved = api.getNote(result.noteId); expect(saved).not.toBeNull(); expect(saved!.frontmatter.title).toBe('Meeting Anna'); expect(saved!.content).toContain('Meeting mit Anna morgen um 10 Uhr'); }); });