Files
Mind-o-Mat/tests/notes-api.test.ts
2026-09-08 17:35:57 +02:00

222 lines
6.8 KiB
TypeScript

// 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, string> = {}): 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');
});
});