Renamed all documentation files to follow kebab-case naming standard: - UPPERCASE files → lowercase (e.g., RBAC.md → rbac.md) - snake_case files → kebab-case (e.g., icons_guide.md → icons-guide.md) - SCREAMING_SNAKE_CASE → kebab-case (e.g., DATABASE_SETUP_GUIDE.md → database-setup-guide.md) Files renamed (15 total): API Documentation: - api/RBAC.md → api/rbac.md Architecture: - architecture/API_CONSOLIDATION_PROPOSAL.md → api-consolidation-proposal.md - architecture/API_MIGRATION_STATUS.md → api-migration-status.md Development: - development/AUTH_DEPENDENCIES_GUIDE.md → auth-dependencies-guide.md - development/CUSTOMER_AUTHENTICATION_IMPLEMENTATION.md → customer-authentication-implementation.md - development/CUSTOMER_AUTH_SUMMARY.md → customer-auth-summary.md - development/icons_guide.md → icons-guide.md Database Seeder: - database-seeder/DATABASE_INIT_GUIDE.md → database-init-guide.md - database-seeder/DATABASE_QUICK_REFERENCE_GUIDE.md → database-quick-reference-guide.md - database-seeder/DATABASE_SEEDER_DOCUMENTATION.md → database-seeder-documentation.md - database-seeder/MAKEFILE_DATABASE_SEEDER.md → makefile-database-seeder.md Error Rendering: - error-rendering/ERROR_RENDERING_DEVELOPER_DOCUMENTATION.md → error-rendering-developer-documentation.md - error-rendering/HTML_ERROR_RENDERING_FLOW_DIAGRAM.md → html-error-rendering-flow-diagram.md Getting Started: - getting-started/DATABASE_QUICK_REFERENCE.md → database-quick-reference.md - getting-started/DATABASE_SETUP_GUIDE.md → database-setup-guide.md Updates: - Updated all references in mkdocs.yml - Updated all cross-references in markdown files - Verified mkdocs builds without warnings or errors Standard: Use kebab-case (lowercase-with-hyphens) for all markdown files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
17 KiB
Vendor API Testing Guide
Overview
Comprehensive integration test suite for vendor API endpoints that use get_current_vendor_api for authentication.
Test Coverage
Current Coverage
| Endpoint | Auth Tests | Functionality Tests | Coverage |
|---|---|---|---|
/api/v1/vendor/auth/me |
✅ Complete | ✅ Complete | 100% |
/api/v1/vendor/dashboard/stats |
✅ Complete | ✅ Complete | 100% |
/api/v1/vendor/profile |
✅ Auth only | ⚠️ Pending | 50% |
/api/v1/vendor/settings |
✅ Auth only | ⚠️ Pending | 50% |
/api/v1/vendor/products |
✅ Auth only | ⚠️ Pending | 50% |
/api/v1/vendor/orders |
✅ Auth only | ⚠️ Pending | 50% |
/api/v1/vendor/customers |
✅ Auth only | ⚠️ Pending | 50% |
/api/v1/vendor/inventory |
✅ Auth only | ⚠️ Pending | 50% |
/api/v1/vendor/marketplace |
✅ Auth only | ⚠️ Pending | 50% |
/api/v1/vendor/analytics |
✅ Auth only | ⚠️ Pending | 50% |
Total Tests Created: 58+ tests Tests in Structure: 28 tests (authentication + dashboard)
Test Files
Location
tests/integration/api/v1/vendor/
├── __init__.py
├── test_authentication.py # 30+ authentication tests
└── test_dashboard.py # 12 dashboard tests
1. Authentication Tests
File: tests/integration/api/v1/vendor/test_authentication.py
Purpose: Verify authentication and authorization for vendor API endpoints
Test Classes
TestVendorAPIAuthentication
Core authentication tests for get_current_vendor_api:
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
@pytest.mark.auth
class TestVendorAPIAuthentication:
"""Test authentication for vendor API endpoints"""
Tests:
- ✅
test_vendor_auth_me_success- Valid vendor token accepted - ✅
test_vendor_auth_me_no_token- Missing token rejected (401) - ✅
test_vendor_auth_me_invalid_token- Invalid token rejected (401) - ✅
test_vendor_auth_me_expired_token- Expired token rejected (401) - ✅
test_vendor_auth_me_malformed_header- Malformed header rejected (401) - ✅
test_admin_user_blocked- Admin users blocked (403) - ✅
test_regular_user_blocked- Regular users blocked (403) - ✅
test_inactive_vendor_user_rejected- Inactive users rejected (401) - ✅
test_csrf_protection_header_required- Requires Authorization header - ✅
test_csrf_protection_cookie_only_rejected- Cookie-only auth rejected - ✅
test_concurrent_requests- Concurrent requests with same token - ✅
test_token_with_missing_claims- Token with missing claims rejected - ✅
test_empty_authorization_header- Empty header rejected
TestVendorAPIConsistency
Consistency checks across all vendor endpoints:
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorAPIConsistency:
"""Test that all vendor API endpoints have consistent auth behavior"""
Tests:
- ✅ Verifies all vendor endpoints require authentication
- ✅ Ensures consistent error responses
- ✅ Checks CSRF protection across all endpoints
What's Tested
Authentication (get_current_vendor_api):
- Requires Authorization header (not cookies)
- Validates JWT token format
- Checks token expiration
- Verifies token signature
- Extracts user from token
- Verifies user exists and is active
Authorization:
- Blocks admin users (403)
- Blocks regular users (403)
- Accepts vendor users (200/404)
- Checks vendor-role requirement
CSRF Protection:
- API endpoints require header auth
- Cookie-only auth rejected
- Consistent across all endpoints
Vendor Context:
- Vendor association required
- Inactive vendors rejected
- Vendor data isolation
- VendorUser relationship working
2. Dashboard Tests
File: tests/integration/api/v1/vendor/test_dashboard.py
Purpose: Test vendor dashboard statistics endpoint
Test Class
TestVendorDashboardAPI
Dashboard functionality tests:
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorDashboardAPI:
"""Test vendor dashboard stats endpoint"""
Tests:
- ✅
test_dashboard_stats_structure- Correct response structure - ✅
test_dashboard_stats_vendor_isolation- Only shows vendor's data - ✅
test_dashboard_stats_empty_vendor- Empty vendor returns zeros - ✅
test_dashboard_stats_product_counts- Accurate product counts - ✅
test_dashboard_stats_order_counts- Accurate order counts - ✅
test_dashboard_stats_customer_counts- Accurate customer counts - ✅
test_dashboard_stats_revenue- Accurate revenue calculations - ✅
test_dashboard_stats_no_vendor_association- User not associated (403) - ✅
test_dashboard_stats_inactive_vendor- Inactive vendor (404) - ✅
test_dashboard_stats_performance- Response time < 2 seconds - ✅
test_dashboard_stats_multiple_requests- Consistency across requests - ✅
test_dashboard_stats_data_types- Correct data types
What's Tested
Data Structure:
- Correct response structure
- All required fields present
- Correct data types
Business Logic:
- Vendor isolation (only shows own data)
- Empty vendor returns zeros
- Accurate product counts (total, active)
- Orders, customers, revenue stats
Error Cases:
- User not associated with vendor (403)
- Inactive vendor (404)
- Missing vendor association
Performance:
- Response time < 2 seconds
- Multiple requests consistency
- Caching behavior
Test Fixtures
Authentication Fixtures
Located in tests/fixtures/auth_fixtures.py:
test_vendor_user
Creates a user with role="vendor":
@pytest.fixture
def test_vendor_user(db, auth_manager):
"""Create a test vendor user with unique username"""
unique_id = str(uuid.uuid4())[:8]
hashed_password = auth_manager.hash_password("vendorpass123")
user = User(
email=f"vendor_{unique_id}@example.com",
username=f"vendoruser_{unique_id}",
hashed_password=hashed_password,
role="vendor",
is_active=True,
)
db.add(user)
db.commit()
db.refresh(user)
return user
Usage: Base fixture for vendor-role users
vendor_user_headers
Returns Authorization headers for vendor user:
@pytest.fixture
def vendor_user_headers(client, test_vendor_user):
"""Get authentication headers for vendor user"""
response = client.post(
"/api/v1/auth/login",
json={
"username": test_vendor_user.username,
"password": "vendorpass123"
},
)
token = response.json()["access_token"]
return {"Authorization": f"Bearer {token}"}
Usage: Use for authenticated vendor API requests
Format: {"Authorization": "Bearer <token>"}
Vendor Fixtures
Located in tests/fixtures/vendor_fixtures.py:
test_vendor_with_vendor_user
Creates a vendor owned by vendor user:
@pytest.fixture
def test_vendor_with_vendor_user(db, test_vendor_user):
"""Create a vendor owned by a vendor user"""
vendor = Vendor(
vendor_code=f"VENDORAPI_{unique_id}",
subdomain=f"vendorapi{unique_id.lower()}",
name=f"Vendor API Test {unique_id}",
owner_user_id=test_vendor_user.id,
is_active=True,
is_verified=True,
)
db.add(vendor)
db.commit()
# Create VendorUser association
vendor_user = VendorUser(
vendor_id=vendor.id,
user_id=test_vendor_user.id,
is_owner=True,
is_active=True,
)
db.add(vendor_user)
db.commit()
return vendor
Usage: Fully configured vendor for API testing with VendorUser association
Running Tests
All Vendor Tests
# Run all vendor integration tests
pytest tests/integration/api/v1/vendor/ -v
# With output
pytest tests/integration/api/v1/vendor/ -v -s
Specific Test Files
# Authentication tests only
pytest tests/integration/api/v1/vendor/test_authentication.py -v
# Dashboard tests only
pytest tests/integration/api/v1/vendor/test_dashboard.py -v
Specific Test Classes
# Authentication tests class
pytest tests/integration/api/v1/vendor/test_authentication.py::TestVendorAPIAuthentication -v
# Dashboard tests class
pytest tests/integration/api/v1/vendor/test_dashboard.py::TestVendorDashboardAPI -v
Specific Tests
# Single authentication test
pytest tests/integration/api/v1/vendor/test_authentication.py::TestVendorAPIAuthentication::test_vendor_auth_me_success -v
# Single dashboard test
pytest tests/integration/api/v1/vendor/test_dashboard.py::TestVendorDashboardAPI::test_dashboard_stats_structure -v
With Coverage
# Coverage for all vendor tests
pytest tests/integration/api/v1/vendor/ \
--cov=app/api/v1/vendor \
--cov-report=html
# View coverage report
open htmlcov/index.html
# Coverage for specific endpoint
pytest tests/integration/api/v1/vendor/test_dashboard.py \
--cov=app/api/v1/vendor/dashboard \
--cov-report=term-missing
Using Markers
# All vendor tests
pytest -m vendor -v
# Vendor API tests only
pytest -m "vendor and api" -v
# Vendor authentication tests
pytest -m "vendor and auth" -v
Test Examples
Basic Authenticated Request
def test_vendor_endpoint(client, vendor_user_headers):
"""Test vendor endpoint with authentication"""
response = client.get(
"/api/v1/vendor/some-endpoint",
headers=vendor_user_headers
)
assert response.status_code == 200
data = response.json()
assert "expected_field" in data
Test with Vendor Context
def test_with_vendor_data(
client,
vendor_user_headers,
test_vendor_with_vendor_user,
db
):
"""Test with specific vendor data"""
vendor = test_vendor_with_vendor_user
# Create test data for this vendor
product = Product(
vendor_id=vendor.id,
name="Test Product",
price=29.99
)
db.add(product)
db.commit()
response = client.get(
"/api/v1/vendor/products",
headers=vendor_user_headers
)
assert response.status_code == 200
data = response.json()
assert len(data["products"]) == 1
Test Negative Cases
def test_rejects_non_vendor(client, auth_headers):
"""Test endpoint rejects non-vendor users"""
response = client.get(
"/api/v1/vendor/endpoint",
headers=auth_headers # Regular user token
)
assert response.status_code == 403
data = response.json()
assert data["detail"] == "This endpoint is only for vendor users"
Test Authentication Failure
def test_requires_authentication(client):
"""Test endpoint requires authentication"""
response = client.get("/api/v1/vendor/endpoint")
assert response.status_code == 401
data = response.json()
assert data["detail"] == "Not authenticated"
Expanding Test Coverage
Recommended Next Tests
1. Vendor Products API
Create tests/integration/api/v1/vendor/test_products.py:
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorProductsAPI:
"""Test vendor product management endpoints"""
def test_list_products(self, client, vendor_user_headers):
"""Test listing products with pagination"""
def test_create_product_from_marketplace(self, client, vendor_user_headers):
"""Test creating product from marketplace"""
def test_update_product(self, client, vendor_user_headers):
"""Test updating product details"""
def test_delete_product(self, client, vendor_user_headers):
"""Test deleting product"""
def test_toggle_product_active(self, client, vendor_user_headers):
"""Test toggling product active status"""
2. Vendor Orders API
Create tests/integration/api/v1/vendor/test_orders.py:
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorOrdersAPI:
"""Test vendor order management endpoints"""
def test_list_orders(self, client, vendor_user_headers):
"""Test listing orders with filters"""
def test_get_order_details(self, client, vendor_user_headers):
"""Test getting order details"""
def test_update_order_status(self, client, vendor_user_headers):
"""Test updating order status"""
def test_order_data_isolation(self, client, vendor_user_headers):
"""Test vendor can only see their orders"""
3. Vendor Profile API
Create tests/integration/api/v1/vendor/test_profile.py:
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorProfileAPI:
"""Test vendor profile endpoints"""
def test_get_profile(self, client, vendor_user_headers):
"""Test getting vendor profile"""
def test_update_profile(self, client, vendor_user_headers):
"""Test updating vendor profile"""
def test_profile_permissions(self, client, vendor_user_headers):
"""Test profile permission checks"""
Troubleshooting
Test Fails with 401 (Unauthorized)
Problem: Tests fail even with valid token
Common Causes:
- User doesn't have
role="vendor" - Token is expired
- Authorization header format incorrect
Solutions:
# Verify user role
assert test_vendor_user.role == "vendor"
# Check token format
assert vendor_user_headers["Authorization"].startswith("Bearer ")
# Generate fresh token
response = client.post("/api/v1/auth/login", json={...})
Test Fails with 403 (Forbidden)
Problem: "Not associated with vendor" error
Common Causes:
VendorUserrecord doesn't exist- Vendor is inactive
- User not linked to vendor
Solutions:
# Use fixture that creates VendorUser
def test_example(
client,
vendor_user_headers,
test_vendor_with_vendor_user # ← Use this fixture
):
pass
# Or manually create VendorUser
vendor_user = VendorUser(
vendor_id=vendor.id,
user_id=test_vendor_user.id,
is_active=True
)
db.add(vendor_user)
db.commit()
Test Fails with 404 (Not Found)
Problem: "Vendor not found" error
Common Causes:
- Vendor is inactive (
is_active=False) - Vendor doesn't exist
- Database not set up correctly
Solutions:
# Verify vendor is active
assert vendor.is_active is True
# Verify vendor exists
vendor_in_db = db.query(Vendor).filter_by(id=vendor.id).first()
assert vendor_in_db is not None
Import Errors for Fixtures
Problem: fixture not found errors
Solutions:
- Fixtures are auto-loaded via
conftest.py - Check
pytest_pluginslist intests/conftest.py:pytest_plugins = [ "tests.fixtures.auth_fixtures", "tests.fixtures.vendor_fixtures", ] - Use absolute imports in test files
Best Practices
1. Use Appropriate Fixtures
# ✅ Good - Use specific fixtures
def test_vendor_endpoint(client, vendor_user_headers, test_vendor_with_vendor_user):
pass
# ❌ Avoid - Manual setup
def test_vendor_endpoint(client):
user = User(role="vendor", ...) # Don't do this
db.add(user)
2. Test Isolation
Each test should be independent:
# ✅ Good - Test creates its own data
def test_product_list(client, vendor_user_headers, db):
product = Product(...)
db.add(product)
db.commit()
response = client.get("/api/v1/vendor/products", headers=vendor_user_headers)
# Test assertions
# ❌ Avoid - Depending on other tests
def test_product_list(client, vendor_user_headers):
# Assumes data from previous test
response = client.get("/api/v1/vendor/products", headers=vendor_user_headers)
3. Comprehensive Assertions
Check multiple aspects:
def test_dashboard_stats(client, vendor_user_headers):
response = client.get(
"/api/v1/vendor/dashboard/stats",
headers=vendor_user_headers
)
# Check status
assert response.status_code == 200
# Check structure
data = response.json()
assert "products" in data
assert "orders" in data
# Check data types
assert isinstance(data["products"]["total"], int)
# Check business logic
assert data["products"]["total"] >= 0
4. Use Markers Consistently
@pytest.mark.integration # Always for integration tests
@pytest.mark.api # For API endpoint tests
@pytest.mark.vendor # For vendor-specific tests
class TestVendorAPI:
pass
5. Clear Test Names
# ✅ Good - Descriptive names
def test_list_products_returns_paginated_results(self):
def test_create_product_requires_authentication(self):
def test_update_product_rejects_invalid_price(self):
# ❌ Avoid - Vague names
def test_products(self):
def test_api_1(self):
def test_endpoint(self):
See Also
- Test Structure - Overall test organization
- Testing Guide - Testing strategy and best practices
- Test Maintenance - Maintaining test quality
- API Authentication - Authentication documentation
- RBAC - Role-based access control