Skip to content

Commit 5c2a17c

Browse files
author
Abel Milash
committed
Add edge-case tests for QueryBuilder and QueryOperations
1 parent 8c5ac8d commit 5c2a17c

2 files changed

Lines changed: 115 additions & 2 deletions

File tree

tests/unit/models/test_query_builder.py

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,13 @@ def test_filter_in_returns_self(self):
125125
qb = QueryBuilder("account")
126126
self.assertIs(qb.filter_in("statecode", [0, 1]), qb)
127127

128-
def test_filter_in_accepts_set(self):
128+
def test_filter_in_with_set(self):
129129
qb = QueryBuilder("account").filter_in("statecode", {0, 1})
130130
result = qb.build()["filter"]
131131
self.assertIn("Microsoft.Dynamics.CRM.In", result)
132132
self.assertIn("statecode", result)
133133

134-
def test_filter_in_accepts_tuple(self):
134+
def test_filter_in_with_tuple(self):
135135
qb = QueryBuilder("account").filter_in("statecode", (0, 1, 2))
136136
self.assertEqual(
137137
qb.build()["filter"],
@@ -297,6 +297,19 @@ def test_filter_not_in_combined_with_other_filters(self):
297297
'statecode eq 0 and Microsoft.Dynamics.CRM.NotIn(PropertyName=\'priority\',PropertyValues=["1","2"])',
298298
)
299299

300+
def test_filter_not_in_with_set(self):
301+
qb = QueryBuilder("account").filter_not_in("statecode", {2, 3})
302+
result = qb.build()["filter"]
303+
self.assertIn("Microsoft.Dynamics.CRM.NotIn", result)
304+
self.assertIn("statecode", result)
305+
306+
def test_filter_not_in_with_tuple(self):
307+
qb = QueryBuilder("account").filter_not_in("statecode", (2, 3))
308+
self.assertEqual(
309+
qb.build()["filter"],
310+
'Microsoft.Dynamics.CRM.NotIn(PropertyName=\'statecode\',PropertyValues=["2","3"])',
311+
)
312+
300313

301314
class TestFilterNotBetween(unittest.TestCase):
302315
"""Tests for the filter_not_between() method."""
@@ -506,6 +519,38 @@ def test_expand_mixed_strings_and_options(self):
506519
["primarycontactid", "Account_Tasks($select=subject)"],
507520
)
508521

522+
def test_expand_option_chained_select_accumulates(self):
523+
"""Calling select() multiple times should accumulate columns."""
524+
from PowerPlatform.Dataverse.models.query_builder import ExpandOption
525+
526+
opt = ExpandOption("Account_Tasks").select("subject").select("createdon")
527+
self.assertEqual(
528+
opt.to_odata(),
529+
"Account_Tasks($select=subject,createdon)",
530+
)
531+
532+
def test_expand_option_multiple_order_by(self):
533+
"""Calling order_by() multiple times should accumulate sort clauses."""
534+
from PowerPlatform.Dataverse.models.query_builder import ExpandOption
535+
536+
opt = (
537+
ExpandOption("Account_Tasks").select("subject").order_by("priority", descending=True).order_by("createdon")
538+
)
539+
self.assertEqual(
540+
opt.to_odata(),
541+
"Account_Tasks($select=subject;$orderby=priority desc,createdon)",
542+
)
543+
544+
def test_expand_option_filter_last_wins(self):
545+
"""Calling filter() multiple times should use the last value."""
546+
from PowerPlatform.Dataverse.models.query_builder import ExpandOption
547+
548+
opt = ExpandOption("Account_Tasks").filter("statecode eq 0").filter("contains(subject,'Task')")
549+
self.assertEqual(
550+
opt.to_odata(),
551+
"Account_Tasks($filter=contains(subject,'Task'))",
552+
)
553+
509554

510555
class TestCount(unittest.TestCase):
511556
"""Tests for the count() method."""
@@ -556,6 +601,19 @@ def test_include_annotations_returns_self(self):
556601
qb = QueryBuilder("account")
557602
self.assertIs(qb.include_annotations(), qb)
558603

604+
def test_include_annotations_overrides_formatted_values(self):
605+
"""Last annotation call should win."""
606+
qb = QueryBuilder("account").include_formatted_values().include_annotations("*")
607+
self.assertEqual(qb.build()["include_annotations"], "*")
608+
609+
def test_include_formatted_values_overrides_annotations(self):
610+
"""Last annotation call should win (reverse order)."""
611+
qb = QueryBuilder("account").include_annotations("*").include_formatted_values()
612+
self.assertEqual(
613+
qb.build()["include_annotations"],
614+
"OData.Community.Display.V1.FormattedValue",
615+
)
616+
559617

560618
class TestBuild(unittest.TestCase):
561619
"""Tests for the build() method."""
@@ -860,6 +918,31 @@ def test_to_dataframe_returns_dataframe(self):
860918
self.assertEqual(len(result), 2)
861919
self.assertListEqual(list(result.columns), ["name", "revenue"])
862920

921+
def test_to_dataframe_forwards_count_and_annotations(self):
922+
"""to_dataframe() should forward count and include_annotations when set."""
923+
import pandas as pd
924+
925+
mock_query_ops = MagicMock()
926+
mock_client = mock_query_ops._client
927+
mock_client.dataframe.get.return_value = pd.DataFrame()
928+
929+
qb = QueryBuilder("account")
930+
qb._query_ops = mock_query_ops
931+
qb.count().include_formatted_values()
932+
qb.to_dataframe()
933+
934+
mock_client.dataframe.get.assert_called_once_with(
935+
"account",
936+
select=None,
937+
filter=None,
938+
orderby=None,
939+
top=None,
940+
expand=None,
941+
page_size=None,
942+
count=True,
943+
include_annotations="OData.Community.Display.V1.FormattedValue",
944+
)
945+
863946

864947
if __name__ == "__main__":
865948
unittest.main()

tests/unit/test_query_operations.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,36 @@ def test_builder_full_fluent_workflow(self):
246246
self.assertEqual(records[0]["name"], "Big Corp")
247247
self.assertEqual(records[1]["name"], "Mega Inc")
248248

249+
def test_builder_to_dataframe(self):
250+
"""builder().to_dataframe() should delegate to client.dataframe.get()."""
251+
import pandas as pd
252+
253+
expected_df = pd.DataFrame([{"name": "Contoso", "revenue": 1000}])
254+
self.client.dataframe = MagicMock()
255+
self.client.dataframe.get.return_value = expected_df
256+
257+
result = (
258+
self.client.query.builder("account")
259+
.select("name", "revenue")
260+
.filter_eq("statecode", 0)
261+
.order_by("name")
262+
.top(50)
263+
.to_dataframe()
264+
)
265+
266+
self.client.dataframe.get.assert_called_once_with(
267+
"account",
268+
select=["name", "revenue"],
269+
filter="statecode eq 0",
270+
orderby=["name"],
271+
top=50,
272+
expand=None,
273+
page_size=None,
274+
count=False,
275+
include_annotations=None,
276+
)
277+
pd.testing.assert_frame_equal(result, expected_df)
278+
249279

250280
if __name__ == "__main__":
251281
unittest.main()

0 commit comments

Comments
 (0)