API: parser de filtres et génération SQL paramétré (#78)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import polars as pl
|
||||
import pytest
|
||||
|
||||
from src.api.filters import FilterError, build_where
|
||||
|
||||
SCHEMA = pl.Schema(
|
||||
{
|
||||
"uid": pl.String,
|
||||
"objet": pl.String,
|
||||
"montant": pl.Float64,
|
||||
"annee": pl.Int64,
|
||||
"dateNotification": pl.Date,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_no_filters_returns_true():
|
||||
where, params, order = build_where([], SCHEMA)
|
||||
assert where == "TRUE"
|
||||
assert params == []
|
||||
assert order is None
|
||||
|
||||
|
||||
def test_exact_filter():
|
||||
where, params, _ = build_where([("uid__exact", "abc")], SCHEMA)
|
||||
assert where == '"uid" = ?'
|
||||
assert params == ["abc"]
|
||||
|
||||
|
||||
def test_contains_filter_uses_like_wildcards():
|
||||
where, params, _ = build_where([("objet__contains", "informatique")], SCHEMA)
|
||||
assert where == '"objet" LIKE ?'
|
||||
assert params == ["%informatique%"]
|
||||
|
||||
|
||||
def test_notcontains_filter():
|
||||
where, params, _ = build_where([("objet__notcontains", "x")], SCHEMA)
|
||||
assert where == '"objet" NOT LIKE ?'
|
||||
assert params == ["%x%"]
|
||||
|
||||
|
||||
def test_comparison_operators_on_int():
|
||||
where, params, _ = build_where([("annee__strictly_greater", "2023")], SCHEMA)
|
||||
assert where == '"annee" > ?'
|
||||
assert params == [2024 - 1] # int coercion: 2023
|
||||
|
||||
|
||||
def test_in_filter_splits_on_commas():
|
||||
where, params, _ = build_where([("annee__in", "2022,2023,2024")], SCHEMA)
|
||||
assert where == '"annee" IN (?,?,?)'
|
||||
assert params == [2022, 2023, 2024]
|
||||
|
||||
|
||||
def test_notin_filter():
|
||||
where, params, _ = build_where([("annee__notin", "2020,2021")], SCHEMA)
|
||||
assert where == '"annee" NOT IN (?,?)'
|
||||
assert params == [2020, 2021]
|
||||
|
||||
|
||||
def test_isnull_filter_ignores_value():
|
||||
where, params, _ = build_where([("dateNotification__isnull", "anything")], SCHEMA)
|
||||
assert where == '"dateNotification" IS NULL'
|
||||
assert params == []
|
||||
|
||||
|
||||
def test_isnotnull_filter():
|
||||
where, params, _ = build_where([("dateNotification__isnotnull", "")], SCHEMA)
|
||||
assert where == '"dateNotification" IS NOT NULL'
|
||||
|
||||
|
||||
def test_multiple_filters_joined_by_and():
|
||||
where, params, _ = build_where(
|
||||
[("uid__exact", "a"), ("annee__greater", "2020")], SCHEMA
|
||||
)
|
||||
assert where == '"uid" = ? AND "annee" >= ?'
|
||||
assert params == ["a", 2020]
|
||||
|
||||
|
||||
def test_unknown_column_raises():
|
||||
with pytest.raises(FilterError) as exc:
|
||||
build_where([("foo__exact", "bar")], SCHEMA)
|
||||
assert "foo" in str(exc.value)
|
||||
assert exc.value.field == "foo__exact"
|
||||
|
||||
|
||||
def test_unknown_operator_raises():
|
||||
with pytest.raises(FilterError) as exc:
|
||||
build_where([("uid__weird", "x")], SCHEMA)
|
||||
assert "weird" in str(exc.value)
|
||||
|
||||
|
||||
def test_bad_int_value_raises():
|
||||
with pytest.raises(FilterError):
|
||||
build_where([("annee__exact", "notanint")], SCHEMA)
|
||||
|
||||
|
||||
def test_bad_date_value_raises():
|
||||
with pytest.raises(FilterError):
|
||||
build_where([("dateNotification__exact", "notadate")], SCHEMA)
|
||||
|
||||
|
||||
def test_date_iso_coercion():
|
||||
where, params, _ = build_where(
|
||||
[("dateNotification__greater", "2024-01-01")], SCHEMA
|
||||
)
|
||||
from datetime import date
|
||||
|
||||
assert params == [date(2024, 1, 1)]
|
||||
|
||||
|
||||
def test_reserved_params_are_ignored():
|
||||
where, params, order = build_where(
|
||||
[
|
||||
("page", "2"),
|
||||
("page_size", "100"),
|
||||
("columns", "uid"),
|
||||
("count", "false"),
|
||||
("uid__exact", "z"),
|
||||
],
|
||||
SCHEMA,
|
||||
)
|
||||
assert where == '"uid" = ?'
|
||||
assert params == ["z"]
|
||||
|
||||
|
||||
def test_sort_returns_order_by():
|
||||
where, params, order = build_where(
|
||||
[("annee__sort", "desc"), ("uid__sort", "asc")], SCHEMA
|
||||
)
|
||||
assert where == "TRUE"
|
||||
assert order == '"annee" DESC, "uid" ASC'
|
||||
|
||||
|
||||
def test_sort_invalid_direction_raises():
|
||||
with pytest.raises(FilterError):
|
||||
build_where([("uid__sort", "sideways")], SCHEMA)
|
||||
|
||||
|
||||
def test_param_without_operator_raises():
|
||||
with pytest.raises(FilterError):
|
||||
build_where([("uidexact", "x")], SCHEMA)
|
||||
Reference in New Issue
Block a user