""" Unit tests for SQL Agent, AST/Regex security guardrails, and read-only validator. """ from __future__ import annotations import pytest from agents.sql_agent import is_safe_sql, sql_node from agents.state import CopilotState def test_is_safe_sql_valid_selects(): """Verify valid SELECT queries pass security check.""" q1 = "SELECT * FROM users WHERE role = 'admin'" safe, msg = is_safe_sql(q1) assert safe is True q2 = "WITH total_sales AS (SELECT SUM(amount) as s FROM orders) SELECT * FROM total_sales" safe, msg = is_safe_sql(q2) assert safe is True def test_is_safe_sql_blocks_dml_ddl(): """Verify mutating queries (DROP, DELETE, UPDATE, INSERT) are blocked.""" forbidden_queries = [ "DROP TABLE users;", "DELETE FROM audit_logs;", "UPDATE users SET role = 'admin';", "INSERT INTO tenants (name) VALUES ('Hacked');", "ALTER TABLE users ADD COLUMN secret text;", "TRUNCATE TABLE documents;" ] for q in forbidden_queries: safe, msg = is_safe_sql(q) assert safe is False, f"Failed to block: {q}" assert "Security Violation" in msg from unittest.mock import patch, AsyncMock from app.services.llm_gateway import LLMResponse @pytest.mark.asyncio async def test_sql_node_execution(): """Test SQL Node execution flow with safe query.""" state: CopilotState = { "query": "How many users are in the system?", "user_role": "admin", "tenant_id": "test-tenant" } mock_resp = LLMResponse( content="SELECT * FROM users", provider="groq", model="llama3-8b-8192", tokens_used=10, latency_ms=50.0 ) with patch("agents.sql_agent.llm_gateway.generate", new_callable=AsyncMock, return_value=mock_resp), \ patch("agents.sql_agent.get_database_schema", new_callable=AsyncMock, return_value="Table: users(id, email)"), \ patch("agents.sql_agent.execute_sql_query", new_callable=AsyncMock, return_value=(["count"], [{"count": 10}])): res_state = await sql_node(state) assert "retrieved_chunks" in res_state assert res_state.get("active_agent") == "sql"