##// END OF EJS Templates
summary: use non-memory cache for readme, and cleanup cache for repo stats.
marcink -
r3892:8afbef06 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,376 +1,374 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import string
22 import string
23 import time
23 import time
24
24
25 import rhodecode
25 import rhodecode
26
26
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28
28
29 from rhodecode.lib.view_utils import get_format_ref_id
29 from rhodecode.lib.view_utils import get_format_ref_id
30 from rhodecode.apps._base import RepoAppView
30 from rhodecode.apps._base import RepoAppView
31 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
31 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
32 from rhodecode.lib import helpers as h, rc_cache
32 from rhodecode.lib import helpers as h, rc_cache
33 from rhodecode.lib.utils2 import safe_str, safe_int
33 from rhodecode.lib.utils2 import safe_str, safe_int
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
35 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
36 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.ext_json import json
37 from rhodecode.lib.vcs.backends.base import EmptyCommit
37 from rhodecode.lib.vcs.backends.base import EmptyCommit
38 from rhodecode.lib.vcs.exceptions import (
38 from rhodecode.lib.vcs.exceptions import (
39 CommitError, EmptyRepositoryError, CommitDoesNotExistError)
39 CommitError, EmptyRepositoryError, CommitDoesNotExistError)
40 from rhodecode.model.db import Statistics, CacheKey, User
40 from rhodecode.model.db import Statistics, CacheKey, User
41 from rhodecode.model.meta import Session
41 from rhodecode.model.meta import Session
42 from rhodecode.model.repo import ReadmeFinder
42 from rhodecode.model.repo import ReadmeFinder
43 from rhodecode.model.scm import ScmModel
43 from rhodecode.model.scm import ScmModel
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class RepoSummaryView(RepoAppView):
48 class RepoSummaryView(RepoAppView):
49
49
50 def load_default_context(self):
50 def load_default_context(self):
51 c = self._get_local_tmpl_context(include_app_defaults=True)
51 c = self._get_local_tmpl_context(include_app_defaults=True)
52 c.rhodecode_repo = None
52 c.rhodecode_repo = None
53 if not c.repository_requirements_missing:
53 if not c.repository_requirements_missing:
54 c.rhodecode_repo = self.rhodecode_vcs_repo
54 c.rhodecode_repo = self.rhodecode_vcs_repo
55 return c
55 return c
56
56
57 def _get_readme_data(self, db_repo, renderer_type):
57 def _get_readme_data(self, db_repo, renderer_type):
58 log.debug('Looking for README file')
58 log.debug('Looking for README file')
59 landing_commit = db_repo.get_landing_commit()
59 landing_commit = db_repo.get_landing_commit()
60 if isinstance(landing_commit, EmptyCommit):
60 if isinstance(landing_commit, EmptyCommit):
61 return None, None
61 return None, None
62
62
63 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
63 cache_namespace_uid = 'cache_repo.{}'.format(db_repo.repo_id)
64 db_repo.repo_id, CacheKey.CACHE_TYPE_README)
64 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
65 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
66 start = time.time()
65 start = time.time()
67
66
68 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
67 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
69 def generate_repo_readme(repo_id, commit_id, _repo_name, _renderer_type):
68 def generate_repo_readme(repo_id, commit_id, _repo_name, _renderer_type):
70 readme_data = None
69 readme_data = None
71 readme_filename = None
70 readme_filename = None
72
71
73 commit = db_repo.get_commit(commit_id)
72 commit = db_repo.get_commit(commit_id)
74 log.debug("Searching for a README file at commit %s.", commit_id)
73 log.debug("Searching for a README file at commit %s.", commit_id)
75 readme_node = ReadmeFinder(_renderer_type).search(commit)
74 readme_node = ReadmeFinder(_renderer_type).search(commit)
76
75
77 if readme_node:
76 if readme_node:
78 log.debug('Found README node: %s', readme_node)
77 log.debug('Found README node: %s', readme_node)
79 relative_urls = {
78 relative_urls = {
80 'raw': h.route_path(
79 'raw': h.route_path(
81 'repo_file_raw', repo_name=_repo_name,
80 'repo_file_raw', repo_name=_repo_name,
82 commit_id=commit.raw_id, f_path=readme_node.path),
81 commit_id=commit.raw_id, f_path=readme_node.path),
83 'standard': h.route_path(
82 'standard': h.route_path(
84 'repo_files', repo_name=_repo_name,
83 'repo_files', repo_name=_repo_name,
85 commit_id=commit.raw_id, f_path=readme_node.path),
84 commit_id=commit.raw_id, f_path=readme_node.path),
86 }
85 }
87 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
86 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
88 readme_filename = readme_node.unicode_path
87 readme_filename = readme_node.unicode_path
89
88
90 return readme_data, readme_filename
89 return readme_data, readme_filename
91
90
92 readme_data, readme_filename = generate_repo_readme(
91 readme_data, readme_filename = generate_repo_readme(
93 db_repo.repo_id, landing_commit.raw_id, db_repo.repo_name, renderer_type,)
92 db_repo.repo_id, landing_commit.raw_id, db_repo.repo_name, renderer_type,)
94 compute_time = time.time() - start
93 compute_time = time.time() - start
95 log.debug('Repo readme generated and computed in %.4fs', compute_time)
94 log.debug('Repo readme generated and computed in %.4fs', compute_time)
96 return readme_data, readme_filename
95 return readme_data, readme_filename
97
96
98 def _render_readme_or_none(self, commit, readme_node, relative_urls):
97 def _render_readme_or_none(self, commit, readme_node, relative_urls):
99 log.debug('Found README file `%s` rendering...', readme_node.path)
98 log.debug('Found README file `%s` rendering...', readme_node.path)
100 renderer = MarkupRenderer()
99 renderer = MarkupRenderer()
101 try:
100 try:
102 html_source = renderer.render(
101 html_source = renderer.render(
103 readme_node.content, filename=readme_node.path)
102 readme_node.content, filename=readme_node.path)
104 if relative_urls:
103 if relative_urls:
105 return relative_links(html_source, relative_urls)
104 return relative_links(html_source, relative_urls)
106 return html_source
105 return html_source
107 except Exception:
106 except Exception:
108 log.exception(
107 log.exception(
109 "Exception while trying to render the README")
108 "Exception while trying to render the README")
110
109
111 def _load_commits_context(self, c):
110 def _load_commits_context(self, c):
112 p = safe_int(self.request.GET.get('page'), 1)
111 p = safe_int(self.request.GET.get('page'), 1)
113 size = safe_int(self.request.GET.get('size'), 10)
112 size = safe_int(self.request.GET.get('size'), 10)
114
113
115 def url_generator(**kw):
114 def url_generator(**kw):
116 query_params = {
115 query_params = {
117 'size': size
116 'size': size
118 }
117 }
119 query_params.update(kw)
118 query_params.update(kw)
120 return h.route_path(
119 return h.route_path(
121 'repo_summary_commits',
120 'repo_summary_commits',
122 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
121 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
123
122
124 pre_load = ['author', 'branch', 'date', 'message']
123 pre_load = ['author', 'branch', 'date', 'message']
125 try:
124 try:
126 collection = self.rhodecode_vcs_repo.get_commits(
125 collection = self.rhodecode_vcs_repo.get_commits(
127 pre_load=pre_load, translate_tags=False)
126 pre_load=pre_load, translate_tags=False)
128 except EmptyRepositoryError:
127 except EmptyRepositoryError:
129 collection = self.rhodecode_vcs_repo
128 collection = self.rhodecode_vcs_repo
130
129
131 c.repo_commits = h.RepoPage(
130 c.repo_commits = h.RepoPage(
132 collection, page=p, items_per_page=size, url=url_generator)
131 collection, page=p, items_per_page=size, url=url_generator)
133 page_ids = [x.raw_id for x in c.repo_commits]
132 page_ids = [x.raw_id for x in c.repo_commits]
134 c.comments = self.db_repo.get_comments(page_ids)
133 c.comments = self.db_repo.get_comments(page_ids)
135 c.statuses = self.db_repo.statuses(page_ids)
134 c.statuses = self.db_repo.statuses(page_ids)
136
135
137 def _prepare_and_set_clone_url(self, c):
136 def _prepare_and_set_clone_url(self, c):
138 username = ''
137 username = ''
139 if self._rhodecode_user.username != User.DEFAULT_USER:
138 if self._rhodecode_user.username != User.DEFAULT_USER:
140 username = safe_str(self._rhodecode_user.username)
139 username = safe_str(self._rhodecode_user.username)
141
140
142 _def_clone_uri = _def_clone_uri_id = c.clone_uri_tmpl
141 _def_clone_uri = _def_clone_uri_id = c.clone_uri_tmpl
143 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
142 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
144
143
145 if '{repo}' in _def_clone_uri:
144 if '{repo}' in _def_clone_uri:
146 _def_clone_uri_id = _def_clone_uri.replace('{repo}', '_{repoid}')
145 _def_clone_uri_id = _def_clone_uri.replace('{repo}', '_{repoid}')
147 elif '{repoid}' in _def_clone_uri:
146 elif '{repoid}' in _def_clone_uri:
148 _def_clone_uri_id = _def_clone_uri.replace('_{repoid}', '{repo}')
147 _def_clone_uri_id = _def_clone_uri.replace('_{repoid}', '{repo}')
149
148
150 c.clone_repo_url = self.db_repo.clone_url(
149 c.clone_repo_url = self.db_repo.clone_url(
151 user=username, uri_tmpl=_def_clone_uri)
150 user=username, uri_tmpl=_def_clone_uri)
152 c.clone_repo_url_id = self.db_repo.clone_url(
151 c.clone_repo_url_id = self.db_repo.clone_url(
153 user=username, uri_tmpl=_def_clone_uri_id)
152 user=username, uri_tmpl=_def_clone_uri_id)
154 c.clone_repo_url_ssh = self.db_repo.clone_url(
153 c.clone_repo_url_ssh = self.db_repo.clone_url(
155 uri_tmpl=_def_clone_uri_ssh, ssh=True)
154 uri_tmpl=_def_clone_uri_ssh, ssh=True)
156
155
157 @LoginRequired()
156 @LoginRequired()
158 @HasRepoPermissionAnyDecorator(
157 @HasRepoPermissionAnyDecorator(
159 'repository.read', 'repository.write', 'repository.admin')
158 'repository.read', 'repository.write', 'repository.admin')
160 @view_config(
159 @view_config(
161 route_name='repo_summary_commits', request_method='GET',
160 route_name='repo_summary_commits', request_method='GET',
162 renderer='rhodecode:templates/summary/summary_commits.mako')
161 renderer='rhodecode:templates/summary/summary_commits.mako')
163 def summary_commits(self):
162 def summary_commits(self):
164 c = self.load_default_context()
163 c = self.load_default_context()
165 self._prepare_and_set_clone_url(c)
164 self._prepare_and_set_clone_url(c)
166 self._load_commits_context(c)
165 self._load_commits_context(c)
167 return self._get_template_context(c)
166 return self._get_template_context(c)
168
167
169 @LoginRequired()
168 @LoginRequired()
170 @HasRepoPermissionAnyDecorator(
169 @HasRepoPermissionAnyDecorator(
171 'repository.read', 'repository.write', 'repository.admin')
170 'repository.read', 'repository.write', 'repository.admin')
172 @view_config(
171 @view_config(
173 route_name='repo_summary', request_method='GET',
172 route_name='repo_summary', request_method='GET',
174 renderer='rhodecode:templates/summary/summary.mako')
173 renderer='rhodecode:templates/summary/summary.mako')
175 @view_config(
174 @view_config(
176 route_name='repo_summary_slash', request_method='GET',
175 route_name='repo_summary_slash', request_method='GET',
177 renderer='rhodecode:templates/summary/summary.mako')
176 renderer='rhodecode:templates/summary/summary.mako')
178 @view_config(
177 @view_config(
179 route_name='repo_summary_explicit', request_method='GET',
178 route_name='repo_summary_explicit', request_method='GET',
180 renderer='rhodecode:templates/summary/summary.mako')
179 renderer='rhodecode:templates/summary/summary.mako')
181 def summary(self):
180 def summary(self):
182 c = self.load_default_context()
181 c = self.load_default_context()
183
182
184 # Prepare the clone URL
183 # Prepare the clone URL
185 self._prepare_and_set_clone_url(c)
184 self._prepare_and_set_clone_url(c)
186
185
187 # update every 5 min
186 # update every 5 min
188 if self.db_repo.last_commit_cache_update_diff > 60 * 5:
187 if self.db_repo.last_commit_cache_update_diff > 60 * 5:
189 self.db_repo.update_commit_cache()
188 self.db_repo.update_commit_cache()
190
189
191 # If enabled, get statistics data
190 # If enabled, get statistics data
192
191
193 c.show_stats = bool(self.db_repo.enable_statistics)
192 c.show_stats = bool(self.db_repo.enable_statistics)
194
193
195 stats = Session().query(Statistics) \
194 stats = Session().query(Statistics) \
196 .filter(Statistics.repository == self.db_repo) \
195 .filter(Statistics.repository == self.db_repo) \
197 .scalar()
196 .scalar()
198
197
199 c.stats_percentage = 0
198 c.stats_percentage = 0
200
199
201 if stats and stats.languages:
200 if stats and stats.languages:
202 c.no_data = False is self.db_repo.enable_statistics
201 c.no_data = False is self.db_repo.enable_statistics
203 lang_stats_d = json.loads(stats.languages)
202 lang_stats_d = json.loads(stats.languages)
204
203
205 # Sort first by decreasing count and second by the file extension,
204 # Sort first by decreasing count and second by the file extension,
206 # so we have a consistent output.
205 # so we have a consistent output.
207 lang_stats_items = sorted(lang_stats_d.iteritems(),
206 lang_stats_items = sorted(lang_stats_d.iteritems(),
208 key=lambda k: (-k[1], k[0]))[:10]
207 key=lambda k: (-k[1], k[0]))[:10]
209 lang_stats = [(x, {"count": y,
208 lang_stats = [(x, {"count": y,
210 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
209 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
211 for x, y in lang_stats_items]
210 for x, y in lang_stats_items]
212
211
213 c.trending_languages = json.dumps(lang_stats)
212 c.trending_languages = json.dumps(lang_stats)
214 else:
213 else:
215 c.no_data = True
214 c.no_data = True
216 c.trending_languages = json.dumps({})
215 c.trending_languages = json.dumps({})
217
216
218 scm_model = ScmModel()
217 scm_model = ScmModel()
219 c.enable_downloads = self.db_repo.enable_downloads
218 c.enable_downloads = self.db_repo.enable_downloads
220 c.repository_followers = scm_model.get_followers(self.db_repo)
219 c.repository_followers = scm_model.get_followers(self.db_repo)
221 c.repository_forks = scm_model.get_forks(self.db_repo)
220 c.repository_forks = scm_model.get_forks(self.db_repo)
222
221
223 # first interaction with the VCS instance after here...
222 # first interaction with the VCS instance after here...
224 if c.repository_requirements_missing:
223 if c.repository_requirements_missing:
225 self.request.override_renderer = \
224 self.request.override_renderer = \
226 'rhodecode:templates/summary/missing_requirements.mako'
225 'rhodecode:templates/summary/missing_requirements.mako'
227 return self._get_template_context(c)
226 return self._get_template_context(c)
228
227
229 c.readme_data, c.readme_file = \
228 c.readme_data, c.readme_file = \
230 self._get_readme_data(self.db_repo, c.visual.default_renderer)
229 self._get_readme_data(self.db_repo, c.visual.default_renderer)
231
230
232 # loads the summary commits template context
231 # loads the summary commits template context
233 self._load_commits_context(c)
232 self._load_commits_context(c)
234
233
235 return self._get_template_context(c)
234 return self._get_template_context(c)
236
235
237 def get_request_commit_id(self):
238 return self.request.matchdict['commit_id']
239
240 @LoginRequired()
236 @LoginRequired()
241 @HasRepoPermissionAnyDecorator(
237 @HasRepoPermissionAnyDecorator(
242 'repository.read', 'repository.write', 'repository.admin')
238 'repository.read', 'repository.write', 'repository.admin')
243 @view_config(
239 @view_config(
244 route_name='repo_stats', request_method='GET',
240 route_name='repo_stats', request_method='GET',
245 renderer='json_ext')
241 renderer='json_ext')
246 def repo_stats(self):
242 def repo_stats(self):
247 commit_id = self.get_request_commit_id()
248 show_stats = bool(self.db_repo.enable_statistics)
243 show_stats = bool(self.db_repo.enable_statistics)
249 repo_id = self.db_repo.repo_id
244 repo_id = self.db_repo.repo_id
250
245
251 cache_seconds = safe_int(
246 landing_commit = self.db_repo.get_landing_commit()
252 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
247 if isinstance(landing_commit, EmptyCommit):
248 return {'size': 0, 'code_stats': {}}
249
250 cache_seconds = safe_int(rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
253 cache_on = cache_seconds > 0
251 cache_on = cache_seconds > 0
252
254 log.debug(
253 log.debug(
255 'Computing REPO TREE for repo_id %s commit_id `%s` '
254 'Computing REPO STATS for repo_id %s commit_id `%s` '
256 'with caching: %s[TTL: %ss]' % (
255 'with caching: %s[TTL: %ss]' % (
257 repo_id, commit_id, cache_on, cache_seconds or 0))
256 repo_id, landing_commit, cache_on, cache_seconds or 0))
258
257
259 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
258 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
260 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
259 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
261
260
262 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
261 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
263 condition=cache_on)
262 condition=cache_on)
264 def compute_stats(repo_id, commit_id, show_stats):
263 def compute_stats(repo_id, commit_id, _show_stats):
265 code_stats = {}
264 code_stats = {}
266 size = 0
265 size = 0
267 try:
266 try:
268 scm_instance = self.db_repo.scm_instance()
267 commit = self.db_repo.get_commit(commit_id)
269 commit = scm_instance.get_commit(commit_id)
270
268
271 for node in commit.get_filenodes_generator():
269 for node in commit.get_filenodes_generator():
272 size += node.size
270 size += node.size
273 if not show_stats:
271 if not _show_stats:
274 continue
272 continue
275 ext = string.lower(node.extension)
273 ext = string.lower(node.extension)
276 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
274 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
277 if ext_info:
275 if ext_info:
278 if ext in code_stats:
276 if ext in code_stats:
279 code_stats[ext]['count'] += 1
277 code_stats[ext]['count'] += 1
280 else:
278 else:
281 code_stats[ext] = {"count": 1, "desc": ext_info}
279 code_stats[ext] = {"count": 1, "desc": ext_info}
282 except (EmptyRepositoryError, CommitDoesNotExistError):
280 except (EmptyRepositoryError, CommitDoesNotExistError):
283 pass
281 pass
284 return {'size': h.format_byte_size_binary(size),
282 return {'size': h.format_byte_size_binary(size),
285 'code_stats': code_stats}
283 'code_stats': code_stats}
286
284
287 stats = compute_stats(self.db_repo.repo_id, commit_id, show_stats)
285 stats = compute_stats(self.db_repo.repo_id, landing_commit.raw_id, show_stats)
288 return stats
286 return stats
289
287
290 @LoginRequired()
288 @LoginRequired()
291 @HasRepoPermissionAnyDecorator(
289 @HasRepoPermissionAnyDecorator(
292 'repository.read', 'repository.write', 'repository.admin')
290 'repository.read', 'repository.write', 'repository.admin')
293 @view_config(
291 @view_config(
294 route_name='repo_refs_data', request_method='GET',
292 route_name='repo_refs_data', request_method='GET',
295 renderer='json_ext')
293 renderer='json_ext')
296 def repo_refs_data(self):
294 def repo_refs_data(self):
297 _ = self.request.translate
295 _ = self.request.translate
298 self.load_default_context()
296 self.load_default_context()
299
297
300 repo = self.rhodecode_vcs_repo
298 repo = self.rhodecode_vcs_repo
301 refs_to_create = [
299 refs_to_create = [
302 (_("Branch"), repo.branches, 'branch'),
300 (_("Branch"), repo.branches, 'branch'),
303 (_("Tag"), repo.tags, 'tag'),
301 (_("Tag"), repo.tags, 'tag'),
304 (_("Bookmark"), repo.bookmarks, 'book'),
302 (_("Bookmark"), repo.bookmarks, 'book'),
305 ]
303 ]
306 res = self._create_reference_data(repo, self.db_repo_name, refs_to_create)
304 res = self._create_reference_data(repo, self.db_repo_name, refs_to_create)
307 data = {
305 data = {
308 'more': False,
306 'more': False,
309 'results': res
307 'results': res
310 }
308 }
311 return data
309 return data
312
310
313 @LoginRequired()
311 @LoginRequired()
314 @HasRepoPermissionAnyDecorator(
312 @HasRepoPermissionAnyDecorator(
315 'repository.read', 'repository.write', 'repository.admin')
313 'repository.read', 'repository.write', 'repository.admin')
316 @view_config(
314 @view_config(
317 route_name='repo_refs_changelog_data', request_method='GET',
315 route_name='repo_refs_changelog_data', request_method='GET',
318 renderer='json_ext')
316 renderer='json_ext')
319 def repo_refs_changelog_data(self):
317 def repo_refs_changelog_data(self):
320 _ = self.request.translate
318 _ = self.request.translate
321 self.load_default_context()
319 self.load_default_context()
322
320
323 repo = self.rhodecode_vcs_repo
321 repo = self.rhodecode_vcs_repo
324
322
325 refs_to_create = [
323 refs_to_create = [
326 (_("Branches"), repo.branches, 'branch'),
324 (_("Branches"), repo.branches, 'branch'),
327 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
325 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
328 # TODO: enable when vcs can handle bookmarks filters
326 # TODO: enable when vcs can handle bookmarks filters
329 # (_("Bookmarks"), repo.bookmarks, "book"),
327 # (_("Bookmarks"), repo.bookmarks, "book"),
330 ]
328 ]
331 res = self._create_reference_data(
329 res = self._create_reference_data(
332 repo, self.db_repo_name, refs_to_create)
330 repo, self.db_repo_name, refs_to_create)
333 data = {
331 data = {
334 'more': False,
332 'more': False,
335 'results': res
333 'results': res
336 }
334 }
337 return data
335 return data
338
336
339 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
337 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
340 format_ref_id = get_format_ref_id(repo)
338 format_ref_id = get_format_ref_id(repo)
341
339
342 result = []
340 result = []
343 for title, refs, ref_type in refs_to_create:
341 for title, refs, ref_type in refs_to_create:
344 if refs:
342 if refs:
345 result.append({
343 result.append({
346 'text': title,
344 'text': title,
347 'children': self._create_reference_items(
345 'children': self._create_reference_items(
348 repo, full_repo_name, refs, ref_type,
346 repo, full_repo_name, refs, ref_type,
349 format_ref_id),
347 format_ref_id),
350 })
348 })
351 return result
349 return result
352
350
353 def _create_reference_items(self, repo, full_repo_name, refs, ref_type, format_ref_id):
351 def _create_reference_items(self, repo, full_repo_name, refs, ref_type, format_ref_id):
354 result = []
352 result = []
355 is_svn = h.is_svn(repo)
353 is_svn = h.is_svn(repo)
356 for ref_name, raw_id in refs.iteritems():
354 for ref_name, raw_id in refs.iteritems():
357 files_url = self._create_files_url(
355 files_url = self._create_files_url(
358 repo, full_repo_name, ref_name, raw_id, is_svn)
356 repo, full_repo_name, ref_name, raw_id, is_svn)
359 result.append({
357 result.append({
360 'text': ref_name,
358 'text': ref_name,
361 'id': format_ref_id(ref_name, raw_id),
359 'id': format_ref_id(ref_name, raw_id),
362 'raw_id': raw_id,
360 'raw_id': raw_id,
363 'type': ref_type,
361 'type': ref_type,
364 'files_url': files_url,
362 'files_url': files_url,
365 'idx': 0,
363 'idx': 0,
366 })
364 })
367 return result
365 return result
368
366
369 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
367 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
370 use_commit_id = '/' in ref_name or is_svn
368 use_commit_id = '/' in ref_name or is_svn
371 return h.route_path(
369 return h.route_path(
372 'repo_files',
370 'repo_files',
373 repo_name=full_repo_name,
371 repo_name=full_repo_name,
374 f_path=ref_name if is_svn else '',
372 f_path=ref_name if is_svn else '',
375 commit_id=raw_id if use_commit_id else ref_name,
373 commit_id=raw_id if use_commit_id else ref_name,
376 _query=dict(at=ref_name))
374 _query=dict(at=ref_name))
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,687 +1,687 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Package for testing various lib/helper functions in rhodecode
23 Package for testing various lib/helper functions in rhodecode
24 """
24 """
25
25
26 import datetime
26 import datetime
27 import string
27 import string
28 import mock
28 import mock
29 import pytest
29 import pytest
30
30
31 from rhodecode.tests import no_newline_id_generator
31 from rhodecode.tests import no_newline_id_generator
32 from rhodecode.tests.utils import run_test_concurrently
32 from rhodecode.tests.utils import run_test_concurrently
33
33
34 from rhodecode.lib import rc_cache
34 from rhodecode.lib import rc_cache
35 from rhodecode.lib.helpers import InitialsGravatar
35 from rhodecode.lib.helpers import InitialsGravatar
36 from rhodecode.lib.utils2 import AttributeDict
36 from rhodecode.lib.utils2 import AttributeDict
37
37
38 from rhodecode.model.db import Repository, CacheKey
38 from rhodecode.model.db import Repository, CacheKey
39
39
40
40
41 def _urls_for_proto(proto):
41 def _urls_for_proto(proto):
42 return [
42 return [
43 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
43 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
44 '%s://127.0.0.1' % proto),
44 '%s://127.0.0.1' % proto),
45 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
45 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
46 '%s://127.0.0.1' % proto),
46 '%s://127.0.0.1' % proto),
47 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
47 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
48 '%s://127.0.0.1' % proto),
48 '%s://127.0.0.1' % proto),
49 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
49 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
50 '%s://127.0.0.1:8080' % proto),
50 '%s://127.0.0.1:8080' % proto),
51 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
51 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
52 '%s://domain.org' % proto),
52 '%s://domain.org' % proto),
53 ('%s://user:pass@domain.org:8080' % proto,
53 ('%s://user:pass@domain.org:8080' % proto,
54 ['%s://' % proto, 'domain.org', '8080'],
54 ['%s://' % proto, 'domain.org', '8080'],
55 '%s://domain.org:8080' % proto),
55 '%s://domain.org:8080' % proto),
56 ]
56 ]
57
57
58 TEST_URLS = _urls_for_proto('http') + _urls_for_proto('https')
58 TEST_URLS = _urls_for_proto('http') + _urls_for_proto('https')
59
59
60
60
61 @pytest.mark.parametrize("test_url, expected, expected_creds", TEST_URLS)
61 @pytest.mark.parametrize("test_url, expected, expected_creds", TEST_URLS)
62 def test_uri_filter(test_url, expected, expected_creds):
62 def test_uri_filter(test_url, expected, expected_creds):
63 from rhodecode.lib.utils2 import uri_filter
63 from rhodecode.lib.utils2 import uri_filter
64 assert uri_filter(test_url) == expected
64 assert uri_filter(test_url) == expected
65
65
66
66
67 @pytest.mark.parametrize("test_url, expected, expected_creds", TEST_URLS)
67 @pytest.mark.parametrize("test_url, expected, expected_creds", TEST_URLS)
68 def test_credentials_filter(test_url, expected, expected_creds):
68 def test_credentials_filter(test_url, expected, expected_creds):
69 from rhodecode.lib.utils2 import credentials_filter
69 from rhodecode.lib.utils2 import credentials_filter
70 assert credentials_filter(test_url) == expected_creds
70 assert credentials_filter(test_url) == expected_creds
71
71
72
72
73 @pytest.mark.parametrize("str_bool, expected", [
73 @pytest.mark.parametrize("str_bool, expected", [
74 ('t', True),
74 ('t', True),
75 ('true', True),
75 ('true', True),
76 ('y', True),
76 ('y', True),
77 ('yes', True),
77 ('yes', True),
78 ('on', True),
78 ('on', True),
79 ('1', True),
79 ('1', True),
80 ('Y', True),
80 ('Y', True),
81 ('yeS', True),
81 ('yeS', True),
82 ('Y', True),
82 ('Y', True),
83 ('TRUE', True),
83 ('TRUE', True),
84 ('T', True),
84 ('T', True),
85 ('False', False),
85 ('False', False),
86 ('F', False),
86 ('F', False),
87 ('FALSE', False),
87 ('FALSE', False),
88 ('0', False),
88 ('0', False),
89 ('-1', False),
89 ('-1', False),
90 ('', False)
90 ('', False)
91 ])
91 ])
92 def test_str2bool(str_bool, expected):
92 def test_str2bool(str_bool, expected):
93 from rhodecode.lib.utils2 import str2bool
93 from rhodecode.lib.utils2 import str2bool
94 assert str2bool(str_bool) == expected
94 assert str2bool(str_bool) == expected
95
95
96
96
97 @pytest.mark.parametrize("text, expected", reduce(lambda a1,a2:a1+a2, [
97 @pytest.mark.parametrize("text, expected", reduce(lambda a1,a2:a1+a2, [
98 [
98 [
99 (pref+"", []),
99 (pref+"", []),
100 (pref+"Hi there @marcink", ['marcink']),
100 (pref+"Hi there @marcink", ['marcink']),
101 (pref+"Hi there @marcink and @bob", ['bob', 'marcink']),
101 (pref+"Hi there @marcink and @bob", ['bob', 'marcink']),
102 (pref+"Hi there @marcink\n", ['marcink']),
102 (pref+"Hi there @marcink\n", ['marcink']),
103 (pref+"Hi there @marcink and @bob\n", ['bob', 'marcink']),
103 (pref+"Hi there @marcink and @bob\n", ['bob', 'marcink']),
104 (pref+"Hi there marcin@rhodecode.com", []),
104 (pref+"Hi there marcin@rhodecode.com", []),
105 (pref+"Hi there @john.malcovic and @bob\n", ['bob', 'john.malcovic']),
105 (pref+"Hi there @john.malcovic and @bob\n", ['bob', 'john.malcovic']),
106 (pref+"This needs to be reviewed: (@marcink,@john)", ["john", "marcink"]),
106 (pref+"This needs to be reviewed: (@marcink,@john)", ["john", "marcink"]),
107 (pref+"This needs to be reviewed: (@marcink, @john)", ["john", "marcink"]),
107 (pref+"This needs to be reviewed: (@marcink, @john)", ["john", "marcink"]),
108 (pref+"This needs to be reviewed: [@marcink,@john]", ["john", "marcink"]),
108 (pref+"This needs to be reviewed: [@marcink,@john]", ["john", "marcink"]),
109 (pref+"This needs to be reviewed: (@marcink @john)", ["john", "marcink"]),
109 (pref+"This needs to be reviewed: (@marcink @john)", ["john", "marcink"]),
110 (pref+"@john @mary, please review", ["john", "mary"]),
110 (pref+"@john @mary, please review", ["john", "mary"]),
111 (pref+"@john,@mary, please review", ["john", "mary"]),
111 (pref+"@john,@mary, please review", ["john", "mary"]),
112 (pref+"Hej @123, @22john,@mary, please review", ['123', '22john', 'mary']),
112 (pref+"Hej @123, @22john,@mary, please review", ['123', '22john', 'mary']),
113 (pref+"@first hi there @marcink here's my email marcin@email.com "
113 (pref+"@first hi there @marcink here's my email marcin@email.com "
114 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three ", ['first', 'lukaszb', 'marcink', 'one', 'one_more22']),
114 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three ", ['first', 'lukaszb', 'marcink', 'one', 'one_more22']),
115 (pref+"@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl", ['2one_more22', 'john', 'MARCIN', 'maRCiN']),
115 (pref+"@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl", ['2one_more22', 'john', 'MARCIN', 'maRCiN']),
116 (pref+"@marian.user just do it @marco-polo and next extract @marco_polo", ['marco-polo', 'marco_polo', 'marian.user']),
116 (pref+"@marian.user just do it @marco-polo and next extract @marco_polo", ['marco-polo', 'marco_polo', 'marian.user']),
117 (pref+"user.dot hej ! not-needed maril@domain.org", []),
117 (pref+"user.dot hej ! not-needed maril@domain.org", []),
118 (pref+"\n@marcin", ['marcin']),
118 (pref+"\n@marcin", ['marcin']),
119 ]
119 ]
120 for pref in ['', '\n', 'hi !', '\t', '\n\n']]), ids=no_newline_id_generator)
120 for pref in ['', '\n', 'hi !', '\t', '\n\n']]), ids=no_newline_id_generator)
121 def test_mention_extractor(text, expected):
121 def test_mention_extractor(text, expected):
122 from rhodecode.lib.utils2 import extract_mentioned_users
122 from rhodecode.lib.utils2 import extract_mentioned_users
123 got = extract_mentioned_users(text)
123 got = extract_mentioned_users(text)
124 assert sorted(got, key=lambda x: x.lower()) == got
124 assert sorted(got, key=lambda x: x.lower()) == got
125 assert set(expected) == set(got)
125 assert set(expected) == set(got)
126
126
127 @pytest.mark.parametrize("age_args, expected, kw", [
127 @pytest.mark.parametrize("age_args, expected, kw", [
128 ({}, u'just now', {}),
128 ({}, u'just now', {}),
129 ({'seconds': -1}, u'1 second ago', {}),
129 ({'seconds': -1}, u'1 second ago', {}),
130 ({'seconds': -60 * 2}, u'2 minutes ago', {}),
130 ({'seconds': -60 * 2}, u'2 minutes ago', {}),
131 ({'hours': -1}, u'1 hour ago', {}),
131 ({'hours': -1}, u'1 hour ago', {}),
132 ({'hours': -24}, u'1 day ago', {}),
132 ({'hours': -24}, u'1 day ago', {}),
133 ({'hours': -24 * 5}, u'5 days ago', {}),
133 ({'hours': -24 * 5}, u'5 days ago', {}),
134 ({'months': -1}, u'1 month ago', {}),
134 ({'months': -1}, u'1 month ago', {}),
135 ({'months': -1, 'days': -2}, u'1 month and 2 days ago', {}),
135 ({'months': -1, 'days': -2}, u'1 month and 2 days ago', {}),
136 ({'years': -1, 'months': -1}, u'1 year and 1 month ago', {}),
136 ({'years': -1, 'months': -1}, u'1 year and 1 month ago', {}),
137 ({}, u'just now', {'short_format': True}),
137 ({}, u'just now', {'short_format': True}),
138 ({'seconds': -1}, u'1sec ago', {'short_format': True}),
138 ({'seconds': -1}, u'1sec ago', {'short_format': True}),
139 ({'seconds': -60 * 2}, u'2min ago', {'short_format': True}),
139 ({'seconds': -60 * 2}, u'2min ago', {'short_format': True}),
140 ({'hours': -1}, u'1h ago', {'short_format': True}),
140 ({'hours': -1}, u'1h ago', {'short_format': True}),
141 ({'hours': -24}, u'1d ago', {'short_format': True}),
141 ({'hours': -24}, u'1d ago', {'short_format': True}),
142 ({'hours': -24 * 5}, u'5d ago', {'short_format': True}),
142 ({'hours': -24 * 5}, u'5d ago', {'short_format': True}),
143 ({'months': -1}, u'1m ago', {'short_format': True}),
143 ({'months': -1}, u'1m ago', {'short_format': True}),
144 ({'months': -1, 'days': -2}, u'1m, 2d ago', {'short_format': True}),
144 ({'months': -1, 'days': -2}, u'1m, 2d ago', {'short_format': True}),
145 ({'years': -1, 'months': -1}, u'1y, 1m ago', {'short_format': True}),
145 ({'years': -1, 'months': -1}, u'1y, 1m ago', {'short_format': True}),
146 ])
146 ])
147 def test_age(age_args, expected, kw, baseapp):
147 def test_age(age_args, expected, kw, baseapp):
148 from rhodecode.lib.utils2 import age
148 from rhodecode.lib.utils2 import age
149 from dateutil import relativedelta
149 from dateutil import relativedelta
150 n = datetime.datetime(year=2012, month=5, day=17)
150 n = datetime.datetime(year=2012, month=5, day=17)
151 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
151 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
152
152
153 def translate(elem):
153 def translate(elem):
154 return elem.interpolate()
154 return elem.interpolate()
155
155
156 assert translate(age(n + delt(**age_args), now=n, **kw)) == expected
156 assert translate(age(n + delt(**age_args), now=n, **kw)) == expected
157
157
158
158
159 @pytest.mark.parametrize("age_args, expected, kw", [
159 @pytest.mark.parametrize("age_args, expected, kw", [
160 ({}, u'just now', {}),
160 ({}, u'just now', {}),
161 ({'seconds': 1}, u'in 1 second', {}),
161 ({'seconds': 1}, u'in 1 second', {}),
162 ({'seconds': 60 * 2}, u'in 2 minutes', {}),
162 ({'seconds': 60 * 2}, u'in 2 minutes', {}),
163 ({'hours': 1}, u'in 1 hour', {}),
163 ({'hours': 1}, u'in 1 hour', {}),
164 ({'hours': 24}, u'in 1 day', {}),
164 ({'hours': 24}, u'in 1 day', {}),
165 ({'hours': 24 * 5}, u'in 5 days', {}),
165 ({'hours': 24 * 5}, u'in 5 days', {}),
166 ({'months': 1}, u'in 1 month', {}),
166 ({'months': 1}, u'in 1 month', {}),
167 ({'months': 1, 'days': 1}, u'in 1 month and 1 day', {}),
167 ({'months': 1, 'days': 1}, u'in 1 month and 1 day', {}),
168 ({'years': 1, 'months': 1}, u'in 1 year and 1 month', {}),
168 ({'years': 1, 'months': 1}, u'in 1 year and 1 month', {}),
169 ({}, u'just now', {'short_format': True}),
169 ({}, u'just now', {'short_format': True}),
170 ({'seconds': 1}, u'in 1sec', {'short_format': True}),
170 ({'seconds': 1}, u'in 1sec', {'short_format': True}),
171 ({'seconds': 60 * 2}, u'in 2min', {'short_format': True}),
171 ({'seconds': 60 * 2}, u'in 2min', {'short_format': True}),
172 ({'hours': 1}, u'in 1h', {'short_format': True}),
172 ({'hours': 1}, u'in 1h', {'short_format': True}),
173 ({'hours': 24}, u'in 1d', {'short_format': True}),
173 ({'hours': 24}, u'in 1d', {'short_format': True}),
174 ({'hours': 24 * 5}, u'in 5d', {'short_format': True}),
174 ({'hours': 24 * 5}, u'in 5d', {'short_format': True}),
175 ({'months': 1}, u'in 1m', {'short_format': True}),
175 ({'months': 1}, u'in 1m', {'short_format': True}),
176 ({'months': 1, 'days': 1}, u'in 1m, 1d', {'short_format': True}),
176 ({'months': 1, 'days': 1}, u'in 1m, 1d', {'short_format': True}),
177 ({'years': 1, 'months': 1}, u'in 1y, 1m', {'short_format': True}),
177 ({'years': 1, 'months': 1}, u'in 1y, 1m', {'short_format': True}),
178 ])
178 ])
179 def test_age_in_future(age_args, expected, kw, baseapp):
179 def test_age_in_future(age_args, expected, kw, baseapp):
180 from rhodecode.lib.utils2 import age
180 from rhodecode.lib.utils2 import age
181 from dateutil import relativedelta
181 from dateutil import relativedelta
182 n = datetime.datetime(year=2012, month=5, day=17)
182 n = datetime.datetime(year=2012, month=5, day=17)
183 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
183 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
184
184
185 def translate(elem):
185 def translate(elem):
186 return elem.interpolate()
186 return elem.interpolate()
187
187
188 assert translate(age(n + delt(**age_args), now=n, **kw)) == expected
188 assert translate(age(n + delt(**age_args), now=n, **kw)) == expected
189
189
190
190
191 @pytest.mark.parametrize("sample, expected_tags", [
191 @pytest.mark.parametrize("sample, expected_tags", [
192 # entry
192 # entry
193 ((
193 ((
194 ""
194 ""
195 ),
195 ),
196 [
196 [
197
197
198 ]),
198 ]),
199 # entry
199 # entry
200 ((
200 ((
201 "hello world [stale]"
201 "hello world [stale]"
202 ),
202 ),
203 [
203 [
204 ('state', '[stale]'),
204 ('state', '[stale]'),
205 ]),
205 ]),
206 # entry
206 # entry
207 ((
207 ((
208 "hello world [v2.0.0] [v1.0.0]"
208 "hello world [v2.0.0] [v1.0.0]"
209 ),
209 ),
210 [
210 [
211 ('generic', '[v2.0.0]'),
211 ('generic', '[v2.0.0]'),
212 ('generic', '[v1.0.0]'),
212 ('generic', '[v1.0.0]'),
213 ]),
213 ]),
214 # entry
214 # entry
215 ((
215 ((
216 "he[ll]o wo[rl]d"
216 "he[ll]o wo[rl]d"
217 ),
217 ),
218 [
218 [
219 ('label', '[ll]'),
219 ('label', '[ll]'),
220 ('label', '[rl]'),
220 ('label', '[rl]'),
221 ]),
221 ]),
222 # entry
222 # entry
223 ((
223 ((
224 "hello world [stale]\n[featured]\n[stale] [dead] [dev]"
224 "hello world [stale]\n[featured]\n[stale] [dead] [dev]"
225 ),
225 ),
226 [
226 [
227 ('state', '[stale]'),
227 ('state', '[stale]'),
228 ('state', '[featured]'),
228 ('state', '[featured]'),
229 ('state', '[stale]'),
229 ('state', '[stale]'),
230 ('state', '[dead]'),
230 ('state', '[dead]'),
231 ('state', '[dev]'),
231 ('state', '[dev]'),
232 ]),
232 ]),
233 # entry
233 # entry
234 ((
234 ((
235 "hello world \n\n [stale] \n [url =&gt; [name](http://rc.com)]"
235 "hello world \n\n [stale] \n [url =&gt; [name](http://rc.com)]"
236 ),
236 ),
237 [
237 [
238 ('state', '[stale]'),
238 ('state', '[stale]'),
239 ('url', '[url =&gt; [name](http://rc.com)]'),
239 ('url', '[url =&gt; [name](http://rc.com)]'),
240 ]),
240 ]),
241 # entry
241 # entry
242 ((
242 ((
243 "[url =&gt; [linkNameJS](javascript:alert(document.domain))]\n"
243 "[url =&gt; [linkNameJS](javascript:alert(document.domain))]\n"
244 "[url =&gt; [linkNameHTTP](http://rhodecode.com)]\n"
244 "[url =&gt; [linkNameHTTP](http://rhodecode.com)]\n"
245 "[url =&gt; [linkNameHTTPS](https://rhodecode.com)]\n"
245 "[url =&gt; [linkNameHTTPS](https://rhodecode.com)]\n"
246 "[url =&gt; [linkNamePath](/repo_group)]\n"
246 "[url =&gt; [linkNamePath](/repo_group)]\n"
247 ),
247 ),
248 [
248 [
249 ('generic', '[linkNameJS]'),
249 ('generic', '[linkNameJS]'),
250 ('url', '[url =&gt; [linkNameHTTP](http://rhodecode.com)]'),
250 ('url', '[url =&gt; [linkNameHTTP](http://rhodecode.com)]'),
251 ('url', '[url =&gt; [linkNameHTTPS](https://rhodecode.com)]'),
251 ('url', '[url =&gt; [linkNameHTTPS](https://rhodecode.com)]'),
252 ('url', '[url =&gt; [linkNamePath](/repo_group)]'),
252 ('url', '[url =&gt; [linkNamePath](/repo_group)]'),
253 ]),
253 ]),
254 # entry
254 # entry
255 ((
255 ((
256 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =&gt;>< sa]"
256 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =&gt;>< sa]"
257 "[requires] [stale] [see<>=&gt;] [see =&gt; http://url.com]"
257 "[requires] [stale] [see<>=&gt;] [see =&gt; http://url.com]"
258 "[requires =&gt; url] [lang =&gt; python] [just a tag] "
258 "[requires =&gt; url] [lang =&gt; python] [just a tag] "
259 "<html_tag first='abc' attr=\"my.url?attr=&another=\"></html_tag>"
259 "<html_tag first='abc' attr=\"my.url?attr=&another=\"></html_tag>"
260 "[,d] [ =&gt; ULR ] [obsolete] [desc]]"
260 "[,d] [ =&gt; ULR ] [obsolete] [desc]]"
261 ),
261 ),
262 [
262 [
263 ('label', '[desc]'),
263 ('label', '[desc]'),
264 ('label', '[obsolete]'),
264 ('label', '[obsolete]'),
265 ('label', '[or]'),
265 ('label', '[or]'),
266 ('label', '[requires]'),
266 ('label', '[requires]'),
267 ('label', '[tag]'),
267 ('label', '[tag]'),
268 ('state', '[stale]'),
268 ('state', '[stale]'),
269 ('lang', '[lang =&gt; python]'),
269 ('lang', '[lang =&gt; python]'),
270 ('ref', '[requires =&gt; url]'),
270 ('ref', '[requires =&gt; url]'),
271 ('see', '[see =&gt; http://url.com]'),
271 ('see', '[see =&gt; http://url.com]'),
272
272
273 ]),
273 ]),
274
274
275 ], ids=no_newline_id_generator)
275 ], ids=no_newline_id_generator)
276 def test_metatag_extraction(sample, expected_tags):
276 def test_metatag_extraction(sample, expected_tags):
277 from rhodecode.lib.helpers import extract_metatags
277 from rhodecode.lib.helpers import extract_metatags
278 tags, value = extract_metatags(sample)
278 tags, value = extract_metatags(sample)
279 assert sorted(tags) == sorted(expected_tags)
279 assert sorted(tags) == sorted(expected_tags)
280
280
281
281
282 @pytest.mark.parametrize("tag_data, expected_html", [
282 @pytest.mark.parametrize("tag_data, expected_html", [
283
283
284 (('state', '[stable]'), '<div class="metatag" tag="state stable">stable</div>'),
284 (('state', '[stable]'), '<div class="metatag" tag="state stable">stable</div>'),
285 (('state', '[stale]'), '<div class="metatag" tag="state stale">stale</div>'),
285 (('state', '[stale]'), '<div class="metatag" tag="state stale">stale</div>'),
286 (('state', '[featured]'), '<div class="metatag" tag="state featured">featured</div>'),
286 (('state', '[featured]'), '<div class="metatag" tag="state featured">featured</div>'),
287 (('state', '[dev]'), '<div class="metatag" tag="state dev">dev</div>'),
287 (('state', '[dev]'), '<div class="metatag" tag="state dev">dev</div>'),
288 (('state', '[dead]'), '<div class="metatag" tag="state dead">dead</div>'),
288 (('state', '[dead]'), '<div class="metatag" tag="state dead">dead</div>'),
289
289
290 (('label', '[personal]'), '<div class="metatag" tag="label">personal</div>'),
290 (('label', '[personal]'), '<div class="metatag" tag="label">personal</div>'),
291 (('generic', '[v2.0.0]'), '<div class="metatag" tag="generic">v2.0.0</div>'),
291 (('generic', '[v2.0.0]'), '<div class="metatag" tag="generic">v2.0.0</div>'),
292
292
293 (('lang', '[lang =&gt; JavaScript]'), '<div class="metatag" tag="lang">JavaScript</div>'),
293 (('lang', '[lang =&gt; JavaScript]'), '<div class="metatag" tag="lang">JavaScript</div>'),
294 (('lang', '[lang =&gt; C++]'), '<div class="metatag" tag="lang">C++</div>'),
294 (('lang', '[lang =&gt; C++]'), '<div class="metatag" tag="lang">C++</div>'),
295 (('lang', '[lang =&gt; C#]'), '<div class="metatag" tag="lang">C#</div>'),
295 (('lang', '[lang =&gt; C#]'), '<div class="metatag" tag="lang">C#</div>'),
296 (('lang', '[lang =&gt; Delphi/Object]'), '<div class="metatag" tag="lang">Delphi/Object</div>'),
296 (('lang', '[lang =&gt; Delphi/Object]'), '<div class="metatag" tag="lang">Delphi/Object</div>'),
297 (('lang', '[lang =&gt; Objective-C]'), '<div class="metatag" tag="lang">Objective-C</div>'),
297 (('lang', '[lang =&gt; Objective-C]'), '<div class="metatag" tag="lang">Objective-C</div>'),
298 (('lang', '[lang =&gt; .NET]'), '<div class="metatag" tag="lang">.NET</div>'),
298 (('lang', '[lang =&gt; .NET]'), '<div class="metatag" tag="lang">.NET</div>'),
299
299
300 (('license', '[license =&gt; BSD 3-clause]'), '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/BSD 3-clause">BSD 3-clause</a></div>'),
300 (('license', '[license =&gt; BSD 3-clause]'), '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/BSD 3-clause">BSD 3-clause</a></div>'),
301 (('license', '[license =&gt; GPLv3]'), '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/GPLv3">GPLv3</a></div>'),
301 (('license', '[license =&gt; GPLv3]'), '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/GPLv3">GPLv3</a></div>'),
302 (('license', '[license =&gt; MIT]'), '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/MIT">MIT</a></div>'),
302 (('license', '[license =&gt; MIT]'), '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/MIT">MIT</a></div>'),
303 (('license', '[license =&gt; AGPLv3]'), '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/AGPLv3">AGPLv3</a></div>'),
303 (('license', '[license =&gt; AGPLv3]'), '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/AGPLv3">AGPLv3</a></div>'),
304
304
305 (('ref', '[requires =&gt; RepoName]'), '<div class="metatag" tag="ref requires">requires: <a href="/RepoName">RepoName</a></div>'),
305 (('ref', '[requires =&gt; RepoName]'), '<div class="metatag" tag="ref requires">requires: <a href="/RepoName">RepoName</a></div>'),
306 (('ref', '[recommends =&gt; GroupName]'), '<div class="metatag" tag="ref recommends">recommends: <a href="/GroupName">GroupName</a></div>'),
306 (('ref', '[recommends =&gt; GroupName]'), '<div class="metatag" tag="ref recommends">recommends: <a href="/GroupName">GroupName</a></div>'),
307 (('ref', '[conflicts =&gt; SomeName]'), '<div class="metatag" tag="ref conflicts">conflicts: <a href="/SomeName">SomeName</a></div>'),
307 (('ref', '[conflicts =&gt; SomeName]'), '<div class="metatag" tag="ref conflicts">conflicts: <a href="/SomeName">SomeName</a></div>'),
308 (('ref', '[base =&gt; SomeName]'), '<div class="metatag" tag="ref base">base: <a href="/SomeName">SomeName</a></div>'),
308 (('ref', '[base =&gt; SomeName]'), '<div class="metatag" tag="ref base">base: <a href="/SomeName">SomeName</a></div>'),
309
309
310 (('see', '[see =&gt; http://rhodecode.com]'), '<div class="metatag" tag="see">see: http://rhodecode.com </div>'),
310 (('see', '[see =&gt; http://rhodecode.com]'), '<div class="metatag" tag="see">see: http://rhodecode.com </div>'),
311
311
312 (('url', '[url =&gt; [linkName](https://rhodecode.com)]'), '<div class="metatag" tag="url"> <a href="https://rhodecode.com">linkName</a> </div>'),
312 (('url', '[url =&gt; [linkName](https://rhodecode.com)]'), '<div class="metatag" tag="url"> <a href="https://rhodecode.com">linkName</a> </div>'),
313 (('url', '[url =&gt; [example link](https://rhodecode.com)]'), '<div class="metatag" tag="url"> <a href="https://rhodecode.com">example link</a> </div>'),
313 (('url', '[url =&gt; [example link](https://rhodecode.com)]'), '<div class="metatag" tag="url"> <a href="https://rhodecode.com">example link</a> </div>'),
314 (('url', '[url =&gt; [v1.0.0](https://rhodecode.com)]'), '<div class="metatag" tag="url"> <a href="https://rhodecode.com">v1.0.0</a> </div>'),
314 (('url', '[url =&gt; [v1.0.0](https://rhodecode.com)]'), '<div class="metatag" tag="url"> <a href="https://rhodecode.com">v1.0.0</a> </div>'),
315
315
316 ])
316 ])
317 def test_metatags_stylize(tag_data, expected_html):
317 def test_metatags_stylize(tag_data, expected_html):
318 from rhodecode.lib.helpers import style_metatag
318 from rhodecode.lib.helpers import style_metatag
319 tag_type,value = tag_data
319 tag_type,value = tag_data
320 assert style_metatag(tag_type, value) == expected_html
320 assert style_metatag(tag_type, value) == expected_html
321
321
322
322
323 @pytest.mark.parametrize("tmpl_url, email, expected", [
323 @pytest.mark.parametrize("tmpl_url, email, expected", [
324 ('http://test.com/{email}', 'test@foo.com', 'http://test.com/test@foo.com'),
324 ('http://test.com/{email}', 'test@foo.com', 'http://test.com/test@foo.com'),
325
325
326 ('http://test.com/{md5email}', 'test@foo.com', 'http://test.com/3cb7232fcc48743000cb86d0d5022bd9'),
326 ('http://test.com/{md5email}', 'test@foo.com', 'http://test.com/3cb7232fcc48743000cb86d0d5022bd9'),
327 ('http://test.com/{md5email}', 'testΔ…Δ‡@foo.com', 'http://test.com/978debb907a3c55cd741872ab293ef30'),
327 ('http://test.com/{md5email}', 'testΔ…Δ‡@foo.com', 'http://test.com/978debb907a3c55cd741872ab293ef30'),
328
328
329 ('http://testX.com/{md5email}?s={size}', 'test@foo.com', 'http://testX.com/3cb7232fcc48743000cb86d0d5022bd9?s=24'),
329 ('http://testX.com/{md5email}?s={size}', 'test@foo.com', 'http://testX.com/3cb7232fcc48743000cb86d0d5022bd9?s=24'),
330 ('http://testX.com/{md5email}?s={size}', 'testΔ…Δ‡@foo.com', 'http://testX.com/978debb907a3c55cd741872ab293ef30?s=24'),
330 ('http://testX.com/{md5email}?s={size}', 'testΔ…Δ‡@foo.com', 'http://testX.com/978debb907a3c55cd741872ab293ef30?s=24'),
331
331
332 ('{scheme}://{netloc}/{md5email}/{size}', 'test@foo.com', 'https://server.com/3cb7232fcc48743000cb86d0d5022bd9/24'),
332 ('{scheme}://{netloc}/{md5email}/{size}', 'test@foo.com', 'https://server.com/3cb7232fcc48743000cb86d0d5022bd9/24'),
333 ('{scheme}://{netloc}/{md5email}/{size}', 'testΔ…Δ‡@foo.com', 'https://server.com/978debb907a3c55cd741872ab293ef30/24'),
333 ('{scheme}://{netloc}/{md5email}/{size}', 'testΔ…Δ‡@foo.com', 'https://server.com/978debb907a3c55cd741872ab293ef30/24'),
334
334
335 ('http://test.com/{email}', 'testΔ…Δ‡@foo.com', 'http://test.com/testΔ…Δ‡@foo.com'),
335 ('http://test.com/{email}', 'testΔ…Δ‡@foo.com', 'http://test.com/testΔ…Δ‡@foo.com'),
336 ('http://test.com/{email}?size={size}', 'test@foo.com', 'http://test.com/test@foo.com?size=24'),
336 ('http://test.com/{email}?size={size}', 'test@foo.com', 'http://test.com/test@foo.com?size=24'),
337 ('http://test.com/{email}?size={size}', 'testΔ…Δ‡@foo.com', 'http://test.com/testΔ…Δ‡@foo.com?size=24'),
337 ('http://test.com/{email}?size={size}', 'testΔ…Δ‡@foo.com', 'http://test.com/testΔ…Δ‡@foo.com?size=24'),
338 ])
338 ])
339 def test_gravatar_url_builder(tmpl_url, email, expected, request_stub):
339 def test_gravatar_url_builder(tmpl_url, email, expected, request_stub):
340 from rhodecode.lib.helpers import gravatar_url
340 from rhodecode.lib.helpers import gravatar_url
341
341
342 def fake_tmpl_context(_url):
342 def fake_tmpl_context(_url):
343 _c = AttributeDict()
343 _c = AttributeDict()
344 _c.visual = AttributeDict()
344 _c.visual = AttributeDict()
345 _c.visual.use_gravatar = True
345 _c.visual.use_gravatar = True
346 _c.visual.gravatar_url = _url
346 _c.visual.gravatar_url = _url
347 return _c
347 return _c
348
348
349 # mock pyramid.threadlocals
349 # mock pyramid.threadlocals
350 def fake_get_current_request():
350 def fake_get_current_request():
351 request_stub.scheme = 'https'
351 request_stub.scheme = 'https'
352 request_stub.host = 'server.com'
352 request_stub.host = 'server.com'
353
353
354 request_stub._call_context = fake_tmpl_context(tmpl_url)
354 request_stub._call_context = fake_tmpl_context(tmpl_url)
355 return request_stub
355 return request_stub
356
356
357 with mock.patch('rhodecode.lib.helpers.get_current_request',
357 with mock.patch('rhodecode.lib.helpers.get_current_request',
358 fake_get_current_request):
358 fake_get_current_request):
359
359
360 grav = gravatar_url(email_address=email, size=24)
360 grav = gravatar_url(email_address=email, size=24)
361 assert grav == expected
361 assert grav == expected
362
362
363
363
364 @pytest.mark.parametrize(
364 @pytest.mark.parametrize(
365 "email, first_name, last_name, expected_initials, expected_color", [
365 "email, first_name, last_name, expected_initials, expected_color", [
366
366
367 ('test@rhodecode.com', '', '', 'TR', '#8a994d'),
367 ('test@rhodecode.com', '', '', 'TR', '#8a994d'),
368 ('marcin.kuzminski@rhodecode.com', '', '', 'MK', '#6559b3'),
368 ('marcin.kuzminski@rhodecode.com', '', '', 'MK', '#6559b3'),
369 # special cases of email
369 # special cases of email
370 ('john.van.dam@rhodecode.com', '', '', 'JD', '#526600'),
370 ('john.van.dam@rhodecode.com', '', '', 'JD', '#526600'),
371 ('Guido.van.Rossum@rhodecode.com', '', '', 'GR', '#990052'),
371 ('Guido.van.Rossum@rhodecode.com', '', '', 'GR', '#990052'),
372 ('Guido.van.Rossum@rhodecode.com', 'Guido', 'Van Rossum', 'GR', '#990052'),
372 ('Guido.van.Rossum@rhodecode.com', 'Guido', 'Van Rossum', 'GR', '#990052'),
373
373
374 ('rhodecode+Guido.van.Rossum@rhodecode.com', '', '', 'RR', '#46598c'),
374 ('rhodecode+Guido.van.Rossum@rhodecode.com', '', '', 'RR', '#46598c'),
375 ('pclouds@rhodecode.com', 'Nguyα»…n ThΓ‘i', 'Tgọc Duy', 'ND', '#665200'),
375 ('pclouds@rhodecode.com', 'Nguyα»…n ThΓ‘i', 'Tgọc Duy', 'ND', '#665200'),
376
376
377 ('john-brown@foo.com', '', '', 'JF', '#73006b'),
377 ('john-brown@foo.com', '', '', 'JF', '#73006b'),
378 ('admin@rhodecode.com', 'Marcin', 'Kuzminski', 'MK', '#104036'),
378 ('admin@rhodecode.com', 'Marcin', 'Kuzminski', 'MK', '#104036'),
379 # partials
379 # partials
380 ('admin@rhodecode.com', 'Marcin', '', 'MR', '#104036'), # fn+email
380 ('admin@rhodecode.com', 'Marcin', '', 'MR', '#104036'), # fn+email
381 ('admin@rhodecode.com', '', 'Kuzminski', 'AK', '#104036'), # em+ln
381 ('admin@rhodecode.com', '', 'Kuzminski', 'AK', '#104036'), # em+ln
382 # non-ascii
382 # non-ascii
383 ('admin@rhodecode.com', 'Marcin', 'Śuzminski', 'MS', '#104036'),
383 ('admin@rhodecode.com', 'Marcin', 'Śuzminski', 'MS', '#104036'),
384 ('marcin.Ε›uzminski@rhodecode.com', '', '', 'MS', '#73000f'),
384 ('marcin.Ε›uzminski@rhodecode.com', '', '', 'MS', '#73000f'),
385
385
386 # special cases, LDAP can provide those...
386 # special cases, LDAP can provide those...
387 ('admin@', 'Marcin', 'Śuzminski', 'MS', '#aa00ff'),
387 ('admin@', 'Marcin', 'Śuzminski', 'MS', '#aa00ff'),
388 ('marcin.Ε›uzminski', '', '', 'MS', '#402020'),
388 ('marcin.Ε›uzminski', '', '', 'MS', '#402020'),
389 ('null', '', '', 'NL', '#8c4646'),
389 ('null', '', '', 'NL', '#8c4646'),
390 ('some.@abc.com', 'some', '', 'SA', '#664e33')
390 ('some.@abc.com', 'some', '', 'SA', '#664e33')
391 ])
391 ])
392 def test_initials_gravatar_pick_of_initials_and_color_algo(
392 def test_initials_gravatar_pick_of_initials_and_color_algo(
393 email, first_name, last_name, expected_initials, expected_color):
393 email, first_name, last_name, expected_initials, expected_color):
394 instance = InitialsGravatar(email, first_name, last_name)
394 instance = InitialsGravatar(email, first_name, last_name)
395 assert instance.get_initials() == expected_initials
395 assert instance.get_initials() == expected_initials
396 assert instance.str2color(email) == expected_color
396 assert instance.str2color(email) == expected_color
397
397
398
398
399 def test_initials_gravatar_mapping_algo():
399 def test_initials_gravatar_mapping_algo():
400 pos = set()
400 pos = set()
401 instance = InitialsGravatar('', '', '')
401 instance = InitialsGravatar('', '', '')
402 iterations = 0
402 iterations = 0
403
403
404 variations = []
404 variations = []
405 for letter1 in string.ascii_letters:
405 for letter1 in string.ascii_letters:
406 for letter2 in string.ascii_letters[::-1][:10]:
406 for letter2 in string.ascii_letters[::-1][:10]:
407 for letter3 in string.ascii_letters[:10]:
407 for letter3 in string.ascii_letters[:10]:
408 variations.append(
408 variations.append(
409 '%s@rhodecode.com' % (letter1+letter2+letter3))
409 '%s@rhodecode.com' % (letter1+letter2+letter3))
410
410
411 max_variations = 4096
411 max_variations = 4096
412 for email in variations[:max_variations]:
412 for email in variations[:max_variations]:
413 iterations += 1
413 iterations += 1
414 pos.add(
414 pos.add(
415 instance.pick_color_bank_index(email,
415 instance.pick_color_bank_index(email,
416 instance.get_color_bank()))
416 instance.get_color_bank()))
417
417
418 # we assume that we have match all 256 possible positions,
418 # we assume that we have match all 256 possible positions,
419 # in reasonable amount of different email addresses
419 # in reasonable amount of different email addresses
420 assert len(pos) == 256
420 assert len(pos) == 256
421 assert iterations == max_variations
421 assert iterations == max_variations
422
422
423
423
424 @pytest.mark.parametrize("tmpl, repo_name, overrides, prefix, expected", [
424 @pytest.mark.parametrize("tmpl, repo_name, overrides, prefix, expected", [
425 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {}, '', 'http://vps1:8000/group/repo1'),
425 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {}, '', 'http://vps1:8000/group/repo1'),
426 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'marcink'}, '', 'http://marcink@vps1:8000/group/repo1'),
426 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'marcink'}, '', 'http://marcink@vps1:8000/group/repo1'),
427 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {}, '/rc', 'http://vps1:8000/rc/group/repo1'),
427 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {}, '/rc', 'http://vps1:8000/rc/group/repo1'),
428 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'user'}, '/rc', 'http://user@vps1:8000/rc/group/repo1'),
428 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'user'}, '/rc', 'http://user@vps1:8000/rc/group/repo1'),
429 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'marcink'}, '/rc', 'http://marcink@vps1:8000/rc/group/repo1'),
429 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'marcink'}, '/rc', 'http://marcink@vps1:8000/rc/group/repo1'),
430 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'user'}, '/rc/', 'http://user@vps1:8000/rc/group/repo1'),
430 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'user'}, '/rc/', 'http://user@vps1:8000/rc/group/repo1'),
431 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'marcink'}, '/rc/', 'http://marcink@vps1:8000/rc/group/repo1'),
431 (Repository.DEFAULT_CLONE_URI, 'group/repo1', {'user': 'marcink'}, '/rc/', 'http://marcink@vps1:8000/rc/group/repo1'),
432 ('{scheme}://{user}@{netloc}/_{repoid}', 'group/repo1', {}, '', 'http://vps1:8000/_23'),
432 ('{scheme}://{user}@{netloc}/_{repoid}', 'group/repo1', {}, '', 'http://vps1:8000/_23'),
433 ('{scheme}://{user}@{netloc}/_{repoid}', 'group/repo1', {'user': 'marcink'}, '', 'http://marcink@vps1:8000/_23'),
433 ('{scheme}://{user}@{netloc}/_{repoid}', 'group/repo1', {'user': 'marcink'}, '', 'http://marcink@vps1:8000/_23'),
434 ('http://{user}@{netloc}/_{repoid}', 'group/repo1', {'user': 'marcink'}, '', 'http://marcink@vps1:8000/_23'),
434 ('http://{user}@{netloc}/_{repoid}', 'group/repo1', {'user': 'marcink'}, '', 'http://marcink@vps1:8000/_23'),
435 ('http://{netloc}/_{repoid}', 'group/repo1', {'user': 'marcink'}, '', 'http://vps1:8000/_23'),
435 ('http://{netloc}/_{repoid}', 'group/repo1', {'user': 'marcink'}, '', 'http://vps1:8000/_23'),
436 ('https://{user}@proxy1.server.com/{repo}', 'group/repo1', {'user': 'marcink'}, '', 'https://marcink@proxy1.server.com/group/repo1'),
436 ('https://{user}@proxy1.server.com/{repo}', 'group/repo1', {'user': 'marcink'}, '', 'https://marcink@proxy1.server.com/group/repo1'),
437 ('https://{user}@proxy1.server.com/{repo}', 'group/repo1', {}, '', 'https://proxy1.server.com/group/repo1'),
437 ('https://{user}@proxy1.server.com/{repo}', 'group/repo1', {}, '', 'https://proxy1.server.com/group/repo1'),
438 ('https://proxy1.server.com/{user}/{repo}', 'group/repo1', {'user': 'marcink'}, '', 'https://proxy1.server.com/marcink/group/repo1'),
438 ('https://proxy1.server.com/{user}/{repo}', 'group/repo1', {'user': 'marcink'}, '', 'https://proxy1.server.com/marcink/group/repo1'),
439 ])
439 ])
440 def test_clone_url_generator(tmpl, repo_name, overrides, prefix, expected):
440 def test_clone_url_generator(tmpl, repo_name, overrides, prefix, expected):
441 from rhodecode.lib.utils2 import get_clone_url
441 from rhodecode.lib.utils2 import get_clone_url
442
442
443 class RequestStub(object):
443 class RequestStub(object):
444 def request_url(self, name):
444 def request_url(self, name):
445 return 'http://vps1:8000' + prefix
445 return 'http://vps1:8000' + prefix
446
446
447 def route_url(self, name):
447 def route_url(self, name):
448 return self.request_url(name)
448 return self.request_url(name)
449
449
450 clone_url = get_clone_url(
450 clone_url = get_clone_url(
451 request=RequestStub(),
451 request=RequestStub(),
452 uri_tmpl=tmpl,
452 uri_tmpl=tmpl,
453 repo_name=repo_name, repo_id=23, **overrides)
453 repo_name=repo_name, repo_id=23, **overrides)
454 assert clone_url == expected
454 assert clone_url == expected
455
455
456
456
457 def _quick_url(text, tmpl="""<a class="revision-link" href="%s">%s</a>""", url_=None):
457 def _quick_url(text, tmpl="""<a class="revision-link" href="%s">%s</a>""", url_=None):
458 """
458 """
459 Changes `some text url[foo]` => `some text <a href="/">foo</a>
459 Changes `some text url[foo]` => `some text <a href="/">foo</a>
460
460
461 :param text:
461 :param text:
462 """
462 """
463 import re
463 import re
464 # quickly change expected url[] into a link
464 # quickly change expected url[] into a link
465 URL_PAT = re.compile(r'(?:url\[)(.+?)(?:\])')
465 URL_PAT = re.compile(r'(?:url\[)(.+?)(?:\])')
466
466
467 def url_func(match_obj):
467 def url_func(match_obj):
468 _url = match_obj.groups()[0]
468 _url = match_obj.groups()[0]
469 return tmpl % (url_ or '/some-url', _url)
469 return tmpl % (url_ or '/some-url', _url)
470 return URL_PAT.sub(url_func, text)
470 return URL_PAT.sub(url_func, text)
471
471
472
472
473 @pytest.mark.parametrize("sample, expected", [
473 @pytest.mark.parametrize("sample, expected", [
474 ("",
474 ("",
475 ""),
475 ""),
476 ("git-svn-id: https://svn.apache.org/repos/asf/libcloud/trunk@1441655 13f79535-47bb-0310-9956-ffa450edef68",
476 ("git-svn-id: https://svn.apache.org/repos/asf/libcloud/trunk@1441655 13f79535-47bb-0310-9956-ffa450edef68",
477 "git-svn-id: https://svn.apache.org/repos/asf/libcloud/trunk@1441655 13f79535-47bb-0310-9956-ffa450edef68"),
477 "git-svn-id: https://svn.apache.org/repos/asf/libcloud/trunk@1441655 13f79535-47bb-0310-9956-ffa450edef68"),
478 ("from rev 000000000000",
478 ("from rev 000000000000",
479 "from rev url[000000000000]"),
479 "from rev url[000000000000]"),
480 ("from rev 000000000000123123 also rev 000000000000",
480 ("from rev 000000000000123123 also rev 000000000000",
481 "from rev url[000000000000123123] also rev url[000000000000]"),
481 "from rev url[000000000000123123] also rev url[000000000000]"),
482 ("this should-000 00",
482 ("this should-000 00",
483 "this should-000 00"),
483 "this should-000 00"),
484 ("longtextffffffffff rev 123123123123",
484 ("longtextffffffffff rev 123123123123",
485 "longtextffffffffff rev url[123123123123]"),
485 "longtextffffffffff rev url[123123123123]"),
486 ("rev ffffffffffffffffffffffffffffffffffffffffffffffffff",
486 ("rev ffffffffffffffffffffffffffffffffffffffffffffffffff",
487 "rev ffffffffffffffffffffffffffffffffffffffffffffffffff"),
487 "rev ffffffffffffffffffffffffffffffffffffffffffffffffff"),
488 ("ffffffffffff some text traalaa",
488 ("ffffffffffff some text traalaa",
489 "url[ffffffffffff] some text traalaa"),
489 "url[ffffffffffff] some text traalaa"),
490 ("""Multi line
490 ("""Multi line
491 123123123123
491 123123123123
492 some text 123123123123
492 some text 123123123123
493 sometimes !
493 sometimes !
494 """,
494 """,
495 """Multi line
495 """Multi line
496 url[123123123123]
496 url[123123123123]
497 some text url[123123123123]
497 some text url[123123123123]
498 sometimes !
498 sometimes !
499 """)
499 """)
500 ], ids=no_newline_id_generator)
500 ], ids=no_newline_id_generator)
501 def test_urlify_commits(sample, expected):
501 def test_urlify_commits(sample, expected):
502 def fake_url(self, *args, **kwargs):
502 def fake_url(self, *args, **kwargs):
503 return '/some-url'
503 return '/some-url'
504
504
505 expected = _quick_url(expected)
505 expected = _quick_url(expected)
506
506
507 with mock.patch('rhodecode.lib.helpers.route_url', fake_url):
507 with mock.patch('rhodecode.lib.helpers.route_url', fake_url):
508 from rhodecode.lib.helpers import urlify_commits
508 from rhodecode.lib.helpers import urlify_commits
509 assert urlify_commits(sample, 'repo_name') == expected
509 assert urlify_commits(sample, 'repo_name') == expected
510
510
511
511
512 @pytest.mark.parametrize("sample, expected, url_", [
512 @pytest.mark.parametrize("sample, expected, url_", [
513 ("",
513 ("",
514 "",
514 "",
515 ""),
515 ""),
516 ("https://svn.apache.org/repos",
516 ("https://svn.apache.org/repos",
517 "url[https://svn.apache.org/repos]",
517 "url[https://svn.apache.org/repos]",
518 "https://svn.apache.org/repos"),
518 "https://svn.apache.org/repos"),
519 ("http://svn.apache.org/repos",
519 ("http://svn.apache.org/repos",
520 "url[http://svn.apache.org/repos]",
520 "url[http://svn.apache.org/repos]",
521 "http://svn.apache.org/repos"),
521 "http://svn.apache.org/repos"),
522 ("from rev a also rev http://google.com",
522 ("from rev a also rev http://google.com",
523 "from rev a also rev url[http://google.com]",
523 "from rev a also rev url[http://google.com]",
524 "http://google.com"),
524 "http://google.com"),
525 ("""Multi line
525 ("""Multi line
526 https://foo.bar.com
526 https://foo.bar.com
527 some text lalala""",
527 some text lalala""",
528 """Multi line
528 """Multi line
529 url[https://foo.bar.com]
529 url[https://foo.bar.com]
530 some text lalala""",
530 some text lalala""",
531 "https://foo.bar.com")
531 "https://foo.bar.com")
532 ], ids=no_newline_id_generator)
532 ], ids=no_newline_id_generator)
533 def test_urlify_test(sample, expected, url_):
533 def test_urlify_test(sample, expected, url_):
534 from rhodecode.lib.helpers import urlify_text
534 from rhodecode.lib.helpers import urlify_text
535 expected = _quick_url(expected, tmpl="""<a href="%s">%s</a>""", url_=url_)
535 expected = _quick_url(expected, tmpl="""<a href="%s">%s</a>""", url_=url_)
536 assert urlify_text(sample) == expected
536 assert urlify_text(sample) == expected
537
537
538
538
539 @pytest.mark.parametrize("test, expected", [
539 @pytest.mark.parametrize("test, expected", [
540 ("", None),
540 ("", None),
541 ("/_2", '2'),
541 ("/_2", '2'),
542 ("_2", '2'),
542 ("_2", '2'),
543 ("/_2/", '2'),
543 ("/_2/", '2'),
544 ("_2/", '2'),
544 ("_2/", '2'),
545
545
546 ("/_21", '21'),
546 ("/_21", '21'),
547 ("_21", '21'),
547 ("_21", '21'),
548 ("/_21/", '21'),
548 ("/_21/", '21'),
549 ("_21/", '21'),
549 ("_21/", '21'),
550
550
551 ("/_21/foobar", '21'),
551 ("/_21/foobar", '21'),
552 ("_21/121", '21'),
552 ("_21/121", '21'),
553 ("/_21/_12", '21'),
553 ("/_21/_12", '21'),
554 ("_21/rc/foo", '21'),
554 ("_21/rc/foo", '21'),
555
555
556 ])
556 ])
557 def test_get_repo_by_id(test, expected):
557 def test_get_repo_by_id(test, expected):
558 from rhodecode.model.repo import RepoModel
558 from rhodecode.model.repo import RepoModel
559 _test = RepoModel()._extract_id_from_repo_name(test)
559 _test = RepoModel()._extract_id_from_repo_name(test)
560 assert _test == expected
560 assert _test == expected
561
561
562
562
563 def test_invalidation_context(baseapp):
563 def test_invalidation_context(baseapp):
564 repo_id = 9999
564 repo_id = 9999
565
565
566 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
566 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
567 repo_id, CacheKey.CACHE_TYPE_README)
567 repo_id, CacheKey.CACHE_TYPE_FEED)
568 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
568 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
569 repo_id=repo_id)
569 repo_id=repo_id)
570 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
570 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
571
571
572 calls = [1, 2]
572 calls = [1, 2]
573
573
574 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
574 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
575 def _dummy_func(cache_key):
575 def _dummy_func(cache_key):
576 val = calls.pop(0)
576 val = calls.pop(0)
577 return 'result:{}'.format(val)
577 return 'result:{}'.format(val)
578
578
579 inv_context_manager = rc_cache.InvalidationContext(
579 inv_context_manager = rc_cache.InvalidationContext(
580 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
580 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
581
581
582 # 1st call, fresh caches
582 # 1st call, fresh caches
583 with inv_context_manager as invalidation_context:
583 with inv_context_manager as invalidation_context:
584 should_invalidate = invalidation_context.should_invalidate()
584 should_invalidate = invalidation_context.should_invalidate()
585 if should_invalidate:
585 if should_invalidate:
586 result = _dummy_func.refresh('some-key')
586 result = _dummy_func.refresh('some-key')
587 else:
587 else:
588 result = _dummy_func('some-key')
588 result = _dummy_func('some-key')
589
589
590 assert isinstance(invalidation_context, rc_cache.FreshRegionCache)
590 assert isinstance(invalidation_context, rc_cache.FreshRegionCache)
591 assert should_invalidate is True
591 assert should_invalidate is True
592
592
593 assert 'result:1' == result
593 assert 'result:1' == result
594 # should be cached so calling it twice will give the same result !
594 # should be cached so calling it twice will give the same result !
595 result = _dummy_func('some-key')
595 result = _dummy_func('some-key')
596 assert 'result:1' == result
596 assert 'result:1' == result
597
597
598 # 2nd call, we create a new context manager, this should be now key aware, and
598 # 2nd call, we create a new context manager, this should be now key aware, and
599 # return an active cache region
599 # return an active cache region
600 with inv_context_manager as invalidation_context:
600 with inv_context_manager as invalidation_context:
601 should_invalidate = invalidation_context.should_invalidate()
601 should_invalidate = invalidation_context.should_invalidate()
602 assert isinstance(invalidation_context, rc_cache.ActiveRegionCache)
602 assert isinstance(invalidation_context, rc_cache.ActiveRegionCache)
603 assert should_invalidate is False
603 assert should_invalidate is False
604
604
605 # Mark invalidation
605 # Mark invalidation
606 CacheKey.set_invalidate(invalidation_namespace)
606 CacheKey.set_invalidate(invalidation_namespace)
607
607
608 # 3nd call, fresh caches
608 # 3nd call, fresh caches
609 with inv_context_manager as invalidation_context:
609 with inv_context_manager as invalidation_context:
610 should_invalidate = invalidation_context.should_invalidate()
610 should_invalidate = invalidation_context.should_invalidate()
611 if should_invalidate:
611 if should_invalidate:
612 result = _dummy_func.refresh('some-key')
612 result = _dummy_func.refresh('some-key')
613 else:
613 else:
614 result = _dummy_func('some-key')
614 result = _dummy_func('some-key')
615
615
616 assert isinstance(invalidation_context, rc_cache.FreshRegionCache)
616 assert isinstance(invalidation_context, rc_cache.FreshRegionCache)
617 assert should_invalidate is True
617 assert should_invalidate is True
618
618
619 assert 'result:2' == result
619 assert 'result:2' == result
620
620
621 # cached again, same result
621 # cached again, same result
622 result = _dummy_func('some-key')
622 result = _dummy_func('some-key')
623 assert 'result:2' == result
623 assert 'result:2' == result
624
624
625
625
626 def test_invalidation_context_exception_in_compute(baseapp):
626 def test_invalidation_context_exception_in_compute(baseapp):
627 repo_id = 888
627 repo_id = 888
628
628
629 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
629 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
630 repo_id, CacheKey.CACHE_TYPE_README)
630 repo_id, CacheKey.CACHE_TYPE_FEED)
631 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
631 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
632 repo_id=repo_id)
632 repo_id=repo_id)
633 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
633 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
634
634
635 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
635 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
636 def _dummy_func(cache_key):
636 def _dummy_func(cache_key):
637 raise Exception('Error in cache func')
637 raise Exception('Error in cache func')
638
638
639 with pytest.raises(Exception):
639 with pytest.raises(Exception):
640 inv_context_manager = rc_cache.InvalidationContext(
640 inv_context_manager = rc_cache.InvalidationContext(
641 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
641 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
642
642
643 # 1st call, fresh caches
643 # 1st call, fresh caches
644 with inv_context_manager as invalidation_context:
644 with inv_context_manager as invalidation_context:
645 should_invalidate = invalidation_context.should_invalidate()
645 should_invalidate = invalidation_context.should_invalidate()
646 if should_invalidate:
646 if should_invalidate:
647 _dummy_func.refresh('some-key-2')
647 _dummy_func.refresh('some-key-2')
648 else:
648 else:
649 _dummy_func('some-key-2')
649 _dummy_func('some-key-2')
650
650
651
651
652 @pytest.mark.parametrize('execution_number', range(5))
652 @pytest.mark.parametrize('execution_number', range(5))
653 def test_cache_invalidation_race_condition(execution_number, baseapp):
653 def test_cache_invalidation_race_condition(execution_number, baseapp):
654 import time
654 import time
655
655
656 repo_id = 777
656 repo_id = 777
657
657
658 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
658 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
659 repo_id, CacheKey.CACHE_TYPE_README)
659 repo_id, CacheKey.CACHE_TYPE_FEED)
660 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
660 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
661 repo_id=repo_id)
661 repo_id=repo_id)
662 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
662 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
663
663
664 @run_test_concurrently(25)
664 @run_test_concurrently(25)
665 def test_create_and_delete_cache_keys():
665 def test_create_and_delete_cache_keys():
666 time.sleep(0.2)
666 time.sleep(0.2)
667
667
668 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
668 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
669 def _dummy_func(cache_key):
669 def _dummy_func(cache_key):
670 val = 'async'
670 val = 'async'
671 return 'result:{}'.format(val)
671 return 'result:{}'.format(val)
672
672
673 inv_context_manager = rc_cache.InvalidationContext(
673 inv_context_manager = rc_cache.InvalidationContext(
674 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
674 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
675
675
676 # 1st call, fresh caches
676 # 1st call, fresh caches
677 with inv_context_manager as invalidation_context:
677 with inv_context_manager as invalidation_context:
678 should_invalidate = invalidation_context.should_invalidate()
678 should_invalidate = invalidation_context.should_invalidate()
679 if should_invalidate:
679 if should_invalidate:
680 _dummy_func.refresh('some-key-3')
680 _dummy_func.refresh('some-key-3')
681 else:
681 else:
682 _dummy_func('some-key-3')
682 _dummy_func('some-key-3')
683
683
684 # Mark invalidation
684 # Mark invalidation
685 CacheKey.set_invalidate(invalidation_namespace)
685 CacheKey.set_invalidate(invalidation_namespace)
686
686
687 test_create_and_delete_cache_keys()
687 test_create_and_delete_cache_keys()
General Comments 0
You need to be logged in to leave comments. Login now