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
2 changes: 1 addition & 1 deletion ofsc/models/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ class CapacityCategoryWorkSkillGroup(BaseModel):
class CapacityCategory(BaseModel):
model_config = ConfigDict(extra="allow")
label: str
name: str
name: Optional[str] = None
timeSlots: Optional[ItemList] = None
translations: Annotated[Optional[TranslationList], Field(alias="translations")] = None
workSkillGroups: Optional[list[CapacityCategoryWorkSkillGroup]] = None
Expand Down
65 changes: 65 additions & 0 deletions tests/async/test_capacity_category_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Capacity categories may omit their display name in OFSC responses (#189)."""

from unittest.mock import AsyncMock, Mock

import pytest

from ofsc.async_client import AsyncOFSC
from ofsc.models import CapacityCategory


NAMELESS_CATEGORY = {
"label": "BASIC",
"active": True,
"workSkills": [],
"workSkillGroups": [],
"timeSlots": [],
"translations": [],
}


def _response(payload: dict) -> Mock:
response = Mock()
response.raise_for_status = Mock()
response.json.return_value = payload
return response


@pytest.mark.parametrize("name_field,expected", [({}, None), ({"name": None}, None), ({"name": "Basic Category"}, "Basic Category")])
def test_capacity_category_accepts_optional_name(name_field: dict, expected: str | None) -> None:
category = CapacityCategory.model_validate({**NAMELESS_CATEGORY, **name_field})
assert category.name == expected
assert category.label == "BASIC"
assert category.workSkills == []


@pytest.mark.asyncio
async def test_get_capacity_category_accepts_nameless_reply(mock_instance: AsyncOFSC) -> None:
mock_instance.metadata._client.get = AsyncMock(return_value=_response(NAMELESS_CATEGORY.copy()))
category = await mock_instance.metadata.get_capacity_category("BASIC")
assert category.name is None
assert category.label == "BASIC"


@pytest.mark.asyncio
async def test_nameless_category_does_not_reject_the_whole_page(mock_instance: AsyncOFSC) -> None:
named = {**NAMELESS_CATEGORY, "label": "NAMED", "name": "Named Category"}
page = {"items": [NAMELESS_CATEGORY.copy(), named], "totalResults": 2, "hasMore": False, "limit": 100, "offset": 0}
mock_instance.metadata._client.get = AsyncMock(return_value=_response(page))
categories = await mock_instance.metadata.get_capacity_categories()
assert [category.label for category in categories.items] == ["BASIC", "NAMED"]
assert [category.name for category in categories.items] == [None, "Named Category"]
assert categories.totalResults == 2


@pytest.mark.asyncio
async def test_nameless_category_can_be_deactivated_and_read_back(mock_instance: AsyncOFSC) -> None:
inactive = {**NAMELESS_CATEGORY, "active": False}
mock_instance.metadata._client.put = AsyncMock(return_value=_response(inactive))
category = CapacityCategory.model_validate(inactive)
result = await mock_instance.metadata.create_or_replace_capacity_category(category)
body = mock_instance.metadata._client.put.call_args.kwargs["json"]
assert "name" not in body
assert body["active"] is False
assert result.name is None
assert result.active is False
Loading