feat(api): add unified response format utilities (Section 1, Step 1.1-1.3)

- Create response.py with success_response(), error_response(), paginated_response()
- Add unit tests for all response utility functions
- Create /api/ping/ health check endpoint using new format
- Update __init__.py to export response functions
- All changes are non-breaking - existing code unaffected

This implements Section 1 Task 1 from API-IMPLEMENTATION-PLAN-SECTION1.md
Ready for testing before applying to existing endpoints.

Ref: unified-api/API-IMPLEMENTATION-PLAN-SECTION1.md
This commit is contained in:
Desktop
2025-11-14 20:05:22 +05:00
parent 069e0a24d8
commit a722f6caa3
6 changed files with 352 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
"""
API Tests Module
"""

View File

@@ -0,0 +1,198 @@
"""
Unit Tests for Response Utility Functions
Tests all response wrapper functions to ensure they return
the correct unified format.
"""
from django.test import TestCase
from rest_framework import status
from igny8_core.api.response import success_response, error_response, paginated_response
class SuccessResponseTestCase(TestCase):
"""Test cases for success_response function"""
def test_success_response_with_data_only(self):
"""Test success_response with data only"""
data = {"id": 1, "name": "Test"}
response = success_response(data=data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response_data = response.data
self.assertTrue(response_data['success'])
self.assertEqual(response_data['data'], data)
self.assertNotIn('message', response_data)
def test_success_response_with_data_and_message(self):
"""Test success_response with data and message"""
data = {"id": 1, "name": "Test"}
message = "Resource created successfully"
response = success_response(data=data, message=message)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response_data = response.data
self.assertTrue(response_data['success'])
self.assertEqual(response_data['data'], data)
self.assertEqual(response_data['message'], message)
def test_success_response_with_custom_status_code_201(self):
"""Test success_response with 201 Created status"""
data = {"id": 1, "name": "Test"}
response = success_response(data=data, status_code=status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response_data = response.data
self.assertTrue(response_data['success'])
self.assertEqual(response_data['data'], data)
def test_success_response_with_custom_status_code_204(self):
"""Test success_response with 204 No Content status"""
response = success_response(status_code=status.HTTP_204_NO_CONTENT)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
response_data = response.data
self.assertTrue(response_data['success'])
def test_success_response_with_list_data(self):
"""Test success_response with list data"""
data = [{"id": 1}, {"id": 2}, {"id": 3}]
response = success_response(data=data)
response_data = response.data
self.assertTrue(response_data['success'])
self.assertEqual(response_data['data'], data)
self.assertIsInstance(response_data['data'], list)
def test_success_response_with_none_data(self):
"""Test success_response with None data"""
response = success_response(data=None)
response_data = response.data
self.assertTrue(response_data['success'])
self.assertIsNone(response_data['data'])
class ErrorResponseTestCase(TestCase):
"""Test cases for error_response function"""
def test_error_response_with_error_only(self):
"""Test error_response with error message only"""
error_msg = "Something went wrong"
response = error_response(error=error_msg)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
response_data = response.data
self.assertFalse(response_data['success'])
self.assertEqual(response_data['error'], error_msg)
self.assertNotIn('errors', response_data)
def test_error_response_with_error_and_errors_dict(self):
"""Test error_response with error and field-specific errors"""
error_msg = "Validation failed"
errors = {
"email": ["Invalid email format"],
"password": ["Password must be at least 8 characters"]
}
response = error_response(error=error_msg, errors=errors)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
response_data = response.data
self.assertFalse(response_data['success'])
self.assertEqual(response_data['error'], error_msg)
self.assertEqual(response_data['errors'], errors)
def test_error_response_with_custom_status_code_403(self):
"""Test error_response with 403 Forbidden status"""
error_msg = "Permission denied"
response = error_response(error=error_msg, status_code=status.HTTP_403_FORBIDDEN)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
response_data = response.data
self.assertFalse(response_data['success'])
self.assertEqual(response_data['error'], error_msg)
def test_error_response_with_custom_status_code_404(self):
"""Test error_response with 404 Not Found status"""
error_msg = "Resource not found"
response = error_response(error=error_msg, status_code=status.HTTP_404_NOT_FOUND)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
response_data = response.data
self.assertFalse(response_data['success'])
self.assertEqual(response_data['error'], error_msg)
def test_error_response_with_custom_status_code_500(self):
"""Test error_response with 500 Internal Server Error status"""
error_msg = "Internal server error"
response = error_response(error=error_msg, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR)
response_data = response.data
self.assertFalse(response_data['success'])
self.assertEqual(response_data['error'], error_msg)
def test_error_response_backward_compatibility_with_message(self):
"""Test error_response backward compatibility with message parameter"""
message = "Old error message"
response = error_response(error=None, message=message)
response_data = response.data
self.assertFalse(response_data['success'])
self.assertEqual(response_data['error'], message)
class PaginatedResponseTestCase(TestCase):
"""Test cases for paginated_response function"""
def test_paginated_response_with_standard_data(self):
"""Test paginated_response with standard pagination data"""
paginated_data = {
"count": 100,
"next": "http://api.example.com/endpoint/?page=2",
"previous": None,
"results": [{"id": 1}, {"id": 2}]
}
response = paginated_response(paginated_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response_data = response.data
self.assertTrue(response_data['success'])
self.assertEqual(response_data['count'], 100)
self.assertEqual(response_data['next'], paginated_data['next'])
self.assertIsNone(response_data['previous'])
self.assertEqual(response_data['results'], paginated_data['results'])
def test_paginated_response_with_message(self):
"""Test paginated_response with optional message"""
paginated_data = {
"count": 50,
"next": None,
"previous": None,
"results": [{"id": 1}]
}
message = "Keywords retrieved successfully"
response = paginated_response(paginated_data, message=message)
response_data = response.data
self.assertTrue(response_data['success'])
self.assertEqual(response_data['message'], message)
self.assertEqual(response_data['count'], 50)
self.assertEqual(response_data['results'], paginated_data['results'])
def test_paginated_response_without_message(self):
"""Test paginated_response without message"""
paginated_data = {
"count": 25,
"next": "http://api.example.com/endpoint/?page=3",
"previous": "http://api.example.com/endpoint/?page=1",
"results": []
}
response = paginated_response(paginated_data)
response_data = response.data
self.assertTrue(response_data['success'])
self.assertNotIn('message', response_data)
self.assertEqual(response_data['count'], 25)
self.assertEqual(len(response_data['results']), 0)