From 3b6e78f807a85ed48b0d38069defe53dc412a7e4 Mon Sep 17 00:00:00 2001 From: Will Foster Date: Mon, 25 May 2026 15:54:45 +0100 Subject: [PATCH 1/2] chore: fix tests --- tests/test_quads.py | 118 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 12 deletions(-) diff --git a/tests/test_quads.py b/tests/test_quads.py index 10eccf7..276146f 100644 --- a/tests/test_quads.py +++ b/tests/test_quads.py @@ -933,7 +933,11 @@ def test_get_schedules(self, mock_get): @patch("requests.Session.request") def test_get_schedules_with_params(self, mock_get): query_data = {"cloud": "cloud1", "start": "2024-03-20"} - expected_response = {"schedules": [{"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"}]} + expected_response = { + "schedules": [ + {"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"} + ] + } mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -941,14 +945,20 @@ def test_get_schedules_with_params(self, mock_get): result = self.api.get_schedules(query_data) mock_get.assert_called_once() - assert str(mock_get.call_args[0][1]).endswith("/schedules?cloud=cloud1&start=2024-03-20") or str(mock_get.call_args[0][1]).endswith( + assert str(mock_get.call_args[0][1]).endswith( + "/schedules?cloud=cloud1&start=2024-03-20" + ) or str(mock_get.call_args[0][1]).endswith( "/schedules?start=2024-03-20&cloud=cloud1" ) assert result == expected_response @patch("requests.Session.request") def test_get_current_schedules(self, mock_get): - expected_response = {"schedules": [{"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"}]} + expected_response = { + "schedules": [ + {"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"} + ] + } mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -962,7 +972,11 @@ def test_get_current_schedules(self, mock_get): @patch("requests.Session.request") def test_get_current_schedules_with_params(self, mock_get): query_data = {"cloud": "cloud1"} - expected_response = {"schedules": [{"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"}]} + expected_response = { + "schedules": [ + {"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"} + ] + } mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -1041,7 +1055,11 @@ def test_get_future_schedules(self, mock_get): @patch("requests.Session.request") def test_get_future_schedules_with_params(self, mock_get): query_data = {"cloud": "cloud1"} - expected_response = {"schedules": [{"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"}]} + expected_response = { + "schedules": [ + {"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"} + ] + } mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -1293,7 +1311,9 @@ def test_update_notification(self, mock_patch): result = self.api.update_notification(notification_id, update_data) mock_patch.assert_called_once() - assert str(mock_patch.call_args[0][1]).endswith(f"/notifications/{notification_id}") + assert str(mock_patch.call_args[0][1]).endswith( + f"/notifications/{notification_id}" + ) assert mock_patch.call_args[1]["json"] == update_data assert result == update_data @@ -1358,7 +1378,9 @@ def test_get_active_cloud_assignment(self, mock_get): result = self.api.get_active_cloud_assignment(cloud_name) mock_get.assert_called_once() - assert str(mock_get.call_args[0][1]).endswith(f"/assignments/active/{cloud_name}") + assert str(mock_get.call_args[0][1]).endswith( + f"/assignments/active/{cloud_name}" + ) assert result == expected_response @patch("requests.Session.request") @@ -1444,7 +1466,9 @@ def test_remove_interface(self, mock_delete): result = self.api.remove_interface(hostname, if_name) mock_delete.assert_called_once() - assert str(mock_delete.call_args[0][1]).endswith(f"/interfaces/{hostname}/{if_name}") + assert str(mock_delete.call_args[0][1]).endswith( + f"/interfaces/{hostname}/{if_name}" + ) assert result == {} @patch("requests.Session.request") @@ -1752,7 +1776,11 @@ def test_get_moves(self, mock_get): @patch("requests.Session.request") def test_get_moves_with_date(self, mock_get): date = "2024-03-20" - expected_response = {"moves": [{"id": 1, "host": "host1", "from_cloud": "cloud1", "to_cloud": "cloud2"}]} + expected_response = { + "moves": [ + {"id": 1, "host": "host1", "from_cloud": "cloud1", "to_cloud": "cloud2"} + ] + } mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -1843,7 +1871,9 @@ def test_create_self_assignment(self, mock_post): with patch.object(self.api, "post") as mock_post: mock_post.return_value = expected_response result = self.api.create_self_assignment(test_data) - mock_post.assert_called_once_with(str(Path("assignments") / "self"), test_data) + mock_post.assert_called_once_with( + str(Path("assignments") / "self"), test_data + ) assert result == expected_response @patch("requests.Session.request") @@ -2019,7 +2049,9 @@ def test_create_self_assignment_logging(self, mock_request, mock_print): self.api.create_self_assignment(assignment_data) - mock_print.assert_called_once_with("Self-assignment created - ID: 123, Cloud: cloud1") + mock_print.assert_called_once_with( + "Self-assignment created - ID: 123, Cloud: cloud1" + ) @patch("builtins.print") @patch("requests.Session.request") @@ -2067,6 +2099,66 @@ def test_create_self_assignment_limit_reached(self, mock_request, mock_print): assert result == error_response +class TestApiTokenAuth: + """Tests for qat_ API token authentication""" + + def test_init_with_api_token(self): + """Test that api_token sets Bearer header and skips BasicAuth""" + api = QuadsApi("", "", "http://example.com/", api_token="qat_test123") + assert api.token == "qat_test123" + assert api.auth is None + assert api.session.headers.get("Authorization") == "Bearer qat_test123" + + def test_init_without_api_token(self): + """Test that without api_token, BasicAuth is set""" + api = QuadsApi("user", "pass", "http://example.com/") + assert api.token is None + assert api.auth is not None + + def test_login_noop_with_qat_token(self): + """Test that login() returns synthetic success for qat_ tokens""" + api = QuadsApi("", "", "http://example.com/", api_token="qat_abc123") + result = api.login() + assert result["status_code"] == 201 + assert result["status"] == "success" + assert result["auth_token"] == "qat_abc123" + + def test_login_noop_does_not_make_request(self): + """Test that login() with qat_ token makes no HTTP request""" + api = QuadsApi("", "", "http://example.com/", api_token="qat_abc123") + api.session.post = Mock(side_effect=AssertionError("should not be called")) + result = api.login() + assert result["status"] == "success" + + @patch("requests.Session.request") + def test_get_user(self, mock_request): + """Test get_user calls /users/{email}""" + api = QuadsApi("user", "pass", "http://example.com/") + expected = {"email": "bob@example.com", "roles": ["user"]} + mock_response = Mock() + mock_response.json.return_value = expected + mock_request.return_value = mock_response + + result = api.get_user("bob@example.com") + + mock_request.assert_called_once() + assert str(mock_request.call_args[0][1]).endswith("/users/bob@example.com") + assert result == expected + + @patch("requests.Session.request") + def test_get_user_with_api_token(self, mock_request): + """Test get_user works with api_token auth""" + api = QuadsApi("", "", "http://example.com/", api_token="qat_test") + expected = {"email": "admin@example.com", "roles": ["admin"]} + mock_response = Mock() + mock_response.json.return_value = expected + mock_request.return_value = mock_response + + result = api.get_user("admin@example.com") + + assert result == expected + + class TestQuadsBase: @pytest.fixture(autouse=True) def setup(self): @@ -2076,7 +2168,9 @@ def setup(self): @pytest.fixture def quads_base(self): - return QuadsBase(username=self.username, password=self.password, base_url=self.base_url) + return QuadsBase( + username=self.username, password=self.password, base_url=self.base_url + ) def test_context_manager_enter(self, quads_base): quads_base.login = Mock() From 699b86d658566c7b110f9f3becf93278a6ab1d66 Mon Sep 17 00:00:00 2001 From: Will Foster Date: Mon, 25 May 2026 15:58:04 +0100 Subject: [PATCH 2/2] chore: ruff --- tests/test_quads.py | 58 ++++++++++----------------------------------- 1 file changed, 12 insertions(+), 46 deletions(-) diff --git a/tests/test_quads.py b/tests/test_quads.py index 276146f..bbefc80 100644 --- a/tests/test_quads.py +++ b/tests/test_quads.py @@ -933,11 +933,7 @@ def test_get_schedules(self, mock_get): @patch("requests.Session.request") def test_get_schedules_with_params(self, mock_get): query_data = {"cloud": "cloud1", "start": "2024-03-20"} - expected_response = { - "schedules": [ - {"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"} - ] - } + expected_response = {"schedules": [{"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"}]} mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -945,20 +941,14 @@ def test_get_schedules_with_params(self, mock_get): result = self.api.get_schedules(query_data) mock_get.assert_called_once() - assert str(mock_get.call_args[0][1]).endswith( - "/schedules?cloud=cloud1&start=2024-03-20" - ) or str(mock_get.call_args[0][1]).endswith( + assert str(mock_get.call_args[0][1]).endswith("/schedules?cloud=cloud1&start=2024-03-20") or str(mock_get.call_args[0][1]).endswith( "/schedules?start=2024-03-20&cloud=cloud1" ) assert result == expected_response @patch("requests.Session.request") def test_get_current_schedules(self, mock_get): - expected_response = { - "schedules": [ - {"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"} - ] - } + expected_response = {"schedules": [{"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"}]} mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -972,11 +962,7 @@ def test_get_current_schedules(self, mock_get): @patch("requests.Session.request") def test_get_current_schedules_with_params(self, mock_get): query_data = {"cloud": "cloud1"} - expected_response = { - "schedules": [ - {"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"} - ] - } + expected_response = {"schedules": [{"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"}]} mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -1055,11 +1041,7 @@ def test_get_future_schedules(self, mock_get): @patch("requests.Session.request") def test_get_future_schedules_with_params(self, mock_get): query_data = {"cloud": "cloud1"} - expected_response = { - "schedules": [ - {"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"} - ] - } + expected_response = {"schedules": [{"id": 1, "cloud": "cloud1", "start": "2024-03-20", "end": "2024-03-21"}]} mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -1311,9 +1293,7 @@ def test_update_notification(self, mock_patch): result = self.api.update_notification(notification_id, update_data) mock_patch.assert_called_once() - assert str(mock_patch.call_args[0][1]).endswith( - f"/notifications/{notification_id}" - ) + assert str(mock_patch.call_args[0][1]).endswith(f"/notifications/{notification_id}") assert mock_patch.call_args[1]["json"] == update_data assert result == update_data @@ -1378,9 +1358,7 @@ def test_get_active_cloud_assignment(self, mock_get): result = self.api.get_active_cloud_assignment(cloud_name) mock_get.assert_called_once() - assert str(mock_get.call_args[0][1]).endswith( - f"/assignments/active/{cloud_name}" - ) + assert str(mock_get.call_args[0][1]).endswith(f"/assignments/active/{cloud_name}") assert result == expected_response @patch("requests.Session.request") @@ -1466,9 +1444,7 @@ def test_remove_interface(self, mock_delete): result = self.api.remove_interface(hostname, if_name) mock_delete.assert_called_once() - assert str(mock_delete.call_args[0][1]).endswith( - f"/interfaces/{hostname}/{if_name}" - ) + assert str(mock_delete.call_args[0][1]).endswith(f"/interfaces/{hostname}/{if_name}") assert result == {} @patch("requests.Session.request") @@ -1776,11 +1752,7 @@ def test_get_moves(self, mock_get): @patch("requests.Session.request") def test_get_moves_with_date(self, mock_get): date = "2024-03-20" - expected_response = { - "moves": [ - {"id": 1, "host": "host1", "from_cloud": "cloud1", "to_cloud": "cloud2"} - ] - } + expected_response = {"moves": [{"id": 1, "host": "host1", "from_cloud": "cloud1", "to_cloud": "cloud2"}]} mock_response = Mock() mock_response.json.return_value = expected_response mock_get.return_value = mock_response @@ -1871,9 +1843,7 @@ def test_create_self_assignment(self, mock_post): with patch.object(self.api, "post") as mock_post: mock_post.return_value = expected_response result = self.api.create_self_assignment(test_data) - mock_post.assert_called_once_with( - str(Path("assignments") / "self"), test_data - ) + mock_post.assert_called_once_with(str(Path("assignments") / "self"), test_data) assert result == expected_response @patch("requests.Session.request") @@ -2049,9 +2019,7 @@ def test_create_self_assignment_logging(self, mock_request, mock_print): self.api.create_self_assignment(assignment_data) - mock_print.assert_called_once_with( - "Self-assignment created - ID: 123, Cloud: cloud1" - ) + mock_print.assert_called_once_with("Self-assignment created - ID: 123, Cloud: cloud1") @patch("builtins.print") @patch("requests.Session.request") @@ -2168,9 +2136,7 @@ def setup(self): @pytest.fixture def quads_base(self): - return QuadsBase( - username=self.username, password=self.password, base_url=self.base_url - ) + return QuadsBase(username=self.username, password=self.password, base_url=self.base_url) def test_context_manager_enter(self, quads_base): quads_base.login = Mock()