style: apply black and isort formatting across entire codebase

- Standardize quote style (single to double quotes)
- Reorder and group imports alphabetically
- Fix line breaks and indentation for consistency
- Apply PEP 8 formatting standards

Also updated Makefile to exclude both venv and .venv from code quality checks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-28 19:30:17 +01:00
parent 13f0094743
commit 21c13ca39b
236 changed files with 8450 additions and 6545 deletions

View File

@@ -5,9 +5,10 @@ System tests for error handling across the LetzVendor API.
Tests the complete error handling flow from FastAPI through custom exception handlers
to ensure proper HTTP status codes, error structures, and client-friendly responses.
"""
import pytest
import json
import pytest
@pytest.mark.system
class TestErrorHandling:
@@ -16,9 +17,7 @@ class TestErrorHandling:
def test_invalid_json_request(self, client, auth_headers):
"""Test handling of malformed JSON requests"""
response = client.post(
"/api/v1/vendor",
headers=auth_headers,
content="{ invalid json syntax"
"/api/v1/vendor", headers=auth_headers, content="{ invalid json syntax"
)
assert response.status_code == 422
@@ -31,9 +30,7 @@ class TestErrorHandling:
"""Test validation errors for missing required fields"""
# Missing name
response = client.post(
"/api/v1/vendor",
headers=auth_headers,
json={"vendor_code": "TESTVENDOR"}
"/api/v1/vendor", headers=auth_headers, json={"vendor_code": "TESTVENDOR"}
)
assert response.status_code == 422
@@ -48,10 +45,7 @@ class TestErrorHandling:
response = client.post(
"/api/v1/vendor",
headers=auth_headers,
json={
"vendor_code": "INVALID@VENDOR!",
"name": "Test Vendor"
}
json={"vendor_code": "INVALID@VENDOR!", "name": "Test Vendor"},
)
assert response.status_code == 422
@@ -92,7 +86,7 @@ class TestErrorHandling:
assert data["status_code"] == 401
def test_vendor_not_found(self, client, auth_headers):
"""Test accessing non-existent vendor """
"""Test accessing non-existent vendor"""
response = client.get("/api/v1/vendor/NONEXISTENT", headers=auth_headers)
assert response.status_code == 404
@@ -104,7 +98,9 @@ class TestErrorHandling:
def test_product_not_found(self, client, auth_headers):
"""Test accessing non-existent product"""
response = client.get("/api/v1/marketplace/product/NONEXISTENT", headers=auth_headers)
response = client.get(
"/api/v1/marketplace/product/NONEXISTENT", headers=auth_headers
)
assert response.status_code == 404
data = response.json()
@@ -117,7 +113,7 @@ class TestErrorHandling:
"""Test creating vendor with duplicate vendor code"""
vendor_data = {
"vendor_code": test_vendor.vendor_code,
"name": "Duplicate Vendor"
"name": "Duplicate Vendor",
}
response = client.post("/api/v1/vendor", headers=auth_headers, json=vendor_data)
@@ -128,25 +124,34 @@ class TestErrorHandling:
assert data["status_code"] == 409
assert data["details"]["vendor_code"] == test_vendor.vendor_code.upper()
def test_duplicate_product_creation(self, client, auth_headers, test_marketplace_product):
def test_duplicate_product_creation(
self, client, auth_headers, test_marketplace_product
):
"""Test creating product with duplicate product ID"""
product_data = {
"marketplace_product_id": test_marketplace_product.marketplace_product_id,
"title": "Duplicate MarketplaceProduct",
"gtin": "1234567890123"
"gtin": "1234567890123",
}
response = client.post("/api/v1/marketplace/product", headers=auth_headers, json=product_data)
response = client.post(
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
)
assert response.status_code == 409
data = response.json()
assert data["error_code"] == "PRODUCT_ALREADY_EXISTS"
assert data["status_code"] == 409
assert data["details"]["marketplace_product_id"] == test_marketplace_product.marketplace_product_id
assert (
data["details"]["marketplace_product_id"]
== test_marketplace_product.marketplace_product_id
)
def test_unauthorized_vendor_access(self, client, auth_headers, inactive_vendor):
"""Test accessing vendor without proper permissions"""
response = client.get(f"/api/v1/vendor/{inactive_vendor.vendor_code}", headers=auth_headers)
response = client.get(
f"/api/v1/vendor/{inactive_vendor.vendor_code}", headers=auth_headers
)
assert response.status_code == 403
data = response.json()
@@ -154,27 +159,33 @@ class TestErrorHandling:
assert data["status_code"] == 403
assert data["details"]["vendor_code"] == inactive_vendor.vendor_code
def test_insufficient_permissions(self, client, auth_headers, admin_only_endpoint="/api/v1/admin/users"):
def test_insufficient_permissions(
self, client, auth_headers, admin_only_endpoint="/api/v1/admin/users"
):
"""Test accessing admin endpoints with regular user"""
response = client.get(admin_only_endpoint, headers=auth_headers)
assert response.status_code in [403, 404] # 403 for permission denied, 404 if endpoint doesn't exist
assert response.status_code in [
403,
404,
] # 403 for permission denied, 404 if endpoint doesn't exist
if response.status_code == 403:
data = response.json()
assert data["error_code"] in ["ADMIN_REQUIRED", "INSUFFICIENT_PERMISSIONS"]
assert data["status_code"] == 403
def test_business_logic_violation_max_vendors(self, client, auth_headers, monkeypatch):
def test_business_logic_violation_max_vendors(
self, client, auth_headers, monkeypatch
):
"""Test business logic violation - creating too many vendors"""
# This test would require mocking the vendor limit check
# For now, test the error structure when creating multiple vendors
vendors_created = []
for i in range(6): # Assume limit is 5
vendor_data = {
"vendor_code": f"VENDOR{i:03d}",
"name": f"Test Vendor {i}"
}
response = client.post("/api/v1/vendor", headers=auth_headers, json=vendor_data)
vendor_data = {"vendor_code": f"VENDOR{i:03d}", "name": f"Test Vendor {i}"}
response = client.post(
"/api/v1/vendor", headers=auth_headers, json=vendor_data
)
vendors_created.append(response)
# At least one should succeed, and if limit is enforced, later ones should fail
@@ -193,10 +204,12 @@ class TestErrorHandling:
product_data = {
"marketplace_product_id": "TESTPROD001",
"title": "Test MarketplaceProduct",
"gtin": "invalid_gtin_format"
"gtin": "invalid_gtin_format",
}
response = client.post("/api/v1/marketplace/product", headers=auth_headers, json=product_data)
response = client.post(
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
)
assert response.status_code == 422
data = response.json()
@@ -204,13 +217,15 @@ class TestErrorHandling:
assert data["status_code"] == 422
assert data["details"]["field"] == "gtin"
def test_inventory_insufficient_quantity(self, client, auth_headers, test_vendor, test_marketplace_product):
def test_inventory_insufficient_quantity(
self, client, auth_headers, test_vendor, test_marketplace_product
):
"""Test business logic error for insufficient inventory"""
# First create some inventory
inventory_data = {
"gtin": test_marketplace_product.gtin,
"location": "WAREHOUSE_A",
"quantity": 5
"quantity": 5,
}
client.post("/api/v1/inventory", headers=auth_headers, json=inventory_data)
@@ -218,9 +233,11 @@ class TestErrorHandling:
remove_data = {
"gtin": test_marketplace_product.gtin,
"location": "WAREHOUSE_A",
"quantity": 10 # More than the 5 we added
"quantity": 10, # More than the 5 we added
}
response = client.post("/api/v1/inventory/remove", headers=auth_headers, json=remove_data)
response = client.post(
"/api/v1/inventory/remove", headers=auth_headers, json=remove_data
)
# This should ALWAYS fail with insufficient inventory error
assert response.status_code == 400
@@ -257,7 +274,7 @@ class TestErrorHandling:
response = client.post(
"/api/v1/vendor",
headers=headers,
content="<vendor ><code>TEST</code></vendor >"
content="<vendor ><code>TEST</code></vendor >",
)
assert response.status_code in [400, 415, 422]
@@ -268,7 +285,7 @@ class TestErrorHandling:
vendor_data = {
"vendor_code": "LARGEVENDOR",
"name": "Large Vendor",
"description": large_description
"description": large_description,
}
response = client.post("/api/v1/vendor", headers=auth_headers, json=vendor_data)
@@ -310,10 +327,12 @@ class TestErrorHandling:
# Test invalid marketplace
import_data = {
"marketplace": "INVALID_MARKETPLACE",
"vendor_code": test_vendor.vendor_code
"vendor_code": test_vendor.vendor_code,
}
response = client.post("/api/v1/imports", headers=auth_headers, json=import_data)
response = client.post(
"/api/v1/imports", headers=auth_headers, json=import_data
)
if response.status_code == 422:
data = response.json()
@@ -329,10 +348,12 @@ class TestErrorHandling:
# Test with potentially problematic external data
import_data = {
"marketplace": "LETZSHOP",
"external_url": "https://nonexistent-marketplace.com/api"
"external_url": "https://nonexistent-marketplace.com/api",
}
response = client.post("/api/v1/imports", headers=auth_headers, json=import_data)
response = client.post(
"/api/v1/imports", headers=auth_headers, json=import_data
)
# If it's a real external service error, check structure
if response.status_code == 502:
@@ -356,7 +377,9 @@ class TestErrorHandling:
# All error responses should have these fields
required_fields = ["error_code", "message", "status_code"]
for field in required_fields:
assert field in data, f"Missing {field} in error response for {endpoint}"
assert (
field in data
), f"Missing {field} in error response for {endpoint}"
# Details field should be present (can be empty dict)
assert "details" in data
@@ -420,5 +443,7 @@ class TestErrorRecovery:
# Check that error was logged (if your app logs 404s as errors)
# Adjust based on your logging configuration
error_logs = [record for record in caplog.records if record.levelno >= logging.ERROR]
error_logs = [
record for record in caplog.records if record.levelno >= logging.ERROR
]
# May or may not have logs depending on whether 404s are logged as errors