experiment: compare evidence and condition scope
This commit is contained in:
@@ -0,0 +1,370 @@
|
||||
/**
|
||||
* Experiment 25A — Tests for assessEvidenceConditionScope.
|
||||
*
|
||||
* Validates scope classification (direct_match, partial_match,
|
||||
* different_timeframe, unrelated, cannot_determine) against explicit
|
||||
* conditions and evidence nodes.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { assessEvidenceConditionScope } from "@/lib/graph/evidence-condition-scope.js";
|
||||
import { buildScenarioFixture } from "@/lib/mocks/scenarios.js";
|
||||
|
||||
/* ── Helpers ─────────────────────────────────────────────── */
|
||||
|
||||
function makeNode(id, text) {
|
||||
return { id, description: text, label: text };
|
||||
}
|
||||
|
||||
const DEMAND_CONDITION = "Credible customer demand exists in Europe";
|
||||
const COMPLIANCE_PRESENT = "The platform currently supports EU data residency requirements";
|
||||
const COMPLIANCE_FUTURE = "European compliance can be achieved within an acceptable time and cost";
|
||||
const VALUE_COST_CONDITION = "The expected market value justifies the cost of entry";
|
||||
|
||||
/* ── direct_match: present-state condition versus present evidence ─── */
|
||||
|
||||
describe("assessEvidenceConditionScope — direct_match", () => {
|
||||
it("returns direct_match for identical compliance subject in present state", () => {
|
||||
const evidence = makeNode(
|
||||
"obs-3",
|
||||
"Our platform does not currently support EU data residency requirements",
|
||||
);
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_PRESENT },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("direct_match");
|
||||
});
|
||||
|
||||
it("returns direct_match for demand condition versus market evidence", () => {
|
||||
const evidence = makeNode(
|
||||
"obs-2",
|
||||
"The European analytics SaaS market is valued at approximately €8B and growing 15% annually",
|
||||
);
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: DEMAND_CONDITION },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("direct_match");
|
||||
});
|
||||
|
||||
it("returns direct_match when both condition and evidence use 'currently'", () => {
|
||||
const evidence = makeNode("t-1", "The platform already supports GDPR requirements and data residency");
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_PRESENT },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("direct_match");
|
||||
});
|
||||
|
||||
it("returns direct_match using the long-investigation compliance example", () => {
|
||||
const fixture = buildScenarioFixture("long", 3);
|
||||
const graphNodes = fixture.situationGraph.nodes;
|
||||
const obs3Node = graphNodes.find((n) => n.id === "obs-3");
|
||||
|
||||
expect(obs3Node).toBeDefined();
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_PRESENT },
|
||||
evidenceNode: obs3Node,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("direct_match");
|
||||
});
|
||||
|
||||
it("returns direct_match for value-cost present-state condition", () => {
|
||||
const evidence = makeNode("v-1", "The total addressable market provides sufficient return on investment");
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: "The platform currently offers sufficient financial viability" },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("direct_match");
|
||||
});
|
||||
});
|
||||
|
||||
/* ── different_timeframe: future condition versus present evidence ─── */
|
||||
|
||||
describe("assessEvidenceConditionScope — different_timeframe", () => {
|
||||
it("returns different_timeframe for compliance future condition versus current evidence", () => {
|
||||
const evidence = makeNode(
|
||||
"obs-3",
|
||||
"Our platform does not currently support EU data residency requirements",
|
||||
);
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_FUTURE },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("different_timeframe");
|
||||
});
|
||||
|
||||
it("returns different_timeframe when evidence describes the present and condition uses 'can be achieved'", () => {
|
||||
const evidence = makeNode("t-2", "Our team currently has no EU regulatory expertise");
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: "Can credible customer demand be achieved in Europe" },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("different_timeframe");
|
||||
});
|
||||
|
||||
it("returns different_timeframe for value_cost future condition versus current evidence", () => {
|
||||
const evidence = makeNode("t-3", "The platform currently has no pricing data for Europe");
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: "Can market value be achieved within acceptable cost" },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("different_timeframe");
|
||||
});
|
||||
});
|
||||
|
||||
/* ── partial_match: feasibility evidence versus future condition ─── */
|
||||
|
||||
describe("assessEvidenceConditionScope — partial_match", () => {
|
||||
it("returns partial_match for compliance feasibility evidence against future condition", () => {
|
||||
const evidence = makeNode(
|
||||
"obs-4",
|
||||
"Achieving compliance would require approximately six months and $500K",
|
||||
);
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_FUTURE },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("partial_match");
|
||||
});
|
||||
|
||||
it("returns partial_match using the long-investigation value/cost example", () => {
|
||||
const fixture = buildScenarioFixture("long", 3);
|
||||
const graphNodes = fixture.situationGraph.nodes;
|
||||
const obs4Node = graphNodes.find((n) => n.id === "obs-4");
|
||||
|
||||
expect(obs4Node).toBeDefined();
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_FUTURE },
|
||||
evidenceNode: obs4Node,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("partial_match");
|
||||
});
|
||||
|
||||
it("returns partial_match for demand feasibility question", () => {
|
||||
const evidence = makeNode("t-4", "Entry would require approximately €2M marketing investment over three years");
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: "Credible customer demand can be achieved within acceptable cost" },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("partial_match");
|
||||
});
|
||||
});
|
||||
|
||||
/* ── unrelated: different subjects ─────────────────────── */
|
||||
|
||||
describe("assessEvidenceConditionScope — unrelated", () => {
|
||||
it("returns unrelated when evidence is about market size but condition is about compliance", () => {
|
||||
const evidence = makeNode(
|
||||
"t-5",
|
||||
"European demand for analytics tools is increasing rapidly",
|
||||
);
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_PRESENT },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("unrelated");
|
||||
});
|
||||
|
||||
it("returns unrelated when both subjects are completely different", () => {
|
||||
const evidence = makeNode("t-6", "The office lease expires in December 2026");
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: DEMAND_CONDITION },
|
||||
evidenceNode: evidence,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("unrelated");
|
||||
});
|
||||
});
|
||||
|
||||
/* ── cannot_determine: missing or unclear input ──────────── */
|
||||
|
||||
describe("assessEvidenceConditionScope — cannot_determine", () => {
|
||||
it("returns cannot_determine when condition is missing", () => {
|
||||
const result = assessEvidenceConditionScope({ evidenceNode: makeNode("t-1", "test") });
|
||||
expect(result.scope).toBe("cannot_determine");
|
||||
});
|
||||
|
||||
it("returns cannot_determine when condition is null", () => {
|
||||
const result = assessEvidenceConditionScope({ condition: null, evidenceNode: makeNode("t-1", "test") });
|
||||
expect(result.scope).toBe("cannot_determine");
|
||||
});
|
||||
|
||||
it("returns cannot_determine when evidenceNode is missing", () => {
|
||||
const result = assessEvidenceConditionScope({ condition: { text: DEMAND_CONDITION } });
|
||||
expect(result.scope).toBe("cannot_determine");
|
||||
});
|
||||
|
||||
it("returns cannot_determine when evidenceNode is null", () => {
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: DEMAND_CONDITION },
|
||||
evidenceNode: null,
|
||||
});
|
||||
expect(result.scope).toBe("cannot_determine");
|
||||
});
|
||||
|
||||
it("returns cannot_determine when condition has empty text", () => {
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: "" },
|
||||
evidenceNode: makeNode("t-1", "test"),
|
||||
});
|
||||
expect(result.scope).toBe("cannot_determine");
|
||||
});
|
||||
|
||||
it("returns cannot_determine when evidenceNode has empty description", () => {
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: DEMAND_CONDITION },
|
||||
evidenceNode: makeNode("t-1", ""),
|
||||
});
|
||||
expect(result.scope).toBe("cannot_determine");
|
||||
});
|
||||
|
||||
it("returns cannot_determine when neither input is provided", () => {
|
||||
const result = assessEvidenceConditionScope({});
|
||||
expect(result.scope).toBe("cannot_determine");
|
||||
});
|
||||
});
|
||||
|
||||
/* ── Deterministic output ───────────────────────────────── */
|
||||
|
||||
describe("assessEvidenceConditionScope — determinism", () => {
|
||||
it("returns identical results for identical inputs across multiple calls", () => {
|
||||
const evidence = makeNode("det-1", "European analytics market valued at €8B");
|
||||
const input = {
|
||||
condition: { text: DEMAND_CONDITION },
|
||||
evidenceNode: evidence,
|
||||
};
|
||||
|
||||
const r1 = assessEvidenceConditionScope(input);
|
||||
const r2 = assessEvidenceConditionScope(input);
|
||||
expect(r1).toEqual(r2);
|
||||
});
|
||||
|
||||
it("deterministic across 5 calls with long-investigation data", () => {
|
||||
const fixture = buildScenarioFixture("long", 3);
|
||||
const graphNodes = fixture.situationGraph.nodes;
|
||||
const obs3Node = graphNodes.find((n) => n.id === "obs-3");
|
||||
|
||||
const input = {
|
||||
condition: { text: COMPLIANCE_PRESENT },
|
||||
evidenceNode: obs3Node,
|
||||
};
|
||||
|
||||
const results = [1, 2, 3, 4, 5].map(() => assessEvidenceConditionScope(input));
|
||||
for (let i = 1; i < results.length; i++) {
|
||||
expect(results[i]).toEqual(results[0]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* ── Input immutability ─────────────────────────────────── */
|
||||
|
||||
describe("assessEvidenceConditionScope — input immutability", () => {
|
||||
it("does not mutate the condition object", () => {
|
||||
const cond = { text: DEMAND_CONDITION };
|
||||
const originalText = cond.text;
|
||||
const evidence = makeNode("imm-1", "European market valued at €8B");
|
||||
|
||||
assessEvidenceConditionScope({ condition: cond, evidenceNode: evidence });
|
||||
expect(cond.text).toBe(originalText);
|
||||
});
|
||||
|
||||
it("does not mutate the evidenceNode object", () => {
|
||||
const node = makeNode("imm-2", "test description");
|
||||
const originalDesc = node.description;
|
||||
const condition = { text: DEMAND_CONDITION };
|
||||
|
||||
assessEvidenceConditionScope({ condition, evidenceNode: node });
|
||||
expect(node.description).toBe(originalDesc);
|
||||
});
|
||||
});
|
||||
|
||||
/* ── Four long-investigation examples from the brief ─────── */
|
||||
|
||||
describe("assessEvidenceConditionScope — required cases from long investigation", () => {
|
||||
it("compliance direct present-state match (condition vs obs-3)", () => {
|
||||
const fixture = buildScenarioFixture("long", 3);
|
||||
const graphNodes = fixture.situationGraph.nodes;
|
||||
const obs3Node = graphNodes.find((n) => n.id === "obs-3");
|
||||
|
||||
expect(obs3Node).toBeDefined();
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_PRESENT },
|
||||
evidenceNode: obs3Node,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("direct_match");
|
||||
});
|
||||
|
||||
it("future feasibility versus current compliance evidence", () => {
|
||||
const fixture = buildScenarioFixture("long", 3);
|
||||
const graphNodes = fixture.situationGraph.nodes;
|
||||
const obs3Node = graphNodes.find((n) => n.id === "obs-3");
|
||||
|
||||
expect(obs3Node).toBeDefined();
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_FUTURE },
|
||||
evidenceNode: obs3Node,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("different_timeframe");
|
||||
});
|
||||
|
||||
it("future feasibility versus implementation evidence (partial_match)", () => {
|
||||
const fixture = buildScenarioFixture("long", 3);
|
||||
const graphNodes = fixture.situationGraph.nodes;
|
||||
const obs4Node = graphNodes.find((n) => n.id === "obs-4");
|
||||
|
||||
expect(obs4Node).toBeDefined();
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: COMPLIANCE_FUTURE },
|
||||
evidenceNode: obs4Node,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("partial_match");
|
||||
});
|
||||
|
||||
it("demand direct_match (obs-2)", () => {
|
||||
const fixture = buildScenarioFixture("long", 3);
|
||||
const graphNodes = fixture.situationGraph.nodes;
|
||||
const obs2Node = graphNodes.find((n) => n.id === "obs-2");
|
||||
|
||||
expect(obs2Node).toBeDefined();
|
||||
|
||||
const result = assessEvidenceConditionScope({
|
||||
condition: { text: DEMAND_CONDITION },
|
||||
evidenceNode: obs2Node,
|
||||
});
|
||||
|
||||
expect(result.scope).toBe("direct_match");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user