test_prerequisites.py 6.5 KB

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