# services/sales/tests/integration/test_api_endpoints.py """ Integration tests for Sales API endpoints """ import pytest import json from decimal import Decimal from datetime import datetime, timezone from uuid import uuid4 @pytest.mark.asyncio class TestSalesAPIEndpoints: """Test Sales API endpoints integration""" async def test_create_sales_record_success(self, test_client, override_get_db, sample_tenant_id): """Test creating a sales record via API""" sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "Pan Integral", "product_category": "Panadería", "quantity_sold": 5, "unit_price": 2.50, "revenue": 12.50, "location_id": "STORE_001", "sales_channel": "in_store", "source": "manual" } response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert response.status_code == 201 data = response.json() assert data["product_name"] == "Pan Integral" assert data["quantity_sold"] == 5 assert "id" in data async def test_create_sales_record_validation_error(self, test_client, override_get_db, sample_tenant_id): """Test creating a sales record with validation errors""" invalid_data = { "date": "invalid-date", "product_name": "", # Empty product name "quantity_sold": -1, # Invalid quantity "revenue": -5.00 # Invalid revenue } response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=invalid_data ) assert response.status_code == 422 # Validation error async def test_get_sales_records(self, test_client, override_get_db, sample_tenant_id, populated_db): """Test getting sales records for tenant""" response = test_client.get(f"/api/v1/sales?tenant_id={sample_tenant_id}") assert response.status_code == 200 data = response.json() assert isinstance(data, list) assert len(data) >= 0 async def test_get_sales_records_with_filters(self, test_client, override_get_db, sample_tenant_id): """Test getting sales records with filters""" # First create a record sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "Croissant", "quantity_sold": 3, "revenue": 7.50, "source": "manual" } create_response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert create_response.status_code == 201 # Get with product filter response = test_client.get( f"/api/v1/sales?tenant_id={sample_tenant_id}&product_name=Croissant" ) assert response.status_code == 200 data = response.json() assert len(data) >= 1 assert all(record["product_name"] == "Croissant" for record in data) async def test_get_sales_record_by_id(self, test_client, override_get_db, sample_tenant_id): """Test getting a specific sales record""" # First create a record sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "Test Product", "quantity_sold": 1, "revenue": 5.00, "source": "manual" } create_response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert create_response.status_code == 201 created_record = create_response.json() # Get the specific record response = test_client.get( f"/api/v1/sales/{created_record['id']}?tenant_id={sample_tenant_id}" ) assert response.status_code == 200 data = response.json() assert data["id"] == created_record["id"] assert data["product_name"] == "Test Product" async def test_get_sales_record_not_found(self, test_client, override_get_db, sample_tenant_id): """Test getting a non-existent sales record""" fake_id = str(uuid4()) response = test_client.get( f"/api/v1/sales/{fake_id}?tenant_id={sample_tenant_id}" ) assert response.status_code == 404 async def test_update_sales_record(self, test_client, override_get_db, sample_tenant_id): """Test updating a sales record""" # First create a record sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "Original Product", "quantity_sold": 1, "revenue": 5.00, "source": "manual" } create_response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert create_response.status_code == 201 created_record = create_response.json() # Update the record update_data = { "product_name": "Updated Product", "quantity_sold": 2, "revenue": 10.00 } response = test_client.put( f"/api/v1/sales/{created_record['id']}?tenant_id={sample_tenant_id}", json=update_data ) assert response.status_code == 200 data = response.json() assert data["product_name"] == "Updated Product" assert data["quantity_sold"] == 2 async def test_delete_sales_record(self, test_client, override_get_db, sample_tenant_id): """Test deleting a sales record""" # First create a record sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "To Delete", "quantity_sold": 1, "revenue": 5.00, "source": "manual" } create_response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert create_response.status_code == 201 created_record = create_response.json() # Delete the record response = test_client.delete( f"/api/v1/sales/{created_record['id']}?tenant_id={sample_tenant_id}" ) assert response.status_code == 200 # Verify it's deleted get_response = test_client.get( f"/api/v1/sales/{created_record['id']}?tenant_id={sample_tenant_id}" ) assert get_response.status_code == 404 async def test_get_sales_analytics(self, test_client, override_get_db, sample_tenant_id): """Test getting sales analytics""" # First create some records for i in range(3): sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": f"Product {i}", "quantity_sold": i + 1, "revenue": (i + 1) * 5.0, "source": "manual" } response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert response.status_code == 201 # Get analytics response = test_client.get( f"/api/v1/sales/analytics?tenant_id={sample_tenant_id}" ) assert response.status_code == 200 data = response.json() assert "total_revenue" in data assert "total_quantity" in data assert "total_transactions" in data async def test_validate_sales_record(self, test_client, override_get_db, sample_tenant_id): """Test validating a sales record""" # First create a record sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "To Validate", "quantity_sold": 1, "revenue": 5.00, "source": "manual" } create_response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert create_response.status_code == 201 created_record = create_response.json() # Validate the record validation_data = { "validation_notes": "Validated by manager" } response = test_client.post( f"/api/v1/sales/{created_record['id']}/validate?tenant_id={sample_tenant_id}", json=validation_data ) assert response.status_code == 200 data = response.json() assert data["is_validated"] is True assert data["validation_notes"] == "Validated by manager" async def test_get_product_sales(self, test_client, override_get_db, sample_tenant_id): """Test getting sales for specific product""" # First create records for different products products = ["Product A", "Product B", "Product A"] for product in products: sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": product, "quantity_sold": 1, "revenue": 5.00, "source": "manual" } response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert response.status_code == 201 # Get sales for Product A response = test_client.get( f"/api/v1/sales/products/Product A?tenant_id={sample_tenant_id}" ) assert response.status_code == 200 data = response.json() assert len(data) == 2 # Two Product A records assert all(record["product_name"] == "Product A" for record in data) async def test_get_product_categories(self, test_client, override_get_db, sample_tenant_id): """Test getting product categories""" # First create records with categories for category in ["Panadería", "Cafetería", "Panadería"]: sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "Test Product", "product_category": category, "quantity_sold": 1, "revenue": 5.00, "source": "manual" } response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert response.status_code == 201 # Get categories response = test_client.get( f"/api/v1/sales/categories?tenant_id={sample_tenant_id}" ) assert response.status_code == 200 data = response.json() assert isinstance(data, list) async def test_export_sales_data_csv(self, test_client, override_get_db, sample_tenant_id): """Test exporting sales data as CSV""" # First create some records for i in range(3): sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": f"Export Product {i}", "quantity_sold": i + 1, "revenue": (i + 1) * 5.0, "source": "manual" } response = test_client.post( f"/api/v1/sales?tenant_id={sample_tenant_id}", json=sales_data ) assert response.status_code == 201 # Export as CSV response = test_client.get( f"/api/v1/sales/export?tenant_id={sample_tenant_id}&format=csv" ) assert response.status_code == 200 assert response.headers["content-type"].startswith("text/csv") assert "Export Product" in response.text async def test_bulk_create_sales_records(self, test_client, override_get_db, sample_tenant_id): """Test bulk creating sales records""" bulk_data = [ { "date": datetime.now(timezone.utc).isoformat(), "product_name": f"Bulk Product {i}", "quantity_sold": i + 1, "revenue": (i + 1) * 3.0, "source": "bulk" } for i in range(5) ] response = test_client.post( f"/api/v1/sales/bulk?tenant_id={sample_tenant_id}", json=bulk_data ) assert response.status_code == 201 data = response.json() assert data["created_count"] == 5 assert data["success"] is True async def test_tenant_isolation(self, test_client, override_get_db): """Test that tenants can only access their own data""" tenant_1 = uuid4() tenant_2 = uuid4() # Create record for tenant 1 sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "Tenant 1 Product", "quantity_sold": 1, "revenue": 5.00, "source": "manual" } create_response = test_client.post( f"/api/v1/sales?tenant_id={tenant_1}", json=sales_data ) assert create_response.status_code == 201 created_record = create_response.json() # Try to access with tenant 2 response = test_client.get( f"/api/v1/sales/{created_record['id']}?tenant_id={tenant_2}" ) assert response.status_code == 404 # Should not be found # Tenant 1 should still be able to access response = test_client.get( f"/api/v1/sales/{created_record['id']}?tenant_id={tenant_1}" ) assert response.status_code == 200 async def test_api_error_handling(self, test_client, override_get_db, sample_tenant_id): """Test API error handling""" # Test missing tenant_id sales_data = { "date": datetime.now(timezone.utc).isoformat(), "product_name": "Test Product", "quantity_sold": 1, "revenue": 5.00 } response = test_client.post("/api/v1/sales", json=sales_data) assert response.status_code == 422 # Missing required parameter # Test invalid UUID response = test_client.get("/api/v1/sales/invalid-uuid?tenant_id={sample_tenant_id}") assert response.status_code == 422 # Invalid UUID format