test_prerequisites.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import json
  2. import re
  3. import tempfile
  4. from unittest.mock import Mock, mock_open
  5. import pytest
  6. from reflex import constants
  7. from reflex.config import Config
  8. from reflex.utils.prerequisites import (
  9. CpuInfo,
  10. _update_next_config,
  11. cached_procedure,
  12. get_cpu_info,
  13. initialize_requirements_txt,
  14. )
  15. @pytest.mark.parametrize(
  16. "config, export, expected_output",
  17. [
  18. (
  19. Config(
  20. app_name="test",
  21. ),
  22. False,
  23. 'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true};',
  24. ),
  25. (
  26. Config(
  27. app_name="test",
  28. next_compression=False,
  29. ),
  30. False,
  31. 'module.exports = {basePath: "", compress: false, reactStrictMode: true, trailingSlash: true};',
  32. ),
  33. (
  34. Config(
  35. app_name="test",
  36. frontend_path="/test",
  37. ),
  38. False,
  39. 'module.exports = {basePath: "/test", compress: true, reactStrictMode: true, trailingSlash: true};',
  40. ),
  41. (
  42. Config(
  43. app_name="test",
  44. frontend_path="/test",
  45. next_compression=False,
  46. ),
  47. False,
  48. 'module.exports = {basePath: "/test", compress: false, reactStrictMode: true, trailingSlash: true};',
  49. ),
  50. (
  51. Config(
  52. app_name="test",
  53. ),
  54. True,
  55. 'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, output: "export", distDir: "_static"};',
  56. ),
  57. ],
  58. )
  59. def test_update_next_config(config, export, expected_output):
  60. output = _update_next_config(config, export=export)
  61. assert output == expected_output
  62. @pytest.mark.parametrize(
  63. ("transpile_packages", "expected_transpile_packages"),
  64. (
  65. (
  66. ["foo", "@bar/baz"],
  67. ["@bar/baz", "foo"],
  68. ),
  69. (
  70. ["foo", "@bar/baz", "foo", "@bar/baz@3.2.1"],
  71. ["@bar/baz", "foo"],
  72. ),
  73. ),
  74. )
  75. def test_transpile_packages(transpile_packages, expected_transpile_packages):
  76. output = _update_next_config(
  77. Config(app_name="test"),
  78. transpile_packages=transpile_packages,
  79. )
  80. transpile_packages_match = re.search(r"transpilePackages: (\[.*?\])", output)
  81. transpile_packages_json = transpile_packages_match.group(1) # type: ignore
  82. actual_transpile_packages = sorted(json.loads(transpile_packages_json))
  83. assert actual_transpile_packages == expected_transpile_packages
  84. def test_initialize_requirements_txt_no_op(mocker):
  85. # File exists, reflex is included, do nothing
  86. mocker.patch("pathlib.Path.exists", return_value=True)
  87. mocker.patch(
  88. "charset_normalizer.from_path",
  89. return_value=Mock(best=lambda: Mock(encoding="utf-8")),
  90. )
  91. mock_fp_touch = mocker.patch("pathlib.Path.touch")
  92. open_mock = mock_open(read_data="reflex==0.2.9")
  93. mocker.patch("builtins.open", open_mock)
  94. initialize_requirements_txt()
  95. assert open_mock.call_count == 1
  96. assert open_mock.call_args.kwargs["encoding"] == "utf-8"
  97. assert open_mock().write.call_count == 0
  98. mock_fp_touch.assert_not_called()
  99. def test_initialize_requirements_txt_missing_reflex(mocker):
  100. # File exists, reflex is not included, add reflex
  101. mocker.patch("pathlib.Path.exists", return_value=True)
  102. mocker.patch(
  103. "charset_normalizer.from_path",
  104. return_value=Mock(best=lambda: Mock(encoding="utf-8")),
  105. )
  106. open_mock = mock_open(read_data="random-package=1.2.3")
  107. mocker.patch("builtins.open", open_mock)
  108. initialize_requirements_txt()
  109. # Currently open for read, then open for append
  110. assert open_mock.call_count == 2
  111. for call_args in open_mock.call_args_list:
  112. assert call_args.kwargs["encoding"] == "utf-8"
  113. assert (
  114. open_mock().write.call_args[0][0]
  115. == f"\n{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
  116. )
  117. def test_initialize_requirements_txt_not_exist(mocker):
  118. # File does not exist, create file with reflex
  119. mocker.patch("pathlib.Path.exists", return_value=False)
  120. open_mock = mock_open()
  121. mocker.patch("builtins.open", open_mock)
  122. initialize_requirements_txt()
  123. assert open_mock.call_count == 2
  124. # By default, use utf-8 encoding
  125. for call_args in open_mock.call_args_list:
  126. assert call_args.kwargs["encoding"] == "utf-8"
  127. assert open_mock().write.call_count == 1
  128. assert (
  129. open_mock().write.call_args[0][0]
  130. == f"{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
  131. )
  132. def test_requirements_txt_cannot_detect_encoding(mocker):
  133. mocker.patch("pathlib.Path.exists", return_value=True)
  134. mock_open = mocker.patch("builtins.open")
  135. mocker.patch(
  136. "charset_normalizer.from_path",
  137. return_value=Mock(best=lambda: None),
  138. )
  139. initialize_requirements_txt()
  140. mock_open.assert_not_called()
  141. def test_requirements_txt_other_encoding(mocker):
  142. mocker.patch("pathlib.Path.exists", return_value=True)
  143. mocker.patch(
  144. "charset_normalizer.from_path",
  145. return_value=Mock(best=lambda: Mock(encoding="utf-16")),
  146. )
  147. initialize_requirements_txt()
  148. open_mock = mock_open(read_data="random-package=1.2.3")
  149. mocker.patch("builtins.open", open_mock)
  150. initialize_requirements_txt()
  151. # Currently open for read, then open for append
  152. assert open_mock.call_count == 2
  153. for call_args in open_mock.call_args_list:
  154. assert call_args.kwargs["encoding"] == "utf-16"
  155. assert (
  156. open_mock().write.call_args[0][0]
  157. == f"\n{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
  158. )
  159. def test_cached_procedure():
  160. call_count = 0
  161. @cached_procedure(tempfile.mktemp(), payload_fn=lambda: "constant")
  162. def _function_with_no_args():
  163. nonlocal call_count
  164. call_count += 1
  165. _function_with_no_args()
  166. assert call_count == 1
  167. _function_with_no_args()
  168. assert call_count == 1
  169. call_count = 0
  170. @cached_procedure(
  171. tempfile.mktemp(),
  172. payload_fn=lambda *args, **kwargs: f"{repr(args), repr(kwargs)}",
  173. )
  174. def _function_with_some_args(*args, **kwargs):
  175. nonlocal call_count
  176. call_count += 1
  177. _function_with_some_args(1, y=2)
  178. assert call_count == 1
  179. _function_with_some_args(1, y=2)
  180. assert call_count == 1
  181. _function_with_some_args(100, y=300)
  182. assert call_count == 2
  183. _function_with_some_args(100, y=300)
  184. assert call_count == 2
  185. def test_get_cpu_info():
  186. cpu_info = get_cpu_info()
  187. assert cpu_info is not None
  188. assert isinstance(cpu_info, CpuInfo)
  189. assert cpu_info.model_name is not None
  190. for attr in ("manufacturer_id", "model_name", "address_width"):
  191. value = getattr(cpu_info, attr)
  192. assert value.strip() if attr != "address_width" else value