""" Test cases for response helper utilities. """ import pytest from unittest.mock import Mock, patch, MagicMock import json from datetime import datetime # Import the modules to test try: from src.server.web.controllers.shared.response_helpers import ( create_response, create_error_response, create_success_response, create_paginated_response, format_anime_data, format_episode_data, format_download_data, format_user_data, format_datetime, format_file_size, add_cors_headers, create_api_response ) except ImportError: # Fallback for testing create_response = None create_error_response = None create_success_response = None create_paginated_response = None format_anime_data = None format_episode_data = None format_download_data = None format_user_data = None format_datetime = None format_file_size = None add_cors_headers = None create_api_response = None class TestResponseCreation: """Test cases for response creation functions.""" def test_create_response_success(self): """Test create_response with success data.""" if not create_response: pytest.skip("Module not available") data = {'test': 'data'} response, status_code = create_response(data, 200) assert status_code == 200 response_data = json.loads(response.data) assert response_data['test'] == 'data' assert response.status_code == 200 def test_create_response_with_headers(self): """Test create_response with custom headers.""" if not create_response: pytest.skip("Module not available") data = {'test': 'data'} headers = {'X-Custom-Header': 'test-value'} response, status_code = create_response(data, 200, headers) assert response.headers.get('X-Custom-Header') == 'test-value' def test_create_error_response_basic(self): """Test create_error_response with basic error.""" if not create_error_response: pytest.skip("Module not available") response, status_code = create_error_response("Test error", 400) assert status_code == 400 response_data = json.loads(response.data) assert response_data['error'] == 'Test error' assert response_data['status'] == 'error' def test_create_error_response_with_details(self): """Test create_error_response with error details.""" if not create_error_response: pytest.skip("Module not available") details = {'field': 'name', 'issue': 'required'} response, status_code = create_error_response("Validation error", 422, details) assert status_code == 422 response_data = json.loads(response.data) assert response_data['error'] == 'Validation error' assert response_data['details'] == details def test_create_success_response_basic(self): """Test create_success_response with basic success.""" if not create_success_response: pytest.skip("Module not available") response, status_code = create_success_response("Operation successful") assert status_code == 200 response_data = json.loads(response.data) assert response_data['message'] == 'Operation successful' assert response_data['status'] == 'success' def test_create_success_response_with_data(self): """Test create_success_response with data.""" if not create_success_response: pytest.skip("Module not available") data = {'created_id': 123} response, status_code = create_success_response("Created successfully", 201, data) assert status_code == 201 response_data = json.loads(response.data) assert response_data['message'] == 'Created successfully' assert response_data['data'] == data def test_create_api_response_success(self): """Test create_api_response for success case.""" if not create_api_response: pytest.skip("Module not available") data = {'test': 'data'} response, status_code = create_api_response(data, success=True) assert status_code == 200 response_data = json.loads(response.data) assert response_data['success'] is True assert response_data['data'] == data def test_create_api_response_error(self): """Test create_api_response for error case.""" if not create_api_response: pytest.skip("Module not available") error_msg = "Something went wrong" response, status_code = create_api_response(error_msg, success=False, status_code=500) assert status_code == 500 response_data = json.loads(response.data) assert response_data['success'] is False assert response_data['error'] == error_msg class TestPaginatedResponse: """Test cases for paginated response creation.""" def test_create_paginated_response_basic(self): """Test create_paginated_response with basic pagination.""" if not create_paginated_response: pytest.skip("Module not available") items = [{'id': 1}, {'id': 2}, {'id': 3}] page = 1 per_page = 10 total = 25 response, status_code = create_paginated_response(items, page, per_page, total) assert status_code == 200 response_data = json.loads(response.data) assert response_data['data'] == items assert response_data['pagination']['page'] == 1 assert response_data['pagination']['per_page'] == 10 assert response_data['pagination']['total'] == 25 assert response_data['pagination']['pages'] == 3 # ceil(25/10) def test_create_paginated_response_with_endpoint(self): """Test create_paginated_response with endpoint for links.""" if not create_paginated_response: pytest.skip("Module not available") items = [{'id': 1}] page = 2 per_page = 5 total = 20 endpoint = '/api/items' response, status_code = create_paginated_response( items, page, per_page, total, endpoint=endpoint ) response_data = json.loads(response.data) links = response_data['pagination']['links'] assert '/api/items?page=1' in links['first'] assert '/api/items?page=3' in links['next'] assert '/api/items?page=1' in links['prev'] assert '/api/items?page=4' in links['last'] def test_create_paginated_response_first_page(self): """Test create_paginated_response on first page.""" if not create_paginated_response: pytest.skip("Module not available") items = [{'id': 1}] response, status_code = create_paginated_response(items, 1, 10, 20) response_data = json.loads(response.data) pagination = response_data['pagination'] assert pagination['has_prev'] is False assert pagination['has_next'] is True def test_create_paginated_response_last_page(self): """Test create_paginated_response on last page.""" if not create_paginated_response: pytest.skip("Module not available") items = [{'id': 1}] response, status_code = create_paginated_response(items, 3, 10, 25) response_data = json.loads(response.data) pagination = response_data['pagination'] assert pagination['has_prev'] is True assert pagination['has_next'] is False def test_create_paginated_response_empty(self): """Test create_paginated_response with empty results.""" if not create_paginated_response: pytest.skip("Module not available") response, status_code = create_paginated_response([], 1, 10, 0) response_data = json.loads(response.data) assert response_data['data'] == [] assert response_data['pagination']['total'] == 0 assert response_data['pagination']['pages'] == 0 class TestDataFormatting: """Test cases for data formatting functions.""" def test_format_anime_data(self): """Test format_anime_data function.""" if not format_anime_data: pytest.skip("Module not available") anime = { 'id': 1, 'name': 'Test Anime', 'url': 'https://example.com/anime/1', 'description': 'A test anime', 'episodes': 12, 'status': 'completed', 'created_at': '2023-01-01 12:00:00', 'updated_at': '2023-01-02 12:00:00' } formatted = format_anime_data(anime) assert formatted['id'] == 1 assert formatted['name'] == 'Test Anime' assert formatted['url'] == 'https://example.com/anime/1' assert formatted['description'] == 'A test anime' assert formatted['episodes'] == 12 assert formatted['status'] == 'completed' assert 'created_at' in formatted assert 'updated_at' in formatted def test_format_anime_data_with_episodes(self): """Test format_anime_data with episode information.""" if not format_anime_data: pytest.skip("Module not available") anime = { 'id': 1, 'name': 'Test Anime', 'url': 'https://example.com/anime/1' } episodes = [ {'id': 1, 'number': 1, 'title': 'Episode 1'}, {'id': 2, 'number': 2, 'title': 'Episode 2'} ] formatted = format_anime_data(anime, include_episodes=True, episodes=episodes) assert 'episodes' in formatted assert len(formatted['episodes']) == 2 assert formatted['episodes'][0]['number'] == 1 def test_format_episode_data(self): """Test format_episode_data function.""" if not format_episode_data: pytest.skip("Module not available") episode = { 'id': 1, 'anime_id': 5, 'number': 1, 'title': 'First Episode', 'url': 'https://example.com/episode/1', 'duration': 1440, # 24 minutes in seconds 'status': 'available', 'created_at': '2023-01-01 12:00:00' } formatted = format_episode_data(episode) assert formatted['id'] == 1 assert formatted['anime_id'] == 5 assert formatted['number'] == 1 assert formatted['title'] == 'First Episode' assert formatted['url'] == 'https://example.com/episode/1' assert formatted['duration'] == 1440 assert formatted['status'] == 'available' assert 'created_at' in formatted def test_format_download_data(self): """Test format_download_data function.""" if not format_download_data: pytest.skip("Module not available") download = { 'id': 1, 'anime_id': 5, 'episode_id': 10, 'status': 'downloading', 'progress': 75.5, 'size': 1073741824, # 1GB in bytes 'downloaded_size': 805306368, # 768MB 'speed': 1048576, # 1MB/s 'eta': 300, # 5 minutes 'created_at': '2023-01-01 12:00:00', 'started_at': '2023-01-01 12:05:00' } formatted = format_download_data(download) assert formatted['id'] == 1 assert formatted['anime_id'] == 5 assert formatted['episode_id'] == 10 assert formatted['status'] == 'downloading' assert formatted['progress'] == 75.5 assert formatted['size'] == 1073741824 assert formatted['downloaded_size'] == 805306368 assert formatted['speed'] == 1048576 assert formatted['eta'] == 300 assert 'created_at' in formatted assert 'started_at' in formatted def test_format_user_data(self): """Test format_user_data function.""" if not format_user_data: pytest.skip("Module not available") user = { 'id': 1, 'username': 'testuser', 'email': 'test@example.com', 'password_hash': 'secret_hash', 'role': 'user', 'last_login': '2023-01-01 12:00:00', 'created_at': '2023-01-01 10:00:00' } formatted = format_user_data(user) assert formatted['id'] == 1 assert formatted['username'] == 'testuser' assert formatted['email'] == 'test@example.com' assert formatted['role'] == 'user' assert 'last_login' in formatted assert 'created_at' in formatted # Should not include sensitive data assert 'password_hash' not in formatted def test_format_user_data_include_sensitive(self): """Test format_user_data with sensitive data included.""" if not format_user_data: pytest.skip("Module not available") user = { 'id': 1, 'username': 'testuser', 'password_hash': 'secret_hash' } formatted = format_user_data(user, include_sensitive=True) assert 'password_hash' in formatted assert formatted['password_hash'] == 'secret_hash' class TestUtilityFormatting: """Test cases for utility formatting functions.""" def test_format_datetime_string(self): """Test format_datetime with string input.""" if not format_datetime: pytest.skip("Module not available") dt_string = "2023-01-01 12:30:45" formatted = format_datetime(dt_string) assert isinstance(formatted, str) assert "2023" in formatted assert "01" in formatted def test_format_datetime_object(self): """Test format_datetime with datetime object.""" if not format_datetime: pytest.skip("Module not available") dt_object = datetime(2023, 1, 1, 12, 30, 45) formatted = format_datetime(dt_object) assert isinstance(formatted, str) assert "2023" in formatted def test_format_datetime_none(self): """Test format_datetime with None input.""" if not format_datetime: pytest.skip("Module not available") formatted = format_datetime(None) assert formatted is None def test_format_datetime_custom_format(self): """Test format_datetime with custom format.""" if not format_datetime: pytest.skip("Module not available") dt_string = "2023-01-01 12:30:45" formatted = format_datetime(dt_string, fmt="%Y/%m/%d") assert formatted == "2023/01/01" def test_format_file_size_bytes(self): """Test format_file_size with bytes.""" if not format_file_size: pytest.skip("Module not available") assert format_file_size(512) == "512 B" assert format_file_size(0) == "0 B" def test_format_file_size_kilobytes(self): """Test format_file_size with kilobytes.""" if not format_file_size: pytest.skip("Module not available") assert format_file_size(1024) == "1.0 KB" assert format_file_size(1536) == "1.5 KB" def test_format_file_size_megabytes(self): """Test format_file_size with megabytes.""" if not format_file_size: pytest.skip("Module not available") assert format_file_size(1048576) == "1.0 MB" assert format_file_size(1572864) == "1.5 MB" def test_format_file_size_gigabytes(self): """Test format_file_size with gigabytes.""" if not format_file_size: pytest.skip("Module not available") assert format_file_size(1073741824) == "1.0 GB" assert format_file_size(2147483648) == "2.0 GB" def test_format_file_size_terabytes(self): """Test format_file_size with terabytes.""" if not format_file_size: pytest.skip("Module not available") assert format_file_size(1099511627776) == "1.0 TB" def test_format_file_size_precision(self): """Test format_file_size with custom precision.""" if not format_file_size: pytest.skip("Module not available") size = 1536 # 1.5 KB assert format_file_size(size, precision=2) == "1.50 KB" assert format_file_size(size, precision=0) == "2 KB" # Rounded up class TestCORSHeaders: """Test cases for CORS header utilities.""" def test_add_cors_headers_basic(self): """Test add_cors_headers with basic response.""" if not add_cors_headers: pytest.skip("Module not available") # Mock response object response = Mock() response.headers = {} result = add_cors_headers(response) assert result.headers['Access-Control-Allow-Origin'] == '*' assert 'GET, POST, PUT, DELETE, OPTIONS' in result.headers['Access-Control-Allow-Methods'] assert 'Content-Type, Authorization' in result.headers['Access-Control-Allow-Headers'] def test_add_cors_headers_custom_origin(self): """Test add_cors_headers with custom origin.""" if not add_cors_headers: pytest.skip("Module not available") response = Mock() response.headers = {} result = add_cors_headers(response, origin='https://example.com') assert result.headers['Access-Control-Allow-Origin'] == 'https://example.com' def test_add_cors_headers_custom_methods(self): """Test add_cors_headers with custom methods.""" if not add_cors_headers: pytest.skip("Module not available") response = Mock() response.headers = {} result = add_cors_headers(response, methods=['GET', 'POST']) assert result.headers['Access-Control-Allow-Methods'] == 'GET, POST' def test_add_cors_headers_existing_headers(self): """Test add_cors_headers preserves existing headers.""" if not add_cors_headers: pytest.skip("Module not available") response = Mock() response.headers = {'X-Custom-Header': 'custom-value'} result = add_cors_headers(response) assert result.headers['X-Custom-Header'] == 'custom-value' assert 'Access-Control-Allow-Origin' in result.headers class TestResponseIntegration: """Integration tests for response helpers.""" def test_formatted_paginated_response(self): """Test creating paginated response with formatted data.""" if not create_paginated_response or not format_anime_data: pytest.skip("Module not available") anime_list = [ {'id': 1, 'name': 'Anime 1', 'url': 'https://example.com/1'}, {'id': 2, 'name': 'Anime 2', 'url': 'https://example.com/2'} ] formatted_items = [format_anime_data(anime) for anime in anime_list] response, status_code = create_paginated_response(formatted_items, 1, 10, 2) assert status_code == 200 response_data = json.loads(response.data) assert len(response_data['data']) == 2 assert response_data['data'][0]['name'] == 'Anime 1' def test_error_response_with_cors(self): """Test error response with CORS headers.""" if not create_error_response or not add_cors_headers: pytest.skip("Module not available") response, status_code = create_error_response("Test error", 400) response_with_cors = add_cors_headers(response) assert 'Access-Control-Allow-Origin' in response_with_cors.headers assert status_code == 400 if __name__ == '__main__': pytest.main([__file__])