Skip to content

Commit 12103ea

Browse files
committed
test(data_modeling): add TestInstanceSort and TestQueryIterSorts
1 parent 08bfda9 commit 12103ea

1 file changed

Lines changed: 144 additions & 0 deletions

File tree

tests/tests_unit/test_data_classes/test_data_models/test_instances.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import warnings
34
from datetime import date, datetime
45
from typing import Any, cast
56

@@ -12,6 +13,7 @@
1213
EdgeApply,
1314
EdgeList,
1415
Float64,
16+
InstanceSort,
1517
Node,
1618
NodeApply,
1719
NodeId,
@@ -638,3 +640,145 @@ def test_to_pandas(self) -> None:
638640
df = info.to_pandas()
639641

640642
pd.testing.assert_frame_equal(df, expected)
643+
644+
645+
class TestInstanceSort:
646+
def test_nulls_first_stays_none_when_omitted(self) -> None:
647+
sort = InstanceSort(["node", "externalId"], direction="ascending")
648+
assert sort.nulls_first is None
649+
sort = InstanceSort(["node", "externalId"], direction="descending")
650+
assert sort.nulls_first is None
651+
652+
def test_no_warning_at_construction_for_any_combination(self) -> None:
653+
with warnings.catch_warnings(record=True) as w:
654+
warnings.simplefilter("always")
655+
InstanceSort(["node", "externalId"], direction="ascending")
656+
InstanceSort(["node", "externalId"], direction="descending")
657+
InstanceSort(["node", "externalId"], direction="ascending", nulls_first=False)
658+
InstanceSort(["node", "externalId"], direction="ascending", nulls_first=True)
659+
InstanceSort(["node", "externalId"], direction="descending", nulls_first=True)
660+
InstanceSort(["node", "externalId"], direction="descending", nulls_first=False)
661+
assert len(w) == 0
662+
663+
@pytest.mark.parametrize(
664+
"direction, nulls_first",
665+
[
666+
("ascending", True),
667+
("descending", False),
668+
],
669+
)
670+
def test_non_index_aligned_nulls_first_is_stored_as_given(self, direction: str, nulls_first: bool) -> None:
671+
sort = InstanceSort(["node", "externalId"], direction=direction, nulls_first=nulls_first) # type: ignore[arg-type]
672+
assert sort.nulls_first is nulls_first
673+
674+
def test_load_does_not_override_nulls_first(self) -> None:
675+
raw = {"property": ["node", "externalId"], "direction": "ascending", "nullsFirst": True}
676+
sort = InstanceSort._load(raw)
677+
assert sort.direction == "ascending"
678+
assert sort.nulls_first is True
679+
680+
def test_dump_omits_nulls_first_when_none(self) -> None:
681+
sort = InstanceSort(["space", "externalId"], direction="descending")
682+
dumped = sort.dump(camel_case=True)
683+
assert dumped["direction"] == "descending"
684+
assert "nullsFirst" not in dumped
685+
686+
@pytest.mark.parametrize(
687+
"direction, nulls_first, expected",
688+
[
689+
("ascending", False, True),
690+
("descending", True, True),
691+
("ascending", True, False),
692+
("descending", False, False),
693+
("ascending", None, True),
694+
("descending", None, True),
695+
],
696+
)
697+
def test_is_index_aligned(self, direction: str, nulls_first: bool | None, expected: bool) -> None:
698+
sort = InstanceSort(["node", "externalId"], direction=direction, nulls_first=nulls_first) # type: ignore[arg-type]
699+
assert sort.is_index_aligned is expected
700+
701+
def test__apply_postgres_defaults_or_maybe_warn_resolves_none(self) -> None:
702+
sort = InstanceSort(["node", "externalId"], direction="ascending")
703+
sort._apply_postgres_defaults_or_maybe_warn()
704+
assert sort.nulls_first is False
705+
sort = InstanceSort(["node", "externalId"], direction="descending")
706+
sort._apply_postgres_defaults_or_maybe_warn()
707+
assert sort.nulls_first is True
708+
709+
@pytest.mark.parametrize("bad_direction", ["asc", "desc", "random"])
710+
def test_invalid_direction_raises(self, bad_direction: str) -> None:
711+
with pytest.raises(ValueError, match="direction must be"):
712+
InstanceSort(["node", "externalId"], direction=bad_direction) # type: ignore[arg-type]
713+
714+
def test_invalid_direction_error_shows_original_value(self) -> None:
715+
with pytest.raises(ValueError, match="'Asc'"):
716+
InstanceSort(["node", "externalId"], direction="Asc") # type: ignore[arg-type]
717+
718+
@pytest.mark.parametrize(
719+
"direction, nulls_first",
720+
[
721+
("ascending", True),
722+
("descending", False),
723+
],
724+
)
725+
def test_warn_fires_for_non_index_aligned_sorts(self, direction: str, nulls_first: bool) -> None:
726+
sort = InstanceSort(["node", "externalId"], direction=direction, nulls_first=nulls_first) # type: ignore[arg-type]
727+
with warnings.catch_warnings(record=True) as w:
728+
warnings.simplefilter("always")
729+
sort._apply_postgres_defaults_or_maybe_warn()
730+
assert len(w) == 1
731+
assert issubclass(w[0].category, UserWarning)
732+
assert "not index-aligned" in str(w[0].message)
733+
assert "index utilization" in str(w[0].message)
734+
735+
@pytest.mark.parametrize(
736+
"direction, nulls_first",
737+
[
738+
("ascending", False),
739+
("descending", True),
740+
("ascending", None),
741+
("descending", None),
742+
],
743+
)
744+
def test_no_warn_for_aligned_or_unset_sorts(self, direction: str, nulls_first: bool | None) -> None:
745+
sort = InstanceSort(["node", "externalId"], direction=direction, nulls_first=nulls_first) # type: ignore[arg-type]
746+
with warnings.catch_warnings(record=True) as w:
747+
warnings.simplefilter("always")
748+
sort._apply_postgres_defaults_or_maybe_warn()
749+
assert len(w) == 0
750+
751+
752+
class TestQueryIterSorts:
753+
def test_collects_sorts_from_with_and_select(self) -> None:
754+
from cognite.client.data_classes.data_modeling import ViewId
755+
from cognite.client.data_classes.data_modeling.query import (
756+
NodeResultSetExpression,
757+
Query,
758+
Select,
759+
SourceSelector,
760+
)
761+
762+
view = ViewId("s", "v", "1")
763+
sort_a = InstanceSort(view.as_property_ref("a"))
764+
sort_b = InstanceSort(view.as_property_ref("b"))
765+
query = Query(
766+
with_={"nodes": NodeResultSetExpression(sort=[sort_a])},
767+
select={"nodes": Select([SourceSelector(view, ["a"])], sort=[sort_b])},
768+
)
769+
result = list(query._iter_sorts())
770+
assert sort_a in result
771+
assert sort_b in result
772+
assert len(result) == 2
773+
774+
def test_collects_backfill_sort_from_sync_query(self) -> None:
775+
from cognite.client.data_classes.data_modeling.query import (
776+
NodeResultSetExpressionSync,
777+
QuerySync,
778+
)
779+
780+
sort = InstanceSort(["node", "externalId"])
781+
query = QuerySync(with_={"nodes": NodeResultSetExpressionSync(backfill_sort=[sort])}, select={})
782+
result = list(query._iter_sorts())
783+
assert sort in result
784+
assert len(result) == 1

0 commit comments

Comments
 (0)