Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Tracker = "https://github.com/ibutsu/ibutsu-client-python/issues"
test = [
"pytest",
"pytest-cov",
"pytest-mock",
"coverage[toml]",
]
dev = [
Expand Down
1 change: 1 addition & 0 deletions test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Test utilities for ibutsu_client tests."""
70 changes: 52 additions & 18 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,15 @@
from ibutsu_client.rest import RESTResponse


@pytest.fixture
def mock_api_client(mocker):
"""Create a mock ApiClient for testing API methods."""
from ibutsu_client.api_client import ApiClient

client = ApiClient()
mocker.patch.object(client, "call_api")
return client


def create_mock_response(
data: dict[str, Any] | list[Any] | None = None,
def _make_mock_response(
data: dict[str, Any] | list[Any] | bytes | None = None,
status: int = 200,
headers: dict[str, str] | None = None,
) -> RESTResponse:
"""Create a mock REST response for testing.
"""Internal helper to create a mock REST response.

Args:
data: The response data (will be JSON-encoded)
data: The response data (will be JSON-encoded unless bytes)
status: HTTP status code
headers: Response headers

Expand All @@ -43,7 +33,11 @@ def create_mock_response(
response = Mock(spec=RESTResponse)
response.status = status
response.headers = headers
response.data = json.dumps(data).encode("utf-8")
# Handle bytes data directly without JSON encoding
if isinstance(data, bytes):
response.data = data
else:
response.data = json.dumps(data).encode("utf-8")
response.reason = "OK" if status < 400 else "Error"

def mock_read():
Expand All @@ -65,6 +59,46 @@ def mock_getheaders() -> dict[str, str]:


@pytest.fixture
def mock_rest_response():
"""Fixture that provides the create_mock_response function."""
return create_mock_response
def mock_api_client():
"""Create a mock ApiClient for testing API methods."""
from ibutsu_client.api_client import ApiClient

client = ApiClient()
client.call_api = Mock()
return client


@pytest.fixture
def create_mock_response(request):
"""Parametrized fixture for creating mock API responses.

Use with indirect parametrization. Call factory functions directly in the parametrize decorator:

Example:
from test.utils import sample_project_data

@pytest.mark.parametrize('create_mock_response', [
{'data': sample_project_data(name='test', title='Test'), 'status': 201},
{'data': {'error': 'not found'}, 'status': 404},
], indirect=True)
def test_api_method(self, mocker, create_mock_response):
api = SomeApi()
mocker.patch.object(api.api_client, "call_api", return_value=create_mock_response)
result = api.method()
...

Args:
request.param: Dict with keys:
- data: Response data dict/list/bytes
- status: HTTP status code (default: 200)
- headers: Response headers dict (optional)

Returns:
Mock RESTResponse object ready to use
"""
params = request.param
data = params.get("data", {})
status = params.get("status", 200)
headers = params.get("headers", None)

return _make_mock_response(data=data, status=status, headers=headers)
124 changes: 85 additions & 39 deletions test/test_admin_project_management_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from urllib.parse import parse_qs, urlparse
from uuid import uuid4

import pytest

from ibutsu_client.api.admin_project_management_api import AdminProjectManagementApi
from ibutsu_client.models.project import Project
from ibutsu_client.models.project_list import ProjectList
Expand All @@ -11,19 +13,30 @@
class TestAdminProjectManagementApi:
"""AdminProjectManagementApi Tests"""

def test_admin_add_project(self, mock_api_client, mock_rest_response):
@pytest.mark.parametrize(
"create_mock_response",
[
{
"data": {
"id": "PROJECT_ID_PLACEHOLDER",
"name": "New Project",
"title": "New Project Title",
},
"status": 201,
}
],
indirect=True,
)
def test_admin_add_project(self, mock_api_client, create_mock_response):
"""Test case for admin_add_project"""
api = AdminProjectManagementApi(api_client=mock_api_client)
project_id = uuid4()
project_data = {
"id": str(project_id),
"name": "New Project",
"title": "New Project Title",
}

# Mock the API response
mock_response = mock_rest_response(data=project_data, status=201)
mock_api_client.call_api.return_value = mock_response
# Update the mock response with the actual project_id
create_mock_response.data = create_mock_response.data.replace(
b"PROJECT_ID_PLACEHOLDER", str(project_id).encode()
)
mock_api_client.call_api.return_value = create_mock_response

# Call the API
new_project = Project(name="New Project", title="New Project Title")
Expand All @@ -40,14 +53,18 @@ def test_admin_add_project(self, mock_api_client, mock_rest_response):
assert args[0] == "POST"
assert args[1].endswith("/admin/project")

def test_admin_delete_project(self, mock_api_client, mock_rest_response):
@pytest.mark.parametrize(
"create_mock_response",
[{"data": {}, "status": 200}],
indirect=True,
)
def test_admin_delete_project(self, mock_api_client, create_mock_response):
"""Test case for admin_delete_project"""
api = AdminProjectManagementApi(api_client=mock_api_client)
project_id = uuid4()

# Mock the API response
mock_response = mock_rest_response(status=200)
mock_api_client.call_api.return_value = mock_response
mock_api_client.call_api.return_value = create_mock_response

# Call the API
api.admin_delete_project(id=project_id)
Expand All @@ -58,18 +75,29 @@ def test_admin_delete_project(self, mock_api_client, mock_rest_response):
assert args[0] == "DELETE"
assert args[1].endswith(f"/admin/project/{project_id}")

def test_admin_get_project(self, mock_api_client, mock_rest_response):
@pytest.mark.parametrize(
"create_mock_response",
[
{
"data": {
"id": "PROJECT_ID_PLACEHOLDER",
"name": "My Project",
},
"status": 200,
}
],
indirect=True,
)
def test_admin_get_project(self, mock_api_client, create_mock_response):
"""Test case for admin_get_project"""
api = AdminProjectManagementApi(api_client=mock_api_client)
project_id = uuid4()
project_data = {
"id": str(project_id),
"name": "My Project",
}

# Mock the API response
mock_response = mock_rest_response(data=project_data, status=200)
mock_api_client.call_api.return_value = mock_response
# Update the mock response with the actual project_id
create_mock_response.data = create_mock_response.data.replace(
b"PROJECT_ID_PLACEHOLDER", str(project_id).encode()
)
mock_api_client.call_api.return_value = create_mock_response

# Call the API
response = api.admin_get_project(id=project_id)
Expand All @@ -85,21 +113,28 @@ def test_admin_get_project(self, mock_api_client, mock_rest_response):
assert args[0] == "GET"
assert args[1].endswith(f"/admin/project/{project_id}")

def test_admin_get_project_list(self, mock_api_client, mock_rest_response):
@pytest.mark.parametrize(
"create_mock_response",
[
{
"data": {
"projects": [
{"id": "00000000-0000-0000-0000-000000000001", "name": "Project 1"},
{"id": "00000000-0000-0000-0000-000000000002", "name": "Project 2"},
],
"pagination": {"page": 1, "pageSize": 25, "totalItems": 2, "totalPages": 1},
},
"status": 200,
}
],
indirect=True,
)
def test_admin_get_project_list(self, mock_api_client, create_mock_response):
"""Test case for admin_get_project_list"""
api = AdminProjectManagementApi(api_client=mock_api_client)

project_list_data = {
"projects": [
{"id": str(uuid4()), "name": "Project 1"},
{"id": str(uuid4()), "name": "Project 2"},
],
"pagination": {"page": 1, "pageSize": 25, "totalItems": 2, "totalPages": 1},
}

# Mock the API response
mock_response = mock_rest_response(data=project_list_data, status=200)
mock_api_client.call_api.return_value = mock_response
mock_api_client.call_api.return_value = create_mock_response

# Call the API
response = api.admin_get_project_list(page=1, page_size=25)
Expand All @@ -121,18 +156,29 @@ def test_admin_get_project_list(self, mock_api_client, mock_rest_response):
assert query_params["page"] == ["1"]
assert query_params["pageSize"] == ["25"]

def test_admin_update_project(self, mock_api_client, mock_rest_response):
@pytest.mark.parametrize(
"create_mock_response",
[
{
"data": {
"id": "PROJECT_ID_PLACEHOLDER",
"name": "Updated Project",
},
"status": 200,
}
],
indirect=True,
)
def test_admin_update_project(self, mock_api_client, create_mock_response):
"""Test case for admin_update_project"""
api = AdminProjectManagementApi(api_client=mock_api_client)
project_id = uuid4()
project_data = {
"id": str(project_id),
"name": "Updated Project",
}

# Mock the API response
mock_response = mock_rest_response(data=project_data, status=200)
mock_api_client.call_api.return_value = mock_response
# Update the mock response with the actual project_id
create_mock_response.data = create_mock_response.data.replace(
b"PROJECT_ID_PLACEHOLDER", str(project_id).encode()
)
mock_api_client.call_api.return_value = create_mock_response

# Call the API
update_project = Project(name="Updated Project")
Expand Down
Loading