diff --git a/rpm/quads-lib.spec b/rpm/quads-lib.spec index f9886ca..b8c87c9 100644 --- a/rpm/quads-lib.spec +++ b/rpm/quads-lib.spec @@ -12,7 +12,7 @@ %define name quads-lib %define reponame python-quads-lib %define branch development -%define version 0.1.13 +%define version 0.1.14 %define build_timestamp %{lua: print(os.date("%Y%m%d"))} Summary: Python client library for interacting with the QUADS API diff --git a/setup.py b/setup.py index 64f320a..8e0d588 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name="quads-lib", - version="0.1.13", + version="0.1.14", license="LGPL-3.0-only", description="Python client library for interacting with the QUADS API", long_description="{}\n{}".format( diff --git a/src/quads_lib/__init__.py b/src/quads_lib/__init__.py index 7ba8667..b22daeb 100644 --- a/src/quads_lib/__init__.py +++ b/src/quads_lib/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.1.13" +__version__ = "0.1.14" from .quads import QuadsApi diff --git a/src/quads_lib/quads.py b/src/quads_lib/quads.py index e7d00a8..e577a08 100644 --- a/src/quads_lib/quads.py +++ b/src/quads_lib/quads.py @@ -37,8 +37,8 @@ def login(self) -> dict: self.session.headers.update({"Authorization": f"Bearer {self.token}"}) return json_response - def get_current_user(self) -> dict: - return self.get("me") + def get_user(self, email: str) -> dict: + return self.get(f"users/{email}") def logout(self) -> dict: json_response = self._make_request("POST", "logout") diff --git a/tests/test_quads.py b/tests/test_quads.py index 10eccf7..bbefc80 100644 --- a/tests/test_quads.py +++ b/tests/test_quads.py @@ -2067,6 +2067,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):