# 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))