openai/openai-python

Public

mirrored from https://github.com/openai/openai-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.31.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

tests/test_utils/test_path.py

89lines · modeblame

9445eac0stainless-app[bot]3 months ago1from __future__ import annotations
2
3from typing import Any
4
5import pytest
6
7from 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}",
30dict(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}",
52dict(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)
59def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None:
60assert path_template(template, **kwargs) == expected
61
62
63def test_missing_kwarg_raises_key_error() -> None:
64with pytest.raises(KeyError, match="org_id"):
65path_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)
87def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None:
88with pytest.raises(ValueError, match="dot-segment"):
89path_template(template, **kwargs)