- Auto-fixed 4,496 lint issues (import sorting, modern syntax, etc.) - Added ignore rules for patterns intentional in this codebase: E402 (late imports), E712 (SQLAlchemy filters), B904 (raise from), SIM108/SIM105/SIM117 (readability preferences) - Added per-file ignores for tests and scripts - Excluded broken scripts/rename_terminology.py (has curly quotes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
316 lines
12 KiB
Python
316 lines
12 KiB
Python
# tests/unit/modules/test_module_definitions.py
|
|
"""
|
|
Unit tests for module definitions.
|
|
|
|
Tests cover:
|
|
- Module definition completeness (MOD-020)
|
|
- Menu/feature consistency (MOD-021)
|
|
- Permission coverage (MOD-022)
|
|
- Router pattern consistency (MOD-023)
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from app.modules.base import PermissionDefinition
|
|
from app.modules.registry import MODULES
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestModuleDefinitionCompleteness:
|
|
"""Test MOD-020: Module definitions have required attributes."""
|
|
|
|
def test_all_modules_have_code(self):
|
|
"""Test that all modules have a code defined."""
|
|
for code, module in MODULES.items():
|
|
assert module.code, f"Module {code} missing 'code' attribute"
|
|
assert module.code == code, f"Module code mismatch: {module.code} != {code}"
|
|
|
|
def test_all_modules_have_name(self):
|
|
"""Test that all modules have a name defined."""
|
|
for code, module in MODULES.items():
|
|
assert module.name, f"Module {code} missing 'name' attribute"
|
|
|
|
def test_all_modules_have_description(self):
|
|
"""Test that all modules have a description defined."""
|
|
for code, module in MODULES.items():
|
|
assert module.description, f"Module {code} missing 'description' attribute"
|
|
|
|
def test_all_modules_have_version(self):
|
|
"""Test that all modules have a version defined."""
|
|
for code, module in MODULES.items():
|
|
assert module.version, f"Module {code} missing 'version' attribute"
|
|
# Version should be semantic versioning format
|
|
parts = module.version.split(".")
|
|
assert len(parts) >= 2, f"Module {code} has invalid version format: {module.version}"
|
|
|
|
def test_all_modules_have_features(self):
|
|
"""Test that all modules have features defined."""
|
|
for code, module in MODULES.items():
|
|
assert module.features is not None, f"Module {code} missing 'features' attribute"
|
|
assert isinstance(module.features, list), f"Module {code} features should be a list"
|
|
# All modules should have at least one feature
|
|
assert len(module.features) > 0, f"Module {code} has empty features list"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestModulePermissions:
|
|
"""Test MOD-022: Feature modules should have permissions."""
|
|
|
|
# Modules exempt from permission requirements
|
|
EXEMPT_MODULES = {
|
|
"contracts", # Infrastructure module
|
|
"dev-tools", # Internal module (is_internal=True)
|
|
"monitoring", # Internal module (is_internal=True)
|
|
}
|
|
|
|
def test_feature_modules_have_permissions(self):
|
|
"""Test that modules with features have permissions (unless exempt)."""
|
|
for code, module in MODULES.items():
|
|
if code in self.EXEMPT_MODULES:
|
|
continue
|
|
|
|
# If module has features, it should have permissions
|
|
if module.features:
|
|
assert module.permissions is not None, (
|
|
f"Module {code} has features but no permissions"
|
|
)
|
|
assert len(module.permissions) > 0, (
|
|
f"Module {code} has features but empty permissions list"
|
|
)
|
|
|
|
def test_internal_modules_exempt_from_permissions(self):
|
|
"""Test that internal modules don't require permissions."""
|
|
internal_modules = [m for m in MODULES.values() if getattr(m, "is_internal", False)]
|
|
# Internal modules exist
|
|
assert len(internal_modules) > 0, "No internal modules found"
|
|
# They may or may not have permissions (exempt from requirement)
|
|
|
|
def test_permission_structure(self):
|
|
"""Test that permissions have required fields."""
|
|
for code, module in MODULES.items():
|
|
if not module.permissions:
|
|
continue
|
|
|
|
for perm in module.permissions:
|
|
assert isinstance(perm, PermissionDefinition), (
|
|
f"Module {code} permission is not a PermissionDefinition"
|
|
)
|
|
assert perm.id, f"Module {code} has permission without id"
|
|
assert perm.label_key, f"Module {code} permission {perm.id} missing label_key"
|
|
assert perm.description_key, (
|
|
f"Module {code} permission {perm.id} missing description_key"
|
|
)
|
|
assert perm.category, f"Module {code} permission {perm.id} missing category"
|
|
|
|
def test_permission_ids_follow_convention(self):
|
|
"""Test that permission IDs follow the module.action convention."""
|
|
for code, module in MODULES.items():
|
|
if not module.permissions:
|
|
continue
|
|
|
|
for perm in module.permissions:
|
|
# Permission ID should contain a dot
|
|
assert "." in perm.id, (
|
|
f"Module {code} permission {perm.id} should follow 'module.action' format"
|
|
)
|
|
|
|
def test_permission_counts(self):
|
|
"""Test that modules have expected permission counts."""
|
|
# Modules that should have permissions
|
|
modules_with_permissions = {
|
|
code: module
|
|
for code, module in MODULES.items()
|
|
if module.permissions
|
|
}
|
|
|
|
# At least 15 modules should have permissions
|
|
assert len(modules_with_permissions) >= 15, (
|
|
f"Expected at least 15 modules with permissions, got {len(modules_with_permissions)}"
|
|
)
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestModuleMenuFeatureConsistency:
|
|
"""Test MOD-021: Modules with menus should have features."""
|
|
|
|
def test_modules_with_menus_have_features(self):
|
|
"""Test that modules with menu definitions have features."""
|
|
for code, module in MODULES.items():
|
|
has_menus = getattr(module, "menus", None) and len(module.menus) > 0
|
|
has_menu_items = (
|
|
getattr(module, "menu_items", None) and
|
|
any(items for items in module.menu_items.values())
|
|
)
|
|
|
|
if has_menus or has_menu_items:
|
|
assert module.features and len(module.features) > 0, (
|
|
f"Module {code} has menus but no features defined"
|
|
)
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestModuleRouterPattern:
|
|
"""Test MOD-023: Router pattern consistency."""
|
|
|
|
def test_modules_with_routers_attribute(self):
|
|
"""Test that modules can have router attributes attached."""
|
|
# After calling get_*_with_routers(), modules should have router attrs
|
|
# This test just verifies the attribute exists (may be None)
|
|
for _code, module in MODULES.items():
|
|
# These are optional attributes set by get_*_with_routers()
|
|
# Just verify they can be accessed without error
|
|
_ = getattr(module, "admin_router", None)
|
|
_ = getattr(module, "store_router", None)
|
|
_ = getattr(module, "platform_router", None)
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSpecificModulePermissions:
|
|
"""Test specific modules have their expected permissions."""
|
|
|
|
def test_cart_module_permissions(self):
|
|
"""Test cart module has required permissions."""
|
|
module = MODULES.get("cart")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "cart.view" in perm_ids
|
|
assert "cart.manage" in perm_ids
|
|
|
|
def test_checkout_module_permissions(self):
|
|
"""Test checkout module has required permissions."""
|
|
module = MODULES.get("checkout")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "checkout.view_settings" in perm_ids
|
|
assert "checkout.manage_settings" in perm_ids
|
|
|
|
def test_analytics_module_permissions(self):
|
|
"""Test analytics module has required permissions."""
|
|
module = MODULES.get("analytics")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "analytics.view" in perm_ids
|
|
assert "analytics.export" in perm_ids
|
|
assert "analytics.manage_dashboards" in perm_ids
|
|
|
|
def test_billing_module_permissions(self):
|
|
"""Test billing module has required permissions."""
|
|
module = MODULES.get("billing")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "billing.view_tiers" in perm_ids
|
|
assert "billing.manage_tiers" in perm_ids
|
|
assert "billing.view_subscriptions" in perm_ids
|
|
assert "billing.manage_subscriptions" in perm_ids
|
|
assert "billing.view_invoices" in perm_ids
|
|
|
|
def test_cms_module_permissions(self):
|
|
"""Test cms module has required permissions."""
|
|
module = MODULES.get("cms")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "cms.view_pages" in perm_ids
|
|
assert "cms.manage_pages" in perm_ids
|
|
assert "cms.view_media" in perm_ids
|
|
assert "cms.manage_media" in perm_ids
|
|
assert "cms.manage_themes" in perm_ids
|
|
|
|
def test_loyalty_module_permissions(self):
|
|
"""Test loyalty module has required permissions."""
|
|
module = MODULES.get("loyalty")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "loyalty.view_programs" in perm_ids
|
|
assert "loyalty.manage_programs" in perm_ids
|
|
assert "loyalty.view_rewards" in perm_ids
|
|
assert "loyalty.manage_rewards" in perm_ids
|
|
|
|
def test_marketplace_module_permissions(self):
|
|
"""Test marketplace module has required permissions."""
|
|
module = MODULES.get("marketplace")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "marketplace.view_integration" in perm_ids
|
|
assert "marketplace.manage_integration" in perm_ids
|
|
assert "marketplace.sync_products" in perm_ids
|
|
|
|
def test_messaging_module_permissions(self):
|
|
"""Test messaging module has required permissions."""
|
|
module = MODULES.get("messaging")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "messaging.view_messages" in perm_ids
|
|
assert "messaging.send_messages" in perm_ids
|
|
assert "messaging.manage_templates" in perm_ids
|
|
|
|
def test_payments_module_permissions(self):
|
|
"""Test payments module has required permissions."""
|
|
module = MODULES.get("payments")
|
|
assert module is not None
|
|
assert module.permissions is not None
|
|
|
|
perm_ids = {p.id for p in module.permissions}
|
|
assert "payments.view_gateways" in perm_ids
|
|
assert "payments.manage_gateways" in perm_ids
|
|
assert "payments.view_transactions" in perm_ids
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestModuleFeatures:
|
|
"""Test module feature definitions."""
|
|
|
|
def test_cart_module_features(self):
|
|
"""Test cart module has required features."""
|
|
module = MODULES.get("cart")
|
|
assert module is not None
|
|
|
|
expected_features = {
|
|
"cart_management",
|
|
"cart_persistence",
|
|
"cart_item_operations",
|
|
"shipping_calculation",
|
|
"promotion_application",
|
|
}
|
|
assert expected_features.issubset(set(module.features))
|
|
|
|
def test_checkout_module_features(self):
|
|
"""Test checkout module has required features."""
|
|
module = MODULES.get("checkout")
|
|
assert module is not None
|
|
|
|
expected_features = {
|
|
"checkout_flow",
|
|
"order_creation",
|
|
"payment_processing",
|
|
"checkout_validation",
|
|
"guest_checkout",
|
|
}
|
|
assert expected_features.issubset(set(module.features))
|
|
|
|
def test_catalog_module_features(self):
|
|
"""Test catalog module has required features."""
|
|
module = MODULES.get("catalog")
|
|
assert module is not None
|
|
|
|
expected_features = {
|
|
"product_catalog",
|
|
"product_search",
|
|
}
|
|
assert expected_features.issubset(set(module.features))
|