diff --git a/tavern/_core/dict_util.py b/tavern/_core/dict_util.py index f65eaeee9..07c5364ad 100644 --- a/tavern/_core/dict_util.py +++ b/tavern/_core/dict_util.py @@ -27,7 +27,7 @@ logger: logging.Logger = logging.getLogger(__name__) -def _check_and_format_values(to_format: str, box_vars: Mapping[str, Any]) -> str: +def _check_and_format_values(to_format: str, box_vars: Box) -> str: formatter = string.Formatter() would_format = formatter.parse(to_format) diff --git a/tests/integration/Dockerfile b/tests/integration/Dockerfile index edd2733d2..7c964d8b6 100644 --- a/tests/integration/Dockerfile +++ b/tests/integration/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.10-alpine -RUN pip3 install 'pyjwt>=2.4.0,<3' 'flask>=2.2.3' +RUN pip3 install 'pyjwt>=2.4.0,<3' 'flask>=2.2.3' "python-box>=6,<7" ENV FLASK_DEBUG=1 ENV PYTHONUNBUFFERED=0 diff --git a/tests/integration/server.py b/tests/integration/server.py index 02c5b86d9..9316b3a11 100644 --- a/tests/integration/server.py +++ b/tests/integration/server.py @@ -12,6 +12,7 @@ from urllib.parse import unquote_plus, urlencode import jwt +from box import Box from flask import Flask, Response, jsonify, make_response, redirect, request, session from itsdangerous import URLSafeTimedSerializer @@ -471,6 +472,14 @@ def get_606_dict(): return jsonify({}) +@app.route("/sub-path-query", methods=["POST"]) +def sub_path_query(): + r = request.get_json(force=True) + sub_path = r["sub_path"] + + return jsonify({"result": Box(r, box_dots=True)[sub_path]}) + + @app.route("/magic-multi-method", methods=["GET", "POST", "DELETE"]) def get_any_method(): return jsonify({"method": request.method}) diff --git a/tests/integration/test_env_var_format.tavern.yaml b/tests/integration/test_env_var_format.tavern.yaml deleted file mode 100644 index 6da193421..000000000 --- a/tests/integration/test_env_var_format.tavern.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -test_name: Test getting format vars from environment variables - -includes: - - !include common.yaml - -stages: - - name: Make requests using environment variables - request: - url: "{tavern.env_vars.TEST_HOST}/{first_part}/{second_part}" - method: GET - response: - status_code: 200 - json: - status: OK diff --git a/tests/integration/test_format.tavern.yaml b/tests/integration/test_format.tavern.yaml new file mode 100644 index 000000000..7a9920edc --- /dev/null +++ b/tests/integration/test_format.tavern.yaml @@ -0,0 +1,31 @@ +--- +test_name: Test getting format vars from environment variables + +includes: + - !include common.yaml + +stages: + - name: Make requests using environment variables + request: + url: "{tavern.env_vars.TEST_HOST}/{first_part}/{second_part}" + method: GET + response: + status_code: 200 + json: + status: OK + +--- +test_name: Test slicing request vars + +stages: + - name: Make request and expect part of list in it to be returned + request: + url: "{global_host}/sub-path-query" + method: POST + json: + sub_path: "a.b[0].c" + "a": { "b": [{ "c": 3 }] } + response: + status_code: 200 + json: + result: !int "{tavern.request_vars.json.a.b[0].c}" diff --git a/tests/unit/test_helpers.py b/tests/unit/test_helpers.py index a322e967d..fd8104a0f 100644 --- a/tests/unit/test_helpers.py +++ b/tests/unit/test_helpers.py @@ -8,6 +8,7 @@ import _pytest import pytest import yaml +from box import Box from tavern._core import exceptions from tavern._core.dict_util import _check_and_format_values, format_keys @@ -291,12 +292,10 @@ def test_validate_schema_incorrect(self, nested_response): class TestCheckParseValues: - @pytest.mark.parametrize( - "item", [[134], {"a": 2}, yaml, yaml.load, yaml.SafeLoader] - ) + @pytest.mark.parametrize("item", [yaml, yaml.load, yaml.SafeLoader]) def test_warns_bad_type(self, item): with patch("tavern._core.dict_util.logger.warning") as wmock: - _check_and_format_values("{fd}", {"fd": item}) + _check_and_format_values("{fd}", Box({"fd": item})) wmock.assert_called_with( "Formatting '%s' will result in it being coerced to a string (it is a %s)", @@ -304,13 +303,45 @@ def test_warns_bad_type(self, item): type(item), ) + @pytest.mark.parametrize( + "item", + [ + [134], + {"a": 2}, + ], + ) + def test_warns_bad_type_box(self, item): + box = Box({"fd": item}) + with patch("tavern._core.dict_util.logger.warning") as wmock: + _check_and_format_values("{fd}", box) + + wmock.assert_called_with( + "Formatting '%s' will result in it being coerced to a string (it is a %s)", + "fd", + type(box["fd"]), + ) + @pytest.mark.parametrize("item", [1, "a", 1.3, format_keys("{s}", {"s": 2})]) def test_no_warn_good_type(self, item): with patch("tavern._core.dict_util.logger.warning") as wmock: - _check_and_format_values("{fd}", {"fd": item}) + _check_and_format_values("{fd}", Box({"fd": item})) assert not wmock.called + def test_format_with_array_access(self): + """Test accessing using array indexing format""" + + assert "hello" == _check_and_format_values( + "{a.b.c[0].d}", Box({"a": {"b": {"c": [{"d": "hello"}]}}}) + ) + + def test_format_with_dict_access(self): + """Test accessing using dict indexing format""" + + assert "hello" == _check_and_format_values( + "{a[b].c[0].d}", Box({"a": {"b": {"c": [{"d": "hello"}]}}}) + ) + class TestFormatWithJson: @pytest.mark.parametrize(