Pārlūkot izejas kodu

async tests to test yield functionality (#1163)

Elijah Ahianyo 1 gadu atpakaļ
vecāks
revīzija
278222db8f
6 mainītis faili ar 85 papildinājumiem un 30 dzēšanām
  1. 13 1
      poetry.lock
  2. 1 0
      pynecone/base.py
  3. 1 0
      pyproject.toml
  4. 29 0
      tests/conftest.py
  5. 37 0
      tests/test_app.py
  6. 4 29
      tests/test_state.py

+ 13 - 1
poetry.lock

@@ -38,6 +38,18 @@ files = [
 [package.dependencies]
 typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
 
+[[package]]
+name = "asynctest"
+version = "0.13.0"
+description = "Enhance the standard unittest package with features for testing asyncio libraries"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+files = [
+    {file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"},
+    {file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"},
+]
+
 [[package]]
 name = "bidict"
 version = "0.22.1"
@@ -1835,4 +1847,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.7"
-content-hash = "8d9847174be44200843831d75f0f58e35a47619a11384606df14a7d64cee003f"
+content-hash = "9875d81be51d7b3c6686cc56c4b6d58069cb4d5df965742811b874a18e522106"

+ 1 - 0
pynecone/base.py

@@ -25,6 +25,7 @@ class Base(pydantic.BaseModel):
 
         arbitrary_types_allowed = True
         use_enum_values = True
+        extra = "allow"
 
     def json(self) -> str:
         """Convert the object to a json string.

+ 1 - 0
pyproject.toml

@@ -58,6 +58,7 @@ pandas = [
     {version = "^1.5.3", python = ">=3.8,<4.0"},
     {version = "^1.1", python = ">=3.7, <3.8"}
 ]
+asynctest = "^0.13.0"
 pre-commit = {version = "^3.2.1", python = ">=3.8,<4.0"}
 
 [tool.poetry.scripts]

+ 29 - 0
tests/conftest.py

@@ -403,3 +403,32 @@ def sqlite_db_config_values(base_db_config_values) -> Dict:
     """
     base_db_config_values["engine"] = "sqlite"
     return base_db_config_values
+
+
+class GenState(pc.State):
+    """A state with event handlers that generate multiple updates."""
+
+    value: int
+
+    def go(self, c: int):
+        """Increment the value c times and update each time.
+
+        Args:
+            c: The number of times to increment.
+
+        Yields:
+            After each increment.
+        """
+        for _ in range(c):
+            self.value += 1
+            yield
+
+
+@pytest.fixture
+def gen_state() -> GenState:
+    """A state.
+
+    Returns:
+        A test state.
+    """
+    return GenState  # type: ignore

+ 37 - 0
tests/test_app.py

@@ -1,7 +1,13 @@
 import io
 import os.path
+import sys
 from typing import List, Tuple, Type
 
+if sys.version_info.major >= 3 and sys.version_info.minor > 7:
+    from unittest.mock import AsyncMock  # type: ignore
+else:
+    # python 3.7 doesn't ship with unittest.mock
+    from asynctest import CoroutineMock as AsyncMock
 import pytest
 import sqlmodel
 from fastapi import UploadFile
@@ -681,6 +687,7 @@ class DynamicState(State):
 
     loaded: int = 0
     counter: int = 0
+
     # side_effect_counter: int = 0
 
     def on_load(self):
@@ -849,3 +856,33 @@ async def test_dynamic_route_var_route_change_completed_on_load(
     assert state.counter == len(exp_vals)
     # print(f"Expected {exp_vals} rendering side effects, got {state.side_effect_counter}")
     # assert state.side_effect_counter == len(exp_vals)
+
+
+@pytest.mark.asyncio
+async def test_process_events(gen_state, mocker):
+    """Test that an event is processed properly and that it is postprocessed
+    n+1 times. Also check that the processing flag of the last stateupdate is set to
+    False.
+
+    Args:
+        gen_state: The state.
+        mocker: mocker object.
+    """
+    router_data = {
+        "pathname": "/",
+        "query": {},
+        "token": "mock_token",
+        "sid": "mock_sid",
+        "headers": {},
+        "ip": "127.0.0.1",
+    }
+    app = App(state=gen_state)
+    mocker.patch.object(app, "postprocess", AsyncMock())
+    event = Event(
+        token="token", name="gen_state.go", payload={"c": 5}, router_data=router_data
+    )
+
+    async for _update in process(app, event, "mock_sid", {}, "127.0.0.1"):
+        pass
+    assert gen_state.value == 5
+    assert app.postprocess.call_count == 6

+ 4 - 29
tests/test_state.py

@@ -91,25 +91,6 @@ class GrandchildState(ChildState):
         pass
 
 
-class GenState(State):
-    """A state with event handlers that generate multiple updates."""
-
-    value: int
-
-    def go(self, c: int):
-        """Increment the value c times and update each time.
-
-        Args:
-            c: The number of times to increment.
-
-        Yields:
-            After each increment.
-        """
-        for _ in range(c):
-            self.value += 1
-            yield
-
-
 @pytest.fixture
 def test_state() -> TestState:
     """A state.
@@ -165,16 +146,6 @@ def grandchild_state(child_state) -> GrandchildState:
     return grandchild_state
 
 
-@pytest.fixture
-def gen_state() -> GenState:
-    """A state.
-
-    Returns:
-        A test state.
-    """
-    return GenState()  # type: ignore
-
-
 def test_base_class_vars(test_state):
     """Test that the class vars are set correctly.
 
@@ -663,6 +634,7 @@ async def test_process_event_generator(gen_state):
     Args:
         gen_state: A state.
     """
+    gen_state = gen_state()
     event = Event(
         token="t",
         name="go",
@@ -675,11 +647,14 @@ async def test_process_event_generator(gen_state):
         count += 1
         if count == 6:
             assert update.delta == {}
+            assert not update.processing
         else:
+
             assert gen_state.value == count
             assert update.delta == {
                 "gen_state": {"value": count},
             }
+            assert update.processing
 
     assert count == 6