"""Tests for `sql_whitelist.validate_read_only`.

This is the security boundary protecting the read-write DuckDB connection
from agent-issued writes — easy to test in isolation, low cost, and the
cost of a regression here is the agent silently mutating a base table.
"""

import pytest

from sql_whitelist import validate_read_only


@pytest.mark.parametrize("query", [
    "SELECT 1",
    "select 1",
    "  SELECT * FROM events LIMIT 5",
    "WITH x AS (SELECT 1) SELECT * FROM x",
    "SHOW TABLES",
    "PRAGMA database_size",
    "EXPLAIN SELECT 1",
    "DESCRIBE events",
    "EXECUTE my_prepared",
    "CREATE TEMP TABLE t AS SELECT 1",
    "CREATE TEMPORARY TABLE t2 (id INTEGER)",
    "CREATE OR REPLACE TEMP VIEW v AS SELECT 1",
    "CREATE OR REPLACE TEMPORARY MACRO m(x) AS x + 1",
    "-- a comment\nSELECT 1",
    "/* multi\nline */ SELECT 1",
    "SELECT 1; SELECT 2",
    "SELECT 1;",
    "",
    "   ",
])
def test_allowed_statements(query):
    validate_read_only(query)


@pytest.mark.parametrize("query", [
    "INSERT INTO events VALUES ('x', 1, 0, '', '', '', '')",
    "UPDATE events SET kind = 1",
    "DELETE FROM events",
    "DROP TABLE events",
    "ALTER TABLE events ADD COLUMN foo VARCHAR",
    "TRUNCATE events",
    "ATTACH 'evil.duckdb'",
    "DETACH nostr",
    "COPY events TO 'file.csv'",
    "BEGIN; INSERT INTO events VALUES (1); COMMIT",
    # CREATE without TEMP touches the persistent catalog — must be rejected.
    "CREATE TABLE x (id INTEGER)",
    "CREATE OR REPLACE TABLE x (id INTEGER)",
    "CREATE VIEW v AS SELECT 1",
    "CREATE INDEX idx ON events(kind)",
    # Multi-statement: any one bad fragment poisons the whole call.
    "SELECT 1; DELETE FROM events",
    "DELETE FROM events; SELECT 1",
])
def test_rejected_statements(query):
    with pytest.raises(ValueError, match="run_sql is read-only"):
        validate_read_only(query)


def test_error_mentions_offending_verb():
    with pytest.raises(ValueError) as excinfo:
        validate_read_only("DELETE FROM events")
    assert "DELETE" in str(excinfo.value)
