##// END OF EJS Templates
moved top-level tests to rhodecode/tests/other....
marcink -
r3824:a5746b83 beta
parent child Browse files
Show More
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -1,816 +1,816
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 import decorator
35 import decorator
36 import warnings
36 import warnings
37 from os.path import abspath
37 from os.path import abspath
38 from os.path import dirname as dn, join as jn
38 from os.path import dirname as dn, join as jn
39
39
40 from paste.script.command import Command, BadCommand
40 from paste.script.command import Command, BadCommand
41
41
42 from mercurial import ui, config
42 from mercurial import ui, config
43
43
44 from webhelpers.text import collapse, remove_formatting, strip_tags
44 from webhelpers.text import collapse, remove_formatting, strip_tags
45
45
46 from rhodecode.lib.vcs import get_backend
46 from rhodecode.lib.vcs import get_backend
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
49 from rhodecode.lib.vcs.utils.helpers import get_scm
49 from rhodecode.lib.vcs.utils.helpers import get_scm
50 from rhodecode.lib.vcs.exceptions import VCSError
50 from rhodecode.lib.vcs.exceptions import VCSError
51
51
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model import meta
54 from rhodecode.model import meta
55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation, UserGroup
56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation, UserGroup
57 from rhodecode.model.meta import Session
57 from rhodecode.model.meta import Session
58 from rhodecode.model.repos_group import ReposGroupModel
58 from rhodecode.model.repos_group import ReposGroupModel
59 from rhodecode.lib.utils2 import safe_str, safe_unicode
59 from rhodecode.lib.utils2 import safe_str, safe_unicode
60 from rhodecode.lib.vcs.utils.fakemod import create_module
60 from rhodecode.lib.vcs.utils.fakemod import create_module
61 from rhodecode.model.users_group import UserGroupModel
61 from rhodecode.model.users_group import UserGroupModel
62
62
63 log = logging.getLogger(__name__)
63 log = logging.getLogger(__name__)
64
64
65 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
65 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
66
66
67
67
68 def recursive_replace(str_, replace=' '):
68 def recursive_replace(str_, replace=' '):
69 """
69 """
70 Recursive replace of given sign to just one instance
70 Recursive replace of given sign to just one instance
71
71
72 :param str_: given string
72 :param str_: given string
73 :param replace: char to find and replace multiple instances
73 :param replace: char to find and replace multiple instances
74
74
75 Examples::
75 Examples::
76 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
76 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
77 'Mighty-Mighty-Bo-sstones'
77 'Mighty-Mighty-Bo-sstones'
78 """
78 """
79
79
80 if str_.find(replace * 2) == -1:
80 if str_.find(replace * 2) == -1:
81 return str_
81 return str_
82 else:
82 else:
83 str_ = str_.replace(replace * 2, replace)
83 str_ = str_.replace(replace * 2, replace)
84 return recursive_replace(str_, replace)
84 return recursive_replace(str_, replace)
85
85
86
86
87 def repo_name_slug(value):
87 def repo_name_slug(value):
88 """
88 """
89 Return slug of name of repository
89 Return slug of name of repository
90 This function is called on each creation/modification
90 This function is called on each creation/modification
91 of repository to prevent bad names in repo
91 of repository to prevent bad names in repo
92 """
92 """
93
93
94 slug = remove_formatting(value)
94 slug = remove_formatting(value)
95 slug = strip_tags(slug)
95 slug = strip_tags(slug)
96
96
97 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
97 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
98 slug = slug.replace(c, '-')
98 slug = slug.replace(c, '-')
99 slug = recursive_replace(slug, '-')
99 slug = recursive_replace(slug, '-')
100 slug = collapse(slug, '-')
100 slug = collapse(slug, '-')
101 return slug
101 return slug
102
102
103
103
104 #==============================================================================
104 #==============================================================================
105 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
105 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
106 #==============================================================================
106 #==============================================================================
107 def get_repo_slug(request):
107 def get_repo_slug(request):
108 _repo = request.environ['pylons.routes_dict'].get('repo_name')
108 _repo = request.environ['pylons.routes_dict'].get('repo_name')
109 if _repo:
109 if _repo:
110 _repo = _repo.rstrip('/')
110 _repo = _repo.rstrip('/')
111 return _repo
111 return _repo
112
112
113
113
114 def get_repos_group_slug(request):
114 def get_repos_group_slug(request):
115 _group = request.environ['pylons.routes_dict'].get('group_name')
115 _group = request.environ['pylons.routes_dict'].get('group_name')
116 if _group:
116 if _group:
117 _group = _group.rstrip('/')
117 _group = _group.rstrip('/')
118 return _group
118 return _group
119
119
120
120
121 def get_user_group_slug(request):
121 def get_user_group_slug(request):
122 _group = request.environ['pylons.routes_dict'].get('id')
122 _group = request.environ['pylons.routes_dict'].get('id')
123 try:
123 try:
124 _group = UserGroup.get(_group)
124 _group = UserGroup.get(_group)
125 if _group:
125 if _group:
126 _group = _group.users_group_name
126 _group = _group.users_group_name
127 except Exception:
127 except Exception:
128 log.debug(traceback.format_exc())
128 log.debug(traceback.format_exc())
129 #catch all failures here
129 #catch all failures here
130 pass
130 pass
131
131
132 return _group
132 return _group
133
133
134
134
135 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
135 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
136 """
136 """
137 Action logger for various actions made by users
137 Action logger for various actions made by users
138
138
139 :param user: user that made this action, can be a unique username string or
139 :param user: user that made this action, can be a unique username string or
140 object containing user_id attribute
140 object containing user_id attribute
141 :param action: action to log, should be on of predefined unique actions for
141 :param action: action to log, should be on of predefined unique actions for
142 easy translations
142 easy translations
143 :param repo: string name of repository or object containing repo_id,
143 :param repo: string name of repository or object containing repo_id,
144 that action was made on
144 that action was made on
145 :param ipaddr: optional ip address from what the action was made
145 :param ipaddr: optional ip address from what the action was made
146 :param sa: optional sqlalchemy session
146 :param sa: optional sqlalchemy session
147
147
148 """
148 """
149
149
150 if not sa:
150 if not sa:
151 sa = meta.Session()
151 sa = meta.Session()
152
152
153 try:
153 try:
154 if hasattr(user, 'user_id'):
154 if hasattr(user, 'user_id'):
155 user_obj = User.get(user.user_id)
155 user_obj = User.get(user.user_id)
156 elif isinstance(user, basestring):
156 elif isinstance(user, basestring):
157 user_obj = User.get_by_username(user)
157 user_obj = User.get_by_username(user)
158 else:
158 else:
159 raise Exception('You have to provide a user object or a username')
159 raise Exception('You have to provide a user object or a username')
160
160
161 if hasattr(repo, 'repo_id'):
161 if hasattr(repo, 'repo_id'):
162 repo_obj = Repository.get(repo.repo_id)
162 repo_obj = Repository.get(repo.repo_id)
163 repo_name = repo_obj.repo_name
163 repo_name = repo_obj.repo_name
164 elif isinstance(repo, basestring):
164 elif isinstance(repo, basestring):
165 repo_name = repo.lstrip('/')
165 repo_name = repo.lstrip('/')
166 repo_obj = Repository.get_by_repo_name(repo_name)
166 repo_obj = Repository.get_by_repo_name(repo_name)
167 else:
167 else:
168 repo_obj = None
168 repo_obj = None
169 repo_name = ''
169 repo_name = ''
170
170
171 user_log = UserLog()
171 user_log = UserLog()
172 user_log.user_id = user_obj.user_id
172 user_log.user_id = user_obj.user_id
173 user_log.username = user_obj.username
173 user_log.username = user_obj.username
174 user_log.action = safe_unicode(action)
174 user_log.action = safe_unicode(action)
175
175
176 user_log.repository = repo_obj
176 user_log.repository = repo_obj
177 user_log.repository_name = repo_name
177 user_log.repository_name = repo_name
178
178
179 user_log.action_date = datetime.datetime.now()
179 user_log.action_date = datetime.datetime.now()
180 user_log.user_ip = ipaddr
180 user_log.user_ip = ipaddr
181 sa.add(user_log)
181 sa.add(user_log)
182
182
183 log.info('Logging action:%s on %s by user:%s ip:%s' %
183 log.info('Logging action:%s on %s by user:%s ip:%s' %
184 (action, safe_unicode(repo), user_obj, ipaddr))
184 (action, safe_unicode(repo), user_obj, ipaddr))
185 if commit:
185 if commit:
186 sa.commit()
186 sa.commit()
187 except Exception:
187 except Exception:
188 log.error(traceback.format_exc())
188 log.error(traceback.format_exc())
189 raise
189 raise
190
190
191
191
192 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
192 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
193 """
193 """
194 Scans given path for repos and return (name,(type,path)) tuple
194 Scans given path for repos and return (name,(type,path)) tuple
195
195
196 :param path: path to scan for repositories
196 :param path: path to scan for repositories
197 :param recursive: recursive search and return names with subdirs in front
197 :param recursive: recursive search and return names with subdirs in front
198 """
198 """
199
199
200 # remove ending slash for better results
200 # remove ending slash for better results
201 path = path.rstrip(os.sep)
201 path = path.rstrip(os.sep)
202 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
202 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
203
203
204 def _get_repos(p):
204 def _get_repos(p):
205 if not os.access(p, os.W_OK):
205 if not os.access(p, os.W_OK):
206 log.warn('ignoring repo path without write access: %s', p)
206 log.warn('ignoring repo path without write access: %s', p)
207 return
207 return
208 for dirpath in os.listdir(p):
208 for dirpath in os.listdir(p):
209 if os.path.isfile(os.path.join(p, dirpath)):
209 if os.path.isfile(os.path.join(p, dirpath)):
210 continue
210 continue
211 cur_path = os.path.join(p, dirpath)
211 cur_path = os.path.join(p, dirpath)
212
212
213 # skip removed repos
213 # skip removed repos
214 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
214 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
215 continue
215 continue
216
216
217 #skip .<somethin> dirs
217 #skip .<somethin> dirs
218 if dirpath.startswith('.'):
218 if dirpath.startswith('.'):
219 continue
219 continue
220
220
221 try:
221 try:
222 scm_info = get_scm(cur_path)
222 scm_info = get_scm(cur_path)
223 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
223 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
224 except VCSError:
224 except VCSError:
225 if not recursive:
225 if not recursive:
226 continue
226 continue
227 #check if this dir containts other repos for recursive scan
227 #check if this dir containts other repos for recursive scan
228 rec_path = os.path.join(p, dirpath)
228 rec_path = os.path.join(p, dirpath)
229 if os.path.isdir(rec_path):
229 if os.path.isdir(rec_path):
230 for inner_scm in _get_repos(rec_path):
230 for inner_scm in _get_repos(rec_path):
231 yield inner_scm
231 yield inner_scm
232
232
233 return _get_repos(path)
233 return _get_repos(path)
234
234
235
235
236 def is_valid_repo(repo_name, base_path, scm=None):
236 def is_valid_repo(repo_name, base_path, scm=None):
237 """
237 """
238 Returns True if given path is a valid repository False otherwise.
238 Returns True if given path is a valid repository False otherwise.
239 If scm param is given also compare if given scm is the same as expected
239 If scm param is given also compare if given scm is the same as expected
240 from scm parameter
240 from scm parameter
241
241
242 :param repo_name:
242 :param repo_name:
243 :param base_path:
243 :param base_path:
244 :param scm:
244 :param scm:
245
245
246 :return True: if given path is a valid repository
246 :return True: if given path is a valid repository
247 """
247 """
248 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
248 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
249
249
250 try:
250 try:
251 scm_ = get_scm(full_path)
251 scm_ = get_scm(full_path)
252 if scm:
252 if scm:
253 return scm_[0] == scm
253 return scm_[0] == scm
254 return True
254 return True
255 except VCSError:
255 except VCSError:
256 return False
256 return False
257
257
258
258
259 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
259 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
260 """
260 """
261 Returns True if given path is a repository group False otherwise
261 Returns True if given path is a repository group False otherwise
262
262
263 :param repo_name:
263 :param repo_name:
264 :param base_path:
264 :param base_path:
265 """
265 """
266 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
266 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
267
267
268 # check if it's not a repo
268 # check if it's not a repo
269 if is_valid_repo(repos_group_name, base_path):
269 if is_valid_repo(repos_group_name, base_path):
270 return False
270 return False
271
271
272 try:
272 try:
273 # we need to check bare git repos at higher level
273 # we need to check bare git repos at higher level
274 # since we might match branches/hooks/info/objects or possible
274 # since we might match branches/hooks/info/objects or possible
275 # other things inside bare git repo
275 # other things inside bare git repo
276 get_scm(os.path.dirname(full_path))
276 get_scm(os.path.dirname(full_path))
277 return False
277 return False
278 except VCSError:
278 except VCSError:
279 pass
279 pass
280
280
281 # check if it's a valid path
281 # check if it's a valid path
282 if skip_path_check or os.path.isdir(full_path):
282 if skip_path_check or os.path.isdir(full_path):
283 return True
283 return True
284
284
285 return False
285 return False
286
286
287
287
288 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
288 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
289 while True:
289 while True:
290 ok = raw_input(prompt)
290 ok = raw_input(prompt)
291 if ok in ('y', 'ye', 'yes'):
291 if ok in ('y', 'ye', 'yes'):
292 return True
292 return True
293 if ok in ('n', 'no', 'nop', 'nope'):
293 if ok in ('n', 'no', 'nop', 'nope'):
294 return False
294 return False
295 retries = retries - 1
295 retries = retries - 1
296 if retries < 0:
296 if retries < 0:
297 raise IOError
297 raise IOError
298 print complaint
298 print complaint
299
299
300 #propagated from mercurial documentation
300 #propagated from mercurial documentation
301 ui_sections = ['alias', 'auth',
301 ui_sections = ['alias', 'auth',
302 'decode/encode', 'defaults',
302 'decode/encode', 'defaults',
303 'diff', 'email',
303 'diff', 'email',
304 'extensions', 'format',
304 'extensions', 'format',
305 'merge-patterns', 'merge-tools',
305 'merge-patterns', 'merge-tools',
306 'hooks', 'http_proxy',
306 'hooks', 'http_proxy',
307 'smtp', 'patch',
307 'smtp', 'patch',
308 'paths', 'profiling',
308 'paths', 'profiling',
309 'server', 'trusted',
309 'server', 'trusted',
310 'ui', 'web', ]
310 'ui', 'web', ]
311
311
312
312
313 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
313 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
314 """
314 """
315 A function that will read python rc files or database
315 A function that will read python rc files or database
316 and make an mercurial ui object from read options
316 and make an mercurial ui object from read options
317
317
318 :param path: path to mercurial config file
318 :param path: path to mercurial config file
319 :param checkpaths: check the path
319 :param checkpaths: check the path
320 :param read_from: read from 'file' or 'db'
320 :param read_from: read from 'file' or 'db'
321 """
321 """
322
322
323 baseui = ui.ui()
323 baseui = ui.ui()
324
324
325 # clean the baseui object
325 # clean the baseui object
326 baseui._ocfg = config.config()
326 baseui._ocfg = config.config()
327 baseui._ucfg = config.config()
327 baseui._ucfg = config.config()
328 baseui._tcfg = config.config()
328 baseui._tcfg = config.config()
329
329
330 if read_from == 'file':
330 if read_from == 'file':
331 if not os.path.isfile(path):
331 if not os.path.isfile(path):
332 log.debug('hgrc file is not present at %s, skipping...' % path)
332 log.debug('hgrc file is not present at %s, skipping...' % path)
333 return False
333 return False
334 log.debug('reading hgrc from %s' % path)
334 log.debug('reading hgrc from %s' % path)
335 cfg = config.config()
335 cfg = config.config()
336 cfg.read(path)
336 cfg.read(path)
337 for section in ui_sections:
337 for section in ui_sections:
338 for k, v in cfg.items(section):
338 for k, v in cfg.items(section):
339 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
339 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
340 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
340 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
341
341
342 elif read_from == 'db':
342 elif read_from == 'db':
343 sa = meta.Session()
343 sa = meta.Session()
344 ret = sa.query(RhodeCodeUi)\
344 ret = sa.query(RhodeCodeUi)\
345 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
345 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
346 .all()
346 .all()
347
347
348 hg_ui = ret
348 hg_ui = ret
349 for ui_ in hg_ui:
349 for ui_ in hg_ui:
350 if ui_.ui_active:
350 if ui_.ui_active:
351 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
351 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
352 ui_.ui_key, ui_.ui_value)
352 ui_.ui_key, ui_.ui_value)
353 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
353 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
354 safe_str(ui_.ui_value))
354 safe_str(ui_.ui_value))
355 if ui_.ui_key == 'push_ssl':
355 if ui_.ui_key == 'push_ssl':
356 # force set push_ssl requirement to False, rhodecode
356 # force set push_ssl requirement to False, rhodecode
357 # handles that
357 # handles that
358 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
358 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
359 False)
359 False)
360 if clear_session:
360 if clear_session:
361 meta.Session.remove()
361 meta.Session.remove()
362 return baseui
362 return baseui
363
363
364
364
365 def set_rhodecode_config(config):
365 def set_rhodecode_config(config):
366 """
366 """
367 Updates pylons config with new settings from database
367 Updates pylons config with new settings from database
368
368
369 :param config:
369 :param config:
370 """
370 """
371 hgsettings = RhodeCodeSetting.get_app_settings()
371 hgsettings = RhodeCodeSetting.get_app_settings()
372
372
373 for k, v in hgsettings.items():
373 for k, v in hgsettings.items():
374 config[k] = v
374 config[k] = v
375
375
376
376
377 def set_vcs_config(config):
377 def set_vcs_config(config):
378 """
378 """
379 Patch VCS config with some RhodeCode specific stuff
379 Patch VCS config with some RhodeCode specific stuff
380
380
381 :param config: rhodecode.CONFIG
381 :param config: rhodecode.CONFIG
382 """
382 """
383 import rhodecode
383 import rhodecode
384 from rhodecode.lib.vcs import conf
384 from rhodecode.lib.vcs import conf
385 from rhodecode.lib.utils2 import aslist
385 from rhodecode.lib.utils2 import aslist
386 conf.settings.BACKENDS = {
386 conf.settings.BACKENDS = {
387 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository',
387 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository',
388 'git': 'rhodecode.lib.vcs.backends.git.GitRepository',
388 'git': 'rhodecode.lib.vcs.backends.git.GitRepository',
389 }
389 }
390
390
391 conf.settings.GIT_EXECUTABLE_PATH = config.get('git_path', 'git')
391 conf.settings.GIT_EXECUTABLE_PATH = config.get('git_path', 'git')
392 conf.settings.GIT_REV_FILTER = config.get('git_rev_filter', '--all').strip()
392 conf.settings.GIT_REV_FILTER = config.get('git_rev_filter', '--all').strip()
393 conf.settings.DEFAULT_ENCODINGS = aslist(config.get('default_encoding',
393 conf.settings.DEFAULT_ENCODINGS = aslist(config.get('default_encoding',
394 'utf8'), sep=',')
394 'utf8'), sep=',')
395
395
396
396
397 def map_groups(path):
397 def map_groups(path):
398 """
398 """
399 Given a full path to a repository, create all nested groups that this
399 Given a full path to a repository, create all nested groups that this
400 repo is inside. This function creates parent-child relationships between
400 repo is inside. This function creates parent-child relationships between
401 groups and creates default perms for all new groups.
401 groups and creates default perms for all new groups.
402
402
403 :param paths: full path to repository
403 :param paths: full path to repository
404 """
404 """
405 sa = meta.Session()
405 sa = meta.Session()
406 groups = path.split(Repository.url_sep())
406 groups = path.split(Repository.url_sep())
407 parent = None
407 parent = None
408 group = None
408 group = None
409
409
410 # last element is repo in nested groups structure
410 # last element is repo in nested groups structure
411 groups = groups[:-1]
411 groups = groups[:-1]
412 rgm = ReposGroupModel(sa)
412 rgm = ReposGroupModel(sa)
413 owner = User.get_first_admin()
413 owner = User.get_first_admin()
414 for lvl, group_name in enumerate(groups):
414 for lvl, group_name in enumerate(groups):
415 group_name = '/'.join(groups[:lvl] + [group_name])
415 group_name = '/'.join(groups[:lvl] + [group_name])
416 group = RepoGroup.get_by_group_name(group_name)
416 group = RepoGroup.get_by_group_name(group_name)
417 desc = '%s group' % group_name
417 desc = '%s group' % group_name
418
418
419 # skip folders that are now removed repos
419 # skip folders that are now removed repos
420 if REMOVED_REPO_PAT.match(group_name):
420 if REMOVED_REPO_PAT.match(group_name):
421 break
421 break
422
422
423 if group is None:
423 if group is None:
424 log.debug('creating group level: %s group_name: %s'
424 log.debug('creating group level: %s group_name: %s'
425 % (lvl, group_name))
425 % (lvl, group_name))
426 group = RepoGroup(group_name, parent)
426 group = RepoGroup(group_name, parent)
427 group.group_description = desc
427 group.group_description = desc
428 group.user = owner
428 group.user = owner
429 sa.add(group)
429 sa.add(group)
430 perm_obj = rgm._create_default_perms(group)
430 perm_obj = rgm._create_default_perms(group)
431 sa.add(perm_obj)
431 sa.add(perm_obj)
432 sa.flush()
432 sa.flush()
433
433
434 parent = group
434 parent = group
435 return group
435 return group
436
436
437
437
438 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
438 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
439 install_git_hook=False):
439 install_git_hook=False):
440 """
440 """
441 maps all repos given in initial_repo_list, non existing repositories
441 maps all repos given in initial_repo_list, non existing repositories
442 are created, if remove_obsolete is True it also check for db entries
442 are created, if remove_obsolete is True it also check for db entries
443 that are not in initial_repo_list and removes them.
443 that are not in initial_repo_list and removes them.
444
444
445 :param initial_repo_list: list of repositories found by scanning methods
445 :param initial_repo_list: list of repositories found by scanning methods
446 :param remove_obsolete: check for obsolete entries in database
446 :param remove_obsolete: check for obsolete entries in database
447 :param install_git_hook: if this is True, also check and install githook
447 :param install_git_hook: if this is True, also check and install githook
448 for a repo if missing
448 for a repo if missing
449 """
449 """
450 from rhodecode.model.repo import RepoModel
450 from rhodecode.model.repo import RepoModel
451 from rhodecode.model.scm import ScmModel
451 from rhodecode.model.scm import ScmModel
452 sa = meta.Session()
452 sa = meta.Session()
453 rm = RepoModel()
453 rm = RepoModel()
454 user = User.get_first_admin()
454 user = User.get_first_admin()
455 added = []
455 added = []
456
456
457 ##creation defaults
457 ##creation defaults
458 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
458 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
459 enable_statistics = defs.get('repo_enable_statistics')
459 enable_statistics = defs.get('repo_enable_statistics')
460 enable_locking = defs.get('repo_enable_locking')
460 enable_locking = defs.get('repo_enable_locking')
461 enable_downloads = defs.get('repo_enable_downloads')
461 enable_downloads = defs.get('repo_enable_downloads')
462 private = defs.get('repo_private')
462 private = defs.get('repo_private')
463
463
464 for name, repo in initial_repo_list.items():
464 for name, repo in initial_repo_list.items():
465 group = map_groups(name)
465 group = map_groups(name)
466 db_repo = rm.get_by_repo_name(name)
466 db_repo = rm.get_by_repo_name(name)
467 # found repo that is on filesystem not in RhodeCode database
467 # found repo that is on filesystem not in RhodeCode database
468 if not db_repo:
468 if not db_repo:
469 log.info('repository %s not found, creating now' % name)
469 log.info('repository %s not found, creating now' % name)
470 added.append(name)
470 added.append(name)
471 desc = (repo.description
471 desc = (repo.description
472 if repo.description != 'unknown'
472 if repo.description != 'unknown'
473 else '%s repository' % name)
473 else '%s repository' % name)
474
474
475 new_repo = rm.create_repo(
475 new_repo = rm.create_repo(
476 repo_name=name,
476 repo_name=name,
477 repo_type=repo.alias,
477 repo_type=repo.alias,
478 description=desc,
478 description=desc,
479 repos_group=getattr(group, 'group_id', None),
479 repos_group=getattr(group, 'group_id', None),
480 owner=user,
480 owner=user,
481 just_db=True,
481 just_db=True,
482 enable_locking=enable_locking,
482 enable_locking=enable_locking,
483 enable_downloads=enable_downloads,
483 enable_downloads=enable_downloads,
484 enable_statistics=enable_statistics,
484 enable_statistics=enable_statistics,
485 private=private
485 private=private
486 )
486 )
487 # we added that repo just now, and make sure it has githook
487 # we added that repo just now, and make sure it has githook
488 # installed
488 # installed
489 if new_repo.repo_type == 'git':
489 if new_repo.repo_type == 'git':
490 ScmModel().install_git_hook(new_repo.scm_instance)
490 ScmModel().install_git_hook(new_repo.scm_instance)
491 new_repo.update_changeset_cache()
491 new_repo.update_changeset_cache()
492 elif install_git_hook:
492 elif install_git_hook:
493 if db_repo.repo_type == 'git':
493 if db_repo.repo_type == 'git':
494 ScmModel().install_git_hook(db_repo.scm_instance)
494 ScmModel().install_git_hook(db_repo.scm_instance)
495
495
496 sa.commit()
496 sa.commit()
497 removed = []
497 removed = []
498 if remove_obsolete:
498 if remove_obsolete:
499 # remove from database those repositories that are not in the filesystem
499 # remove from database those repositories that are not in the filesystem
500 for repo in sa.query(Repository).all():
500 for repo in sa.query(Repository).all():
501 if repo.repo_name not in initial_repo_list.keys():
501 if repo.repo_name not in initial_repo_list.keys():
502 log.debug("Removing non-existing repository found in db `%s`" %
502 log.debug("Removing non-existing repository found in db `%s`" %
503 repo.repo_name)
503 repo.repo_name)
504 try:
504 try:
505 removed.append(repo.repo_name)
505 removed.append(repo.repo_name)
506 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
506 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
507 sa.commit()
507 sa.commit()
508 except Exception:
508 except Exception:
509 #don't hold further removals on error
509 #don't hold further removals on error
510 log.error(traceback.format_exc())
510 log.error(traceback.format_exc())
511 sa.rollback()
511 sa.rollback()
512 return added, removed
512 return added, removed
513
513
514
514
515 # set cache regions for beaker so celery can utilise it
515 # set cache regions for beaker so celery can utilise it
516 def add_cache(settings):
516 def add_cache(settings):
517 cache_settings = {'regions': None}
517 cache_settings = {'regions': None}
518 for key in settings.keys():
518 for key in settings.keys():
519 for prefix in ['beaker.cache.', 'cache.']:
519 for prefix in ['beaker.cache.', 'cache.']:
520 if key.startswith(prefix):
520 if key.startswith(prefix):
521 name = key.split(prefix)[1].strip()
521 name = key.split(prefix)[1].strip()
522 cache_settings[name] = settings[key].strip()
522 cache_settings[name] = settings[key].strip()
523 if cache_settings['regions']:
523 if cache_settings['regions']:
524 for region in cache_settings['regions'].split(','):
524 for region in cache_settings['regions'].split(','):
525 region = region.strip()
525 region = region.strip()
526 region_settings = {}
526 region_settings = {}
527 for key, value in cache_settings.items():
527 for key, value in cache_settings.items():
528 if key.startswith(region):
528 if key.startswith(region):
529 region_settings[key.split('.')[1]] = value
529 region_settings[key.split('.')[1]] = value
530 region_settings['expire'] = int(region_settings.get('expire',
530 region_settings['expire'] = int(region_settings.get('expire',
531 60))
531 60))
532 region_settings.setdefault('lock_dir',
532 region_settings.setdefault('lock_dir',
533 cache_settings.get('lock_dir'))
533 cache_settings.get('lock_dir'))
534 region_settings.setdefault('data_dir',
534 region_settings.setdefault('data_dir',
535 cache_settings.get('data_dir'))
535 cache_settings.get('data_dir'))
536
536
537 if 'type' not in region_settings:
537 if 'type' not in region_settings:
538 region_settings['type'] = cache_settings.get('type',
538 region_settings['type'] = cache_settings.get('type',
539 'memory')
539 'memory')
540 beaker.cache.cache_regions[region] = region_settings
540 beaker.cache.cache_regions[region] = region_settings
541
541
542
542
543 def load_rcextensions(root_path):
543 def load_rcextensions(root_path):
544 import rhodecode
544 import rhodecode
545 from rhodecode.config import conf
545 from rhodecode.config import conf
546
546
547 path = os.path.join(root_path, 'rcextensions', '__init__.py')
547 path = os.path.join(root_path, 'rcextensions', '__init__.py')
548 if os.path.isfile(path):
548 if os.path.isfile(path):
549 rcext = create_module('rc', path)
549 rcext = create_module('rc', path)
550 EXT = rhodecode.EXTENSIONS = rcext
550 EXT = rhodecode.EXTENSIONS = rcext
551 log.debug('Found rcextensions now loading %s...' % rcext)
551 log.debug('Found rcextensions now loading %s...' % rcext)
552
552
553 # Additional mappings that are not present in the pygments lexers
553 # Additional mappings that are not present in the pygments lexers
554 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
554 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
555
555
556 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
556 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
557
557
558 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
558 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
559 log.debug('settings custom INDEX_EXTENSIONS')
559 log.debug('settings custom INDEX_EXTENSIONS')
560 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
560 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
561
561
562 #ADDITIONAL MAPPINGS
562 #ADDITIONAL MAPPINGS
563 log.debug('adding extra into INDEX_EXTENSIONS')
563 log.debug('adding extra into INDEX_EXTENSIONS')
564 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
564 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
565
565
566 # auto check if the module is not missing any data, set to default if is
566 # auto check if the module is not missing any data, set to default if is
567 # this will help autoupdate new feature of rcext module
567 # this will help autoupdate new feature of rcext module
568 from rhodecode.config import rcextensions
568 from rhodecode.config import rcextensions
569 for k in dir(rcextensions):
569 for k in dir(rcextensions):
570 if not k.startswith('_') and not hasattr(EXT, k):
570 if not k.startswith('_') and not hasattr(EXT, k):
571 setattr(EXT, k, getattr(rcextensions, k))
571 setattr(EXT, k, getattr(rcextensions, k))
572
572
573
573
574 def get_custom_lexer(extension):
574 def get_custom_lexer(extension):
575 """
575 """
576 returns a custom lexer if it's defined in rcextensions module, or None
576 returns a custom lexer if it's defined in rcextensions module, or None
577 if there's no custom lexer defined
577 if there's no custom lexer defined
578 """
578 """
579 import rhodecode
579 import rhodecode
580 from pygments import lexers
580 from pygments import lexers
581 #check if we didn't define this extension as other lexer
581 #check if we didn't define this extension as other lexer
582 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
582 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
583 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
583 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
584 return lexers.get_lexer_by_name(_lexer_name)
584 return lexers.get_lexer_by_name(_lexer_name)
585
585
586
586
587 #==============================================================================
587 #==============================================================================
588 # TEST FUNCTIONS AND CREATORS
588 # TEST FUNCTIONS AND CREATORS
589 #==============================================================================
589 #==============================================================================
590 def create_test_index(repo_location, config, full_index):
590 def create_test_index(repo_location, config, full_index):
591 """
591 """
592 Makes default test index
592 Makes default test index
593
593
594 :param config: test config
594 :param config: test config
595 :param full_index:
595 :param full_index:
596 """
596 """
597
597
598 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
598 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
599 from rhodecode.lib.pidlock import DaemonLock, LockHeld
599 from rhodecode.lib.pidlock import DaemonLock, LockHeld
600
600
601 repo_location = repo_location
601 repo_location = repo_location
602
602
603 index_location = os.path.join(config['app_conf']['index_dir'])
603 index_location = os.path.join(config['app_conf']['index_dir'])
604 if not os.path.exists(index_location):
604 if not os.path.exists(index_location):
605 os.makedirs(index_location)
605 os.makedirs(index_location)
606
606
607 try:
607 try:
608 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
608 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
609 WhooshIndexingDaemon(index_location=index_location,
609 WhooshIndexingDaemon(index_location=index_location,
610 repo_location=repo_location)\
610 repo_location=repo_location)\
611 .run(full_index=full_index)
611 .run(full_index=full_index)
612 l.release()
612 l.release()
613 except LockHeld:
613 except LockHeld:
614 pass
614 pass
615
615
616
616
617 def create_test_env(repos_test_path, config):
617 def create_test_env(repos_test_path, config):
618 """
618 """
619 Makes a fresh database and
619 Makes a fresh database and
620 install test repository into tmp dir
620 install test repository into tmp dir
621 """
621 """
622 from rhodecode.lib.db_manage import DbManage
622 from rhodecode.lib.db_manage import DbManage
623 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
623 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
624
624
625 # PART ONE create db
625 # PART ONE create db
626 dbconf = config['sqlalchemy.db1.url']
626 dbconf = config['sqlalchemy.db1.url']
627 log.debug('making test db %s' % dbconf)
627 log.debug('making test db %s' % dbconf)
628
628
629 # create test dir if it doesn't exist
629 # create test dir if it doesn't exist
630 if not os.path.isdir(repos_test_path):
630 if not os.path.isdir(repos_test_path):
631 log.debug('Creating testdir %s' % repos_test_path)
631 log.debug('Creating testdir %s' % repos_test_path)
632 os.makedirs(repos_test_path)
632 os.makedirs(repos_test_path)
633
633
634 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
634 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
635 tests=True)
635 tests=True)
636 dbmanage.create_tables(override=True)
636 dbmanage.create_tables(override=True)
637 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
637 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
638 dbmanage.create_default_user()
638 dbmanage.create_default_user()
639 dbmanage.admin_prompt()
639 dbmanage.admin_prompt()
640 dbmanage.create_permissions()
640 dbmanage.create_permissions()
641 dbmanage.populate_default_permissions()
641 dbmanage.populate_default_permissions()
642 Session().commit()
642 Session().commit()
643 # PART TWO make test repo
643 # PART TWO make test repo
644 log.debug('making test vcs repositories')
644 log.debug('making test vcs repositories')
645
645
646 idx_path = config['app_conf']['index_dir']
646 idx_path = config['app_conf']['index_dir']
647 data_path = config['app_conf']['cache_dir']
647 data_path = config['app_conf']['cache_dir']
648
648
649 #clean index and data
649 #clean index and data
650 if idx_path and os.path.exists(idx_path):
650 if idx_path and os.path.exists(idx_path):
651 log.debug('remove %s' % idx_path)
651 log.debug('remove %s' % idx_path)
652 shutil.rmtree(idx_path)
652 shutil.rmtree(idx_path)
653
653
654 if data_path and os.path.exists(data_path):
654 if data_path and os.path.exists(data_path):
655 log.debug('remove %s' % data_path)
655 log.debug('remove %s' % data_path)
656 shutil.rmtree(data_path)
656 shutil.rmtree(data_path)
657
657
658 #CREATE DEFAULT TEST REPOS
658 #CREATE DEFAULT TEST REPOS
659 cur_dir = dn(dn(abspath(__file__)))
659 cur_dir = dn(dn(abspath(__file__)))
660 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
660 tar = tarfile.open(jn(cur_dir, 'tests', 'fixtures', "vcs_test_hg.tar.gz"))
661 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
661 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
662 tar.close()
662 tar.close()
663
663
664 cur_dir = dn(dn(abspath(__file__)))
664 cur_dir = dn(dn(abspath(__file__)))
665 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
665 tar = tarfile.open(jn(cur_dir, 'tests', 'fixtures', "vcs_test_git.tar.gz"))
666 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
666 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
667 tar.close()
667 tar.close()
668
668
669 #LOAD VCS test stuff
669 #LOAD VCS test stuff
670 from rhodecode.tests.vcs import setup_package
670 from rhodecode.tests.vcs import setup_package
671 setup_package()
671 setup_package()
672
672
673
673
674 #==============================================================================
674 #==============================================================================
675 # PASTER COMMANDS
675 # PASTER COMMANDS
676 #==============================================================================
676 #==============================================================================
677 class BasePasterCommand(Command):
677 class BasePasterCommand(Command):
678 """
678 """
679 Abstract Base Class for paster commands.
679 Abstract Base Class for paster commands.
680
680
681 The celery commands are somewhat aggressive about loading
681 The celery commands are somewhat aggressive about loading
682 celery.conf, and since our module sets the `CELERY_LOADER`
682 celery.conf, and since our module sets the `CELERY_LOADER`
683 environment variable to our loader, we have to bootstrap a bit and
683 environment variable to our loader, we have to bootstrap a bit and
684 make sure we've had a chance to load the pylons config off of the
684 make sure we've had a chance to load the pylons config off of the
685 command line, otherwise everything fails.
685 command line, otherwise everything fails.
686 """
686 """
687 min_args = 1
687 min_args = 1
688 min_args_error = "Please provide a paster config file as an argument."
688 min_args_error = "Please provide a paster config file as an argument."
689 takes_config_file = 1
689 takes_config_file = 1
690 requires_config_file = True
690 requires_config_file = True
691
691
692 def notify_msg(self, msg, log=False):
692 def notify_msg(self, msg, log=False):
693 """Make a notification to user, additionally if logger is passed
693 """Make a notification to user, additionally if logger is passed
694 it logs this action using given logger
694 it logs this action using given logger
695
695
696 :param msg: message that will be printed to user
696 :param msg: message that will be printed to user
697 :param log: logging instance, to use to additionally log this message
697 :param log: logging instance, to use to additionally log this message
698
698
699 """
699 """
700 if log and isinstance(log, logging):
700 if log and isinstance(log, logging):
701 log(msg)
701 log(msg)
702
702
703 def run(self, args):
703 def run(self, args):
704 """
704 """
705 Overrides Command.run
705 Overrides Command.run
706
706
707 Checks for a config file argument and loads it.
707 Checks for a config file argument and loads it.
708 """
708 """
709 if len(args) < self.min_args:
709 if len(args) < self.min_args:
710 raise BadCommand(
710 raise BadCommand(
711 self.min_args_error % {'min_args': self.min_args,
711 self.min_args_error % {'min_args': self.min_args,
712 'actual_args': len(args)})
712 'actual_args': len(args)})
713
713
714 # Decrement because we're going to lob off the first argument.
714 # Decrement because we're going to lob off the first argument.
715 # @@ This is hacky
715 # @@ This is hacky
716 self.min_args -= 1
716 self.min_args -= 1
717 self.bootstrap_config(args[0])
717 self.bootstrap_config(args[0])
718 self.update_parser()
718 self.update_parser()
719 return super(BasePasterCommand, self).run(args[1:])
719 return super(BasePasterCommand, self).run(args[1:])
720
720
721 def update_parser(self):
721 def update_parser(self):
722 """
722 """
723 Abstract method. Allows for the class's parser to be updated
723 Abstract method. Allows for the class's parser to be updated
724 before the superclass's `run` method is called. Necessary to
724 before the superclass's `run` method is called. Necessary to
725 allow options/arguments to be passed through to the underlying
725 allow options/arguments to be passed through to the underlying
726 celery command.
726 celery command.
727 """
727 """
728 raise NotImplementedError("Abstract Method.")
728 raise NotImplementedError("Abstract Method.")
729
729
730 def bootstrap_config(self, conf):
730 def bootstrap_config(self, conf):
731 """
731 """
732 Loads the pylons configuration.
732 Loads the pylons configuration.
733 """
733 """
734 from pylons import config as pylonsconfig
734 from pylons import config as pylonsconfig
735
735
736 self.path_to_ini_file = os.path.realpath(conf)
736 self.path_to_ini_file = os.path.realpath(conf)
737 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
737 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
738 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
738 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
739
739
740 def _init_session(self):
740 def _init_session(self):
741 """
741 """
742 Inits SqlAlchemy Session
742 Inits SqlAlchemy Session
743 """
743 """
744 logging.config.fileConfig(self.path_to_ini_file)
744 logging.config.fileConfig(self.path_to_ini_file)
745 from pylons import config
745 from pylons import config
746 from rhodecode.model import init_model
746 from rhodecode.model import init_model
747 from rhodecode.lib.utils2 import engine_from_config
747 from rhodecode.lib.utils2 import engine_from_config
748
748
749 #get to remove repos !!
749 #get to remove repos !!
750 add_cache(config)
750 add_cache(config)
751 engine = engine_from_config(config, 'sqlalchemy.db1.')
751 engine = engine_from_config(config, 'sqlalchemy.db1.')
752 init_model(engine)
752 init_model(engine)
753
753
754
754
755 def check_git_version():
755 def check_git_version():
756 """
756 """
757 Checks what version of git is installed in system, and issues a warning
757 Checks what version of git is installed in system, and issues a warning
758 if it's too old for RhodeCode to properly work.
758 if it's too old for RhodeCode to properly work.
759 """
759 """
760 from rhodecode import BACKENDS
760 from rhodecode import BACKENDS
761 from rhodecode.lib.vcs.backends.git.repository import GitRepository
761 from rhodecode.lib.vcs.backends.git.repository import GitRepository
762 from rhodecode.lib.vcs.conf import settings
762 from rhodecode.lib.vcs.conf import settings
763 from distutils.version import StrictVersion
763 from distutils.version import StrictVersion
764
764
765 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
765 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
766 _safe=True)
766 _safe=True)
767
767
768 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
768 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
769 if len(ver.split('.')) > 3:
769 if len(ver.split('.')) > 3:
770 #StrictVersion needs to be only 3 element type
770 #StrictVersion needs to be only 3 element type
771 ver = '.'.join(ver.split('.')[:3])
771 ver = '.'.join(ver.split('.')[:3])
772 try:
772 try:
773 _ver = StrictVersion(ver)
773 _ver = StrictVersion(ver)
774 except Exception:
774 except Exception:
775 _ver = StrictVersion('0.0.0')
775 _ver = StrictVersion('0.0.0')
776 stderr = traceback.format_exc()
776 stderr = traceback.format_exc()
777
777
778 req_ver = '1.7.4'
778 req_ver = '1.7.4'
779 to_old_git = False
779 to_old_git = False
780 if _ver < StrictVersion(req_ver):
780 if _ver < StrictVersion(req_ver):
781 to_old_git = True
781 to_old_git = True
782
782
783 if 'git' in BACKENDS:
783 if 'git' in BACKENDS:
784 log.debug('GIT executable: "%s" version detected: %s'
784 log.debug('GIT executable: "%s" version detected: %s'
785 % (settings.GIT_EXECUTABLE_PATH, stdout))
785 % (settings.GIT_EXECUTABLE_PATH, stdout))
786 if stderr:
786 if stderr:
787 log.warning('Unable to detect git version, org error was: %r' % stderr)
787 log.warning('Unable to detect git version, org error was: %r' % stderr)
788 elif to_old_git:
788 elif to_old_git:
789 log.warning('RhodeCode detected git version %s, which is too old '
789 log.warning('RhodeCode detected git version %s, which is too old '
790 'for the system to function properly. Make sure '
790 'for the system to function properly. Make sure '
791 'its version is at least %s' % (ver, req_ver))
791 'its version is at least %s' % (ver, req_ver))
792 return _ver
792 return _ver
793
793
794
794
795 @decorator.decorator
795 @decorator.decorator
796 def jsonify(func, *args, **kwargs):
796 def jsonify(func, *args, **kwargs):
797 """Action decorator that formats output for JSON
797 """Action decorator that formats output for JSON
798
798
799 Given a function that will return content, this decorator will turn
799 Given a function that will return content, this decorator will turn
800 the result into JSON, with a content-type of 'application/json' and
800 the result into JSON, with a content-type of 'application/json' and
801 output it.
801 output it.
802
802
803 """
803 """
804 from pylons.decorators.util import get_pylons
804 from pylons.decorators.util import get_pylons
805 from rhodecode.lib.compat import json
805 from rhodecode.lib.compat import json
806 pylons = get_pylons(args)
806 pylons = get_pylons(args)
807 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
807 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
808 data = func(*args, **kwargs)
808 data = func(*args, **kwargs)
809 if isinstance(data, (list, tuple)):
809 if isinstance(data, (list, tuple)):
810 msg = "JSON responses with Array envelopes are susceptible to " \
810 msg = "JSON responses with Array envelopes are susceptible to " \
811 "cross-site data leak attacks, see " \
811 "cross-site data leak attacks, see " \
812 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
812 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
813 warnings.warn(msg, Warning, 2)
813 warnings.warn(msg, Warning, 2)
814 log.warning(msg)
814 log.warning(msg)
815 log.debug("Returning JSON wrapped action output")
815 log.debug("Returning JSON wrapped action output")
816 return json.dumps(data, encoding='utf-8')
816 return json.dumps(data, encoding='utf-8')
1 NO CONTENT: file renamed from rhodecode/tests/vcs_test_git.tar.gz to rhodecode/tests/fixtures/vcs_test_git.tar.gz
NO CONTENT: file renamed from rhodecode/tests/vcs_test_git.tar.gz to rhodecode/tests/fixtures/vcs_test_git.tar.gz
1 NO CONTENT: file renamed from rhodecode/tests/vcs_test_hg.tar.gz to rhodecode/tests/fixtures/vcs_test_hg.tar.gz
NO CONTENT: file renamed from rhodecode/tests/vcs_test_hg.tar.gz to rhodecode/tests/fixtures/vcs_test_hg.tar.gz
1 NO CONTENT: file renamed from rhodecode/tests/test_libs.py to rhodecode/tests/other/test_libs.py
NO CONTENT: file renamed from rhodecode/tests/test_libs.py to rhodecode/tests/other/test_libs.py
@@ -1,255 +1,254
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 import unittest
2 import unittest
3 import formencode
3 import formencode
4
4
5 from rhodecode.tests import *
5 from rhodecode.tests import *
6
6
7 from rhodecode.model import validators as v
7 from rhodecode.model import validators as v
8 from rhodecode.model.users_group import UserGroupModel
8 from rhodecode.model.users_group import UserGroupModel
9
9
10 from rhodecode.model.meta import Session
10 from rhodecode.model.meta import Session
11 from rhodecode.model.repos_group import ReposGroupModel
11 from rhodecode.model.repos_group import ReposGroupModel
12 from rhodecode.config.routing import ADMIN_PREFIX
13 from rhodecode.model.db import ChangesetStatus, Repository
12 from rhodecode.model.db import ChangesetStatus, Repository
14 from rhodecode.model.changeset_status import ChangesetStatusModel
13 from rhodecode.model.changeset_status import ChangesetStatusModel
15 from rhodecode.tests.fixture import Fixture
14 from rhodecode.tests.fixture import Fixture
16
15
17 fixture = Fixture()
16 fixture = Fixture()
18
17
19
18
20 class TestReposGroups(unittest.TestCase):
19 class TestReposGroups(unittest.TestCase):
21
20
22 def setUp(self):
21 def setUp(self):
23 pass
22 pass
24
23
25 def tearDown(self):
24 def tearDown(self):
26 Session.remove()
25 Session.remove()
27
26
28 def test_Message_extractor(self):
27 def test_Message_extractor(self):
29 validator = v.ValidUsername()
28 validator = v.ValidUsername()
30 self.assertRaises(formencode.Invalid, validator.to_python, 'default')
29 self.assertRaises(formencode.Invalid, validator.to_python, 'default')
31
30
32 class StateObj(object):
31 class StateObj(object):
33 pass
32 pass
34
33
35 self.assertRaises(formencode.Invalid,
34 self.assertRaises(formencode.Invalid,
36 validator.to_python, 'default', StateObj)
35 validator.to_python, 'default', StateObj)
37
36
38 def test_ValidUsername(self):
37 def test_ValidUsername(self):
39 validator = v.ValidUsername()
38 validator = v.ValidUsername()
40
39
41 self.assertRaises(formencode.Invalid, validator.to_python, 'default')
40 self.assertRaises(formencode.Invalid, validator.to_python, 'default')
42 self.assertRaises(formencode.Invalid, validator.to_python, 'new_user')
41 self.assertRaises(formencode.Invalid, validator.to_python, 'new_user')
43 self.assertRaises(formencode.Invalid, validator.to_python, '.,')
42 self.assertRaises(formencode.Invalid, validator.to_python, '.,')
44 self.assertRaises(formencode.Invalid, validator.to_python,
43 self.assertRaises(formencode.Invalid, validator.to_python,
45 TEST_USER_ADMIN_LOGIN)
44 TEST_USER_ADMIN_LOGIN)
46 self.assertEqual('test', validator.to_python('test'))
45 self.assertEqual('test', validator.to_python('test'))
47
46
48 validator = v.ValidUsername(edit=True, old_data={'user_id': 1})
47 validator = v.ValidUsername(edit=True, old_data={'user_id': 1})
49
48
50 def test_ValidRepoUser(self):
49 def test_ValidRepoUser(self):
51 validator = v.ValidRepoUser()
50 validator = v.ValidRepoUser()
52 self.assertRaises(formencode.Invalid, validator.to_python, 'nouser')
51 self.assertRaises(formencode.Invalid, validator.to_python, 'nouser')
53 self.assertEqual(TEST_USER_ADMIN_LOGIN,
52 self.assertEqual(TEST_USER_ADMIN_LOGIN,
54 validator.to_python(TEST_USER_ADMIN_LOGIN))
53 validator.to_python(TEST_USER_ADMIN_LOGIN))
55
54
56 def test_ValidUserGroup(self):
55 def test_ValidUserGroup(self):
57 validator = v.ValidUserGroup()
56 validator = v.ValidUserGroup()
58 self.assertRaises(formencode.Invalid, validator.to_python, 'default')
57 self.assertRaises(formencode.Invalid, validator.to_python, 'default')
59 self.assertRaises(formencode.Invalid, validator.to_python, '.,')
58 self.assertRaises(formencode.Invalid, validator.to_python, '.,')
60
59
61 gr = fixture.create_user_group('test')
60 gr = fixture.create_user_group('test')
62 gr2 = fixture.create_user_group('tes2')
61 gr2 = fixture.create_user_group('tes2')
63 Session().commit()
62 Session().commit()
64 self.assertRaises(formencode.Invalid, validator.to_python, 'test')
63 self.assertRaises(formencode.Invalid, validator.to_python, 'test')
65 assert gr.users_group_id != None
64 assert gr.users_group_id != None
66 validator = v.ValidUserGroup(edit=True,
65 validator = v.ValidUserGroup(edit=True,
67 old_data={'users_group_id':
66 old_data={'users_group_id':
68 gr2.users_group_id})
67 gr2.users_group_id})
69
68
70 self.assertRaises(formencode.Invalid, validator.to_python, 'test')
69 self.assertRaises(formencode.Invalid, validator.to_python, 'test')
71 self.assertRaises(formencode.Invalid, validator.to_python, 'TesT')
70 self.assertRaises(formencode.Invalid, validator.to_python, 'TesT')
72 self.assertRaises(formencode.Invalid, validator.to_python, 'TEST')
71 self.assertRaises(formencode.Invalid, validator.to_python, 'TEST')
73 UserGroupModel().delete(gr)
72 UserGroupModel().delete(gr)
74 UserGroupModel().delete(gr2)
73 UserGroupModel().delete(gr2)
75 Session().commit()
74 Session().commit()
76
75
77 def test_ValidReposGroup(self):
76 def test_ValidReposGroup(self):
78 validator = v.ValidReposGroup()
77 validator = v.ValidReposGroup()
79 model = ReposGroupModel()
78 model = ReposGroupModel()
80 self.assertRaises(formencode.Invalid, validator.to_python,
79 self.assertRaises(formencode.Invalid, validator.to_python,
81 {'group_name': HG_REPO, })
80 {'group_name': HG_REPO, })
82 gr = model.create(group_name='test_gr', group_description='desc',
81 gr = model.create(group_name='test_gr', group_description='desc',
83 parent=None,
82 parent=None,
84 just_db=True,
83 just_db=True,
85 owner=TEST_USER_ADMIN_LOGIN)
84 owner=TEST_USER_ADMIN_LOGIN)
86 self.assertRaises(formencode.Invalid,
85 self.assertRaises(formencode.Invalid,
87 validator.to_python, {'group_name': gr.group_name, })
86 validator.to_python, {'group_name': gr.group_name, })
88
87
89 validator = v.ValidReposGroup(edit=True,
88 validator = v.ValidReposGroup(edit=True,
90 old_data={'group_id': gr.group_id})
89 old_data={'group_id': gr.group_id})
91 self.assertRaises(formencode.Invalid,
90 self.assertRaises(formencode.Invalid,
92 validator.to_python, {
91 validator.to_python, {
93 'group_name': gr.group_name + 'n',
92 'group_name': gr.group_name + 'n',
94 'group_parent_id': gr.group_id
93 'group_parent_id': gr.group_id
95 })
94 })
96 model.delete(gr)
95 model.delete(gr)
97
96
98 def test_ValidPassword(self):
97 def test_ValidPassword(self):
99 validator = v.ValidPassword()
98 validator = v.ValidPassword()
100 self.assertEqual('lol', validator.to_python('lol'))
99 self.assertEqual('lol', validator.to_python('lol'))
101 self.assertEqual(None, validator.to_python(None))
100 self.assertEqual(None, validator.to_python(None))
102 self.assertRaises(formencode.Invalid, validator.to_python, 'ąćżź')
101 self.assertRaises(formencode.Invalid, validator.to_python, 'ąćżź')
103
102
104 def test_ValidPasswordsMatch(self):
103 def test_ValidPasswordsMatch(self):
105 validator = v.ValidPasswordsMatch()
104 validator = v.ValidPasswordsMatch()
106 self.assertRaises(formencode.Invalid,
105 self.assertRaises(formencode.Invalid,
107 validator.to_python, {'password': 'pass',
106 validator.to_python, {'password': 'pass',
108 'password_confirmation': 'pass2'})
107 'password_confirmation': 'pass2'})
109
108
110 self.assertRaises(formencode.Invalid,
109 self.assertRaises(formencode.Invalid,
111 validator.to_python, {'new_password': 'pass',
110 validator.to_python, {'new_password': 'pass',
112 'password_confirmation': 'pass2'})
111 'password_confirmation': 'pass2'})
113
112
114 self.assertEqual({'new_password': 'pass',
113 self.assertEqual({'new_password': 'pass',
115 'password_confirmation': 'pass'},
114 'password_confirmation': 'pass'},
116 validator.to_python({'new_password': 'pass',
115 validator.to_python({'new_password': 'pass',
117 'password_confirmation': 'pass'}))
116 'password_confirmation': 'pass'}))
118
117
119 self.assertEqual({'password': 'pass',
118 self.assertEqual({'password': 'pass',
120 'password_confirmation': 'pass'},
119 'password_confirmation': 'pass'},
121 validator.to_python({'password': 'pass',
120 validator.to_python({'password': 'pass',
122 'password_confirmation': 'pass'}))
121 'password_confirmation': 'pass'}))
123
122
124 def test_ValidAuth(self):
123 def test_ValidAuth(self):
125 validator = v.ValidAuth()
124 validator = v.ValidAuth()
126 valid_creds = {
125 valid_creds = {
127 'username': TEST_USER_REGULAR2_LOGIN,
126 'username': TEST_USER_REGULAR2_LOGIN,
128 'password': TEST_USER_REGULAR2_PASS,
127 'password': TEST_USER_REGULAR2_PASS,
129 }
128 }
130 invalid_creds = {
129 invalid_creds = {
131 'username': 'err',
130 'username': 'err',
132 'password': 'err',
131 'password': 'err',
133 }
132 }
134 self.assertEqual(valid_creds, validator.to_python(valid_creds))
133 self.assertEqual(valid_creds, validator.to_python(valid_creds))
135 self.assertRaises(formencode.Invalid,
134 self.assertRaises(formencode.Invalid,
136 validator.to_python, invalid_creds)
135 validator.to_python, invalid_creds)
137
136
138 def test_ValidAuthToken(self):
137 def test_ValidAuthToken(self):
139 validator = v.ValidAuthToken()
138 validator = v.ValidAuthToken()
140 # this is untestable without a threadlocal
139 # this is untestable without a threadlocal
141 # self.assertRaises(formencode.Invalid,
140 # self.assertRaises(formencode.Invalid,
142 # validator.to_python, 'BadToken')
141 # validator.to_python, 'BadToken')
143 validator
142 validator
144
143
145 def test_ValidRepoName(self):
144 def test_ValidRepoName(self):
146 validator = v.ValidRepoName()
145 validator = v.ValidRepoName()
147
146
148 self.assertRaises(formencode.Invalid,
147 self.assertRaises(formencode.Invalid,
149 validator.to_python, {'repo_name': ''})
148 validator.to_python, {'repo_name': ''})
150
149
151 self.assertRaises(formencode.Invalid,
150 self.assertRaises(formencode.Invalid,
152 validator.to_python, {'repo_name': HG_REPO})
151 validator.to_python, {'repo_name': HG_REPO})
153
152
154 gr = ReposGroupModel().create(group_name='group_test',
153 gr = ReposGroupModel().create(group_name='group_test',
155 group_description='desc',
154 group_description='desc',
156 parent=None,
155 parent=None,
157 owner=TEST_USER_ADMIN_LOGIN)
156 owner=TEST_USER_ADMIN_LOGIN)
158 self.assertRaises(formencode.Invalid,
157 self.assertRaises(formencode.Invalid,
159 validator.to_python, {'repo_name': gr.group_name})
158 validator.to_python, {'repo_name': gr.group_name})
160
159
161 #TODO: write an error case for that ie. create a repo withinh a group
160 #TODO: write an error case for that ie. create a repo withinh a group
162 # self.assertRaises(formencode.Invalid,
161 # self.assertRaises(formencode.Invalid,
163 # validator.to_python, {'repo_name': 'some',
162 # validator.to_python, {'repo_name': 'some',
164 # 'repo_group': gr.group_id})
163 # 'repo_group': gr.group_id})
165
164
166 def test_ValidForkName(self):
165 def test_ValidForkName(self):
167 # this uses ValidRepoName validator
166 # this uses ValidRepoName validator
168 assert True
167 assert True
169
168
170 @parameterized.expand([
169 @parameterized.expand([
171 ('test', 'test'), ('lolz!', 'lolz'), (' aavv', 'aavv'),
170 ('test', 'test'), ('lolz!', 'lolz'), (' aavv', 'aavv'),
172 ('ala ma kota', 'ala-ma-kota'), ('@nooo', 'nooo'),
171 ('ala ma kota', 'ala-ma-kota'), ('@nooo', 'nooo'),
173 ('$!haha lolz !', 'haha-lolz'), ('$$$$$', ''), ('{}OK!', 'OK'),
172 ('$!haha lolz !', 'haha-lolz'), ('$$$$$', ''), ('{}OK!', 'OK'),
174 ('/]re po', 're-po')])
173 ('/]re po', 're-po')])
175 def test_SlugifyName(self, name, expected):
174 def test_SlugifyName(self, name, expected):
176 validator = v.SlugifyName()
175 validator = v.SlugifyName()
177 self.assertEqual(expected, validator.to_python(name))
176 self.assertEqual(expected, validator.to_python(name))
178
177
179 def test_ValidCloneUri(self):
178 def test_ValidCloneUri(self):
180 #TODO: write this one
179 #TODO: write this one
181 pass
180 pass
182
181
183 def test_ValidForkType(self):
182 def test_ValidForkType(self):
184 validator = v.ValidForkType(old_data={'repo_type': 'hg'})
183 validator = v.ValidForkType(old_data={'repo_type': 'hg'})
185 self.assertEqual('hg', validator.to_python('hg'))
184 self.assertEqual('hg', validator.to_python('hg'))
186 self.assertRaises(formencode.Invalid, validator.to_python, 'git')
185 self.assertRaises(formencode.Invalid, validator.to_python, 'git')
187
186
188 def test_ValidPerms(self):
187 def test_ValidPerms(self):
189 #TODO: write this one
188 #TODO: write this one
190 pass
189 pass
191
190
192 def test_ValidSettings(self):
191 def test_ValidSettings(self):
193 validator = v.ValidSettings()
192 validator = v.ValidSettings()
194 self.assertEqual({'pass': 'pass'},
193 self.assertEqual({'pass': 'pass'},
195 validator.to_python(value={'user': 'test',
194 validator.to_python(value={'user': 'test',
196 'pass': 'pass'}))
195 'pass': 'pass'}))
197
196
198 self.assertEqual({'user2': 'test', 'pass': 'pass'},
197 self.assertEqual({'user2': 'test', 'pass': 'pass'},
199 validator.to_python(value={'user2': 'test',
198 validator.to_python(value={'user2': 'test',
200 'pass': 'pass'}))
199 'pass': 'pass'}))
201
200
202 def test_ValidPath(self):
201 def test_ValidPath(self):
203 validator = v.ValidPath()
202 validator = v.ValidPath()
204 self.assertEqual(TESTS_TMP_PATH,
203 self.assertEqual(TESTS_TMP_PATH,
205 validator.to_python(TESTS_TMP_PATH))
204 validator.to_python(TESTS_TMP_PATH))
206 self.assertRaises(formencode.Invalid, validator.to_python,
205 self.assertRaises(formencode.Invalid, validator.to_python,
207 '/no_such_dir')
206 '/no_such_dir')
208
207
209 def test_UniqSystemEmail(self):
208 def test_UniqSystemEmail(self):
210 validator = v.UniqSystemEmail(old_data={})
209 validator = v.UniqSystemEmail(old_data={})
211
210
212 self.assertEqual('mail@python.org',
211 self.assertEqual('mail@python.org',
213 validator.to_python('MaiL@Python.org'))
212 validator.to_python('MaiL@Python.org'))
214
213
215 email = TEST_USER_REGULAR2_EMAIL
214 email = TEST_USER_REGULAR2_EMAIL
216 self.assertRaises(formencode.Invalid, validator.to_python, email)
215 self.assertRaises(formencode.Invalid, validator.to_python, email)
217
216
218 def test_ValidSystemEmail(self):
217 def test_ValidSystemEmail(self):
219 validator = v.ValidSystemEmail()
218 validator = v.ValidSystemEmail()
220 email = TEST_USER_REGULAR2_EMAIL
219 email = TEST_USER_REGULAR2_EMAIL
221
220
222 self.assertEqual(email, validator.to_python(email))
221 self.assertEqual(email, validator.to_python(email))
223 self.assertRaises(formencode.Invalid, validator.to_python, 'err')
222 self.assertRaises(formencode.Invalid, validator.to_python, 'err')
224
223
225 def test_LdapLibValidator(self):
224 def test_LdapLibValidator(self):
226 if ldap_lib_installed:
225 if ldap_lib_installed:
227 validator = v.LdapLibValidator()
226 validator = v.LdapLibValidator()
228 self.assertEqual("DN", validator.to_python('DN'))
227 self.assertEqual("DN", validator.to_python('DN'))
229 else:
228 else:
230 validator = v.LdapLibValidator()
229 validator = v.LdapLibValidator()
231 self.assertRaises(v.LdapImportError, validator.to_python, 'err')
230 self.assertRaises(v.LdapImportError, validator.to_python, 'err')
232
231
233 def test_AttrLoginValidator(self):
232 def test_AttrLoginValidator(self):
234 validator = v.AttrLoginValidator()
233 validator = v.AttrLoginValidator()
235 self.assertEqual('DN_attr', validator.to_python('DN_attr'))
234 self.assertEqual('DN_attr', validator.to_python('DN_attr'))
236
235
237 def test_NotReviewedRevisions(self):
236 def test_NotReviewedRevisions(self):
238 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
237 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
239 validator = v.NotReviewedRevisions(repo_id)
238 validator = v.NotReviewedRevisions(repo_id)
240 rev = '0' * 40
239 rev = '0' * 40
241 # add status for a rev, that should throw an error because it is already
240 # add status for a rev, that should throw an error because it is already
242 # reviewed
241 # reviewed
243 new_status = ChangesetStatus()
242 new_status = ChangesetStatus()
244 new_status.author = ChangesetStatusModel()._get_user(TEST_USER_ADMIN_LOGIN)
243 new_status.author = ChangesetStatusModel()._get_user(TEST_USER_ADMIN_LOGIN)
245 new_status.repo = ChangesetStatusModel()._get_repo(HG_REPO)
244 new_status.repo = ChangesetStatusModel()._get_repo(HG_REPO)
246 new_status.status = ChangesetStatus.STATUS_APPROVED
245 new_status.status = ChangesetStatus.STATUS_APPROVED
247 new_status.comment = None
246 new_status.comment = None
248 new_status.revision = rev
247 new_status.revision = rev
249 Session().add(new_status)
248 Session().add(new_status)
250 Session().commit()
249 Session().commit()
251 try:
250 try:
252 self.assertRaises(formencode.Invalid, validator.to_python, [rev])
251 self.assertRaises(formencode.Invalid, validator.to_python, [rev])
253 finally:
252 finally:
254 Session().delete(new_status)
253 Session().delete(new_status)
255 Session().commit()
254 Session().commit()
1 NO CONTENT: file renamed from rhodecode/tests/scripts/test_vcs_operations.py to rhodecode/tests/other/test_vcs_operations.py
NO CONTENT: file renamed from rhodecode/tests/scripts/test_vcs_operations.py to rhodecode/tests/other/test_vcs_operations.py
General Comments 0
You need to be logged in to leave comments. Login now