| """ |
| Test script for the location API endpoints. |
| This script demonstrates how to use the location API and tests basic functionality. |
| """ |
|
|
| import asyncio |
| import json |
| import logging |
| from typing import Dict, Any |
| import httpx |
|
|
| |
| logging.basicConfig(level=logging.INFO) |
| logger = logging.getLogger(__name__) |
|
|
| |
| BASE_URL = "http://localhost:8000/api" |
|
|
| class LocationAPITester: |
| def __init__(self, base_url: str = BASE_URL): |
| self.base_url = base_url |
| self.client = httpx.AsyncClient() |
| |
| async def close(self): |
| """Close the HTTP client.""" |
| await self.client.aclose() |
| |
| async def test_health_check(self) -> bool: |
| """Test the health check endpoint.""" |
| try: |
| logger.info("Testing health check...") |
| response = await self.client.get(f"{self.base_url}/locations/health") |
| |
| if response.status_code == 200: |
| logger.info("β
Health check passed") |
| return True |
| else: |
| logger.error(f"β Health check failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β Health check error: {e}") |
| return False |
| |
| async def test_list_locations(self) -> bool: |
| """Test listing all locations.""" |
| try: |
| logger.info("Testing list locations...") |
| response = await self.client.get(f"{self.base_url}/locations") |
| |
| if response.status_code == 200: |
| data = response.json() |
| logger.info(f"β
List locations passed - Found {data.get('total', 0)} locations") |
| return True |
| else: |
| logger.error(f"β List locations failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β List locations error: {e}") |
| return False |
| |
| async def test_get_countries(self) -> bool: |
| """Test getting all countries.""" |
| try: |
| logger.info("Testing get countries...") |
| response = await self.client.get(f"{self.base_url}/countries") |
| |
| if response.status_code == 200: |
| countries = response.json() |
| logger.info(f"β
Get countries passed - Found {len(countries)} countries") |
| |
| |
| for country in countries[:3]: |
| logger.info(f" π {country['name']} ({country['iso2']}) - {country['currency']['symbol']}") |
| |
| return True |
| else: |
| logger.error(f"β Get countries failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β Get countries error: {e}") |
| return False |
| |
| async def test_get_location_by_id(self, location_id: str = "country:US") -> bool: |
| """Test getting a specific location by ID.""" |
| try: |
| logger.info(f"Testing get location by ID: {location_id}") |
| response = await self.client.get(f"{self.base_url}/locations/{location_id}") |
| |
| if response.status_code == 200: |
| location = response.json() |
| logger.info(f"β
Get location by ID passed - Found: {location['name']}") |
| return True |
| elif response.status_code == 404: |
| logger.warning(f"β οΈ Location {location_id} not found (expected if not seeded)") |
| return True |
| else: |
| logger.error(f"β Get location by ID failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β Get location by ID error: {e}") |
| return False |
| |
| async def test_search_locations(self, query: str = "States") -> bool: |
| """Test searching locations.""" |
| try: |
| logger.info(f"Testing search locations with query: {query}") |
| |
| search_data = { |
| "query": query, |
| "limit": 10 |
| } |
| |
| response = await self.client.post( |
| f"{self.base_url}/locations/search", |
| json=search_data |
| ) |
| |
| if response.status_code == 200: |
| results = response.json() |
| logger.info(f"β
Search locations passed - Found {len(results)} results") |
| |
| |
| for result in results[:3]: |
| logger.info(f" π {result['name']} ({result['level']})") |
| |
| return True |
| else: |
| logger.error(f"β Search locations failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β Search locations error: {e}") |
| return False |
| |
| async def test_get_hierarchy_tree(self) -> bool: |
| """Test getting location hierarchy tree.""" |
| try: |
| logger.info("Testing get hierarchy tree...") |
| response = await self.client.get(f"{self.base_url}/locations/hierarchy/tree") |
| |
| if response.status_code == 200: |
| tree = response.json() |
| logger.info(f"β
Get hierarchy tree passed - Found {len(tree)} root nodes") |
| |
| |
| for node in tree[:3]: |
| children_count = len(node.get('children', [])) |
| logger.info(f" π³ {node['name']} ({children_count} children)") |
| |
| return True |
| else: |
| logger.error(f"β Get hierarchy tree failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β Get hierarchy tree error: {e}") |
| return False |
| |
| async def test_get_statistics(self) -> bool: |
| """Test getting location statistics.""" |
| try: |
| logger.info("Testing get statistics...") |
| response = await self.client.get(f"{self.base_url}/locations/statistics") |
| |
| if response.status_code == 200: |
| stats = response.json() |
| logger.info("β
Get statistics passed") |
| |
| |
| logger.info(f" π Total locations: {stats.get('total_locations', 0)}") |
| logger.info(f" π Active locations: {stats.get('active_locations', 0)}") |
| logger.info(f" π Countries: {stats.get('country_active', 0)}") |
| logger.info(f" π States: {stats.get('state_active', 0)}") |
| logger.info(f" π Cities: {stats.get('city_active', 0)}") |
| |
| return True |
| else: |
| logger.error(f"β Get statistics failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β Get statistics error: {e}") |
| return False |
| |
| async def test_create_location(self) -> bool: |
| """Test creating a new location.""" |
| try: |
| logger.info("Testing create location...") |
| |
| |
| test_location = { |
| "id": "country:TEST", |
| "level": "country", |
| "name": "Test Country", |
| "iso2": "TC", |
| "iso3": "TCY", |
| "code": "country:TEST", |
| "parent_code": None, |
| "hierarchy": ["country:TEST"], |
| "currency": {"code": "TST", "symbol": "T$", "decimals": 2}, |
| "phone_country_code": "+999", |
| "timezone": "UTC", |
| "flag": "https://example.com/flag.png", |
| "has_states": False, |
| "is_active": True |
| } |
| |
| response = await self.client.post( |
| f"{self.base_url}/locations", |
| json=test_location |
| ) |
| |
| if response.status_code == 201: |
| result = response.json() |
| logger.info(f"β
Create location passed - Created: {result.get('location_id')}") |
| |
| |
| await self.test_deactivate_location("country:TEST") |
| |
| return True |
| elif response.status_code == 409: |
| logger.warning("β οΈ Test location already exists (expected if run multiple times)") |
| return True |
| else: |
| logger.error(f"β Create location failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β Create location error: {e}") |
| return False |
| |
| async def test_deactivate_location(self, location_id: str) -> bool: |
| """Test deactivating a location.""" |
| try: |
| logger.info(f"Testing deactivate location: {location_id}") |
| |
| response = await self.client.patch( |
| f"{self.base_url}/locations/{location_id}/deactivate" |
| ) |
| |
| if response.status_code == 200: |
| logger.info("β
Deactivate location passed") |
| return True |
| elif response.status_code == 404: |
| logger.warning(f"β οΈ Location {location_id} not found") |
| return True |
| else: |
| logger.error(f"β Deactivate location failed: {response.status_code} - {response.text}") |
| return False |
| |
| except Exception as e: |
| logger.error(f"β Deactivate location error: {e}") |
| return False |
| |
| async def run_all_tests(self) -> Dict[str, bool]: |
| """Run all tests and return results.""" |
| logger.info("π Starting Location API Tests") |
| logger.info("=" * 60) |
| |
| tests = [ |
| ("Health Check", self.test_health_check()), |
| ("List Locations", self.test_list_locations()), |
| ("Get Countries", self.test_get_countries()), |
| ("Get Location by ID", self.test_get_location_by_id()), |
| ("Search Locations", self.test_search_locations()), |
| ("Get Hierarchy Tree", self.test_get_hierarchy_tree()), |
| ("Get Statistics", self.test_get_statistics()), |
| ("Create Location", self.test_create_location()), |
| ] |
| |
| results = {} |
| |
| for test_name, test_coro in tests: |
| logger.info(f"\nπ§ͺ Running: {test_name}") |
| try: |
| result = await test_coro |
| results[test_name] = result |
| except Exception as e: |
| logger.error(f"β {test_name} threw exception: {e}") |
| results[test_name] = False |
| |
| await asyncio.sleep(0.5) |
| |
| return results |
|
|
| async def main(): |
| """Main function to run the tests.""" |
| tester = LocationAPITester() |
| |
| try: |
| |
| results = await tester.run_all_tests() |
| |
| |
| logger.info("\n" + "=" * 60) |
| logger.info("π TEST SUMMARY") |
| logger.info("=" * 60) |
| |
| passed = 0 |
| total = len(results) |
| |
| for test_name, passed_test in results.items(): |
| status = "β
PASS" if passed_test else "β FAIL" |
| logger.info(f"{status} - {test_name}") |
| if passed_test: |
| passed += 1 |
| |
| logger.info(f"\nπ― Results: {passed}/{total} tests passed") |
| |
| if passed == total: |
| logger.info("π All tests passed! Location API is working correctly.") |
| else: |
| logger.warning(f"β οΈ {total - passed} tests failed. Check the logs above for details.") |
| |
| finally: |
| await tester.close() |
|
|
| if __name__ == "__main__": |
| |
| asyncio.run(main()) |