From f32b0179ec75881e3616413606f728bfdbed2e8c Mon Sep 17 00:00:00 2001 From: dennismeister93 Date: Fri, 7 Jul 2023 10:14:20 +0200 Subject: [PATCH] Adapt tests for seat adjuster to work after importing to template (#88) * Adapt tests for seat adjuster to work after importing to template * Update pre commit hook versions * Align linter with template * Add adaptions for correct unit testing --------- Signed-off-by: Dennis Meister --- .github/actions/pre-commit-action/action.yml | 34 +++ .github/workflows/ci.yaml | 2 +- .pre-commit-config.yaml | 4 +- NOTICE-3RD-PARTY-CONTENT.md | 1 - examples/dog-mode/src/main.py | 1 - examples/seat-adjuster/Dockerfile | 2 +- examples/seat-adjuster/requirements-links.txt | 2 +- examples/seat-adjuster/src/main.py | 76 +----- examples/seat-adjuster/src/vapp.py | 99 +++++++ .../tests/integration/integration_test.py | 152 +++++------ examples/seat-adjuster/tests/unit/test_run.py | 246 +++++++++++++----- setup.py | 2 +- tests/unit/client_test.py | 1 - tests/unit/dapr_pusbub_client_test.py | 2 - tests/unit/model_test.py | 1 - 15 files changed, 403 insertions(+), 222 deletions(-) create mode 100644 .github/actions/pre-commit-action/action.yml create mode 100644 examples/seat-adjuster/src/vapp.py diff --git a/.github/actions/pre-commit-action/action.yml b/.github/actions/pre-commit-action/action.yml new file mode 100644 index 00000000..0815fa8a --- /dev/null +++ b/.github/actions/pre-commit-action/action.yml @@ -0,0 +1,34 @@ +# Copyright (c) 2022-2023 Robert Bosch GmbH and Microsoft Corporation +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: pre-commit +description: run pre-commit +inputs: + extra_args: + description: Options to pass to pre-commit run + required: false + default: "--all-files" +runs: + using: composite + steps: + - run: python -m pip install pre-commit + shell: bash + - run: python -m pip freeze --local + shell: bash + - uses: actions/cache@v3 + with: + path: ~/.cache/pre-commit + key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} + - run: pre-commit run --show-diff-on-failure --color=always ${{ inputs.extra_args }} + shell: bash diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 677b097d..3fe95e05 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -57,7 +57,7 @@ jobs: python3 -m pip install tox-gh-actions - name: Run Linters - uses: pre-commit/action@v3.0.0 + uses: ./.github/actions/pre-commit-action - name: Run the daparized databroker binary run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c99f36f7..0001c2c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -57,7 +57,7 @@ repos: )$ - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 23.1.0 hooks: - id: black exclude: > @@ -103,7 +103,7 @@ repos: pass_filenames: false - repo: https://github.com/pycqa/pydocstyle - rev: 6.1.1 + rev: 6.3.0 hooks: - id: pydocstyle exclude: > diff --git a/NOTICE-3RD-PARTY-CONTENT.md b/NOTICE-3RD-PARTY-CONTENT.md index 1561fbb3..4487406e 100644 --- a/NOTICE-3RD-PARTY-CONTENT.md +++ b/NOTICE-3RD-PARTY-CONTENT.md @@ -100,5 +100,4 @@ |actions/setup-python|v4|MIT License| |actions/upload-artifact|v3|MIT License| |github/codeql-action|v2|MIT License| -|pre-commit/action|v3.0.0|MIT License| |softprops/action-gh-release|v1|MIT License| diff --git a/examples/dog-mode/src/main.py b/examples/dog-mode/src/main.py index fc556f84..553bae0b 100644 --- a/examples/dog-mode/src/main.py +++ b/examples/dog-mode/src/main.py @@ -108,7 +108,6 @@ async def on_change(self, data: DataPointReply): ) async def display_values(self): - logger.info("Publish Current Temperature and StateOfCharge") try: logger.info( diff --git a/examples/seat-adjuster/Dockerfile b/examples/seat-adjuster/Dockerfile index 9790f50e..ab78f666 100644 --- a/examples/seat-adjuster/Dockerfile +++ b/examples/seat-adjuster/Dockerfile @@ -41,7 +41,7 @@ RUN pip3 install --no-cache-dir pyinstaller==5.9.0 \ WORKDIR /app -RUN pyinstaller --clean -F -s src/main.py +RUN pyinstaller --clean -F -s --paths=src src/main.py WORKDIR /app/dist diff --git a/examples/seat-adjuster/requirements-links.txt b/examples/seat-adjuster/requirements-links.txt index c0e770dc..686cd11c 100644 --- a/examples/seat-adjuster/requirements-links.txt +++ b/examples/seat-adjuster/requirements-links.txt @@ -1 +1 @@ -git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.9.1 +git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git@v0.9.2 diff --git a/examples/seat-adjuster/src/main.py b/examples/seat-adjuster/src/main.py index 96dc910f..ae4bf966 100644 --- a/examples/seat-adjuster/src/main.py +++ b/examples/seat-adjuster/src/main.py @@ -12,21 +12,19 @@ # # SPDX-License-Identifier: Apache-2.0 -"""A sample Velocitas vehicle app for adjusting seat position.""" +"""The launcher for starting a Vehicle App.""" import asyncio -import json import logging import signal -from vehicle import Vehicle, vehicle # type: ignore +from vapp import SeatAdjusterApp # type: ignore # noqa: E402 +from vehicle import vehicle # type: ignore from sdv.util.log import ( # type: ignore get_opentelemetry_log_factory, get_opentelemetry_log_format, ) -from sdv.vdb.subscriptions import DataPointReply -from sdv.vehicle_app import VehicleApp, subscribe_topic logging.setLogRecordFactory(get_opentelemetry_log_factory()) logging.basicConfig(format=get_opentelemetry_log_format()) @@ -34,75 +32,7 @@ logger = logging.getLogger(__name__) -class SeatAdjusterApp(VehicleApp): - """ - Sample Velocitas Vehicle App. - - The SeatAdjusterApp subscribes to a MQTT topic to listen for incoming - requests to change the seat position and calls the SeatService to move the seat - upon such a request, but only if Vehicle.Speed equals 0. - - It also subcribes to the VehicleDataBroker for updates of the - Vehicle.Cabin.Seat.Row1.Pos1.Position signal and publishes this - information via another specific MQTT topic - """ - - def __init__(self, vehicle_client: Vehicle): - super().__init__() - self.Vehicle = vehicle_client - - async def on_start(self): - """Run when the vehicle app starts""" - await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.subscribe( - self.on_seat_position_changed - ) - - async def on_seat_position_changed(self, data: DataPointReply): - response_topic = "seatadjuster/currentPosition" - await self.publish_event( - response_topic, - json.dumps( - {"position": data.get(self.Vehicle.Cabin.Seat.Row1.Pos1.Position).value} - ), - ) - - @subscribe_topic("seatadjuster/setPosition/request") - async def on_set_position_request_received(self, data_str: str) -> None: - data = json.loads(data_str) - response_topic = "seatadjuster/setPosition/response" - response_data = {"requestId": data["requestId"], "result": {}} - - vehicle_speed = (await self.Vehicle.Speed.get()).value - - position = data["position"] - if vehicle_speed == 0: - try: - await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.set(position) - response_data["result"] = { - "status": 0, - "message": f"Set Seat position to: {position}", - } - except ValueError as error: - response_data["result"] = { - "status": 1, - "message": f"Failed to set the position {position}, error: {error}", - } - except Exception: - response_data["result"] = { - "status": 1, - "message": "Exception on set Seat position", - } - - else: - error_msg = f"""Not allowed to move seat because vehicle speed - is {vehicle_speed} and not 0""" - response_data["result"] = {"status": 1, "message": error_msg} - - await self.publish_event(response_topic, json.dumps(response_data)) - - async def main(): - """Main function""" logger.info("Starting seat adjuster app...") seat_adjuster_app = SeatAdjusterApp(vehicle) diff --git a/examples/seat-adjuster/src/vapp.py b/examples/seat-adjuster/src/vapp.py new file mode 100644 index 00000000..abae495a --- /dev/null +++ b/examples/seat-adjuster/src/vapp.py @@ -0,0 +1,99 @@ +# Copyright (c) 2022-2023 Robert Bosch GmbH and Microsoft Corporation +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +"""A sample Velocitas vehicle app for adjusting seat position.""" + +import json +import logging + +from vehicle import Vehicle # type: ignore + +from sdv.util.log import ( # type: ignore + get_opentelemetry_log_factory, + get_opentelemetry_log_format, +) +from sdv.vdb.reply import DataPointReply +from sdv.vehicle_app import VehicleApp, subscribe_topic + +logging.setLogRecordFactory(get_opentelemetry_log_factory()) +logging.basicConfig(format=get_opentelemetry_log_format()) +logging.getLogger().setLevel("DEBUG") +logger = logging.getLogger(__name__) + + +class SeatAdjusterApp(VehicleApp): + """ + Sample Velocitas Vehicle App. + + The SeatAdjusterApp subscribes to a MQTT topic to listen for incoming + requests to change the seat position and calls the SeatService to move the seat + upon such a request, but only if Vehicle.Speed equals 0. + + It also subcribes to the VehicleDataBroker for updates of the + Vehicle.Cabin.Seat.Row1.Pos1.Position signal and publishes this + information via another specific MQTT topic + """ + + def __init__(self, vehicle_client: Vehicle): + super().__init__() + self.Vehicle = vehicle_client + + async def on_start(self): + """Run when the vehicle app starts""" + await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.subscribe( + self.on_seat_position_changed + ) + + async def on_seat_position_changed(self, data: DataPointReply): + response_topic = "seatadjuster/currentPosition" + await self.publish_event( + response_topic, + json.dumps( + {"position": data.get(self.Vehicle.Cabin.Seat.Row1.Pos1.Position).value} + ), + ) + + @subscribe_topic("seatadjuster/setPosition/request") + async def on_set_position_request_received(self, data_str: str) -> None: + data = json.loads(data_str) + response_topic = "seatadjuster/setPosition/response" + response_data = {"requestId": data["requestId"], "result": {}} + + vehicle_speed = (await self.Vehicle.Speed.get()).value + + position = data["position"] + if vehicle_speed == 0: + try: + await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.set(position) + response_data["result"] = { + "status": 0, + "message": f"Set Seat position to: {position}", + } + except ValueError as error: + response_data["result"] = { + "status": 1, + "message": f"Failed to set the position {position}, error: {error}", + } + except Exception: + response_data["result"] = { + "status": 1, + "message": "Exception on set Seat position", + } + + else: + error_msg = f"""Not allowed to move seat because vehicle speed + is {vehicle_speed} and not 0""" + response_data["result"] = {"status": 1, "message": error_msg} + + await self.publish_event(response_topic, json.dumps(response_data)) diff --git a/examples/seat-adjuster/tests/integration/integration_test.py b/examples/seat-adjuster/tests/integration/integration_test.py index 8efa6e06..52ef2b43 100644 --- a/examples/seat-adjuster/tests/integration/integration_test.py +++ b/examples/seat-adjuster/tests/integration/integration_test.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation +# Copyright (c) 2022-2023 Robert Bosch GmbH and Microsoft Corporation # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at @@ -13,7 +13,6 @@ # SPDX-License-Identifier: Apache-2.0 import json -from asyncio import sleep import pytest @@ -28,7 +27,7 @@ async def test_set_position_not_allowed(): mqtt_client = MqttClient() inttesthelper = IntTestHelper() - request_id = "abc" + request_id = 123 payload = {"position": 300, "requestId": request_id} speed_value = 50 @@ -54,100 +53,105 @@ async def test_set_position_not_allowed(): assert body["result"]["message"] == error_msg -# add message to get it assert +# If you want to run the following integration tests, +# make sure the feedercan is not running in your runtime. +# @pytest.mark.asyncio +# async def test_set_position_allowed(): +# mqtt_client = MqttClient() +# inttesthelper = IntTestHelper() +# request_id = 123 -@pytest.mark.asyncio -async def test_set_position_allowed(): - mqtt_client = MqttClient() - inttesthelper = IntTestHelper() - request_id = "abc" +# payload = {"position": 0, "requestId": request_id} - payload = {"position": 0, "requestId": request_id} +# response = await inttesthelper.set_float_datapoint(name="Vehicle.Speed", value=0) - response = await inttesthelper.set_float_datapoint(name="Vehicle.Speed", value=0) +# assert len(response.errors) == 0 - assert len(response.errors) == 0 +# response = mqtt_client.publish_and_wait_for_response( +# request_topic=REQUEST_TOPIC, +# response_topic=RESPONSE_TOPIC, +# payload=payload, +# ) - response = mqtt_client.publish_and_wait_for_response( - request_topic=REQUEST_TOPIC, - response_topic=RESPONSE_TOPIC, - payload=payload, - ) +# body = json.loads(response) +# assert body["result"]["status"] == 0 - body = json.loads(response) - assert body["result"]["status"] == 0 +# await sleep(1) - await sleep(1) +# position = 200 +# payload = {"position": position, "requestId": request_id} - position = 200 - payload = {"position": position, "requestId": request_id} +# response = mqtt_client.publish_and_wait_for_property( +# request_topic=REQUEST_TOPIC, +# response_topic="seatadjuster/currentPosition", +# payload=payload, +# path=["position"], +# value=position, +# ) - response = mqtt_client.publish_and_wait_for_property( - request_topic=REQUEST_TOPIC, - response_topic="seatadjuster/currentPosition", - payload=payload, - path=["position"], - value=position, - ) +# assert response != "" - assert response != "" +# body = json.loads(response) +# assert body["position"] == position - body = json.loads(response) - assert body["position"] == position +# @pytest.mark.asyncio +# async def test_set_position_lt_0(): +# mqtt_client = MqttClient() +# inttesthelper = IntTestHelper() +# request_id = 123 +# seat_position = -1 +# payload = {"position": seat_position, "requestId": request_id} -@pytest.mark.asyncio -async def test_set_position_lt_0(): - mqtt_client = MqttClient() - inttesthelper = IntTestHelper() - request_id = "abc" - seat_position = -1 - payload = {"position": seat_position, "requestId": request_id} +# response = await inttesthelper.set_float_datapoint(name="Vehicle.Speed", value=0) - response = await inttesthelper.set_float_datapoint(name="Vehicle.Speed", value=0) +# assert len(response.errors) == 0 - assert len(response.errors) == 0 +# response = mqtt_client.publish_and_wait_for_response( +# request_topic=REQUEST_TOPIC, +# response_topic=RESPONSE_TOPIC, +# payload=payload, +# ) - response = mqtt_client.publish_and_wait_for_response( - request_topic=REQUEST_TOPIC, - response_topic=RESPONSE_TOPIC, - payload=payload, - ) +# assert response != "" - assert response != "" +# body = json.loads(response) +# error_msg = ( +# f"Failed to set the position {seat_position}, " +# f"error: Value out of range: {seat_position}" +# ) +# assert body["requestId"] == request_id +# assert body["result"]["status"] == 1 +# assert body["result"]["message"] == error_msg - body = json.loads(response) - error_msg = f"""Provided position '{seat_position}' \ - should be in between (0-1000)""" - assert body["requestId"] == request_id - assert body["result"]["status"] == 1 - assert body["result"]["message"] == error_msg +# @pytest.mark.asyncio +# async def test_set_position__gt_1000(): +# mqtt_client = MqttClient() +# inttesthelper = IntTestHelper() +# request_id = 123 +# seat_position = 10000000001 +# payload = {"position": seat_position, "requestId": request_id} -@pytest.mark.asyncio -async def test_set_position__gt_1000(): - mqtt_client = MqttClient() - inttesthelper = IntTestHelper() - request_id = "abc" - seat_position = 1001 - payload = {"position": seat_position, "requestId": request_id} +# response = await inttesthelper.set_float_datapoint(name="Vehicle.Speed", value=0) - response = await inttesthelper.set_float_datapoint(name="Vehicle.Speed", value=0) +# assert len(response.errors) == 0 - assert len(response.errors) == 0 +# response = mqtt_client.publish_and_wait_for_response( +# request_topic=REQUEST_TOPIC, +# response_topic=RESPONSE_TOPIC, +# payload=payload, +# ) - response = mqtt_client.publish_and_wait_for_response( - request_topic=REQUEST_TOPIC, - response_topic=RESPONSE_TOPIC, - payload=payload, - ) +# assert response != "" - assert response != "" +# body = json.loads(response) +# error_msg = ( +# f"Failed to set the position {seat_position}, " +# f"error: Value out of range: {seat_position}" +# ) - body = json.loads(response) - error_msg = f"""Provided position '{seat_position}' \ - should be in between (0-1000)""" - assert body["requestId"] == request_id - assert body["result"]["status"] == 1 - assert body["result"]["message"] == error_msg +# assert body["requestId"] == request_id +# assert body["result"]["status"] == 1 +# assert body["result"]["message"] == error_msg diff --git a/examples/seat-adjuster/tests/unit/test_run.py b/examples/seat-adjuster/tests/unit/test_run.py index efe78738..aa0c4a34 100644 --- a/examples/seat-adjuster/tests/unit/test_run.py +++ b/examples/seat-adjuster/tests/unit/test_run.py @@ -12,106 +12,226 @@ # # SPDX-License-Identifier: Apache-2.0 +import json +import os +import sys from unittest import mock +from unittest.mock import AsyncMock, MagicMock import pytest -from sdv_model.Cabin.SeatService import SeatService # type: ignore -from sdv_model.proto.seats_pb2 import BASE, SeatLocation # type: ignore +from google.protobuf.timestamp_pb2 import Timestamp +from vehicle import vehicle # type: ignore -from sdv.vehicle_app import VehicleApp +from sdv.vdb.reply import DataPointReply +from sdv.vdb.types import TypedDataPointResult + +# Get the app directory path +app_dir_path = os.path.abspath(os.path.join(__file__, "../../..")) + +# Add the src directory to the sys.path +src_dir = os.path.join(app_dir_path, "src") +sys.path.insert(0, src_dir) + +# Import the SeatAdjusterApp class from vapp.py +from vapp import SeatAdjusterApp # type: ignore # noqa: E402 @pytest.mark.asyncio -async def test_for_seats_movecomponent(): +async def test_on_seat_position_changed(): + seat_adjuster_app = SeatAdjusterApp(vehicle) + # Create a MagicMock instance to mock the value attribute + position_data_point_value_mock = MagicMock(value=5) + + # Create a MagicMock instance to mock the data point + position_data_point_mock = MagicMock() + position_data_point_mock.get.return_value = position_data_point_value_mock + + # Create a MagicMock instance to mock the DataPointReply class + position_data_point_reply_mock = MagicMock(spec=DataPointReply) + position_data_point_reply_mock.return_value = position_data_point_mock + + expected_data = {"position": position_data_point_value_mock.value} + + # Mock the publish_event method + seat_adjuster_app.publish_event = AsyncMock() + + # Call the method under test + await seat_adjuster_app.on_seat_position_changed(position_data_point_reply_mock()) + + # Assert that publish_event was called with the correct arguments + seat_adjuster_app.publish_event.assert_called_once_with( + "seatadjuster/currentPosition", + json.dumps(expected_data), + ) + + +@pytest.mark.asyncio +async def test_on_set_position_request_received_vehicle_not_moving(): + seat_adjuster_app = SeatAdjusterApp(vehicle) + vehicle_speed_data_point = TypedDataPointResult[float]( + "foo", 0, Timestamp(seconds=10, nanos=0) + ) + + request_data_str = get_valid_request_data_str() + request_data = json.loads(request_data_str) + + expected_response_data = { + "requestId": request_data["requestId"], + "result": { + "status": 0, + "message": f"Set Seat position to: {request_data['position']}", + }, + } + with mock.patch.object( - SeatService, - "MoveComponent", + vehicle.Speed, + "get", new_callable=mock.AsyncMock, - return_value=get_sample_request_data(), + return_value=vehicle_speed_data_point, ): - location = SeatLocation(row=1, index=1) - get_position = get_sample_request_data() - response = await SeatService.MoveComponent( - location, BASE, get_position["position"] # type: ignore + called_data_point = seat_adjuster_app.Vehicle.Cabin.Seat.Row1.Pos1.Position + called_data_point.set = AsyncMock() + seat_adjuster_app.publish_event = AsyncMock() + + await seat_adjuster_app.on_set_position_request_received(request_data_str) + + called_data_point.set.assert_called_once_with(request_data["position"]) + seat_adjuster_app.publish_event.assert_called_once_with( + "seatadjuster/setPosition/response", + json.dumps(expected_response_data), ) - assert response == get_sample_request_data() @pytest.mark.asyncio -async def test_for_seats_movecomponent_set_high_position(): +async def test_on_set_position_request_received_vehicle_moving(): + seat_adjuster_app = SeatAdjusterApp(vehicle) + vehicle_speed_data_point = TypedDataPointResult[float]( + "foo", 5, Timestamp(seconds=10, nanos=0) + ) + + request_data_str = get_valid_request_data_str() + request_data = json.loads(request_data_str) + + error_msg = f"""Not allowed to move seat because vehicle speed + is {vehicle_speed_data_point.value} and not 0""" + + expected_response_data = { + "requestId": request_data["requestId"], + "result": { + "status": 1, + "message": error_msg, + }, + } + with mock.patch.object( - SeatService, - "MoveComponent", + vehicle.Speed, + "get", new_callable=mock.AsyncMock, - return_value=get_error_invalid_arg_response(), + return_value=vehicle_speed_data_point, ): - location = SeatLocation(row=1, index=1) - set_position = set_seat_position_high() - response = await SeatService.MoveComponent( - location, BASE, set_position["position"] # type: ignore + seat_adjuster_app.publish_event = AsyncMock() + + await seat_adjuster_app.on_set_position_request_received(request_data_str) + + seat_adjuster_app.publish_event.assert_called_once_with( + "seatadjuster/setPosition/response", + json.dumps(expected_response_data), ) - assert response == get_error_invalid_arg_response() @pytest.mark.asyncio -async def test_for_seats_movecomponent_error_path(): +async def test_on_set_position_request_received_error_path(): + seat_adjuster_app = SeatAdjusterApp(vehicle) + vehicle_speed_data_point = TypedDataPointResult[float]( + "foo", 0, Timestamp(seconds=10, nanos=0) + ) + + request_data_str = get_valid_request_data_str() + request_data = json.loads(request_data_str) + + expected_response_data = { + "requestId": request_data["requestId"], + "result": { + "status": 1, + "message": "Exception on set Seat position", + }, + } + with mock.patch.object( - SeatService, - "MoveComponent", + vehicle.Speed, + "get", new_callable=mock.AsyncMock, - return_value=get_error_response(), + return_value=vehicle_speed_data_point, ): - location = SeatLocation(row=1, index=1) - get_position = get_sample_request_data() - response = await SeatService.MoveComponent( - location, BASE, get_position["position"] # type: ignore + called_data_point = seat_adjuster_app.Vehicle.Cabin.Seat.Row1.Pos1.Position + called_data_point.set = AsyncMock(side_effect=async_raise_exception) + seat_adjuster_app.publish_event = AsyncMock() + + await seat_adjuster_app.on_set_position_request_received(request_data_str) + + called_data_point.set.assert_called_once_with(request_data["position"]) + seat_adjuster_app.publish_event.assert_called_once_with( + "seatadjuster/setPosition/response", + json.dumps(expected_response_data), ) - assert response == get_error_response() @pytest.mark.asyncio -async def test_for_publish_to_topic(): +async def test_on_set_position_request_received_high_position(): + seat_adjuster_app = SeatAdjusterApp(vehicle) + vehicle_speed_data_point = TypedDataPointResult[float]( + "foo", 0, Timestamp(seconds=10, nanos=0) + ) + + request_data_str = get_invalid_request_data_str() + request_data = json.loads(request_data_str) + + error_msg = f"""Provided position {request_data["position"]} \ + should not be Greater than 1000 (Max)""" + + expected_response_data = { + "requestId": request_data["requestId"], + "result": { + "status": 1, + "message": f"Failed to set the position \ +{request_data['position']}, error: {error_msg}", + }, + } + with mock.patch.object( - VehicleApp, "publish_mqtt_event", new_callable=mock.AsyncMock, return_value=-1 + vehicle.Speed, + "get", + new_callable=mock.AsyncMock, + return_value=vehicle_speed_data_point, ): - response = await VehicleApp.publish_mqtt_event( - str("sampleTopic"), get_sample_request_data() # type: ignore - ) - assert response == -1 + called_data_point = seat_adjuster_app.Vehicle.Cabin.Seat.Row1.Pos1.Position + called_data_point.set = AsyncMock(side_effect=async_raise_value_error) + seat_adjuster_app.publish_event = AsyncMock() + await seat_adjuster_app.on_set_position_request_received(request_data_str) -def get_sample_request_data(): - return {"position": 330, "requestId": "123456789"} + called_data_point.set.assert_called_once_with(request_data["position"]) + seat_adjuster_app.publish_event.assert_called_once_with( + "seatadjuster/setPosition/response", + json.dumps(expected_response_data), + ) -def set_seat_position_high(): - return {"position": 1001, "requestId": "123456789"} +def get_valid_request_data_str(): + return '{"requestId": 123, "position": 10}' -def get_error_invalid_arg_response(): - data = set_seat_position_high() - error_msg = f"""Provided position {data["position"]} \ - should not be Greater than 1000 (Max)""" - resp_data = { - "requestId": data["requestId"], - "result": {"status": 1, "message": error_msg}, - } - return resp_data +def get_invalid_request_data_str(): + return '{"requestId": 123, "position": 1001}' -def get_sample_response(): - get_position = get_sample_request_data() - resp_data = { - "requestId": {"requestId": get_position["position"], "result": {"status": 0}} - } - return resp_data +async def async_raise_exception(*args): + raise Exception("Unknown error") -def get_error_response(): - data = get_sample_request_data() - error_msg = "Received unknown RPC error" - resp_data = { - "requestId": data["requestId"], - "result": {"status": 1, "message": error_msg}, - } - return resp_data +async def async_raise_value_error(*args): + data = json.loads(get_invalid_request_data_str()) + raise ValueError( + f"""Provided position {data['position']} \ + should not be Greater than 1000 (Max)""" + ) diff --git a/setup.py b/setup.py index 0cdb3b0f..ebdf7426 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,7 @@ setup( name="sdv", - version="0.9.1", + version="0.9.2", description="A Python SDK for Vehicle app", long_description=long_description, long_description_content_type="text/markdown", diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index b8725ce2..94cd6fe2 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -44,7 +44,6 @@ def reset(): @pytest.mark.asyncio async def test_for_get_metadata(): - client = get_vehicle_client_instance() with mock.patch.object( diff --git a/tests/unit/dapr_pusbub_client_test.py b/tests/unit/dapr_pusbub_client_test.py index 7a8f02cf..16c59fcd 100644 --- a/tests/unit/dapr_pusbub_client_test.py +++ b/tests/unit/dapr_pusbub_client_test.py @@ -38,7 +38,6 @@ def reset(): @pytest.mark.asyncio async def test_for_subscribe_topic(): - middleware = get_middleware_instance() with mock.patch.object( middleware.pubsub_client, @@ -52,7 +51,6 @@ async def test_for_subscribe_topic(): @pytest.mark.asyncio async def test_for_get_publish_event(): - middleware = get_middleware_instance() with mock.patch.object( middleware.pubsub_client, diff --git a/tests/unit/model_test.py b/tests/unit/model_test.py index 0369837a..ac751161 100755 --- a/tests/unit/model_test.py +++ b/tests/unit/model_test.py @@ -1474,7 +1474,6 @@ async def test_setting_multiple_data_points_atomically(): new_callable=mock.AsyncMock, return_value=SetDatapointsReply(errors={}), ) as mock_set_dps: - ( await vehicle.set_many() .add(vehicle.Bool, False)