Some checks failed
- Add redis-exporter container to docker-compose (oliver006/redis_exporter, 32MB) - Add Redis scrape target to Prometheus config - Add 4 Redis alert rules: RedisDown, HighMemory, HighConnections, RejectedConnections - Document Step 19b (Sentry Error Tracking) in Hetzner deployment guide - Document Step 19c (Redis Monitoring) in Hetzner deployment guide - Update resource budget and port reference tables Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
334 lines
11 KiB
Python
334 lines
11 KiB
Python
# tests/integration/api/v1/store/test_onboarding.py
|
|
"""
|
|
Integration tests for store onboarding API endpoints.
|
|
|
|
Tests cover:
|
|
1. Onboarding status retrieval
|
|
2. Step 1: Merchant 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 app.modules.marketplace.models import (
|
|
OnboardingStatus,
|
|
OnboardingStep,
|
|
StoreOnboarding,
|
|
)
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.api
|
|
@pytest.mark.store
|
|
class TestStoreOnboardingStatusAPI:
|
|
"""Test onboarding status endpoint"""
|
|
|
|
def test_get_status_creates_onboarding_if_missing(
|
|
self, client, store_user_headers, test_store_with_store_user, db
|
|
):
|
|
"""Test that getting status creates onboarding record if none exists"""
|
|
# First ensure no onboarding exists
|
|
existing = (
|
|
db.query(StoreOnboarding)
|
|
.filter(StoreOnboarding.store_id == test_store_with_store_user.id)
|
|
.first()
|
|
)
|
|
if existing:
|
|
db.delete(existing)
|
|
db.commit()
|
|
|
|
response = client.get(
|
|
"/api/v1/store/onboarding/status", headers=store_user_headers
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
assert data["store_id"] == test_store_with_store_user.id
|
|
assert data["status"] == OnboardingStatus.NOT_STARTED.value
|
|
assert data["current_step"] == OnboardingStep.MERCHANT_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, store_user_headers, test_store_with_store_user, db
|
|
):
|
|
"""Test status response has correct structure"""
|
|
response = client.get(
|
|
"/api/v1/store/onboarding/status", headers=store_user_headers
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
# Verify structure
|
|
assert "id" in data
|
|
assert "store_id" in data
|
|
assert "status" in data
|
|
assert "current_step" in data
|
|
assert "merchant_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/store/onboarding/status")
|
|
assert response.status_code == 401
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.api
|
|
@pytest.mark.store
|
|
class TestStoreOnboardingStep1API:
|
|
"""Test Step 1: Merchant Profile endpoints"""
|
|
|
|
def test_get_merchant_profile_data(
|
|
self, client, store_user_headers, test_store_with_store_user, db
|
|
):
|
|
"""Test getting merchant profile data"""
|
|
response = client.get(
|
|
"/api/v1/store/onboarding/step/merchant-profile",
|
|
headers=store_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_merchant_profile_success(
|
|
self, client, store_user_headers, test_store_with_store_user, db
|
|
):
|
|
"""Test saving merchant profile completes step 1"""
|
|
# First ensure clean state
|
|
existing = (
|
|
db.query(StoreOnboarding)
|
|
.filter(StoreOnboarding.store_id == test_store_with_store_user.id)
|
|
.first()
|
|
)
|
|
if existing:
|
|
db.delete(existing)
|
|
db.commit()
|
|
|
|
response = client.post(
|
|
"/api/v1/store/onboarding/step/merchant-profile",
|
|
headers=store_user_headers,
|
|
json={
|
|
"merchant_name": "Test Merchant Ltd",
|
|
"brand_name": "Test Brand",
|
|
"description": "A test merchant 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(StoreOnboarding)
|
|
.filter(StoreOnboarding.store_id == test_store_with_store_user.id)
|
|
.first()
|
|
)
|
|
db.refresh(onboarding)
|
|
assert onboarding.step_merchant_profile_completed is True
|
|
assert onboarding.status == OnboardingStatus.IN_PROGRESS.value
|
|
|
|
def test_save_merchant_profile_with_minimal_data(
|
|
self, client, store_user_headers, test_store_with_store_user, db
|
|
):
|
|
"""Test saving merchant profile with minimal data"""
|
|
# Clear existing onboarding
|
|
existing = (
|
|
db.query(StoreOnboarding)
|
|
.filter(StoreOnboarding.store_id == test_store_with_store_user.id)
|
|
.first()
|
|
)
|
|
if existing:
|
|
db.delete(existing)
|
|
db.commit()
|
|
|
|
response = client.post(
|
|
"/api/v1/store/onboarding/step/merchant-profile",
|
|
headers=store_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.store
|
|
class TestStoreOnboardingStep2API:
|
|
"""Test Step 2: Letzshop API Configuration endpoints"""
|
|
|
|
def test_letzshop_api_test_endpoint(
|
|
self, client, store_user_headers, test_store_with_store_user
|
|
):
|
|
"""Test the Letzshop API test endpoint"""
|
|
response = client.post(
|
|
"/api/v1/store/onboarding/step/letzshop-api/test",
|
|
headers=store_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, store_user_headers, test_store_with_store_user, db
|
|
):
|
|
"""Test that step 2 requires step 1 to be completed"""
|
|
# Ensure fresh state with no steps completed
|
|
existing = (
|
|
db.query(StoreOnboarding)
|
|
.filter(StoreOnboarding.store_id == test_store_with_store_user.id)
|
|
.first()
|
|
)
|
|
if existing:
|
|
db.delete(existing)
|
|
db.commit()
|
|
|
|
# Try to save step 2 directly
|
|
response = client.post(
|
|
"/api/v1/store/onboarding/step/letzshop-api",
|
|
headers=store_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.store
|
|
class TestStoreOnboardingStep3API:
|
|
"""Test Step 3: Product Import Configuration endpoints"""
|
|
|
|
def test_get_product_import_config(
|
|
self, client, store_user_headers, test_store_with_store_user
|
|
):
|
|
"""Test getting product import configuration"""
|
|
response = client.get(
|
|
"/api/v1/store/onboarding/step/product-import",
|
|
headers=store_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, store_user_headers, test_store_with_store_user, db
|
|
):
|
|
"""Test that product import requires at least one CSV URL"""
|
|
# Set up: complete steps 1 and 2 first
|
|
onboarding = (
|
|
db.query(StoreOnboarding)
|
|
.filter(StoreOnboarding.store_id == test_store_with_store_user.id)
|
|
.first()
|
|
)
|
|
if not onboarding:
|
|
onboarding = StoreOnboarding(
|
|
store_id=test_store_with_store_user.id,
|
|
status=OnboardingStatus.IN_PROGRESS.value,
|
|
current_step=OnboardingStep.PRODUCT_IMPORT.value,
|
|
)
|
|
db.add(onboarding)
|
|
|
|
onboarding.step_merchant_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/store/onboarding/step/product-import",
|
|
headers=store_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.store
|
|
class TestStoreOnboardingStep4API:
|
|
"""Test Step 4: Order Sync endpoints"""
|
|
|
|
def test_order_sync_progress_endpoint(
|
|
self, client, store_user_headers, test_store_with_store_user
|
|
):
|
|
"""Test getting order sync progress for non-existent job"""
|
|
response = client.get(
|
|
"/api/v1/store/onboarding/step/order-sync/progress/99999",
|
|
headers=store_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.store
|
|
class TestStoreOnboardingFlowAPI:
|
|
"""Test complete onboarding flow"""
|