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