import pytest import aiohttp from unittest.mock import Mock, patch, AsyncMock from exceptions import DatabaseError import api_calls class TestUtilityFunctions: """Test utility functions in api_calls.""" def test_param_char_with_params(self): """Test param_char returns & when other_params is truthy.""" assert api_calls.param_char(True) == '&' assert api_calls.param_char(['param1']) == '&' assert api_calls.param_char({'key': 'value'}) == '&' assert api_calls.param_char('some_param') == '&' def test_param_char_without_params(self): """Test param_char returns ? when other_params is falsy.""" assert api_calls.param_char(False) == '?' assert api_calls.param_char(None) == '?' assert api_calls.param_char([]) == '?' assert api_calls.param_char({}) == '?' assert api_calls.param_char('') == '?' assert api_calls.param_char(0) == '?' @patch('api_calls.DB_URL', 'https://test.example.com/api') def test_get_req_url_basic(self): """Test basic URL generation without object_id or params.""" result = api_calls.get_req_url('teams') expected = 'https://test.example.com/api/v2/teams' assert result == expected @patch('api_calls.DB_URL', 'https://test.example.com/api') def test_get_req_url_with_version(self): """Test URL generation with custom API version.""" result = api_calls.get_req_url('teams', api_ver=1) expected = 'https://test.example.com/api/v1/teams' assert result == expected @patch('api_calls.DB_URL', 'https://test.example.com/api') def test_get_req_url_with_object_id(self): """Test URL generation with object_id.""" result = api_calls.get_req_url('teams', object_id=123) expected = 'https://test.example.com/api/v2/teams/123' assert result == expected @patch('api_calls.DB_URL', 'https://test.example.com/api') def test_get_req_url_with_params(self): """Test URL generation with parameters.""" params = [('season', '7'), ('active', 'true')] result = api_calls.get_req_url('teams', params=params) expected = 'https://test.example.com/api/v2/teams?season=7&active=true' assert result == expected @patch('api_calls.DB_URL', 'https://test.example.com/api') def test_get_req_url_complete(self): """Test URL generation with all parameters.""" params = [('season', '7'), ('limit', '10')] result = api_calls.get_req_url('games', api_ver=1, object_id=456, params=params) expected = 'https://test.example.com/api/v1/games/456?season=7&limit=10' assert result == expected @patch('api_calls.logger') def test_log_return_value_short_string(self, mock_logger): """Test logging short return values.""" api_calls.log_return_value('Short log message') mock_logger.info.assert_called_once_with('\n\nreturn: Short log message') @patch('api_calls.logger') def test_log_return_value_long_string(self, mock_logger): """Test logging long return values that get chunked.""" long_string = 'A' * 5000 # 5000 character string api_calls.log_return_value(long_string) # Should have been called twice (first chunk + second chunk) assert mock_logger.info.call_count == 2 # First call should include the "return:" prefix assert '\n\nreturn: ' in mock_logger.info.call_args_list[0][0][0] @patch('api_calls.logger') def test_log_return_value_extremely_long_string(self, mock_logger): """Test logging extremely long return values that get snipped.""" extremely_long_string = 'B' * 400000 # 400k character string (exceeds 300k limit) api_calls.log_return_value(extremely_long_string) # Should warn about snipping mock_logger.warning.assert_called_with('[ S N I P P E D ]') def test_team_hash(self): """Test team hash generation.""" mock_team = { 'sname': 'TestTeam', 'gmid': 1234567 } result = api_calls.team_hash(mock_team) # Expected format: last char + gmid/6950123 + second-to-last char + gmid/42069123 expected = f'm{1234567 / 6950123:.0f}a{1234567 / 42069123:.0f}' assert result == expected # Note: Async database function tests are complex due to aiohttp mocking # For now, focusing on utility functions which provide significant coverage improvement class TestSpecificFunctions: """Test specific API wrapper functions.""" @pytest.mark.asyncio @patch('api_calls.db_get') async def test_get_team_by_abbrev_found(self, mock_db_get): """Test get_team_by_abbrev function when team is found.""" mock_db_get.return_value = { 'count': 1, 'teams': [{'id': 123, 'abbrev': 'TEST', 'name': 'Test Team'}] } result = await api_calls.get_team_by_abbrev('TEST') assert result == {'id': 123, 'abbrev': 'TEST', 'name': 'Test Team'} mock_db_get.assert_called_once_with('teams', params=[('abbrev', 'TEST')]) @pytest.mark.asyncio @patch('api_calls.db_get') async def test_get_team_by_abbrev_not_found(self, mock_db_get): """Test get_team_by_abbrev function when team is not found.""" mock_db_get.return_value = { 'count': 0, 'teams': [] } result = await api_calls.get_team_by_abbrev('NONEXISTENT') assert result is None mock_db_get.assert_called_once_with('teams', params=[('abbrev', 'NONEXISTENT')]) @pytest.mark.asyncio @patch('api_calls.db_post') async def test_post_to_dex(self, mock_db_post): """Test post_to_dex function.""" mock_db_post.return_value = {'id': 456, 'posted': True} mock_player = {'id': 123} mock_team = {'id': 456} result = await api_calls.post_to_dex(mock_player, mock_team) assert result == {'id': 456, 'posted': True} mock_db_post.assert_called_once_with('paperdex', payload={'player_id': 123, 'team_id': 456}) class TestEnvironmentConfiguration: """Test environment-based configuration.""" def test_db_url_exists(self): """Test that DB_URL is configured.""" assert api_calls.DB_URL is not None assert 'manticorum.com' in api_calls.DB_URL def test_auth_token_exists(self): """Test that AUTH_TOKEN is configured.""" assert api_calls.AUTH_TOKEN is not None assert 'Authorization' in api_calls.AUTH_TOKEN