recon + sqlglot validator + drill_down package; guard ReAct dimension picks against candidate list
This commit is contained in:
106
tests/test_recon.py
Normal file
106
tests/test_recon.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""Regression tests for the recon package — type round-trips, join_path,
|
||||
and column_to_tables index."""
|
||||
|
||||
from api.recon.types import Recon, Relationship, Table, Column, Metric
|
||||
|
||||
|
||||
def _sample_recon() -> Recon:
|
||||
cols_account = [
|
||||
Column("account_id", "BIGINT", False),
|
||||
Column("district_id", "BIGINT", True),
|
||||
]
|
||||
cols_district = [
|
||||
Column("district_id", "BIGINT", False),
|
||||
Column("A2", "TEXT", True),
|
||||
Column("A11", "BIGINT", True),
|
||||
]
|
||||
cols_loan = [
|
||||
Column("loan_id", "BIGINT", False),
|
||||
Column("account_id", "BIGINT", True),
|
||||
Column("amount", "BIGINT", True),
|
||||
Column("status", "TEXT", True),
|
||||
]
|
||||
rels = [
|
||||
Relationship("account", "district_id", "district", "district_id"),
|
||||
Relationship("loan", "account_id", "account", "account_id"),
|
||||
]
|
||||
c2t = {
|
||||
"account_id": sorted(["account", "loan"]),
|
||||
"district_id": sorted(["account", "district"]),
|
||||
"A2": ["district"],
|
||||
"A11": ["district"],
|
||||
"loan_id": ["loan"],
|
||||
"amount": ["loan"],
|
||||
"status": ["loan"],
|
||||
}
|
||||
return Recon(
|
||||
schema="financial",
|
||||
tables={
|
||||
"account": Table("account", "Accounts", cols_account),
|
||||
"district": Table("district", "Districts", cols_district),
|
||||
"loan": Table("loan", "Loans", cols_loan),
|
||||
},
|
||||
metrics={
|
||||
"loan_volume": Metric("loan_volume", "Total CZK lent",
|
||||
"SUM(amount)", "loan", None, "CZK"),
|
||||
},
|
||||
column_to_tables=c2t,
|
||||
relationships=rels,
|
||||
)
|
||||
|
||||
|
||||
def test_owning_tables():
|
||||
r = _sample_recon()
|
||||
assert r.owning_tables("A2") == ["district"]
|
||||
assert r.owning_tables("amount") == ["loan"]
|
||||
assert r.owning_tables("account_id") == ["account", "loan"]
|
||||
assert r.owning_tables("nonsense") == []
|
||||
|
||||
|
||||
def test_join_path_direct():
|
||||
r = _sample_recon()
|
||||
assert r.join_path("loan", "account") == ["loan", "account"]
|
||||
assert r.join_path("account", "district") == ["account", "district"]
|
||||
|
||||
|
||||
def test_join_path_multi_hop():
|
||||
r = _sample_recon()
|
||||
assert r.join_path("loan", "district") == ["loan", "account", "district"]
|
||||
|
||||
|
||||
def test_join_path_same():
|
||||
r = _sample_recon()
|
||||
assert r.join_path("loan", "loan") == ["loan"]
|
||||
|
||||
|
||||
def test_join_path_unknown_returns_none():
|
||||
r = _sample_recon()
|
||||
assert r.join_path("loan", "does_not_exist") is None
|
||||
|
||||
|
||||
def test_recon_to_dict_from_dict_roundtrip():
|
||||
r = _sample_recon()
|
||||
d = r.to_dict()
|
||||
r2 = Recon.from_dict(d)
|
||||
assert r2.schema == r.schema
|
||||
assert sorted(r2.tables) == sorted(r.tables)
|
||||
assert sorted(r2.metrics) == sorted(r.metrics)
|
||||
assert r2.column_to_tables == r.column_to_tables
|
||||
assert len(r2.relationships) == len(r.relationships)
|
||||
assert r2.join_path("loan", "district") == ["loan", "account", "district"]
|
||||
|
||||
|
||||
def test_relationship_parse_dotted():
|
||||
r = Relationship.parse({"from": "loan.account_id", "to": "account.account_id"})
|
||||
assert r.from_table == "loan"
|
||||
assert r.from_column == "account_id"
|
||||
assert r.to_table == "account"
|
||||
assert r.to_column == "account_id"
|
||||
|
||||
|
||||
def test_relationship_parse_explicit():
|
||||
r = Relationship.parse({
|
||||
"from_table": "loan", "from_column": "account_id",
|
||||
"to_table": "account", "to_column": "account_id",
|
||||
})
|
||||
assert r.from_table == "loan"
|
||||
Reference in New Issue
Block a user