mindnet_obsidian/src/interview/renderer.test.ts
Lars 054cfcf82d
Some checks are pending
Node.js build / build (20.x) (push) Waiting to run
Node.js build / build (22.x) (push) Waiting to run
Enhance interview configuration and rendering for WP-26 integration
- Expanded the interview configuration YAML to include new profiles for experience and insight, with detailed steps for capturing user input.
- Updated the parsing logic to support new input types, including select options, enhancing user interaction during interviews.
- Improved the rendering logic to ensure correct handling of section edges and types, aligning with the updated configuration structure.
- Enhanced tests to validate the new configurations and rendering behavior, ensuring robustness in the interview process.
2026-01-30 18:27:38 +01:00

294 lines
8.6 KiB
TypeScript

/**
* Tests für WP-26 Interview-Wizard Renderer
* Prüft Section-Types, Block-IDs, automatische Edge-Vorschläge und Selbstreferenz-Prüfung
*/
import { describe, it, expect } from "vitest";
import { renderProfileToMarkdown, type RenderAnswers, type RenderOptions } from "./renderer";
import type { InterviewProfile } from "./types";
import type { GraphSchema, EdgeTypeHints } from "../mapping/graphSchema";
import { Vocabulary } from "../vocab/Vocabulary";
import { parseEdgeVocabulary } from "../vocab/parseEdgeVocabulary";
describe("WP-26 Interview Renderer", () => {
// Mock Vocabulary
const mockVocabularyText = `| System-Typ (Canonical) | Inverser Typ | Erlaubte Aliasse (User) | Beschreibung |
|------------------------|--------------|-------------------------|-------------|
| resulted_in | resulted_from| führt zu, bewirkt | Forward edge |
| resulted_from | resulted_in | stammt aus, kommt von | Inverse edge |
| related_to | related_to | verbunden mit | Bidirectional |`;
const mockVocabulary = new Vocabulary(parseEdgeVocabulary(mockVocabularyText));
// Mock GraphSchema
const mockGraphSchema: GraphSchema = {
schema: new Map<string, Map<string, EdgeTypeHints>>([
[
"experience",
new Map<string, EdgeTypeHints>([
[
"insight",
{
typical: ["resulted_in"],
prohibited: [],
},
],
]),
],
]),
};
const mockOptions: RenderOptions = {
graphSchema: mockGraphSchema,
vocabulary: mockVocabulary,
noteType: "experience",
};
it("sollte keine Selbstreferenz bei automatischen Edges generieren", () => {
const profile: InterviewProfile = {
key: "test",
label: "Test",
note_type: "experience",
steps: [
{
type: "capture_text",
key: "context",
section: "## Kontext",
section_type: "experience",
generate_block_id: true,
},
{
type: "capture_text",
key: "insight",
section: "## Einsicht",
section_type: "insight",
generate_block_id: true,
},
],
};
const answers: RenderAnswers = {
collectedData: new Map([
["context", "Kontext-Text"],
["insight", "Einsicht-Text"],
]),
loopContexts: new Map(),
};
const result = renderProfileToMarkdown(profile, answers, mockOptions);
// Prüfe, dass keine Selbstreferenz vorhanden ist
const contextSection = result.match(/## Kontext[\s\S]*?## Einsicht/)?.[0];
expect(contextSection).toBeDefined();
// Prüfe, dass keine Edge auf sich selbst zeigt
const selfReference = contextSection?.match(/\[\[#\^context\]\]/);
expect(selfReference).toBeNull();
// Prüfe, dass Edges generiert wurden
const edges = result.match(/> \[!edge\]/g);
expect(edges?.length).toBeGreaterThan(0);
});
it("sollte Edge-Types aus graph_schema verwenden", () => {
const profile: InterviewProfile = {
key: "test",
label: "Test",
note_type: "experience",
steps: [
{
type: "capture_text",
key: "experience",
section: "## Erfahrung",
section_type: "experience",
generate_block_id: true,
},
{
type: "capture_text",
key: "insight",
section: "## Einsicht",
section_type: "insight",
generate_block_id: true,
},
],
};
const answers: RenderAnswers = {
collectedData: new Map([
["experience", "Erfahrungs-Text"],
["insight", "Einsicht-Text"],
]),
loopContexts: new Map(),
};
const result = renderProfileToMarkdown(profile, answers, mockOptions);
// Debug: Zeige das Ergebnis
console.log("Render-Ergebnis:", result);
// Prüfe, dass Edges generiert wurden
const edges = result.match(/> \[!edge\]/g);
expect(edges?.length).toBeGreaterThan(0);
// Prüfe, dass "resulted_in" oder "resulted_from" verwendet wird (aus graph_schema)
// Falls graph_schema nicht verfügbar ist, wird "related_to" verwendet (Fallback)
const hasResultedEdge = result.match(/resulted_(in|from)/);
const hasRelatedEdge = result.match(/related_to/);
// Mindestens eine Edge sollte vorhanden sein
expect(hasResultedEdge || hasRelatedEdge).toBeTruthy();
// Wenn graph_schema verfügbar ist, sollte "resulted_in" oder "resulted_from" verwendet werden
if (mockOptions.graphSchema) {
expect(hasResultedEdge).toBeTruthy();
}
});
it("sollte Backlinks automatisch generieren", () => {
const profile: InterviewProfile = {
key: "test",
label: "Test",
note_type: "experience",
steps: [
{
type: "capture_text",
key: "context",
section: "## Kontext",
section_type: "experience",
generate_block_id: true,
},
{
type: "capture_text",
key: "insight",
section: "## Einsicht",
section_type: "insight",
generate_block_id: true,
},
],
};
const answers: RenderAnswers = {
collectedData: new Map([
["context", "Kontext-Text"],
["insight", "Einsicht-Text"],
]),
loopContexts: new Map(),
};
const result = renderProfileToMarkdown(profile, answers, mockOptions);
// Prüfe, dass beide Edge-Types vorhanden sind (Forward und Backward)
const forwardEdge = result.match(/resulted_in/);
const backwardEdge = result.match(/resulted_from/);
expect(forwardEdge).toBeDefined();
expect(backwardEdge).toBeDefined();
});
it("sollte keine Selbstreferenz bei expliziten Referenzen erlauben", () => {
const profile: InterviewProfile = {
key: "test",
label: "Test",
note_type: "experience",
steps: [
{
type: "capture_text",
key: "context",
section: "## Kontext",
section_type: "experience",
block_id: "context",
references: [
{
block_id: "context", // Selbstreferenz - sollte verhindert werden
edge_type: "related_to",
},
],
},
],
};
const answers: RenderAnswers = {
collectedData: new Map([
["context", "Kontext-Text"],
]),
loopContexts: new Map(),
};
const result = renderProfileToMarkdown(profile, answers, mockOptions);
// Prüfe, dass keine Selbstreferenz generiert wurde
const selfReference = result.match(/\[\[#\^context\]\]/);
// Sollte null sein, da Selbstreferenz verhindert wird
expect(selfReference).toBeNull();
});
it("sollte Section-Type-Callout direkt nach Heading platzieren", () => {
const profile: InterviewProfile = {
key: "test",
label: "Test",
note_type: "experience",
steps: [
{
type: "capture_text",
key: "context",
section: "## Kontext",
section_type: "experience",
generate_block_id: true,
},
],
};
const answers: RenderAnswers = {
collectedData: new Map([
["context", "Kontext-Text"],
]),
loopContexts: new Map(),
};
const result = renderProfileToMarkdown(profile, answers, mockOptions);
// Prüfe, dass Section-Type-Callout direkt nach Heading steht
const sectionPattern = /## Kontext[\s\S]*?\n> \[!section\] experience/;
expect(result).toMatch(sectionPattern);
});
it("sollte Edges am Ende der Sektion platzieren", () => {
const profile: InterviewProfile = {
key: "test",
label: "Test",
note_type: "experience",
steps: [
{
type: "capture_text",
key: "context",
section: "## Kontext",
section_type: "experience",
generate_block_id: true,
},
{
type: "capture_text",
key: "insight",
section: "## Einsicht",
section_type: "insight",
generate_block_id: true,
},
],
};
const answers: RenderAnswers = {
collectedData: new Map([
["context", "Kontext-Text"],
["insight", "Einsicht-Text"],
]),
loopContexts: new Map(),
};
const result = renderProfileToMarkdown(profile, answers, mockOptions);
// Prüfe, dass Edges am Ende der Einsicht-Sektion stehen (nach dem Text; Abstract-Wrapper nutzt >>)
const insightSection = result.match(/## Einsicht[\s\S]*?Einsicht-Text\n\n([\s\S]*?\[!edge\][\s\S]*)/)?.[1];
expect(insightSection).toBeDefined();
expect(insightSection).toMatch(/>+ \[!edge\]/);
});
});