diff --git a/services/base_service.py b/services/base_service.py index faf0dba..7046efb 100644 --- a/services/base_service.py +++ b/services/base_service.py @@ -245,7 +245,7 @@ class BaseService(Generic[T]): """ try: client = await self.get_client() - response = await client.post(self.endpoint, model_data) + response = await client.post(f"{self.endpoint}/", model_data) if not response: logger.warning(f"No response from {self.model_class.__name__} creation") diff --git a/services/draft_list_service.py b/services/draft_list_service.py index 2a7017d..934a44f 100644 --- a/services/draft_list_service.py +++ b/services/draft_list_service.py @@ -3,13 +3,14 @@ Draft list service for Discord Bot v2.0 Handles team draft list (auto-draft queue) operations. NO CACHING - lists change frequently. """ + import logging from typing import Optional, List from services.base_service import BaseService from models.draft_list import DraftList -logger = logging.getLogger(f'{__name__}.DraftListService') +logger = logging.getLogger(f"{__name__}.DraftListService") class DraftListService(BaseService[DraftList]): @@ -32,7 +33,7 @@ class DraftListService(BaseService[DraftList]): def __init__(self): """Initialize draft list service.""" - super().__init__(DraftList, 'draftlist') + super().__init__(DraftList, "draftlist") logger.debug("DraftListService initialized") def _extract_items_and_count_from_response(self, data): @@ -54,20 +55,16 @@ class DraftListService(BaseService[DraftList]): return [], 0 # Get count - count = data.get('count', 0) + count = data.get("count", 0) # API returns items under 'picks' key (not 'draftlist') - if 'picks' in data and isinstance(data['picks'], list): - return data['picks'], count or len(data['picks']) + if "picks" in data and isinstance(data["picks"], list): + return data["picks"], count or len(data["picks"]) # Fallback to standard extraction return super()._extract_items_and_count_from_response(data) - async def get_team_list( - self, - season: int, - team_id: int - ) -> List[DraftList]: + async def get_team_list(self, season: int, team_id: int) -> List[DraftList]: """ Get team's draft list ordered by rank. @@ -82,8 +79,8 @@ class DraftListService(BaseService[DraftList]): """ try: params = [ - ('season', str(season)), - ('team_id', str(team_id)) + ("season", str(season)), + ("team_id", str(team_id)), # NOTE: API does not support 'sort' param - results must be sorted client-side ] @@ -100,11 +97,7 @@ class DraftListService(BaseService[DraftList]): return [] async def add_to_list( - self, - season: int, - team_id: int, - player_id: int, - rank: Optional[int] = None + self, season: int, team_id: int, player_id: int, rank: Optional[int] = None ) -> Optional[List[DraftList]]: """ Add player to team's draft list. @@ -133,10 +126,10 @@ class DraftListService(BaseService[DraftList]): # Create new entry data new_entry_data = { - 'season': season, - 'team_id': team_id, - 'player_id': player_id, - 'rank': rank + "season": season, + "team_id": team_id, + "player_id": player_id, + "rank": rank, } # Build complete list for bulk replacement @@ -146,36 +139,42 @@ class DraftListService(BaseService[DraftList]): for entry in current_list: if entry.rank >= rank: # Shift down entries at or after insertion point - draft_list_entries.append({ - 'season': entry.season, - 'team_id': entry.team_id, - 'player_id': entry.player_id, - 'rank': entry.rank + 1 - }) + draft_list_entries.append( + { + "season": entry.season, + "team_id": entry.team_id, + "player_id": entry.player_id, + "rank": entry.rank + 1, + } + ) else: # Keep existing rank for entries before insertion point - draft_list_entries.append({ - 'season': entry.season, - 'team_id': entry.team_id, - 'player_id': entry.player_id, - 'rank': entry.rank - }) + draft_list_entries.append( + { + "season": entry.season, + "team_id": entry.team_id, + "player_id": entry.player_id, + "rank": entry.rank, + } + ) # Add new entry draft_list_entries.append(new_entry_data) # Sort by rank for consistency - draft_list_entries.sort(key=lambda x: x['rank']) + draft_list_entries.sort(key=lambda x: x["rank"]) # POST entire list (bulk replacement) client = await self.get_client() payload = { - 'count': len(draft_list_entries), - 'draft_list': draft_list_entries + "count": len(draft_list_entries), + "draft_list": draft_list_entries, } - logger.debug(f"Posting draft list for team {team_id}: {len(draft_list_entries)} entries") - response = await client.post(self.endpoint, payload) + logger.debug( + f"Posting draft list for team {team_id}: {len(draft_list_entries)} entries" + ) + response = await client.post(f"{self.endpoint}/", payload) logger.debug(f"POST response: {response}") # Verify by fetching the list back (API returns full objects) @@ -184,20 +183,21 @@ class DraftListService(BaseService[DraftList]): # Verify the player was added if not any(entry.player_id == player_id for entry in verification): - logger.error(f"Player {player_id} not found in list after POST - operation may have failed") + logger.error( + f"Player {player_id} not found in list after POST - operation may have failed" + ) return None - logger.info(f"Added player {player_id} to team {team_id} draft list at rank {rank}") + logger.info( + f"Added player {player_id} to team {team_id} draft list at rank {rank}" + ) return verification # Return full updated list except Exception as e: logger.error(f"Error adding player {player_id} to draft list: {e}") return None - async def remove_from_list( - self, - entry_id: int - ) -> bool: + async def remove_from_list(self, entry_id: int) -> bool: """ Remove entry from draft list by ID. @@ -209,14 +209,13 @@ class DraftListService(BaseService[DraftList]): Returns: True if deletion succeeded """ - logger.warning("remove_from_list() called with entry_id - use remove_player_from_list() instead") + logger.warning( + "remove_from_list() called with entry_id - use remove_player_from_list() instead" + ) return False async def remove_player_from_list( - self, - season: int, - team_id: int, - player_id: int + self, season: int, team_id: int, player_id: int ) -> bool: """ Remove specific player from team's draft list. @@ -238,7 +237,9 @@ class DraftListService(BaseService[DraftList]): # Check if player is in list player_found = any(entry.player_id == player_id for entry in current_list) if not player_found: - logger.warning(f"Player {player_id} not found in team {team_id} draft list") + logger.warning( + f"Player {player_id} not found in team {team_id} draft list" + ) return False # Build new list without the player, adjusting ranks @@ -246,22 +247,24 @@ class DraftListService(BaseService[DraftList]): new_rank = 1 for entry in current_list: if entry.player_id != player_id: - draft_list_entries.append({ - 'season': entry.season, - 'team_id': entry.team_id, - 'player_id': entry.player_id, - 'rank': new_rank - }) + draft_list_entries.append( + { + "season": entry.season, + "team_id": entry.team_id, + "player_id": entry.player_id, + "rank": new_rank, + } + ) new_rank += 1 # POST updated list (bulk replacement) client = await self.get_client() payload = { - 'count': len(draft_list_entries), - 'draft_list': draft_list_entries + "count": len(draft_list_entries), + "draft_list": draft_list_entries, } - await client.post(self.endpoint, payload) + await client.post(f"{self.endpoint}/", payload) logger.info(f"Removed player {player_id} from team {team_id} draft list") return True @@ -270,11 +273,7 @@ class DraftListService(BaseService[DraftList]): logger.error(f"Error removing player {player_id} from draft list: {e}") return False - async def clear_list( - self, - season: int, - team_id: int - ) -> bool: + async def clear_list(self, season: int, team_id: int) -> bool: """ Clear entire draft list for team. @@ -309,10 +308,7 @@ class DraftListService(BaseService[DraftList]): return False async def reorder_list( - self, - season: int, - team_id: int, - new_order: List[int] + self, season: int, team_id: int, new_order: List[int] ) -> bool: """ Reorder team's draft list. @@ -342,21 +338,23 @@ class DraftListService(BaseService[DraftList]): continue entry = entry_map[player_id] - draft_list_entries.append({ - 'season': entry.season, - 'team_id': entry.team_id, - 'player_id': entry.player_id, - 'rank': new_rank - }) + draft_list_entries.append( + { + "season": entry.season, + "team_id": entry.team_id, + "player_id": entry.player_id, + "rank": new_rank, + } + ) # POST reordered list (bulk replacement) client = await self.get_client() payload = { - 'count': len(draft_list_entries), - 'draft_list': draft_list_entries + "count": len(draft_list_entries), + "draft_list": draft_list_entries, } - await client.post(self.endpoint, payload) + await client.post(f"{self.endpoint}/", payload) logger.info(f"Reordered draft list for team {team_id}") return True @@ -365,12 +363,7 @@ class DraftListService(BaseService[DraftList]): logger.error(f"Error reordering draft list for team {team_id}: {e}") return False - async def move_entry_up( - self, - season: int, - team_id: int, - player_id: int - ) -> bool: + async def move_entry_up(self, season: int, team_id: int, player_id: int) -> bool: """ Move player up one position in draft list (higher priority). @@ -403,7 +396,9 @@ class DraftListService(BaseService[DraftList]): return False # Find entry above (rank - 1) - above_entry = next((e for e in entries if e.rank == current_entry.rank - 1), None) + above_entry = next( + (e for e in entries if e.rank == current_entry.rank - 1), None + ) if not above_entry: logger.error(f"Could not find entry above rank {current_entry.rank}") return False @@ -421,24 +416,26 @@ class DraftListService(BaseService[DraftList]): # Keep existing rank new_rank = entry.rank - draft_list_entries.append({ - 'season': entry.season, - 'team_id': entry.team_id, - 'player_id': entry.player_id, - 'rank': new_rank - }) + draft_list_entries.append( + { + "season": entry.season, + "team_id": entry.team_id, + "player_id": entry.player_id, + "rank": new_rank, + } + ) # Sort by rank - draft_list_entries.sort(key=lambda x: x['rank']) + draft_list_entries.sort(key=lambda x: x["rank"]) # POST updated list (bulk replacement) client = await self.get_client() payload = { - 'count': len(draft_list_entries), - 'draft_list': draft_list_entries + "count": len(draft_list_entries), + "draft_list": draft_list_entries, } - await client.post(self.endpoint, payload) + await client.post(f"{self.endpoint}/", payload) logger.info(f"Moved player {player_id} up to rank {current_entry.rank - 1}") return True @@ -447,12 +444,7 @@ class DraftListService(BaseService[DraftList]): logger.error(f"Error moving player {player_id} up in draft list: {e}") return False - async def move_entry_down( - self, - season: int, - team_id: int, - player_id: int - ) -> bool: + async def move_entry_down(self, season: int, team_id: int, player_id: int) -> bool: """ Move player down one position in draft list (lower priority). @@ -485,7 +477,9 @@ class DraftListService(BaseService[DraftList]): return False # Find entry below (rank + 1) - below_entry = next((e for e in entries if e.rank == current_entry.rank + 1), None) + below_entry = next( + (e for e in entries if e.rank == current_entry.rank + 1), None + ) if not below_entry: logger.error(f"Could not find entry below rank {current_entry.rank}") return False @@ -503,25 +497,29 @@ class DraftListService(BaseService[DraftList]): # Keep existing rank new_rank = entry.rank - draft_list_entries.append({ - 'season': entry.season, - 'team_id': entry.team_id, - 'player_id': entry.player_id, - 'rank': new_rank - }) + draft_list_entries.append( + { + "season": entry.season, + "team_id": entry.team_id, + "player_id": entry.player_id, + "rank": new_rank, + } + ) # Sort by rank - draft_list_entries.sort(key=lambda x: x['rank']) + draft_list_entries.sort(key=lambda x: x["rank"]) # POST updated list (bulk replacement) client = await self.get_client() payload = { - 'count': len(draft_list_entries), - 'draft_list': draft_list_entries + "count": len(draft_list_entries), + "draft_list": draft_list_entries, } - await client.post(self.endpoint, payload) - logger.info(f"Moved player {player_id} down to rank {current_entry.rank + 1}") + await client.post(f"{self.endpoint}/", payload) + logger.info( + f"Moved player {player_id} down to rank {current_entry.rank + 1}" + ) return True diff --git a/services/injury_service.py b/services/injury_service.py index 555a7a5..0164447 100644 --- a/services/injury_service.py +++ b/services/injury_service.py @@ -3,13 +3,14 @@ Injury service for Discord Bot v2.0 Handles injury-related operations including checking, creating, and clearing injuries. """ + import logging from typing import Optional, List from services.base_service import BaseService from models.injury import Injury -logger = logging.getLogger(f'{__name__}.InjuryService') +logger = logging.getLogger(f"{__name__}.InjuryService") class InjuryService(BaseService[Injury]): @@ -25,7 +26,7 @@ class InjuryService(BaseService[Injury]): def __init__(self): """Initialize injury service.""" - super().__init__(Injury, 'injuries') + super().__init__(Injury, "injuries") logger.debug("InjuryService initialized") async def get_active_injury(self, player_id: int, season: int) -> Optional[Injury]: @@ -41,25 +42,31 @@ class InjuryService(BaseService[Injury]): """ try: params = [ - ('player_id', str(player_id)), - ('season', str(season)), - ('is_active', 'true') + ("player_id", str(player_id)), + ("season", str(season)), + ("is_active", "true"), ] injuries = await self.get_all_items(params=params) if injuries: - logger.debug(f"Found active injury for player {player_id} in season {season}") + logger.debug( + f"Found active injury for player {player_id} in season {season}" + ) return injuries[0] - logger.debug(f"No active injury found for player {player_id} in season {season}") + logger.debug( + f"No active injury found for player {player_id} in season {season}" + ) return None except Exception as e: logger.error(f"Error getting active injury for player {player_id}: {e}") return None - async def get_injuries_by_player(self, player_id: int, season: int, active_only: bool = False) -> List[Injury]: + async def get_injuries_by_player( + self, player_id: int, season: int, active_only: bool = False + ) -> List[Injury]: """ Get all injuries for a player in a specific season. @@ -72,13 +79,10 @@ class InjuryService(BaseService[Injury]): List of injuries for the player """ try: - params = [ - ('player_id', str(player_id)), - ('season', str(season)) - ] + params = [("player_id", str(player_id)), ("season", str(season))] if active_only: - params.append(('is_active', 'true')) + params.append(("is_active", "true")) injuries = await self.get_all_items(params=params) logger.debug(f"Retrieved {len(injuries)} injuries for player {player_id}") @@ -88,7 +92,9 @@ class InjuryService(BaseService[Injury]): logger.error(f"Error getting injuries for player {player_id}: {e}") return [] - async def get_injuries_by_team(self, team_id: int, season: int, active_only: bool = True) -> List[Injury]: + async def get_injuries_by_team( + self, team_id: int, season: int, active_only: bool = True + ) -> List[Injury]: """ Get all injuries for a team in a specific season. @@ -101,13 +107,10 @@ class InjuryService(BaseService[Injury]): List of injuries for the team """ try: - params = [ - ('team_id', str(team_id)), - ('season', str(season)) - ] + params = [("team_id", str(team_id)), ("season", str(season))] if active_only: - params.append(('is_active', 'true')) + params.append(("is_active", "true")) injuries = await self.get_all_items(params=params) logger.debug(f"Retrieved {len(injuries)} injuries for team {team_id}") @@ -125,7 +128,7 @@ class InjuryService(BaseService[Injury]): start_week: int, start_game: int, end_week: int, - end_game: int + end_game: int, ) -> Optional[Injury]: """ Create a new injury record. @@ -144,22 +147,24 @@ class InjuryService(BaseService[Injury]): """ try: injury_data = { - 'season': season, - 'player_id': player_id, - 'total_games': total_games, - 'start_week': start_week, - 'start_game': start_game, - 'end_week': end_week, - 'end_game': end_game, - 'is_active': True + "season": season, + "player_id": player_id, + "total_games": total_games, + "start_week": start_week, + "start_game": start_game, + "end_week": end_week, + "end_game": end_game, + "is_active": True, } # Call the API to create the injury client = await self.get_client() - response = await client.post(self.endpoint, injury_data) + response = await client.post(f"{self.endpoint}/", injury_data) if not response: - logger.error(f"Failed to create injury for player {player_id}: No response from API") + logger.error( + f"Failed to create injury for player {player_id}: No response from API" + ) return None # Merge the request data with the response to ensure all required fields are present @@ -187,7 +192,9 @@ class InjuryService(BaseService[Injury]): """ try: # Note: API expects is_active as query parameter, not JSON body - updated_injury = await self.patch(injury_id, {'is_active': False}, use_query_params=True) + updated_injury = await self.patch( + injury_id, {"is_active": False}, use_query_params=True + ) if updated_injury: logger.info(f"Cleared injury {injury_id}") @@ -216,16 +223,18 @@ class InjuryService(BaseService[Injury]): try: client = await self.get_client() params = [ - ('season', str(season)), - ('is_active', 'true'), - ('sort', 'return-asc') + ("season", str(season)), + ("is_active", "true"), + ("sort", "return-asc"), ] response = await client.get(self.endpoint, params=params) - if response and 'injuries' in response: - logger.debug(f"Retrieved {len(response['injuries'])} active injuries for season {season}") - return response['injuries'] + if response and "injuries" in response: + logger.debug( + f"Retrieved {len(response['injuries'])} active injuries for season {season}" + ) + return response["injuries"] logger.debug(f"No active injuries found for season {season}") return [] diff --git a/services/transaction_service.py b/services/transaction_service.py index 80ce0e5..57c9c90 100644 --- a/services/transaction_service.py +++ b/services/transaction_service.py @@ -248,7 +248,7 @@ class TransactionService(BaseService[Transaction]): # POST batch to API client = await self.get_client() - response = await client.post(self.endpoint, data=batch_data) + response = await client.post(f"{self.endpoint}/", data=batch_data) # API returns a string like "2 transactions have been added" # We need to return the original Transaction objects (they won't have IDs assigned by API) diff --git a/tests/test_services_base_service.py b/tests/test_services_base_service.py index 4ef6602..19a3d42 100644 --- a/tests/test_services_base_service.py +++ b/tests/test_services_base_service.py @@ -1,6 +1,7 @@ """ Tests for BaseService functionality """ + import pytest from unittest.mock import AsyncMock @@ -10,6 +11,7 @@ from models.base import SBABaseModel class MockModel(SBABaseModel): """Mock model for testing BaseService.""" + id: int name: str value: int = 100 @@ -17,240 +19,229 @@ class MockModel(SBABaseModel): class TestBaseService: """Test BaseService functionality.""" - + @pytest.fixture def mock_client(self): """Mock API client.""" client = AsyncMock() return client - + @pytest.fixture def base_service(self, mock_client): """Create BaseService instance for testing.""" - service = BaseService(MockModel, 'mocks', client=mock_client) + service = BaseService(MockModel, "mocks", client=mock_client) return service - + @pytest.mark.asyncio async def test_init(self): """Test service initialization.""" - service = BaseService(MockModel, 'test_endpoint') + service = BaseService(MockModel, "test_endpoint") assert service.model_class == MockModel - assert service.endpoint == 'test_endpoint' + assert service.endpoint == "test_endpoint" assert service._client is None - + @pytest.mark.asyncio async def test_get_by_id_success(self, base_service, mock_client): """Test successful get_by_id.""" - mock_data = {'id': 1, 'name': 'Test', 'value': 200} + mock_data = {"id": 1, "name": "Test", "value": 200} mock_client.get.return_value = mock_data - + result = await base_service.get_by_id(1) - + assert isinstance(result, MockModel) assert result.id == 1 - assert result.name == 'Test' + assert result.name == "Test" assert result.value == 200 - mock_client.get.assert_called_once_with('mocks', object_id=1) - + mock_client.get.assert_called_once_with("mocks", object_id=1) + @pytest.mark.asyncio async def test_get_by_id_not_found(self, base_service, mock_client): """Test get_by_id when object not found.""" mock_client.get.return_value = None - + result = await base_service.get_by_id(999) - + assert result is None - mock_client.get.assert_called_once_with('mocks', object_id=999) - + mock_client.get.assert_called_once_with("mocks", object_id=999) + @pytest.mark.asyncio async def test_get_all_with_count(self, base_service, mock_client): """Test get_all with count response format.""" mock_data = { - 'count': 2, - 'mocks': [ - {'id': 1, 'name': 'Test1', 'value': 100}, - {'id': 2, 'name': 'Test2', 'value': 200} - ] + "count": 2, + "mocks": [ + {"id": 1, "name": "Test1", "value": 100}, + {"id": 2, "name": "Test2", "value": 200}, + ], } mock_client.get.return_value = mock_data - + result, count = await base_service.get_all() - + assert len(result) == 2 assert count == 2 assert all(isinstance(item, MockModel) for item in result) - mock_client.get.assert_called_once_with('mocks', params=None) - + mock_client.get.assert_called_once_with("mocks", params=None) + @pytest.mark.asyncio async def test_get_all_items_convenience(self, base_service, mock_client): """Test get_all_items convenience method.""" - mock_data = { - 'count': 1, - 'mocks': [{'id': 1, 'name': 'Test', 'value': 100}] - } + mock_data = {"count": 1, "mocks": [{"id": 1, "name": "Test", "value": 100}]} mock_client.get.return_value = mock_data - + result = await base_service.get_all_items() - + assert len(result) == 1 assert isinstance(result[0], MockModel) - + @pytest.mark.asyncio async def test_create_success(self, base_service, mock_client): """Test successful object creation.""" - input_data = {'name': 'New Item', 'value': 300} - response_data = {'id': 3, 'name': 'New Item', 'value': 300} + input_data = {"name": "New Item", "value": 300} + response_data = {"id": 3, "name": "New Item", "value": 300} mock_client.post.return_value = response_data - + result = await base_service.create(input_data) - + assert isinstance(result, MockModel) assert result.id == 3 - assert result.name == 'New Item' - mock_client.post.assert_called_once_with('mocks', input_data) - + assert result.name == "New Item" + mock_client.post.assert_called_once_with("mocks/", input_data) + @pytest.mark.asyncio async def test_update_success(self, base_service, mock_client): """Test successful object update.""" - update_data = {'name': 'Updated'} - response_data = {'id': 1, 'name': 'Updated', 'value': 100} + update_data = {"name": "Updated"} + response_data = {"id": 1, "name": "Updated", "value": 100} mock_client.put.return_value = response_data - + result = await base_service.update(1, update_data) - + assert isinstance(result, MockModel) - assert result.name == 'Updated' - mock_client.put.assert_called_once_with('mocks', update_data, object_id=1) - + assert result.name == "Updated" + mock_client.put.assert_called_once_with("mocks", update_data, object_id=1) + @pytest.mark.asyncio async def test_delete_success(self, base_service, mock_client): """Test successful object deletion.""" mock_client.delete.return_value = True - + result = await base_service.delete(1) - + assert result is True - mock_client.delete.assert_called_once_with('mocks', object_id=1) - - + mock_client.delete.assert_called_once_with("mocks", object_id=1) + @pytest.mark.asyncio async def test_get_by_field(self, base_service, mock_client): """Test get_by_field functionality.""" - mock_data = { - 'count': 1, - 'mocks': [{'id': 1, 'name': 'Test', 'value': 100}] - } + mock_data = {"count": 1, "mocks": [{"id": 1, "name": "Test", "value": 100}]} mock_client.get.return_value = mock_data - - result = await base_service.get_by_field('name', 'Test') - + + result = await base_service.get_by_field("name", "Test") + assert len(result) == 1 - mock_client.get.assert_called_once_with('mocks', params=[('name', 'Test')]) - + mock_client.get.assert_called_once_with("mocks", params=[("name", "Test")]) + def test_extract_items_and_count_standard_format(self, base_service): """Test response parsing for standard format.""" data = { - 'count': 3, - 'mocks': [ - {'id': 1, 'name': 'Test1'}, - {'id': 2, 'name': 'Test2'}, - {'id': 3, 'name': 'Test3'} - ] + "count": 3, + "mocks": [ + {"id": 1, "name": "Test1"}, + {"id": 2, "name": "Test2"}, + {"id": 3, "name": "Test3"}, + ], } - + items, count = base_service._extract_items_and_count_from_response(data) - + assert len(items) == 3 assert count == 3 - assert items[0]['name'] == 'Test1' - + assert items[0]["name"] == "Test1" + def test_extract_items_and_count_single_object(self, base_service): """Test response parsing for single object.""" - data = {'id': 1, 'name': 'Single'} - + data = {"id": 1, "name": "Single"} + items, count = base_service._extract_items_and_count_from_response(data) - + assert len(items) == 1 assert count == 1 assert items[0] == data - + def test_extract_items_and_count_direct_list(self, base_service): """Test response parsing for direct list.""" - data = [ - {'id': 1, 'name': 'Test1'}, - {'id': 2, 'name': 'Test2'} - ] - + data = [{"id": 1, "name": "Test1"}, {"id": 2, "name": "Test2"}] + items, count = base_service._extract_items_and_count_from_response(data) - + assert len(items) == 2 assert count == 2 class TestBaseServiceExtras: """Additional coverage tests for BaseService edge cases.""" - + @pytest.mark.asyncio async def test_base_service_additional_methods(self): """Test additional BaseService methods for coverage.""" from services.base_service import BaseService from models.base import SBABaseModel - + class TestModel(SBABaseModel): name: str value: int = 100 - + mock_client = AsyncMock() - service = BaseService(TestModel, 'test', client=mock_client) - - + service = BaseService(TestModel, "test", client=mock_client) + # Test count method mock_client.reset_mock() - mock_client.get.return_value = {'count': 42, 'test': []} - count = await service.count(params=[('active', 'true')]) + mock_client.get.return_value = {"count": 42, "test": []} + count = await service.count(params=[("active", "true")]) assert count == 42 - + # Test update_from_model with ID mock_client.reset_mock() model = TestModel(id=1, name="Updated", value=300) mock_client.put.return_value = {"id": 1, "name": "Updated", "value": 300} result = await service.update_from_model(model) assert result.name == "Updated" - + # Test update_from_model without ID model_no_id = TestModel(name="Test") with pytest.raises(ValueError, match="Cannot update TestModel without ID"): await service.update_from_model(model_no_id) - + def test_base_service_response_parsing_edge_cases(self): """Test edge cases in response parsing.""" from services.base_service import BaseService from models.base import SBABaseModel - + class TestModel(SBABaseModel): name: str - - service = BaseService(TestModel, 'test') - + + service = BaseService(TestModel, "test") + # Test with 'items' field - data = {'count': 2, 'items': [{'name': 'Item1'}, {'name': 'Item2'}]} + data = {"count": 2, "items": [{"name": "Item1"}, {"name": "Item2"}]} items, count = service._extract_items_and_count_from_response(data) assert len(items) == 2 assert count == 2 - + # Test with 'data' field - data = {'count': 1, 'data': [{'name': 'DataItem'}]} + data = {"count": 1, "data": [{"name": "DataItem"}]} items, count = service._extract_items_and_count_from_response(data) assert len(items) == 1 assert count == 1 - + # Test with count but no recognizable list field - data = {'count': 5, 'unknown_field': [{'name': 'Item'}]} + data = {"count": 5, "unknown_field": [{"name": "Item"}]} items, count = service._extract_items_and_count_from_response(data) assert len(items) == 0 assert count == 5 - + # Test with unexpected data type items, count = service._extract_items_and_count_from_response("unexpected") assert len(items) == 0 - assert count == 0 \ No newline at end of file + assert count == 0