From bec80e0505ed2b9d969c7872c5f3fec478e3e4d0 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 20 Mar 2024 12:02:10 +0000 Subject: [PATCH] pydantic and json load error --- src/blueapi/cli/cli.py | 2 +- tests/test_cli.py | 122 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 108 insertions(+), 16 deletions(-) diff --git a/src/blueapi/cli/cli.py b/src/blueapi/cli/cli.py index 6a331605e..0a8216ab9 100644 --- a/src/blueapi/cli/cli.py +++ b/src/blueapi/cli/cli.py @@ -182,7 +182,7 @@ def store_finished_event(event: WorkerEvent) -> None: if event.is_complete(): finished_event.append(event) - parameters = parameters or "{}" + parameters = json.loads(parameters) or "{}" schema: PlanModel = client.get_plan(name) progress_tracking = f"Trying to run plan: {name}." print(progress_tracking) diff --git a/tests/test_cli.py b/tests/test_cli.py index a8d0443c9..3b72ad9bc 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -149,33 +149,62 @@ def test_config_passed_down_to_command_children( ): config_path = "tests/example_yaml/rest_config.yaml" + mock_handler.side_effect = Mock(return_value=handler) with patch("uvicorn.run", side_effect=None): result = runner.invoke(main, ["-c", config_path, "serve"]) assert result.exit_code == 0 - mock_requests.return_value = client.get("/plans/sleep") - output = runner.invoke( - main, ["-c", config_path, "controller", "run", "sleep", '{"time": 5}'] - ) assert result.exit_code == 0 - print(output) - # 'Trying to run plan: sleep.\nChecking supplied parameters against expected parameters...\nInput validation failed: __root__: value is not a valid dict\nfailed to run the sleep plan, supplied params {"time": 5}\n do not match the expected params: {\'time\': {\'title\': \'Time\', \'type\': \'number\'}}\n' - # expect the first call to be to the helper + # Put a plan in handler.context manually. + plan = Plan(name="my-plan", model=MyModel) + handler._context.plans = {"my-plan": plan} + + # Setup requests.get call to return the output of the FastAPI call for plans. + # Call the CLI function and check the output. + mock_requests.return_value = client.get("/plans") + plans = runner.invoke(main, ["controller", "plans"]) + + assert ( + plans.output == "{'plans': [{'description': None,\n" + " 'name': 'my-plan',\n" + " 'parameter_schema': {'properties': {'id': {'title': 'Id',\n" + " 'type': 'string'}},\n" + " 'required': ['id'],\n" + " 'title': 'MyModel',\n" + " 'type': 'object'}}]}\n" + ) + + # Setup requests.get call to return the output of the FastAPI call for devices. + # Call the CLI function and check the output - expect nothing as no devices set. + handler._context.devices = {} + mock_requests.return_value = client.get("/devices") + unset_devices = runner.invoke(main, [ "-c", config_path, "controller", "devices"]) + assert unset_devices.output == "{'devices': []}\n" + assert mock_requests.call_args[0] == ( "GET", - "http://a.fake.host:12345/plans/sleep", + "http://a.fake.host:12345/devices", ) - mock_requests.return_value = client.post( - "/tasks", json={"name": "sleep", "params": {"time": 5}} - ) + # Put a device in handler.context manually. + device = MyDevice("my-device") + handler._context.devices = {"my-device": device} + + # Setup requests.get call to return the output of the FastAPI call for devices. + # Call the CLI function and check the output. + mock_requests.return_value = client.get("/devices") + devices = runner.invoke(main, ["controller", "devices"]) assert mock_requests.call_args[1] == ( - "POST", - "http://a.fake.host:12345/tasks", + "GET", + "http://a.fake.host:12345/devices", ) + assert ( + devices.output + == "{'devices': [{'name': 'my-device', 'protocols': ['HasName']}]}\n" + ) @pytest.mark.handler @patch("blueapi.service.handler.Handler") @@ -187,7 +216,38 @@ def test_plan_accepted_with_right_parameters( client: TestClient, runner: CliRunner, ): - pass + + # needed so that the handler is instantiated as MockHandler() instead of Handler(). + mock_handler.side_effect = Mock(return_value=handler) + + # Setup the (Mock)Handler. + with patch("uvicorn.run", side_effect=None): + result = runner.invoke(main, ["serve"]) + + assert result.exit_code == 0 + + mock_requests.return_value = client.get("/plans/sleep") + output = runner.invoke( + main, [ "controller", "run", "sleep", '{"time": 5}'] + ) + assert result.exit_code == 0 + print(output) + # 'Trying to run plan: sleep.\nChecking supplied parameters against expected parameters...\nInput validation failed: __root__: value is not a valid dict\nfailed to run the sleep plan, supplied params {"time": 5}\n do not match the expected params: {\'time\': {\'title\': \'Time\', \'type\': \'number\'}}\n' + + # expect the first call to be to the helper + assert mock_requests.call_args[0] == ( + "GET", + "http://localhost:12345/plans/sleep", + ) + + mock_requests.return_value = client.post( + "/tasks", json={"name": "sleep", "params": {"time": 5}} + ) + + assert mock_requests.call_args[1] == ( + "POST", + "http://localhost:12345/tasks", + ) @pytest.mark.handler @@ -200,4 +260,36 @@ def test_plan_rejected_with_wrong_parameters( client: TestClient, runner: CliRunner, ): - pass + + # needed so that the handler is instantiated as MockHandler() instead of Handler(). + mock_handler.side_effect = Mock(return_value=handler) + + # Setup the (Mock)Handler. + with patch("uvicorn.run", side_effect=None): + result = runner.invoke(main, ["serve"]) + + assert result.exit_code == 0 + + mock_requests.return_value = client.get("/plans/sleep") + output = runner.invoke( + main, [ "controller", "run", "sleep", '{"time": 5}'] + ) + assert result.exit_code == 0 + print(output) + # 'Trying to run plan: sleep.\nChecking supplied parameters against expected parameters...\nInput validation failed: __root__: value is not a valid dict\nfailed to run the sleep plan, supplied params {"time": 5}\n do not match the expected params: {\'time\': {\'title\': \'Time\', \'type\': \'number\'}}\n' + + # expect the first call to be to the helper + assert mock_requests.call_args[0] == ( + "GET", + "http://a.fake.host:12345/plans/sleep", + ) + + mock_requests.return_value = client.post( + "/tasks", json={"name": "sleep", "params": {"time": 5}} + ) + + assert mock_requests.call_args[1] == ( + "POST", + "http://a.fake.host:12345/tasks", + ) +