""" Unit tests for rate limiting Tests DebugScopedRateThrottle with bypass logic """ from django.test import TestCase, override_settings from rest_framework.test import APIRequestFactory from rest_framework.views import APIView from igny8_core.api.throttles import DebugScopedRateThrottle from igny8_core.auth.models import User, Account, Plan class ThrottlesTestCase(TestCase): """Test cases for rate limiting""" def setUp(self): """Set up test fixtures""" self.factory = APIRequestFactory() self.view = APIView() self.view.throttle_scope = 'planner' # Create test plan and account self.plan = Plan.objects.create( name="Test Plan", slug="test-plan", price=0, credits_per_month=1000 ) # Create owner user first self.owner_user = User.objects.create_user( username='owner', email='owner@test.com', password='testpass123', role='owner' ) # Create test account with owner self.account = Account.objects.create( name="Test Account", slug="test-account", plan=self.plan, owner=self.owner_user ) # Update owner user to have account self.owner_user.account = self.account self.owner_user.save() # Create regular user self.user = User.objects.create_user( username='user', email='user@test.com', password='testpass123', role='viewer', account=self.account ) # Create admin user self.admin_user = User.objects.create_user( username='admin', email='admin@test.com', password='testpass123', role='admin', account=self.account ) # Create system account owner self.system_owner = User.objects.create_user( username='system_owner', email='system_owner@test.com', password='testpass123', role='owner' ) # Create system account user self.system_account = Account.objects.create( name="AWS Admin", slug="aws-admin", plan=self.plan, owner=self.system_owner ) self.system_owner.account = self.system_account self.system_owner.save() self.system_user = User.objects.create_user( username='system', email='system@test.com', password='testpass123', role='viewer', account=self.system_account ) @override_settings(DEBUG=True) def test_debug_mode_bypass(self): """Test throttling is bypassed in DEBUG mode""" throttle = DebugScopedRateThrottle() request = self.factory.get('/test/') request.user = self.user result = throttle.allow_request(request, self.view) self.assertTrue(result) # Should bypass in DEBUG mode @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=True) def test_env_bypass(self): """Test throttling is bypassed when IGNY8_DEBUG_THROTTLE=True""" throttle = DebugScopedRateThrottle() request = self.factory.get('/test/') request.user = self.user result = throttle.allow_request(request, self.view) self.assertTrue(result) # Should bypass when IGNY8_DEBUG_THROTTLE=True @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) def test_system_account_bypass(self): """Test throttling is bypassed for system account users""" throttle = DebugScopedRateThrottle() request = self.factory.get('/test/') request.user = self.system_user result = throttle.allow_request(request, self.view) self.assertTrue(result) # System account users should bypass @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) def test_admin_bypass(self): """Test throttling is bypassed for admin/developer users""" throttle = DebugScopedRateThrottle() request = self.factory.get('/test/') request.user = self.admin_user result = throttle.allow_request(request, self.view) self.assertTrue(result) # Admin users should bypass @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) def test_get_rate(self): """Test get_rate returns correct rate for scope""" throttle = DebugScopedRateThrottle() throttle.scope = 'planner' rate = throttle.get_rate() self.assertIsNotNone(rate) self.assertIn('/', rate) # Should be in format "60/min" @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) def test_get_rate_default_fallback(self): """Test get_rate falls back to default if scope not found""" throttle = DebugScopedRateThrottle() throttle.scope = 'nonexistent_scope' rate = throttle.get_rate() self.assertIsNotNone(rate) self.assertEqual(rate, '100/min') # Should fallback to default def test_parse_rate_minutes(self): """Test parse_rate correctly parses minutes""" throttle = DebugScopedRateThrottle() num, duration = throttle.parse_rate('60/min') self.assertEqual(num, 60) self.assertEqual(duration, 60) def test_parse_rate_seconds(self): """Test parse_rate correctly parses seconds""" throttle = DebugScopedRateThrottle() num, duration = throttle.parse_rate('10/sec') self.assertEqual(num, 10) self.assertEqual(duration, 1) def test_parse_rate_hours(self): """Test parse_rate correctly parses hours""" throttle = DebugScopedRateThrottle() num, duration = throttle.parse_rate('100/hour') self.assertEqual(num, 100) self.assertEqual(duration, 3600) def test_parse_rate_invalid_format(self): """Test parse_rate handles invalid format gracefully""" throttle = DebugScopedRateThrottle() num, duration = throttle.parse_rate('invalid') self.assertEqual(num, 100) # Should default to 100 self.assertEqual(duration, 60) # Should default to 60 seconds (1 min) @override_settings(DEBUG=True) def test_debug_info_set(self): """Test debug info is set when bypassing in DEBUG mode""" throttle = DebugScopedRateThrottle() request = self.factory.get('/test/') request.user = self.user result = throttle.allow_request(request, self.view) self.assertTrue(result) self.assertTrue(hasattr(request, '_throttle_debug_info')) self.assertIn('scope', request._throttle_debug_info) self.assertIn('rate', request._throttle_debug_info) self.assertIn('limit', request._throttle_debug_info)