test_health_endpoint.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import json
  2. from unittest.mock import MagicMock, Mock
  3. import pytest
  4. import sqlalchemy.exc
  5. from pytest_mock import MockerFixture
  6. from redis.exceptions import RedisError
  7. from reflex.app import health
  8. from reflex.model import get_db_status
  9. from reflex.utils.prerequisites import get_redis_status
  10. @pytest.mark.asyncio
  11. @pytest.mark.parametrize(
  12. "mock_redis_client, expected_status",
  13. [
  14. # Case 1: Redis client is available and responds to ping
  15. (Mock(ping=lambda: None), {"redis": True}),
  16. # Case 2: Redis client raises RedisError
  17. (Mock(ping=lambda: (_ for _ in ()).throw(RedisError)), {"redis": False}),
  18. # Case 3: Redis client is not used
  19. (None, {"redis": None}),
  20. ],
  21. )
  22. async def test_get_redis_status(
  23. mock_redis_client, expected_status, mocker: MockerFixture
  24. ):
  25. # Mock the `get_redis_sync` function to return the mock Redis client
  26. mock_get_redis_sync = mocker.patch(
  27. "reflex.utils.prerequisites.get_redis_sync", return_value=mock_redis_client
  28. )
  29. # Call the function
  30. status = await get_redis_status()
  31. # Verify the result
  32. assert status == expected_status
  33. mock_get_redis_sync.assert_called_once()
  34. @pytest.mark.asyncio
  35. @pytest.mark.parametrize(
  36. "mock_engine, execute_side_effect, expected_status",
  37. [
  38. # Case 1: Database is accessible
  39. (MagicMock(), None, {"db": True}),
  40. # Case 2: Database connection error (OperationalError)
  41. (
  42. MagicMock(),
  43. sqlalchemy.exc.OperationalError("error", "error", "error"), # pyright: ignore[reportArgumentType]
  44. {"db": False},
  45. ),
  46. ],
  47. )
  48. async def test_get_db_status(
  49. mock_engine, execute_side_effect, expected_status, mocker: MockerFixture
  50. ):
  51. # Mock get_engine to return the mock_engine
  52. mock_get_engine = mocker.patch("reflex.model.get_engine", return_value=mock_engine)
  53. # Mock the connection and its execute method
  54. if mock_engine:
  55. mock_connection = mock_engine.connect.return_value.__enter__.return_value
  56. if execute_side_effect:
  57. # Simulate execute method raising an exception
  58. mock_connection.execute.side_effect = execute_side_effect
  59. else:
  60. # Simulate successful execute call
  61. mock_connection.execute.return_value = None
  62. # Call the function
  63. status = await get_db_status()
  64. # Verify the result
  65. assert status == expected_status
  66. mock_get_engine.assert_called_once()
  67. @pytest.mark.asyncio
  68. @pytest.mark.parametrize(
  69. "db_enabled, redis_enabled, db_status, redis_status, expected_status, expected_code",
  70. [
  71. # Case 1: Both services are connected
  72. (True, True, True, True, {"status": True, "db": True, "redis": True}, 200),
  73. # Case 2: Database not connected, Redis connected
  74. (True, True, False, True, {"status": False, "db": False, "redis": True}, 503),
  75. # Case 3: Database connected, Redis not connected
  76. (True, True, True, False, {"status": False, "db": True, "redis": False}, 503),
  77. # Case 4: Both services not connected
  78. (True, True, False, False, {"status": False, "db": False, "redis": False}, 503),
  79. # Case 5: Database Connected, Redis not used
  80. (True, False, True, None, {"status": True, "db": True}, 200),
  81. # Case 6: Database not used, Redis Connected
  82. (False, True, None, True, {"status": True, "redis": True}, 200),
  83. # Case 7: Both services not used
  84. (False, False, None, None, {"status": True}, 200),
  85. ],
  86. )
  87. async def test_health(
  88. db_enabled,
  89. redis_enabled,
  90. db_status,
  91. redis_status,
  92. expected_status,
  93. expected_code,
  94. mocker: MockerFixture,
  95. ):
  96. # Mock get_db_status and get_redis_status
  97. mocker.patch(
  98. "reflex.utils.prerequisites.check_db_used",
  99. return_value=db_enabled,
  100. )
  101. mocker.patch(
  102. "reflex.utils.prerequisites.check_redis_used",
  103. return_value=redis_enabled,
  104. )
  105. mocker.patch(
  106. "reflex.app.get_db_status",
  107. return_value={"db": db_status},
  108. )
  109. mocker.patch(
  110. "reflex.utils.prerequisites.get_redis_status",
  111. return_value={"redis": redis_status},
  112. )
  113. request = Mock()
  114. # Call the async health function
  115. response = await health(request)
  116. # Verify the response content and status code
  117. assert response.status_code == expected_code
  118. assert isinstance(response.body, bytes)
  119. assert json.loads(response.body) == expected_status