diff --git a/.github/workflows/python-testing.yml b/.github/workflows/python-testing.yml new file mode 100644 index 0000000..6843d42 --- /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 coverage + 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..282ab2f --- /dev/null +++ b/tests/test_dimo.py @@ -0,0 +1,60 @@ +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("Valuations", "/v2/vehicles/1234/valuations") + assert result == "https://valuations-api.dev.dimo.zone/v2/vehicles/1234/valuations" + + +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 + assert args[0] == "POST" + assert args[1] == "Trips" + assert args[2] == "" + + # Assert correct headers + 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 + + assert result == {"data": {"result": True}} 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) 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]