Mercurial > kallithea
changeset 1366:9c0f5d558789 beta
fixes #200, rewrote the whole caching mechanism to get rid of such problems. Now cached instances are attached
to db repository instance, and then fetched from cache. Also made all current test work.
author | Marcin Kuzminski <marcin@python-works.com> |
---|---|
date | Tue, 07 Jun 2011 17:58:51 +0200 |
parents | cd865113423e |
children | e8afa84ab131 |
files | rhodecode/controllers/admin/repos.py rhodecode/controllers/admin/repos_groups.py rhodecode/controllers/admin/settings.py rhodecode/controllers/home.py rhodecode/controllers/settings.py rhodecode/lib/base.py rhodecode/lib/celerylib/tasks.py rhodecode/lib/helpers.py rhodecode/lib/utils.py rhodecode/model/db.py rhodecode/model/forms.py rhodecode/model/repo.py rhodecode/model/scm.py rhodecode/templates/admin/users/user_edit.html rhodecode/templates/admin/users/user_edit_my_account.html rhodecode/tests/__init__.py rhodecode/tests/functional/test_admin_repos.py rhodecode/tests/functional/test_admin_settings.py rhodecode/tests/functional/test_login.py rhodecode/tests/functional/test_summary.py test.ini |
diffstat | 21 files changed, 398 insertions(+), 238 deletions(-) [+] |
line wrap: on
line diff
--- a/rhodecode/controllers/admin/repos.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/controllers/admin/repos.py Tue Jun 07 17:58:51 2011 +0200 @@ -89,10 +89,8 @@ """ self.__load_defaults() - repo, dbrepo = ScmModel().get(repo_name, retval='repo') - - repo_model = RepoModel() - c.repo_info = repo_model.get_by_repo_name(repo_name) + c.repo_info = db_repo = Repository.by_repo_name(repo_name) + repo = scm_repo = db_repo.scm_instance if c.repo_info is None: h.flash(_('%s repository is not mapped to db perhaps' @@ -153,10 +151,9 @@ """GET /repos: All items in the collection""" # url('repos') - all_repos = [r.repo_name for r in Repository.query().all()] - - cached_repo_list = ScmModel().get_repos(all_repos) - c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort')) + c.repos_list = ScmModel().get_repos(Repository.query() + .order_by(Repository.repo_name) + .all(), sort_key='name_sort') return render('admin/repos/repos.html') @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
--- a/rhodecode/controllers/admin/repos_groups.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/controllers/admin/repos_groups.py Tue Jun 07 17:58:51 2011 +0200 @@ -181,12 +181,14 @@ """GET /repos_groups/id: Show a specific item""" # url('repos_group', id=ID) - c.group = Group.get(id) + gr = c.group = Group.get(id) + if c.group: c.group_repos = c.group.repositories.all() else: return redirect(url('repos_group')) + sortables = ['name', 'description', 'last_change', 'tip', 'owner'] current_sort = request.GET.get('sort', 'name') current_sort_slug = current_sort.replace('-', '') @@ -201,18 +203,12 @@ sort_key = current_sort_slug + '_sort' #overwrite our cached list with current filter - gr_filter = [r.repo_name for r in c.group_repos] + gr_filter = c.group_repos c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter) - if c.sort_by.startswith('-'): - c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key), - reverse=True) - else: - c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key), - reverse=False) + c.repos_list = c.cached_repo_list - c.repo_cnt = len(c.repos_list) - + c.repo_cnt = 0 c.groups = self.sa.query(Group).order_by(Group.group_name)\ .filter(Group.group_parent_id == id).all()
--- a/rhodecode/controllers/admin/settings.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/controllers/admin/settings.py Tue Jun 07 17:58:51 2011 +0200 @@ -258,9 +258,10 @@ # url('admin_settings_my_account') c.user = UserModel().get(self.rhodecode_user.user_id, cache=False) - all_repos = [r.repo_name for r in self.sa.query(Repository)\ + all_repos = self.sa.query(Repository)\ .filter(Repository.user_id == c.user.user_id)\ - .order_by(func.lower(Repository.repo_name)).all()] + .order_by(func.lower(Repository.repo_name)).all() + c.user_repos = ScmModel().get_repos(all_repos) if c.user.username == 'default':
--- a/rhodecode/controllers/home.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/controllers/home.py Tue Jun 07 17:58:51 2011 +0200 @@ -31,7 +31,7 @@ from rhodecode.lib.auth import LoginRequired from rhodecode.lib.base import BaseController, render -from rhodecode.model.db import Group +from rhodecode.model.db import Group, Repository log = logging.getLogger(__name__) @@ -56,16 +56,11 @@ sort_key = current_sort_slug + '_sort' - if c.sort_by.startswith('-'): - c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key), - reverse=True) - else: - c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key), - reverse=False) + + c.repos_list = self.scm_model.get_repos(sort_key=sort_key) c.repo_cnt = len(c.repos_list) - c.groups = Group.query().filter(Group.group_parent_id == None).all() @@ -73,8 +68,9 @@ def repo_switcher(self): if request.is_xhr: - c.repos_list = sorted(c.cached_repo_list, - key=itemgetter('name_sort'), reverse=False) + all_repos = Repository.query().order_by(Repository.repo_name).all() + c.repos_list = self.scm_model.get_repos(all_repos, + sort_key='name_sort') return render('/repo_switcher_list.html') else: return HTTPBadRequest()
--- a/rhodecode/controllers/settings.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/controllers/settings.py Tue Jun 07 17:58:51 2011 +0200 @@ -155,6 +155,7 @@ invalidate_cache('get_repo_cached_%s' % repo_name) h.flash(_('deleted repository %s') % repo_name, category='success') except Exception: + log.error(traceback.format_exc()) h.flash(_('An error occurred during deletion of %s') % repo_name, category='error') @@ -205,4 +206,9 @@ errors=errors.error_dict or {}, prefix_error=False, encoding="UTF-8") + except Exception: + log.error(traceback.format_exc()) + h.flash(_('An error occurred during repository forking %s') % + repo_name, category='error') + return redirect(url('home'))
--- a/rhodecode/lib/base.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/lib/base.py Tue Jun 07 17:58:51 2011 +0200 @@ -12,6 +12,7 @@ from rhodecode.model import meta from rhodecode.model.scm import ScmModel from rhodecode import BACKENDS +from rhodecode.model.db import Repository class BaseController(WSGIController): @@ -26,7 +27,7 @@ self.sa = meta.Session() self.scm_model = ScmModel(self.sa) - c.cached_repo_list = self.scm_model.get_repos() + #c.unread_journal = scm_model.get_unread_journal() def __call__(self, environ, start_response): @@ -62,8 +63,7 @@ super(BaseRepoController, self).__before__() if c.repo_name: - c.rhodecode_repo, dbrepo = self.scm_model.get(c.repo_name, - retval='repo') + c.rhodecode_repo = Repository.by_repo_name(c.repo_name).scm_instance if c.rhodecode_repo is not None: c.repository_followers = \
--- a/rhodecode/lib/celerylib/tasks.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/lib/celerylib/tasks.py Tue Jun 07 17:58:51 2011 +0200 @@ -102,7 +102,6 @@ lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y, ts_max_y) lockkey_path = dn(dn(dn(dn(os.path.abspath(__file__))))) - print jn(lockkey_path, lockkey) log.info('running task with lockkey %s', lockkey) try: lock = l = DaemonLock(jn(lockkey_path, lockkey))
--- a/rhodecode/lib/helpers.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/lib/helpers.py Tue Jun 07 17:58:51 2011 +0200 @@ -372,8 +372,7 @@ repo_name = user_log.repository.repo_name from rhodecode.model.scm import ScmModel - repo, dbrepo = ScmModel().get(repo_name, retval='repo', - invalidation_list=[]) + repo = user_log.repository.scm_instance message = lambda rev: get_changeset_safe(repo, rev).message cs_links = []
--- a/rhodecode/lib/utils.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/lib/utils.py Tue Jun 07 17:58:51 2011 +0200 @@ -472,7 +472,7 @@ shutil.rmtree(index_location) try: - l = DaemonLock(file=jn(dn(dn(index_location)), 'make_index.lock')) + l = DaemonLock(file=jn(dn(index_location), 'make_index.lock')) WhooshIndexingDaemon(index_location=index_location, repo_location=repo_location)\ .run(full_index=full_index)
--- a/rhodecode/model/db.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/model/db.py Tue Jun 07 17:58:51 2011 +0200 @@ -26,13 +26,23 @@ import os import logging import datetime +import traceback from datetime import date from sqlalchemy import * from sqlalchemy.exc import DatabaseError -from sqlalchemy.orm import relationship, backref +from sqlalchemy.orm import relationship, backref, joinedload from sqlalchemy.orm.interfaces import MapperExtension +from beaker.cache import cache_region, region_invalidate + + +from vcs import get_backend +from vcs.utils.helpers import get_scm +from vcs.exceptions import RepositoryError, VCSError +from vcs.utils.lazy import LazyProperty +from vcs.nodes import FileNode + from rhodecode.lib import str2bool from rhodecode.model.meta import Base, Session from rhodecode.model.caching_query import FromCache @@ -150,6 +160,7 @@ return self.admin def __repr__(self): + return 'ahmmm' return "<%s('id:%s:%s')>" % (self.__class__.__name__, self.user_id, self.username) @@ -266,8 +277,13 @@ @classmethod def by_repo_name(cls, repo_name): - return Session.query(cls).filter(cls.repo_name == repo_name).one() + q = Session.query(cls).filter(cls.repo_name == repo_name) + q = q.options(joinedload(Repository.fork))\ + .options(joinedload(Repository.user))\ + .options(joinedload(Repository.group))\ + + return q.one() @classmethod def get_repo_forks(cls, repo_id): @@ -298,6 +314,127 @@ def groups_and_repo(self): return self.groups_with_parents, self.just_name + @LazyProperty + def repo_path(self): + """ + Returns base full path for that repository means where it actually + exists on a filesystem + """ + + q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one() + return q.ui_value + + @property + def repo_full_path(self): + p = [self.repo_path] + # we need to split the name by / since this is how we store the + # names in the database, but that eventually needs to be converted + # into a valid system path + p += self.repo_name.split('/') + return os.path.join(*p) + + @property + def _ui(self): + """ + Creates an db based ui object for this repository + """ + from mercurial import ui + from mercurial import config + baseui = ui.ui() + + #clean the baseui object + baseui._ocfg = config.config() + baseui._ucfg = config.config() + baseui._tcfg = config.config() + + + ret = Session.query(RhodeCodeUi)\ + .options(FromCache("sql_cache_short", + "repository_repo_ui")).all() + + hg_ui = ret + for ui_ in hg_ui: + if ui_.ui_active: + log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, + ui_.ui_key, ui_.ui_value) + baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value) + + return baseui + + #========================================================================== + # SCM CACHE INSTANCE + #========================================================================== + + @property + def invalidate(self): + """ + Returns Invalidation object if this repo should be invalidated + None otherwise. `cache_active = False` means that this cache + state is not valid and needs to be invalidated + """ + return Session.query(CacheInvalidation)\ + .filter(CacheInvalidation.cache_key == self.repo_name)\ + .filter(CacheInvalidation.cache_active == False)\ + .scalar() + + @property + def set_invalidate(self): + """ + set a cache for invalidation for this instance + """ + inv = Session.query(CacheInvalidation)\ + .filter(CacheInvalidation.cache_key == self.repo_name)\ + .scalar() + + if inv is None: + inv = CacheInvalidation(self.repo_name) + inv.cache_active = True + Session.add(inv) + Session.commit() + + @property + def scm_instance(self): + return self.__get_instance(self.repo_name) + + @property + def scm_instance_cached(self): + @cache_region('long_term') + def _c(repo_name): + return self.__get_instance(repo_name) + + inv = self.invalidate + if inv: + region_invalidate(_c, None, self.repo_name) + #update our cache + inv.cache_key.cache_active = True + Session.add(inv) + Session.commit() + + return _c(self.repo_name) + + def __get_instance(self, repo_name): + try: + alias = get_scm(self.repo_full_path)[0] + log.debug('Creating instance of %s repository', alias) + backend = get_backend(alias) + except VCSError: + log.error(traceback.format_exc()) + log.error('Perhaps this repository is in db and not in ' + 'filesystem run rescan repositories with ' + '"destroy old data " option from admin panel') + return + + if alias == 'hg': + repo = backend(self.repo_full_path, create=False, + baseui=self._ui) + #skip hidden web repository + if repo._get_hidden(): + return + else: + repo = backend(self.repo_full_path, create=False) + + return repo + class Group(Base): __tablename__ = 'groups'
--- a/rhodecode/model/forms.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/model/forms.py Tue Jun 07 17:58:51 2011 +0200 @@ -280,6 +280,13 @@ return _ValidRepoName +def ValidForkName(): + class _ValidForkName(formencode.validators.FancyValidator): + def to_python(self, value, state): + return value + return _ValidForkName + + def SlugifyName(): class _SlugifyName(formencode.validators.FancyValidator): @@ -326,6 +333,7 @@ if old_data['repo_type'] != value: raise formencode.Invalid(_('Fork have to be the same ' 'type as original'), value, state) + return value return _ValidForkType @@ -583,6 +591,9 @@ description = UnicodeString(strip=True, min=1, not_empty=True) private = StringBoolean(if_missing=False) repo_type = All(ValidForkType(old_data), OneOf(supported_backends)) + + chained_validators = [ValidForkName()] + return _RepoForkForm def RepoSettingsForm(edit=False, old_data={}):
--- a/rhodecode/model/repo.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/model/repo.py Tue Jun 07 17:58:51 2011 +0200 @@ -70,28 +70,6 @@ "get_repo_%s" % repo_name)) return repo.scalar() - def get_full(self, repo_name, cache=False, invalidate=False): - repo = self.sa.query(Repository)\ - .options(joinedload(Repository.fork))\ - .options(joinedload(Repository.user))\ - .options(joinedload(Repository.group))\ - .filter(Repository.repo_name == repo_name)\ - - if cache: - repo = repo.options(FromCache("sql_cache_long", - "get_repo_full_%s" % repo_name)) - if invalidate and cache: - repo.invalidate() - - ret = repo.scalar() - - #make transient for sake of errors - make_transient(ret) - for k in ['fork', 'user', 'group']: - attr = getattr(ret, k, False) - if attr: - make_transient(attr) - return ret def get_users_js(self): @@ -193,12 +171,13 @@ raise def create(self, form_data, cur_user, just_db=False, fork=False): + try: if fork: #force str since hg doesn't go with unicode repo_name = str(form_data['fork_name']) org_name = str(form_data['repo_name']) - org_full_name = str(form_data['repo_name_full']) + org_full_name = org_name#str(form_data['fork_name_full']) else: org_name = repo_name = str(form_data['repo_name']) @@ -208,7 +187,10 @@ new_repo.enable_statistics = False for k, v in form_data.items(): if k == 'repo_name': - v = repo_name_full + if fork: + v = repo_name + else: + v = repo_name_full if k == 'repo_group': k = 'group_id' @@ -216,7 +198,7 @@ if fork: parent_repo = self.sa.query(Repository)\ - .filter(Repository.repo_name == org_full_name).scalar() + .filter(Repository.repo_name == org_full_name).one() new_repo.fork = parent_repo new_repo.user_id = cur_user.user_id
--- a/rhodecode/model/scm.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/model/scm.py Tue Jun 07 17:58:51 2011 +0200 @@ -70,6 +70,75 @@ def __repr__(self): return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id) +class CachedRepoList(object): + + def __init__(self, db_repo_list, invalidation_list, repos_path, + order_by=None): + self.db_repo_list = db_repo_list + self.invalidation_list = invalidation_list + self.repos_path = repos_path + self.order_by = order_by + self.reversed = (order_by or '').startswith('-') + + def __len__(self): + return len(self.db_repo_list) + + def __repr__(self): + return '<%s (%s)>' % (self.__class__.__name__, self.__len__()) + + def __iter__(self): + for db_repo in self.db_repo_list: + dbr = db_repo + + # invalidate the repo cache if needed before getting the + # scm instance + + scm_invalidate = False + if self.invalidation_list is not None: + scm_invalidate = dbr.repo_name in self.invalidation_list + + if scm_invalidate: + log.info('invalidating cache for repository %s', + dbr.repo_name) + db_repo.set_invalidate + + scmr = db_repo.scm_instance_cached + + #check permission at this level + if not HasRepoPermissionAny('repository.read', + 'repository.write', + 'repository.admin')(dbr.repo_name, + 'get repo check'): + continue + + + + + + last_change = scmr.last_change + tip = h.get_changeset_safe(scmr, 'tip') + + tmp_d = {} + tmp_d['name'] = dbr.repo_name + tmp_d['name_sort'] = tmp_d['name'].lower() + tmp_d['description'] = dbr.description + tmp_d['description_sort'] = tmp_d['description'] + tmp_d['last_change'] = last_change + tmp_d['last_change_sort'] = time.mktime(last_change \ + .timetuple()) + tmp_d['tip'] = tip.raw_id + tmp_d['tip_sort'] = tip.revision + tmp_d['rev'] = tip.revision + tmp_d['contact'] = dbr.user.full_contact + tmp_d['contact_sort'] = tmp_d['contact'] + tmp_d['owner_sort'] = tmp_d['contact'] + tmp_d['repo_archives'] = list(scmr._get_archives()) + tmp_d['last_msg'] = tip.message + tmp_d['repo'] = scmr + tmp_d['dbrepo'] = dbr.get_dict() + tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \ + else {} + yield tmp_d class ScmModel(BaseModel): """Generic Scm Model @@ -118,7 +187,7 @@ return repos_list - def get_repos(self, all_repos=None): + def get_repos(self, all_repos=None, sort_key=None): """ Get all repos from db and for each repo create it's backend instance and fill that backed with information from database @@ -127,120 +196,21 @@ give specific repositories list, good for filtering """ if all_repos is None: - repos = self.sa.query(Repository)\ + all_repos = self.sa.query(Repository)\ .filter(Repository.group_id == None)\ .order_by(Repository.repo_name).all() - all_repos = [r.repo_name for r in repos] #get the repositories that should be invalidated invalidation_list = [str(x.cache_key) for x in \ self.sa.query(CacheInvalidation.cache_key)\ .filter(CacheInvalidation.cache_active == False)\ .all()] - for r_name in all_repos: - r_dbr = self.get(r_name, invalidation_list) - if r_dbr is not None: - repo, dbrepo = r_dbr - - if repo is None or dbrepo is None: - log.error('Repository "%s" looks somehow corrupted ' - 'fs-repo:%s,db-repo:%s both values should be ' - 'present', r_name, repo, dbrepo) - continue - last_change = repo.last_change - tip = h.get_changeset_safe(repo, 'tip') - - tmp_d = {} - tmp_d['name'] = dbrepo.repo_name - tmp_d['name_sort'] = tmp_d['name'].lower() - tmp_d['description'] = dbrepo.description - tmp_d['description_sort'] = tmp_d['description'] - tmp_d['last_change'] = last_change - tmp_d['last_change_sort'] = time.mktime(last_change \ - .timetuple()) - tmp_d['tip'] = tip.raw_id - tmp_d['tip_sort'] = tip.revision - tmp_d['rev'] = tip.revision - tmp_d['contact'] = dbrepo.user.full_contact - tmp_d['contact_sort'] = tmp_d['contact'] - tmp_d['owner_sort'] = tmp_d['contact'] - tmp_d['repo_archives'] = list(repo._get_archives()) - tmp_d['last_msg'] = tip.message - tmp_d['repo'] = repo - tmp_d['dbrepo'] = dbrepo.get_dict() - tmp_d['dbrepo_fork'] = dbrepo.fork.get_dict() if dbrepo.fork \ - else {} - yield tmp_d - - def get(self, repo_name, invalidation_list=None, retval='all'): - """Returns a tuple of Repository,DbRepository, - Get's repository from given name, creates BackendInstance and - propagates it's data from database with all additional information - - :param repo_name: - :param invalidation_list: if a invalidation list is given the get - method should not manually check if this repository needs - invalidation and just invalidate the repositories in list - :param retval: string specifing what to return one of 'repo','dbrepo', - 'all'if repo or dbrepo is given it'll just lazy load chosen type - and return None as the second - """ - if not HasRepoPermissionAny('repository.read', 'repository.write', - 'repository.admin')(repo_name, 'get repo check'): - return - #====================================================================== - # CACHE FUNCTION - #====================================================================== - @cache_region('long_term') - def _get_repo(repo_name): - - repo_path = os.path.join(self.repos_path, repo_name) - - try: - alias = get_scm(repo_path)[0] - log.debug('Creating instance of %s repository', alias) - backend = get_backend(alias) - except VCSError: - log.error(traceback.format_exc()) - log.error('Perhaps this repository is in db and not in ' - 'filesystem run rescan repositories with ' - '"destroy old data " option from admin panel') - return + repo_iter = CachedRepoList(all_repos, invalidation_list, + repos_path=self.repos_path, + order_by=sort_key) - if alias == 'hg': - repo = backend(repo_path, create=False, baseui=make_ui('db')) - #skip hidden web repository - if repo._get_hidden(): - return - else: - repo = backend(repo_path, create=False) - - return repo - - pre_invalidate = True - dbinvalidate = False - - if invalidation_list is not None: - pre_invalidate = repo_name in invalidation_list - - if pre_invalidate: - #this returns object to invalidate - invalidate = self._should_invalidate(repo_name) - if invalidate: - log.info('invalidating cache for repository %s', repo_name) - region_invalidate(_get_repo, None, repo_name) - self._mark_invalidated(invalidate) - dbinvalidate = True - - r, dbr = None, None - if retval == 'repo' or 'all': - r = _get_repo(repo_name) - if retval == 'dbrepo' or 'all': - dbr = RepoModel().get_full(repo_name, cache=True, - invalidate=dbinvalidate) - - return r, dbr + return repo_iter def mark_for_invalidation(self, repo_name): """Puts cache invalidation task into db for
--- a/rhodecode/templates/admin/users/user_edit.html Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/templates/admin/users/user_edit.html Tue Jun 07 17:58:51 2011 +0200 @@ -65,7 +65,7 @@ <label for="new_password">${_('New password')}:</label> </div> <div class="input"> - ${h.password('new_password',class_='medium')} + ${h.password('new_password',class_='medium',autocomplete="off")} </div> </div>
--- a/rhodecode/templates/admin/users/user_edit_my_account.html Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/templates/admin/users/user_edit_my_account.html Tue Jun 07 17:58:51 2011 +0200 @@ -54,7 +54,7 @@ <label for="new_password">${_('New password')}:</label> </div> <div class="input"> - ${h.password('new_password',class_="medium")} + ${h.password('new_password',class_="medium",autocomplete="off")} </div> </div>
--- a/rhodecode/tests/__init__.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/tests/__init__.py Tue Jun 07 17:58:51 2011 +0200 @@ -7,6 +7,9 @@ This module initializes the application via ``websetup`` (`paster setup-app`) and provides the base testing objects. """ +import os +from os.path import join as jn + from unittest import TestCase from paste.deploy import loadapp @@ -14,7 +17,7 @@ from pylons import config, url from routes.util import URLGenerator from webtest import TestApp -import os + from rhodecode.model import meta import logging @@ -35,7 +38,7 @@ environ = {} #SOME GLOBALS FOR TESTS -TESTS_TMP_PATH = '/tmp' +TESTS_TMP_PATH = jn('/', 'tmp') HG_REPO = 'vcs_test_hg' GIT_REPO = 'vcs_test_git' @@ -64,8 +67,8 @@ 'password':password}) if 'invalid user name' in response.body: - assert False, 'could not login using %s %s' % (username, password) + self.fail('could not login using %s %s' % (username, password)) - assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status - assert response.session['rhodecode_user'].username == username, 'wrong logged in user got %s expected %s' % (response.session['rhodecode_user'].username, username) + self.assertEqual(response.status, '302 Found') + self.assertEqual(response.session['rhodecode_user'].username, username) return response.follow()
--- a/rhodecode/tests/functional/test_admin_repos.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/tests/functional/test_admin_repos.py Tue Jun 07 17:58:51 2011 +0200 @@ -6,6 +6,11 @@ class TestAdminReposController(TestController): + + def __make_repo(self): + pass + + def test_index(self): self.log_user() response = self.app.get(url('repos')) @@ -21,31 +26,39 @@ private = False response = self.app.post(url('repos'), {'repo_name':repo_name, 'repo_type':'hg', + 'clone_uri':'', + 'repo_group':'', 'description':description, 'private':private}) - + self.assertTrue('flash' in response.session) #test if we have a message for that repository - assert '''created repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about new repo' + self.assertTrue('''created repository %s''' % (repo_name) in + response.session['flash'][0]) - #test if the fork was created in the database - new_repo = self.sa.query(Repository).filter(Repository.repo_name == repo_name).one() + #test if the repo was created in the database + new_repo = self.sa.query(Repository).filter(Repository.repo_name == + repo_name).one() - assert new_repo.repo_name == repo_name, 'wrong name of repo name in db' - assert new_repo.description == description, 'wrong description' + self.assertEqual(new_repo.repo_name, repo_name) + self.assertEqual(new_repo.description, description) #test if repository is visible in the list ? response = response.follow() - assert repo_name in response.body, 'missing new repo from the main repos list' + self.assertTrue(repo_name in response.body) #test if repository was created on filesystem try: vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name)) except: - assert False , 'no repo in filesystem' + self.fail('no repo in filesystem') + + def test_create_hg_in_group(self): + #TODO: write test ! + pass def test_create_git(self): return @@ -55,6 +68,8 @@ private = False response = self.app.post(url('repos'), {'repo_name':repo_name, 'repo_type':'git', + 'clone_uri':'', + 'repo_group':'', 'description':description, 'private':private}) @@ -90,58 +105,74 @@ response = self.app.put(url('repo', repo_name=HG_REPO)) def test_update_browser_fakeout(self): - response = self.app.post(url('repo', repo_name=HG_REPO), params=dict(_method='put')) + response = self.app.post(url('repo', repo_name=HG_REPO), + params=dict(_method='put')) def test_delete(self): self.log_user() repo_name = 'vcs_test_new_to_delete' description = 'description for newly created repo' private = False + response = self.app.post(url('repos'), {'repo_name':repo_name, 'repo_type':'hg', - 'description':description, - 'private':private}) - + 'clone_uri':'', + 'repo_group':'', + 'description':description, + 'private':private}) + self.assertTrue('flash' in response.session) #test if we have a message for that repository - assert '''created repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about new repo' + self.assertTrue('''created repository %s''' % (repo_name) in + response.session['flash'][0]) #test if the repo was created in the database - new_repo = self.sa.query(Repository).filter(Repository.repo_name == repo_name).one() + new_repo = self.sa.query(Repository).filter(Repository.repo_name == + repo_name).one() - assert new_repo.repo_name == repo_name, 'wrong name of repo name in db' - assert new_repo.description == description, 'wrong description' + self.assertEqual(new_repo.repo_name, repo_name) + self.assertEqual(new_repo.description, description) #test if repository is visible in the list ? response = response.follow() - assert repo_name in response.body, 'missing new repo from the main repos list' + self.assertTrue(repo_name in response.body) response = self.app.delete(url('repo', repo_name=repo_name)) - assert '''deleted repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about delete repo' + self.assertTrue('''deleted repository %s''' % (repo_name) in + response.session['flash'][0]) response.follow() #check if repo was deleted from db - deleted_repo = self.sa.query(Repository).filter(Repository.repo_name == repo_name).scalar() + deleted_repo = self.sa.query(Repository).filter(Repository.repo_name + == repo_name).scalar() + + self.assertEqual(deleted_repo, None) - assert deleted_repo is None, 'Deleted repository was found in db' + + def test_delete_repo_with_group(self): + #TODO: + pass def test_delete_browser_fakeout(self): - response = self.app.post(url('repo', repo_name=HG_REPO), params=dict(_method='delete')) + response = self.app.post(url('repo', repo_name=HG_REPO), + params=dict(_method='delete')) def test_show(self): self.log_user() response = self.app.get(url('repo', repo_name=HG_REPO)) def test_show_as_xml(self): - response = self.app.get(url('formatted_repo', repo_name=HG_REPO, format='xml')) + response = self.app.get(url('formatted_repo', repo_name=HG_REPO, + format='xml')) def test_edit(self): response = self.app.get(url('edit_repo', repo_name=HG_REPO)) def test_edit_as_xml(self): - response = self.app.get(url('formatted_edit_repo', repo_name=HG_REPO, format='xml')) + response = self.app.get(url('formatted_edit_repo', repo_name=HG_REPO, + format='xml'))
--- a/rhodecode/tests/functional/test_admin_settings.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/tests/functional/test_admin_settings.py Tue Jun 07 17:58:51 2011 +0200 @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + from rhodecode.lib.auth import get_crypt_password, check_password from rhodecode.model.db import User, RhodeCodeSettings from rhodecode.tests import * @@ -42,7 +44,8 @@ response = self.app.get(url('admin_edit_setting', setting_id=1)) def test_edit_as_xml(self): - response = self.app.get(url('formatted_admin_edit_setting', setting_id=1, format='xml')) + response = self.app.get(url('formatted_admin_edit_setting', + setting_id=1, format='xml')) def test_ga_code_active(self): @@ -58,11 +61,14 @@ rhodecode_ga_code=new_ga_code )) - assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change' - assert RhodeCodeSettings.get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database' + self.assertTrue('Updated application settings' in + response.session['flash'][0][1]) + self.assertEqual(RhodeCodeSettings + .get_app_settings()['rhodecode_ga_code'], new_ga_code) response = response.follow() - assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code in response.body + self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code + in response.body) def test_ga_code_inactive(self): self.log_user() @@ -77,11 +83,14 @@ rhodecode_ga_code=new_ga_code )) - assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change' - assert RhodeCodeSettings.get_app_settings()['rhodecode_ga_code'] == new_ga_code, 'change not in database' + self.assertTrue('Updated application settings' in + response.session['flash'][0][1]) + self.assertEqual(RhodeCodeSettings + .get_app_settings()['rhodecode_ga_code'], new_ga_code) response = response.follow() - assert """_gaq.push(['_setAccount', '%s']);""" % new_ga_code not in response.body + self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code + not in response.body) def test_title_change(self): @@ -89,27 +98,33 @@ old_title = 'RhodeCode' new_title = old_title + '_changed' old_realm = 'RhodeCode authentication' - response = self.app.post(url('admin_setting', setting_id='global'), - params=dict( - _method='put', - rhodecode_title=new_title, - rhodecode_realm=old_realm, - rhodecode_ga_code='' - )) + + for new_title in ['Changed', 'Żółwik', old_title]: + response = self.app.post(url('admin_setting', setting_id='global'), + params=dict( + _method='put', + rhodecode_title=new_title, + rhodecode_realm=old_realm, + rhodecode_ga_code='' + )) - assert 'Updated application settings' in response.session['flash'][0][1], 'no flash message about success of change' - assert RhodeCodeSettings.get_app_settings()['rhodecode_title'] == new_title, 'change not in database' + self.assertTrue('Updated application settings' in + response.session['flash'][0][1]) + self.assertEqual(RhodeCodeSettings + .get_app_settings()['rhodecode_title'], + new_title.decode('utf-8')) - response = response.follow() - assert """<h1><a href="/">%s</a></h1>""" % new_title in response.body + response = response.follow() + self.assertTrue("""<h1><a href="/">%s</a></h1>""" % new_title + in response.body) def test_my_account(self): self.log_user() response = self.app.get(url('admin_settings_my_account')) - print response - assert 'value="test_admin' in response.body + + self.assertTrue('value="test_admin' in response.body) def test_my_account_update(self): self.log_user() @@ -120,14 +135,14 @@ new_password = 'test123' - response = self.app.post(url('admin_settings_my_account_update'), params=dict( - _method='put', - username='test_admin', - new_password=new_password, - password='', - name=new_name, - lastname=new_lastname, - email=new_email,)) + response = self.app.post(url('admin_settings_my_account_update'), + params=dict(_method='put', + username='test_admin', + new_password=new_password, + password='', + name=new_name, + lastname=new_lastname, + email=new_email,)) response.follow() assert 'Your account was updated successfully' in response.session['flash'][0][1], 'no flash message about success of change'
--- a/rhodecode/tests/functional/test_login.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/tests/functional/test_login.py Tue Jun 07 17:58:51 2011 +0200 @@ -45,11 +45,11 @@ def test_login_short_password(self): response = self.app.post(url(controller='login', action='index'), - {'username':'error', - 'password':'test'}) - assert response.status == '200 OK', 'Wrong response from login page' + {'username':'test_admin', + 'password':'as'}) + self.assertEqual(response.status, '200 OK') print response.body - assert 'Enter 6 characters or more' in response.body, 'No error password message in response' + self.assertTrue('Enter 3 characters or more' in response.body) def test_login_wrong_username_password(self): response = self.app.post(url(controller='login', action='index'),
--- a/rhodecode/tests/functional/test_summary.py Mon Jun 06 17:30:34 2011 +0200 +++ b/rhodecode/tests/functional/test_summary.py Tue Jun 07 17:58:51 2011 +0200 @@ -6,22 +6,38 @@ def test_index(self): self.log_user() - response = self.app.get(url(controller='summary', action='index', repo_name=HG_REPO)) + response = self.app.get(url(controller='summary', + action='index', repo_name=HG_REPO)) #repo type - assert """<img style="margin-bottom:2px" class="icon" title="Mercurial repository" alt="Mercurial repository" src="/images/icons/hgicon.png"/>""" in response.body - assert """<img style="margin-bottom:2px" class="icon" title="public repository" alt="public repository" src="/images/icons/lock_open.png"/>""" in response.body + self.assertTrue("""<img style="margin-bottom:2px" class="icon" """ + """title="Mercurial repository" alt="Mercurial """ + """repository" src="/images/icons/hgicon.png"/>""" + in response.body) + self.assertTrue("""<img style="margin-bottom:2px" class="icon" """ + """title="public repository" alt="public """ + """repository" src="/images/icons/lock_open.png"/>""" + in response.body) #codes stats + self._enable_stats() - self._enable_stats() invalidate_cache('get_repo_cached_%s' % HG_REPO) - response = self.app.get(url(controller='summary', action='index', repo_name=HG_REPO)) - 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' + response = self.app.get(url(controller='summary', action='index', + repo_name=HG_REPO)) + + self.assertTrue("""var data = {"py": {"count": 42, "desc": """ + """["Python"]}, "rst": {"count": 11, "desc": """ + """["Rst"]}, "sh": {"count": 2, "desc": ["Bash"]}, """ + """"makefile": {"count": 1, "desc": ["Makefile", """ + """"Makefile"]}, "cfg": {"count": 1, "desc": ["Ini"]},""" + """ "css": {"count": 1, "desc": ["Css"]}, "bat": """ + """{"count": 1, "desc": ["Batch"]}};""" + in response.body) # clone url... - 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 + 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) def _enable_stats(self):
--- a/test.ini Mon Jun 06 17:30:34 2011 +0200 +++ b/test.ini Tue Jun 07 17:58:51 2011 +0200 @@ -7,6 +7,7 @@ [DEFAULT] debug = true +pdebug = false ################################################################################ ## Uncomment and replace with the address which should receive ## ## any error reports after application crash ##