1+ import json
2+
13import pytest
24import requests_mock
35import requests
@@ -93,6 +95,14 @@ def test_get_projects_success_no_elements_key(client, requests_mock):
9395
9496 assert projects == mock_response_data
9597
98+ def test_get_projects_success_unexpected_scalar_response (client , monkeypatch ):
99+ """Tests retrieving projects when the API returns a scalar value."""
100+ monkeypatch .setattr (client , "_request" , lambda ** kwargs : "unexpected" )
101+
102+ projects = client .get_projects ()
103+
104+ assert projects == []
105+
96106def test_get_projects_auth_error (client , requests_mock ):
97107 """Tests authentication error during get_projects."""
98108 mock_url = f"{ TEST_BASE_URL } /projects"
@@ -165,6 +175,17 @@ def test_create_project_api_error(client, requests_mock):
165175 with pytest .raises (SysMLV2APIError , match = "Unexpected status code for POST /projects" ):
166176 client .create_project (request_data )
167177
178+ def test_delete_project_success (client , requests_mock ):
179+ """Tests successfully deleting a project."""
180+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } "
181+ requests_mock .delete (mock_url , json = {}, status_code = 200 )
182+
183+ result = client .delete_project (TEST_PROJECT_ID )
184+
185+ assert result == {}
186+ assert requests_mock .last_request .url == mock_url
187+ assert requests_mock .last_request .method == "DELETE"
188+
168189
169190# --- Test Get Element ---
170191
@@ -229,6 +250,24 @@ def test_get_owned_elements_empty(client, requests_mock):
229250
230251 assert owned_elements == []
231252
253+ def test_get_owned_elements_success_list_response (client , requests_mock ):
254+ """Tests retrieving owned elements when the API returns a bare list."""
255+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /commits/{ TEST_COMMIT_ID } /elements/{ TEST_ELEMENT_ID } /owned"
256+ mock_response_data = [{"id" : "owned_elem_1" }]
257+ requests_mock .get (mock_url , json = mock_response_data , status_code = 200 )
258+
259+ owned_elements = client .get_owned_elements (TEST_PROJECT_ID , TEST_ELEMENT_ID , TEST_COMMIT_ID )
260+
261+ assert owned_elements == mock_response_data
262+
263+ def test_get_owned_elements_success_unexpected_scalar_response (client , monkeypatch ):
264+ """Tests retrieving owned elements when the API returns a scalar value."""
265+ monkeypatch .setattr (client , "_request" , lambda ** kwargs : "unexpected" )
266+
267+ owned_elements = client .get_owned_elements (TEST_PROJECT_ID , TEST_ELEMENT_ID , TEST_COMMIT_ID )
268+
269+ assert owned_elements == []
270+
232271# --- Test Create Commit ---
233272
234273def test_create_commit_success (client , requests_mock ):
@@ -245,6 +284,55 @@ def test_create_commit_success(client, requests_mock):
245284 assert requests_mock .last_request .method == "POST"
246285 assert requests_mock .last_request .json () == request_data
247286
287+ def test_create_commit_with_branch_id (client , requests_mock ):
288+ """Tests commit creation with a branch query parameter."""
289+ branch_id = "branch_123"
290+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /commits?branchId={ branch_id } "
291+ request_data = {"message" : "Branch commit" , "parentCommitId" : None }
292+ response_data = {"id" : "branch_commit_id" , ** request_data }
293+ requests_mock .post (mock_url , json = response_data , status_code = 200 )
294+
295+ created_commit = client .create_commit (TEST_PROJECT_ID , request_data , branch_id = branch_id )
296+
297+ assert created_commit == response_data
298+ assert requests_mock .last_request .url == mock_url
299+ assert requests_mock .last_request .method == "POST"
300+ assert requests_mock .last_request .json () == request_data
301+
302+ def test_create_commit_with_replace (client , requests_mock ):
303+ """Tests commit creation with the replace query parameter."""
304+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /commits?replace=true"
305+ request_data = {"message" : "Replace commit" , "parentCommitId" : None }
306+ response_data = {"id" : "replace_commit_id" , ** request_data }
307+ requests_mock .post (mock_url , json = response_data , status_code = 200 )
308+
309+ created_commit = client .create_commit (TEST_PROJECT_ID , request_data , replace = True )
310+
311+ assert created_commit == response_data
312+ assert requests_mock .last_request .url == mock_url
313+ assert requests_mock .last_request .method == "POST"
314+ assert requests_mock .last_request .json () == request_data
315+
316+ def test_create_commit_with_branch_id_and_replace (client , requests_mock ):
317+ """Tests commit creation with both branchId and replace query parameters."""
318+ branch_id = "branch_123"
319+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /commits?replace=true&branchId={ branch_id } "
320+ request_data = {"message" : "Replace branch commit" , "parentCommitId" : None }
321+ response_data = {"id" : "replace_branch_commit_id" , ** request_data }
322+ requests_mock .post (mock_url , json = response_data , status_code = 200 )
323+
324+ created_commit = client .create_commit (
325+ TEST_PROJECT_ID ,
326+ request_data ,
327+ branch_id = branch_id ,
328+ replace = True ,
329+ )
330+
331+ assert created_commit == response_data
332+ assert requests_mock .last_request .url == mock_url
333+ assert requests_mock .last_request .method == "POST"
334+ assert requests_mock .last_request .json () == request_data
335+
248336def test_create_commit_bad_request (client , requests_mock ):
249337 """Tests 400 Bad Request during commit creation."""
250338 mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /commits"
@@ -329,6 +417,14 @@ def test_list_commits_success_dict_response(client, requests_mock):
329417
330418 assert commits == mock_response_data ["elements" ]
331419
420+ def test_list_commits_success_unexpected_scalar_response (client , monkeypatch ):
421+ """Tests listing commits when the API returns a scalar value."""
422+ monkeypatch .setattr (client , "_request" , lambda ** kwargs : "unexpected" )
423+
424+ commits = client .list_commits (TEST_PROJECT_ID )
425+
426+ assert commits == []
427+
332428def test_list_commits_project_not_found (client , requests_mock ):
333429 """Tests 404 when listing commits for a non-existent project."""
334430 mock_url = f"{ TEST_BASE_URL } /projects/invalid_project/commits"
@@ -349,6 +445,18 @@ def test_list_branches_success(client, requests_mock):
349445 branches = client .list_branches (TEST_PROJECT_ID )
350446 assert branches == mock_response
351447
448+ def test_list_branches_success_dict_response (client , requests_mock ):
449+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /branches"
450+ mock_response = {"elements" : [{"id" : TEST_BRANCH_ID , "name" : "develop" }]}
451+ requests_mock .get (mock_url , json = mock_response , status_code = 200 )
452+ branches = client .list_branches (TEST_PROJECT_ID )
453+ assert branches == mock_response ["elements" ]
454+
455+ def test_list_branches_success_unexpected_scalar_response (client , monkeypatch ):
456+ monkeypatch .setattr (client , "_request" , lambda ** kwargs : "unexpected" )
457+ branches = client .list_branches (TEST_PROJECT_ID )
458+ assert branches == []
459+
352460def test_create_branch_success (client , requests_mock ):
353461 mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /branches"
354462 request_data = {"name" : "feature-branch" , "head" : {"@id" : TEST_COMMIT_ID }}
@@ -389,6 +497,18 @@ def test_list_tags_success(client, requests_mock):
389497 tags = client .list_tags (TEST_PROJECT_ID )
390498 assert tags == mock_response
391499
500+ def test_list_tags_success_dict_response (client , requests_mock ):
501+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /tags"
502+ mock_response = {"elements" : [{"id" : TEST_TAG_ID , "name" : "v1.0" }]}
503+ requests_mock .get (mock_url , json = mock_response , status_code = 200 )
504+ tags = client .list_tags (TEST_PROJECT_ID )
505+ assert tags == mock_response ["elements" ]
506+
507+ def test_list_tags_success_unexpected_scalar_response (client , monkeypatch ):
508+ monkeypatch .setattr (client , "_request" , lambda ** kwargs : "unexpected" )
509+ tags = client .list_tags (TEST_PROJECT_ID )
510+ assert tags == []
511+
392512def test_create_tag_success (client , requests_mock ):
393513 mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /tags"
394514 request_data = {"name" : "v1.0-release" , "taggedCommit" : {"@id" : TEST_COMMIT_ID }}
@@ -435,6 +555,20 @@ def test_list_elements_commit_not_found(client, requests_mock):
435555 with pytest .raises (SysMLV2NotFoundError ):
436556 client .list_elements (TEST_PROJECT_ID , "invalid_commit" )
437557
558+ def test_list_elements_success_dict_response (client , requests_mock ):
559+ """Tests listing elements when the API returns a dict with 'elements'."""
560+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /commits/{ TEST_COMMIT_ID } /elements"
561+ mock_response_data = {"elements" : [{"id" : "elem1" }, {"id" : "elem2" }]}
562+ requests_mock .get (mock_url , json = mock_response_data , status_code = 200 )
563+ elements = client .list_elements (TEST_PROJECT_ID , TEST_COMMIT_ID )
564+ assert elements == mock_response_data ["elements" ]
565+
566+ def test_list_elements_success_unexpected_scalar_response (client , monkeypatch ):
567+ """Tests listing elements when the API returns a scalar value."""
568+ monkeypatch .setattr (client , "_request" , lambda ** kwargs : "unexpected" )
569+ elements = client .list_elements (TEST_PROJECT_ID , TEST_COMMIT_ID )
570+ assert elements == []
571+
438572
439573# --- Test List Relationships ---
440574
@@ -460,4 +594,63 @@ def test_list_relationships_element_not_found(client, requests_mock):
460594 mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /commits/{ TEST_COMMIT_ID } /elements/invalid_element/relationships?direction=both"
461595 requests_mock .get (mock_url , status_code = 404 )
462596 with pytest .raises (SysMLV2NotFoundError ):
463- client .list_relationships (TEST_PROJECT_ID , "invalid_element" , TEST_COMMIT_ID )
597+ client .list_relationships (TEST_PROJECT_ID , "invalid_element" , TEST_COMMIT_ID )
598+
599+ def test_list_relationships_success_dict_response (client , requests_mock ):
600+ """Tests listing relationships when the API returns a dict with 'elements'."""
601+ mock_url = f"{ TEST_BASE_URL } /projects/{ TEST_PROJECT_ID } /commits/{ TEST_COMMIT_ID } /elements/{ TEST_ELEMENT_ID } /relationships?direction=both"
602+ mock_response_data = {"elements" : [{"id" : "rel1" }]}
603+ requests_mock .get (mock_url , json = mock_response_data , status_code = 200 )
604+ relationships = client .list_relationships (TEST_PROJECT_ID , TEST_ELEMENT_ID , TEST_COMMIT_ID )
605+ assert relationships == mock_response_data ["elements" ]
606+
607+ def test_list_relationships_success_unexpected_scalar_response (client , monkeypatch ):
608+ """Tests listing relationships when the API returns a scalar value."""
609+ monkeypatch .setattr (client , "_request" , lambda ** kwargs : "unexpected" )
610+ relationships = client .list_relationships (TEST_PROJECT_ID , TEST_ELEMENT_ID , TEST_COMMIT_ID )
611+ assert relationships == []
612+
613+
614+ # --- Test _request Edge Cases ---
615+
616+ def test_request_bad_request_uses_text_when_json_decode_fails (client , monkeypatch ):
617+ """Tests 400 handling falls back to response text when JSON decoding fails."""
618+
619+ class FakeResponse :
620+ status_code = 400
621+ text = "plain error text"
622+ content = b"plain error text"
623+
624+ def json (self ):
625+ raise json .JSONDecodeError ("Expecting value" , "plain error text" , 0 )
626+
627+ monkeypatch .setattr (client ._session , "request" , lambda ** kwargs : FakeResponse ())
628+
629+ with pytest .raises (SysMLV2BadRequestError , match = "plain error text" ):
630+ client ._request (method = "GET" , endpoint = "/projects" )
631+
632+ def test_request_network_error_wrapped (client , monkeypatch ):
633+ """Tests request-layer network exceptions are wrapped in SysMLV2Error."""
634+ def raise_request_exception (** kwargs ):
635+ raise requests .exceptions .ConnectionError ("connection dropped" )
636+
637+ monkeypatch .setattr (client ._session , "request" , raise_request_exception )
638+
639+ with pytest .raises (SysMLV2Error , match = "Network error during request" ):
640+ client ._request (method = "GET" , endpoint = "/projects" )
641+
642+ def test_request_success_json_decode_error_wrapped (client , monkeypatch ):
643+ """Tests invalid JSON on a successful response is wrapped in SysMLV2Error."""
644+
645+ class FakeResponse :
646+ status_code = 200
647+ text = "not json"
648+ content = b"not json"
649+
650+ def json (self ):
651+ raise json .JSONDecodeError ("Expecting value" , "not json" , 0 )
652+
653+ monkeypatch .setattr (client ._session , "request" , lambda ** kwargs : FakeResponse ())
654+
655+ with pytest .raises (SysMLV2Error , match = "Failed to decode JSON response" ):
656+ client ._request (method = "GET" , endpoint = "/projects" )
0 commit comments