Skip to content

Commit 05f399e

Browse files
suyask-msftclaude
andcommitted
Add @odata.bind casing tests for _create() and _update() paths
Cover the two write paths that were missing test coverage for @odata.bind key preservation: _create() (POST) and _update() (PATCH). Each verifies that regular keys are lowercased while @odata.bind keys retain their original navigation property casing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e26dfbc commit 05f399e

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

tests/unit/data/test_odata_internal.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,98 @@ def test_select_bare_string_raises_type_error(self):
291291
self.assertIn("list of property names", str(ctx.exception))
292292

293293

294+
class TestCreate(unittest.TestCase):
295+
"""Unit tests for _ODataClient._create."""
296+
297+
def setUp(self):
298+
self.od = _make_odata_client()
299+
# Mock response with OData-EntityId header containing a GUID
300+
mock_resp = MagicMock()
301+
mock_resp.headers = {
302+
"OData-EntityId": "https://example.crm.dynamics.com/api/data/v9.2/accounts(00000000-0000-0000-0000-000000000001)"
303+
}
304+
self.od._request.return_value = mock_resp
305+
306+
def _post_call(self):
307+
"""Return the single POST call args from _request."""
308+
post_calls = [c for c in self.od._request.call_args_list if c.args[0] == "post"]
309+
self.assertEqual(len(post_calls), 1, "expected exactly one POST call")
310+
return post_calls[0]
311+
312+
def test_record_keys_lowercased(self):
313+
"""Regular record field names are lowercased before sending."""
314+
self.od._create("accounts", "account", {"Name": "Contoso", "AccountNumber": "ACC-001"})
315+
call = self._post_call()
316+
payload = call.kwargs["json"]
317+
self.assertIn("name", payload)
318+
self.assertIn("accountnumber", payload)
319+
self.assertNotIn("Name", payload)
320+
self.assertNotIn("AccountNumber", payload)
321+
322+
def test_odata_bind_keys_preserve_case(self):
323+
"""@odata.bind keys preserve navigation property casing in _create."""
324+
self.od._create(
325+
"new_tickets",
326+
"new_ticket",
327+
{
328+
"new_name": "Ticket 1",
329+
"new_CustomerId@odata.bind": "/contacts(00000000-0000-0000-0000-000000000001)",
330+
"new_AgentId@odata.bind": "/systemusers(00000000-0000-0000-0000-000000000002)",
331+
},
332+
)
333+
call = self._post_call()
334+
payload = call.kwargs["json"]
335+
self.assertIn("new_name", payload)
336+
self.assertIn("new_CustomerId@odata.bind", payload)
337+
self.assertIn("new_AgentId@odata.bind", payload)
338+
self.assertNotIn("new_customerid@odata.bind", payload)
339+
self.assertNotIn("new_agentid@odata.bind", payload)
340+
341+
def test_returns_guid(self):
342+
"""_create returns the GUID from the OData-EntityId header."""
343+
result = self.od._create("accounts", "account", {"name": "Contoso"})
344+
self.assertEqual(result, "00000000-0000-0000-0000-000000000001")
345+
346+
347+
class TestUpdate(unittest.TestCase):
348+
"""Unit tests for _ODataClient._update."""
349+
350+
def setUp(self):
351+
self.od = _make_odata_client()
352+
# _update needs _entity_set_from_schema_name to resolve entity set
353+
self.od._entity_set_from_schema_name = MagicMock(return_value="new_tickets")
354+
355+
def _patch_call(self):
356+
"""Return the single PATCH call args from _request."""
357+
patch_calls = [c for c in self.od._request.call_args_list if c.args[0] == "patch"]
358+
self.assertEqual(len(patch_calls), 1, "expected exactly one PATCH call")
359+
return patch_calls[0]
360+
361+
def test_record_keys_lowercased(self):
362+
"""Regular field names are lowercased in _update."""
363+
self.od._update("new_ticket", "00000000-0000-0000-0000-000000000001", {"New_Status": 100000001})
364+
call = self._patch_call()
365+
payload = call.kwargs["json"]
366+
self.assertIn("new_status", payload)
367+
self.assertNotIn("New_Status", payload)
368+
369+
def test_odata_bind_keys_preserve_case(self):
370+
"""@odata.bind keys preserve navigation property casing in _update."""
371+
self.od._update(
372+
"new_ticket",
373+
"00000000-0000-0000-0000-000000000001",
374+
{
375+
"new_status": 100000001,
376+
"new_CustomerId@odata.bind": "/contacts(00000000-0000-0000-0000-000000000002)",
377+
},
378+
)
379+
call = self._patch_call()
380+
payload = call.kwargs["json"]
381+
self.assertIn("new_status", payload)
382+
self.assertIn("new_CustomerId@odata.bind", payload)
383+
self.assertNotIn("new_customerid@odata.bind", payload)
384+
385+
294386
class TestUpsert(unittest.TestCase):
295387
"""Unit tests for _ODataClient._upsert."""
296388

0 commit comments

Comments
 (0)