##// END OF EJS Templates
fixes #200, rewrote the whole caching mechanism to get rid of such problems. Now cached instances are attached...
marcink -
r1366:9c0f5d55 beta
parent child Browse files
Show More
@@ -89,10 +89,8 b' class ReposController(BaseController):'
89 89 """
90 90 self.__load_defaults()
91 91
92 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
93
94 repo_model = RepoModel()
95 c.repo_info = repo_model.get_by_repo_name(repo_name)
92 c.repo_info = db_repo = Repository.by_repo_name(repo_name)
93 repo = scm_repo = db_repo.scm_instance
96 94
97 95 if c.repo_info is None:
98 96 h.flash(_('%s repository is not mapped to db perhaps'
@@ -153,10 +151,9 b' class ReposController(BaseController):'
153 151 """GET /repos: All items in the collection"""
154 152 # url('repos')
155 153
156 all_repos = [r.repo_name for r in Repository.query().all()]
157
158 cached_repo_list = ScmModel().get_repos(all_repos)
159 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
154 c.repos_list = ScmModel().get_repos(Repository.query()
155 .order_by(Repository.repo_name)
156 .all(), sort_key='name_sort')
160 157 return render('admin/repos/repos.html')
161 158
162 159 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
@@ -181,12 +181,14 b' class ReposGroupsController(BaseControll'
181 181 """GET /repos_groups/id: Show a specific item"""
182 182 # url('repos_group', id=ID)
183 183
184 c.group = Group.get(id)
184 gr = c.group = Group.get(id)
185
185 186 if c.group:
186 187 c.group_repos = c.group.repositories.all()
187 188 else:
188 189 return redirect(url('repos_group'))
189 190
191
190 192 sortables = ['name', 'description', 'last_change', 'tip', 'owner']
191 193 current_sort = request.GET.get('sort', 'name')
192 194 current_sort_slug = current_sort.replace('-', '')
@@ -201,18 +203,12 b' class ReposGroupsController(BaseControll'
201 203 sort_key = current_sort_slug + '_sort'
202 204
203 205 #overwrite our cached list with current filter
204 gr_filter = [r.repo_name for r in c.group_repos]
206 gr_filter = c.group_repos
205 207 c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
206 208
207 if c.sort_by.startswith('-'):
208 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
209 reverse=True)
210 else:
211 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
212 reverse=False)
209 c.repos_list = c.cached_repo_list
213 210
214 c.repo_cnt = len(c.repos_list)
215
211 c.repo_cnt = 0
216 212
217 213 c.groups = self.sa.query(Group).order_by(Group.group_name)\
218 214 .filter(Group.group_parent_id == id).all()
@@ -258,9 +258,10 b' class SettingsController(BaseController)'
258 258 # url('admin_settings_my_account')
259 259
260 260 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
261 all_repos = [r.repo_name for r in self.sa.query(Repository)\
261 all_repos = self.sa.query(Repository)\
262 262 .filter(Repository.user_id == c.user.user_id)\
263 .order_by(func.lower(Repository.repo_name)).all()]
263 .order_by(func.lower(Repository.repo_name)).all()
264
264 265 c.user_repos = ScmModel().get_repos(all_repos)
265 266
266 267 if c.user.username == 'default':
@@ -31,7 +31,7 b' from paste.httpexceptions import HTTPBad'
31 31
32 32 from rhodecode.lib.auth import LoginRequired
33 33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.model.db import Group
34 from rhodecode.model.db import Group, Repository
35 35
36 36 log = logging.getLogger(__name__)
37 37
@@ -56,16 +56,11 b' class HomeController(BaseController):'
56 56
57 57 sort_key = current_sort_slug + '_sort'
58 58
59 if c.sort_by.startswith('-'):
60 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
61 reverse=True)
62 else:
63 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
64 reverse=False)
59
60 c.repos_list = self.scm_model.get_repos(sort_key=sort_key)
65 61
66 62 c.repo_cnt = len(c.repos_list)
67 63
68
69 64 c.groups = Group.query().filter(Group.group_parent_id == None).all()
70 65
71 66
@@ -73,8 +68,9 b' class HomeController(BaseController):'
73 68
74 69 def repo_switcher(self):
75 70 if request.is_xhr:
76 c.repos_list = sorted(c.cached_repo_list,
77 key=itemgetter('name_sort'), reverse=False)
71 all_repos = Repository.query().order_by(Repository.repo_name).all()
72 c.repos_list = self.scm_model.get_repos(all_repos,
73 sort_key='name_sort')
78 74 return render('/repo_switcher_list.html')
79 75 else:
80 76 return HTTPBadRequest()
@@ -155,6 +155,7 b' class SettingsController(BaseRepoControl'
155 155 invalidate_cache('get_repo_cached_%s' % repo_name)
156 156 h.flash(_('deleted repository %s') % repo_name, category='success')
157 157 except Exception:
158 log.error(traceback.format_exc())
158 159 h.flash(_('An error occurred during deletion of %s') % repo_name,
159 160 category='error')
160 161
@@ -205,4 +206,9 b' class SettingsController(BaseRepoControl'
205 206 errors=errors.error_dict or {},
206 207 prefix_error=False,
207 208 encoding="UTF-8")
209 except Exception:
210 log.error(traceback.format_exc())
211 h.flash(_('An error occurred during repository forking %s') %
212 repo_name, category='error')
213
208 214 return redirect(url('home'))
@@ -12,6 +12,7 b' from rhodecode.lib.utils import get_repo'
12 12 from rhodecode.model import meta
13 13 from rhodecode.model.scm import ScmModel
14 14 from rhodecode import BACKENDS
15 from rhodecode.model.db import Repository
15 16
16 17
17 18 class BaseController(WSGIController):
@@ -26,7 +27,7 b' class BaseController(WSGIController):'
26 27
27 28 self.sa = meta.Session()
28 29 self.scm_model = ScmModel(self.sa)
29 c.cached_repo_list = self.scm_model.get_repos()
30
30 31 #c.unread_journal = scm_model.get_unread_journal()
31 32
32 33 def __call__(self, environ, start_response):
@@ -62,8 +63,7 b' class BaseRepoController(BaseController)'
62 63 super(BaseRepoController, self).__before__()
63 64 if c.repo_name:
64 65
65 c.rhodecode_repo, dbrepo = self.scm_model.get(c.repo_name,
66 retval='repo')
66 c.rhodecode_repo = Repository.by_repo_name(c.repo_name).scm_instance
67 67
68 68 if c.rhodecode_repo is not None:
69 69 c.repository_followers = \
@@ -102,7 +102,6 b' def get_commits_stats(repo_name, ts_min_'
102 102 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
103 103 ts_max_y)
104 104 lockkey_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
105 print jn(lockkey_path, lockkey)
106 105 log.info('running task with lockkey %s', lockkey)
107 106 try:
108 107 lock = l = DaemonLock(jn(lockkey_path, lockkey))
@@ -372,8 +372,7 b' def action_parser(user_log, feed=False):'
372 372 repo_name = user_log.repository.repo_name
373 373
374 374 from rhodecode.model.scm import ScmModel
375 repo, dbrepo = ScmModel().get(repo_name, retval='repo',
376 invalidation_list=[])
375 repo = user_log.repository.scm_instance
377 376
378 377 message = lambda rev: get_changeset_safe(repo, rev).message
379 378 cs_links = []
@@ -472,7 +472,7 b' def create_test_index(repo_location, ful'
472 472 shutil.rmtree(index_location)
473 473
474 474 try:
475 l = DaemonLock(file=jn(dn(dn(index_location)), 'make_index.lock'))
475 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
476 476 WhooshIndexingDaemon(index_location=index_location,
477 477 repo_location=repo_location)\
478 478 .run(full_index=full_index)
@@ -26,13 +26,23 b''
26 26 import os
27 27 import logging
28 28 import datetime
29 import traceback
29 30 from datetime import date
30 31
31 32 from sqlalchemy import *
32 33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.orm import relationship, backref
34 from sqlalchemy.orm import relationship, backref, joinedload
34 35 from sqlalchemy.orm.interfaces import MapperExtension
35 36
37 from beaker.cache import cache_region, region_invalidate
38
39
40 from vcs import get_backend
41 from vcs.utils.helpers import get_scm
42 from vcs.exceptions import RepositoryError, VCSError
43 from vcs.utils.lazy import LazyProperty
44 from vcs.nodes import FileNode
45
36 46 from rhodecode.lib import str2bool
37 47 from rhodecode.model.meta import Base, Session
38 48 from rhodecode.model.caching_query import FromCache
@@ -150,6 +160,7 b' class User(Base):'
150 160 return self.admin
151 161
152 162 def __repr__(self):
163 return 'ahmmm'
153 164 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
154 165 self.user_id, self.username)
155 166
@@ -266,8 +277,13 b' class Repository(Base):'
266 277
267 278 @classmethod
268 279 def by_repo_name(cls, repo_name):
269 return Session.query(cls).filter(cls.repo_name == repo_name).one()
280 q = Session.query(cls).filter(cls.repo_name == repo_name)
270 281
282 q = q.options(joinedload(Repository.fork))\
283 .options(joinedload(Repository.user))\
284 .options(joinedload(Repository.group))\
285
286 return q.one()
271 287
272 288 @classmethod
273 289 def get_repo_forks(cls, repo_id):
@@ -298,6 +314,127 b' class Repository(Base):'
298 314 def groups_and_repo(self):
299 315 return self.groups_with_parents, self.just_name
300 316
317 @LazyProperty
318 def repo_path(self):
319 """
320 Returns base full path for that repository means where it actually
321 exists on a filesystem
322 """
323
324 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
325 return q.ui_value
326
327 @property
328 def repo_full_path(self):
329 p = [self.repo_path]
330 # we need to split the name by / since this is how we store the
331 # names in the database, but that eventually needs to be converted
332 # into a valid system path
333 p += self.repo_name.split('/')
334 return os.path.join(*p)
335
336 @property
337 def _ui(self):
338 """
339 Creates an db based ui object for this repository
340 """
341 from mercurial import ui
342 from mercurial import config
343 baseui = ui.ui()
344
345 #clean the baseui object
346 baseui._ocfg = config.config()
347 baseui._ucfg = config.config()
348 baseui._tcfg = config.config()
349
350
351 ret = Session.query(RhodeCodeUi)\
352 .options(FromCache("sql_cache_short",
353 "repository_repo_ui")).all()
354
355 hg_ui = ret
356 for ui_ in hg_ui:
357 if ui_.ui_active:
358 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
359 ui_.ui_key, ui_.ui_value)
360 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
361
362 return baseui
363
364 #==========================================================================
365 # SCM CACHE INSTANCE
366 #==========================================================================
367
368 @property
369 def invalidate(self):
370 """
371 Returns Invalidation object if this repo should be invalidated
372 None otherwise. `cache_active = False` means that this cache
373 state is not valid and needs to be invalidated
374 """
375 return Session.query(CacheInvalidation)\
376 .filter(CacheInvalidation.cache_key == self.repo_name)\
377 .filter(CacheInvalidation.cache_active == False)\
378 .scalar()
379
380 @property
381 def set_invalidate(self):
382 """
383 set a cache for invalidation for this instance
384 """
385 inv = Session.query(CacheInvalidation)\
386 .filter(CacheInvalidation.cache_key == self.repo_name)\
387 .scalar()
388
389 if inv is None:
390 inv = CacheInvalidation(self.repo_name)
391 inv.cache_active = True
392 Session.add(inv)
393 Session.commit()
394
395 @property
396 def scm_instance(self):
397 return self.__get_instance(self.repo_name)
398
399 @property
400 def scm_instance_cached(self):
401 @cache_region('long_term')
402 def _c(repo_name):
403 return self.__get_instance(repo_name)
404
405 inv = self.invalidate
406 if inv:
407 region_invalidate(_c, None, self.repo_name)
408 #update our cache
409 inv.cache_key.cache_active = True
410 Session.add(inv)
411 Session.commit()
412
413 return _c(self.repo_name)
414
415 def __get_instance(self, repo_name):
416 try:
417 alias = get_scm(self.repo_full_path)[0]
418 log.debug('Creating instance of %s repository', alias)
419 backend = get_backend(alias)
420 except VCSError:
421 log.error(traceback.format_exc())
422 log.error('Perhaps this repository is in db and not in '
423 'filesystem run rescan repositories with '
424 '"destroy old data " option from admin panel')
425 return
426
427 if alias == 'hg':
428 repo = backend(self.repo_full_path, create=False,
429 baseui=self._ui)
430 #skip hidden web repository
431 if repo._get_hidden():
432 return
433 else:
434 repo = backend(self.repo_full_path, create=False)
435
436 return repo
437
301 438
302 439 class Group(Base):
303 440 __tablename__ = 'groups'
@@ -280,6 +280,13 b' def ValidRepoName(edit, old_data):'
280 280
281 281 return _ValidRepoName
282 282
283 def ValidForkName():
284 class _ValidForkName(formencode.validators.FancyValidator):
285 def to_python(self, value, state):
286 return value
287 return _ValidForkName
288
289
283 290 def SlugifyName():
284 291 class _SlugifyName(formencode.validators.FancyValidator):
285 292
@@ -326,6 +333,7 b' def ValidForkType(old_data):'
326 333 if old_data['repo_type'] != value:
327 334 raise formencode.Invalid(_('Fork have to be the same '
328 335 'type as original'), value, state)
336
329 337 return value
330 338 return _ValidForkType
331 339
@@ -583,6 +591,9 b' def RepoForkForm(edit=False, old_data={}'
583 591 description = UnicodeString(strip=True, min=1, not_empty=True)
584 592 private = StringBoolean(if_missing=False)
585 593 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
594
595 chained_validators = [ValidForkName()]
596
586 597 return _RepoForkForm
587 598
588 599 def RepoSettingsForm(edit=False, old_data={}):
@@ -70,28 +70,6 b' class RepoModel(BaseModel):'
70 70 "get_repo_%s" % repo_name))
71 71 return repo.scalar()
72 72
73 def get_full(self, repo_name, cache=False, invalidate=False):
74 repo = self.sa.query(Repository)\
75 .options(joinedload(Repository.fork))\
76 .options(joinedload(Repository.user))\
77 .options(joinedload(Repository.group))\
78 .filter(Repository.repo_name == repo_name)\
79
80 if cache:
81 repo = repo.options(FromCache("sql_cache_long",
82 "get_repo_full_%s" % repo_name))
83 if invalidate and cache:
84 repo.invalidate()
85
86 ret = repo.scalar()
87
88 #make transient for sake of errors
89 make_transient(ret)
90 for k in ['fork', 'user', 'group']:
91 attr = getattr(ret, k, False)
92 if attr:
93 make_transient(attr)
94 return ret
95 73
96 74 def get_users_js(self):
97 75
@@ -193,12 +171,13 b' class RepoModel(BaseModel):'
193 171 raise
194 172
195 173 def create(self, form_data, cur_user, just_db=False, fork=False):
174
196 175 try:
197 176 if fork:
198 177 #force str since hg doesn't go with unicode
199 178 repo_name = str(form_data['fork_name'])
200 179 org_name = str(form_data['repo_name'])
201 org_full_name = str(form_data['repo_name_full'])
180 org_full_name = org_name#str(form_data['fork_name_full'])
202 181
203 182 else:
204 183 org_name = repo_name = str(form_data['repo_name'])
@@ -208,7 +187,10 b' class RepoModel(BaseModel):'
208 187 new_repo.enable_statistics = False
209 188 for k, v in form_data.items():
210 189 if k == 'repo_name':
211 v = repo_name_full
190 if fork:
191 v = repo_name
192 else:
193 v = repo_name_full
212 194 if k == 'repo_group':
213 195 k = 'group_id'
214 196
@@ -216,7 +198,7 b' class RepoModel(BaseModel):'
216 198
217 199 if fork:
218 200 parent_repo = self.sa.query(Repository)\
219 .filter(Repository.repo_name == org_full_name).scalar()
201 .filter(Repository.repo_name == org_full_name).one()
220 202 new_repo.fork = parent_repo
221 203
222 204 new_repo.user_id = cur_user.user_id
@@ -70,6 +70,75 b' class RepoTemp(object):'
70 70 def __repr__(self):
71 71 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
72 72
73 class CachedRepoList(object):
74
75 def __init__(self, db_repo_list, invalidation_list, repos_path,
76 order_by=None):
77 self.db_repo_list = db_repo_list
78 self.invalidation_list = invalidation_list
79 self.repos_path = repos_path
80 self.order_by = order_by
81 self.reversed = (order_by or '').startswith('-')
82
83 def __len__(self):
84 return len(self.db_repo_list)
85
86 def __repr__(self):
87 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
88
89 def __iter__(self):
90 for db_repo in self.db_repo_list:
91 dbr = db_repo
92
93 # invalidate the repo cache if needed before getting the
94 # scm instance
95
96 scm_invalidate = False
97 if self.invalidation_list is not None:
98 scm_invalidate = dbr.repo_name in self.invalidation_list
99
100 if scm_invalidate:
101 log.info('invalidating cache for repository %s',
102 dbr.repo_name)
103 db_repo.set_invalidate
104
105 scmr = db_repo.scm_instance_cached
106
107 #check permission at this level
108 if not HasRepoPermissionAny('repository.read',
109 'repository.write',
110 'repository.admin')(dbr.repo_name,
111 'get repo check'):
112 continue
113
114
115
116
117
118 last_change = scmr.last_change
119 tip = h.get_changeset_safe(scmr, 'tip')
120
121 tmp_d = {}
122 tmp_d['name'] = dbr.repo_name
123 tmp_d['name_sort'] = tmp_d['name'].lower()
124 tmp_d['description'] = dbr.description
125 tmp_d['description_sort'] = tmp_d['description']
126 tmp_d['last_change'] = last_change
127 tmp_d['last_change_sort'] = time.mktime(last_change \
128 .timetuple())
129 tmp_d['tip'] = tip.raw_id
130 tmp_d['tip_sort'] = tip.revision
131 tmp_d['rev'] = tip.revision
132 tmp_d['contact'] = dbr.user.full_contact
133 tmp_d['contact_sort'] = tmp_d['contact']
134 tmp_d['owner_sort'] = tmp_d['contact']
135 tmp_d['repo_archives'] = list(scmr._get_archives())
136 tmp_d['last_msg'] = tip.message
137 tmp_d['repo'] = scmr
138 tmp_d['dbrepo'] = dbr.get_dict()
139 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
140 else {}
141 yield tmp_d
73 142
74 143 class ScmModel(BaseModel):
75 144 """Generic Scm Model
@@ -118,7 +187,7 b' class ScmModel(BaseModel):'
118 187
119 188 return repos_list
120 189
121 def get_repos(self, all_repos=None):
190 def get_repos(self, all_repos=None, sort_key=None):
122 191 """
123 192 Get all repos from db and for each repo create it's
124 193 backend instance and fill that backed with information from database
@@ -127,120 +196,21 b' class ScmModel(BaseModel):'
127 196 give specific repositories list, good for filtering
128 197 """
129 198 if all_repos is None:
130 repos = self.sa.query(Repository)\
199 all_repos = self.sa.query(Repository)\
131 200 .filter(Repository.group_id == None)\
132 201 .order_by(Repository.repo_name).all()
133 all_repos = [r.repo_name for r in repos]
134 202
135 203 #get the repositories that should be invalidated
136 204 invalidation_list = [str(x.cache_key) for x in \
137 205 self.sa.query(CacheInvalidation.cache_key)\
138 206 .filter(CacheInvalidation.cache_active == False)\
139 207 .all()]
140 for r_name in all_repos:
141 r_dbr = self.get(r_name, invalidation_list)
142 if r_dbr is not None:
143 repo, dbrepo = r_dbr
144
145 if repo is None or dbrepo is None:
146 log.error('Repository "%s" looks somehow corrupted '
147 'fs-repo:%s,db-repo:%s both values should be '
148 'present', r_name, repo, dbrepo)
149 continue
150 last_change = repo.last_change
151 tip = h.get_changeset_safe(repo, 'tip')
152
153 tmp_d = {}
154 tmp_d['name'] = dbrepo.repo_name
155 tmp_d['name_sort'] = tmp_d['name'].lower()
156 tmp_d['description'] = dbrepo.description
157 tmp_d['description_sort'] = tmp_d['description']
158 tmp_d['last_change'] = last_change
159 tmp_d['last_change_sort'] = time.mktime(last_change \
160 .timetuple())
161 tmp_d['tip'] = tip.raw_id
162 tmp_d['tip_sort'] = tip.revision
163 tmp_d['rev'] = tip.revision
164 tmp_d['contact'] = dbrepo.user.full_contact
165 tmp_d['contact_sort'] = tmp_d['contact']
166 tmp_d['owner_sort'] = tmp_d['contact']
167 tmp_d['repo_archives'] = list(repo._get_archives())
168 tmp_d['last_msg'] = tip.message
169 tmp_d['repo'] = repo
170 tmp_d['dbrepo'] = dbrepo.get_dict()
171 tmp_d['dbrepo_fork'] = dbrepo.fork.get_dict() if dbrepo.fork \
172 else {}
173 yield tmp_d
174
175 def get(self, repo_name, invalidation_list=None, retval='all'):
176 """Returns a tuple of Repository,DbRepository,
177 Get's repository from given name, creates BackendInstance and
178 propagates it's data from database with all additional information
179
180 :param repo_name:
181 :param invalidation_list: if a invalidation list is given the get
182 method should not manually check if this repository needs
183 invalidation and just invalidate the repositories in list
184 :param retval: string specifing what to return one of 'repo','dbrepo',
185 'all'if repo or dbrepo is given it'll just lazy load chosen type
186 and return None as the second
187 """
188 if not HasRepoPermissionAny('repository.read', 'repository.write',
189 'repository.admin')(repo_name, 'get repo check'):
190 return
191 208
192 #======================================================================
193 # CACHE FUNCTION
194 #======================================================================
195 @cache_region('long_term')
196 def _get_repo(repo_name):
197
198 repo_path = os.path.join(self.repos_path, repo_name)
199
200 try:
201 alias = get_scm(repo_path)[0]
202 log.debug('Creating instance of %s repository', alias)
203 backend = get_backend(alias)
204 except VCSError:
205 log.error(traceback.format_exc())
206 log.error('Perhaps this repository is in db and not in '
207 'filesystem run rescan repositories with '
208 '"destroy old data " option from admin panel')
209 return
209 repo_iter = CachedRepoList(all_repos, invalidation_list,
210 repos_path=self.repos_path,
211 order_by=sort_key)
210 212
211 if alias == 'hg':
212 repo = backend(repo_path, create=False, baseui=make_ui('db'))
213 #skip hidden web repository
214 if repo._get_hidden():
215 return
216 else:
217 repo = backend(repo_path, create=False)
218
219 return repo
220
221 pre_invalidate = True
222 dbinvalidate = False
223
224 if invalidation_list is not None:
225 pre_invalidate = repo_name in invalidation_list
226
227 if pre_invalidate:
228 #this returns object to invalidate
229 invalidate = self._should_invalidate(repo_name)
230 if invalidate:
231 log.info('invalidating cache for repository %s', repo_name)
232 region_invalidate(_get_repo, None, repo_name)
233 self._mark_invalidated(invalidate)
234 dbinvalidate = True
235
236 r, dbr = None, None
237 if retval == 'repo' or 'all':
238 r = _get_repo(repo_name)
239 if retval == 'dbrepo' or 'all':
240 dbr = RepoModel().get_full(repo_name, cache=True,
241 invalidate=dbinvalidate)
242
243 return r, dbr
213 return repo_iter
244 214
245 215 def mark_for_invalidation(self, repo_name):
246 216 """Puts cache invalidation task into db for
@@ -65,7 +65,7 b''
65 65 <label for="new_password">${_('New password')}:</label>
66 66 </div>
67 67 <div class="input">
68 ${h.password('new_password',class_='medium')}
68 ${h.password('new_password',class_='medium',autocomplete="off")}
69 69 </div>
70 70 </div>
71 71
@@ -54,7 +54,7 b''
54 54 <label for="new_password">${_('New password')}:</label>
55 55 </div>
56 56 <div class="input">
57 ${h.password('new_password',class_="medium")}
57 ${h.password('new_password',class_="medium",autocomplete="off")}
58 58 </div>
59 59 </div>
60 60
@@ -7,6 +7,9 b' command.'
7 7 This module initializes the application via ``websetup`` (`paster
8 8 setup-app`) and provides the base testing objects.
9 9 """
10 import os
11 from os.path import join as jn
12
10 13 from unittest import TestCase
11 14
12 15 from paste.deploy import loadapp
@@ -14,7 +17,7 b' from paste.script.appinstall import Setu'
14 17 from pylons import config, url
15 18 from routes.util import URLGenerator
16 19 from webtest import TestApp
17 import os
20
18 21 from rhodecode.model import meta
19 22 import logging
20 23
@@ -35,7 +38,7 b' import pylons.test'
35 38 environ = {}
36 39
37 40 #SOME GLOBALS FOR TESTS
38 TESTS_TMP_PATH = '/tmp'
41 TESTS_TMP_PATH = jn('/', 'tmp')
39 42
40 43 HG_REPO = 'vcs_test_hg'
41 44 GIT_REPO = 'vcs_test_git'
@@ -64,8 +67,8 b' class TestController(TestCase):'
64 67 'password':password})
65 68
66 69 if 'invalid user name' in response.body:
67 assert False, 'could not login using %s %s' % (username, password)
70 self.fail('could not login using %s %s' % (username, password))
68 71
69 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
70 assert response.session['rhodecode_user'].username == username, 'wrong logged in user got %s expected %s' % (response.session['rhodecode_user'].username, username)
72 self.assertEqual(response.status, '302 Found')
73 self.assertEqual(response.session['rhodecode_user'].username, username)
71 74 return response.follow()
@@ -6,6 +6,11 b' from rhodecode.tests import *'
6 6
7 7 class TestAdminReposController(TestController):
8 8
9
10 def __make_repo(self):
11 pass
12
13
9 14 def test_index(self):
10 15 self.log_user()
11 16 response = self.app.get(url('repos'))
@@ -21,31 +26,39 b' class TestAdminReposController(TestContr'
21 26 private = False
22 27 response = self.app.post(url('repos'), {'repo_name':repo_name,
23 28 'repo_type':'hg',
29 'clone_uri':'',
30 'repo_group':'',
24 31 'description':description,
25 32 'private':private})
26
33 self.assertTrue('flash' in response.session)
27 34
28 35 #test if we have a message for that repository
29 assert '''created repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about new repo'
36 self.assertTrue('''created repository %s''' % (repo_name) in
37 response.session['flash'][0])
30 38
31 #test if the fork was created in the database
32 new_repo = self.sa.query(Repository).filter(Repository.repo_name == repo_name).one()
39 #test if the repo was created in the database
40 new_repo = self.sa.query(Repository).filter(Repository.repo_name ==
41 repo_name).one()
33 42
34 assert new_repo.repo_name == repo_name, 'wrong name of repo name in db'
35 assert new_repo.description == description, 'wrong description'
43 self.assertEqual(new_repo.repo_name, repo_name)
44 self.assertEqual(new_repo.description, description)
36 45
37 46 #test if repository is visible in the list ?
38 47 response = response.follow()
39 48
40 assert repo_name in response.body, 'missing new repo from the main repos list'
49 self.assertTrue(repo_name in response.body)
41 50
42 51
43 52 #test if repository was created on filesystem
44 53 try:
45 54 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
46 55 except:
47 assert False , 'no repo in filesystem'
56 self.fail('no repo in filesystem')
57
48 58
59 def test_create_hg_in_group(self):
60 #TODO: write test !
61 pass
49 62
50 63 def test_create_git(self):
51 64 return
@@ -55,6 +68,8 b' class TestAdminReposController(TestContr'
55 68 private = False
56 69 response = self.app.post(url('repos'), {'repo_name':repo_name,
57 70 'repo_type':'git',
71 'clone_uri':'',
72 'repo_group':'',
58 73 'description':description,
59 74 'private':private})
60 75
@@ -90,58 +105,74 b' class TestAdminReposController(TestContr'
90 105 response = self.app.put(url('repo', repo_name=HG_REPO))
91 106
92 107 def test_update_browser_fakeout(self):
93 response = self.app.post(url('repo', repo_name=HG_REPO), params=dict(_method='put'))
108 response = self.app.post(url('repo', repo_name=HG_REPO),
109 params=dict(_method='put'))
94 110
95 111 def test_delete(self):
96 112 self.log_user()
97 113 repo_name = 'vcs_test_new_to_delete'
98 114 description = 'description for newly created repo'
99 115 private = False
116
100 117 response = self.app.post(url('repos'), {'repo_name':repo_name,
101 118 'repo_type':'hg',
102 'description':description,
103 'private':private})
104
119 'clone_uri':'',
120 'repo_group':'',
121 'description':description,
122 'private':private})
123 self.assertTrue('flash' in response.session)
105 124
106 125 #test if we have a message for that repository
107 assert '''created repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about new repo'
126 self.assertTrue('''created repository %s''' % (repo_name) in
127 response.session['flash'][0])
108 128
109 129 #test if the repo was created in the database
110 new_repo = self.sa.query(Repository).filter(Repository.repo_name == repo_name).one()
130 new_repo = self.sa.query(Repository).filter(Repository.repo_name ==
131 repo_name).one()
111 132
112 assert new_repo.repo_name == repo_name, 'wrong name of repo name in db'
113 assert new_repo.description == description, 'wrong description'
133 self.assertEqual(new_repo.repo_name, repo_name)
134 self.assertEqual(new_repo.description, description)
114 135
115 136 #test if repository is visible in the list ?
116 137 response = response.follow()
117 138
118 assert repo_name in response.body, 'missing new repo from the main repos list'
139 self.assertTrue(repo_name in response.body)
119 140
120 141
121 142 response = self.app.delete(url('repo', repo_name=repo_name))
122 143
123 assert '''deleted repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about delete repo'
144 self.assertTrue('''deleted repository %s''' % (repo_name) in
145 response.session['flash'][0])
124 146
125 147 response.follow()
126 148
127 149 #check if repo was deleted from db
128 deleted_repo = self.sa.query(Repository).filter(Repository.repo_name == repo_name).scalar()
150 deleted_repo = self.sa.query(Repository).filter(Repository.repo_name
151 == repo_name).scalar()
152
153 self.assertEqual(deleted_repo, None)
129 154
130 assert deleted_repo is None, 'Deleted repository was found in db'
155
156 def test_delete_repo_with_group(self):
157 #TODO:
158 pass
131 159
132 160
133 161 def test_delete_browser_fakeout(self):
134 response = self.app.post(url('repo', repo_name=HG_REPO), params=dict(_method='delete'))
162 response = self.app.post(url('repo', repo_name=HG_REPO),
163 params=dict(_method='delete'))
135 164
136 165 def test_show(self):
137 166 self.log_user()
138 167 response = self.app.get(url('repo', repo_name=HG_REPO))
139 168
140 169 def test_show_as_xml(self):
141 response = self.app.get(url('formatted_repo', repo_name=HG_REPO, format='xml'))
170 response = self.app.get(url('formatted_repo', repo_name=HG_REPO,
171 format='xml'))
142 172
143 173 def test_edit(self):
144 174 response = self.app.get(url('edit_repo', repo_name=HG_REPO))
145 175
146 176 def test_edit_as_xml(self):
147 response = self.app.get(url('formatted_edit_repo', repo_name=HG_REPO, format='xml'))
177 response = self.app.get(url('formatted_edit_repo', repo_name=HG_REPO,
178 format='xml'))
@@ -1,3 +1,5 b''
1 # -*- coding: utf-8 -*-
2
1 3 from rhodecode.lib.auth import get_crypt_password, check_password
2 4 from rhodecode.model.db import User, RhodeCodeSettings
3 5 from rhodecode.tests import *
@@ -42,7 +44,8 b' class TestAdminSettingsController(TestCo'
42 44 response = self.app.get(url('admin_edit_setting', setting_id=1))
43 45
44 46 def test_edit_as_xml(self):
45 response = self.app.get(url('formatted_admin_edit_setting', setting_id=1, format='xml'))
47 response = self.app.get(url('formatted_admin_edit_setting',
48 setting_id=1, format='xml'))
46 49
47 50
48 51 def test_ga_code_active(self):
@@ -58,11 +61,14 b' class TestAdminSettingsController(TestCo'
58 61 rhodecode_ga_code=new_ga_code
59 62 ))
60 63
61 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
62 assert RhodeCodeSettings.get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
64 self.assertTrue('Updated application settings' in
65 response.session['flash'][0][1])
66 self.assertEqual(RhodeCodeSettings
67 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
63 68
64 69 response = response.follow()
65 assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code in response.body
70 self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
71 in response.body)
66 72
67 73 def test_ga_code_inactive(self):
68 74 self.log_user()
@@ -77,11 +83,14 b' class TestAdminSettingsController(TestCo'
77 83 rhodecode_ga_code=new_ga_code
78 84 ))
79 85
80 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
81 assert RhodeCodeSettings.get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database'
86 self.assertTrue('Updated application settings' in
87 response.session['flash'][0][1])
88 self.assertEqual(RhodeCodeSettings
89 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
82 90
83 91 response = response.follow()
84 assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code not in response.body
92 self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
93 not in response.body)
85 94
86 95
87 96 def test_title_change(self):
@@ -89,27 +98,33 b' class TestAdminSettingsController(TestCo'
89 98 old_title = 'RhodeCode'
90 99 new_title = old_title + '_changed'
91 100 old_realm = 'RhodeCode authentication'
92 response = self.app.post(url('admin_setting', setting_id='global'),
93 params=dict(
94 _method='put',
95 rhodecode_title=new_title,
96 rhodecode_realm=old_realm,
97 rhodecode_ga_code=''
98 ))
101
102 for new_title in ['Changed', 'Żółwik', old_title]:
103 response = self.app.post(url('admin_setting', setting_id='global'),
104 params=dict(
105 _method='put',
106 rhodecode_title=new_title,
107 rhodecode_realm=old_realm,
108 rhodecode_ga_code=''
109 ))
99 110
100 111
101 assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change'
102 assert RhodeCodeSettings.get_app_settings()['rhodecode_title'] == new_title, 'change not in database'
112 self.assertTrue('Updated application settings' in
113 response.session['flash'][0][1])
114 self.assertEqual(RhodeCodeSettings
115 .get_app_settings()['rhodecode_title'],
116 new_title.decode('utf-8'))
103 117
104 response = response.follow()
105 assert """<h1><a href="/">%s</a></h1>""" % new_title in response.body
118 response = response.follow()
119 self.assertTrue("""<h1><a href="/">%s</a></h1>""" % new_title
120 in response.body)
106 121
107 122
108 123 def test_my_account(self):
109 124 self.log_user()
110 125 response = self.app.get(url('admin_settings_my_account'))
111 print response
112 assert 'value="test_admin' in response.body
126
127 self.assertTrue('value="test_admin' in response.body)
113 128
114 129 def test_my_account_update(self):
115 130 self.log_user()
@@ -120,14 +135,14 b' class TestAdminSettingsController(TestCo'
120 135 new_password = 'test123'
121 136
122 137
123 response = self.app.post(url('admin_settings_my_account_update'), params=dict(
124 _method='put',
125 username='test_admin',
126 new_password=new_password,
127 password='',
128 name=new_name,
129 lastname=new_lastname,
130 email=new_email,))
138 response = self.app.post(url('admin_settings_my_account_update'),
139 params=dict(_method='put',
140 username='test_admin',
141 new_password=new_password,
142 password='',
143 name=new_name,
144 lastname=new_lastname,
145 email=new_email,))
131 146 response.follow()
132 147
133 148 assert 'Your account was updated successfully' in response.session['flash'][0][1], 'no flash message about success of change'
@@ -45,11 +45,11 b' class TestLoginController(TestController'
45 45
46 46 def test_login_short_password(self):
47 47 response = self.app.post(url(controller='login', action='index'),
48 {'username':'error',
49 'password':'test'})
50 assert response.status == '200 OK', 'Wrong response from login page'
48 {'username':'test_admin',
49 'password':'as'})
50 self.assertEqual(response.status, '200 OK')
51 51 print response.body
52 assert 'Enter 6 characters or more' in response.body, 'No error password message in response'
52 self.assertTrue('Enter 3 characters or more' in response.body)
53 53
54 54 def test_login_wrong_username_password(self):
55 55 response = self.app.post(url(controller='login', action='index'),
@@ -6,22 +6,38 b' class TestSummaryController(TestControll'
6 6
7 7 def test_index(self):
8 8 self.log_user()
9 response = self.app.get(url(controller='summary', action='index', repo_name=HG_REPO))
9 response = self.app.get(url(controller='summary',
10 action='index', repo_name=HG_REPO))
10 11
11 12 #repo type
12 assert """<img style="margin-bottom:2px" class="icon" title="Mercurial repository" alt="Mercurial repository" src="/images/icons/hgicon.png"/>""" in response.body
13 assert """<img style="margin-bottom:2px" class="icon" title="public repository" alt="public repository" src="/images/icons/lock_open.png"/>""" in response.body
13 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
14 """title="Mercurial repository" alt="Mercurial """
15 """repository" src="/images/icons/hgicon.png"/>"""
16 in response.body)
17 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
18 """title="public repository" alt="public """
19 """repository" src="/images/icons/lock_open.png"/>"""
20 in response.body)
14 21
15 22 #codes stats
23 self._enable_stats()
16 24
17 25
18 self._enable_stats()
19 26 invalidate_cache('get_repo_cached_%s' % HG_REPO)
20 response = self.app.get(url(controller='summary', action='index', repo_name=HG_REPO))
21 assert """var data = {"Python": 42, "Rst": 11, "Bash": 2, "Makefile": 1, "Batch": 1, "Ini": 1, "Css": 1};""" in response.body, 'wrong info about % of codes stats'
27 response = self.app.get(url(controller='summary', action='index',
28 repo_name=HG_REPO))
29
30 self.assertTrue("""var data = {"py": {"count": 42, "desc": """
31 """["Python"]}, "rst": {"count": 11, "desc": """
32 """["Rst"]}, "sh": {"count": 2, "desc": ["Bash"]}, """
33 """"makefile": {"count": 1, "desc": ["Makefile", """
34 """"Makefile"]}, "cfg": {"count": 1, "desc": ["Ini"]},"""
35 """ "css": {"count": 1, "desc": ["Css"]}, "bat": """
36 """{"count": 1, "desc": ["Batch"]}};"""
37 in response.body)
22 38
23 39 # clone url...
24 assert """<input type="text" id="clone_url" readonly="readonly" value="hg clone http://test_admin@localhost:80/%s" size="70"/>""" % HG_REPO in response.body
40 self.assertTrue("""<input type="text" id="clone_url" readonly="readonly" value="hg clone http://test_admin@localhost:80/%s" size="70"/>""" % HG_REPO in response.body)
25 41
26 42
27 43 def _enable_stats(self):
@@ -7,6 +7,7 b''
7 7
8 8 [DEFAULT]
9 9 debug = true
10 pdebug = false
10 11 ################################################################################
11 12 ## Uncomment and replace with the address which should receive ##
12 13 ## any error reports after application crash ##
General Comments 0
You need to be logged in to leave comments. Login now