mindnet_obsidian/src/tests/analysis/chainInspector.topN.test.ts
Lars 725adb5302
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 UI and functionality for Chain Workbench and related features
- Introduced a wide two-column layout for the Chain Workbench modal, improving user experience and accessibility.
- Added new styles for workbench components, including headers, filters, and main containers, to enhance visual organization.
- Updated chain templates to allow for multiple distinct matches per template, improving flexibility in template matching.
- Enhanced documentation to clarify the new settings and commands related to the Chain Workbench and edge detection features.
- Implemented logging for better tracking of missing configurations, ensuring users are informed about any loading issues.
2026-02-05 11:41:15 +01:00

200 lines
6.4 KiB
TypeScript

/**
* Tests for Chain Inspector top-N template matches (v0.4.4).
*/
import { describe, it, expect } from "vitest";
import { inspectChains } from "../../analysis/chainInspector";
import { createVaultAppFromFixtures } from "../helpers/vaultHelper";
import { loadChainRolesFromFixtures, loadChainTemplatesFromFixtures } from "../helpers/configHelper";
import type { SectionContext } from "../../analysis/sectionContext";
import type { ChainTemplatesConfig } from "../../dictionary/types";
describe("Chain Inspector top-N template matches", () => {
it("should return top 3 matches sorted by confidence, score, templateName", async () => {
const app = createVaultAppFromFixtures();
const chainRoles = loadChainRolesFromFixtures();
const baseTemplates = loadChainTemplatesFromFixtures();
if (!chainRoles || !baseTemplates) {
console.warn("Skipping test: real config files not found in fixtures");
return;
}
// Create templates config with multiple templates that should match
const chainTemplates: ChainTemplatesConfig = {
...baseTemplates,
defaults: {
...baseTemplates.defaults,
profiles: {
discovery: {
required_links: false,
min_slots_filled_for_gap_findings: 1,
min_score_for_gap_findings: 0,
},
},
},
templates: [
...baseTemplates.templates,
{
name: "simple_chain",
slots: [
{ id: "start", allowed_node_types: ["experience"] },
{ id: "end", allowed_node_types: ["decision"] },
],
links: [
{ from: "start", to: "end", allowed_edge_roles: ["causal"] },
],
},
{
name: "trigger_transformation",
slots: [
{ id: "trigger", allowed_node_types: ["experience"] },
{ id: "transformation", allowed_node_types: ["insight"] },
],
links: [
{ from: "trigger", to: "transformation", allowed_edge_roles: ["causal", "influences"] },
],
},
],
};
const context: SectionContext = {
file: "Tests/01_experience_trigger.md",
heading: "Kontext",
zoneKind: "content",
sectionIndex: 0,
};
const report = await inspectChains(
app,
context,
{
includeNoteLinks: true,
includeCandidates: false,
maxDepth: 3,
direction: "both",
},
chainRoles,
"_system/dictionary/edge_vocabulary.md",
chainTemplates,
undefined,
"discovery"
);
// Should have template matches (may be empty if no templates match)
if (!report.templateMatches || report.templateMatches.length === 0) {
// If no matches, topNUsed should still be set if templates were processed
if (report.templatesSource) {
expect(report.analysisMeta?.topNUsed).toBe(3);
}
return; // Skip ordering tests if no matches
}
// Should have at most 3 matches per template (topN per chain type)
const byTemplate = new Map<string, number>();
for (const m of report.templateMatches) {
byTemplate.set(m.templateName, (byTemplate.get(m.templateName) ?? 0) + 1);
}
for (const count of byTemplate.values()) {
expect(count).toBeLessThanOrEqual(3);
}
// Should have topNUsed in analysisMeta
expect(report.analysisMeta?.topNUsed).toBe(3);
// Verify deterministic ordering: confidence rank (confirmed > plausible > weak), then score desc, then templateName asc
const confidenceRank = (c: "confirmed" | "plausible" | "weak"): number => {
if (c === "confirmed") return 3;
if (c === "plausible") return 2;
return 1; // weak
};
for (let i = 0; i < report.templateMatches.length - 1; i++) {
const current = report.templateMatches[i];
const next = report.templateMatches[i + 1];
if (!current || !next) continue;
const currentRank = confidenceRank(current.confidence);
const nextRank = confidenceRank(next.confidence);
// If confidence ranks differ, current should be higher
if (currentRank !== nextRank) {
expect(currentRank).toBeGreaterThan(nextRank);
continue;
}
// If scores differ, current should be higher
if (current.score !== next.score) {
expect(current.score).toBeGreaterThanOrEqual(next.score);
continue;
}
// If templateNames differ, current should be lexicographically smaller
expect(current.templateName.localeCompare(next.templateName)).toBeLessThanOrEqual(0);
}
// If we have multiple templates, both should appear
const templateNames = report.templateMatches.map(m => m.templateName);
if (templateNames.length >= 2) {
// At least one template should be present
expect(templateNames.length).toBeGreaterThan(0);
}
});
it("should show Why evidence in pretty print format", async () => {
const app = createVaultAppFromFixtures();
const chainRoles = loadChainRolesFromFixtures();
const chainTemplates = loadChainTemplatesFromFixtures();
if (!chainRoles || !chainTemplates) {
console.warn("Skipping test: real config files not found in fixtures");
return;
}
const context: SectionContext = {
file: "Tests/01_experience_trigger.md",
heading: "Kontext",
zoneKind: "content",
sectionIndex: 0,
};
const report = await inspectChains(
app,
context,
{
includeNoteLinks: true,
includeCandidates: false,
maxDepth: 3,
direction: "both",
},
chainRoles,
"_system/dictionary/edge_vocabulary.md",
chainTemplates,
undefined,
"discovery"
);
// Check that matches have roleEvidence if they have links
if (report.templateMatches && report.templateMatches.length > 0) {
for (const match of report.templateMatches) {
if (match.requiredLinks > 0 && match.satisfiedLinks > 0) {
// Should have roleEvidence
expect(match.roleEvidence).toBeDefined();
if (match.roleEvidence && match.roleEvidence.length > 0) {
// Each evidence should have from, to, edgeRole, rawEdgeType
for (const ev of match.roleEvidence) {
expect(ev.from).toBeDefined();
expect(ev.to).toBeDefined();
expect(ev.edgeRole).toBeDefined();
expect(ev.rawEdgeType).toBeDefined();
}
}
}
}
}
});
});