diff --git a/poetry.lock b/poetry.lock index df1271cfd..671da3654 100644 --- a/poetry.lock +++ b/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" diff --git a/pynecone/base.py b/pynecone/base.py index 750cd6c4f..2370b5aec 100644 --- a/pynecone/base.py +++ b/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. diff --git a/pyproject.toml b/pyproject.toml index c2b3e2ade..fb0d81342 100644 --- a/pyproject.toml +++ b/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] diff --git a/tests/conftest.py b/tests/conftest.py index d1d2002e3..6e13ed6b1 100644 --- a/tests/conftest.py +++ b/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 diff --git a/tests/test_app.py b/tests/test_app.py index 93a7f3590..15cbce39a 100644 --- a/tests/test_app.py +++ b/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 diff --git a/tests/test_state.py b/tests/test_state.py index f6c022226..667a00c36 100644 --- a/tests/test_state.py +++ b/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