feat: complete dynamic menu system across all frontends
All checks were successful
All checks were successful
- Add "Merchant Frontend" tab to admin menu-config page - Merchant render endpoint now respects AdminMenuConfig visibility via get_merchant_primary_platform_id() platform resolution - New store menu render endpoint (GET /store/core/menu/render/store) with platform-scoped visibility and store_code interpolation - Store sidebar migrated from hardcoded Jinja2 macros to dynamic Alpine.js x-for rendering with loading skeleton and fallback - Store init-alpine.js: add loadMenuConfig(), expandSectionForCurrentPage() - Include store page route fixes, login template updates, and tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,388 @@
|
||||
# app/modules/marketplace/tests/integration/test_store_page_routes.py
|
||||
"""
|
||||
Integration tests for marketplace store page routes.
|
||||
|
||||
Tests the onboarding, marketplace, and letzshop page routes at:
|
||||
GET /store/{store_code}/onboarding
|
||||
GET /store/{store_code}/marketplace
|
||||
GET /store/{store_code}/letzshop
|
||||
|
||||
Verifies:
|
||||
- Onboarding page redirects to dashboard on non-marketplace platforms
|
||||
- Onboarding page redirects to dashboard when already completed
|
||||
- Onboarding page renders when marketplace enabled and not completed
|
||||
- Marketplace/Letzshop pages redirect to onboarding when not completed
|
||||
- Marketplace/Letzshop pages render when onboarding is completed
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from app.api.deps import get_current_store_from_cookie_or_header
|
||||
from app.modules.marketplace.models import OnboardingStatus, StoreOnboarding
|
||||
from app.modules.tenancy.models import Merchant, Platform, Store, User
|
||||
from app.modules.tenancy.models.platform_module import PlatformModule
|
||||
from app.modules.tenancy.models.store_platform import StorePlatform
|
||||
from main import app
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
# ============================================================================
|
||||
# Fixtures
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_admin(db):
|
||||
"""Create an admin user for enabling modules."""
|
||||
from middleware.auth import AuthManager
|
||||
|
||||
auth = AuthManager()
|
||||
user = User(
|
||||
email=f"mpadmin_{uuid.uuid4().hex[:8]}@test.com",
|
||||
username=f"mpadmin_{uuid.uuid4().hex[:8]}",
|
||||
hashed_password=auth.hash_password("pass123"),
|
||||
role="super_admin",
|
||||
is_active=True,
|
||||
)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_owner(db):
|
||||
"""Create a store owner user."""
|
||||
from middleware.auth import AuthManager
|
||||
|
||||
auth = AuthManager()
|
||||
user = User(
|
||||
email=f"mpowner_{uuid.uuid4().hex[:8]}@test.com",
|
||||
username=f"mpowner_{uuid.uuid4().hex[:8]}",
|
||||
hashed_password=auth.hash_password("pass123"),
|
||||
role="merchant_owner",
|
||||
is_active=True,
|
||||
)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_platform_no_marketplace(db):
|
||||
"""Create a platform without marketplace module enabled."""
|
||||
platform = Platform(
|
||||
code=f"mpnm_{uuid.uuid4().hex[:8]}",
|
||||
name="No Marketplace Platform",
|
||||
is_active=True,
|
||||
)
|
||||
db.add(platform)
|
||||
db.commit()
|
||||
db.refresh(platform)
|
||||
return platform
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_platform_with_marketplace(db, mp_admin):
|
||||
"""Create a platform with marketplace module enabled."""
|
||||
platform = Platform(
|
||||
code=f"mpmk_{uuid.uuid4().hex[:8]}",
|
||||
name="Marketplace Platform",
|
||||
is_active=True,
|
||||
)
|
||||
db.add(platform)
|
||||
db.flush()
|
||||
|
||||
pm = PlatformModule(
|
||||
platform_id=platform.id,
|
||||
module_code="marketplace",
|
||||
is_enabled=True,
|
||||
enabled_at=datetime.now(UTC),
|
||||
enabled_by_user_id=mp_admin.id,
|
||||
config={},
|
||||
)
|
||||
db.add(pm)
|
||||
db.commit()
|
||||
db.refresh(platform)
|
||||
return platform
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_merchant(db, mp_owner):
|
||||
"""Create a merchant."""
|
||||
merchant = Merchant(
|
||||
name="MP Test Merchant",
|
||||
owner_user_id=mp_owner.id,
|
||||
contact_email=mp_owner.email,
|
||||
is_active=True,
|
||||
is_verified=True,
|
||||
)
|
||||
db.add(merchant)
|
||||
db.commit()
|
||||
db.refresh(merchant)
|
||||
return merchant
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_store(db, mp_merchant):
|
||||
"""Create a store."""
|
||||
uid = uuid.uuid4().hex[:8]
|
||||
store = Store(
|
||||
merchant_id=mp_merchant.id,
|
||||
store_code=f"MPSTORE_{uid.upper()}",
|
||||
subdomain=f"mpstore{uid.lower()}",
|
||||
name="MP Test Store",
|
||||
is_active=True,
|
||||
is_verified=True,
|
||||
)
|
||||
db.add(store)
|
||||
db.commit()
|
||||
db.refresh(store)
|
||||
return store
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_store_no_marketplace(db, mp_store, mp_platform_no_marketplace):
|
||||
"""Link store to a platform without marketplace."""
|
||||
sp = StorePlatform(
|
||||
store_id=mp_store.id,
|
||||
platform_id=mp_platform_no_marketplace.id,
|
||||
is_active=True,
|
||||
)
|
||||
db.add(sp)
|
||||
db.commit()
|
||||
db.refresh(sp)
|
||||
return sp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_store_with_marketplace(db, mp_store, mp_platform_with_marketplace):
|
||||
"""Link store to a platform with marketplace enabled."""
|
||||
sp = StorePlatform(
|
||||
store_id=mp_store.id,
|
||||
platform_id=mp_platform_with_marketplace.id,
|
||||
is_active=True,
|
||||
)
|
||||
db.add(sp)
|
||||
db.commit()
|
||||
db.refresh(sp)
|
||||
return sp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_onboarding_not_completed(db, mp_store):
|
||||
"""Create an incomplete onboarding record for the store."""
|
||||
onboarding = StoreOnboarding(
|
||||
store_id=mp_store.id,
|
||||
status=OnboardingStatus.IN_PROGRESS.value,
|
||||
)
|
||||
db.add(onboarding)
|
||||
db.commit()
|
||||
db.refresh(onboarding)
|
||||
return onboarding
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_onboarding_completed(db, mp_store):
|
||||
"""Create a completed onboarding record for the store."""
|
||||
onboarding = StoreOnboarding(
|
||||
store_id=mp_store.id,
|
||||
status=OnboardingStatus.COMPLETED.value,
|
||||
step_merchant_profile_completed=True,
|
||||
step_letzshop_api_completed=True,
|
||||
step_product_import_completed=True,
|
||||
step_order_sync_completed=True,
|
||||
)
|
||||
db.add(onboarding)
|
||||
db.commit()
|
||||
db.refresh(onboarding)
|
||||
return onboarding
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mp_auth(mp_owner, mp_store):
|
||||
"""Override auth dependency for store cookie/header auth."""
|
||||
user_context = UserContext(
|
||||
id=mp_owner.id,
|
||||
email=mp_owner.email,
|
||||
username=mp_owner.username,
|
||||
role="merchant_owner",
|
||||
is_active=True,
|
||||
token_store_id=mp_store.id,
|
||||
token_store_code=mp_store.store_code,
|
||||
)
|
||||
|
||||
def _override():
|
||||
return user_context
|
||||
|
||||
app.dependency_overrides[get_current_store_from_cookie_or_header] = _override
|
||||
yield {"Authorization": "Bearer fake-token"}
|
||||
app.dependency_overrides.pop(get_current_store_from_cookie_or_header, None)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Onboarding page tests
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.marketplace
|
||||
class TestOnboardingPageRoutes:
|
||||
"""Tests for GET /store/{store_code}/onboarding."""
|
||||
|
||||
def test_redirects_to_dashboard_on_non_marketplace_platform(
|
||||
self, client, db, mp_auth, mp_store, mp_store_no_marketplace
|
||||
):
|
||||
"""Onboarding page redirects to dashboard on platform without marketplace."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/onboarding",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert f"/store/{mp_store.subdomain}/dashboard" in response.headers["location"]
|
||||
|
||||
def test_redirects_to_dashboard_when_onboarding_completed(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
mp_onboarding_completed,
|
||||
):
|
||||
"""Onboarding page redirects to dashboard when already completed."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/onboarding",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert f"/store/{mp_store.subdomain}/dashboard" in response.headers["location"]
|
||||
|
||||
def test_renders_onboarding_when_not_completed(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
mp_onboarding_not_completed,
|
||||
):
|
||||
"""Onboarding page renders wizard when marketplace enabled and not completed."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/onboarding",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_renders_onboarding_when_no_onboarding_record(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
):
|
||||
"""Onboarding page renders wizard when marketplace enabled and no record exists."""
|
||||
# No mp_onboarding_* fixture — is_completed() returns False for missing record
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/onboarding",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_requires_auth(self, client):
|
||||
"""Returns 401 without auth."""
|
||||
app.dependency_overrides.pop(get_current_store_from_cookie_or_header, None)
|
||||
response = client.get(
|
||||
"/store/anystore/onboarding",
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Marketplace page tests
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.marketplace
|
||||
class TestMarketplacePageRoutes:
|
||||
"""Tests for GET /store/{store_code}/marketplace."""
|
||||
|
||||
def test_redirects_to_onboarding_when_not_completed(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
mp_onboarding_not_completed,
|
||||
):
|
||||
"""Marketplace page redirects to onboarding when not completed."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/marketplace",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert f"/store/{mp_store.subdomain}/onboarding" in response.headers["location"]
|
||||
|
||||
def test_renders_when_onboarding_completed(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
mp_onboarding_completed,
|
||||
):
|
||||
"""Marketplace page renders when onboarding is completed."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/marketplace",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_redirects_when_no_onboarding_record(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
):
|
||||
"""Marketplace page redirects when no onboarding record (not completed)."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/marketplace",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert f"/store/{mp_store.subdomain}/onboarding" in response.headers["location"]
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Letzshop page tests
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.marketplace
|
||||
class TestLetzshopPageRoutes:
|
||||
"""Tests for GET /store/{store_code}/letzshop."""
|
||||
|
||||
def test_redirects_to_onboarding_when_not_completed(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
mp_onboarding_not_completed,
|
||||
):
|
||||
"""Letzshop page redirects to onboarding when not completed."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/letzshop",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert f"/store/{mp_store.subdomain}/onboarding" in response.headers["location"]
|
||||
|
||||
def test_renders_when_onboarding_completed(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
mp_onboarding_completed,
|
||||
):
|
||||
"""Letzshop page renders when onboarding is completed."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/letzshop",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_redirects_when_no_onboarding_record(
|
||||
self, client, db, mp_auth, mp_store, mp_store_with_marketplace,
|
||||
):
|
||||
"""Letzshop page redirects when no onboarding record (not completed)."""
|
||||
response = client.get(
|
||||
f"/store/{mp_store.subdomain}/letzshop",
|
||||
headers=mp_auth,
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert f"/store/{mp_store.subdomain}/onboarding" in response.headers["location"]
|
||||
298
app/modules/marketplace/tests/unit/test_store_page_routes.py
Normal file
298
app/modules/marketplace/tests/unit/test_store_page_routes.py
Normal file
@@ -0,0 +1,298 @@
|
||||
# app/modules/marketplace/tests/unit/test_store_page_routes.py
|
||||
"""
|
||||
Unit tests for marketplace store page routes.
|
||||
|
||||
Tests the onboarding, marketplace, and letzshop page route logic:
|
||||
- Onboarding page guards (marketplace module check, completion redirect)
|
||||
- Marketplace page onboarding gate
|
||||
- Letzshop page onboarding gate
|
||||
"""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi import Request
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
from app.modules.marketplace.routes.pages.store import (
|
||||
store_letzshop_page,
|
||||
store_marketplace_page,
|
||||
store_onboarding_page,
|
||||
)
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
|
||||
def _make_user_context(store_id: int = 1, store_code: str = "teststore") -> UserContext:
|
||||
"""Create a UserContext for testing."""
|
||||
return UserContext(
|
||||
id=1,
|
||||
email="test@test.com",
|
||||
username="testuser",
|
||||
role="merchant_owner",
|
||||
is_active=True,
|
||||
token_store_id=store_id,
|
||||
token_store_code=store_code,
|
||||
)
|
||||
|
||||
|
||||
def _make_request() -> MagicMock:
|
||||
"""Create a mock Request."""
|
||||
return MagicMock(spec=Request)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ONBOARDING PAGE
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.marketplace
|
||||
@pytest.mark.asyncio
|
||||
class TestOnboardingPageGuard:
|
||||
"""Test that onboarding page redirects to dashboard when marketplace is not enabled."""
|
||||
|
||||
@patch("app.modules.marketplace.routes.pages.store.module_service")
|
||||
@patch("app.modules.marketplace.routes.pages.store.get_store_context")
|
||||
@patch("app.modules.marketplace.routes.pages.store.templates")
|
||||
async def test_redirects_to_dashboard_when_no_store_platform(
|
||||
self, mock_templates, mock_ctx, mock_module_service, db
|
||||
):
|
||||
"""Redirect to dashboard if store has no StorePlatform record."""
|
||||
user = _make_user_context(store_id=99999)
|
||||
response = await store_onboarding_page(
|
||||
request=_make_request(),
|
||||
store_code="teststore",
|
||||
current_user=user,
|
||||
db=db,
|
||||
)
|
||||
assert isinstance(response, RedirectResponse)
|
||||
assert response.status_code == 302
|
||||
assert "/store/teststore/dashboard" in response.headers["location"]
|
||||
|
||||
@patch("app.modules.marketplace.routes.pages.store.module_service")
|
||||
@patch("app.modules.marketplace.routes.pages.store.get_store_context")
|
||||
@patch("app.modules.marketplace.routes.pages.store.templates")
|
||||
async def test_redirects_to_dashboard_when_marketplace_not_enabled(
|
||||
self, mock_templates, mock_ctx, mock_module_service, db, test_store
|
||||
):
|
||||
"""Redirect to dashboard if marketplace module is not enabled on platform."""
|
||||
from app.modules.tenancy.models import Platform
|
||||
from app.modules.tenancy.models.store_platform import StorePlatform
|
||||
|
||||
platform = Platform(code="nomarket", name="No Marketplace", is_active=True)
|
||||
db.add(platform)
|
||||
db.flush()
|
||||
|
||||
sp = StorePlatform(store_id=test_store.id, platform_id=platform.id, is_active=True)
|
||||
db.add(sp)
|
||||
db.commit()
|
||||
|
||||
mock_module_service.is_module_enabled.return_value = False
|
||||
|
||||
user = _make_user_context(store_id=test_store.id)
|
||||
response = await store_onboarding_page(
|
||||
request=_make_request(),
|
||||
store_code="teststore",
|
||||
current_user=user,
|
||||
db=db,
|
||||
)
|
||||
assert isinstance(response, RedirectResponse)
|
||||
assert response.status_code == 302
|
||||
assert "/store/teststore/dashboard" in response.headers["location"]
|
||||
mock_module_service.is_module_enabled.assert_called_once_with(
|
||||
db, platform.id, "marketplace"
|
||||
)
|
||||
|
||||
@patch("app.modules.marketplace.routes.pages.store.OnboardingService")
|
||||
@patch("app.modules.marketplace.routes.pages.store.module_service")
|
||||
@patch("app.modules.marketplace.routes.pages.store.get_store_context")
|
||||
@patch("app.modules.marketplace.routes.pages.store.templates")
|
||||
async def test_redirects_to_dashboard_when_onboarding_completed(
|
||||
self, mock_templates, mock_ctx, mock_module_service, mock_onboarding_cls, db, test_store
|
||||
):
|
||||
"""Redirect to dashboard if onboarding is already completed."""
|
||||
from app.modules.tenancy.models import Platform
|
||||
from app.modules.tenancy.models.store_platform import StorePlatform
|
||||
|
||||
platform = Platform(code="mktplace", name="Marketplace", is_active=True)
|
||||
db.add(platform)
|
||||
db.flush()
|
||||
|
||||
sp = StorePlatform(store_id=test_store.id, platform_id=platform.id, is_active=True)
|
||||
db.add(sp)
|
||||
db.commit()
|
||||
|
||||
mock_module_service.is_module_enabled.return_value = True
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.is_completed.return_value = True
|
||||
mock_onboarding_cls.return_value = mock_svc
|
||||
|
||||
user = _make_user_context(store_id=test_store.id)
|
||||
response = await store_onboarding_page(
|
||||
request=_make_request(),
|
||||
store_code="teststore",
|
||||
current_user=user,
|
||||
db=db,
|
||||
)
|
||||
assert isinstance(response, RedirectResponse)
|
||||
assert response.status_code == 302
|
||||
assert "/store/teststore/dashboard" in response.headers["location"]
|
||||
|
||||
@patch("app.modules.marketplace.routes.pages.store.OnboardingService")
|
||||
@patch("app.modules.marketplace.routes.pages.store.module_service")
|
||||
@patch("app.modules.marketplace.routes.pages.store.get_store_context")
|
||||
@patch("app.modules.marketplace.routes.pages.store.templates")
|
||||
async def test_renders_onboarding_when_marketplace_enabled_and_not_completed(
|
||||
self, mock_templates, mock_ctx, mock_module_service, mock_onboarding_cls, db, test_store
|
||||
):
|
||||
"""Render onboarding wizard when marketplace enabled and onboarding not completed."""
|
||||
from app.modules.tenancy.models import Platform
|
||||
from app.modules.tenancy.models.store_platform import StorePlatform
|
||||
|
||||
platform = Platform(code="mktplace2", name="Marketplace 2", is_active=True)
|
||||
db.add(platform)
|
||||
db.flush()
|
||||
|
||||
sp = StorePlatform(store_id=test_store.id, platform_id=platform.id, is_active=True)
|
||||
db.add(sp)
|
||||
db.commit()
|
||||
|
||||
mock_module_service.is_module_enabled.return_value = True
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.is_completed.return_value = False
|
||||
mock_onboarding_cls.return_value = mock_svc
|
||||
|
||||
mock_ctx.return_value = {"request": _make_request()}
|
||||
mock_templates.TemplateResponse.return_value = "rendered"
|
||||
|
||||
user = _make_user_context(store_id=test_store.id)
|
||||
response = await store_onboarding_page(
|
||||
request=_make_request(),
|
||||
store_code="teststore",
|
||||
current_user=user,
|
||||
db=db,
|
||||
)
|
||||
assert response == "rendered"
|
||||
mock_templates.TemplateResponse.assert_called_once()
|
||||
template_name = mock_templates.TemplateResponse.call_args[0][0]
|
||||
assert template_name == "marketplace/store/onboarding.html"
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# MARKETPLACE PAGE
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.marketplace
|
||||
@pytest.mark.asyncio
|
||||
class TestMarketplacePageOnboardingGate:
|
||||
"""Test that marketplace page redirects to onboarding when not completed."""
|
||||
|
||||
@patch("app.modules.marketplace.routes.pages.store.OnboardingService")
|
||||
@patch("app.modules.marketplace.routes.pages.store.get_store_context")
|
||||
@patch("app.modules.marketplace.routes.pages.store.templates")
|
||||
async def test_redirects_to_onboarding_when_not_completed(
|
||||
self, mock_templates, mock_ctx, mock_onboarding_cls, db
|
||||
):
|
||||
"""Redirect to onboarding if onboarding not completed."""
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.is_completed.return_value = False
|
||||
mock_onboarding_cls.return_value = mock_svc
|
||||
|
||||
user = _make_user_context()
|
||||
response = await store_marketplace_page(
|
||||
request=_make_request(),
|
||||
store_code="teststore",
|
||||
current_user=user,
|
||||
db=db,
|
||||
)
|
||||
assert isinstance(response, RedirectResponse)
|
||||
assert response.status_code == 302
|
||||
assert "/store/teststore/onboarding" in response.headers["location"]
|
||||
|
||||
@patch("app.modules.marketplace.routes.pages.store.OnboardingService")
|
||||
@patch("app.modules.marketplace.routes.pages.store.get_store_context")
|
||||
@patch("app.modules.marketplace.routes.pages.store.templates")
|
||||
async def test_renders_marketplace_when_onboarding_completed(
|
||||
self, mock_templates, mock_ctx, mock_onboarding_cls, db
|
||||
):
|
||||
"""Render marketplace page if onboarding is completed."""
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.is_completed.return_value = True
|
||||
mock_onboarding_cls.return_value = mock_svc
|
||||
|
||||
mock_ctx.return_value = {"request": _make_request()}
|
||||
mock_templates.TemplateResponse.return_value = "rendered"
|
||||
|
||||
user = _make_user_context()
|
||||
response = await store_marketplace_page(
|
||||
request=_make_request(),
|
||||
store_code="teststore",
|
||||
current_user=user,
|
||||
db=db,
|
||||
)
|
||||
assert response == "rendered"
|
||||
mock_templates.TemplateResponse.assert_called_once()
|
||||
template_name = mock_templates.TemplateResponse.call_args[0][0]
|
||||
assert template_name == "marketplace/store/marketplace.html"
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# LETZSHOP PAGE
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.marketplace
|
||||
@pytest.mark.asyncio
|
||||
class TestLetzshopPageOnboardingGate:
|
||||
"""Test that letzshop page redirects to onboarding when not completed."""
|
||||
|
||||
@patch("app.modules.marketplace.routes.pages.store.OnboardingService")
|
||||
@patch("app.modules.marketplace.routes.pages.store.get_store_context")
|
||||
@patch("app.modules.marketplace.routes.pages.store.templates")
|
||||
async def test_redirects_to_onboarding_when_not_completed(
|
||||
self, mock_templates, mock_ctx, mock_onboarding_cls, db
|
||||
):
|
||||
"""Redirect to onboarding if onboarding not completed."""
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.is_completed.return_value = False
|
||||
mock_onboarding_cls.return_value = mock_svc
|
||||
|
||||
user = _make_user_context()
|
||||
response = await store_letzshop_page(
|
||||
request=_make_request(),
|
||||
store_code="teststore",
|
||||
current_user=user,
|
||||
db=db,
|
||||
)
|
||||
assert isinstance(response, RedirectResponse)
|
||||
assert response.status_code == 302
|
||||
assert "/store/teststore/onboarding" in response.headers["location"]
|
||||
|
||||
@patch("app.modules.marketplace.routes.pages.store.OnboardingService")
|
||||
@patch("app.modules.marketplace.routes.pages.store.get_store_context")
|
||||
@patch("app.modules.marketplace.routes.pages.store.templates")
|
||||
async def test_renders_letzshop_when_onboarding_completed(
|
||||
self, mock_templates, mock_ctx, mock_onboarding_cls, db
|
||||
):
|
||||
"""Render letzshop page if onboarding is completed."""
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.is_completed.return_value = True
|
||||
mock_onboarding_cls.return_value = mock_svc
|
||||
|
||||
mock_ctx.return_value = {"request": _make_request()}
|
||||
mock_templates.TemplateResponse.return_value = "rendered"
|
||||
|
||||
user = _make_user_context()
|
||||
response = await store_letzshop_page(
|
||||
request=_make_request(),
|
||||
store_code="teststore",
|
||||
current_user=user,
|
||||
db=db,
|
||||
)
|
||||
assert response == "rendered"
|
||||
mock_templates.TemplateResponse.assert_called_once()
|
||||
template_name = mock_templates.TemplateResponse.call_args[0][0]
|
||||
assert template_name == "marketplace/store/letzshop.html"
|
||||
Reference in New Issue
Block a user