|
3 | 3 | import pytest
|
4 | 4 | import django
|
5 | 5 |
|
| 6 | +try: |
| 7 | + from unittest import mock # python 3.3 and above |
| 8 | +except ImportError: |
| 9 | + import mock # python < 3.3 |
| 10 | + |
| 11 | + |
| 12 | +# django<2.0 has only `url` with regex based patterns. |
| 13 | +# django>=2.0 renames `url` to `re_path`, and additionally introduces `path` |
| 14 | +# for new style URL patterns, e.g. <int:article_id>. |
6 | 15 | if django.VERSION >= (2, 0):
|
7 |
| - # TODO: once we stop supporting django < 2, use the real name of this |
8 |
| - # function (re_path) |
9 |
| - from django.urls import re_path as url |
| 16 | + from django.urls import path, re_path |
| 17 | + from django.urls.converters import PathConverter |
10 | 18 | from django.conf.urls import include
|
11 | 19 | else:
|
12 |
| - from django.conf.urls import url, include |
| 20 | + from django.conf.urls import url as re_path, include |
13 | 21 |
|
14 | 22 | if django.VERSION < (1, 9):
|
15 |
| - included_url_conf = (url(r"^foo/bar/(?P<param>[\w]+)", lambda x: ""),), "", "" |
| 23 | + included_url_conf = (re_path(r"^foo/bar/(?P<param>[\w]+)", lambda x: ""),), "", "" |
16 | 24 | else:
|
17 |
| - included_url_conf = ((url(r"^foo/bar/(?P<param>[\w]+)", lambda x: ""),), "") |
| 25 | + included_url_conf = ((re_path(r"^foo/bar/(?P<param>[\w]+)", lambda x: ""),), "") |
18 | 26 |
|
19 | 27 | from sentry_sdk.integrations.django.transactions import RavenResolver
|
20 | 28 |
|
21 | 29 |
|
22 | 30 | example_url_conf = (
|
23 |
| - url(r"^api/(?P<project_id>[\w_-]+)/store/$", lambda x: ""), |
24 |
| - url(r"^api/(?P<version>(v1|v2))/author/$", lambda x: ""), |
25 |
| - url( |
| 31 | + re_path(r"^api/(?P<project_id>[\w_-]+)/store/$", lambda x: ""), |
| 32 | + re_path(r"^api/(?P<version>(v1|v2))/author/$", lambda x: ""), |
| 33 | + re_path( |
26 | 34 | r"^api/(?P<project_id>[^\/]+)/product/(?P<pid>(?:\d+|[A-Fa-f0-9-]{32,36}))/$",
|
27 | 35 | lambda x: "",
|
28 | 36 | ),
|
29 |
| - url(r"^report/", lambda x: ""), |
30 |
| - url(r"^example/", include(included_url_conf)), |
| 37 | + re_path(r"^report/", lambda x: ""), |
| 38 | + re_path(r"^example/", include(included_url_conf)), |
31 | 39 | )
|
32 | 40 |
|
33 | 41 |
|
34 |
| -def test_legacy_resolver_no_match(): |
| 42 | +def test_resolver_no_match(): |
35 | 43 | resolver = RavenResolver()
|
36 | 44 | result = resolver.resolve("/foo/bar", example_url_conf)
|
37 | 45 | assert result is None
|
38 | 46 |
|
39 | 47 |
|
40 |
| -def test_legacy_resolver_complex_match(): |
| 48 | +def test_resolver_re_path_complex_match(): |
41 | 49 | resolver = RavenResolver()
|
42 | 50 | result = resolver.resolve("/api/1234/store/", example_url_conf)
|
43 | 51 | assert result == "/api/{project_id}/store/"
|
44 | 52 |
|
45 | 53 |
|
46 |
| -def test_legacy_resolver_complex_either_match(): |
| 54 | +def test_resolver_re_path_complex_either_match(): |
47 | 55 | resolver = RavenResolver()
|
48 | 56 | result = resolver.resolve("/api/v1/author/", example_url_conf)
|
49 | 57 | assert result == "/api/{version}/author/"
|
50 | 58 | result = resolver.resolve("/api/v2/author/", example_url_conf)
|
51 | 59 | assert result == "/api/{version}/author/"
|
52 | 60 |
|
53 | 61 |
|
54 |
| -def test_legacy_resolver_included_match(): |
| 62 | +def test_resolver_re_path_included_match(): |
55 | 63 | resolver = RavenResolver()
|
56 | 64 | result = resolver.resolve("/example/foo/bar/baz", example_url_conf)
|
57 | 65 | assert result == "/example/foo/bar/{param}"
|
58 | 66 |
|
59 | 67 |
|
60 |
| -def test_capture_multiple_named_groups(): |
| 68 | +def test_resolver_re_path_multiple_groups(): |
61 | 69 | resolver = RavenResolver()
|
62 | 70 | result = resolver.resolve(
|
63 | 71 | "/api/myproject/product/cb4ef1caf3554c34ae134f3c1b3d605f/", example_url_conf
|
64 | 72 | )
|
65 | 73 | assert result == "/api/{project_id}/product/{pid}/"
|
66 | 74 |
|
67 | 75 |
|
68 |
| -@pytest.mark.skipif(django.VERSION < (2, 0), reason="Requires Django > 2.0") |
69 |
| -def test_legacy_resolver_newstyle_django20_urlconf(): |
70 |
| - from django.urls import path |
71 |
| - |
| 76 | +@pytest.mark.skipif( |
| 77 | + django.VERSION < (2, 0), |
| 78 | + reason="Django>=2.0 required for <converter:parameter> patterns", |
| 79 | +) |
| 80 | +def test_resolver_path_group(): |
72 | 81 | url_conf = (path("api/v2/<int:project_id>/store/", lambda x: ""),)
|
73 | 82 | resolver = RavenResolver()
|
74 | 83 | result = resolver.resolve("/api/v2/1234/store/", url_conf)
|
75 | 84 | assert result == "/api/v2/{project_id}/store/"
|
76 | 85 |
|
77 | 86 |
|
78 |
| -@pytest.mark.skipif(django.VERSION < (2, 0), reason="Requires Django > 2.0") |
79 |
| -def test_legacy_resolver_newstyle_django20_urlconf_multiple_groups(): |
80 |
| - from django.urls import path |
81 |
| - |
82 |
| - url_conf = (path("api/v2/<int:project_id>/product/<int:pid>", lambda x: ""),) |
| 87 | +@pytest.mark.skipif( |
| 88 | + django.VERSION < (2, 0), |
| 89 | + reason="Django>=2.0 required for <converter:parameter> patterns", |
| 90 | +) |
| 91 | +def test_resolver_path_multiple_groups(): |
| 92 | + url_conf = (path("api/v2/<str:project_id>/product/<int:pid>", lambda x: ""),) |
83 | 93 | resolver = RavenResolver()
|
84 |
| - result = resolver.resolve("/api/v2/1234/product/5689", url_conf) |
| 94 | + result = resolver.resolve("/api/v2/myproject/product/5689", url_conf) |
85 | 95 | assert result == "/api/v2/{project_id}/product/{pid}"
|
| 96 | + |
| 97 | + |
| 98 | +@pytest.mark.skipif( |
| 99 | + django.VERSION < (2, 0), |
| 100 | + reason="Django>=2.0 required for <converter:parameter> patterns", |
| 101 | +) |
| 102 | +def test_resolver_path_complex_path(): |
| 103 | + class CustomPathConverter(PathConverter): |
| 104 | + regex = r"[^/]+(/[^/]+){0,2}" |
| 105 | + |
| 106 | + with mock.patch( |
| 107 | + "django.urls.resolvers.get_converter", return_value=CustomPathConverter |
| 108 | + ): |
| 109 | + url_conf = (path("api/v3/<custom_path:my_path>", lambda x: ""),) |
| 110 | + resolver = RavenResolver() |
| 111 | + result = resolver.resolve("/api/v3/abc/def/ghi", url_conf) |
| 112 | + assert result == "/api/v3/{my_path}" |
| 113 | + |
| 114 | + |
| 115 | +@pytest.mark.skipif( |
| 116 | + django.VERSION < (2, 0), |
| 117 | + reason="Django>=2.0 required for <converter:parameter> patterns", |
| 118 | +) |
| 119 | +def test_resolver_path_no_converter(): |
| 120 | + url_conf = (path("api/v4/<project_id>", lambda x: ""),) |
| 121 | + resolver = RavenResolver() |
| 122 | + result = resolver.resolve("/api/v4/myproject", url_conf) |
| 123 | + assert result == "/api/v4/{project_id}" |
0 commit comments