aboutsummaryrefslogtreecommitdiff
path: root/tests/unit/test_path_template.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/test_path_template.py')
-rw-r--r--tests/unit/test_path_template.py389
1 files changed, 389 insertions, 0 deletions
diff --git a/tests/unit/test_path_template.py b/tests/unit/test_path_template.py
new file mode 100644
index 0000000..2c5216e
--- /dev/null
+++ b/tests/unit/test_path_template.py
@@ -0,0 +1,389 @@
+# Copyright 2017 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://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.
+
+from __future__ import unicode_literals
+
+import mock
+import pytest
+
+from google.api_core import path_template
+
+
+@pytest.mark.parametrize(
+ "tmpl, args, kwargs, expected_result",
+ [
+ # Basic positional params
+ ["/v1/*", ["a"], {}, "/v1/a"],
+ ["/v1/**", ["a/b"], {}, "/v1/a/b"],
+ ["/v1/*/*", ["a", "b"], {}, "/v1/a/b"],
+ ["/v1/*/*/**", ["a", "b", "c/d"], {}, "/v1/a/b/c/d"],
+ # Basic named params
+ ["/v1/{name}", [], {"name": "parent"}, "/v1/parent"],
+ ["/v1/{name=**}", [], {"name": "parent/child"}, "/v1/parent/child"],
+ # Named params with a sub-template
+ ["/v1/{name=parent/*}", [], {"name": "parent/child"}, "/v1/parent/child"],
+ [
+ "/v1/{name=parent/**}",
+ [],
+ {"name": "parent/child/object"},
+ "/v1/parent/child/object",
+ ],
+ # Combining positional and named params
+ ["/v1/*/{name}", ["a"], {"name": "parent"}, "/v1/a/parent"],
+ ["/v1/{name}/*", ["a"], {"name": "parent"}, "/v1/parent/a"],
+ [
+ "/v1/{parent}/*/{child}/*",
+ ["a", "b"],
+ {"parent": "thor", "child": "thorson"},
+ "/v1/thor/a/thorson/b",
+ ],
+ ["/v1/{name}/**", ["a/b"], {"name": "parent"}, "/v1/parent/a/b"],
+ # Combining positional and named params with sub-templates.
+ [
+ "/v1/{name=parent/*}/*",
+ ["a"],
+ {"name": "parent/child"},
+ "/v1/parent/child/a",
+ ],
+ [
+ "/v1/*/{name=parent/**}",
+ ["a"],
+ {"name": "parent/child/object"},
+ "/v1/a/parent/child/object",
+ ],
+ ],
+)
+def test_expand_success(tmpl, args, kwargs, expected_result):
+ result = path_template.expand(tmpl, *args, **kwargs)
+ assert result == expected_result
+ assert path_template.validate(tmpl, result)
+
+
+@pytest.mark.parametrize(
+ "tmpl, args, kwargs, exc_match",
+ [
+ # Missing positional arg.
+ ["v1/*", [], {}, "Positional"],
+ # Missing named arg.
+ ["v1/{name}", [], {}, "Named"],
+ ],
+)
+def test_expanded_failure(tmpl, args, kwargs, exc_match):
+ with pytest.raises(ValueError, match=exc_match):
+ path_template.expand(tmpl, *args, **kwargs)
+
+
+@pytest.mark.parametrize(
+ "request_obj, field, expected_result",
+ [
+ [{"field": "stringValue"}, "field", "stringValue"],
+ [{"field": "stringValue"}, "nosuchfield", None],
+ [{"field": "stringValue"}, "field.subfield", None],
+ [{"field": {"subfield": "stringValue"}}, "field", None],
+ [{"field": {"subfield": "stringValue"}}, "field.subfield", "stringValue"],
+ [{"field": {"subfield": [1, 2, 3]}}, "field.subfield", [1, 2, 3]],
+ [{"field": {"subfield": "stringValue"}}, "field", None],
+ [{"field": {"subfield": "stringValue"}}, "field.nosuchfield", None],
+ [
+ {"field": {"subfield": {"subsubfield": "stringValue"}}},
+ "field.subfield.subsubfield",
+ "stringValue",
+ ],
+ ["string", "field", None],
+ ],
+)
+def test_get_field(request_obj, field, expected_result):
+ result = path_template.get_field(request_obj, field)
+ assert result == expected_result
+
+
+@pytest.mark.parametrize(
+ "request_obj, field, expected_result",
+ [
+ [{"field": "stringValue"}, "field", {}],
+ [{"field": "stringValue"}, "nosuchfield", {"field": "stringValue"}],
+ [{"field": "stringValue"}, "field.subfield", {"field": "stringValue"}],
+ [{"field": {"subfield": "stringValue"}}, "field.subfield", {"field": {}}],
+ [
+ {"field": {"subfield": "stringValue", "q": "w"}, "e": "f"},
+ "field.subfield",
+ {"field": {"q": "w"}, "e": "f"},
+ ],
+ [
+ {"field": {"subfield": "stringValue"}},
+ "field.nosuchfield",
+ {"field": {"subfield": "stringValue"}},
+ ],
+ [
+ {"field": {"subfield": {"subsubfield": "stringValue", "q": "w"}}},
+ "field.subfield.subsubfield",
+ {"field": {"subfield": {"q": "w"}}},
+ ],
+ ["string", "field", "string"],
+ ["string", "field.subfield", "string"],
+ ],
+)
+def test_delete_field(request_obj, field, expected_result):
+ path_template.delete_field(request_obj, field)
+ assert request_obj == expected_result
+
+
+@pytest.mark.parametrize(
+ "tmpl, path",
+ [
+ # Single segment template, but multi segment value
+ ["v1/*", "v1/a/b"],
+ ["v1/*/*", "v1/a/b/c"],
+ # Single segement named template, but multi segment value
+ ["v1/{name}", "v1/a/b"],
+ ["v1/{name}/{value}", "v1/a/b/c"],
+ # Named value with a sub-template but invalid value
+ ["v1/{name=parent/*}", "v1/grandparent/child"],
+ ],
+)
+def test_validate_failure(tmpl, path):
+ assert not path_template.validate(tmpl, path)
+
+
+def test__expand_variable_match_unexpected():
+ match = mock.Mock(spec=["group"])
+ match.group.return_value = None
+ with pytest.raises(ValueError, match="Unknown"):
+ path_template._expand_variable_match([], {}, match)
+
+
+def test__replace_variable_with_pattern():
+ match = mock.Mock(spec=["group"])
+ match.group.return_value = None
+ with pytest.raises(ValueError, match="Unknown"):
+ path_template._replace_variable_with_pattern(match)
+
+
+@pytest.mark.parametrize(
+ "http_options, request_kwargs, expected_result",
+ [
+ [
+ [["get", "/v1/no/template", ""]],
+ {"foo": "bar"},
+ ["get", "/v1/no/template", {}, {"foo": "bar"}],
+ ],
+ # Single templates
+ [
+ [["get", "/v1/{field}", ""]],
+ {"field": "parent"},
+ ["get", "/v1/parent", {}, {}],
+ ],
+ [
+ [["get", "/v1/{field.sub}", ""]],
+ {"field": {"sub": "parent"}, "foo": "bar"},
+ ["get", "/v1/parent", {}, {"field": {}, "foo": "bar"}],
+ ],
+ ],
+)
+def test_transcode_base_case(http_options, request_kwargs, expected_result):
+ http_options, expected_result = helper_test_transcode(http_options, expected_result)
+ result = path_template.transcode(http_options, **request_kwargs)
+ assert result == expected_result
+
+
+@pytest.mark.parametrize(
+ "http_options, request_kwargs, expected_result",
+ [
+ [
+ [["get", "/v1/{field.subfield}", ""]],
+ {"field": {"subfield": "parent"}, "foo": "bar"},
+ ["get", "/v1/parent", {}, {"field": {}, "foo": "bar"}],
+ ],
+ [
+ [["get", "/v1/{field.subfield.subsubfield}", ""]],
+ {"field": {"subfield": {"subsubfield": "parent"}}, "foo": "bar"},
+ ["get", "/v1/parent", {}, {"field": {"subfield": {}}, "foo": "bar"}],
+ ],
+ [
+ [["get", "/v1/{field.subfield1}/{field.subfield2}", ""]],
+ {"field": {"subfield1": "parent", "subfield2": "child"}, "foo": "bar"},
+ ["get", "/v1/parent/child", {}, {"field": {}, "foo": "bar"}],
+ ],
+ ],
+)
+def test_transcode_subfields(http_options, request_kwargs, expected_result):
+ http_options, expected_result = helper_test_transcode(http_options, expected_result)
+ result = path_template.transcode(http_options, **request_kwargs)
+ assert result == expected_result
+
+
+@pytest.mark.parametrize(
+ "http_options, request_kwargs, expected_result",
+ [
+ # Single segment wildcard
+ [
+ [["get", "/v1/{field=*}", ""]],
+ {"field": "parent"},
+ ["get", "/v1/parent", {}, {}],
+ ],
+ [
+ [["get", "/v1/{field=a/*/b/*}", ""]],
+ {"field": "a/parent/b/child", "foo": "bar"},
+ ["get", "/v1/a/parent/b/child", {}, {"foo": "bar"}],
+ ],
+ # Double segment wildcard
+ [
+ [["get", "/v1/{field=**}", ""]],
+ {"field": "parent/p1"},
+ ["get", "/v1/parent/p1", {}, {}],
+ ],
+ [
+ [["get", "/v1/{field=a/**/b/**}", ""]],
+ {"field": "a/parent/p1/b/child/c1", "foo": "bar"},
+ ["get", "/v1/a/parent/p1/b/child/c1", {}, {"foo": "bar"}],
+ ],
+ # Combined single and double segment wildcard
+ [
+ [["get", "/v1/{field=a/*/b/**}", ""]],
+ {"field": "a/parent/b/child/c1"},
+ ["get", "/v1/a/parent/b/child/c1", {}, {}],
+ ],
+ [
+ [["get", "/v1/{field=a/**/b/*}/v2/{name}", ""]],
+ {"field": "a/parent/p1/b/child", "name": "first", "foo": "bar"},
+ ["get", "/v1/a/parent/p1/b/child/v2/first", {}, {"foo": "bar"}],
+ ],
+ ],
+)
+def test_transcode_with_wildcard(http_options, request_kwargs, expected_result):
+ http_options, expected_result = helper_test_transcode(http_options, expected_result)
+ result = path_template.transcode(http_options, **request_kwargs)
+ assert result == expected_result
+
+
+@pytest.mark.parametrize(
+ "http_options, request_kwargs, expected_result",
+ [
+ # Single field body
+ [
+ [["post", "/v1/no/template", "data"]],
+ {"data": {"id": 1, "info": "some info"}, "foo": "bar"},
+ ["post", "/v1/no/template", {"id": 1, "info": "some info"}, {"foo": "bar"}],
+ ],
+ [
+ [["post", "/v1/{field=a/*}/b/{name=**}", "data"]],
+ {
+ "field": "a/parent",
+ "name": "first/last",
+ "data": {"id": 1, "info": "some info"},
+ "foo": "bar",
+ },
+ [
+ "post",
+ "/v1/a/parent/b/first/last",
+ {"id": 1, "info": "some info"},
+ {"foo": "bar"},
+ ],
+ ],
+ # Wildcard body
+ [
+ [["post", "/v1/{field=a/*}/b/{name=**}", "*"]],
+ {
+ "field": "a/parent",
+ "name": "first/last",
+ "data": {"id": 1, "info": "some info"},
+ "foo": "bar",
+ },
+ [
+ "post",
+ "/v1/a/parent/b/first/last",
+ {"data": {"id": 1, "info": "some info"}, "foo": "bar"},
+ {},
+ ],
+ ],
+ ],
+)
+def test_transcode_with_body(http_options, request_kwargs, expected_result):
+ http_options, expected_result = helper_test_transcode(http_options, expected_result)
+ result = path_template.transcode(http_options, **request_kwargs)
+ assert result == expected_result
+
+
+@pytest.mark.parametrize(
+ "http_options, request_kwargs, expected_result",
+ [
+ # Additional bindings
+ [
+ [
+ ["post", "/v1/{field=a/*}/b/{name=**}", "extra_data"],
+ ["post", "/v1/{field=a/*}/b/{name=**}", "*"],
+ ],
+ {
+ "field": "a/parent",
+ "name": "first/last",
+ "data": {"id": 1, "info": "some info"},
+ "foo": "bar",
+ },
+ [
+ "post",
+ "/v1/a/parent/b/first/last",
+ {"data": {"id": 1, "info": "some info"}, "foo": "bar"},
+ {},
+ ],
+ ],
+ [
+ [
+ ["get", "/v1/{field=a/*}/b/{name=**}", ""],
+ ["get", "/v1/{field=a/*}/b/first/last", ""],
+ ],
+ {"field": "a/parent", "foo": "bar"},
+ ["get", "/v1/a/parent/b/first/last", {}, {"foo": "bar"}],
+ ],
+ ],
+)
+def test_transcode_with_additional_bindings(
+ http_options, request_kwargs, expected_result
+):
+ http_options, expected_result = helper_test_transcode(http_options, expected_result)
+ result = path_template.transcode(http_options, **request_kwargs)
+ assert result == expected_result
+
+
+@pytest.mark.parametrize(
+ "http_options, request_kwargs",
+ [
+ [[["get", "/v1/{name}", ""]], {"foo": "bar"}],
+ [[["get", "/v1/{name}", ""]], {"name": "first/last"}],
+ [[["get", "/v1/{name=mr/*/*}", ""]], {"name": "first/last"}],
+ [[["post", "/v1/{name}", "data"]], {"name": "first/last"}],
+ ],
+)
+def test_transcode_fails(http_options, request_kwargs):
+ http_options, _ = helper_test_transcode(http_options, range(4))
+ with pytest.raises(ValueError):
+ path_template.transcode(http_options, **request_kwargs)
+
+
+def helper_test_transcode(http_options_list, expected_result_list):
+ http_options = []
+ for opt_list in http_options_list:
+ http_option = {"method": opt_list[0], "uri": opt_list[1]}
+ if opt_list[2]:
+ http_option["body"] = opt_list[2]
+ http_options.append(http_option)
+
+ expected_result = {
+ "method": expected_result_list[0],
+ "uri": expected_result_list[1],
+ "query_params": expected_result_list[3],
+ }
+ if expected_result_list[2]:
+ expected_result["body"] = expected_result_list[2]
+
+ return (http_options, expected_result)