Files
bakery-ia/services/sales/tests/integration/test_api_endpoints.py

417 lines
15 KiB
Python
Raw Normal View History

2025-08-12 18:17:30 +02:00
# 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