##// END OF EJS Templates
chore(regex): properly escape \d used in regexes
super-admin -
r5187:51a848c7 default
parent child Browse files
Show More
@@ -1,240 +1,240 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import copy
21 21 import mock
22 22 import pytest
23 23
24 24 from rhodecode.lib import helpers
25 25 from rhodecode.lib.utils2 import AttributeDict
26 26 from rhodecode.model.settings import IssueTrackerSettingsModel
27 27 from rhodecode.tests import no_newline_id_generator
28 28
29 29
30 30 @pytest.mark.parametrize('url, expected_url', [
31 31 (r'https://rc.com', '<a href="https://rc.com">https://rc.com</a>'),
32 32 (r'https://rc.com/test', '<a href="https://rc.com/test">https://rc.com/test</a>'),
33 33 (r'https://rc.com/!foo', '<a href="https://rc.com/!foo">https://rc.com/!foo</a>'),
34 34 (r'https://rc.com/&foo', '<a href="https://rc.com/&amp;foo">https://rc.com/&amp;foo</a>'),
35 35 (r'https://rc.com/?foo-1&bar=1', '<a href="https://rc.com/?foo-1&amp;bar=1">https://rc.com/?foo-1&amp;bar=1</a>'),
36 36 (r'https://rc.com?foo-1&bar=1', '<a href="https://rc.com?foo-1&amp;bar=1">https://rc.com?foo-1&amp;bar=1</a>'),
37 37 (r'https://rc.com/#foo', '<a href="https://rc.com/#foo">https://rc.com/#foo</a>'),
38 38 (r'https://rc.com/@foo', '<a href="https://rc.com/@foo">https://rc.com/@foo</a>'),
39 39 ])
40 40 def test_urlify_text(url, expected_url):
41 41 assert helpers.urlify_text(url) == expected_url
42 42
43 43
44 44 @pytest.mark.parametrize('repo_name, commit_id, path, expected_result', [
45 45 # Simple case 1
46 46 ('repo', 'commit', 'a/b',
47 47 '<a href="/repo/files/commit/"><i class="icon-home"></i></a>'
48 48 ' / '
49 49 '<a href="/repo/files/commit/a">a</a>'
50 50 ' / '
51 51 'b'),
52 52
53 53 # Simple case
54 54 ('rX<X', 'cX<X', 'pX<X/aX<X/bX<X',
55 55 '<a href="/rX%3CX/files/cX%3CX/"><i class="icon-home"></i></a>'
56 56 ' / '
57 57 '<a href="/rX%3CX/files/cX%3CX/pX%3CX">pX&lt;X</a>'
58 58 ' / '
59 59 '<a href="/rX%3CX/files/cX%3CX/pX%3CX/aX%3CX">aX&lt;X</a>'
60 60 ' / '
61 61 'bX&lt;X'),
62 62
63 63 # Path with only one segment
64 64 ('rX<X', 'cX<X', 'pX<X',
65 65 '<a href="/rX%3CX/files/cX%3CX/"><i class="icon-home"></i></a>'
66 66 ' / '
67 67 'pX&lt;X'),
68 68
69 69 # Empty path
70 70 ('rX<X', 'cX<X', '',
71 71 '<i class="icon-home"></i>'),
72 72
73 73 # simple quote
74 74 ('rX"X', 'cX"X', 'pX"X/aX"X/bX"X',
75 75 '<a href="/rX%22X/files/cX%22X/"><i class="icon-home"></i></a>'
76 76 ' / '
77 77 '<a href="/rX%22X/files/cX%22X/pX%22X">pX&#34;X</a>'
78 78 ' / '
79 79 '<a href="/rX%22X/files/cX%22X/pX%22X/aX%22X">aX&#34;X</a>'
80 80 ' / '
81 81 'bX&#34;X'),
82 82
83 83 ], ids=['simple1', 'simple2', 'one_segment', 'empty_path', 'simple_quote'])
84 84 def test_files_breadcrumbs_xss(repo_name, commit_id, path, app, expected_result):
85 85 result = helpers.files_breadcrumbs(repo_name, 'hg', commit_id, path)
86 86 # Expect it to encode all path fragments properly. This is important
87 87 # because it returns an instance of `literal`.
88 88 if path != '':
89 89 expected_result = expected_result + helpers.files_icon.format(helpers.escape(path))
90 90 assert result == expected_result
91 91
92 92
93 93 def test_format_binary():
94 94 assert helpers.format_byte_size_binary(298489462784) == '278.0 GiB'
95 95
96 96
97 97 @pytest.mark.parametrize('text_string, pattern, expected', [
98 98 ('No issue here', r'(?:#)(?P<issue_id>\d+)', []),
99 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
99 ('Fix #42', r'(?:#)(?P<issue_id>\d+)',
100 100 [{'url': 'https://r.io/{repo}/i/42', 'id': '42'}]),
101 ('Fix #42, #53', '(?:#)(?P<issue_id>\d+)', [
101 ('Fix #42, #53', r'(?:#)(?P<issue_id>\d+)', [
102 102 {'url': 'https://r.io/{repo}/i/42', 'id': '42'},
103 103 {'url': 'https://r.io/{repo}/i/53', 'id': '53'}]),
104 ('Fix #42', '(?:#)?<issue_id>\d+)', []), # Broken regex
104 ('Fix #42', r'(?:#)?<issue_id>\d+)', []), # Broken regex
105 105 ])
106 106 def test_extract_issues(backend, text_string, pattern, expected):
107 107 repo = backend.create_repo()
108 108 config = {
109 109 '123': {
110 110 'uid': '123',
111 111 'pat': pattern,
112 112 'url': r'https://r.io/${repo}/i/${issue_id}',
113 113 'pref': '#',
114 114 'desc': 'Test Pattern'
115 115 }
116 116 }
117 117
118 118 def get_settings_mock(self, cache=True):
119 119 return config
120 120
121 121 with mock.patch.object(IssueTrackerSettingsModel,
122 122 'get_settings', get_settings_mock):
123 123 text, issues, errors = helpers.process_patterns(text_string, repo.repo_name)
124 124
125 125 expected = copy.deepcopy(expected)
126 126 for item in expected:
127 127 item['url'] = item['url'].format(repo=repo.repo_name)
128 128
129 129 assert issues == expected
130 130
131 131
132 132 @pytest.mark.parametrize('text_string, pattern, link_format, expected_text', [
133 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'html',
133 ('Fix #42', r'(?:#)(?P<issue_id>\d+)', 'html',
134 134 'Fix <a class="tooltip issue-tracker-link" href="https://r.io/{repo}/i/42" title="Test Pattern">#42</a>'),
135 135
136 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'markdown',
136 ('Fix #42', r'(?:#)(?P<issue_id>\d+)', 'markdown',
137 137 'Fix [#42](https://r.io/{repo}/i/42)'),
138 138
139 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'rst',
139 ('Fix #42', r'(?:#)(?P<issue_id>\d+)', 'rst',
140 140 'Fix `#42 <https://r.io/{repo}/i/42>`_'),
141 141
142 ('Fix #42', '(?:#)?<issue_id>\d+)', 'html',
142 ('Fix #42', r'(?:#)?<issue_id>\d+)', 'html',
143 143 'Fix #42'), # Broken regex
144 144 ])
145 145 def test_process_patterns_repo(backend, text_string, pattern, expected_text, link_format):
146 146 repo = backend.create_repo()
147 147
148 148 def get_settings_mock(self, cache=True):
149 149 return {
150 150 '123': {
151 151 'uid': '123',
152 152 'pat': pattern,
153 153 'url': 'https://r.io/${repo}/i/${issue_id}',
154 154 'pref': '#',
155 155 'desc': 'Test Pattern'
156 156 }
157 157 }
158 158
159 159 with mock.patch.object(IssueTrackerSettingsModel,
160 160 'get_settings', get_settings_mock):
161 161 processed_text, issues, error = helpers.process_patterns(
162 162 text_string, repo.repo_name, link_format)
163 163
164 164 assert processed_text == expected_text.format(repo=repo.repo_name)
165 165
166 166
167 167 @pytest.mark.parametrize('text_string, pattern, expected_text', [
168 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
168 ('Fix #42', r'(?:#)(?P<issue_id>\d+)',
169 169 'Fix <a class="tooltip issue-tracker-link" href="https://r.io/i/42" title="Test Pattern">#42</a>'),
170 ('Fix #42', '(?:#)?<issue_id>\d+)',
170 ('Fix #42', r'(?:#)?<issue_id>\d+)',
171 171 'Fix #42'), # Broken regex
172 172 ])
173 173 def test_process_patterns_no_repo(text_string, pattern, expected_text):
174 174
175 175 def get_settings_mock(self, cache=True):
176 176 return {
177 177 '123': {
178 178 'uid': '123',
179 179 'pat': pattern,
180 180 'url': 'https://r.io/i/${issue_id}',
181 181 'pref': '#',
182 182 'desc': 'Test Pattern'
183 183 }
184 184 }
185 185
186 186 with mock.patch.object(IssueTrackerSettingsModel,
187 187 'get_global_settings', get_settings_mock):
188 188 processed_text, issues, errors = helpers.process_patterns(
189 189 text_string, '')
190 190
191 191 assert processed_text == expected_text
192 192
193 193
194 194 def test_process_patterns_non_existent_repo_name(backend):
195 195 text_string = 'Fix #42'
196 196 pattern = r'(?:#)(?P<issue_id>\d+)'
197 197 expected_text = ('Fix <a class="tooltip issue-tracker-link" '
198 198 'href="https://r.io/do-not-exist/i/42" title="Test Pattern">#42</a>')
199 199
200 200 def get_settings_mock(self, cache=True):
201 201 return {
202 202 '123': {
203 203 'uid': '123',
204 204 'pat': pattern,
205 205 'url': 'https://r.io/${repo}/i/${issue_id}',
206 206 'pref': '#',
207 207 'desc': 'Test Pattern'
208 208 }
209 209 }
210 210
211 211 with mock.patch.object(IssueTrackerSettingsModel,
212 212 'get_global_settings', get_settings_mock):
213 213 processed_text, issues, errors = helpers.process_patterns(
214 214 text_string, 'do-not-exist')
215 215
216 216 assert processed_text == expected_text
217 217
218 218
219 219 def test_get_visual_attr(baseapp):
220 220 from rhodecode.apps._base import TemplateArgs
221 221 c = TemplateArgs()
222 222 assert None is helpers.get_visual_attr(c, 'fakse')
223 223
224 224 # emulate the c.visual behaviour
225 225 c.visual = AttributeDict({})
226 226 assert None is helpers.get_visual_attr(c, 'some_var')
227 227
228 228 c.visual.some_var = 'foobar'
229 229 assert 'foobar' == helpers.get_visual_attr(c, 'some_var')
230 230
231 231
232 232 @pytest.mark.parametrize('test_text, inclusive, expected_text', [
233 233 ('just a string', False, 'just a string'),
234 234 ('just a string\n', False, 'just a string'),
235 235 ('just a string\n next line', False, 'just a string...'),
236 236 ('just a string\n next line', True, 'just a string\n...'),
237 237 ], ids=no_newline_id_generator)
238 238 def test_chop_at(test_text, inclusive, expected_text):
239 239 assert helpers.chop_at_smart(
240 240 test_text, '\n', inclusive, '...') == expected_text
General Comments 0
You need to be logged in to leave comments. Login now