140 lines
8.7 KiB
JavaScript
140 lines
8.7 KiB
JavaScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
focusedDeconstruct,
|
|
buildFocusedDeconstructPrompt,
|
|
validateFocusedDeconstructSchema,
|
|
focusedDeconstructJsonSchema,
|
|
} from "@/lib/graph/focused-investigation.js";
|
|
|
|
const FAKE_TARGET_NODE_ID = "test-node-001";
|
|
const FAKE_LABEL = "Market demand signal";
|
|
const FAKE_DESCRIPTION = "Evidence that customers want this.";
|
|
const FAKE_CENTRAL = "Should we launch the product now?";
|
|
const FAKE_QUESTION = "What evidence would clarify market demand?";
|
|
const FAKE_ANSWER = "Some evidence suggests demand.";
|
|
|
|
function makeValidResponse() {
|
|
return {
|
|
targetNodeId: "model-returned-001",
|
|
observations: ["Observation A"],
|
|
uncertainties: ["Uncertainty B"],
|
|
assumptions: ["Assumption C"],
|
|
relationships: [{ from: "A", to: "B", type: "supports" }],
|
|
possibleFollowUpQuestions: ["What next?"],
|
|
};
|
|
}
|
|
|
|
function makeInvalidResponse() {
|
|
return { targetNodeId: "model-returned-001", observations: ["Obs"] };
|
|
}
|
|
|
|
describe("focusedDeconstruct — success", () => {
|
|
it("calls provider.generateReconstruction exactly once and returns success", () => {
|
|
let callCount = 0;
|
|
const fake = { generateReconstruction() { callCount++; return { response: makeValidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(callCount).toBe(1);
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it("preserves observations from provider response", () => {
|
|
const fake = { generateReconstruction() { return { response: makeValidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(Array.isArray(result.observations)).toBe(true);
|
|
expect(result.observations.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("preserves uncertainties from provider response", () => {
|
|
const fake = { generateReconstruction() { return { response: makeValidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(Array.isArray(result.uncertainties)).toBe(true);
|
|
expect(result.uncertainties.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("preserves assumptions from provider response", () => {
|
|
const fake = { generateReconstruction() { return { response: makeValidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(Array.isArray(result.assumptions)).toBe(true);
|
|
expect(result.assumptions.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("preserves relationships from provider response", () => {
|
|
const fake = { generateReconstruction() { return { response: makeValidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(Array.isArray(result.relationships)).toBe(true);
|
|
expect(result.relationships.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("preserves possibleFollowUpQuestions from provider response", () => {
|
|
const fake = { generateReconstruction() { return { response: makeValidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(Array.isArray(result.possibleFollowUpQuestions)).toBe(true);
|
|
expect(result.possibleFollowUpQuestions.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("preserves caller target identity (result.targetNodeId === request targetNodeId)", () => {
|
|
const fake = { generateReconstruction() { return { response: makeValidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(result.targetNodeId).toBe(FAKE_TARGET_NODE_ID);
|
|
});
|
|
|
|
it("passes correct prompt to provider", () => {
|
|
let capturedPrompt = null;
|
|
const fake = { generateReconstruction(prompt) { capturedPrompt = prompt; return { response: makeValidResponse() }; } };
|
|
focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
const expected = buildFocusedDeconstructPrompt({ targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(capturedPrompt).toBe(expected);
|
|
});
|
|
|
|
it("passes modelName unchanged to provider", () => {
|
|
let capturedModel = null;
|
|
const fake = { generateReconstruction(prompt, receivedModel) { capturedModel = receivedModel; return { response: makeValidResponse() }; } };
|
|
focusedDeconstruct({ provider: fake, modelName: "my-custom-model-v2", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(capturedModel).toBe("my-custom-model-v2");
|
|
});
|
|
|
|
it("passes focusedDeconstructJsonSchema as the schema argument", () => {
|
|
let capturedSchema = null;
|
|
const fake = { generateReconstruction(prompt, modelName, schema) { capturedSchema = schema; return { response: makeValidResponse() }; } };
|
|
focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(capturedSchema).toBe(focusedDeconstructJsonSchema);
|
|
});
|
|
})
|
|
|
|
describe("focusedDeconstruct — validation failure", () => {
|
|
it("returns failure result when provider response fails canonical validation", () => {
|
|
const fake = { generateReconstruction() { return { response: makeInvalidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(result.success).toBe(false);
|
|
});
|
|
|
|
it("validation errors remain available on failure result", () => {
|
|
const fake = { generateReconstruction() { return { response: makeInvalidResponse() }; } };
|
|
const result = focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(Array.isArray(result.errors)).toBe(true);
|
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("no second provider call occurs on validation failure", () => {
|
|
let callCount = 0;
|
|
const fake = { generateReconstruction() { callCount++; return { response: makeInvalidResponse() }; } };
|
|
focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER });
|
|
expect(callCount).toBe(1);
|
|
});
|
|
})
|
|
|
|
describe("focusedDeconstruct — provider failure", () => {
|
|
it("propagates error with original identity/metadata intact", () => {
|
|
const fake = {
|
|
generateReconstruction() {
|
|
const err = new Error("Connection refused to ollama endpoint");
|
|
err.name = "ProviderError";
|
|
err.providerApiPath = "/api/v1/completions";
|
|
err.code = "PROVIDER_UNAVAILABLE";
|
|
throw err;
|
|
},
|
|
};
|
|
expect(() => focusedDeconstruct({ provider: fake, modelName: "test-model", targetNodeId: FAKE_TARGET_NODE_ID, targetLabel: FAKE_LABEL, targetDescription: FAKE_DESCRIPTION, centralStatement: FAKE_CENTRAL, question: FAKE_QUESTION, answer: FAKE_ANSWER })).toThrow("Connection refused to ollama endpoint");
|
|
});
|
|
})
|