"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_test_1 = __importDefault(require("node:test"));
const strict_1 = __importDefault(require("node:assert/strict"));
const cypher_validate_js_1 = require("../cypher-validate.js");
const snapshot = {
    labels: new Set(["Person", "Organization", "Task", "KnowledgeDocument"]),
    relationshipTypes: new Set(["KNOWS", "PARTICIPANT", "REFERENCES", "MENTIONS", "PART_OF"]),
};
(0, node_test_1.default)("write mode allows CREATE node + relationship statement", () => {
    const result = (0, cypher_validate_js_1.validate)("MATCH (a:Person), (b:Organization) WHERE a.name = $a AND b.name = $b CREATE (a)-[:KNOWS]->(b)", snapshot, { mode: "write" });
    strict_1.default.equal(result.ok, true, "expected valid write");
    strict_1.default.deepEqual(result.unknown, []);
    strict_1.default.deepEqual(result.forbidden, []);
});
(0, node_test_1.default)("write mode allows MERGE with SET clause", () => {
    const result = (0, cypher_validate_js_1.validate)("MATCH (a:Person {name: $name}) MERGE (a)-[r:KNOWS]->(b:Person {name: $other}) SET r.createdAt = datetime()", snapshot, { mode: "write" });
    strict_1.default.equal(result.ok, true);
});
(0, node_test_1.default)("write mode allows DETACH DELETE", () => {
    const result = (0, cypher_validate_js_1.validate)("MATCH (n:Person) WHERE n.archived = true DETACH DELETE n", snapshot, { mode: "write" });
    strict_1.default.equal(result.ok, true);
});
(0, node_test_1.default)("write mode allows REMOVE label + SET label (re-label)", () => {
    // Allow renaming labels — the new label is unknown to the schema until the
    // write commits, but the validator must not block legitimate normalisation.
    const result = (0, cypher_validate_js_1.validate)("MATCH (n:Person) WHERE n.legacy = true REMOVE n:Person SET n:LegacyPerson", snapshot, { mode: "write" });
    // LegacyPerson is unknown — fail-soft for write mode (audit emits warning, not reject).
    // The token check is shared with read-mode; the write mode adds DDL/admin reject layer.
    // For this test we accept the write-mode-specific outcome: no forbidden patterns.
    strict_1.default.deepEqual(result.forbidden, []);
});
(0, node_test_1.default)("write mode allows CALL apoc.refactor.mergeNodes", () => {
    const result = (0, cypher_validate_js_1.validate)("MATCH (a:Person), (b:Person) WHERE a.email = b.email AND id(a) < id(b) WITH a, b CALL apoc.refactor.mergeNodes([a, b]) YIELD node RETURN node", snapshot, { mode: "write" });
    strict_1.default.deepEqual(result.forbidden, []);
});
(0, node_test_1.default)("write mode REJECTS DROP DATABASE", () => {
    const result = (0, cypher_validate_js_1.validate)("DROP DATABASE neo4j", snapshot, { mode: "write" });
    strict_1.default.equal(result.ok, false);
    strict_1.default.equal(result.forbidden.length, 1);
    strict_1.default.equal(result.forbidden[0].kind, "drop-database");
});
(0, node_test_1.default)("write mode REJECTS CREATE INDEX", () => {
    const result = (0, cypher_validate_js_1.validate)("CREATE INDEX person_name_idx FOR (n:Person) ON (n.name)", snapshot, { mode: "write" });
    strict_1.default.equal(result.ok, false);
    const f = result.forbidden.find((x) => x.kind === "create-index");
    strict_1.default.ok(f, "expected create-index forbidden");
});
(0, node_test_1.default)("write mode REJECTS CREATE CONSTRAINT", () => {
    const result = (0, cypher_validate_js_1.validate)("CREATE CONSTRAINT person_email_unique FOR (n:Person) REQUIRE n.email IS UNIQUE", snapshot, { mode: "write" });
    strict_1.default.equal(result.ok, false);
    const f = result.forbidden.find((x) => x.kind === "create-constraint");
    strict_1.default.ok(f);
});
(0, node_test_1.default)("write mode REJECTS CALL dbms.security.*", () => {
    const result = (0, cypher_validate_js_1.validate)("CALL dbms.security.createUser('attacker', 'pwd', false)", snapshot, { mode: "write" });
    strict_1.default.equal(result.ok, false);
    const f = result.forbidden.find((x) => x.kind === "call-dbms");
    strict_1.default.ok(f);
});
(0, node_test_1.default)("write mode REJECTS CALL db.create*", () => {
    const result = (0, cypher_validate_js_1.validate)("CALL db.createLabel('Foo')", snapshot, { mode: "write" });
    strict_1.default.equal(result.ok, false);
    const f = result.forbidden.find((x) => x.kind === "call-db-create");
    strict_1.default.ok(f);
});
(0, node_test_1.default)("write mode allows CALL db.labels (read-only db.* call)", () => {
    // db.labels is a read primitive — not a create/drop. Validator only
    // forbids db.create* family; CALL db.labels remains allowed.
    const result = (0, cypher_validate_js_1.validate)("CALL db.labels()", snapshot, { mode: "write" });
    strict_1.default.deepEqual(result.forbidden, []);
});
(0, node_test_1.default)("read mode does NOT apply forbidden checks (DDL pattern in MATCH still allowed)", () => {
    // String literals containing forbidden tokens never trip — DDL forbidden check
    // is mode-gated. Read-mode WHERE clauses comparing on a string with the
    // word DROP must not reject.
    const result = (0, cypher_validate_js_1.validate)("MATCH (n:Person) WHERE n.bio CONTAINS 'DROP DATABASE' RETURN n", snapshot, { mode: "read" });
    strict_1.default.deepEqual(result.forbidden, []);
    strict_1.default.equal(result.ok, true);
});
(0, node_test_1.default)("write mode strips string literals before forbidden check", () => {
    // The forbidden check must use stripStringLiterals so a string property
    // value like 'DROP DATABASE archive' does not trip the rejection.
    const result = (0, cypher_validate_js_1.validate)("MATCH (n:Person) WHERE n.bio CONTAINS 'DROP DATABASE' SET n.flagged = true", snapshot, { mode: "write" });
    strict_1.default.deepEqual(result.forbidden, [], "string-literal content must not trip DDL check");
});
(0, node_test_1.default)("default mode is read (backward compatibility)", () => {
    // Existing call sites pass no mode option — must remain read-mode behavior.
    const result = (0, cypher_validate_js_1.validate)("MATCH (n:Person) RETURN n", snapshot);
    strict_1.default.equal(result.ok, true);
    strict_1.default.equal(Array.isArray(result.forbidden), true);
    strict_1.default.equal(result.forbidden.length, 0);
});
//# sourceMappingURL=cypher-validate-write.test.js.map