# tests/integration/api/v1/modules/test_module_access.py """ Integration tests for module-based access control. Tests verify that: - Disabled modules return 403 Forbidden - Enabled modules allow access - Core modules are always accessible - Module dependencies are enforced """ import pytest from app.modules.tenancy.models import Platform @pytest.mark.integration @pytest.mark.api @pytest.mark.modules class TestModuleAccessControl: """Tests for module-based access control on API endpoints.""" # ======================================================================== # Billing Module Access Tests # ======================================================================== def test_billing_accessible_when_enabled( self, client, auth_headers, test_vendor, db ): """Test billing endpoints accessible when module enabled.""" # Ensure billing module is enabled (default - no config means all enabled) response = client.get( "/api/v1/vendor/billing/subscription", headers=auth_headers, ) # Should succeed (200) or have other error, but NOT 403 for module assert response.status_code != 403 or "module" not in response.json().get("message", "").lower() def test_billing_forbidden_when_disabled( self, client, auth_headers, test_vendor, db, test_platform ): """Test billing endpoints return 403 when module disabled.""" # Disable billing module test_platform.settings = {"enabled_modules": ["core", "platform-admin", "inventory"]} db.commit() response = client.get( "/api/v1/vendor/billing/subscription", headers=auth_headers, ) # Should return 403 with module disabled message assert response.status_code == 403 data = response.json() assert "module" in data.get("message", "").lower() or data.get("error_code") == "MODULE_DISABLED" # ======================================================================== # Inventory Module Access Tests # ======================================================================== def test_inventory_accessible_when_enabled( self, client, auth_headers, test_inventory ): """Test inventory endpoints accessible when module enabled.""" response = client.get( "/api/v1/vendor/inventory", headers=auth_headers, ) # Should succeed assert response.status_code == 200 def test_inventory_forbidden_when_disabled( self, client, auth_headers, db, test_platform ): """Test inventory endpoints return 403 when module disabled.""" # Disable inventory module test_platform.settings = {"enabled_modules": ["core", "platform-admin", "billing"]} db.commit() response = client.get( "/api/v1/vendor/inventory", headers=auth_headers, ) # Should return 403 assert response.status_code == 403 data = response.json() assert "module" in data.get("message", "").lower() or data.get("error_code") == "MODULE_DISABLED" # ======================================================================== # Orders Module Access Tests # ======================================================================== def test_orders_accessible_when_enabled( self, client, auth_headers, test_order ): """Test orders endpoints accessible when module enabled.""" response = client.get( "/api/v1/vendor/orders", headers=auth_headers, ) # Should succeed assert response.status_code == 200 def test_orders_forbidden_when_disabled( self, client, auth_headers, db, test_platform ): """Test orders endpoints return 403 when module disabled.""" # Disable orders module test_platform.settings = {"enabled_modules": ["core", "platform-admin"]} db.commit() response = client.get( "/api/v1/vendor/orders", headers=auth_headers, ) # Should return 403 assert response.status_code == 403 # ======================================================================== # Marketplace Module Access Tests # ======================================================================== def test_marketplace_accessible_when_enabled( self, client, auth_headers ): """Test marketplace endpoints accessible when module enabled.""" response = client.get( "/api/v1/vendor/marketplace/settings", headers=auth_headers, ) # Should not return 403 for module disabled # (might be 404 if no settings exist, or 200) assert response.status_code != 403 or "module" not in response.json().get("message", "").lower() def test_marketplace_forbidden_when_disabled( self, client, auth_headers, db, test_platform ): """Test marketplace endpoints return 403 when module disabled.""" # Disable marketplace module but keep inventory (its dependency) test_platform.settings = {"enabled_modules": ["core", "platform-admin", "inventory"]} db.commit() response = client.get( "/api/v1/vendor/marketplace/settings", headers=auth_headers, ) # Should return 403 assert response.status_code == 403 # ======================================================================== # Core Module Tests # ======================================================================== def test_core_always_accessible( self, client, auth_headers, db, test_platform ): """Test core endpoints always accessible even with empty modules.""" # Set empty module list (but core is always added) test_platform.settings = {"enabled_modules": []} db.commit() # Dashboard is a core endpoint response = client.get( "/api/v1/vendor/dashboard", headers=auth_headers, ) # Should NOT return 403 for module disabled assert response.status_code != 403 or "module" not in response.json().get("message", "").lower() # ======================================================================== # Admin Module Access Tests # ======================================================================== def test_admin_inventory_accessible_when_enabled( self, client, admin_headers, test_inventory ): """Test admin inventory endpoints accessible when module enabled.""" response = client.get( "/api/v1/admin/inventory", headers=admin_headers, ) # Should succeed assert response.status_code == 200 def test_admin_inventory_forbidden_when_disabled( self, client, admin_headers, db, test_platform ): """Test admin inventory endpoints return 403 when module disabled.""" # Disable inventory module test_platform.settings = {"enabled_modules": ["core", "platform-admin"]} db.commit() response = client.get( "/api/v1/admin/inventory", headers=admin_headers, ) # Should return 403 assert response.status_code == 403 @pytest.mark.integration @pytest.mark.api @pytest.mark.modules class TestModuleDependencyAccess: """Tests for module dependency enforcement in access control.""" def test_marketplace_requires_inventory( self, client, auth_headers, db, test_platform ): """Test marketplace requires inventory to be enabled.""" # Enable marketplace but disable inventory test_platform.settings = {"enabled_modules": ["marketplace"]} db.commit() # Due to dependency resolution, inventory should be auto-enabled response = client.get( "/api/v1/vendor/inventory", headers=auth_headers, ) # Should be accessible because marketplace depends on inventory # The module service should auto-enable inventory assert response.status_code != 403 or "module" not in response.json().get("message", "").lower() def test_disabling_dependency_disables_dependent( self, client, auth_headers, db, test_platform ): """Test that disabling a dependency also affects dependent modules.""" # First enable both test_platform.settings = {"enabled_modules": ["inventory", "marketplace"]} db.commit() # Now disable inventory - marketplace should also be affected test_platform.settings = {"enabled_modules": []} # Only core remains db.commit() # Marketplace should be disabled response = client.get( "/api/v1/vendor/marketplace/settings", headers=auth_headers, ) assert response.status_code == 403