123 lines
4.2 KiB
Python
123 lines
4.2 KiB
Python
"""Pick / Filter / OrderBy validation — round-trips and rejection cases."""
|
|
|
|
import pytest
|
|
|
|
from api.composer.types import Filter, OrderBy, Pick, PickValidationError
|
|
|
|
|
|
# ── Filter ──────────────────────────────────────────────────
|
|
|
|
def test_filter_equals_roundtrip():
|
|
f = Filter.from_dict({"column": "status", "equals": "B"})
|
|
assert f.kind() == "equals"
|
|
assert f.to_dict() == {"column": "status", "equals": "B"}
|
|
|
|
|
|
def test_filter_in_values_roundtrip():
|
|
f = Filter.from_dict({"column": "status", "in_values": ["A", "B"]})
|
|
assert f.kind() == "in_values"
|
|
assert f.to_dict() == {"column": "status", "in_values": ["A", "B"]}
|
|
|
|
|
|
def test_filter_between_normalised_to_tuple():
|
|
f = Filter.from_dict({"column": "amount", "between": [1000, 5000]})
|
|
assert f.kind() == "between"
|
|
assert f.between == (1000, 5000)
|
|
# round-trip emits a list, not a tuple
|
|
assert f.to_dict() == {"column": "amount", "between": [1000, 5000]}
|
|
|
|
|
|
def test_filter_date_range_roundtrip():
|
|
f = Filter.from_dict({"column": "date", "date_range": "1996"})
|
|
assert f.kind() == "date_range"
|
|
assert f.to_dict() == {"column": "date", "date_range": "1996"}
|
|
|
|
|
|
def test_filter_missing_value_raises():
|
|
with pytest.raises(PickValidationError, match="no value field"):
|
|
Filter(column="x").kind()
|
|
|
|
|
|
def test_filter_multiple_value_fields_raises():
|
|
f = Filter(column="x", equals="A", in_values=["A", "B"])
|
|
with pytest.raises(PickValidationError, match="multiple value fields"):
|
|
f.kind()
|
|
|
|
|
|
def test_filter_missing_column_raises():
|
|
with pytest.raises(PickValidationError, match="missing 'column'"):
|
|
Filter.from_dict({"equals": "B"})
|
|
|
|
|
|
def test_filter_between_wrong_arity_raises():
|
|
with pytest.raises(PickValidationError, match="2-element"):
|
|
Filter.from_dict({"column": "x", "between": [1, 2, 3]})
|
|
|
|
|
|
# ── OrderBy ─────────────────────────────────────────────────
|
|
|
|
def test_order_by_metric():
|
|
ob = OrderBy.from_dict({"by": "metric", "direction": "desc"})
|
|
assert ob.by == "metric"
|
|
assert ob.direction == "desc"
|
|
assert ob.to_dict() == {"by": "metric", "direction": "desc"}
|
|
|
|
|
|
def test_order_by_dimension_requires_dimension():
|
|
with pytest.raises(PickValidationError, match="requires order_by.dimension"):
|
|
OrderBy.from_dict({"by": "dimension"})
|
|
|
|
|
|
def test_order_by_invalid_direction_raises():
|
|
with pytest.raises(PickValidationError, match="direction"):
|
|
OrderBy.from_dict({"by": "metric", "direction": "sideways"})
|
|
|
|
|
|
# ── Pick ────────────────────────────────────────────────────
|
|
|
|
def test_pick_minimal():
|
|
p = Pick.from_dict({"kind": "aggregate", "metric": "loan_volume"})
|
|
assert p.metric == "loan_volume"
|
|
assert p.group_by == []
|
|
assert p.where == []
|
|
assert p.order_by is None
|
|
assert p.limit is None
|
|
|
|
|
|
def test_pick_full_roundtrip():
|
|
raw = {
|
|
"kind": "aggregate",
|
|
"metric": "loan_default_rate",
|
|
"group_by": ["A2"],
|
|
"where": [{"column": "date", "date_range": "1996"}],
|
|
"order_by": {"by": "metric", "direction": "desc"},
|
|
"limit": 10,
|
|
}
|
|
p = Pick.from_dict(raw)
|
|
assert p.to_dict() == raw
|
|
|
|
|
|
def test_pick_rejects_non_aggregate_kind():
|
|
with pytest.raises(PickValidationError, match="aggregate"):
|
|
Pick.from_dict({"kind": "ratio", "metric": "x"})
|
|
|
|
|
|
def test_pick_missing_metric_raises():
|
|
with pytest.raises(PickValidationError, match="metric"):
|
|
Pick.from_dict({"kind": "aggregate"})
|
|
|
|
|
|
def test_pick_group_by_must_be_list_of_strings():
|
|
with pytest.raises(PickValidationError, match="group_by"):
|
|
Pick.from_dict({"kind": "aggregate", "metric": "x", "group_by": [1, 2]})
|
|
|
|
|
|
def test_pick_where_must_be_list():
|
|
with pytest.raises(PickValidationError, match="where must be a list"):
|
|
Pick.from_dict({"kind": "aggregate", "metric": "x", "where": {"foo": "bar"}})
|
|
|
|
|
|
def test_pick_limit_must_be_int():
|
|
with pytest.raises(PickValidationError, match="limit"):
|
|
Pick.from_dict({"kind": "aggregate", "metric": "x", "limit": "ten"})
|