Files
orion/tests/integration/api/v1/vendor/test_onboarding.py
Samir Boulahtit 73f612a01a fix: use custom exceptions in onboarding and add tests
- Create onboarding-specific exceptions (OnboardingNotFoundException, etc.)
- Remove HTTPException usage from API endpoints per architecture rules
- Let exceptions propagate to global handler
- Add 12 integration tests for onboarding API endpoints

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 21:55:03 +01:00

369 lines
12 KiB
Python

# tests/integration/api/v1/vendor/test_onboarding.py
"""
Integration tests for vendor onboarding API endpoints.
Tests cover:
1. Onboarding status retrieval
2. Step 1: Company profile setup
3. Step 2: Letzshop API configuration
4. Step 3: Product import configuration
5. Step 4: Order sync (mocked)
6. Step order validation (can't skip ahead)
7. Onboarding completion flow
"""
import pytest
from models.database.onboarding import OnboardingStatus, OnboardingStep, VendorOnboarding
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorOnboardingStatusAPI:
"""Test onboarding status endpoint"""
def test_get_status_creates_onboarding_if_missing(
self, client, vendor_user_headers, test_vendor_with_vendor_user, db
):
"""Test that getting status creates onboarding record if none exists"""
# First ensure no onboarding exists
existing = (
db.query(VendorOnboarding)
.filter(VendorOnboarding.vendor_id == test_vendor_with_vendor_user.id)
.first()
)
if existing:
db.delete(existing)
db.commit()
response = client.get(
"/api/v1/vendor/onboarding/status", headers=vendor_user_headers
)
assert response.status_code == 200
data = response.json()
assert data["vendor_id"] == test_vendor_with_vendor_user.id
assert data["status"] == OnboardingStatus.NOT_STARTED.value
assert data["current_step"] == OnboardingStep.COMPANY_PROFILE.value
assert data["completed_steps_count"] == 0
assert data["total_steps"] == 4
assert data["is_completed"] is False
def test_get_status_structure(
self, client, vendor_user_headers, test_vendor_with_vendor_user, db
):
"""Test status response has correct structure"""
response = client.get(
"/api/v1/vendor/onboarding/status", headers=vendor_user_headers
)
assert response.status_code == 200
data = response.json()
# Verify structure
assert "id" in data
assert "vendor_id" in data
assert "status" in data
assert "current_step" in data
assert "company_profile" in data
assert "letzshop_api" in data
assert "product_import" in data
assert "order_sync" in data
assert "completion_percentage" in data
assert "completed_steps_count" in data
assert "total_steps" in data
assert "is_completed" in data
def test_get_status_requires_auth(self, client):
"""Test that onboarding status requires authentication"""
response = client.get("/api/v1/vendor/onboarding/status")
assert response.status_code == 401
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorOnboardingStep1API:
"""Test Step 1: Company Profile endpoints"""
def test_get_company_profile_data(
self, client, vendor_user_headers, test_vendor_with_vendor_user, db
):
"""Test getting company profile data"""
response = client.get(
"/api/v1/vendor/onboarding/step/company-profile",
headers=vendor_user_headers,
)
assert response.status_code == 200
data = response.json()
# Should return pre-filled data
assert "brand_name" in data
assert "contact_email" in data
assert "default_language" in data
def test_save_company_profile_success(
self, client, vendor_user_headers, test_vendor_with_vendor_user, db
):
"""Test saving company profile completes step 1"""
# First ensure clean state
existing = (
db.query(VendorOnboarding)
.filter(VendorOnboarding.vendor_id == test_vendor_with_vendor_user.id)
.first()
)
if existing:
db.delete(existing)
db.commit()
response = client.post(
"/api/v1/vendor/onboarding/step/company-profile",
headers=vendor_user_headers,
json={
"company_name": "Test Company Ltd",
"brand_name": "Test Brand",
"description": "A test company for testing",
"contact_email": "test@example.com",
"contact_phone": "+352123456789",
"website": "https://test.example.com",
"business_address": "123 Test Street, Luxembourg",
"tax_number": "LU12345678",
"default_language": "fr",
"dashboard_language": "en",
},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["step_completed"] is True
assert data["next_step"] == OnboardingStep.LETZSHOP_API.value
# Verify onboarding was updated
onboarding = (
db.query(VendorOnboarding)
.filter(VendorOnboarding.vendor_id == test_vendor_with_vendor_user.id)
.first()
)
db.refresh(onboarding)
assert onboarding.step_company_profile_completed is True
assert onboarding.status == OnboardingStatus.IN_PROGRESS.value
def test_save_company_profile_with_minimal_data(
self, client, vendor_user_headers, test_vendor_with_vendor_user, db
):
"""Test saving company profile with minimal data"""
# Clear existing onboarding
existing = (
db.query(VendorOnboarding)
.filter(VendorOnboarding.vendor_id == test_vendor_with_vendor_user.id)
.first()
)
if existing:
db.delete(existing)
db.commit()
response = client.post(
"/api/v1/vendor/onboarding/step/company-profile",
headers=vendor_user_headers,
json={
"default_language": "fr",
"dashboard_language": "fr",
},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorOnboardingStep2API:
"""Test Step 2: Letzshop API Configuration endpoints"""
def test_letzshop_api_test_endpoint(
self, client, vendor_user_headers, test_vendor_with_vendor_user
):
"""Test the Letzshop API test endpoint"""
response = client.post(
"/api/v1/vendor/onboarding/step/letzshop-api/test",
headers=vendor_user_headers,
json={
"api_key": "test_invalid_key_12345",
"shop_slug": "test-shop",
},
)
assert response.status_code == 200
data = response.json()
# Response should indicate success or failure
assert "success" in data
assert "message" in data
def test_letzshop_api_requires_step1_complete(
self, client, vendor_user_headers, test_vendor_with_vendor_user, db
):
"""Test that step 2 requires step 1 to be completed"""
# Ensure fresh state with no steps completed
existing = (
db.query(VendorOnboarding)
.filter(VendorOnboarding.vendor_id == test_vendor_with_vendor_user.id)
.first()
)
if existing:
db.delete(existing)
db.commit()
# Try to save step 2 directly
response = client.post(
"/api/v1/vendor/onboarding/step/letzshop-api",
headers=vendor_user_headers,
json={
"api_key": "test_api_key_12345678901234567890",
"shop_slug": "test-shop",
},
)
# Should fail because step 1 is not complete
assert response.status_code == 422 # Validation error
data = response.json()
assert "step" in str(data).lower() or "complete" in str(data).lower()
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorOnboardingStep3API:
"""Test Step 3: Product Import Configuration endpoints"""
def test_get_product_import_config(
self, client, vendor_user_headers, test_vendor_with_vendor_user
):
"""Test getting product import configuration"""
response = client.get(
"/api/v1/vendor/onboarding/step/product-import",
headers=vendor_user_headers,
)
assert response.status_code == 200
data = response.json()
assert "csv_url_fr" in data
assert "csv_url_en" in data
assert "csv_url_de" in data
assert "default_tax_rate" in data
assert "delivery_method" in data
def test_product_import_requires_csv_url(
self, client, vendor_user_headers, test_vendor_with_vendor_user, db
):
"""Test that product import requires at least one CSV URL"""
# Set up: complete steps 1 and 2 first
onboarding = (
db.query(VendorOnboarding)
.filter(VendorOnboarding.vendor_id == test_vendor_with_vendor_user.id)
.first()
)
if not onboarding:
onboarding = VendorOnboarding(
vendor_id=test_vendor_with_vendor_user.id,
status=OnboardingStatus.IN_PROGRESS.value,
current_step=OnboardingStep.PRODUCT_IMPORT.value,
)
db.add(onboarding)
onboarding.step_company_profile_completed = True
onboarding.step_letzshop_api_completed = True
onboarding.current_step = OnboardingStep.PRODUCT_IMPORT.value
db.commit()
# Try to save without any CSV URL
response = client.post(
"/api/v1/vendor/onboarding/step/product-import",
headers=vendor_user_headers,
json={
"default_tax_rate": 17,
"delivery_method": "package_delivery",
"preorder_days": 1,
},
)
# Should fail with validation error
assert response.status_code == 422
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorOnboardingStep4API:
"""Test Step 4: Order Sync endpoints"""
def test_order_sync_progress_endpoint(
self, client, vendor_user_headers, test_vendor_with_vendor_user
):
"""Test getting order sync progress for non-existent job"""
response = client.get(
"/api/v1/vendor/onboarding/step/order-sync/progress/99999",
headers=vendor_user_headers,
)
assert response.status_code == 200
data = response.json()
# Should return not_found status for non-existent job
assert data["status"] == "not_found"
assert data["job_id"] == 99999
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.vendor
class TestVendorOnboardingFlowAPI:
"""Test complete onboarding flow"""
def test_completion_percentage_updates(
self, client, vendor_user_headers, test_vendor_with_vendor_user, db
):
"""Test that completion percentage updates as steps are completed"""
# Clear existing onboarding
existing = (
db.query(VendorOnboarding)
.filter(VendorOnboarding.vendor_id == test_vendor_with_vendor_user.id)
.first()
)
if existing:
db.delete(existing)
db.commit()
# Check initial state
response = client.get(
"/api/v1/vendor/onboarding/status", headers=vendor_user_headers
)
data = response.json()
assert data["completion_percentage"] == 0
# Complete step 1
client.post(
"/api/v1/vendor/onboarding/step/company-profile",
headers=vendor_user_headers,
json={
"default_language": "fr",
"dashboard_language": "fr",
},
)
# Check progress
response = client.get(
"/api/v1/vendor/onboarding/status", headers=vendor_user_headers
)
data = response.json()
assert data["completion_percentage"] == 25 # 1/4 = 25%
assert data["completed_steps_count"] == 1