"""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, 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(): assert _bounds("1996") == ("1996-01-01", "1996-12-31") def test_bounds_quarter_q1(): assert _bounds("1996-Q1") == ("1996-01-01", "1996-03-31") def test_bounds_quarter_q3(): assert _bounds("1996-Q3") == ("1996-07-01", "1996-09-30") def test_bounds_unsupported_raises(): with pytest.raises(PickValidationError, match="unsupported"): _bounds("Q3-1996") def test_resolve_real_date_column(): col = Column(name="date", sql_type="DATE", nullable=False) 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 = _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_via_default_encoding(): col = Column(name="date", sql_type="INTEGER", nullable=False, semantic_type="date_yymmdd") 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, _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})"