mindnet_obsidian/src/tests/interview/parseInterviewConfig.test.ts
Lars bab84549e2
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 functionality and settings; add YAML dependency
- Introduced profile selection modal for creating notes from interview profiles.
- Added settings for interview configuration path, auto-starting interviews, and intercepting unresolved link clicks.
- Updated package files to include YAML dependency for configuration handling.
- Enhanced CSS for profile selection and interview wizard UI elements.
2026-01-16 12:27:44 +01:00

349 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, expect } from "vitest";
import { parseInterviewConfig, validateConfig } from "../../interview/parseInterviewConfig";
import type { InterviewConfig } from "../../interview/types";
describe("parseInterviewConfig", () => {
const fixtureYaml = `
version: "2.0"
frontmatterWhitelist:
- title
- id
- date
- tags
profiles:
- key: profile1
label: "Test Profile 1"
note_type: "note"
group: "test"
defaults:
template: "default"
steps:
- type: loop
key: items_loop
label: "Items Loop"
items:
- type: capture_frontmatter
key: title_field
label: "Title"
field: title
required: true
- type: capture_text
key: content_field
label: "Content"
required: false
- type: llm_dialog
key: llm_step
label: "LLM Dialog"
prompt: "What is the main topic?"
system_prompt: "You are a helpful assistant."
model: "gpt-4"
temperature: 0.7
max_tokens: 500
- key: profile2
label: "Test Profile 2"
note_type: "interview"
steps:
- type: capture_frontmatter
key: date_field
field: date
required: false
- type: llm_dialog
key: analysis
prompt: "Analyze the content"
`;
it("parses YAML correctly", () => {
const result = parseInterviewConfig(fixtureYaml);
expect(result.errors.length).toBe(0);
expect(result.config.version).toBe("2.0");
expect(result.config.frontmatterWhitelist).toEqual(["title", "id", "date", "tags"]);
expect(result.config.profiles.length).toBe(2);
});
it("parses profile structure correctly", () => {
const result = parseInterviewConfig(fixtureYaml);
const profile1 = result.config.profiles[0];
if (!profile1) throw new Error("Expected profile1");
expect(profile1.key).toBe("profile1");
expect(profile1.label).toBe("Test Profile 1");
expect(profile1.note_type).toBe("note");
expect(profile1.group).toBe("test");
expect(profile1.defaults).toEqual({ template: "default" });
expect(profile1.steps.length).toBe(2);
});
it("parses loop step correctly", () => {
const result = parseInterviewConfig(fixtureYaml);
const profile1 = result.config.profiles[0];
if (!profile1) throw new Error("Expected profile1");
const loopStep = profile1.steps[0];
if (!loopStep || loopStep.type !== "loop") {
throw new Error("Expected loop step");
}
expect(loopStep.key).toBe("items_loop");
expect(loopStep.label).toBe("Items Loop");
expect(loopStep.items.length).toBe(2);
const firstItem = loopStep.items[0];
if (!firstItem || firstItem.type !== "capture_frontmatter") {
throw new Error("Expected capture_frontmatter step");
}
expect(firstItem.key).toBe("title_field");
expect(firstItem.field).toBe("title");
expect(firstItem.required).toBe(true);
});
it("parses llm_dialog step correctly", () => {
const result = parseInterviewConfig(fixtureYaml);
const profile1 = result.config.profiles[0];
if (!profile1) throw new Error("Expected profile1");
const llmStep = profile1.steps[1];
if (!llmStep || llmStep.type !== "llm_dialog") {
throw new Error("Expected llm_dialog step");
}
expect(llmStep.key).toBe("llm_step");
expect(llmStep.prompt).toBe("What is the main topic?");
expect(llmStep.system_prompt).toBe("You are a helpful assistant.");
expect(llmStep.model).toBe("gpt-4");
expect(llmStep.temperature).toBe(0.7);
expect(llmStep.max_tokens).toBe(500);
});
it("parses capture_frontmatter step correctly", () => {
const result = parseInterviewConfig(fixtureYaml);
const profile2 = result.config.profiles[1];
if (!profile2) throw new Error("Expected profile2");
const captureStep = profile2.steps[0];
if (!captureStep || captureStep.type !== "capture_frontmatter") {
throw new Error("Expected capture_frontmatter step");
}
expect(captureStep.key).toBe("date_field");
expect(captureStep.field).toBe("date");
expect(captureStep.required).toBe(false);
});
it("parses capture_text step correctly", () => {
const result = parseInterviewConfig(fixtureYaml);
const profile1 = result.config.profiles[0];
if (!profile1) throw new Error("Expected profile1");
const loopStep = profile1.steps[0];
if (!loopStep || loopStep.type !== "loop") {
throw new Error("Expected loop step");
}
const captureTextStep = loopStep.items[1];
if (!captureTextStep || captureTextStep.type !== "capture_text") {
throw new Error("Expected capture_text step");
}
expect(captureTextStep.key).toBe("content_field");
expect(captureTextStep.required).toBe(false);
});
it("validates unique profile keys", () => {
const yamlWithDuplicate = `
version: "2.0"
frontmatterWhitelist: []
profiles:
- key: profile1
label: "Profile 1"
note_type: "note"
steps: []
- key: profile1
label: "Profile 1 Duplicate"
note_type: "note"
steps: []
`;
const result = parseInterviewConfig(yamlWithDuplicate);
const validationErrors = validateConfig(result.config);
expect(validationErrors.some(e => e.includes("Duplicate profile key"))).toBe(true);
});
it("validates capture_frontmatter.field in whitelist", () => {
const yamlWithInvalidField = `
version: "2.0"
frontmatterWhitelist:
- title
- id
profiles:
- key: profile1
label: "Profile 1"
note_type: "note"
steps:
- type: capture_frontmatter
key: invalid_field
field: invalid_field_name
`;
const result = parseInterviewConfig(yamlWithInvalidField);
const validationErrors = validateConfig(result.config);
expect(
validationErrors.some(e =>
e.includes("capture_frontmatter field 'invalid_field_name' not in frontmatterWhitelist")
)
).toBe(true);
});
it("validates capture_frontmatter.field in nested loop", () => {
const yamlWithNestedInvalidField = `
version: "2.0"
frontmatterWhitelist:
- title
profiles:
- key: profile1
label: "Profile 1"
note_type: "note"
steps:
- type: loop
key: items
items:
- type: capture_frontmatter
key: invalid
field: invalid_field
`;
const result = parseInterviewConfig(yamlWithNestedInvalidField);
const validationErrors = validateConfig(result.config);
expect(
validationErrors.some(e =>
e.includes("capture_frontmatter field 'invalid_field' not in frontmatterWhitelist")
)
).toBe(true);
});
it("handles invalid YAML gracefully", () => {
const invalidYaml = "invalid: yaml: content: [";
const result = parseInterviewConfig(invalidYaml);
expect(result.errors.length).toBeGreaterThan(0);
expect(result.config.profiles.length).toBe(0);
});
it("handles missing required fields", () => {
const incompleteYaml = `
version: "2.0"
frontmatterWhitelist: []
profiles:
- key: profile1
label: "Profile 1"
# missing note_type
steps: []
`;
const result = parseInterviewConfig(incompleteYaml);
// Profile should be skipped due to missing note_type
expect(result.config.profiles.length).toBe(0);
expect(result.errors.length).toBeGreaterThan(0);
});
it("parses YAML with 'kind' and 'id' fields (real-world format)", () => {
const yamlWithKindAndId = `
version: 2
frontmatter_whitelist:
- id
- title
- type
profiles:
- key: experience_hub
group: experience
label: "Experience Hub"
note_type: experience
defaults:
status: active
steps:
- id: title
kind: capture_frontmatter
field: title
label: "Hub Titel"
required: true
- id: items
kind: loop
label: "Erlebnisse"
steps:
- id: item_text
kind: capture_text
label: "Erlebnis"
required: true
- id: review
kind: review
label: "Review & Apply"
`;
const result = parseInterviewConfig(yamlWithKindAndId);
// Log errors for debugging
if (result.errors.length > 0) {
console.log("Parse errors:", result.errors);
}
expect(result.errors.length).toBe(0);
expect(result.config.profiles.length).toBe(1);
const profile = result.config.profiles[0];
if (!profile) throw new Error("Expected profile");
expect(profile.key).toBe("experience_hub");
expect(profile.label).toBe("Experience Hub");
expect(profile.note_type).toBe("experience");
expect(profile.group).toBe("experience");
expect(profile.steps.length).toBe(3);
// First step: capture_frontmatter
const titleStep = profile.steps[0];
if (!titleStep || titleStep.type !== "capture_frontmatter") {
throw new Error("Expected capture_frontmatter step");
}
expect(titleStep.key).toBe("title");
expect(titleStep.field).toBe("title");
expect(titleStep.label).toBe("Hub Titel");
expect(titleStep.required).toBe(true);
// Second step: loop
const loopStep = profile.steps[1];
if (!loopStep || loopStep.type !== "loop") {
throw new Error("Expected loop step");
}
expect(loopStep.key).toBe("items");
expect(loopStep.label).toBe("Erlebnisse");
expect(loopStep.items.length).toBe(1);
const nestedStep = loopStep.items[0];
if (!nestedStep || nestedStep.type !== "capture_text") {
throw new Error("Expected capture_text step in loop");
}
expect(nestedStep.key).toBe("item_text");
expect(nestedStep.label).toBe("Erlebnis");
expect(nestedStep.required).toBe(true);
// Third step: review
const reviewStep = profile.steps[2];
if (!reviewStep || reviewStep.type !== "review") {
throw new Error("Expected review step");
}
expect(reviewStep.key).toBe("review");
expect(reviewStep.label).toBe("Review & Apply");
});
});