forked from sagnik/Project_Velocity
fix: Oracle Canvas Metadata and deterministic semantic repair
This commit is contained in:
168
backend/tests/oracle/test_natural_db_semantics.py
Normal file
168
backend/tests/oracle/test_natural_db_semantics.py
Normal file
@@ -0,0 +1,168 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
|
||||
|
||||
def test_semantic_context_exposes_qd_score_type_values():
|
||||
from oracle.semantic_catalog import VALID_QD_SCORE_TYPES, build_semantic_context_for_planner
|
||||
|
||||
context = build_semantic_context_for_planner(["qd_score"])
|
||||
|
||||
assert "score_type" in context
|
||||
assert "QD" in context
|
||||
for score_type in VALID_QD_SCORE_TYPES:
|
||||
assert score_type in context
|
||||
|
||||
|
||||
def test_verifier_rejects_impossible_qd_score_type_filter():
|
||||
from oracle.plan_verifier import plan_verifier
|
||||
|
||||
sql = """
|
||||
SELECT p.full_name, q.current_value
|
||||
FROM intel_qd_scores q
|
||||
JOIN crm_people p ON p.person_id = q.person_id
|
||||
WHERE q.score_type = 'QD'
|
||||
ORDER BY q.current_value DESC
|
||||
LIMIT 8
|
||||
"""
|
||||
|
||||
result = plan_verifier.verify(sql, "Give me top QD clients", ["qd_score"], 8)
|
||||
|
||||
assert not result.passed
|
||||
assert any(violation.rule == "impossible_score_type" for violation in result.violations)
|
||||
|
||||
|
||||
def test_verifier_allows_valid_qd_score_type_filter():
|
||||
from oracle.plan_verifier import plan_verifier
|
||||
|
||||
sql = """
|
||||
SELECT p.full_name, q.current_value AS qd_score
|
||||
FROM intel_qd_scores q
|
||||
JOIN crm_people p ON p.person_id = q.person_id
|
||||
WHERE q.score_type = 'overall'
|
||||
ORDER BY q.current_value DESC
|
||||
LIMIT 8
|
||||
"""
|
||||
|
||||
result = plan_verifier.verify(sql, "Give me top QD clients", ["qd_score"], 8)
|
||||
|
||||
assert result.passed
|
||||
|
||||
|
||||
def test_verifier_semantically_repairs_bad_qd_column_even_if_llm_repair_repeats_it():
|
||||
import asyncio
|
||||
|
||||
from oracle.plan_verifier import plan_verifier
|
||||
|
||||
broken_sql = """
|
||||
SELECT p.full_name, p.qd_score
|
||||
FROM crm_people p
|
||||
ORDER BY p.qd_score DESC
|
||||
LIMIT 8
|
||||
"""
|
||||
|
||||
class BadRepairService:
|
||||
async def chat(self, **kwargs):
|
||||
return {"message": {"parsedJson": {"sql": broken_sql}}}
|
||||
|
||||
result = asyncio.run(
|
||||
plan_verifier.verify_and_repair(
|
||||
broken_sql,
|
||||
"Give me the top eight clients which has the highest QD Score",
|
||||
["qd_score"],
|
||||
50,
|
||||
BadRepairService(),
|
||||
)
|
||||
)
|
||||
|
||||
assert result.passed
|
||||
assert result.was_repaired
|
||||
assert "intel_qd_scores" in result.sql
|
||||
assert "q.score_type = 'overall'" in result.sql
|
||||
assert "p.qd_score" not in result.sql
|
||||
assert "LIMIT 8" in result.sql
|
||||
|
||||
|
||||
def test_verifier_semantic_qd_repair_preserves_lowest_project_scope():
|
||||
import asyncio
|
||||
|
||||
from oracle.plan_verifier import plan_verifier
|
||||
|
||||
broken_sql = """
|
||||
SELECT p.full_name, p.qd_score
|
||||
FROM crm_people p
|
||||
ORDER BY p.qd_score ASC
|
||||
LIMIT 50
|
||||
"""
|
||||
|
||||
class BadRepairService:
|
||||
async def chat(self, **kwargs):
|
||||
return {"message": {"parsedJson": {"sql": broken_sql}}}
|
||||
|
||||
result = asyncio.run(
|
||||
plan_verifier.verify_and_repair(
|
||||
broken_sql,
|
||||
"Which five clients have the lowest QD Scores in Atri Surya Toron?",
|
||||
["qd_score"],
|
||||
50,
|
||||
BadRepairService(),
|
||||
)
|
||||
)
|
||||
|
||||
assert result.passed
|
||||
assert "crm_property_interests" in result.sql
|
||||
assert "pi.project_name ILIKE '%Atri Surya Toron%'" in result.sql
|
||||
assert "ORDER BY q.current_value ASC" in result.sql
|
||||
assert "LIMIT 5" in result.sql
|
||||
|
||||
|
||||
def test_verifier_rejects_legacy_recency_columns_for_contact_prompts():
|
||||
from oracle.plan_verifier import plan_verifier
|
||||
|
||||
sql = """
|
||||
SELECT p.full_name, max(e.timestamp) AS last_contacted_at
|
||||
FROM edge_communication_events e
|
||||
JOIN crm_people p ON p.person_id = e.person_id
|
||||
WHERE e.timestamp >= now() - interval '3 months'
|
||||
GROUP BY p.full_name
|
||||
LIMIT 5
|
||||
"""
|
||||
|
||||
result = plan_verifier.verify(sql, "Who contacted us recently?", ["last_contacted"], 5)
|
||||
|
||||
assert not result.passed
|
||||
assert any(violation.rule == "deprecated_timestamp" for violation in result.violations)
|
||||
|
||||
|
||||
def test_verifier_repairs_contact_prompt_to_live_last_contact_column():
|
||||
import asyncio
|
||||
|
||||
from oracle.plan_verifier import plan_verifier
|
||||
|
||||
broken_sql = """
|
||||
SELECT p.full_name, lc.last_contacted_at
|
||||
FROM read_last_contacted lc
|
||||
JOIN crm_people p ON p.person_id = lc.person_id
|
||||
WHERE lc.last_contacted_at >= NOW() - INTERVAL '3 months'
|
||||
LIMIT 10
|
||||
"""
|
||||
|
||||
class BadRepairService:
|
||||
async def chat(self, **kwargs):
|
||||
return {"message": {"parsedJson": {"sql": broken_sql}}}
|
||||
|
||||
result = asyncio.run(
|
||||
plan_verifier.verify_and_repair(
|
||||
broken_sql,
|
||||
"Who are the clients who contacted us in last three months which are showing most interest?",
|
||||
["last_contacted", "qd_score"],
|
||||
50,
|
||||
BadRepairService(),
|
||||
)
|
||||
)
|
||||
|
||||
assert result.passed
|
||||
assert "lc.last_contact_at" in result.sql
|
||||
assert "lc.last_contacted_at" not in result.sql
|
||||
assert "INTERVAL '3 months'" in result.sql
|
||||
Reference in New Issue
Block a user