refactor composer to use sqlalchemy, avoid string concatenations and follow conventions
This commit is contained in:
@@ -1,10 +1,25 @@
|
||||
"""Date-range resolver tests — bounds parsing + dispatch on column type."""
|
||||
"""Date-range resolver tests — bounds parsing + dispatch on column type/encoding.
|
||||
|
||||
`resolve_date_range` now returns an `exp` node and takes the effective encoding
|
||||
map (built-in defaults overlaid with any dataset overrides). Tests render the
|
||||
node to SQL to assert on the predicate.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from sqlglot import exp
|
||||
|
||||
from api.composer.dates import _bounds, resolve_date_range
|
||||
from api.composer.encodings import DEFAULT_ENCODINGS, effective_encodings
|
||||
from api.composer.types import PickValidationError
|
||||
from api.recon.types import Column
|
||||
from api.recon.types import Column, Recon
|
||||
|
||||
|
||||
def _col(name: str = "date", table: str = "l") -> exp.Column:
|
||||
return exp.column(name, table=table, quoted=True)
|
||||
|
||||
|
||||
def _sql(node: exp.Expression) -> str:
|
||||
return node.sql(dialect="postgres", identify=True)
|
||||
|
||||
|
||||
def test_bounds_year():
|
||||
@@ -26,25 +41,56 @@ def test_bounds_unsupported_raises():
|
||||
|
||||
def test_resolve_real_date_column():
|
||||
col = Column(name="date", sql_type="DATE", nullable=False)
|
||||
out = resolve_date_range("1996", col, '"l"."date"')
|
||||
out = _sql(resolve_date_range("1996", col, _col(), {}))
|
||||
assert out == "\"l\".\"date\" BETWEEN '1996-01-01' AND '1996-12-31'"
|
||||
|
||||
|
||||
def test_resolve_timestamp_column():
|
||||
col = Column(name="created_at", sql_type="TIMESTAMP WITHOUT TIME ZONE", nullable=True)
|
||||
out = resolve_date_range("1996-Q2", col, '"x"."created_at"')
|
||||
out = _sql(resolve_date_range("1996-Q2", col, _col("created_at", "x"), {}))
|
||||
assert out == "\"x\".\"created_at\" BETWEEN '1996-04-01' AND '1996-06-30'"
|
||||
|
||||
|
||||
def test_resolve_yymmdd_semantic_type():
|
||||
def test_resolve_yymmdd_via_default_encoding():
|
||||
col = Column(name="date", sql_type="INTEGER", nullable=False,
|
||||
semantic_type="date_yymmdd")
|
||||
out = resolve_date_range("1996", col, '"l"."date"')
|
||||
out = _sql(resolve_date_range("1996", col, _col(), DEFAULT_ENCODINGS))
|
||||
assert "TO_DATE(LPAD(" in out
|
||||
assert "BETWEEN '1996-01-01' AND '1996-12-31'" in out
|
||||
|
||||
|
||||
def test_dataset_encoding_overrides_default():
|
||||
# A dataset can override a built-in encoding with its own coercion SQL.
|
||||
col = Column(name="d", sql_type="INTEGER", nullable=False,
|
||||
semantic_type="date_yymmdd")
|
||||
override = {"date_yymmdd": {"as_date": "MAKE_DATE(1900 + {col} / 10000, 1, 1)"}}
|
||||
out = _sql(resolve_date_range("1996", col, _col("d"), override))
|
||||
assert "MAKE_DATE(" in out
|
||||
assert "TO_DATE" not in out
|
||||
|
||||
|
||||
def test_resolve_semantic_type_without_encoding_raises():
|
||||
# Declared semantic_type but no matching encoding and not a native date type.
|
||||
col = Column(name="d", sql_type="INTEGER", nullable=False,
|
||||
semantic_type="date_yymmdd")
|
||||
with pytest.raises(PickValidationError, match="not supported"):
|
||||
resolve_date_range("1996", col, _col("d"), {})
|
||||
|
||||
|
||||
def test_resolve_unsupported_column_type_raises():
|
||||
col = Column(name="x", sql_type="TEXT", nullable=True)
|
||||
with pytest.raises(PickValidationError, match="not supported"):
|
||||
resolve_date_range("1996", col, '"x"."x"')
|
||||
resolve_date_range("1996", col, _col("x", "x"), {})
|
||||
|
||||
|
||||
def test_effective_encodings_merges_defaults_with_dataset():
|
||||
recon = Recon(schema="t", tables={}, metrics={},
|
||||
encodings={"my_epoch": {"as_date": "TO_TIMESTAMP({col})::date"}})
|
||||
eff = effective_encodings(recon)
|
||||
assert "date_yymmdd" in eff # default survives
|
||||
assert eff["my_epoch"]["as_date"].startswith("TO_TIMESTAMP") # dataset added
|
||||
|
||||
# Dataset entry wins on key collision.
|
||||
recon2 = Recon(schema="t", tables={}, metrics={},
|
||||
encodings={"date_yymmdd": {"as_date": "CUSTOM({col})"}})
|
||||
assert effective_encodings(recon2)["date_yymmdd"]["as_date"] == "CUSTOM({col})"
|
||||
|
||||
Reference in New Issue
Block a user