123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- import uuid
- from datetime import datetime, timedelta, timezone
- from uuid import UUID
- import pytest
- from core.base import LimitSettings
- from core.database.postgres import PostgresLimitsHandler
- from shared.abstractions import User
- @pytest.mark.asyncio
- async def test_log_request_and_count(limits_handler):
- """
- Test that when we log requests, the count increments, and rate-limits are enforced.
- Route-specific test using the /v3/retrieval/search endpoint limits.
- """
- # Clear existing logs first
- clear_query = f"DELETE FROM {limits_handler._get_table_name(PostgresLimitsHandler.TABLE_NAME)}"
- await limits_handler.connection_manager.execute_query(clear_query)
- user_id = uuid.uuid4()
- route = "/v3/retrieval/search" # Using actual route from config
- test_user = User(
- id=user_id,
- email="test@example.com",
- is_active=True,
- is_verified=True,
- is_superuser=False,
- limits_overrides=None,
- )
- # Set route limit to match config: 5 requests per minute
- old_route_limits = limits_handler.config.route_limits
- new_route_limits = {
- route: LimitSettings(route_per_min=5, monthly_limit=10)
- }
- limits_handler.config.route_limits = new_route_limits
- print(f"\nTesting with route limits: {new_route_limits}")
- print(f"Route settings: {limits_handler.config.route_limits[route]}")
- try:
- # Initial check should pass (no requests yet)
- await limits_handler.check_limits(test_user, route)
- print("Initial check passed (no requests)")
- # Log 5 requests (exactly at limit)
- for i in range(5):
- await limits_handler.log_request(user_id, route)
- now = datetime.now(timezone.utc)
- one_min_ago = now - timedelta(minutes=1)
- route_count = await limits_handler._count_requests(
- user_id, route, one_min_ago
- )
- print(f"Route count after request {i+1}: {route_count}")
- # This should pass for all 5 requests
- await limits_handler.check_limits(test_user, route)
- print(f"Check limits passed after request {i+1}")
- # Log the 6th request (over limit)
- await limits_handler.log_request(user_id, route)
- route_count = await limits_handler._count_requests(
- user_id, route, one_min_ago
- )
- print(f"Route count after request 6: {route_count}")
- # This check should fail as we've exceeded route_per_min=5
- with pytest.raises(
- ValueError, match="Per-route per-minute rate limit exceeded"
- ):
- await limits_handler.check_limits(test_user, route)
- finally:
- limits_handler.config.route_limits = old_route_limits
- @pytest.mark.asyncio
- async def test_global_limit(limits_handler):
- """
- Test global limit using the configured limit of 10 requests per minute
- """
- # Clear existing logs
- clear_query = f"DELETE FROM {limits_handler._get_table_name(PostgresLimitsHandler.TABLE_NAME)}"
- await limits_handler.connection_manager.execute_query(clear_query)
- user_id = uuid.uuid4()
- route = "/global-test"
- test_user = User(
- id=user_id,
- email="globaltest@example.com",
- is_active=True,
- is_verified=True,
- is_superuser=False,
- limits_overrides=None,
- )
- # Set global limit to match config: 10 requests per minute
- old_limits = limits_handler.config.limits
- limits_handler.config.limits = LimitSettings(
- global_per_min=10, monthly_limit=20
- )
- try:
- # Initial check should pass (no requests)
- await limits_handler.check_limits(test_user, route)
- print("Initial global check passed (no requests)")
- # Log 10 requests (hits the limit)
- for i in range(11):
- await limits_handler.log_request(user_id, route)
- # Debug counts
- now = datetime.now(timezone.utc)
- one_min_ago = now - timedelta(minutes=1)
- global_count = await limits_handler._count_requests(
- user_id, None, one_min_ago
- )
- print(f"Global count after 10 requests: {global_count}")
- # This should fail as we've hit global_per_min=10
- with pytest.raises(
- ValueError, match="Global per-minute rate limit exceeded"
- ):
- await limits_handler.check_limits(test_user, route)
- finally:
- limits_handler.config.limits = old_limits
- @pytest.mark.asyncio
- async def test_monthly_limit(limits_handler):
- """
- Test monthly limit using the configured limit of 20 requests per month
- """
- # Clear existing logs
- clear_query = f"DELETE FROM {limits_handler._get_table_name(PostgresLimitsHandler.TABLE_NAME)}"
- await limits_handler.connection_manager.execute_query(clear_query)
- user_id = uuid.uuid4()
- route = "/monthly-test"
- test_user = User(
- id=user_id,
- email="monthly@example.com",
- is_active=True,
- is_verified=True,
- is_superuser=False,
- limits_overrides=None,
- )
- old_limits = limits_handler.config.limits
- limits_handler.config.limits = LimitSettings(monthly_limit=20)
- try:
- # Initial check should pass (no requests)
- await limits_handler.check_limits(test_user, route)
- print("Initial monthly check passed (no requests)")
- # Log 20 requests (hits the monthly limit)
- for i in range(21):
- await limits_handler.log_request(user_id, route)
- # Get current month's count
- now = datetime.now(timezone.utc)
- first_of_month = now.replace(
- day=1, hour=0, minute=0, second=0, microsecond=0
- )
- monthly_count = await limits_handler._count_requests(
- user_id, None, first_of_month
- )
- print(f"Monthly count after 20 requests: {monthly_count}")
- # This should fail as we've hit monthly_limit=20
- with pytest.raises(ValueError, match="Monthly rate limit exceeded"):
- await limits_handler.check_limits(test_user, route)
- finally:
- limits_handler.config.limits = old_limits
- @pytest.mark.asyncio
- async def test_user_level_override(limits_handler):
- """
- Test user-specific override limits with debug logging
- """
- user_id = UUID("47e53676-b478-5b3f-a409-234ca2164de5")
- route = "/test-route"
- # Clear existing logs first
- clear_query = f"DELETE FROM {limits_handler._get_table_name(PostgresLimitsHandler.TABLE_NAME)}"
- await limits_handler.connection_manager.execute_query(clear_query)
- test_user = User(
- id=user_id,
- email="override@example.com",
- is_active=True,
- is_verified=True,
- is_superuser=False,
- limits_overrides={
- "global_per_min": 2,
- "route_per_min": 1,
- "route_overrides": {"/test-route": {"route_per_min": 1}},
- },
- )
- # Set default limits that should be overridden
- old_limits = limits_handler.config.limits
- limits_handler.config.limits = LimitSettings(
- global_per_min=10, monthly_limit=20
- )
- # Debug: Print current limits
- print(f"\nDefault limits: {limits_handler.config.limits}")
- print(f"User overrides: {test_user.limits_overrides}")
- try:
- # First check limits (should pass as no requests yet)
- await limits_handler.check_limits(test_user, route)
- print("Initial check passed (no requests yet)")
- # Log first request
- await limits_handler.log_request(user_id, route)
- # Debug: Get current counts
- now = datetime.now(timezone.utc)
- one_min_ago = now - timedelta(minutes=1)
- global_count = await limits_handler._count_requests(
- user_id, None, one_min_ago
- )
- route_count = await limits_handler._count_requests(
- user_id, route, one_min_ago
- )
- print(f"\nAfter first request:")
- print(f"Global count: {global_count}")
- print(f"Route count: {route_count}")
- # Log second request
- await limits_handler.log_request(user_id, route)
- # This check should fail as we've hit route_per_min=1
- with pytest.raises(
- ValueError, match="Per-route per-minute rate limit exceeded"
- ):
- await limits_handler.check_limits(test_user, route)
- finally:
- # Cleanup
- limits_handler.config.limits = old_limits
|