##// END OF EJS Templates
fixed whoosh failure on new repository...
marcink -
r567:80dc0a23 default
parent child Browse files
Show More
@@ -1,243 +1,246 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # whoosh indexer daemon for rhodecode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on Jan 26, 2010
22 22
23 23 @author: marcink
24 24 A deamon will read from task table and run tasks
25 25 """
26 26 import sys
27 27 import os
28 28 from os.path import dirname as dn
29 29 from os.path import join as jn
30 30
31 31 #to get the rhodecode import
32 32 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
33 33 sys.path.append(project_path)
34 34
35 35 from rhodecode.lib.pidlock import LockHeld, DaemonLock
36 36 from rhodecode.model.hg_model import HgModel
37 37 from rhodecode.lib.helpers import safe_unicode
38 38 from whoosh.index import create_in, open_dir
39 39 from shutil import rmtree
40 40 from rhodecode.lib.indexers import INDEX_EXTENSIONS, IDX_LOCATION, SCHEMA, IDX_NAME
41 41
42 42 from time import mktime
43 from vcs.exceptions import ChangesetError
43 from vcs.exceptions import ChangesetError, RepositoryError
44 44
45 45 import logging
46 46
47 47 log = logging.getLogger('whooshIndexer')
48 48 # create logger
49 49 log.setLevel(logging.DEBUG)
50 50 log.propagate = False
51 51 # create console handler and set level to debug
52 52 ch = logging.StreamHandler()
53 53 ch.setLevel(logging.DEBUG)
54 54
55 55 # create formatter
56 56 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
57 57
58 58 # add formatter to ch
59 59 ch.setFormatter(formatter)
60 60
61 61 # add ch to logger
62 62 log.addHandler(ch)
63 63
64 64 def scan_paths(root_location):
65 65 return HgModel.repo_scan('/', root_location, None, True)
66 66
67 67 class WhooshIndexingDaemon(object):
68 68 """
69 69 Deamon for atomic jobs
70 70 """
71 71
72 72 def __init__(self, indexname='HG_INDEX', repo_location=None):
73 73 self.indexname = indexname
74 74 self.repo_location = repo_location
75 75 self.repo_paths = scan_paths(self.repo_location)
76 76 self.initial = False
77 77 if not os.path.isdir(IDX_LOCATION):
78 78 os.mkdir(IDX_LOCATION)
79 79 log.info('Cannot run incremental index since it does not'
80 80 ' yet exist running full build')
81 81 self.initial = True
82 82
83 83 def get_paths(self, repo):
84 84 """
85 85 recursive walk in root dir and return a set of all path in that dir
86 86 based on repository walk function
87 87 """
88 88 index_paths_ = set()
89 try:
89 90 for topnode, dirs, files in repo.walk('/', 'tip'):
90 91 for f in files:
91 92 index_paths_.add(jn(repo.path, f.path))
92 93 for dir in dirs:
93 94 for f in files:
94 95 index_paths_.add(jn(repo.path, f.path))
95 96
97 except RepositoryError:
98 pass
96 99 return index_paths_
97 100
98 101 def get_node(self, repo, path):
99 102 n_path = path[len(repo.path) + 1:]
100 103 node = repo.get_changeset().get_node(n_path)
101 104 return node
102 105
103 106 def get_node_mtime(self, node):
104 107 return mktime(node.last_changeset.date.timetuple())
105 108
106 109 def add_doc(self, writer, path, repo):
107 110 """Adding doc to writer"""
108 111 node = self.get_node(repo, path)
109 112
110 113 #we just index the content of chosen files
111 114 if node.extension in INDEX_EXTENSIONS:
112 115 log.debug(' >> %s [WITH CONTENT]' % path)
113 116 u_content = node.content
114 117 else:
115 118 log.debug(' >> %s' % path)
116 119 #just index file name without it's content
117 120 u_content = u''
118 121
119 122 writer.add_document(owner=unicode(repo.contact),
120 123 repository=safe_unicode(repo.name),
121 124 path=safe_unicode(path),
122 125 content=u_content,
123 126 modtime=self.get_node_mtime(node),
124 127 extension=node.extension)
125 128
126 129
127 130 def build_index(self):
128 131 if os.path.exists(IDX_LOCATION):
129 132 log.debug('removing previous index')
130 133 rmtree(IDX_LOCATION)
131 134
132 135 if not os.path.exists(IDX_LOCATION):
133 136 os.mkdir(IDX_LOCATION)
134 137
135 138 idx = create_in(IDX_LOCATION, SCHEMA, indexname=IDX_NAME)
136 139 writer = idx.writer()
137 140
138 141 for cnt, repo in enumerate(self.repo_paths.values()):
139 142 log.debug('building index @ %s' % repo.path)
140 143
141 144 for idx_path in self.get_paths(repo):
142 145 self.add_doc(writer, idx_path, repo)
143 146
144 147 log.debug('>> COMMITING CHANGES <<')
145 148 writer.commit(merge=True)
146 149 log.debug('>>> FINISHED BUILDING INDEX <<<')
147 150
148 151
149 152 def update_index(self):
150 153 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
151 154
152 155 idx = open_dir(IDX_LOCATION, indexname=self.indexname)
153 156 # The set of all paths in the index
154 157 indexed_paths = set()
155 158 # The set of all paths we need to re-index
156 159 to_index = set()
157 160
158 161 reader = idx.reader()
159 162 writer = idx.writer()
160 163
161 164 # Loop over the stored fields in the index
162 165 for fields in reader.all_stored_fields():
163 166 indexed_path = fields['path']
164 167 indexed_paths.add(indexed_path)
165 168
166 169 repo = self.repo_paths[fields['repository']]
167 170
168 171 try:
169 172 node = self.get_node(repo, indexed_path)
170 173 except ChangesetError:
171 174 # This file was deleted since it was indexed
172 175 log.debug('removing from index %s' % indexed_path)
173 176 writer.delete_by_term('path', indexed_path)
174 177
175 178 else:
176 179 # Check if this file was changed since it was indexed
177 180 indexed_time = fields['modtime']
178 181 mtime = self.get_node_mtime(node)
179 182 if mtime > indexed_time:
180 183 # The file has changed, delete it and add it to the list of
181 184 # files to reindex
182 185 log.debug('adding to reindex list %s' % indexed_path)
183 186 writer.delete_by_term('path', indexed_path)
184 187 to_index.add(indexed_path)
185 188
186 189 # Loop over the files in the filesystem
187 190 # Assume we have a function that gathers the filenames of the
188 191 # documents to be indexed
189 192 for repo in self.repo_paths.values():
190 193 for path in self.get_paths(repo):
191 194 if path in to_index or path not in indexed_paths:
192 195 # This is either a file that's changed, or a new file
193 196 # that wasn't indexed before. So index it!
194 197 self.add_doc(writer, path, repo)
195 198 log.debug('re indexing %s' % path)
196 199
197 200 log.debug('>> COMMITING CHANGES <<')
198 201 writer.commit(merge=True)
199 202 log.debug('>>> FINISHED REBUILDING INDEX <<<')
200 203
201 204 def run(self, full_index=False):
202 205 """Run daemon"""
203 206 if full_index or self.initial:
204 207 self.build_index()
205 208 else:
206 209 self.update_index()
207 210
208 211 if __name__ == "__main__":
209 212 arg = sys.argv[1:]
210 213 if len(arg) != 2:
211 214 sys.stderr.write('Please specify indexing type [full|incremental]'
212 215 'and path to repositories as script args \n')
213 216 sys.exit()
214 217
215 218
216 219 if arg[0] == 'full':
217 220 full_index = True
218 221 elif arg[0] == 'incremental':
219 222 # False means looking just for changes
220 223 full_index = False
221 224 else:
222 225 sys.stdout.write('Please use [full|incremental]'
223 226 ' as script first arg \n')
224 227 sys.exit()
225 228
226 229 if not os.path.isdir(arg[1]):
227 230 sys.stderr.write('%s is not a valid path \n' % arg[1])
228 231 sys.exit()
229 232 else:
230 233 if arg[1].endswith('/'):
231 234 repo_location = arg[1] + '*'
232 235 else:
233 236 repo_location = arg[1] + '/*'
234 237
235 238 try:
236 239 l = DaemonLock()
237 240 WhooshIndexingDaemon(repo_location=repo_location)\
238 241 .run(full_index=full_index)
239 242 l.release()
240 243 reload(logging)
241 244 except LockHeld:
242 245 sys.exit(1)
243 246
@@ -1,490 +1,490 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Utilities for hg app
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19
20 20 """
21 21 Created on April 18, 2010
22 22 Utilities for hg app
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region
26 26 from mercurial import ui, config, hg
27 27 from mercurial.error import RepoError
28 28 from rhodecode.model import meta
29 29 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, UserLog
30 30 from vcs.backends.base import BaseChangeset
31 31 from vcs.utils.lazy import LazyProperty
32 32 import logging
33 33 import datetime
34 34 import os
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 def get_repo_slug(request):
40 40 return request.environ['pylons.routes_dict'].get('repo_name')
41 41
42 42 def is_mercurial(environ):
43 43 """
44 44 Returns True if request's target is mercurial server - header
45 45 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
46 46 """
47 47 http_accept = environ.get('HTTP_ACCEPT')
48 48 if http_accept and http_accept.startswith('application/mercurial'):
49 49 return True
50 50 return False
51 51
52 52 def action_logger(user, action, repo, ipaddr, sa=None):
53 53 """
54 54 Action logger for various action made by users
55 55 """
56 56
57 57 if not sa:
58 58 sa = meta.Session
59 59
60 60 try:
61 61 if hasattr(user, 'user_id'):
62 62 user_id = user.user_id
63 63 elif isinstance(user, basestring):
64 64 user_id = sa.query(User).filter(User.username == user).one()
65 65 else:
66 66 raise Exception('You have to provide user object or username')
67 67
68 68 repo_name = repo.lstrip('/')
69 69 user_log = UserLog()
70 70 user_log.user_id = user_id
71 71 user_log.action = action
72 72 user_log.repository_name = repo_name
73 73 user_log.repository = sa.query(Repository)\
74 74 .filter(Repository.repo_name == repo_name).one()
75 75 user_log.action_date = datetime.datetime.now()
76 76 user_log.user_ip = ipaddr
77 77 sa.add(user_log)
78 78 sa.commit()
79 79 log.info('Adding user %s, action %s on %s',
80 80 user.username, action, repo)
81 81 except Exception, e:
82 82 raise
83 83 sa.rollback()
84 84 log.error('could not log user action:%s', str(e))
85 85
86 86 def check_repo_dir(paths):
87 87 repos_path = paths[0][1].split('/')
88 88 if repos_path[-1] in ['*', '**']:
89 89 repos_path = repos_path[:-1]
90 90 if repos_path[0] != '/':
91 91 repos_path[0] = '/'
92 92 if not os.path.isdir(os.path.join(*repos_path)):
93 93 raise Exception('Not a valid repository in %s' % paths[0][1])
94 94
95 95 def check_repo_fast(repo_name, base_path):
96 96 if os.path.isdir(os.path.join(base_path, repo_name)):return False
97 97 return True
98 98
99 99 def check_repo(repo_name, base_path, verify=True):
100 100
101 101 repo_path = os.path.join(base_path, repo_name)
102 102
103 103 try:
104 104 if not check_repo_fast(repo_name, base_path):
105 105 return False
106 106 r = hg.repository(ui.ui(), repo_path)
107 107 if verify:
108 108 hg.verify(r)
109 109 #here we hnow that repo exists it was verified
110 110 log.info('%s repo is already created', repo_name)
111 111 return False
112 112 except RepoError:
113 113 #it means that there is no valid repo there...
114 114 log.info('%s repo is free for creation', repo_name)
115 115 return True
116 116
117 117 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
118 118 while True:
119 119 ok = raw_input(prompt)
120 120 if ok in ('y', 'ye', 'yes'): return True
121 121 if ok in ('n', 'no', 'nop', 'nope'): return False
122 122 retries = retries - 1
123 123 if retries < 0: raise IOError
124 124 print complaint
125 125
126 126 @cache_region('super_short_term', 'cached_hg_ui')
127 127 def get_hg_ui_cached():
128 128 try:
129 129 sa = meta.Session
130 130 ret = sa.query(RhodeCodeUi).all()
131 131 finally:
132 132 meta.Session.remove()
133 133 return ret
134 134
135 135
136 136 def get_hg_settings():
137 137 try:
138 138 sa = meta.Session
139 139 ret = sa.query(RhodeCodeSettings).all()
140 140 finally:
141 141 meta.Session.remove()
142 142
143 143 if not ret:
144 144 raise Exception('Could not get application settings !')
145 145 settings = {}
146 146 for each in ret:
147 147 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
148 148
149 149 return settings
150 150
151 151 def get_hg_ui_settings():
152 152 try:
153 153 sa = meta.Session
154 154 ret = sa.query(RhodeCodeUi).all()
155 155 finally:
156 156 meta.Session.remove()
157 157
158 158 if not ret:
159 159 raise Exception('Could not get application ui settings !')
160 160 settings = {}
161 161 for each in ret:
162 162 k = each.ui_key
163 163 v = each.ui_value
164 164 if k == '/':
165 165 k = 'root_path'
166 166
167 167 if k.find('.') != -1:
168 168 k = k.replace('.', '_')
169 169
170 170 if each.ui_section == 'hooks':
171 171 v = each.ui_active
172 172
173 173 settings[each.ui_section + '_' + k] = v
174 174
175 175 return settings
176 176
177 177 #propagated from mercurial documentation
178 178 ui_sections = ['alias', 'auth',
179 179 'decode/encode', 'defaults',
180 180 'diff', 'email',
181 181 'extensions', 'format',
182 182 'merge-patterns', 'merge-tools',
183 183 'hooks', 'http_proxy',
184 184 'smtp', 'patch',
185 185 'paths', 'profiling',
186 186 'server', 'trusted',
187 187 'ui', 'web', ]
188 188
189 189 def make_ui(read_from='file', path=None, checkpaths=True):
190 190 """
191 191 A function that will read python rc files or database
192 192 and make an mercurial ui object from read options
193 193
194 194 @param path: path to mercurial config file
195 195 @param checkpaths: check the path
196 196 @param read_from: read from 'file' or 'db'
197 197 """
198 198
199 199 baseui = ui.ui()
200 200
201 201 if read_from == 'file':
202 202 if not os.path.isfile(path):
203 203 log.warning('Unable to read config file %s' % path)
204 204 return False
205 205 log.debug('reading hgrc from %s', path)
206 206 cfg = config.config()
207 207 cfg.read(path)
208 208 for section in ui_sections:
209 209 for k, v in cfg.items(section):
210 210 baseui.setconfig(section, k, v)
211 211 log.debug('settings ui from file[%s]%s:%s', section, k, v)
212 212 if checkpaths:check_repo_dir(cfg.items('paths'))
213 213
214 214
215 215 elif read_from == 'db':
216 216 hg_ui = get_hg_ui_cached()
217 217 for ui_ in hg_ui:
218 218 if ui_.ui_active:
219 219 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
220 220 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
221 221
222 222
223 223 return baseui
224 224
225 225
226 226 def set_rhodecode_config(config):
227 227 hgsettings = get_hg_settings()
228 228
229 229 for k, v in hgsettings.items():
230 230 config[k] = v
231 231
232 232 def invalidate_cache(name, *args):
233 233 """Invalidates given name cache"""
234 234
235 235 from beaker.cache import region_invalidate
236 236 log.info('INVALIDATING CACHE FOR %s', name)
237 237
238 238 """propagate our arguments to make sure invalidation works. First
239 239 argument has to be the name of cached func name give to cache decorator
240 240 without that the invalidation would not work"""
241 241 tmp = [name]
242 242 tmp.extend(args)
243 243 args = tuple(tmp)
244 244
245 245 if name == 'cached_repo_list':
246 246 from rhodecode.model.hg_model import _get_repos_cached
247 247 region_invalidate(_get_repos_cached, None, *args)
248 248
249 249 if name == 'full_changelog':
250 250 from rhodecode.model.hg_model import _full_changelog_cached
251 251 region_invalidate(_full_changelog_cached, None, *args)
252 252
253 253 class EmptyChangeset(BaseChangeset):
254 254 """
255 255 An dummy empty changeset.
256 256 """
257 257
258 258 revision = -1
259 259 message = ''
260 260 author = ''
261 261 date = ''
262 262 @LazyProperty
263 263 def raw_id(self):
264 264 """
265 265 Returns raw string identifing this changeset, useful for web
266 266 representation.
267 267 """
268 268 return '0' * 40
269 269
270 270 @LazyProperty
271 271 def short_id(self):
272 272 return self.raw_id[:12]
273 273
274 274 def get_file_changeset(self, path):
275 275 return self
276 276
277 277 def get_file_content(self, path):
278 278 return u''
279 279
280 280 def get_file_size(self, path):
281 281 return 0
282 282
283 283 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
284 284 """
285 285 maps all found repositories into db
286 286 """
287 287 from rhodecode.model.repo_model import RepoModel
288 288
289 289 sa = meta.Session
290 290 user = sa.query(User).filter(User.admin == True).first()
291 291
292 292 rm = RepoModel()
293 293
294 294 for name, repo in initial_repo_list.items():
295 295 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
296 296 log.info('repository %s not found creating default', name)
297 297
298 298 form_data = {
299 299 'repo_name':name,
300 300 'description':repo.description if repo.description != 'unknown' else \
301 301 'auto description for %s' % name,
302 302 'private':False
303 303 }
304 304 rm.create(form_data, user, just_db=True)
305 305
306 306
307 307 if remove_obsolete:
308 308 #remove from database those repositories that are not in the filesystem
309 309 for repo in sa.query(Repository).all():
310 310 if repo.repo_name not in initial_repo_list.keys():
311 311 sa.delete(repo)
312 312 sa.commit()
313 313
314 314
315 315 meta.Session.remove()
316 316
317 317 from UserDict import DictMixin
318 318
319 319 class OrderedDict(dict, DictMixin):
320 320
321 321 def __init__(self, *args, **kwds):
322 322 if len(args) > 1:
323 323 raise TypeError('expected at most 1 arguments, got %d' % len(args))
324 324 try:
325 325 self.__end
326 326 except AttributeError:
327 327 self.clear()
328 328 self.update(*args, **kwds)
329 329
330 330 def clear(self):
331 331 self.__end = end = []
332 332 end += [None, end, end] # sentinel node for doubly linked list
333 333 self.__map = {} # key --> [key, prev, next]
334 334 dict.clear(self)
335 335
336 336 def __setitem__(self, key, value):
337 337 if key not in self:
338 338 end = self.__end
339 339 curr = end[1]
340 340 curr[2] = end[1] = self.__map[key] = [key, curr, end]
341 341 dict.__setitem__(self, key, value)
342 342
343 343 def __delitem__(self, key):
344 344 dict.__delitem__(self, key)
345 345 key, prev, next = self.__map.pop(key)
346 346 prev[2] = next
347 347 next[1] = prev
348 348
349 349 def __iter__(self):
350 350 end = self.__end
351 351 curr = end[2]
352 352 while curr is not end:
353 353 yield curr[0]
354 354 curr = curr[2]
355 355
356 356 def __reversed__(self):
357 357 end = self.__end
358 358 curr = end[1]
359 359 while curr is not end:
360 360 yield curr[0]
361 361 curr = curr[1]
362 362
363 363 def popitem(self, last=True):
364 364 if not self:
365 365 raise KeyError('dictionary is empty')
366 366 if last:
367 367 key = reversed(self).next()
368 368 else:
369 369 key = iter(self).next()
370 370 value = self.pop(key)
371 371 return key, value
372 372
373 373 def __reduce__(self):
374 374 items = [[k, self[k]] for k in self]
375 375 tmp = self.__map, self.__end
376 376 del self.__map, self.__end
377 377 inst_dict = vars(self).copy()
378 378 self.__map, self.__end = tmp
379 379 if inst_dict:
380 380 return (self.__class__, (items,), inst_dict)
381 381 return self.__class__, (items,)
382 382
383 383 def keys(self):
384 384 return list(self)
385 385
386 386 setdefault = DictMixin.setdefault
387 387 update = DictMixin.update
388 388 pop = DictMixin.pop
389 389 values = DictMixin.values
390 390 items = DictMixin.items
391 391 iterkeys = DictMixin.iterkeys
392 392 itervalues = DictMixin.itervalues
393 393 iteritems = DictMixin.iteritems
394 394
395 395 def __repr__(self):
396 396 if not self:
397 397 return '%s()' % (self.__class__.__name__,)
398 398 return '%s(%r)' % (self.__class__.__name__, self.items())
399 399
400 400 def copy(self):
401 401 return self.__class__(self)
402 402
403 403 @classmethod
404 404 def fromkeys(cls, iterable, value=None):
405 405 d = cls()
406 406 for key in iterable:
407 407 d[key] = value
408 408 return d
409 409
410 410 def __eq__(self, other):
411 411 if isinstance(other, OrderedDict):
412 412 return len(self) == len(other) and self.items() == other.items()
413 413 return dict.__eq__(self, other)
414 414
415 415 def __ne__(self, other):
416 416 return not self == other
417 417
418 418
419 419 #===============================================================================
420 420 # TEST FUNCTIONS
421 421 #===============================================================================
422 422 def create_test_index(repo_location, full_index):
423 423 """Makes default test index
424 424 @param repo_location:
425 425 @param full_index:
426 426 """
427 427 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
428 428 from rhodecode.lib.pidlock import DaemonLock, LockHeld
429 429 from rhodecode.lib.indexers import IDX_LOCATION
430 430 import shutil
431 431
432 432 if os.path.exists(IDX_LOCATION):
433 433 shutil.rmtree(IDX_LOCATION)
434 434
435 435 try:
436 436 l = DaemonLock()
437 437 WhooshIndexingDaemon(repo_location=repo_location)\
438 438 .run(full_index=full_index)
439 439 l.release()
440 440 except LockHeld:
441 441 pass
442 442
443 443 def create_test_env(repos_test_path, config):
444 444 """Makes a fresh database and
445 445 install test repository into tmp dir
446 446 """
447 447 from rhodecode.lib.db_manage import DbManage
448 448 import tarfile
449 449 import shutil
450 450 from os.path import dirname as dn, join as jn, abspath
451 451
452 452 log = logging.getLogger('TestEnvCreator')
453 453 # create logger
454 454 log.setLevel(logging.DEBUG)
455 455 log.propagate = True
456 456 # create console handler and set level to debug
457 457 ch = logging.StreamHandler()
458 458 ch.setLevel(logging.DEBUG)
459 459
460 460 # create formatter
461 461 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
462 462
463 463 # add formatter to ch
464 464 ch.setFormatter(formatter)
465 465
466 466 # add ch to logger
467 467 log.addHandler(ch)
468 468
469 469 #PART ONE create db
470 log.debug('making test db in %s', repos_test_path)
471 470 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
471 log.debug('making test db %s', dbname)
472 472
473 473 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
474 474 tests=True)
475 475 dbmanage.create_tables(override=True)
476 476 dbmanage.config_prompt(repos_test_path)
477 477 dbmanage.create_default_user()
478 478 dbmanage.admin_prompt()
479 479 dbmanage.create_permissions()
480 480 dbmanage.populate_default_permissions()
481 481
482 482 #PART TWO make test repo
483 483 log.debug('making test vcs repo')
484 484 if os.path.isdir('/tmp/vcs_test'):
485 485 shutil.rmtree('/tmp/vcs_test')
486 486
487 487 cur_dir = dn(dn(abspath(__file__)))
488 488 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
489 489 tar.extractall('/tmp')
490 490 tar.close()
@@ -1,8 +1,31 b''
1 1 from rhodecode.tests import *
2 2
3 3 class TestChangelogController(TestController):
4 4
5 5 def test_index(self):
6 6 self.log_user()
7 7 response = self.app.get(url(controller='changelog', action='index',repo_name='vcs_test'))
8 # Test response...
8
9 print response
10 assert """<div id="chg_20" class="container">""" in response.body, 'wrong info about number ofchanges'
11 assert """Small update at simplevcs app""" in response.body, 'missing info about commit message'
12 assert """<span class="removed" title="removed">0</span>""" in response.body, 'wrong info about removed nodes'
13 assert """<span class="changed" title="changed">2</span>""" in response.body, 'wrong info about changed nodes'
14 assert """<span class="added" title="added">1</span>""" in response.body, 'wrong info about added nodes'
15
16 #pagination
17
18 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':1})
19 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':2})
20 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':3})
21 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':4})
22 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':5})
23 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':6})
24 # Test response after pagination...
25
26 assert """<span class="removed" title="removed">20</span>"""in response.body, 'wrong info about number of removed'
27 assert """<span class="changed" title="changed">1</span>"""in response.body, 'wrong info about number of changes'
28 assert """<span class="added" title="added">0</span>"""in response.body, 'wrong info about number of added'
29 assert """<div class="date">commit 64: 46ad32a4f974@2010-04-20 00:33:21</div>"""in response.body, 'wrong info about commit 64'
30 assert """<div class="message"><a href="/vcs_test/changeset/46ad32a4f974">Merge with 2e6a2bf9356ca56df08807f4ad86d480da72a8f4</a></div>"""in response.body, 'wrong info about commit 64 is a merge'
31
@@ -1,147 +1,147 b''
1 1 from rhodecode.tests import *
2 2 from rhodecode.model.db import User
3 3 from rhodecode.lib.auth import check_password
4 4
5 5
6 6 class TestLoginController(TestController):
7 7
8 8 def test_index(self):
9 9 response = self.app.get(url(controller='login', action='index'))
10 10 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
11 11 # Test response...
12 12
13 13 def test_login_admin_ok(self):
14 14 response = self.app.post(url(controller='login', action='index'),
15 15 {'username':'test_admin',
16 16 'password':'test12'})
17 17 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
18 18 assert response.session['rhodecode_user'].username == 'test_admin', 'wrong logged in user'
19 19 response = response.follow()
20 20 assert 'auto description for vcs_test' in response.body
21 21
22 22 def test_login_regular_ok(self):
23 23 response = self.app.post(url(controller='login', action='index'),
24 24 {'username':'test_regular',
25 25 'password':'test12'})
26 26 print response
27 27 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
28 28 assert response.session['rhodecode_user'].username == 'test_regular', 'wrong logged in user'
29 29 response = response.follow()
30 30 assert 'auto description for vcs_test' in response.body
31 31 assert '<a title="Admin" href="/_admin">' not in response.body
32 32
33 33 def test_login_ok_came_from(self):
34 34 test_came_from = '/_admin/users'
35 35 response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
36 36 {'username':'test_admin',
37 37 'password':'test12'})
38 38 assert response.status == '302 Found', 'Wrong response code from came from redirection'
39 39 response = response.follow()
40 40
41 41 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
42 42 assert 'Users administration' in response.body, 'No proper title in response'
43 43
44 44
45 45 def test_login_short_password(self):
46 46 response = self.app.post(url(controller='login', action='index'),
47 47 {'username':'error',
48 48 'password':'test'})
49 49 assert response.status == '200 OK', 'Wrong response from login page'
50
51 assert 'Enter a value 6 characters long or more' in response.body, 'No error password message in response'
50 print response.body
51 assert 'Enter 6 characters or more' in response.body, 'No error password message in response'
52 52
53 53 def test_login_wrong_username_password(self):
54 54 response = self.app.post(url(controller='login', action='index'),
55 55 {'username':'error',
56 56 'password':'test12'})
57 57 assert response.status == '200 OK', 'Wrong response from login page'
58 58
59 59 assert 'invalid user name' in response.body, 'No error username message in response'
60 60 assert 'invalid password' in response.body, 'No error password message in response'
61 61
62 62
63 63 def test_register(self):
64 64 response = self.app.get(url(controller='login', action='register'))
65 65 assert 'Sign Up to rhodecode' in response.body, 'wrong page for user registration'
66 66
67 67 def test_register_err_same_username(self):
68 68 response = self.app.post(url(controller='login', action='register'),
69 69 {'username':'test_admin',
70 70 'password':'test',
71 71 'email':'goodmail@domain.com',
72 72 'name':'test',
73 73 'lastname':'test'})
74 74
75 75 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
76 76 assert 'This username already exists' in response.body
77 77
78 78 def test_register_err_wrong_data(self):
79 79 response = self.app.post(url(controller='login', action='register'),
80 80 {'username':'xs',
81 81 'password':'',
82 82 'email':'goodmailm',
83 83 'name':'test',
84 84 'lastname':'test'})
85 85
86 86 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
87 87 assert 'An email address must contain a single @' in response.body
88 88 assert 'Please enter a value' in response.body
89 89
90 90
91 91
92 92 def test_register_ok(self):
93 93 username = 'test_regular4'
94 94 password = 'qweqwe'
95 95 email = 'marcin@test.com'
96 96 name = 'testname'
97 97 lastname = 'testlastname'
98 98
99 99 response = self.app.post(url(controller='login', action='register'),
100 100 {'username':username,
101 101 'password':password,
102 102 'email':email,
103 103 'name':name,
104 104 'lastname':lastname})
105 105 print response.body
106 106 assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
107 107 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
108 108
109 109 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
110 110 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
111 111 assert check_password(password, ret.password) == True , 'password mismatch'
112 112 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
113 113 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
114 114 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
115 115
116 116
117 117 def test_forgot_password_wrong_mail(self):
118 118 response = self.app.post(url(controller='login', action='password_reset'),
119 119 {'email':'marcin@wrongmail.org', })
120 120
121 121 assert "That e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
122 122
123 123 def test_forgot_password(self):
124 124 response = self.app.get(url(controller='login', action='password_reset'))
125 125 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
126 126
127 127 username = 'test_password_reset_1'
128 128 password = 'qweqwe'
129 129 email = 'marcin@python-works.com'
130 130 name = 'passwd'
131 131 lastname = 'reset'
132 132
133 133 response = self.app.post(url(controller='login', action='register'),
134 134 {'username':username,
135 135 'password':password,
136 136 'email':email,
137 137 'name':name,
138 138 'lastname':lastname})
139 139 #register new user for email test
140 140 response = self.app.post(url(controller='login', action='password_reset'),
141 141 {'email':email, })
142 142 print response.session['flash']
143 143 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
144 144 assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
145 145
146 146
147 147
@@ -1,43 +1,72 b''
1 from rhodecode.model.db import Repository
1 2 from rhodecode.tests import *
2 3
3 4 class TestReposController(TestController):
4 5
5 6 def test_index(self):
7 self.log_user()
6 8 response = self.app.get(url('repos'))
7 9 # Test response...
8 10
9 11 def test_index_as_xml(self):
10 12 response = self.app.get(url('formatted_repos', format='xml'))
11 13
12 14 def test_create(self):
13 response = self.app.post(url('repos'))
15 self.log_user()
16 repo_name = 'vcs_test_new'
17 description = 'description for newly created repo'
18 private = False
19 response = self.app.post(url('repos'), {'repo_name':repo_name,
20 'description':description,
21 'private':private})
22
23 print response
24
25 #test if we have a message that fork is ok
26 assert '''created repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about new repo'
27
28 #test if the fork was created in the database
29 new_repo = self.sa.query(Repository).filter(Repository.repo_name == repo_name).one()
30
31 assert new_repo.repo_name == repo_name, 'wrong name of repo name in db'
32 assert new_repo.description == description, 'wrong description'
33
34 #test if repository is visible in the list ?
35 response = response.follow()
36
37 assert repo_name in response.body, 'missing new repo from the main repos list'
38
39
40
14 41
15 42 def test_new(self):
43 self.log_user()
16 44 response = self.app.get(url('new_repo'))
17 45
18 46 def test_new_as_xml(self):
19 47 response = self.app.get(url('formatted_new_repo', format='xml'))
20 48
21 49 def test_update(self):
22 50 response = self.app.put(url('repo', repo_name='vcs_test'))
23 51
24 52 def test_update_browser_fakeout(self):
25 53 response = self.app.post(url('repo', repo_name='vcs_test'), params=dict(_method='put'))
26 54
27 55 def test_delete(self):
28 56 response = self.app.delete(url('repo', repo_name='vcs_test'))
29 57
30 58 def test_delete_browser_fakeout(self):
31 59 response = self.app.post(url('repo', repo_name='vcs_test'), params=dict(_method='delete'))
32 60
33 61 def test_show(self):
62 self.log_user()
34 63 response = self.app.get(url('repo', repo_name='vcs_test'))
35 64
36 65 def test_show_as_xml(self):
37 66 response = self.app.get(url('formatted_repo', repo_name='vcs_test', format='xml'))
38 67
39 68 def test_edit(self):
40 69 response = self.app.get(url('edit_repo', repo_name='vcs_test'))
41 70
42 71 def test_edit_as_xml(self):
43 72 response = self.app.get(url('formatted_edit_repo', repo_name='vcs_test', format='xml'))
@@ -1,55 +1,55 b''
1 1 from rhodecode.model.db import Repository
2 2 from rhodecode.tests import *
3 3
4 4 class TestSettingsController(TestController):
5 5
6 6 def test_index(self):
7 7 self.log_user()
8 8 response = self.app.get(url(controller='settings', action='index',
9 9 repo_name='vcs_test'))
10 10 # Test response...
11 11
12 12 def test_fork(self):
13 13 self.log_user()
14 14 response = self.app.get(url(controller='settings', action='fork',
15 15 repo_name='vcs_test'))
16 16
17 17
18 18 def test_fork_create(self):
19 19 self.log_user()
20 20 fork_name = 'vcs_test_fork'
21 21 description = 'fork of vcs test'
22 22 repo_name = 'vcs_test'
23 23 response = self.app.post(url(controller='settings', action='fork_create',
24 24 repo_name=repo_name),
25 25 {'fork_name':fork_name,
26 26 'description':description,
27 27 'private':'False'})
28 28
29 29
30 30 print response
31 31
32 32 #test if we have a message that fork is ok
33 33 assert 'fork %s repository as %s task added' \
34 34 % (repo_name, fork_name) in response.session['flash'][0], 'No flash message about fork'
35 35
36 36 #test if the fork was created in the database
37 37 fork_repo = self.sa.query(Repository).filter(Repository.repo_name == fork_name).one()
38 38
39 39 assert fork_repo.repo_name == fork_name, 'wrong name of repo name in new db fork repo'
40 assert fork_repo.fork.repo_name == repo_name, 'wron fork parrent'
40 assert fork_repo.fork.repo_name == repo_name, 'wrong fork parrent'
41 41
42 42
43 43 #test if fork is visible in the list ?
44 44 response = response.follow()
45 45
46 46
47 47 #check if fork is marked as fork
48 48 response = self.app.get(url(controller='summary', action='index',
49 49 repo_name=fork_name))
50 50
51 51
52 52 print response
53 53
54 54 assert 'Fork of %s' % repo_name in response.body, 'no message about that this repo is a fork'
55 55
@@ -1,8 +1,11 b''
1 1 from rhodecode.tests import *
2 2
3 3 class TestSummaryController(TestController):
4 4
5 5 def test_index(self):
6 6 self.log_user()
7 7 response = self.app.get(url(controller='summary', action='index',repo_name='vcs_test'))
8 print response
9 assert """<img style="margin-bottom:2px" class="icon" title="public repository" alt="public" src="/images/icons/lock_open.png"/>""" in response.body
10
8 11 # Test response...
General Comments 0
You need to be logged in to leave comments. Login now