// Task 465 — :Section.body is server-derived from offsets, not agent-supplied.
//
// The classifier emits (sourceStart, sourceEnd) into the cached text. The
// server slices that text to produce :Section.body so a hallucinated /
// paraphrased / summarised body cannot reach Neo4j.
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { deriveSectionBodies } from "../memory-ingest.js";
describe("deriveSectionBodies", () => {
    let logSpy;
    beforeEach(() => {
        logSpy = vi.spyOn(console, "error").mockImplementation(() => { });
    });
    afterEach(() => {
        logSpy.mockRestore();
    });
    it("(a) returns server-derived slices on the happy path (multi-section)", () => {
        const cachedText = "Section One body.\nSection Two body.\nSection Three body.";
        const sections = [
            { sourceStart: 0, sourceEnd: 17 },
            { sourceStart: 18, sourceEnd: 35 },
            { sourceStart: 36, sourceEnd: 55 },
        ];
        const result = deriveSectionBodies(sections, cachedText, "att-happy");
        expect(result).toEqual([
            "Section One body.",
            "Section Two body.",
            "Section Three body.",
        ]);
        expect(logSpy).not.toHaveBeenCalled();
    });
    it("(b) returns empty string and warns on a zero-width section", () => {
        const cachedText = "Some text.";
        const sections = [{ sourceStart: 5, sourceEnd: 5 }];
        const result = deriveSectionBodies(sections, cachedText, "att-zero");
        expect(result).toEqual([""]);
        expect(logSpy).toHaveBeenCalledTimes(1);
        expect(logSpy.mock.calls[0][0]).toContain("zero-width section");
        expect(logSpy.mock.calls[0][0]).toContain("sectionIndex=0");
        expect(logSpy.mock.calls[0][0]).toContain("att-zero");
    });
    it("(c) throws on reversed offsets (sourceStart > sourceEnd)", () => {
        const cachedText = "Some text.";
        const sections = [{ sourceStart: 7, sourceEnd: 3 }];
        expect(() => deriveSectionBodies(sections, cachedText, "att-rev")).toThrow(/reversed offsets.*sectionIndex=0.*sourceStart=7.*sourceEnd=3.*att-rev/);
    });
    it("(d) throws on out-of-bounds sourceEnd", () => {
        const cachedText = "Short text."; // length 11
        const sections = [{ sourceStart: 0, sourceEnd: 100 }];
        expect(() => deriveSectionBodies(sections, cachedText, "att-oob")).toThrow(/out-of-bounds sourceEnd.*sectionIndex=0.*sourceEnd=100.*textLength=11/);
    });
    it("(d2) throws on negative sourceStart", () => {
        const cachedText = "Some text.";
        const sections = [{ sourceStart: -1, sourceEnd: 5 }];
        expect(() => deriveSectionBodies(sections, cachedText, "att-neg")).toThrow(/invalid sourceStart.*sourceStart=-1.*att-neg/);
    });
    it("(e) logs and ignores an agent-supplied body — returns slice, not agent text", () => {
        const cachedText = "Real document content here.";
        const agentBody = "HALLUCINATED SUMMARY THAT MUST NOT BE STORED";
        const sections = [{ sourceStart: 0, sourceEnd: 27, body: agentBody }];
        const result = deriveSectionBodies(sections, cachedText, "att-hall");
        expect(result).toEqual(["Real document content here."]);
        expect(result[0]).not.toContain("HALLUCINATED");
        expect(logSpy).toHaveBeenCalledTimes(1);
        expect(logSpy.mock.calls[0][0]).toContain("agent body suppressed");
        expect(logSpy.mock.calls[0][0]).toContain(`agentBodyLength=${agentBody.length}`);
        expect(logSpy.mock.calls[0][0]).toContain("agentBodyPreview=");
        expect(logSpy.mock.calls[0][0]).toContain("HALLUCINATED");
        expect(logSpy.mock.calls[0][0]).toContain("att-hall");
    });
    it("(f) correctly slices each section in a multi-section document with gaps", () => {
        const cachedText = "AAA\n\nBBB\n\nCCC";
        const sections = [
            { sourceStart: 0, sourceEnd: 3 },
            { sourceStart: 5, sourceEnd: 8 },
            { sourceStart: 10, sourceEnd: 13 },
        ];
        const result = deriveSectionBodies(sections, cachedText, "att-multi");
        expect(result).toEqual(["AAA", "BBB", "CCC"]);
    });
    it("includes the failing sectionIndex when one of several sections is invalid", () => {
        const cachedText = "Hello world.";
        const sections = [
            { sourceStart: 0, sourceEnd: 5 },
            { sourceStart: 10, sourceEnd: 6 }, // reversed
        ];
        expect(() => deriveSectionBodies(sections, cachedText, "att-mix")).toThrow(/sectionIndex=1/);
    });
    it("uses default sourceKey='unknown' when omitted (still produces a grep-able log)", () => {
        const cachedText = "abc";
        const sections = [{ sourceStart: 0, sourceEnd: 0 }];
        deriveSectionBodies(sections, cachedText);
        expect(logSpy.mock.calls[0][0]).toContain("sourceKey=unknown");
    });
});
//# sourceMappingURL=memory-ingest-body-server-sliced.test.js.map