// @vitest-environment node
import { describe, it, expect, beforeEach, vi } from "vitest";
function makeSession(runResults) {
    let i = 0;
    return {
        run: vi.fn().mockImplementation(async () => {
            const res = runResults[i++] ?? { records: [] };
            return res;
        }),
        close: vi.fn().mockResolvedValue(undefined),
    };
}
function ownerOk(id) {
    return { records: [{ get: (_) => id }] };
}
function ownerFail() {
    return { records: [] };
}
function mergeResult(created) {
    return {
        records: [{ get: (_) => ({}) }],
        summary: {
            counters: {
                updates: () => ({ relationshipsCreated: created, relationshipsDeleted: 0 }),
            },
        },
    };
}
function deleteResult(deleted) {
    return {
        records: [],
        summary: {
            counters: {
                updates: () => ({ relationshipsCreated: 0, relationshipsDeleted: deleted }),
            },
        },
    };
}
const ACCOUNT_ID = "acct-1";
const FROM_ID = "4:eid:node-a";
const TO_ID = "4:eid:node-b";
let mockSession;
vi.mock("../../lib/neo4j.js", () => ({
    getSession: () => mockSession,
}));
import { memoryEdge } from "../memory-edge.js";
beforeEach(() => {
    vi.clearAllMocks();
    vi.spyOn(process.stderr, "write").mockImplementation(() => true);
});
describe("memory-edge — create", () => {
    it("creates edge on first call — returned created: true", async () => {
        mockSession = makeSession([ownerOk(FROM_ID), ownerOk(TO_ID), mergeResult(1)]);
        const result = await memoryEdge({
            action: "create",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "RELATES_TO",
            accountId: ACCOUNT_ID,
        });
        expect(result).toEqual({ created: true });
        expect(mockSession.run).toHaveBeenCalledTimes(3);
    });
    it("idempotent on second call — returned created: false", async () => {
        mockSession = makeSession([ownerOk(FROM_ID), ownerOk(TO_ID), mergeResult(0)]);
        const result = await memoryEdge({
            action: "create",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "RELATES_TO",
            accountId: ACCOUNT_ID,
        });
        expect(result).toEqual({ created: false });
    });
    it("uppercases the relationship type in the Cypher query", async () => {
        mockSession = makeSession([ownerOk(FROM_ID), ownerOk(TO_ID), mergeResult(1)]);
        await memoryEdge({
            action: "create",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "relates_to",
            accountId: ACCOUNT_ID,
        });
        const mergeCall = mockSession.run.mock.calls[2][0];
        expect(mergeCall).toContain("`RELATES_TO`");
    });
});
describe("memory-edge — delete", () => {
    it("deletes a present edge — returned deleted: true", async () => {
        mockSession = makeSession([ownerOk(FROM_ID), ownerOk(TO_ID), deleteResult(1)]);
        const result = await memoryEdge({
            action: "delete",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "RELATES_TO",
            accountId: ACCOUNT_ID,
        });
        expect(result).toEqual({ deleted: true });
    });
    it("absent edge is a no-op — returned deleted: false", async () => {
        mockSession = makeSession([ownerOk(FROM_ID), ownerOk(TO_ID), deleteResult(0)]);
        const result = await memoryEdge({
            action: "delete",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "RELATES_TO",
            accountId: ACCOUNT_ID,
        });
        expect(result).toEqual({ deleted: false });
    });
});
describe("memory-edge — account ownership", () => {
    it("rejects when fromId does not belong to account — foreign-node error, no mutation", async () => {
        mockSession = makeSession([ownerFail()]);
        const result = await memoryEdge({
            action: "create",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "RELATES_TO",
            accountId: ACCOUNT_ID,
        });
        expect(result).toEqual({ error: "foreign-node", elementId: FROM_ID });
        expect(mockSession.run).toHaveBeenCalledTimes(1);
    });
    it("rejects when toId does not belong to account — foreign-node error, no mutation", async () => {
        mockSession = makeSession([ownerOk(FROM_ID), ownerFail()]);
        const result = await memoryEdge({
            action: "create",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "RELATES_TO",
            accountId: ACCOUNT_ID,
        });
        expect(result).toEqual({ error: "foreign-node", elementId: TO_ID });
        expect(mockSession.run).toHaveBeenCalledTimes(2);
    });
});
describe("memory-edge — reserved type guard", () => {
    it("rejects underscore-prefixed type before opening a session", async () => {
        mockSession = makeSession([]);
        const result = await memoryEdge({
            action: "create",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "_INTERNAL",
            accountId: ACCOUNT_ID,
        });
        expect(result).toEqual({ error: "reserved-type", type: "_INTERNAL" });
        expect(mockSession.run).not.toHaveBeenCalled();
    });
    it("uppercase-coerces before reserved check — _soft_delete is also rejected", async () => {
        mockSession = makeSession([]);
        const result = await memoryEdge({
            action: "create",
            fromId: FROM_ID,
            toId: TO_ID,
            relationshipType: "_soft_delete",
            accountId: ACCOUNT_ID,
        });
        expect(result).toEqual({ error: "reserved-type", type: "_SOFT_DELETE" });
        expect(mockSession.run).not.toHaveBeenCalled();
    });
});
//# sourceMappingURL=memory-edge.test.js.map