/**
 * `profile-update.personFields` is now an open `Record<string,
 * unknown>` validated centrally by the schema-validator.
 *
 * These tests cover the wiring on `profileUpdate(...)` itself — proving
 * that synonym + Forbidden Properties rejection fires at the tool surface,
 * not just inside the validator unit. A future refactor that drops the
 * `validateWrite(...)` call from `profile-update.ts` would silently bypass
 * the central enforcement; these tests catch that.
 *
 * Validation runs in `mode: "update"` and throws BEFORE getSession() /
 * embed() are called (the defensive ordering), so these tests do
 * not need to mock neo4j-driver.
 */
import { describe, it, expect } from "vitest";
import { profileUpdate } from "../profile-update.js";
const ACCT = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
const USER = "user-893-validation";
function buildValidator() {
    const schema = {
        markdownLabels: new Map([
            [
                "Person",
                {
                    label: "Person",
                    required: [
                        { kind: "simple", name: "givenName" },
                        { kind: "simple", name: "familyName" },
                        { kind: "any-of", names: ["email", "telephone"] },
                    ],
                    sourceFile: "schema-base.md",
                },
            ],
        ]),
        wrongToCorrect: new Map([
            ["phone", { correct: "telephone", sourceFile: "schema-base.md" }],
            ["firstName", { correct: "givenName", sourceFile: "schema-base.md" }],
        ]),
        forbiddenByLabel: new Map([
            [
                "Person",
                new Map([
                    [
                        "name",
                        {
                            rationale: "use givenName + familyName",
                            sourceFile: "schema-base.md",
                        },
                    ],
                ]),
            ],
        ]),
        sourceFiles: ["schema-base.md"],
        brandVertical: null,
    };
    const liveSource = {
        recognisedLabels: () => new Set(["Person", "AdminUser"]),
        liveLabels: () => new Set(["Person", "AdminUser"]),
        declaredLabels: () => new Set(["Person", "AdminUser"]),
        liveReady: () => true,
    };
    return { schema, liveSource, emit: () => { } };
}
describe("profile-update — personFields open Record + central validator", () => {
    it("rejects synonym key (`phone`) before opening any session", async () => {
        await expect(profileUpdate({
            accountId: ACCT,
            userId: USER,
            category: "communication",
            key: "preferredChannel",
            value: "whatsapp",
            personFields: { phone: "+447111111111" },
            validator: buildValidator(),
        })).rejects.toThrow(/property name 'phone' is not Schema\.org — use 'telephone'/);
    });
    it("rejects Forbidden Property `name` before opening any session", async () => {
        await expect(profileUpdate({
            accountId: ACCT,
            userId: USER,
            category: "communication",
            key: "preferredChannel",
            value: "whatsapp",
            personFields: { name: "Joel Smalley" },
            validator: buildValidator(),
        })).rejects.toThrow(/'name' is not a valid Person property/);
    });
    it("loud-fails when personFields is non-empty but no validator is supplied", async () => {
        // The tool surface (index.ts) closure-captures the validator at
        // startup; a regression that drops it at the call site must throw
        // rather than silently skip validation.
        await expect(profileUpdate({
            accountId: ACCT,
            userId: USER,
            category: "communication",
            key: "preferredChannel",
            value: "whatsapp",
            personFields: { homeLocation: "London" },
            // validator deliberately omitted
        })).rejects.toThrow(/`personFields` requires the schema validator/);
    });
    it("loud-fails when LLM tries to overwrite system-managed Person properties (account-isolation guard)", async () => {
        // Direct attack: agent passes `accountId: <other>` hoping `SET p +=
        // $fields` will silently overwrite the row's account scope. The
        // tool surface must reject before any Neo4j touch.
        await expect(profileUpdate({
            accountId: ACCT,
            userId: USER,
            category: "communication",
            key: "preferredChannel",
            value: "whatsapp",
            personFields: { accountId: "ffffffff-ffff-ffff-ffff-ffffffffffff" },
            validator: buildValidator(),
        })).rejects.toThrow(/'accountId' is a system-managed Person property/);
        // Same check for provenance-stamp tampering.
        await expect(profileUpdate({
            accountId: ACCT,
            userId: USER,
            category: "communication",
            key: "preferredChannel",
            value: "whatsapp",
            personFields: { createdByAgent: "system" },
            validator: buildValidator(),
        })).rejects.toThrow(/'createdByAgent' is a system-managed Person property/);
    });
    it("personFields with all-empty-string values is treated as a no-op (no validator call required)", async () => {
        // Hygiene strips empty strings BEFORE validate. If after hygiene
        // there's nothing left, validation is skipped — no validator
        // requirement, no throw, no Neo4j touch.
        //
        // We use category=communication + a non-empty Preference value so
        // the function passes its base validations; then asserts that the
        // hygiene-empty personFields does not trigger the validator-missing
        // throw. The function will then proceed past the synchronous block
        // into the Neo4j path which is unreachable in this test environment;
        // we expect any thrown error to NOT be the "requires the schema
        // validator" error.
        await expect(profileUpdate({
            accountId: ACCT,
            userId: USER,
            category: "communication",
            key: "preferredChannel",
            value: "whatsapp",
            personFields: { homeLocation: "", knowsLanguage: "   " },
            // validator deliberately omitted — hygiene drops both keys, so
            // the validator is never called.
        })).rejects.not.toThrow(/requires the schema validator/);
    });
});
//# sourceMappingURL=profile-update-personfields-open.test.js.map