예제 #1
0
def test_websocket_process_detail_with_abort(test_client, test_workflow):
    if websocket_manager.broadcaster_type != "memory":
        pytest.skip("test does not work with redis")

    response = test_client.post(f"/api/processes/{test_workflow}", json=[{}])

    assert (
        HTTPStatus.CREATED == response.status_code
    ), f"Invalid response status code (response data: {response.json()})"

    pid = response.json()["id"]

    try:
        with test_client.websocket_connect(
                "api/processes/all?token=") as websocket:
            # Abort process
            response = test_client.put(f"/api/processes/{pid}/abort")
            assert HTTPStatus.NO_CONTENT == response.status_code

            data = websocket.receive_text()
            process = json_loads(data)["process"]
            assert process["status"] == ProcessStatus.SUSPENDED
            assert process["assignee"] == Assignee.CHANGES

            data = websocket.receive_text()
            process = json_loads(data)["process"]
            assert process["status"] == ProcessStatus.ABORTED
            assert process["assignee"] == Assignee.SYSTEM

            # close and call receive_text to check websocket close exception
            websocket.close()
            data = websocket.receive_text()
    except WebSocketDisconnect as exception:
        assert exception.code == status.WS_1000_NORMAL_CLOSURE
예제 #2
0
def test_deserialization_datetime():
    json_str = '{"end_date": "2019-12-06T19:25:22+00:00"}'
    dct = json_loads(json_str)
    assert "end_date" in dct
    assert dct["end_date"] == datetime(2019, 12, 6, 19, 25, 22, 0,
                                       timezone.utc)

    dct = {"end_date": datetime(2019, 12, 6, 19, 25, 22, 0, timezone.utc)}
    assert json_loads(json_dumps(dct)) == dct
예제 #3
0
def test_subscription_detail_with_domain_model_cache(test_client,
                                                     generic_subscription_1):
    # test with a subscription that has domain model and without
    subscription = SubscriptionModel.from_subscription(generic_subscription_1)
    extended_model = build_extendend_domain_model(subscription)
    etag = _generate_etag(extended_model)

    app_settings.CACHE_DOMAIN_MODELS = True

    to_redis(extended_model)

    response = test_client.get(
        URL("api/subscriptions/domain-model") / generic_subscription_1)

    cache = Redis(host=app_settings.CACHE_HOST, port=app_settings.CACHE_PORT)
    result = cache.get(f"domain:{generic_subscription_1}")
    cached_model = json_dumps(json_loads(result))
    cached_etag = cache.get(f"domain:etag:{generic_subscription_1}")
    assert cached_model == json_dumps(extended_model)
    assert cached_etag.decode("utf-8") == etag

    assert response.status_code == HTTPStatus.OK
    assert response.json()["subscription_id"] == generic_subscription_1
    app_settings.CACHE_DOMAIN_MODELS = False
    cache.delete(f"domain:{generic_subscription_1}")
def test_accept_ok():
    class Form(FormPage):
        accept: Accept

    validated_data = Form(accept="ACCEPTED").dict()

    expected = {"accept": True}
    assert expected == json_loads(json_dumps(validated_data))
예제 #5
0
    async def sender(self, websocket: WebSocket, channel: str) -> None:
        async with self.sub_broadcast.subscribe(channel=channel) as subscriber:
            async for event in subscriber:
                await websocket.send_text(event.message)

                json = json_loads(event.message)
                if type(json) is dict and "close" in json and json["close"] and channel != "processes":
                    await self.disconnect(websocket)
                    break
예제 #6
0
def test_post_process_yield():
    def input_form(state):

        user_input = yield TestForm
        return {**user_input.dict(), "extra": 234}

    validated_data = post_process(input_form, {"previous": True},
                                  [{
                                      "generic_select": "a"
                                  }])

    expected = {"generic_select": "a", "extra": 234}
    assert expected == json_loads(json_dumps(validated_data))
예제 #7
0
async def form_error_handler(request: Request,
                             exc: FormException) -> JSONResponse:
    if isinstance(exc, FormValidationError):
        return JSONResponse(
            {
                "type": type(exc).__name__,
                "detail": str(exc),
                "traceback": show_ex(exc),
                "title": "Form not valid",
                # We need to make sure the is nothing the default json.dumps cannot handle
                "validation_errors": json_loads(json_dumps(exc.errors)),
                "status": HTTPStatus.BAD_REQUEST,
            },
            status_code=HTTPStatus.BAD_REQUEST,
        )
    elif isinstance(exc, FormNotCompleteError):
        return JSONResponse(
            {
                "type": type(exc).__name__,
                "detail": str(exc),
                "traceback": show_ex(exc),
                # We need to make sure the is nothing the default json.dumps cannot handle
                "form": json_loads(json_dumps(exc.form)),
                "title": "Form not complete",
                "status": HTTPStatus.NOT_EXTENDED,
            },
            status_code=HTTPStatus.NOT_EXTENDED,
        )
    else:
        return JSONResponse(
            {
                "detail": str(exc),
                "title": "Internal Server Error",
                "status": HTTPStatus.INTERNAL_SERVER_ERROR,
                "type": type(exc).__name__,
            },
            status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
        )
예제 #8
0
def from_redis(subscription_id: UUID) -> Optional[Tuple[Any, str]]:
    if caching_models_enabled():
        logger.info("Retrieving subscription from cache",
                    subscription=subscription_id)
        obj = cache.get(f"domain:{subscription_id}")
        etag = cache.get(f"domain:etag:{subscription_id}")
        if obj and etag:
            return json_loads(obj), etag.decode("utf-8")  # type: ignore
        else:
            return None
    else:
        logger.warning("Caching disabled, not loading subscription",
                       subscription=subscription_id)
        return None
예제 #9
0
def log_user_info(user_name: str, message: Dict = Body(...)) -> Dict:
    """Log frontend messages that are related to user actions.

    When the frontend finalizes the setup of a login session it will do a HTTP POST to this endpoint. The frontend
    will also post to this endpoint when it ends a user session.

    Args:
        user_name: the username (email) of the user involved
        message: A log message.

    Returns:
        {}

    """
    try:
        json_dict: Any = json_loads(str(message))
        _message = json_dict["message"]
    except Exception:
        _message = message
    logger.info("Client sent user info", message=_message, user_name=user_name)
    return {}
예제 #10
0
def test_websocket_process_list_multiple_workflows(test_client, test_workflow,
                                                   test_workflow_2):
    # This tests the ProcessDataBroadcastThread as well
    if websocket_manager.broadcaster_type != "memory":
        pytest.skip("test does not work with redis")

    # to keep track of the amount of websocket messages
    message_count = 0

    expected_workflow_1_steps = [
        {
            "assignee": Assignee.SYSTEM,
            "status": ProcessStatus.RUNNING,
            "step": "Start",
            "step_status": StepStatus.SUCCESS,
        },
        {
            "assignee": Assignee.SYSTEM,
            "status": ProcessStatus.RUNNING,
            "step": "Insert UUID in state",
            "step_status": StepStatus.SUCCESS,
        },
        {
            "assignee": Assignee.SYSTEM,
            "status": ProcessStatus.RUNNING,
            "step": "Test that it is a string now",
            "step_status": StepStatus.SUCCESS,
        },
        {
            "assignee": Assignee.CHANGES,
            "status": ProcessStatus.SUSPENDED,
            "step": "Modify",
            "step_status": StepStatus.SUSPEND,
        },
    ]

    expected_workflow_2_steps = [
        {
            "assignee": Assignee.SYSTEM,
            "status": ProcessStatus.RUNNING,
            "step": "Start",
            "step_status": StepStatus.SUCCESS,
        },
        {
            "assignee": Assignee.SYSTEM,
            "status": ProcessStatus.RUNNING,
            "step": "Insert UUID in state",
            "step_status": StepStatus.SUCCESS,
        },
        {
            "assignee": Assignee.SYSTEM,
            "status": ProcessStatus.RUNNING,
            "step": "Test that it is a string now",
            "step_status": StepStatus.SUCCESS,
        },
        {
            "assignee": Assignee.SYSTEM,
            "status": ProcessStatus.RUNNING,
            "step": "immediate step",
            "step_status": StepStatus.SUCCESS,
        },
        {
            "assignee": Assignee.SYSTEM,
            "status": ProcessStatus.COMPLETED,
            "step": "Done",
            "step_status": StepStatus.COMPLETE,
        },
    ]

    test_workflow_1_messages = []
    test_workflow_2_messages = []

    try:
        with test_client.websocket_connect(
                "api/processes/all/?token=") as websocket:
            # start test_workflow
            test_workflow_response = test_client.post(
                f"/api/processes/{test_workflow}", json=[{}])

            assert (
                HTTPStatus.CREATED == test_workflow_response.status_code
            ), f"Invalid response status code (response data: {test_workflow_response.json()})"

            test_workflow_1_pid = test_workflow_response.json()["id"]

            # Start test_workflow_2
            response = test_client.post(f"/api/processes/{test_workflow_2}",
                                        json=[{}])
            assert (
                HTTPStatus.CREATED == response.status_code
            ), f"Invalid response status code (response data: {response.json()})"

            test_workflow_2_pid = response.json()["id"]

            # Make sure it started again
            time.sleep(1)

            # close and call receive_text to check websocket close exception
            websocket.close()

            # Checks if the correct messages are send, without order for which workflow.
            while True:
                if message_count == 9:
                    break
                message = websocket.receive_text()
                message_count += 1
                json_data = json_loads(message)
                assert "process" in json_data, f"Websocket message does not contain process: {json_data}"
                process_id = json_data["process"]["id"]

                if process_id == test_workflow_1_pid:
                    test_workflow_1_messages.append(json_data)
                elif process_id == test_workflow_2_pid:
                    test_workflow_2_messages.append(json_data)
    except WebSocketDisconnect as exception:
        assert exception.code == status.WS_1000_NORMAL_CLOSURE
    except AssertionError as e:
        raise e

    assert message_count == 9

    for index, message in enumerate(test_workflow_1_messages):
        process = message["process"]
        expectedData = expected_workflow_1_steps.pop(0)

        assert process["assignee"] == expectedData["assignee"]
        assert process["status"] == expectedData["status"]

        assert process["step"] == expectedData["step"]
        assert process["steps"][index]["name"] == expectedData["step"]
        assert process["steps"][index]["status"] == expectedData["step_status"]

    for index, message in enumerate(test_workflow_2_messages):
        process = message["process"]
        expectedData = expected_workflow_2_steps.pop(0)

        assert process["assignee"] == expectedData["assignee"]
        assert process["status"] == expectedData["status"]

        assert process["step"] == expectedData["step"]
        assert process["steps"][index]["name"] == expectedData["step"]
        assert process["steps"][index]["status"] == expectedData["step_status"]
예제 #11
0
def test_websocket_process_detail_workflow(test_client, long_running_workflow):
    if websocket_manager.broadcaster_type != "memory" or app_settings.ENVIRONMENT != "local":
        pytest.skip("test does not work with redis")

    app_settings.TESTING = False

    # Start the workflow
    response = test_client.post(f"/api/processes/{long_running_workflow}",
                                json=[{}])
    assert (
        HTTPStatus.CREATED == response.status_code
    ), f"Invalid response status code (response data: {response.json()})"

    pid = response.json()["id"]

    response = test_client.get(f"api/processes/{pid}")
    assert HTTPStatus.OK == response.status_code

    # Make sure it started again
    time.sleep(1)

    try:
        with test_client.websocket_connect(
                "api/processes/all?token=") as websocket:
            # Check the websocket messages.
            # the initial process details.
            data = websocket.receive_text()
            process = json_loads(data)["process"]
            assert process["workflow_name"] == "long_running_workflow_py"
            assert process["status"] == ProcessStatus.RUNNING

            # Let first long step finish, receive_text would otherwise wait for a message indefinitely.
            with test_condition:
                test_condition.notify_all()
            time.sleep(1)

            # message step 1.
            data = websocket.receive_text()
            json_data = json_loads(data)
            process = json_data["process"]
            assert process["status"] == ProcessStatus.RUNNING
            assert process["steps"][1]["name"] == LONG_RUNNING_STEP
            assert process["steps"][1]["status"] == StepStatus.SUCCESS

            # message step 2.
            data = websocket.receive_text()
            json_data = json_loads(data)
            process = json_data["process"]
            assert process["status"] == ProcessStatus.RUNNING
            assert process["steps"][2]["name"] == IMMEDIATE_STEP
            assert process["steps"][2]["status"] == StepStatus.SUCCESS

            # Let second long step finish, receive_text would otherwise wait for a message indefinitely.
            with test_condition:
                test_condition.notify_all()
            time.sleep(1)

            # message step 3.
            data = websocket.receive_text()
            json_data = json_loads(data)
            process = json_data["process"]
            assert process["status"] == ProcessStatus.RUNNING
            assert process["steps"][3]["name"] == LONG_RUNNING_STEP
            assert process["steps"][3]["status"] == StepStatus.SUCCESS

            # message step 4.
            data = websocket.receive_text()
            json_data = json_loads(data)
            process = json_data["process"]
            assert process["status"] == ProcessStatus.COMPLETED
            assert process["steps"][4]["name"] == "Done"
            assert process["steps"][4]["status"] == StepStatus.COMPLETE
            assert json_data["close"] is True

            # close and call receive_text to check websocket close exception
            websocket.close()
            data = websocket.receive_text()
    except WebSocketDisconnect as exception:
        assert exception.code == status.WS_1000_NORMAL_CLOSURE
    except AssertionError as e:
        # Finish steps so the test doesn't get stuck waiting forever.
        with test_condition:
            test_condition.notify_all()
        with test_condition:
            test_condition.notify_all()
        app_settings.TESTING = True
        raise e

    with test_condition:
        test_condition.notify_all()
    app_settings.TESTING = True
예제 #12
0
def test_post_process_wizard():
    # Return if there is no form
    assert post_process(None, {}, []) == {}
    assert post_process([], {}, []) == {}

    def input_form(state):
        class TestForm1(FormPage):
            generic_select1: TestChoices

            class Config:
                title = "Some title"

        class TestForm2(FormPage):
            generic_select2: TestChoices

        class TestForm3(FormPage):
            generic_select3: TestChoices

        user_input_1 = yield TestForm1

        if user_input_1.generic_select1 == TestChoices.A:
            user_input_2 = yield TestForm2
        else:
            user_input_2 = yield TestForm3

        return {**user_input_1.dict(), **user_input_2.dict()}

    # Submit 1
    with pytest.raises(FormNotCompleteError) as error_info:
        post_process(input_form, {"previous": True}, [])

    assert error_info.value.form == {
        "title": "Some title",
        "type": "object",
        "additionalProperties": False,
        "definitions": {
            "TestChoices": {
                "description": "An enumeration.",
                "enum": ["a", "b"],
                "title": "TestChoices",
                "type": "string",
            }
        },
        "properties": {
            "generic_select1": {
                "$ref": "#/definitions/TestChoices"
            }
        },
        "required": ["generic_select1"],
    }

    # Submit 2
    with pytest.raises(FormNotCompleteError) as error_info:
        post_process(input_form, {"previous": True}, [{
            "generic_select1": "b"
        }])

    assert error_info.value.form == {
        "title": "unknown",
        "type": "object",
        "additionalProperties": False,
        "definitions": {
            "TestChoices": {
                "description": "An enumeration.",
                "enum": ["a", "b"],
                "title": "TestChoices",
                "type": "string",
            }
        },
        "properties": {
            "generic_select3": {
                "$ref": "#/definitions/TestChoices"
            }
        },
        "required": ["generic_select3"],
    }

    # Submit complete
    validated_data = post_process(input_form, {"previous": True},
                                  [{
                                      "generic_select1": "b"
                                  }, {
                                      "generic_select3": "a"
                                  }])

    expected = {"generic_select1": "b", "generic_select3": "a"}
    assert expected == json_loads(json_dumps(validated_data))

    # Submit overcomplete
    validated_data = post_process(input_form, {"previous": True},
                                  [{
                                      "generic_select1": "b"
                                  }, {
                                      "generic_select3": "a"
                                  }, {
                                      "to_much": True
                                  }])

    expected = {"generic_select1": "b", "generic_select3": "a"}
    assert expected == json_loads(json_dumps(validated_data))