test_hydrate_middleware.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. from typing import Any, Dict
  2. import pytest
  3. from pynecone.app import App
  4. from pynecone.constants import IS_HYDRATED
  5. from pynecone.middleware.hydrate_middleware import HydrateMiddleware
  6. from pynecone.state import State, StateUpdate
  7. def exp_is_hydrated(state: State) -> Dict[str, Any]:
  8. """Expected IS_HYDRATED delta that would be emitted by HydrateMiddleware.
  9. Args:
  10. state: the State that is hydrated
  11. Returns:
  12. dict similar to that returned by `State.get_delta` with IS_HYDRATED: True
  13. """
  14. return {state.get_name(): {IS_HYDRATED: "true"}}
  15. class TestState(State):
  16. """A test state with no return in handler."""
  17. __test__ = False
  18. num: int = 0
  19. def test_handler(self):
  20. """Test handler."""
  21. self.num += 1
  22. class TestState2(State):
  23. """A test state with return in handler."""
  24. __test__ = False
  25. num: int = 0
  26. name: str
  27. def test_handler(self):
  28. """Test handler that calls another handler.
  29. Returns:
  30. Chain of EventHandlers
  31. """
  32. self.num += 1
  33. return self.change_name
  34. def change_name(self):
  35. """Test handler to change name."""
  36. self.name = "random"
  37. class TestState3(State):
  38. """A test state with async handler."""
  39. __test__ = False
  40. num: int = 0
  41. async def test_handler(self):
  42. """Test handler."""
  43. self.num += 1
  44. @pytest.fixture
  45. def hydrate_middleware() -> HydrateMiddleware:
  46. """Fixture creates an instance of HydrateMiddleware per test case.
  47. Returns:
  48. instance of HydrateMiddleware
  49. """
  50. return HydrateMiddleware()
  51. @pytest.mark.asyncio
  52. @pytest.mark.parametrize(
  53. "State, expected, event_fixture",
  54. [
  55. (TestState, {"test_state": {"num": 1}}, "event1"),
  56. (TestState2, {"test_state2": {"num": 1}}, "event2"),
  57. (TestState3, {"test_state3": {"num": 1}}, "event3"),
  58. ],
  59. )
  60. async def test_preprocess(State, hydrate_middleware, request, event_fixture, expected):
  61. """Test that a state hydrate event is processed correctly.
  62. Args:
  63. State: state to process event
  64. hydrate_middleware: instance of HydrateMiddleware
  65. request: pytest fixture request
  66. event_fixture: The event fixture(an Event)
  67. expected: expected delta
  68. """
  69. app = App(state=State, load_events={"index": [State.test_handler]})
  70. state = State()
  71. update = await hydrate_middleware.preprocess(
  72. app=app, event=request.getfixturevalue(event_fixture), state=state
  73. )
  74. assert isinstance(update, StateUpdate)
  75. assert update.delta == {state.get_name(): state.dict()}
  76. events = update.events
  77. assert len(events) == 2
  78. # Apply the on_load event.
  79. update = await state._process(events[0]).__anext__()
  80. assert update.delta == expected
  81. # Apply the hydrate event.
  82. update = await state._process(events[1]).__anext__()
  83. assert update.delta == exp_is_hydrated(state)
  84. @pytest.mark.asyncio
  85. async def test_preprocess_multiple_load_events(hydrate_middleware, event1):
  86. """Test that a state hydrate event for multiple on-load events is processed correctly.
  87. Args:
  88. hydrate_middleware: instance of HydrateMiddleware
  89. event1: an Event.
  90. """
  91. app = App(
  92. state=TestState,
  93. load_events={"index": [TestState.test_handler, TestState.test_handler]},
  94. )
  95. state = TestState()
  96. update = await hydrate_middleware.preprocess(app=app, event=event1, state=state)
  97. assert isinstance(update, StateUpdate)
  98. assert update.delta == {"test_state": state.dict()}
  99. assert len(update.events) == 3
  100. # Apply the events.
  101. events = update.events
  102. update = await state._process(events[0]).__anext__()
  103. assert update.delta == {"test_state": {"num": 1}}
  104. update = await state._process(events[1]).__anext__()
  105. assert update.delta == {"test_state": {"num": 2}}
  106. update = await state._process(events[2]).__anext__()
  107. assert update.delta == exp_is_hydrated(state)
  108. @pytest.mark.asyncio
  109. async def test_preprocess_no_events(hydrate_middleware, event1):
  110. """Test that app without on_load is processed correctly.
  111. Args:
  112. hydrate_middleware: instance of HydrateMiddleware
  113. event1: an Event.
  114. """
  115. state = TestState()
  116. update = await hydrate_middleware.preprocess(
  117. app=App(state=TestState),
  118. event=event1,
  119. state=state,
  120. )
  121. assert isinstance(update, StateUpdate)
  122. assert update.delta == {"test_state": state.dict()}
  123. assert len(update.events) == 1
  124. assert isinstance(update, StateUpdate)
  125. update = await state._process(update.events[0]).__anext__()
  126. assert update.delta == exp_is_hydrated(state)