Files
orion/tests/integration/api/v1/admin/test_modules.py
Samir Boulahtit c419090531 feat: complete modular platform architecture (Phases 1-5)
Phase 1 - Vendor Router Integration:
- Wire up vendor module routers in app/api/v1/vendor/__init__.py
- Use lazy imports via __getattr__ to avoid circular dependencies

Phase 2 - Extract Remaining Modules:
- Create 6 new module directories: customers, cms, analytics, messaging,
  dev_tools, monitoring
- Each module has definition.py and route wrappers
- Update registry to import from extracted modules

Phase 3 - Database Table Migration:
- Add PlatformModule junction table for auditable module tracking
- Add migration zc2m3n4o5p6q7_add_platform_modules_table.py
- Add modules relationship to Platform model
- Update ModuleService with JSON-to-junction-table migration

Phase 4 - Module-Specific Configuration UI:
- Add /api/v1/admin/module-config/* endpoints
- Add module-config.html template and JS

Phase 5 - Integration Tests:
- Add tests/fixtures/module_fixtures.py
- Add tests/integration/api/v1/admin/test_modules.py
- Add tests/integration/api/v1/modules/test_module_access.py

Architecture fixes:
- Fix JS-003 errors: use ...data() directly in Alpine components
- Fix JS-005 warnings: add init() guards to prevent duplicate init
- Fix API-001 errors: add MenuActionResponse Pydantic model
- Add FE-008 noqa for dynamic number input in template

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 18:19:00 +01:00

329 lines
12 KiB
Python

# tests/integration/api/v1/admin/test_modules.py
"""
Integration tests for admin module management endpoints.
Tests the /api/v1/admin/modules/* and /api/v1/admin/module-config/* endpoints.
All endpoints require super admin access.
"""
import pytest
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.admin
@pytest.mark.modules
class TestAdminModulesAPI:
"""Tests for admin module management endpoints."""
# ========================================================================
# List Modules Tests
# ========================================================================
def test_list_all_modules(self, client, super_admin_headers):
"""Test super admin listing all modules."""
response = client.get("/api/v1/admin/modules", headers=super_admin_headers)
assert response.status_code == 200
data = response.json()
assert "modules" in data
assert "total" in data
assert data["total"] >= 10 # At least 10 modules defined
# Check expected modules exist
module_codes = [m["code"] for m in data["modules"]]
assert "core" in module_codes
assert "billing" in module_codes
assert "inventory" in module_codes
def test_list_modules_requires_super_admin(self, client, admin_headers):
"""Test that listing modules requires super admin."""
response = client.get("/api/v1/admin/modules", headers=admin_headers)
# Should require super admin
assert response.status_code == 403
def test_list_modules_unauthenticated(self, client):
"""Test that listing modules requires authentication."""
response = client.get("/api/v1/admin/modules")
assert response.status_code == 401
# ========================================================================
# Get Platform Modules Tests
# ========================================================================
def test_get_platform_modules(self, client, super_admin_headers, test_platform):
"""Test getting modules for a specific platform."""
response = client.get(
f"/api/v1/admin/modules/platforms/{test_platform.id}",
headers=super_admin_headers,
)
assert response.status_code == 200
data = response.json()
assert data["platform_id"] == test_platform.id
assert data["platform_code"] == test_platform.code
assert "modules" in data
assert "enabled" in data
assert "disabled" in data
def test_get_platform_modules_not_found(self, client, super_admin_headers):
"""Test getting modules for non-existent platform."""
response = client.get(
"/api/v1/admin/modules/platforms/99999",
headers=super_admin_headers,
)
assert response.status_code == 404
# ========================================================================
# Enable/Disable Module Tests
# ========================================================================
def test_enable_module(self, client, super_admin_headers, test_platform, db):
"""Test enabling a module for a platform."""
# First disable the module via settings
test_platform.settings = {"enabled_modules": ["core", "platform-admin"]}
db.commit()
response = client.post(
f"/api/v1/admin/modules/platforms/{test_platform.id}/enable",
headers=super_admin_headers,
json={"module_code": "billing"},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert "billing" in data["message"].lower() or "enabled" in data["message"].lower()
def test_disable_module(self, client, super_admin_headers, test_platform, db):
"""Test disabling a module for a platform."""
# Ensure module is enabled
test_platform.settings = {"enabled_modules": ["billing", "inventory"]}
db.commit()
response = client.post(
f"/api/v1/admin/modules/platforms/{test_platform.id}/disable",
headers=super_admin_headers,
json={"module_code": "billing"},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
def test_cannot_disable_core_module(self, client, super_admin_headers, test_platform):
"""Test that core modules cannot be disabled."""
response = client.post(
f"/api/v1/admin/modules/platforms/{test_platform.id}/disable",
headers=super_admin_headers,
json={"module_code": "core"},
)
assert response.status_code == 400
data = response.json()
assert "core" in data.get("message", "").lower() or "cannot" in data.get("message", "").lower()
def test_enable_invalid_module(self, client, super_admin_headers, test_platform):
"""Test enabling a non-existent module."""
response = client.post(
f"/api/v1/admin/modules/platforms/{test_platform.id}/enable",
headers=super_admin_headers,
json={"module_code": "invalid_module"},
)
assert response.status_code == 400
# ========================================================================
# Bulk Operations Tests
# ========================================================================
def test_update_platform_modules(self, client, super_admin_headers, test_platform):
"""Test updating all enabled modules at once."""
response = client.put(
f"/api/v1/admin/modules/platforms/{test_platform.id}",
headers=super_admin_headers,
json={"module_codes": ["billing", "inventory", "orders"]},
)
assert response.status_code == 200
data = response.json()
assert data["platform_id"] == test_platform.id
# Check that specified modules are enabled
enabled_codes = [m["code"] for m in data["modules"] if m["is_enabled"]]
assert "billing" in enabled_codes
assert "inventory" in enabled_codes
assert "orders" in enabled_codes
# Core modules should always be enabled
assert "core" in enabled_codes
def test_enable_all_modules(self, client, super_admin_headers, test_platform):
"""Test enabling all modules for a platform."""
response = client.post(
f"/api/v1/admin/modules/platforms/{test_platform.id}/enable-all",
headers=super_admin_headers,
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["enabled_count"] >= 10
def test_disable_optional_modules(self, client, super_admin_headers, test_platform):
"""Test disabling all optional modules."""
response = client.post(
f"/api/v1/admin/modules/platforms/{test_platform.id}/disable-optional",
headers=super_admin_headers,
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert "core" in data["core_modules"]
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.admin
@pytest.mark.modules
class TestAdminModuleConfigAPI:
"""Tests for admin module configuration endpoints."""
# ========================================================================
# Get Module Config Tests
# ========================================================================
def test_get_module_config(self, client, super_admin_headers, test_platform):
"""Test getting module configuration."""
response = client.get(
f"/api/v1/admin/module-config/platforms/{test_platform.id}/modules/billing/config",
headers=super_admin_headers,
)
assert response.status_code == 200
data = response.json()
assert data["module_code"] == "billing"
assert "config" in data
assert "schema_info" in data
assert "defaults" in data
def test_get_module_config_has_defaults(self, client, super_admin_headers, test_platform):
"""Test that module config includes default values."""
response = client.get(
f"/api/v1/admin/module-config/platforms/{test_platform.id}/modules/billing/config",
headers=super_admin_headers,
)
assert response.status_code == 200
data = response.json()
# Should have default billing config
assert "stripe_mode" in data["config"]
assert "default_trial_days" in data["config"]
def test_get_module_config_invalid_module(self, client, super_admin_headers, test_platform):
"""Test getting config for invalid module."""
response = client.get(
f"/api/v1/admin/module-config/platforms/{test_platform.id}/modules/invalid_module/config",
headers=super_admin_headers,
)
assert response.status_code == 400
# ========================================================================
# Update Module Config Tests
# ========================================================================
def test_update_module_config(self, client, super_admin_headers, test_platform):
"""Test updating module configuration."""
response = client.put(
f"/api/v1/admin/module-config/platforms/{test_platform.id}/modules/billing/config",
headers=super_admin_headers,
json={
"config": {
"stripe_mode": "live",
"default_trial_days": 7,
}
},
)
assert response.status_code == 200
data = response.json()
assert data["config"]["stripe_mode"] == "live"
assert data["config"]["default_trial_days"] == 7
def test_update_module_config_persists(self, client, super_admin_headers, test_platform):
"""Test that config updates persist across requests."""
# Update config
client.put(
f"/api/v1/admin/module-config/platforms/{test_platform.id}/modules/inventory/config",
headers=super_admin_headers,
json={
"config": {
"low_stock_threshold": 25,
}
},
)
# Fetch again
response = client.get(
f"/api/v1/admin/module-config/platforms/{test_platform.id}/modules/inventory/config",
headers=super_admin_headers,
)
assert response.status_code == 200
data = response.json()
assert data["config"]["low_stock_threshold"] == 25
# ========================================================================
# Reset Config Tests
# ========================================================================
def test_reset_module_config(self, client, super_admin_headers, test_platform):
"""Test resetting module config to defaults."""
# First set custom config
client.put(
f"/api/v1/admin/module-config/platforms/{test_platform.id}/modules/billing/config",
headers=super_admin_headers,
json={
"config": {
"stripe_mode": "live",
"default_trial_days": 1,
}
},
)
# Reset to defaults
response = client.post(
f"/api/v1/admin/module-config/platforms/{test_platform.id}/modules/billing/reset",
headers=super_admin_headers,
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
# Config should be reset to defaults
assert data["config"]["stripe_mode"] == "test"
assert data["config"]["default_trial_days"] == 14
# ========================================================================
# Get Defaults Tests
# ========================================================================
def test_get_config_defaults(self, client, super_admin_headers):
"""Test getting default config for a module."""
response = client.get(
"/api/v1/admin/module-config/defaults/billing",
headers=super_admin_headers,
)
assert response.status_code == 200
data = response.json()
assert data["module_code"] == "billing"
assert "defaults" in data
assert "schema_info" in data
assert data["defaults"]["stripe_mode"] == "test"