From 4c3ec09253c0ed4eb8d51f3085efbbdd88c32279 Mon Sep 17 00:00:00 2001 From: ardevd Date: Tue, 29 Apr 2025 21:53:52 +0200 Subject: [PATCH 1/8] test: added unit test for SDK root code. Added github actions This adds a few unit tests for the SDK root code in dimo.py. This also adds a github action to run tests, which should run on every commit to main as well as pull requests. --- .github/workflows/python-testing.yml | 39 +++++++++++++++++ tests/__init__.py | 0 tests/test_dimo.py | 62 ++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 .github/workflows/python-testing.yml create mode 100644 tests/__init__.py create mode 100644 tests/test_dimo.py diff --git a/.github/workflows/python-testing.yml b/.github/workflows/python-testing.yml new file mode 100644 index 0000000..4dd8ef2 --- /dev/null +++ b/.github/workflows/python-testing.yml @@ -0,0 +1,39 @@ +name: Python application + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + python-version: ["3.13"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + pip install -r requirements.txt + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: DIMO Api test + run: | + PYTHONPATH=$(pwd) coverage run -m pytest -v && coverage xml -o coverage.xml diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_dimo.py b/tests/test_dimo.py new file mode 100644 index 0000000..e8da83b --- /dev/null +++ b/tests/test_dimo.py @@ -0,0 +1,62 @@ +from unittest.mock import MagicMock + +from dimo.dimo import DIMO + + +def test_get_full_path_no_params(): + client = DIMO(env="Dev") + result = client._get_full_path("Telemetry", "/path") + assert result == "https://telemetry-api.dev.dimo.zone/query/path" + + +def test_get_full_path_with_params(): + client = DIMO(env="Dev") + result = client._get_full_path( + "Telemetry", + "/items/:item_id", + {"item_id": 123, "detail_id": "abc"}, + ) + assert result == "https://telemetry-api.dev.dimo.zone/query/items/123" + + +def test_get_auth_headers(): + client = DIMO(env="Dev") + headers = client._get_auth_headers("token123") + assert headers == { + "Authorization": "Bearer token123", + "Content-Type": "application/json", + } + + +def test_query_calls_request_with_correct_payload(monkeypatch): + client = DIMO(env="Dev") + # Create a fake request method on the client + fake_request = MagicMock(return_value={"data": {"result": True}}) + monkeypatch.setattr(client, "request", fake_request) + + query_str = "query { test }" + variables = {"key": "value"} + result = client.query("Trips", query_str, variables=variables, token="mocked_token") + + # Verify the fake request was invoked once + fake_request.assert_called_once() + # Inspect call arguments + args, kwargs = fake_request.call_args + # args: (http_method, service, path) + assert args[0] == "POST" + assert args[1] == "Trips" + assert args[2] == "" # GraphQL posts to root + + # Check headers set in kwargs + headers = kwargs["headers"] + assert headers["Authorization"] == "Bearer mocked_token" + assert headers["Content-Type"] == "application/json" + assert headers["User-Agent"] == "dimo-python-sdk" + + # Check payload data + data = kwargs["data"] + assert data["query"] == query_str + assert data["variables"] == variables + + # And ensure the return value is forwarded + assert result == {"data": {"result": True}} From ab9954e15840c15ab14c15dbc1ef2e33d22cfd8c Mon Sep 17 00:00:00 2001 From: ardevd Date: Tue, 29 Apr 2025 22:01:49 +0200 Subject: [PATCH 2/8] test: cleaned up tests --- tests/test_dimo.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_dimo.py b/tests/test_dimo.py index e8da83b..a7c2941 100644 --- a/tests/test_dimo.py +++ b/tests/test_dimo.py @@ -42,12 +42,11 @@ def test_query_calls_request_with_correct_payload(monkeypatch): fake_request.assert_called_once() # Inspect call arguments args, kwargs = fake_request.call_args - # args: (http_method, service, path) assert args[0] == "POST" assert args[1] == "Trips" - assert args[2] == "" # GraphQL posts to root + assert args[2] == "" - # Check headers set in kwargs + # Assert correct headers headers = kwargs["headers"] assert headers["Authorization"] == "Bearer mocked_token" assert headers["Content-Type"] == "application/json" @@ -58,5 +57,4 @@ def test_query_calls_request_with_correct_payload(monkeypatch): assert data["query"] == query_str assert data["variables"] == variables - # And ensure the return value is forwarded assert result == {"data": {"result": True}} From 8476448c9835f68e6560ae75778dee938c9b083c Mon Sep 17 00:00:00 2001 From: ardevd Date: Tue, 29 Apr 2025 22:06:04 +0200 Subject: [PATCH 3/8] test: added unit tests for errors.py --- tests/test_errors.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/test_errors.py diff --git a/tests/test_errors.py b/tests/test_errors.py new file mode 100644 index 0000000..290ef05 --- /dev/null +++ b/tests/test_errors.py @@ -0,0 +1,33 @@ +import pytest +from dimo.errors import DimoTypeError, check_type, check_optional_type + + +def test_check_type_passes_for_correct_type(): + # call check_type with valid args which should not raise anything + check_type("count", 5, int) + + +def test_check_type_raises_for_incorrect_type(): + with pytest.raises(DimoTypeError) as exc: + check_type("name", 123, str) + err = exc.value + assert err.param_name == "name" + assert err.expected_type is str + assert isinstance(err.actual_value, int) + + assert "name must be a str" in str(err) + assert "but was entered as type int" in str(err) + + +def test_check_optional_type_allows_none(): + # None is allowed + check_optional_type("maybe", None, dict) + + +def test_check_optional_type_raises_for_wrong_non_none(): + with pytest.raises(DimoTypeError) as exc: + check_optional_type("maybe", 3.14, str) + err = exc.value + assert err.param_name == "maybe" + assert err.expected_type is str + assert isinstance(err.actual_value, float) From 58998f77154a4b1055079c511967f06a27bbd533 Mon Sep 17 00:00:00 2001 From: ardevd Date: Tue, 29 Apr 2025 22:11:26 +0200 Subject: [PATCH 4/8] test: added unit test for the permission decoder --- tests/test_permission_decoder.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/test_permission_decoder.py diff --git a/tests/test_permission_decoder.py b/tests/test_permission_decoder.py new file mode 100644 index 0000000..2576b01 --- /dev/null +++ b/tests/test_permission_decoder.py @@ -0,0 +1,13 @@ +from dimo.permission_decoder import PermissionDecoder + + +def test_dimo_client_hex_to_permissions(): + permissions_hex = "0x3ffc" + permissions_list = PermissionDecoder.decode_permission_bits(permissions_hex) + assert permissions_list == [1, 2, 3, 4, 5, 6] + + one_to_five_hex = "0xffc" + assert PermissionDecoder.decode_permission_bits(one_to_five_hex) == [1, 2, 3, 4, 5] + + another_hex = "0x3fcc" + assert PermissionDecoder.decode_permission_bits(another_hex) == [1, 3, 4, 5, 6] From 3158539ef12dd2720529df6ff9c32727d9fede34 Mon Sep 17 00:00:00 2001 From: ardevd Date: Tue, 29 Apr 2025 22:17:08 +0200 Subject: [PATCH 5/8] refactor(test): slightly tweaked a unit test --- tests/test_dimo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_dimo.py b/tests/test_dimo.py index a7c2941..282ab2f 100644 --- a/tests/test_dimo.py +++ b/tests/test_dimo.py @@ -5,8 +5,8 @@ def test_get_full_path_no_params(): client = DIMO(env="Dev") - result = client._get_full_path("Telemetry", "/path") - assert result == "https://telemetry-api.dev.dimo.zone/query/path" + result = client._get_full_path("Valuations", "/v2/vehicles/1234/valuations") + assert result == "https://valuations-api.dev.dimo.zone/v2/vehicles/1234/valuations" def test_get_full_path_with_params(): From bc61918138d60a7a91dab1b236a5bcb4290f99df Mon Sep 17 00:00:00 2001 From: ardevd Date: Tue, 29 Apr 2025 22:32:33 +0200 Subject: [PATCH 6/8] ci: added codecov action to generate code coverage badge --- .github/workflows/python-testing.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/python-testing.yml b/.github/workflows/python-testing.yml index 4dd8ef2..2cee1ae 100644 --- a/.github/workflows/python-testing.yml +++ b/.github/workflows/python-testing.yml @@ -37,3 +37,7 @@ jobs: - name: DIMO Api test run: | PYTHONPATH=$(pwd) coverage run -m pytest -v && coverage xml -o coverage.xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: coverage.xml From 44d0661a53b7bc3beb7ce8abdc50ad58d26761cb Mon Sep 17 00:00:00 2001 From: ardevd Date: Tue, 29 Apr 2025 22:50:19 +0200 Subject: [PATCH 7/8] install coverage --- .github/workflows/python-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-testing.yml b/.github/workflows/python-testing.yml index 2cee1ae..d76cff7 100644 --- a/.github/workflows/python-testing.yml +++ b/.github/workflows/python-testing.yml @@ -26,7 +26,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install flake8 pytest + pip install flake8 pytest coverage pip install -r requirements.txt - name: Lint with flake8 run: | From 4103d4c9fff2584b6dcafe9cdd157575bc84a05d Mon Sep 17 00:00:00 2001 From: ardevd Date: Tue, 29 Apr 2025 22:52:39 +0200 Subject: [PATCH 8/8] refactor(ci): removed codecov job --- .github/workflows/python-testing.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/python-testing.yml b/.github/workflows/python-testing.yml index d76cff7..6843d42 100644 --- a/.github/workflows/python-testing.yml +++ b/.github/workflows/python-testing.yml @@ -37,7 +37,3 @@ jobs: - name: DIMO Api test run: | PYTHONPATH=$(pwd) coverage run -m pytest -v && coverage xml -o coverage.xml - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - files: coverage.xml