openai/openai-python
Publicmirrored from https://github.com/openai/openai-pythonAvailable
tests/test_utils/test_path.py
89lines · modeblame
9445eac0stainless-app[bot]3 months ago | 1 | from __future__ import annotations |
| 2 | | |
| 3 | from typing import Any | |
| 4 | | |
| 5 | import pytest | |
| 6 | | |
| 7 | from openai._utils._path import path_template | |
| 8 | | |
| 9 | | |
| 10 | @pytest.mark.parametrize( | |
| 11 | "template, kwargs, expected", | |
| 12 | [ | |
| 13 | ("/v1/{id}", dict(id="abc"), "/v1/abc"), | |
| 14 | ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"), | |
| 15 | ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"), | |
| 16 | ("/{w}/{w}", dict(w="echo"), "/echo/echo"), | |
| 17 | ("/v1/static", {}, "/v1/static"), | |
| 18 | ("", {}, ""), | |
| 19 | ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"), | |
| 20 | ("/v1/{v}", dict(v=None), "/v1/null"), | |
| 21 | ("/v1/{v}", dict(v=True), "/v1/true"), | |
| 22 | ("/v1/{v}", dict(v=False), "/v1/false"), | |
| 23 | ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok | |
| 24 | ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok | |
| 25 | ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok | |
| 26 | ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok | |
| 27 | ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine | |
| 28 | ( | |
| 29 | "/v1/{a}?query={b}", | |
| 30 | dict(a="../../other/endpoint", b="a&bad=true"), | |
| 31 | "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue", | |
| 32 | ), | |
| 33 | ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"), | |
| 34 | ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"), | |
| 35 | ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"), | |
| 36 | ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input | |
| 37 | # Query: slash and ? are safe, # is not | |
| 38 | ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"), | |
| 39 | ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"), | |
| 40 | ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"), | |
| 41 | ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"), | |
| 42 | # Fragment: slash and ? are safe | |
| 43 | ("/docs#{v}", dict(v="a/b"), "/docs#a/b"), | |
| 44 | ("/docs#{v}", dict(v="a?b"), "/docs#a?b"), | |
| 45 | # Path: slash, ? and # are all encoded | |
| 46 | ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"), | |
| 47 | ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"), | |
| 48 | ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"), | |
| 49 | # same var encoded differently by component | |
| 50 | ( | |
| 51 | "/v1/{v}?q={v}#{v}", | |
| 52 | dict(v="a/b?c#d"), | |
| 53 | "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d", | |
| 54 | ), | |
| 55 | ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection | |
| 56 | ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection | |
| 57 | ], | |
| 58 | ) | |
| 59 | def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None: | |
| 60 | assert path_template(template, **kwargs) == expected | |
| 61 | | |
| 62 | | |
| 63 | def test_missing_kwarg_raises_key_error() -> None: | |
| 64 | with pytest.raises(KeyError, match="org_id"): | |
| 65 | path_template("/v1/{org_id}") | |
| 66 | | |
| 67 | | |
| 68 | @pytest.mark.parametrize( | |
| 69 | "template, kwargs", | |
| 70 | [ | |
| 71 | ("{a}/path", dict(a=".")), | |
| 72 | ("{a}/path", dict(a="..")), | |
| 73 | ("/v1/{a}", dict(a=".")), | |
| 74 | ("/v1/{a}", dict(a="..")), | |
| 75 | ("/v1/{a}/path", dict(a=".")), | |
| 76 | ("/v1/{a}/path", dict(a="..")), | |
| 77 | ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".." | |
| 78 | ("/v1/{a}.", dict(a=".")), # var + static → ".." | |
| 79 | ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "." | |
| 80 | ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text | |
| 81 | ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static | |
| 82 | ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static | |
| 83 | ("/v1/{v}?q=1", dict(v="..")), | |
| 84 | ("/v1/{v}#frag", dict(v="..")), | |
| 85 | ], | |
| 86 | ) | |
| 87 | def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None: | |
| 88 | with pytest.raises(ValueError, match="dot-segment"): | |
| 89 | path_template(template, **kwargs) |