test_config.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import multiprocessing
  2. import os
  3. from pathlib import Path
  4. from typing import Any, Dict
  5. import pytest
  6. import reflex as rx
  7. import reflex.config
  8. from reflex.config import (
  9. EnvVar,
  10. env_var,
  11. environment,
  12. interpret_boolean_env,
  13. interpret_enum_env,
  14. interpret_int_env,
  15. )
  16. from reflex.constants import Endpoint, Env
  17. def test_requires_app_name():
  18. """Test that a config requires an app_name."""
  19. with pytest.raises(ValueError):
  20. rx.Config() # type: ignore
  21. def test_set_app_name(base_config_values):
  22. """Test that the app name is set to the value passed in.
  23. Args:
  24. base_config_values: Config values.
  25. """
  26. config = rx.Config(**base_config_values)
  27. assert config.app_name == base_config_values["app_name"]
  28. @pytest.mark.parametrize(
  29. "env_var, value",
  30. [
  31. ("APP_NAME", "my_test_app"),
  32. ("FRONTEND_PORT", 3001),
  33. ("FRONTEND_PATH", "/test"),
  34. ("BACKEND_PORT", 8001),
  35. ("API_URL", "https://mybackend.com:8000"),
  36. ("DEPLOY_URL", "https://myfrontend.com"),
  37. ("BACKEND_HOST", "127.0.0.1"),
  38. ("DB_URL", "postgresql://user:pass@localhost:5432/db"),
  39. ("REDIS_URL", "redis://localhost:6379"),
  40. ("TIMEOUT", 600),
  41. ("TELEMETRY_ENABLED", False),
  42. ("TELEMETRY_ENABLED", True),
  43. ],
  44. )
  45. def test_update_from_env(
  46. base_config_values: Dict[str, Any],
  47. monkeypatch: pytest.MonkeyPatch,
  48. env_var: str,
  49. value: Any,
  50. ):
  51. """Test that environment variables override config values.
  52. Args:
  53. base_config_values: Config values.
  54. monkeypatch: The pytest monkeypatch object.
  55. env_var: The environment variable name.
  56. value: The environment variable value.
  57. """
  58. monkeypatch.setenv(env_var, str(value))
  59. assert os.environ.get(env_var) == str(value)
  60. config = rx.Config(**base_config_values)
  61. assert getattr(config, env_var.lower()) == value
  62. def test_update_from_env_path(
  63. base_config_values: Dict[str, Any],
  64. monkeypatch: pytest.MonkeyPatch,
  65. tmp_path: Path,
  66. ):
  67. """Test that environment variables override config values.
  68. Args:
  69. base_config_values: Config values.
  70. monkeypatch: The pytest monkeypatch object.
  71. tmp_path: The pytest tmp_path fixture object.
  72. """
  73. monkeypatch.setenv("BUN_PATH", "/test")
  74. assert os.environ.get("BUN_PATH") == "/test"
  75. with pytest.raises(ValueError):
  76. rx.Config(**base_config_values)
  77. monkeypatch.setenv("BUN_PATH", str(tmp_path))
  78. assert os.environ.get("BUN_PATH") == str(tmp_path)
  79. config = rx.Config(**base_config_values)
  80. assert config.bun_path == tmp_path
  81. @pytest.mark.parametrize(
  82. "kwargs, expected",
  83. [
  84. (
  85. {"app_name": "test_app", "api_url": "http://example.com"},
  86. f"{Endpoint.EVENT}",
  87. ),
  88. (
  89. {"app_name": "test_app", "api_url": "http://example.com/api"},
  90. f"/api{Endpoint.EVENT}",
  91. ),
  92. ],
  93. )
  94. def test_event_namespace(mocker, kwargs, expected):
  95. """Test the event namespace.
  96. Args:
  97. mocker: The pytest mock object.
  98. kwargs: The Config kwargs.
  99. expected: Expected namespace
  100. """
  101. conf = rx.Config(**kwargs)
  102. mocker.patch("reflex.config.get_config", return_value=conf)
  103. config = reflex.config.get_config()
  104. assert conf == config
  105. assert config.get_event_namespace() == expected
  106. DEFAULT_CONFIG = rx.Config(app_name="a")
  107. @pytest.mark.parametrize(
  108. ("config_kwargs", "env_vars", "set_persistent_vars", "exp_config_values"),
  109. [
  110. (
  111. {},
  112. {},
  113. {},
  114. {
  115. "api_url": DEFAULT_CONFIG.api_url,
  116. "backend_port": DEFAULT_CONFIG.backend_port,
  117. "deploy_url": DEFAULT_CONFIG.deploy_url,
  118. "frontend_port": DEFAULT_CONFIG.frontend_port,
  119. },
  120. ),
  121. # Ports set in config kwargs
  122. (
  123. {"backend_port": 8001, "frontend_port": 3001},
  124. {},
  125. {},
  126. {
  127. "api_url": "http://localhost:8001",
  128. "backend_port": 8001,
  129. "deploy_url": "http://localhost:3001",
  130. "frontend_port": 3001,
  131. },
  132. ),
  133. # Ports set in environment take precedence
  134. (
  135. {"backend_port": 8001, "frontend_port": 3001},
  136. {"BACKEND_PORT": 8002},
  137. {},
  138. {
  139. "api_url": "http://localhost:8002",
  140. "backend_port": 8002,
  141. "deploy_url": "http://localhost:3001",
  142. "frontend_port": 3001,
  143. },
  144. ),
  145. # Ports set on the command line take precedence
  146. (
  147. {"backend_port": 8001, "frontend_port": 3001},
  148. {"BACKEND_PORT": 8002},
  149. {"frontend_port": "3005"},
  150. {
  151. "api_url": "http://localhost:8002",
  152. "backend_port": 8002,
  153. "deploy_url": "http://localhost:3005",
  154. "frontend_port": 3005,
  155. },
  156. ),
  157. # api_url / deploy_url already set should not be overridden
  158. (
  159. {"api_url": "http://foo.bar:8900", "deploy_url": "http://foo.bar:3001"},
  160. {"BACKEND_PORT": 8002},
  161. {"frontend_port": "3005"},
  162. {
  163. "api_url": "http://foo.bar:8900",
  164. "backend_port": 8002,
  165. "deploy_url": "http://foo.bar:3001",
  166. "frontend_port": 3005,
  167. },
  168. ),
  169. ],
  170. )
  171. def test_replace_defaults(
  172. monkeypatch,
  173. config_kwargs,
  174. env_vars,
  175. set_persistent_vars,
  176. exp_config_values,
  177. ):
  178. """Test that the config replaces defaults with values from the environment.
  179. Args:
  180. monkeypatch: The pytest monkeypatch object.
  181. config_kwargs: The config kwargs.
  182. env_vars: The environment variables.
  183. set_persistent_vars: The values passed to config._set_persistent variables.
  184. exp_config_values: The expected config values.
  185. """
  186. mock_os_env = os.environ.copy()
  187. monkeypatch.setattr(reflex.config.os, "environ", mock_os_env) # type: ignore
  188. mock_os_env.update({k: str(v) for k, v in env_vars.items()})
  189. c = rx.Config(app_name="a", **config_kwargs)
  190. c._set_persistent(**set_persistent_vars)
  191. for key, value in exp_config_values.items():
  192. assert getattr(c, key) == value
  193. def reflex_dir_constant() -> Path:
  194. return environment.REFLEX_DIR.get()
  195. def test_reflex_dir_env_var(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
  196. """Test that the REFLEX_DIR environment variable is used to set the Reflex.DIR constant.
  197. Args:
  198. monkeypatch: The pytest monkeypatch object.
  199. tmp_path: The pytest tmp_path object.
  200. """
  201. monkeypatch.setenv("REFLEX_DIR", str(tmp_path))
  202. mp_ctx = multiprocessing.get_context(method="spawn")
  203. assert reflex_dir_constant() == tmp_path
  204. with mp_ctx.Pool(processes=1) as pool:
  205. assert pool.apply(reflex_dir_constant) == tmp_path
  206. def test_interpret_enum_env() -> None:
  207. assert interpret_enum_env(Env.PROD.value, Env, "REFLEX_ENV") == Env.PROD
  208. def test_interpret_int_env() -> None:
  209. assert interpret_int_env("3001", "FRONTEND_PORT") == 3001
  210. @pytest.mark.parametrize("value, expected", [("true", True), ("false", False)])
  211. def test_interpret_bool_env(value: str, expected: bool) -> None:
  212. assert interpret_boolean_env(value, "TELEMETRY_ENABLED") == expected
  213. def test_env_var():
  214. class TestEnv:
  215. BLUBB: EnvVar[str] = env_var("default")
  216. INTERNAL: EnvVar[str] = env_var("default", internal=True)
  217. BOOLEAN: EnvVar[bool] = env_var(False)
  218. assert TestEnv.BLUBB.get() == "default"
  219. assert TestEnv.BLUBB.name == "BLUBB"
  220. TestEnv.BLUBB.set("new")
  221. assert os.environ.get("BLUBB") == "new"
  222. assert TestEnv.BLUBB.get() == "new"
  223. TestEnv.BLUBB.set(None)
  224. assert "BLUBB" not in os.environ
  225. assert TestEnv.INTERNAL.get() == "default"
  226. assert TestEnv.INTERNAL.name == "__INTERNAL"
  227. TestEnv.INTERNAL.set("new")
  228. assert os.environ.get("__INTERNAL") == "new"
  229. assert TestEnv.INTERNAL.get() == "new"
  230. assert TestEnv.INTERNAL.getenv() == "new"
  231. TestEnv.INTERNAL.set(None)
  232. assert "__INTERNAL" not in os.environ
  233. assert TestEnv.BOOLEAN.get() is False
  234. assert TestEnv.BOOLEAN.name == "BOOLEAN"
  235. TestEnv.BOOLEAN.set(True)
  236. assert os.environ.get("BOOLEAN") == "True"
  237. assert TestEnv.BOOLEAN.get() is True
  238. TestEnv.BOOLEAN.set(False)
  239. assert os.environ.get("BOOLEAN") == "False"
  240. assert TestEnv.BOOLEAN.get() is False
  241. TestEnv.BOOLEAN.set(None)
  242. assert "BOOLEAN" not in os.environ