##// END OF EJS Templates
integrations: rewrote usage of pylons components inside integrations....
marcink -
r1990:32399c6a default
parent child Browse files
Show More
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -1,226 +1,230 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2017 RhodeCode GmbH
3 # Copyright (C) 2012-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from rhodecode.apps._base import ADMIN_PREFIX, add_route_requirements
23 from rhodecode.apps._base import ADMIN_PREFIX, add_route_requirements
24 from rhodecode.lib.utils2 import safe_int
24 from rhodecode.model.db import Repository, Integration, RepoGroup
25 from rhodecode.model.db import Repository, Integration, RepoGroup
25 from rhodecode.integrations import integration_type_registry
26 from rhodecode.integrations import integration_type_registry
26
27
27 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
28
29
29
30
30 def includeme(config):
31 def includeme(config):
31
32
32 # global integrations
33 # global integrations
33
34 config.add_route('global_integrations_new',
34 config.add_route('global_integrations_new',
35 ADMIN_PREFIX + '/integrations/new')
35 ADMIN_PREFIX + '/integrations/new')
36 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
36 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
37 attr='new_integration',
37 attr='new_integration',
38 renderer='rhodecode:templates/admin/integrations/new.mako',
38 renderer='rhodecode:templates/admin/integrations/new.mako',
39 request_method='GET',
39 request_method='GET',
40 route_name='global_integrations_new')
40 route_name='global_integrations_new')
41
41
42 config.add_route('global_integrations_home',
42 config.add_route('global_integrations_home',
43 ADMIN_PREFIX + '/integrations')
43 ADMIN_PREFIX + '/integrations')
44 config.add_route('global_integrations_list',
44 config.add_route('global_integrations_list',
45 ADMIN_PREFIX + '/integrations/{integration}')
45 ADMIN_PREFIX + '/integrations/{integration}')
46 for route_name in ['global_integrations_home', 'global_integrations_list']:
46 for route_name in ['global_integrations_home', 'global_integrations_list']:
47 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
47 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
48 attr='index',
48 attr='integration_list',
49 renderer='rhodecode:templates/admin/integrations/list.mako',
49 renderer='rhodecode:templates/admin/integrations/list.mako',
50 request_method='GET',
50 request_method='GET',
51 route_name=route_name)
51 route_name=route_name)
52
52
53 config.add_route('global_integrations_create',
53 config.add_route('global_integrations_create',
54 ADMIN_PREFIX + '/integrations/{integration}/new',
54 ADMIN_PREFIX + '/integrations/{integration}/new',
55 custom_predicates=(valid_integration,))
55 custom_predicates=(valid_integration,))
56 config.add_route('global_integrations_edit',
56 config.add_route('global_integrations_edit',
57 ADMIN_PREFIX + '/integrations/{integration}/{integration_id}',
57 ADMIN_PREFIX + '/integrations/{integration}/{integration_id}',
58 custom_predicates=(valid_integration,))
58 custom_predicates=(valid_integration,))
59
59
60
61 for route_name in ['global_integrations_create', 'global_integrations_edit']:
60 for route_name in ['global_integrations_create', 'global_integrations_edit']:
62 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
61 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
63 attr='settings_get',
62 attr='settings_get',
64 renderer='rhodecode:templates/admin/integrations/form.mako',
63 renderer='rhodecode:templates/admin/integrations/form.mako',
65 request_method='GET',
64 request_method='GET',
66 route_name=route_name)
65 route_name=route_name)
67 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
66 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
68 attr='settings_post',
67 attr='settings_post',
69 renderer='rhodecode:templates/admin/integrations/form.mako',
68 renderer='rhodecode:templates/admin/integrations/form.mako',
70 request_method='POST',
69 request_method='POST',
71 route_name=route_name)
70 route_name=route_name)
72
71
73
74 # repo group integrations
72 # repo group integrations
75 config.add_route('repo_group_integrations_home',
73 config.add_route('repo_group_integrations_home',
76 add_route_requirements(
74 add_route_requirements('/{repo_group_name}/settings/integrations'),
77 '{repo_group_name}/settings/integrations',
75 repo_group_route=True)
78 ),
76
79 custom_predicates=(valid_repo_group,)
77 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
80 )
78 attr='integration_list',
81 config.add_route('repo_group_integrations_list',
79 renderer='rhodecode:templates/admin/integrations/list.mako',
82 add_route_requirements(
80 request_method='GET',
83 '{repo_group_name}/settings/integrations/{integration}',
81 route_name='repo_group_integrations_home')
84 ),
85 custom_predicates=(valid_repo_group, valid_integration))
86 for route_name in ['repo_group_integrations_home', 'repo_group_integrations_list']:
87 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
88 attr='index',
89 renderer='rhodecode:templates/admin/integrations/list.mako',
90 request_method='GET',
91 route_name=route_name)
92
82
93 config.add_route('repo_group_integrations_new',
83 config.add_route('repo_group_integrations_new',
94 add_route_requirements(
84 add_route_requirements('/{repo_group_name}/settings/integrations/new'),
95 '{repo_group_name}/settings/integrations/new',
85 repo_group_route=True)
96 ),
97 custom_predicates=(valid_repo_group,))
98 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
86 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
99 attr='new_integration',
87 attr='new_integration',
100 renderer='rhodecode:templates/admin/integrations/new.mako',
88 renderer='rhodecode:templates/admin/integrations/new.mako',
101 request_method='GET',
89 request_method='GET',
102 route_name='repo_group_integrations_new')
90 route_name='repo_group_integrations_new')
103
91
92 config.add_route('repo_group_integrations_list',
93 add_route_requirements('/{repo_group_name}/settings/integrations/{integration}'),
94 repo_group_route=True,
95 custom_predicates=(valid_integration,))
96 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
97 attr='integration_list',
98 renderer='rhodecode:templates/admin/integrations/list.mako',
99 request_method='GET',
100 route_name='repo_group_integrations_list')
101
104 config.add_route('repo_group_integrations_create',
102 config.add_route('repo_group_integrations_create',
105 add_route_requirements(
103 add_route_requirements('/{repo_group_name}/settings/integrations/{integration}/new'),
106 '{repo_group_name}/settings/integrations/{integration}/new',
104 repo_group_route=True,
107 ),
105 custom_predicates=(valid_integration,))
108 custom_predicates=(valid_repo_group, valid_integration))
106 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
107 attr='settings_get',
108 renderer='rhodecode:templates/admin/integrations/form.mako',
109 request_method='GET',
110 route_name='repo_group_integrations_create')
111 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
112 attr='settings_post',
113 renderer='rhodecode:templates/admin/integrations/form.mako',
114 request_method='POST',
115 route_name='repo_group_integrations_create')
116
109 config.add_route('repo_group_integrations_edit',
117 config.add_route('repo_group_integrations_edit',
110 add_route_requirements(
118 add_route_requirements('/{repo_group_name}/settings/integrations/{integration}/{integration_id}'),
111 '{repo_group_name}/settings/integrations/{integration}/{integration_id}',
119 repo_group_route=True,
112 ),
120 custom_predicates=(valid_integration,))
113 custom_predicates=(valid_repo_group, valid_integration))
114 for route_name in ['repo_group_integrations_edit', 'repo_group_integrations_create']:
115 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
116 attr='settings_get',
117 renderer='rhodecode:templates/admin/integrations/form.mako',
118 request_method='GET',
119 route_name=route_name)
120 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
121 attr='settings_post',
122 renderer='rhodecode:templates/admin/integrations/form.mako',
123 request_method='POST',
124 route_name=route_name)
125
121
122 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
123 attr='settings_get',
124 renderer='rhodecode:templates/admin/integrations/form.mako',
125 request_method='GET',
126 route_name='repo_group_integrations_edit')
127 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
128 attr='settings_post',
129 renderer='rhodecode:templates/admin/integrations/form.mako',
130 request_method='POST',
131 route_name='repo_group_integrations_edit')
126
132
127 # repo integrations
133 # repo integrations
128 config.add_route('repo_integrations_home',
134 config.add_route('repo_integrations_home',
129 add_route_requirements(
135 add_route_requirements('/{repo_name}/settings/integrations'),
130 '{repo_name}/settings/integrations',
136 repo_route=True)
131 ),
137 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
132 custom_predicates=(valid_repo,))
138 attr='integration_list',
133 config.add_route('repo_integrations_list',
139 request_method='GET',
134 add_route_requirements(
140 renderer='rhodecode:templates/admin/integrations/list.mako',
135 '{repo_name}/settings/integrations/{integration}',
141 route_name='repo_integrations_home')
136 ),
137 custom_predicates=(valid_repo, valid_integration))
138 for route_name in ['repo_integrations_home', 'repo_integrations_list']:
139 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
140 attr='index',
141 request_method='GET',
142 renderer='rhodecode:templates/admin/integrations/list.mako',
143 route_name=route_name)
144
142
145 config.add_route('repo_integrations_new',
143 config.add_route('repo_integrations_new',
146 add_route_requirements(
144 add_route_requirements('/{repo_name}/settings/integrations/new'),
147 '{repo_name}/settings/integrations/new',
145 repo_route=True)
148 ),
149 custom_predicates=(valid_repo,))
150 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
146 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
151 attr='new_integration',
147 attr='new_integration',
152 renderer='rhodecode:templates/admin/integrations/new.mako',
148 renderer='rhodecode:templates/admin/integrations/new.mako',
153 request_method='GET',
149 request_method='GET',
154 route_name='repo_integrations_new')
150 route_name='repo_integrations_new')
155
151
152 config.add_route('repo_integrations_list',
153 add_route_requirements('/{repo_name}/settings/integrations/{integration}'),
154 repo_route=True,
155 custom_predicates=(valid_integration,))
156 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
157 attr='integration_list',
158 request_method='GET',
159 renderer='rhodecode:templates/admin/integrations/list.mako',
160 route_name='repo_integrations_list')
161
156 config.add_route('repo_integrations_create',
162 config.add_route('repo_integrations_create',
157 add_route_requirements(
163 add_route_requirements('/{repo_name}/settings/integrations/{integration}/new'),
158 '{repo_name}/settings/integrations/{integration}/new',
164 repo_route=True,
159 ),
165 custom_predicates=(valid_integration,))
160 custom_predicates=(valid_repo, valid_integration))
166 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
167 attr='settings_get',
168 renderer='rhodecode:templates/admin/integrations/form.mako',
169 request_method='GET',
170 route_name='repo_integrations_create')
171 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
172 attr='settings_post',
173 renderer='rhodecode:templates/admin/integrations/form.mako',
174 request_method='POST',
175 route_name='repo_integrations_create')
176
161 config.add_route('repo_integrations_edit',
177 config.add_route('repo_integrations_edit',
162 add_route_requirements(
178 add_route_requirements('/{repo_name}/settings/integrations/{integration}/{integration_id}'),
163 '{repo_name}/settings/integrations/{integration}/{integration_id}',
179 repo_route=True,
164 ),
180 custom_predicates=(valid_integration,))
165 custom_predicates=(valid_repo, valid_integration))
181 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
166 for route_name in ['repo_integrations_edit', 'repo_integrations_create']:
182 attr='settings_get',
167 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
183 renderer='rhodecode:templates/admin/integrations/form.mako',
168 attr='settings_get',
184 request_method='GET',
169 renderer='rhodecode:templates/admin/integrations/form.mako',
185 route_name='repo_integrations_edit')
170 request_method='GET',
186 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
171 route_name=route_name)
187 attr='settings_post',
172 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
188 renderer='rhodecode:templates/admin/integrations/form.mako',
173 attr='settings_post',
189 request_method='POST',
174 renderer='rhodecode:templates/admin/integrations/form.mako',
190 route_name='repo_integrations_edit')
175 request_method='POST',
176 route_name=route_name)
177
191
178
192
179 def valid_repo(info, request):
180 repo = Repository.get_by_repo_name(info['match']['repo_name'])
181 if repo:
182 return True
183
184
185 def valid_repo_group(info, request):
186 repo_group = RepoGroup.get_by_group_name(info['match']['repo_group_name'])
187 if repo_group:
188 return True
189 return False
190
191
193
192 def valid_integration(info, request):
194 def valid_integration(info, request):
193 integration_type = info['match']['integration']
195 integration_type = info['match']['integration']
194 integration_id = info['match'].get('integration_id')
196 integration_id = info['match'].get('integration_id')
195 repo_name = info['match'].get('repo_name')
196 repo_group_name = info['match'].get('repo_group_name')
197
197
198 if integration_type not in integration_type_registry:
198 if integration_type not in integration_type_registry:
199 return False
199 return False
200
200
201 repo, repo_group = None, None
201 if integration_id:
202 if repo_name:
202 if not safe_int(integration_id):
203 repo = Repository.get_by_repo_name(repo_name)
204 if not repo:
205 return False
203 return False
206
204
207 if repo_group_name:
208 repo_group = RepoGroup.get_by_group_name(repo_group_name)
209 if not repo_group:
210 return False
211
212 if repo_name and repo_group:
213 raise Exception('Either repo or repo_group can be set, not both')
214
215 if integration_id:
216 integration = Integration.get(integration_id)
205 integration = Integration.get(integration_id)
217 if not integration:
206 if not integration:
218 return False
207 return False
219 if integration.integration_type != integration_type:
208 if integration.integration_type != integration_type:
220 return False
209 return False
210
211 # match types to repo or repo group
212 repo_name = info['match'].get('repo_name')
213 repo_group_name = info['match'].get('repo_group_name')
214 repo, repo_group = None, None
215 if repo_name:
216 repo = Repository.get_by_repo_name(repo_name)
217 if not repo:
218 return False
219
220 if repo_group_name:
221 repo_group = RepoGroup.get_by_group_name(repo_group_name)
222 if not repo_group:
223 return False
224
221 if repo and repo.repo_id != integration.repo_id:
225 if repo and repo.repo_id != integration.repo_id:
222 return False
226 return False
223 if repo_group and repo_group.group_id != integration.repo_group_id:
227 if repo_group and repo_group.group_id != integration.repo_group_id:
224 return False
228 return False
225
229
226 return True
230 return True
@@ -1,259 +1,264 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.apps._base import ADMIN_PREFIX
23 from rhodecode.model.db import Integration
24 from rhodecode.model.db import Integration
24 from rhodecode.model.meta import Session
25 from rhodecode.model.meta import Session
25 from rhodecode.integrations import integration_type_registry
26 from rhodecode.integrations import integration_type_registry
26 from rhodecode.config.routing import ADMIN_PREFIX
27
28
29 def route_path(name, **kwargs):
30 return {
31 'home': '/',
32 }[name].format(**kwargs)
27
33
28
34
29 @pytest.mark.usefixtures('app', 'autologin_user')
35 @pytest.mark.usefixtures('app', 'autologin_user')
30 class TestIntegrationsView(object):
36 class TestIntegrationsView(object):
31 pass
37 pass
32
38
33
39
34 class TestGlobalIntegrationsView(TestIntegrationsView):
40 class TestGlobalIntegrationsView(TestIntegrationsView):
35 def test_index_no_integrations(self, app):
41 def test_index_no_integrations(self):
36 url = ADMIN_PREFIX + '/integrations'
42 url = ADMIN_PREFIX + '/integrations'
37 response = app.get(url)
43 response = self.app.get(url)
38
44
39 assert response.status_code == 200
45 assert response.status_code == 200
40 assert 'exist yet' in response.body
46 assert 'exist yet' in response.body
41
47
42 def test_index_with_integrations(self, app, global_integration_stub):
48 def test_index_with_integrations(self, global_integration_stub):
43 url = ADMIN_PREFIX + '/integrations'
49 url = ADMIN_PREFIX + '/integrations'
44 response = app.get(url)
50 response = self.app.get(url)
45
51
46 assert response.status_code == 200
52 assert response.status_code == 200
47 assert 'exist yet' not in response.body
53 assert 'exist yet' not in response.body
48 assert global_integration_stub.name in response.body
54 assert global_integration_stub.name in response.body
49
55
50 def test_new_integration_page(self, app):
56 @pytest.mark.parametrize(
57 'IntegrationType', integration_type_registry.values())
58 def test_new_integration_page(self, IntegrationType):
51 url = ADMIN_PREFIX + '/integrations/new'
59 url = ADMIN_PREFIX + '/integrations/new'
52
60
53 response = app.get(url)
61 response = self.app.get(url, status=200)
54
55 assert response.status_code == 200
56
62
57 for integration_key in integration_type_registry:
63 url = (ADMIN_PREFIX + '/integrations/{integration}/new').format(
58 nurl = (ADMIN_PREFIX + '/integrations/{integration}/new').format(
64 integration=IntegrationType.key)
59 integration=integration_key)
65 assert url in response.body
60 assert nurl in response.body
61
66
62 @pytest.mark.parametrize(
67 @pytest.mark.parametrize(
63 'IntegrationType', integration_type_registry.values())
68 'IntegrationType', integration_type_registry.values())
64 def test_get_create_integration_page(self, app, IntegrationType):
69 def test_get_create_integration_page(self, IntegrationType):
65 url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format(
70 url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format(
66 integration_key=IntegrationType.key)
71 integration_key=IntegrationType.key)
67
72
68 response = app.get(url)
73 response = self.app.get(url, status=200)
69
74
70 assert response.status_code == 200
71 assert IntegrationType.display_name in response.body
75 assert IntegrationType.display_name in response.body
72
76
73 def test_post_integration_page(self, app, StubIntegrationType, csrf_token,
77 def test_post_integration_page(self, StubIntegrationType, csrf_token,
74 test_repo_group, backend_random):
78 test_repo_group, backend_random):
75 url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format(
79 url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format(
76 integration_key=StubIntegrationType.key)
80 integration_key=StubIntegrationType.key)
77
81
78 _post_integration_test_helper(app, url, csrf_token, admin_view=True,
82 _post_integration_test_helper(self.app, url, csrf_token, admin_view=True,
83 repo=backend_random.repo, repo_group=test_repo_group)
84
85
86 class TestRepoIntegrationsView(TestIntegrationsView):
87 def test_index_no_integrations(self, backend_random):
88 url = '/{repo_name}/settings/integrations'.format(
89 repo_name=backend_random.repo.repo_name)
90 response = self.app.get(url)
91
92 assert response.status_code == 200
93 assert 'exist yet' in response.body
94
95 def test_index_with_integrations(self, repo_integration_stub):
96 url = '/{repo_name}/settings/integrations'.format(
97 repo_name=repo_integration_stub.repo.repo_name)
98 stub_name = repo_integration_stub.name
99
100 response = self.app.get(url)
101
102 assert response.status_code == 200
103 assert stub_name in response.body
104 assert 'exist yet' not in response.body
105
106 @pytest.mark.parametrize(
107 'IntegrationType', integration_type_registry.values())
108 def test_new_integration_page(self, backend_random, IntegrationType):
109 repo_name = backend_random.repo.repo_name
110 url = '/{repo_name}/settings/integrations/new'.format(
111 repo_name=repo_name)
112
113 response = self.app.get(url, status=200)
114
115 url = '/{repo_name}/settings/integrations/{integration}/new'.format(
116 repo_name=repo_name,
117 integration=IntegrationType.key)
118
119 assert url in response.body
120
121 @pytest.mark.parametrize(
122 'IntegrationType', integration_type_registry.values())
123 def test_get_create_integration_page(self, backend_random, IntegrationType):
124 repo_name = backend_random.repo.repo_name
125 url = '/{repo_name}/settings/integrations/{integration_key}/new'.format(
126 repo_name=repo_name, integration_key=IntegrationType.key)
127
128 response = self.app.get(url, status=200)
129
130 assert IntegrationType.display_name in response.body
131
132 def test_post_integration_page(self, backend_random, test_repo_group,
133 StubIntegrationType, csrf_token):
134 repo_name = backend_random.repo.repo_name
135 url = '/{repo_name}/settings/integrations/{integration_key}/new'.format(
136 repo_name=repo_name, integration_key=StubIntegrationType.key)
137
138 _post_integration_test_helper(
139 self.app, url, csrf_token, admin_view=False,
79 repo=backend_random.repo, repo_group=test_repo_group)
140 repo=backend_random.repo, repo_group=test_repo_group)
80
141
81
142
82 class TestRepoGroupIntegrationsView(TestIntegrationsView):
143 class TestRepoGroupIntegrationsView(TestIntegrationsView):
83 def test_index_no_integrations(self, app, test_repo_group):
144 def test_index_no_integrations(self, test_repo_group):
84 url = '/{repo_group_name}/settings/integrations'.format(
145 url = '/{repo_group_name}/settings/integrations'.format(
85 repo_group_name=test_repo_group.group_name)
146 repo_group_name=test_repo_group.group_name)
86 response = app.get(url)
147 response = self.app.get(url)
87
148
88 assert response.status_code == 200
149 assert response.status_code == 200
89 assert 'exist yet' in response.body
150 assert 'exist yet' in response.body
90
151
91 def test_index_with_integrations(self, app, test_repo_group,
152 def test_index_with_integrations(
92 repogroup_integration_stub):
153 self, test_repo_group, repogroup_integration_stub):
154
93 url = '/{repo_group_name}/settings/integrations'.format(
155 url = '/{repo_group_name}/settings/integrations'.format(
94 repo_group_name=test_repo_group.group_name)
156 repo_group_name=test_repo_group.group_name)
95
157
96 stub_name = repogroup_integration_stub.name
158 stub_name = repogroup_integration_stub.name
97 response = app.get(url)
159 response = self.app.get(url)
98
160
99 assert response.status_code == 200
161 assert response.status_code == 200
100 assert 'exist yet' not in response.body
162 assert 'exist yet' not in response.body
101 assert stub_name in response.body
163 assert stub_name in response.body
102
164
103 def test_new_integration_page(self, app, test_repo_group):
165 def test_new_integration_page(self, test_repo_group):
104 repo_group_name = test_repo_group.group_name
166 repo_group_name = test_repo_group.group_name
105 url = '/{repo_group_name}/settings/integrations/new'.format(
167 url = '/{repo_group_name}/settings/integrations/new'.format(
106 repo_group_name=test_repo_group.group_name)
168 repo_group_name=test_repo_group.group_name)
107
169
108 response = app.get(url)
170 response = self.app.get(url)
109
171
110 assert response.status_code == 200
172 assert response.status_code == 200
111
173
112 for integration_key in integration_type_registry:
174 for integration_key in integration_type_registry:
113 nurl = ('/{repo_group_name}/settings/integrations'
175 nurl = ('/{repo_group_name}/settings/integrations/{integration}/new').format(
114 '/{integration}/new').format(
115 repo_group_name=repo_group_name,
176 repo_group_name=repo_group_name,
116 integration=integration_key)
177 integration=integration_key)
117
178
118 assert nurl in response.body
179 assert nurl in response.body
119
180
120 @pytest.mark.parametrize(
181 @pytest.mark.parametrize(
121 'IntegrationType', integration_type_registry.values())
182 'IntegrationType', integration_type_registry.values())
122 def test_get_create_integration_page(self, app, test_repo_group,
183 def test_get_create_integration_page(
123 IntegrationType):
184 self, test_repo_group, IntegrationType):
185
124 repo_group_name = test_repo_group.group_name
186 repo_group_name = test_repo_group.group_name
125 url = ('/{repo_group_name}/settings/integrations/{integration_key}/new'
187 url = ('/{repo_group_name}/settings/integrations/{integration_key}/new'
126 ).format(repo_group_name=repo_group_name,
188 ).format(repo_group_name=repo_group_name,
127 integration_key=IntegrationType.key)
189 integration_key=IntegrationType.key)
128
190
129 response = app.get(url)
191 response = self.app.get(url)
130
192
131 assert response.status_code == 200
193 assert response.status_code == 200
132 assert IntegrationType.display_name in response.body
194 assert IntegrationType.display_name in response.body
133
195
134 def test_post_integration_page(self, app, test_repo_group, backend_random,
196 def test_post_integration_page(self, test_repo_group, backend_random,
135 StubIntegrationType, csrf_token):
197 StubIntegrationType, csrf_token):
198
136 repo_group_name = test_repo_group.group_name
199 repo_group_name = test_repo_group.group_name
137 url = ('/{repo_group_name}/settings/integrations/{integration_key}/new'
200 url = ('/{repo_group_name}/settings/integrations/{integration_key}/new'
138 ).format(repo_group_name=repo_group_name,
201 ).format(repo_group_name=repo_group_name,
139 integration_key=StubIntegrationType.key)
202 integration_key=StubIntegrationType.key)
140
203
141 _post_integration_test_helper(app, url, csrf_token, admin_view=False,
204 _post_integration_test_helper(
142 repo=backend_random.repo, repo_group=test_repo_group)
205 self.app, url, csrf_token, admin_view=False,
143
144
145 class TestRepoIntegrationsView(TestIntegrationsView):
146 def test_index_no_integrations(self, app, backend_random):
147 url = '/{repo_name}/settings/integrations'.format(
148 repo_name=backend_random.repo.repo_name)
149 response = app.get(url)
150
151 assert response.status_code == 200
152 assert 'exist yet' in response.body
153
154 def test_index_with_integrations(self, app, repo_integration_stub):
155 url = '/{repo_name}/settings/integrations'.format(
156 repo_name=repo_integration_stub.repo.repo_name)
157 stub_name = repo_integration_stub.name
158
159 response = app.get(url)
160
161 assert response.status_code == 200
162 assert stub_name in response.body
163 assert 'exist yet' not in response.body
164
165 def test_new_integration_page(self, app, backend_random):
166 repo_name = backend_random.repo.repo_name
167 url = '/{repo_name}/settings/integrations/new'.format(
168 repo_name=repo_name)
169
170 response = app.get(url)
171
172 assert response.status_code == 200
173
174 for integration_key in integration_type_registry:
175 nurl = ('/{repo_name}/settings/integrations'
176 '/{integration}/new').format(
177 repo_name=repo_name,
178 integration=integration_key)
179
180 assert nurl in response.body
181
182 @pytest.mark.parametrize(
183 'IntegrationType', integration_type_registry.values())
184 def test_get_create_integration_page(self, app, backend_random,
185 IntegrationType):
186 repo_name = backend_random.repo.repo_name
187 url = '/{repo_name}/settings/integrations/{integration_key}/new'.format(
188 repo_name=repo_name, integration_key=IntegrationType.key)
189
190 response = app.get(url)
191
192 assert response.status_code == 200
193 assert IntegrationType.display_name in response.body
194
195 def test_post_integration_page(self, app, backend_random, test_repo_group,
196 StubIntegrationType, csrf_token):
197 repo_name = backend_random.repo.repo_name
198 url = '/{repo_name}/settings/integrations/{integration_key}/new'.format(
199 repo_name=repo_name, integration_key=StubIntegrationType.key)
200
201 _post_integration_test_helper(app, url, csrf_token, admin_view=False,
202 repo=backend_random.repo, repo_group=test_repo_group)
206 repo=backend_random.repo, repo_group=test_repo_group)
203
207
204
208
205 def _post_integration_test_helper(app, url, csrf_token, repo, repo_group,
209 def _post_integration_test_helper(app, url, csrf_token, repo, repo_group,
206 admin_view):
210 admin_view):
207 """
211 """
208 Posts form data to create integration at the url given then deletes it and
212 Posts form data to create integration at the url given then deletes it and
209 checks if the redirect url is correct.
213 checks if the redirect url is correct.
210 """
214 """
211
215 repo_name = repo.repo_name
216 repo_group_name = repo_group.group_name
212 app.post(url, params={}, status=403) # missing csrf check
217 app.post(url, params={}, status=403) # missing csrf check
213 response = app.post(url, params={'csrf_token': csrf_token})
218 response = app.post(url, params={'csrf_token': csrf_token})
214 assert response.status_code == 200
219 assert response.status_code == 200
215 assert 'Errors exist' in response.body
220 assert 'Errors exist' in response.body
216
221
217 scopes_destinations = [
222 scopes_destinations = [
218 ('global',
223 ('global',
219 ADMIN_PREFIX + '/integrations'),
224 ADMIN_PREFIX + '/integrations'),
220 ('root-repos',
225 ('root-repos',
221 ADMIN_PREFIX + '/integrations'),
226 ADMIN_PREFIX + '/integrations'),
222 ('repo:%s' % repo.repo_name,
227 ('repo:%s' % repo_name,
223 '/%s/settings/integrations' % repo.repo_name),
228 '/%s/settings/integrations' % repo_name),
224 ('repogroup:%s' % repo_group.group_name,
229 ('repogroup:%s' % repo_group_name,
225 '/%s/settings/integrations' % repo_group.group_name),
230 '/%s/settings/integrations' % repo_group_name),
226 ('repogroup-recursive:%s' % repo_group.group_name,
231 ('repogroup-recursive:%s' % repo_group_name,
227 '/%s/settings/integrations' % repo_group.group_name),
232 '/%s/settings/integrations' % repo_group_name),
228 ]
233 ]
229
234
230 for scope, destination in scopes_destinations:
235 for scope, destination in scopes_destinations:
231 if admin_view:
236 if admin_view:
232 destination = ADMIN_PREFIX + '/integrations'
237 destination = ADMIN_PREFIX + '/integrations'
233
238
234 form_data = [
239 form_data = [
235 ('csrf_token', csrf_token),
240 ('csrf_token', csrf_token),
236 ('__start__', 'options:mapping'),
241 ('__start__', 'options:mapping'),
237 ('name', 'test integration'),
242 ('name', 'test integration'),
238 ('scope', scope),
243 ('scope', scope),
239 ('enabled', 'true'),
244 ('enabled', 'true'),
240 ('__end__', 'options:mapping'),
245 ('__end__', 'options:mapping'),
241 ('__start__', 'settings:mapping'),
246 ('__start__', 'settings:mapping'),
242 ('test_int_field', '34'),
247 ('test_int_field', '34'),
243 ('test_string_field', ''), # empty value on purpose as it's required
248 ('test_string_field', ''), # empty value on purpose as it's required
244 ('__end__', 'settings:mapping'),
249 ('__end__', 'settings:mapping'),
245 ]
250 ]
246 errors_response = app.post(url, form_data)
251 errors_response = app.post(url, form_data)
247 assert 'Errors exist' in errors_response.body
252 assert 'Errors exist' in errors_response.body
248
253
249 form_data[-2] = ('test_string_field', 'data!')
254 form_data[-2] = ('test_string_field', 'data!')
250 assert Session().query(Integration).count() == 0
255 assert Session().query(Integration).count() == 0
251 created_response = app.post(url, form_data)
256 created_response = app.post(url, form_data)
252 assert Session().query(Integration).count() == 1
257 assert Session().query(Integration).count() == 1
253
258
254 delete_response = app.post(
259 delete_response = app.post(
255 created_response.location,
260 created_response.location,
256 params={'csrf_token': csrf_token, 'delete': 'delete'})
261 params={'csrf_token': csrf_token, 'delete': 'delete'})
257
262
258 assert Session().query(Integration).count() == 0
263 assert Session().query(Integration).count() == 0
259 assert delete_response.location.endswith(destination)
264 assert delete_response.location.endswith(destination)
@@ -1,390 +1,455 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2017 RhodeCode GmbH
3 # Copyright (C) 2012-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pylons
22 import deform
21 import deform
23 import logging
22 import logging
24 import colander
25 import peppercorn
23 import peppercorn
26 import webhelpers.paginate
24 import webhelpers.paginate
27
25
28 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
29 from pyramid.renderers import render
30 from pyramid.response import Response
31
27
28 from rhodecode.apps._base import BaseAppView
29 from rhodecode.integrations import integration_type_registry
32 from rhodecode.apps.admin.navigation import navigation_list
30 from rhodecode.apps.admin.navigation import navigation_list
33 from rhodecode.lib import auth
31 from rhodecode.lib.auth import (
34 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 LoginRequired, CSRFRequired, HasPermissionAnyDecorator,
33 HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator)
35 from rhodecode.lib.utils2 import safe_int
34 from rhodecode.lib.utils2 import safe_int
36 from rhodecode.lib.helpers import Page
35 from rhodecode.lib.helpers import Page
37 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
36 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
38 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.integration import IntegrationModel
38 from rhodecode.model.integration import IntegrationModel
40 from rhodecode.translation import _
41 from rhodecode.integrations import integration_type_registry
42 from rhodecode.model.validation_schema.schemas.integration_schema import (
39 from rhodecode.model.validation_schema.schemas.integration_schema import (
43 make_integration_schema, IntegrationScopeType)
40 make_integration_schema, IntegrationScopeType)
44
41
45 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
46
43
47
44
48 class IntegrationSettingsViewBase(object):
45 class IntegrationSettingsViewBase(BaseAppView):
49 """ Base Integration settings view used by both repo / global settings """
46 """
47 Base Integration settings view used by both repo / global settings
48 """
50
49
51 def __init__(self, context, request):
50 def __init__(self, context, request):
52 self.context = context
51 super(IntegrationSettingsViewBase, self).__init__(context, request)
53 self.request = request
52 self._load_view_context()
54 self._load_general_context()
55
53
56 if not self.perm_check(request.user):
54 def _load_view_context(self):
57 raise HTTPForbidden()
58
59 def _load_general_context(self):
60 """
55 """
61 This avoids boilerplate for repo/global+list/edit+views/templates
56 This avoids boilerplate for repo/global+list/edit+views/templates
62 by doing all possible contexts at the same time however it should
57 by doing all possible contexts at the same time however it should
63 be split up into separate functions once more "contexts" exist
58 be split up into separate functions once more "contexts" exist
64 """
59 """
65
60
66 self.IntegrationType = None
61 self.IntegrationType = None
67 self.repo = None
62 self.repo = None
68 self.repo_group = None
63 self.repo_group = None
69 self.integration = None
64 self.integration = None
70 self.integrations = {}
65 self.integrations = {}
71
66
72 request = self.request
67 request = self.request
73
68
74 if 'repo_name' in request.matchdict: # in repo settings context
69 if 'repo_name' in request.matchdict: # in repo settings context
75 repo_name = request.matchdict['repo_name']
70 repo_name = request.matchdict['repo_name']
76 self.repo = Repository.get_by_repo_name(repo_name)
71 self.repo = Repository.get_by_repo_name(repo_name)
77
72
78 if 'repo_group_name' in request.matchdict: # in group settings context
73 if 'repo_group_name' in request.matchdict: # in group settings context
79 repo_group_name = request.matchdict['repo_group_name']
74 repo_group_name = request.matchdict['repo_group_name']
80 self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
75 self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
81
76
82 if 'integration' in request.matchdict: # integration type context
77 if 'integration' in request.matchdict: # integration type context
83 integration_type = request.matchdict['integration']
78 integration_type = request.matchdict['integration']
84 self.IntegrationType = integration_type_registry[integration_type]
79 self.IntegrationType = integration_type_registry[integration_type]
85
80
86 if 'integration_id' in request.matchdict: # single integration context
81 if 'integration_id' in request.matchdict: # single integration context
87 integration_id = request.matchdict['integration_id']
82 integration_id = request.matchdict['integration_id']
88 self.integration = Integration.get(integration_id)
83 self.integration = Integration.get(integration_id)
89
84
90 # extra perms check just in case
85 # extra perms check just in case
91 if not self._has_perms_for_integration(self.integration):
86 if not self._has_perms_for_integration(self.integration):
92 raise HTTPForbidden()
87 raise HTTPForbidden()
93
88
94 self.settings = self.integration and self.integration.settings or {}
89 self.settings = self.integration and self.integration.settings or {}
95 self.admin_view = not (self.repo or self.repo_group)
90 self.admin_view = not (self.repo or self.repo_group)
96
91
97 def _has_perms_for_integration(self, integration):
92 def _has_perms_for_integration(self, integration):
98 perms = self.request.user.permissions
93 perms = self.request.user.permissions
99
94
100 if 'hg.admin' in perms['global']:
95 if 'hg.admin' in perms['global']:
101 return True
96 return True
102
97
103 if integration.repo:
98 if integration.repo:
104 return perms['repositories'].get(
99 return perms['repositories'].get(
105 integration.repo.repo_name) == 'repository.admin'
100 integration.repo.repo_name) == 'repository.admin'
106
101
107 if integration.repo_group:
102 if integration.repo_group:
108 return perms['repositories_groups'].get(
103 return perms['repositories_groups'].get(
109 integration.repo_group.group_name) == 'group.admin'
104 integration.repo_group.group_name) == 'group.admin'
110
105
111 return False
106 return False
112
107
113 def _template_c_context(self):
108 def _get_local_tmpl_context(self, include_app_defaults=False):
114 # TODO: dan: this is a stopgap in order to inherit from current pylons
109 _ = self.request.translate
115 # based admin/repo settings templates - this should be removed entirely
110 c = super(IntegrationSettingsViewBase, self)._get_local_tmpl_context(
116 # after port to pyramid
111 include_app_defaults=include_app_defaults)
117
112
118 c = pylons.tmpl_context
119 c.active = 'integrations'
113 c.active = 'integrations'
120 c.rhodecode_user = self.request.user
121 c.repo = self.repo
122 c.repo_group = self.repo_group
123 c.repo_name = self.repo and self.repo.repo_name or None
124 c.repo_group_name = self.repo_group and self.repo_group.group_name or None
125
126 if self.repo:
127 c.repo_info = self.repo
128 c.rhodecode_db_repo = self.repo
129 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
130 else:
131 c.navlist = navigation_list(self.request)
132
114
133 return c
115 return c
134
116
135 def _form_schema(self):
117 def _form_schema(self):
136 schema = make_integration_schema(IntegrationType=self.IntegrationType,
118 schema = make_integration_schema(IntegrationType=self.IntegrationType,
137 settings=self.settings)
119 settings=self.settings)
138
120
139 # returns a clone, important if mutating the schema later
121 # returns a clone, important if mutating the schema later
140 return schema.bind(
122 return schema.bind(
141 permissions=self.request.user.permissions,
123 permissions=self.request.user.permissions,
142 no_scope=not self.admin_view)
124 no_scope=not self.admin_view)
143
125
144 def _form_defaults(self):
126 def _form_defaults(self):
127 _ = self.request.translate
145 defaults = {}
128 defaults = {}
146
129
147 if self.integration:
130 if self.integration:
148 defaults['settings'] = self.integration.settings or {}
131 defaults['settings'] = self.integration.settings or {}
149 defaults['options'] = {
132 defaults['options'] = {
150 'name': self.integration.name,
133 'name': self.integration.name,
151 'enabled': self.integration.enabled,
134 'enabled': self.integration.enabled,
152 'scope': {
135 'scope': {
153 'repo': self.integration.repo,
136 'repo': self.integration.repo,
154 'repo_group': self.integration.repo_group,
137 'repo_group': self.integration.repo_group,
155 'child_repos_only': self.integration.child_repos_only,
138 'child_repos_only': self.integration.child_repos_only,
156 },
139 },
157 }
140 }
158 else:
141 else:
159 if self.repo:
142 if self.repo:
160 scope = _('{repo_name} repository').format(
143 scope = _('{repo_name} repository').format(
161 repo_name=self.repo.repo_name)
144 repo_name=self.repo.repo_name)
162 elif self.repo_group:
145 elif self.repo_group:
163 scope = _('{repo_group_name} repo group').format(
146 scope = _('{repo_group_name} repo group').format(
164 repo_group_name=self.repo_group.group_name)
147 repo_group_name=self.repo_group.group_name)
165 else:
148 else:
166 scope = _('Global')
149 scope = _('Global')
167
150
168 defaults['options'] = {
151 defaults['options'] = {
169 'enabled': True,
152 'enabled': True,
170 'name': _('{name} integration').format(
153 'name': _('{name} integration').format(
171 name=self.IntegrationType.display_name),
154 name=self.IntegrationType.display_name),
172 }
155 }
173 defaults['options']['scope'] = {
156 defaults['options']['scope'] = {
174 'repo': self.repo,
157 'repo': self.repo,
175 'repo_group': self.repo_group,
158 'repo_group': self.repo_group,
176 }
159 }
177
160
178 return defaults
161 return defaults
179
162
180 def _delete_integration(self, integration):
163 def _delete_integration(self, integration):
181 Session().delete(self.integration)
164 _ = self.request.translate
165 Session().delete(integration)
182 Session().commit()
166 Session().commit()
183 self.request.session.flash(
167 self.request.session.flash(
184 _('Integration {integration_name} deleted successfully.').format(
168 _('Integration {integration_name} deleted successfully.').format(
185 integration_name=self.integration.name),
169 integration_name=integration.name),
186 queue='success')
170 queue='success')
187
171
188 if self.repo:
172 if self.repo:
189 redirect_to = self.request.route_url(
173 redirect_to = self.request.route_path(
190 'repo_integrations_home', repo_name=self.repo.repo_name)
174 'repo_integrations_home', repo_name=self.repo.repo_name)
191 elif self.repo_group:
175 elif self.repo_group:
192 redirect_to = self.request.route_url(
176 redirect_to = self.request.route_path(
193 'repo_group_integrations_home',
177 'repo_group_integrations_home',
194 repo_group_name=self.repo_group.group_name)
178 repo_group_name=self.repo_group.group_name)
195 else:
179 else:
196 redirect_to = self.request.route_url('global_integrations_home')
180 redirect_to = self.request.route_path('global_integrations_home')
197 raise HTTPFound(redirect_to)
181 raise HTTPFound(redirect_to)
198
182
199 def settings_get(self, defaults=None, form=None):
183 def _integration_list(self):
184 """ List integrations """
185
186 c = self.load_default_context()
187 if self.repo:
188 scope = self.repo
189 elif self.repo_group:
190 scope = self.repo_group
191 else:
192 scope = 'all'
193
194 integrations = []
195
196 for IntType, integration in IntegrationModel().get_integrations(
197 scope=scope, IntegrationType=self.IntegrationType):
198
199 # extra permissions check *just in case*
200 if not self._has_perms_for_integration(integration):
201 continue
202
203 integrations.append((IntType, integration))
204
205 sort_arg = self.request.GET.get('sort', 'name:asc')
206 if ':' in sort_arg:
207 sort_field, sort_dir = sort_arg.split(':')
208 else:
209 sort_field = sort_arg, 'asc'
210
211 assert sort_field in ('name', 'integration_type', 'enabled', 'scope')
212
213 integrations.sort(
214 key=lambda x: getattr(x[1], sort_field),
215 reverse=(sort_dir == 'desc'))
216
217 page_url = webhelpers.paginate.PageURL(
218 self.request.path, self.request.GET)
219 page = safe_int(self.request.GET.get('page', 1), 1)
220
221 integrations = Page(
222 integrations, page=page, items_per_page=10, url=page_url)
223
224 c.rev_sort_dir = sort_dir != 'desc' and 'desc' or 'asc'
225
226 c.current_IntegrationType = self.IntegrationType
227 c.integrations_list = integrations
228 c.available_integrations = integration_type_registry
229
230 return self._get_template_context(c)
231
232 def _settings_get(self, defaults=None, form=None):
200 """
233 """
201 View that displays the integration settings as a form.
234 View that displays the integration settings as a form.
202 """
235 """
236 c = self.load_default_context()
203
237
204 defaults = defaults or self._form_defaults()
238 defaults = defaults or self._form_defaults()
205 schema = self._form_schema()
239 schema = self._form_schema()
206
240
207 if self.integration:
241 if self.integration:
208 buttons = ('submit', 'delete')
242 buttons = ('submit', 'delete')
209 else:
243 else:
210 buttons = ('submit',)
244 buttons = ('submit',)
211
245
212 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
246 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
213
247
214 template_context = {
248 c.form = form
215 'form': form,
249 c.current_IntegrationType = self.IntegrationType
216 'current_IntegrationType': self.IntegrationType,
250 c.integration = self.integration
217 'integration': self.integration,
218 'c': self._template_c_context(),
219 }
220
251
221 return template_context
252 return self._get_template_context(c)
222
253
223 @auth.CSRFRequired()
254 def _settings_post(self):
224 def settings_post(self):
225 """
255 """
226 View that validates and stores the integration settings.
256 View that validates and stores the integration settings.
227 """
257 """
258 _ = self.request.translate
259
228 controls = self.request.POST.items()
260 controls = self.request.POST.items()
229 pstruct = peppercorn.parse(controls)
261 pstruct = peppercorn.parse(controls)
230
262
231 if self.integration and pstruct.get('delete'):
263 if self.integration and pstruct.get('delete'):
232 return self._delete_integration(self.integration)
264 return self._delete_integration(self.integration)
233
265
234 schema = self._form_schema()
266 schema = self._form_schema()
235
267
236 skip_settings_validation = False
268 skip_settings_validation = False
237 if self.integration and 'enabled' not in pstruct.get('options', {}):
269 if self.integration and 'enabled' not in pstruct.get('options', {}):
238 skip_settings_validation = True
270 skip_settings_validation = True
239 schema['settings'].validator = None
271 schema['settings'].validator = None
240 for field in schema['settings'].children:
272 for field in schema['settings'].children:
241 field.validator = None
273 field.validator = None
242 field.missing = ''
274 field.missing = ''
243
275
244 if self.integration:
276 if self.integration:
245 buttons = ('submit', 'delete')
277 buttons = ('submit', 'delete')
246 else:
278 else:
247 buttons = ('submit',)
279 buttons = ('submit',)
248
280
249 form = deform.Form(schema, buttons=buttons)
281 form = deform.Form(schema, buttons=buttons)
250
282
251 if not self.admin_view:
283 if not self.admin_view:
252 # scope is read only field in these cases, and has to be added
284 # scope is read only field in these cases, and has to be added
253 options = pstruct.setdefault('options', {})
285 options = pstruct.setdefault('options', {})
254 if 'scope' not in options:
286 if 'scope' not in options:
255 options['scope'] = IntegrationScopeType().serialize(None, {
287 options['scope'] = IntegrationScopeType().serialize(None, {
256 'repo': self.repo,
288 'repo': self.repo,
257 'repo_group': self.repo_group,
289 'repo_group': self.repo_group,
258 })
290 })
259
291
260 try:
292 try:
261 valid_data = form.validate_pstruct(pstruct)
293 valid_data = form.validate_pstruct(pstruct)
262 except deform.ValidationFailure as e:
294 except deform.ValidationFailure as e:
263 self.request.session.flash(
295 self.request.session.flash(
264 _('Errors exist when saving integration settings. '
296 _('Errors exist when saving integration settings. '
265 'Please check the form inputs.'),
297 'Please check the form inputs.'),
266 queue='error')
298 queue='error')
267 return self.settings_get(form=e)
299 return self._settings_get(form=e)
268
300
269 if not self.integration:
301 if not self.integration:
270 self.integration = Integration()
302 self.integration = Integration()
271 self.integration.integration_type = self.IntegrationType.key
303 self.integration.integration_type = self.IntegrationType.key
272 Session().add(self.integration)
304 Session().add(self.integration)
273
305
274 scope = valid_data['options']['scope']
306 scope = valid_data['options']['scope']
275
307
276 IntegrationModel().update_integration(self.integration,
308 IntegrationModel().update_integration(self.integration,
277 name=valid_data['options']['name'],
309 name=valid_data['options']['name'],
278 enabled=valid_data['options']['enabled'],
310 enabled=valid_data['options']['enabled'],
279 settings=valid_data['settings'],
311 settings=valid_data['settings'],
280 repo=scope['repo'],
312 repo=scope['repo'],
281 repo_group=scope['repo_group'],
313 repo_group=scope['repo_group'],
282 child_repos_only=scope['child_repos_only'],
314 child_repos_only=scope['child_repos_only'],
283 )
315 )
284
316
285 self.integration.settings = valid_data['settings']
317 self.integration.settings = valid_data['settings']
286 Session().commit()
318 Session().commit()
287 # Display success message and redirect.
319 # Display success message and redirect.
288 self.request.session.flash(
320 self.request.session.flash(
289 _('Integration {integration_name} updated successfully.').format(
321 _('Integration {integration_name} updated successfully.').format(
290 integration_name=self.IntegrationType.display_name),
322 integration_name=self.IntegrationType.display_name),
291 queue='success')
323 queue='success')
292
324
293 # if integration scope changes, we must redirect to the right place
325 # if integration scope changes, we must redirect to the right place
294 # keeping in mind if the original view was for /repo/ or /_admin/
326 # keeping in mind if the original view was for /repo/ or /_admin/
295 admin_view = not (self.repo or self.repo_group)
327 admin_view = not (self.repo or self.repo_group)
296
328
297 if self.integration.repo and not admin_view:
329 if self.integration.repo and not admin_view:
298 redirect_to = self.request.route_path(
330 redirect_to = self.request.route_path(
299 'repo_integrations_edit',
331 'repo_integrations_edit',
300 repo_name=self.integration.repo.repo_name,
332 repo_name=self.integration.repo.repo_name,
301 integration=self.integration.integration_type,
333 integration=self.integration.integration_type,
302 integration_id=self.integration.integration_id)
334 integration_id=self.integration.integration_id)
303 elif self.integration.repo_group and not admin_view:
335 elif self.integration.repo_group and not admin_view:
304 redirect_to = self.request.route_path(
336 redirect_to = self.request.route_path(
305 'repo_group_integrations_edit',
337 'repo_group_integrations_edit',
306 repo_group_name=self.integration.repo_group.group_name,
338 repo_group_name=self.integration.repo_group.group_name,
307 integration=self.integration.integration_type,
339 integration=self.integration.integration_type,
308 integration_id=self.integration.integration_id)
340 integration_id=self.integration.integration_id)
309 else:
341 else:
310 redirect_to = self.request.route_path(
342 redirect_to = self.request.route_path(
311 'global_integrations_edit',
343 'global_integrations_edit',
312 integration=self.integration.integration_type,
344 integration=self.integration.integration_type,
313 integration_id=self.integration.integration_id)
345 integration_id=self.integration.integration_id)
314
346
315 return HTTPFound(redirect_to)
347 return HTTPFound(redirect_to)
316
348
317 def index(self):
349 def _new_integration(self):
318 """ List integrations """
350 c = self.load_default_context()
319 if self.repo:
351 c.available_integrations = integration_type_registry
320 scope = self.repo
352 return self._get_template_context(c)
321 elif self.repo_group:
322 scope = self.repo_group
323 else:
324 scope = 'all'
325
326 integrations = []
327
328 for IntType, integration in IntegrationModel().get_integrations(
329 scope=scope, IntegrationType=self.IntegrationType):
330
331 # extra permissions check *just in case*
332 if not self._has_perms_for_integration(integration):
333 continue
334
335 integrations.append((IntType, integration))
336
337 sort_arg = self.request.GET.get('sort', 'name:asc')
338 if ':' in sort_arg:
339 sort_field, sort_dir = sort_arg.split(':')
340 else:
341 sort_field = sort_arg, 'asc'
342
343 assert sort_field in ('name', 'integration_type', 'enabled', 'scope')
344
353
345 integrations.sort(
354 def load_default_context(self):
346 key=lambda x: getattr(x[1], sort_field),
355 raise NotImplementedError()
347 reverse=(sort_dir == 'desc'))
348
349 page_url = webhelpers.paginate.PageURL(
350 self.request.path, self.request.GET)
351 page = safe_int(self.request.GET.get('page', 1), 1)
352
353 integrations = Page(integrations, page=page, items_per_page=10,
354 url=page_url)
355
356 template_context = {
357 'sort_field': sort_field,
358 'rev_sort_dir': sort_dir != 'desc' and 'desc' or 'asc',
359 'current_IntegrationType': self.IntegrationType,
360 'integrations_list': integrations,
361 'available_integrations': integration_type_registry,
362 'c': self._template_c_context(),
363 'request': self.request,
364 }
365 return template_context
366
367 def new_integration(self):
368 template_context = {
369 'available_integrations': integration_type_registry,
370 'c': self._template_c_context(),
371 }
372 return template_context
373
356
374
357
375 class GlobalIntegrationsView(IntegrationSettingsViewBase):
358 class GlobalIntegrationsView(IntegrationSettingsViewBase):
376 def perm_check(self, user):
359 def load_default_context(self):
377 return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
360 c = self._get_local_tmpl_context()
361 c.repo = self.repo
362 c.repo_group = self.repo_group
363 c.navlist = navigation_list(self.request)
364 self._register_global_c(c)
365 return c
366
367 @LoginRequired()
368 @HasPermissionAnyDecorator('hg.admin')
369 def integration_list(self):
370 return self._integration_list()
371
372 @LoginRequired()
373 @HasPermissionAnyDecorator('hg.admin')
374 def settings_get(self):
375 return self._settings_get()
376
377 @LoginRequired()
378 @HasPermissionAnyDecorator('hg.admin')
379 @CSRFRequired()
380 def settings_post(self):
381 return self._settings_post()
382
383 @LoginRequired()
384 @HasPermissionAnyDecorator('hg.admin')
385 def new_integration(self):
386 return self._new_integration()
378
387
379
388
380 class RepoIntegrationsView(IntegrationSettingsViewBase):
389 class RepoIntegrationsView(IntegrationSettingsViewBase):
381 def perm_check(self, user):
390 def load_default_context(self):
382 return auth.HasRepoPermissionAll('repository.admin')(
391 c = self._get_local_tmpl_context()
383 repo_name=self.repo.repo_name, user=user)
392
393 c.repo = self.repo
394 c.repo_group = self.repo_group
395
396 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
397 c.repo_info = self.db_repo = self.repo
398 c.rhodecode_db_repo = self.repo
399 c.repo_name = self.db_repo.repo_name
400 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
401
402 self._register_global_c(c)
403 return c
404
405 @LoginRequired()
406 @HasRepoPermissionAnyDecorator('repository.admin')
407 def integration_list(self):
408 return self._integration_list()
409
410 @LoginRequired()
411 @HasRepoPermissionAnyDecorator('repository.admin')
412 def settings_get(self):
413 return self._settings_get()
414
415 @LoginRequired()
416 @HasRepoPermissionAnyDecorator('repository.admin')
417 @CSRFRequired()
418 def settings_post(self):
419 return self._settings_post()
420
421 @LoginRequired()
422 @HasRepoPermissionAnyDecorator('repository.admin')
423 def new_integration(self):
424 return self._new_integration()
384
425
385
426
386 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
427 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
387 def perm_check(self, user):
428 def load_default_context(self):
388 return auth.HasRepoGroupPermissionAll('group.admin')(
429 c = self._get_local_tmpl_context()
389 group_name=self.repo_group.group_name, user=user)
430 c.repo = self.repo
431 c.repo_group = self.repo_group
432 c.navlist = navigation_list(self.request)
433 self._register_global_c(c)
434 return c
435
436 @LoginRequired()
437 @HasRepoGroupPermissionAnyDecorator('group.admin')
438 def integration_list(self):
439 return self._integration_list()
390
440
441 @LoginRequired()
442 @HasRepoGroupPermissionAnyDecorator('group.admin')
443 def settings_get(self):
444 return self._settings_get()
445
446 @LoginRequired()
447 @HasRepoGroupPermissionAnyDecorator('group.admin')
448 @CSRFRequired()
449 def settings_post(self):
450 return self._settings_post()
451
452 @LoginRequired()
453 @HasRepoGroupPermissionAnyDecorator('group.admin')
454 def new_integration(self):
455 return self._new_integration()
@@ -1,226 +1,226 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('favicon', '/favicon.ico', []);
17 pyroutes.register('favicon', '/favicon.ico', []);
18 pyroutes.register('robots', '/robots.txt', []);
18 pyroutes.register('robots', '/robots.txt', []);
19 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
19 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
20 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
20 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
21 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
21 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
22 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
22 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
23 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
23 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
24 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
24 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
25 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
26 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
26 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
28 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
28 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
29 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
29 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
30 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
30 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
31 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
31 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
33 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
33 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
34 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
34 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
35 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
35 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
36 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
36 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
37 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
37 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
38 pyroutes.register('admin_home', '/_admin', []);
38 pyroutes.register('admin_home', '/_admin', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
40 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
41 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
41 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
43 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
44 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
44 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
45 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
45 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
46 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
46 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
47 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
47 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
48 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
48 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
49 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
49 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
50 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
50 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
51 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
51 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
52 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
52 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
53 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
53 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
54 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
54 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
55 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
55 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
56 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
56 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
57 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
57 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
58 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
58 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
59 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
59 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
60 pyroutes.register('users', '/_admin/users', []);
60 pyroutes.register('users', '/_admin/users', []);
61 pyroutes.register('users_data', '/_admin/users_data', []);
61 pyroutes.register('users_data', '/_admin/users_data', []);
62 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
62 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
63 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
63 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
64 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
64 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
65 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
65 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
66 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
66 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
67 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
67 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
68 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
68 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
69 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
69 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
70 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
70 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
71 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
71 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
72 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
72 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
73 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
73 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
74 pyroutes.register('user_groups', '/_admin/user_groups', []);
74 pyroutes.register('user_groups', '/_admin/user_groups', []);
75 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
75 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
76 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
76 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
77 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
77 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
78 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
78 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
79 pyroutes.register('channelstream_proxy', '/_channelstream', []);
79 pyroutes.register('channelstream_proxy', '/_channelstream', []);
80 pyroutes.register('login', '/_admin/login', []);
80 pyroutes.register('login', '/_admin/login', []);
81 pyroutes.register('logout', '/_admin/logout', []);
81 pyroutes.register('logout', '/_admin/logout', []);
82 pyroutes.register('register', '/_admin/register', []);
82 pyroutes.register('register', '/_admin/register', []);
83 pyroutes.register('reset_password', '/_admin/password_reset', []);
83 pyroutes.register('reset_password', '/_admin/password_reset', []);
84 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
84 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
85 pyroutes.register('home', '/', []);
85 pyroutes.register('home', '/', []);
86 pyroutes.register('user_autocomplete_data', '/_users', []);
86 pyroutes.register('user_autocomplete_data', '/_users', []);
87 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
87 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
88 pyroutes.register('repo_list_data', '/_repos', []);
88 pyroutes.register('repo_list_data', '/_repos', []);
89 pyroutes.register('goto_switcher_data', '/_goto_data', []);
89 pyroutes.register('goto_switcher_data', '/_goto_data', []);
90 pyroutes.register('journal', '/_admin/journal', []);
90 pyroutes.register('journal', '/_admin/journal', []);
91 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
91 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
92 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
92 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
93 pyroutes.register('journal_public', '/_admin/public_journal', []);
93 pyroutes.register('journal_public', '/_admin/public_journal', []);
94 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
94 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
95 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
95 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
96 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
96 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
97 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
97 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
98 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
98 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
99 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
99 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
100 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
100 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
101 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
101 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
102 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
102 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
103 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
103 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
104 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
104 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
105 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
105 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
106 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
106 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
107 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
107 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
108 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
108 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
109 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
109 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
110 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
110 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
111 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
111 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
112 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
112 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
113 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
113 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
114 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
114 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
115 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
115 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
116 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
116 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
117 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
117 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
118 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
118 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
119 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
119 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
120 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
120 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
121 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
121 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
122 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
122 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
123 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
123 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
124 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
124 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
125 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
125 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
126 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
126 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
127 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
127 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
128 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
128 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
129 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
129 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
130 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
130 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
131 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
131 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
132 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
132 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
133 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
133 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
134 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
134 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
135 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
135 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
136 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
136 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
137 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
137 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
138 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
138 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
139 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
139 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
140 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
140 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
141 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
141 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
142 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
142 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
143 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
143 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
144 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
144 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
145 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
145 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
146 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
146 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
147 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
147 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
148 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
148 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
149 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
149 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
150 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
150 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
151 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
151 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
152 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
152 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
153 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
153 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
154 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
154 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
155 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
155 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
156 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
156 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
157 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
157 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
158 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
158 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
159 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
159 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
160 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
160 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
161 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
161 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
162 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
162 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
163 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
163 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
164 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
164 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
165 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
165 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
166 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
166 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
167 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
167 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
168 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
168 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
169 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
169 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
170 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
170 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
171 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
171 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
172 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
172 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
173 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
173 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
174 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
174 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
175 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
175 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
176 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
176 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
177 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
177 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
178 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
178 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
179 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
179 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
180 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
180 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
181 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
181 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
182 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
182 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
183 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
183 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
184 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
184 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
185 pyroutes.register('search', '/_admin/search', []);
185 pyroutes.register('search', '/_admin/search', []);
186 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
186 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
187 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
187 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
188 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
188 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
189 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
189 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
190 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
190 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
191 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
191 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
192 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
192 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
193 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
193 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
194 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
194 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
195 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
195 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
196 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
196 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
197 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
197 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
198 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
198 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
199 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
199 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
200 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
200 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
201 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
201 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
202 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
202 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
203 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
203 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
204 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
204 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
205 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
205 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
206 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
206 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
207 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
207 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
208 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
208 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
209 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
209 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
210 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
210 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
211 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
211 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
212 pyroutes.register('gists_show', '/_admin/gists', []);
212 pyroutes.register('gists_show', '/_admin/gists', []);
213 pyroutes.register('gists_new', '/_admin/gists/new', []);
213 pyroutes.register('gists_new', '/_admin/gists/new', []);
214 pyroutes.register('gists_create', '/_admin/gists/create', []);
214 pyroutes.register('gists_create', '/_admin/gists/create', []);
215 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
215 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
216 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
216 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
217 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
217 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
218 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
218 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
219 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
219 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
220 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
220 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
221 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
221 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
222 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
222 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
223 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
223 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
224 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
224 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
225 pyroutes.register('apiv2', '/_admin/api', []);
225 pyroutes.register('apiv2', '/_admin/api', []);
226 }
226 }
@@ -1,69 +1,69 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 &raquo;
7 &raquo;
8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 &raquo;
9 &raquo;
10 ${h.link_to(current_IntegrationType.display_name,
10 ${h.link_to(c.current_IntegrationType.display_name,
11 request.route_url(route_name='repo_integrations_list',
11 request.route_url(route_name='repo_integrations_list',
12 repo_name=c.repo.repo_name,
12 repo_name=c.repo.repo_name,
13 integration=current_IntegrationType.key))}
13 integration=c.current_IntegrationType.key))}
14 %elif c.repo_group:
14 %elif c.repo_group:
15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
17 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
18 &raquo;
18 &raquo;
19 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
19 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
20 &raquo;
20 &raquo;
21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
22 &raquo;
22 &raquo;
23 ${h.link_to(current_IntegrationType.display_name,
23 ${h.link_to(c.current_IntegrationType.display_name,
24 request.route_url(route_name='repo_group_integrations_list',
24 request.route_url(route_name='repo_group_integrations_list',
25 repo_group_name=c.repo_group.group_name,
25 repo_group_name=c.repo_group.group_name,
26 integration=current_IntegrationType.key))}
26 integration=c.current_IntegrationType.key))}
27 %else:
27 %else:
28 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
28 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
29 &raquo;
29 &raquo;
30 ${h.link_to(_('Settings'),h.url('admin_settings'))}
30 ${h.link_to(_('Settings'),h.url('admin_settings'))}
31 &raquo;
31 &raquo;
32 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
32 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
33 &raquo;
33 &raquo;
34 ${h.link_to(current_IntegrationType.display_name,
34 ${h.link_to(c.current_IntegrationType.display_name,
35 request.route_url(route_name='global_integrations_list',
35 request.route_url(route_name='global_integrations_list',
36 integration=current_IntegrationType.key))}
36 integration=c.current_IntegrationType.key))}
37 %endif
37 %endif
38
38
39 %if integration:
39 %if c.integration:
40 &raquo;
40 &raquo;
41 ${integration.name}
41 ${c.integration.name}
42 %elif current_IntegrationType:
42 %elif c.current_IntegrationType:
43 &raquo;
43 &raquo;
44 ${current_IntegrationType.display_name}
44 ${c.current_IntegrationType.display_name}
45 %endif
45 %endif
46 </%def>
46 </%def>
47
47
48 <style>
48 <style>
49 .control-inputs.item-options, .control-inputs.item-settings {
49 .control-inputs.item-options, .control-inputs.item-settings {
50 float: left;
50 float: left;
51 width: 100%;
51 width: 100%;
52 }
52 }
53 </style>
53 </style>
54 <div class="panel panel-default">
54 <div class="panel panel-default">
55 <div class="panel-heading">
55 <div class="panel-heading">
56 <h2 class="panel-title">
56 <h2 class="panel-title">
57 %if integration:
57 %if c.integration:
58 ${current_IntegrationType.display_name} - ${integration.name}
58 ${c.current_IntegrationType.display_name} - ${c.integration.name}
59 %else:
59 %else:
60 ${_('Create New %(integration_type)s Integration') % {
60 ${_('Create New %(integration_type)s Integration') % {
61 'integration_type': current_IntegrationType.display_name
61 'integration_type': c.current_IntegrationType.display_name
62 }}
62 }}
63 %endif
63 %endif
64 </h2>
64 </h2>
65 </div>
65 </div>
66 <div class="panel-body">
66 <div class="panel-body">
67 ${form.render() | n}
67 ${c.form.render() | n}
68 </div>
68 </div>
69 </div>
69 </div>
@@ -1,252 +1,254 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 %elif c.repo_group:
7 %elif c.repo_group:
8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
10 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
12 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
13 %else:
13 %else:
14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 &raquo;
15 &raquo;
16 ${h.link_to(_('Settings'),h.url('admin_settings'))}
16 ${h.link_to(_('Settings'),h.url('admin_settings'))}
17 %endif
17 %endif
18 %if current_IntegrationType:
18 %if c.current_IntegrationType:
19 &raquo;
19 &raquo;
20 %if c.repo:
20 %if c.repo:
21 ${h.link_to(_('Integrations'),
21 ${h.link_to(_('Integrations'),
22 request.route_url(route_name='repo_integrations_home',
22 request.route_path(route_name='repo_integrations_home',
23 repo_name=c.repo.repo_name))}
23 repo_name=c.repo.repo_name))}
24 %elif c.repo_group:
24 %elif c.repo_group:
25 ${h.link_to(_('Integrations'),
25 ${h.link_to(_('Integrations'),
26 request.route_url(route_name='repo_group_integrations_home',
26 request.route_path(route_name='repo_group_integrations_home',
27 repo_group_name=c.repo_group.group_name))}
27 repo_group_name=c.repo_group.group_name))}
28 %else:
28 %else:
29 ${h.link_to(_('Integrations'),
29 ${h.link_to(_('Integrations'),
30 request.route_url(route_name='global_integrations_home'))}
30 request.route_path(route_name='global_integrations_home'))}
31 %endif
31 %endif
32 &raquo;
32 &raquo;
33 ${current_IntegrationType.display_name}
33 ${c.current_IntegrationType.display_name}
34 %else:
34 %else:
35 &raquo;
35 &raquo;
36 ${_('Integrations')}
36 ${_('Integrations')}
37 %endif
37 %endif
38 </%def>
38 </%def>
39
39
40 <div class="panel panel-default">
40 <div class="panel panel-default">
41 <div class="panel-heading">
41 <div class="panel-heading">
42 <h3 class="panel-title">
42 <h3 class="panel-title">
43 %if c.repo:
43 %if c.repo:
44 ${_('Current Integrations for Repository: {repo_name}').format(repo_name=c.repo.repo_name)}
44 ${_('Current Integrations for Repository: {repo_name}').format(repo_name=c.repo.repo_name)}
45 %elif c.repo_group:
45 %elif c.repo_group:
46 ${_('Current Integrations for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
46 ${_('Current Integrations for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
47 %else:
47 %else:
48 ${_('Current Integrations')}
48 ${_('Current Integrations')}
49 %endif
49 %endif
50 </h3>
50 </h3>
51 </div>
51 </div>
52 <div class="panel-body">
52 <div class="panel-body">
53 <%
53 <%
54 if c.repo:
54 if c.repo:
55 home_url = request.route_path('repo_integrations_home',
55 home_url = request.route_path('repo_integrations_home',
56 repo_name=c.repo.repo_name)
56 repo_name=c.repo.repo_name)
57 elif c.repo_group:
57 elif c.repo_group:
58 home_url = request.route_path('repo_group_integrations_home',
58 home_url = request.route_path('repo_group_integrations_home',
59 repo_group_name=c.repo_group.group_name)
59 repo_group_name=c.repo_group.group_name)
60 else:
60 else:
61 home_url = request.route_path('global_integrations_home')
61 home_url = request.route_path('global_integrations_home')
62 %>
62 %>
63
63
64 <a href="${home_url}" class="btn ${not current_IntegrationType and 'btn-primary' or ''}">${_('All')}</a>
64 <a href="${home_url}" class="btn ${not c.current_IntegrationType and 'btn-primary' or ''}">${_('All')}</a>
65
65
66 %for integration_key, IntegrationType in available_integrations.items():
66 %for integration_key, IntegrationType in c.available_integrations.items():
67 <%
67 <%
68 if c.repo:
68 if c.repo:
69 list_url = request.route_path('repo_integrations_list',
69 list_url = request.route_path('repo_integrations_list',
70 repo_name=c.repo.repo_name,
70 repo_name=c.repo.repo_name,
71 integration=integration_key)
71 integration=integration_key)
72 elif c.repo_group:
72 elif c.repo_group:
73 list_url = request.route_path('repo_group_integrations_list',
73 list_url = request.route_path('repo_group_integrations_list',
74 repo_group_name=c.repo_group.group_name,
74 repo_group_name=c.repo_group.group_name,
75 integration=integration_key)
75 integration=integration_key)
76 else:
76 else:
77 list_url = request.route_path('global_integrations_list',
77 list_url = request.route_path('global_integrations_list',
78 integration=integration_key)
78 integration=integration_key)
79 %>
79 %>
80 <a href="${list_url}"
80 <a href="${list_url}"
81 class="btn ${current_IntegrationType and integration_key == current_IntegrationType.key and 'btn-primary' or ''}">
81 class="btn ${c.current_IntegrationType and integration_key == c.current_IntegrationType.key and 'btn-primary' or ''}">
82 ${IntegrationType.display_name}
82 ${IntegrationType.display_name}
83 </a>
83 </a>
84 %endfor
84 %endfor
85
85
86 <%
86 <%
87 integration_type = c.current_IntegrationType and c.current_IntegrationType.display_name or ''
88
87 if c.repo:
89 if c.repo:
88 create_url = h.route_path('repo_integrations_new', repo_name=c.repo.repo_name)
90 create_url = h.route_path('repo_integrations_new', repo_name=c.repo.repo_name)
89 elif c.repo_group:
91 elif c.repo_group:
90 create_url = h.route_path('repo_group_integrations_new', repo_group_name=c.repo_group.group_name)
92 create_url = h.route_path('repo_group_integrations_new', repo_group_name=c.repo_group.group_name)
91 else:
93 else:
92 create_url = h.route_path('global_integrations_new')
94 create_url = h.route_path('global_integrations_new')
93 %>
95 %>
94 <p class="pull-right">
96 <p class="pull-right">
95 <a href="${create_url}" class="btn btn-small btn-success">${_(u'Create new integration')}</a>
97 <a href="${create_url}" class="btn btn-small btn-success">${_(u'Create new integration')}</a>
96 </p>
98 </p>
97
99
98 <table class="rctable integrations">
100 <table class="rctable integrations">
99 <thead>
101 <thead>
100 <tr>
102 <tr>
101 <th><a href="?sort=enabled:${rev_sort_dir}">${_('Enabled')}</a></th>
103 <th><a href="?sort=enabled:${c.rev_sort_dir}">${_('Enabled')}</a></th>
102 <th><a href="?sort=name:${rev_sort_dir}">${_('Name')}</a></th>
104 <th><a href="?sort=name:${c.rev_sort_dir}">${_('Name')}</a></th>
103 <th colspan="2"><a href="?sort=integration_type:${rev_sort_dir}">${_('Type')}</a></th>
105 <th colspan="2"><a href="?sort=integration_type:${c.rev_sort_dir}">${_('Type')}</a></th>
104 <th><a href="?sort=scope:${rev_sort_dir}">${_('Scope')}</a></th>
106 <th><a href="?sort=scope:${c.rev_sort_dir}">${_('Scope')}</a></th>
105 <th>${_('Actions')}</th>
107 <th>${_('Actions')}</th>
106 <th></th>
108 <th></th>
107 </tr>
109 </tr>
108 </thead>
110 </thead>
109 <tbody>
111 <tbody>
110 %if not integrations_list:
112 %if not c.integrations_list:
111 <tr>
113 <tr>
112 <td colspan="7">
114 <td colspan="7">
113 <% integration_type = current_IntegrationType and current_IntegrationType.display_name or '' %>
115
114 %if c.repo:
116 %if c.repo:
115 ${_('No {type} integrations for repo {repo} exist yet.').format(type=integration_type, repo=c.repo.repo_name)}
117 ${_('No {type} integrations for repo {repo} exist yet.').format(type=integration_type, repo=c.repo.repo_name)}
116 %elif c.repo_group:
118 %elif c.repo_group:
117 ${_('No {type} integrations for repogroup {repogroup} exist yet.').format(type=integration_type, repogroup=c.repo_group.group_name)}
119 ${_('No {type} integrations for repogroup {repogroup} exist yet.').format(type=integration_type, repogroup=c.repo_group.group_name)}
118 %else:
120 %else:
119 ${_('No {type} integrations exist yet.').format(type=integration_type)}
121 ${_('No {type} integrations exist yet.').format(type=integration_type)}
120 %endif
122 %endif
121
123
122 %if current_IntegrationType:
124 %if c.current_IntegrationType:
123 <%
125 <%
124 if c.repo:
126 if c.repo:
125 create_url = h.route_path('repo_integrations_create', repo_name=c.repo.repo_name, integration=current_IntegrationType.key)
127 create_url = h.route_path('repo_integrations_create', repo_name=c.repo.repo_name, integration=c.current_IntegrationType.key)
126 elif c.repo_group:
128 elif c.repo_group:
127 create_url = h.route_path('repo_group_integrations_create', repo_group_name=c.repo_group.group_name, integration=current_IntegrationType.key)
129 create_url = h.route_path('repo_group_integrations_create', repo_group_name=c.repo_group.group_name, integration=c.current_IntegrationType.key)
128 else:
130 else:
129 create_url = h.route_path('global_integrations_create', integration=current_IntegrationType.key)
131 create_url = h.route_path('global_integrations_create', integration=c.current_IntegrationType.key)
130 %>
132 %>
131 %endif
133 %endif
132
134
133 <a href="${create_url}">${_(u'Create one')}</a>
135 <a href="${create_url}">${_(u'Create one')}</a>
134 </td>
136 </td>
135 </tr>
137 </tr>
136 %endif
138 %endif
137 %for IntegrationType, integration in integrations_list:
139 %for IntegrationType, integration in c.integrations_list:
138 <tr id="integration_${integration.integration_id}">
140 <tr id="integration_${integration.integration_id}">
139 <td class="td-enabled">
141 <td class="td-enabled">
140 %if integration.enabled:
142 %if integration.enabled:
141 <div class="flag_status approved pull-left"></div>
143 <div class="flag_status approved pull-left"></div>
142 %else:
144 %else:
143 <div class="flag_status rejected pull-left"></div>
145 <div class="flag_status rejected pull-left"></div>
144 %endif
146 %endif
145 </td>
147 </td>
146 <td class="td-description">
148 <td class="td-description">
147 ${integration.name}
149 ${integration.name}
148 </td>
150 </td>
149 <td class="td-icon">
151 <td class="td-icon">
150 %if integration.integration_type in available_integrations:
152 %if integration.integration_type in c.available_integrations:
151 <div class="integration-icon">
153 <div class="integration-icon">
152 ${available_integrations[integration.integration_type].icon|n}
154 ${c.available_integrations[integration.integration_type].icon|n}
153 </div>
155 </div>
154 %else:
156 %else:
155 ?
157 ?
156 %endif
158 %endif
157 </td>
159 </td>
158 <td class="td-type">
160 <td class="td-type">
159 ${integration.integration_type}
161 ${integration.integration_type}
160 </td>
162 </td>
161 <td class="td-scope">
163 <td class="td-scope">
162 %if integration.repo:
164 %if integration.repo:
163 <a href="${h.route_path('repo_summary', repo_name=integration.repo.repo_name)}">
165 <a href="${h.route_path('repo_summary', repo_name=integration.repo.repo_name)}">
164 ${_('repo')}:${integration.repo.repo_name}
166 ${_('repo')}:${integration.repo.repo_name}
165 </a>
167 </a>
166 %elif integration.repo_group:
168 %elif integration.repo_group:
167 <a href="${h.route_path('repo_group_home', repo_group_name=integration.repo_group.group_name)}">
169 <a href="${h.route_path('repo_group_home', repo_group_name=integration.repo_group.group_name)}">
168 ${_('repogroup')}:${integration.repo_group.group_name}
170 ${_('repogroup')}:${integration.repo_group.group_name}
169 %if integration.child_repos_only:
171 %if integration.child_repos_only:
170 ${_('child repos only')}
172 ${_('child repos only')}
171 %else:
173 %else:
172 ${_('cascade to all')}
174 ${_('cascade to all')}
173 %endif
175 %endif
174 </a>
176 </a>
175 %else:
177 %else:
176 %if integration.child_repos_only:
178 %if integration.child_repos_only:
177 ${_('top level repos only')}
179 ${_('top level repos only')}
178 %else:
180 %else:
179 ${_('global')}
181 ${_('global')}
180 %endif
182 %endif
181 </td>
183 </td>
182 %endif
184 %endif
183 <td class="td-action">
185 <td class="td-action">
184 %if not IntegrationType:
186 %if not IntegrationType:
185 ${_('unknown integration')}
187 ${_('unknown integration')}
186 %else:
188 %else:
187 <%
189 <%
188 if c.repo:
190 if c.repo:
189 edit_url = request.route_path('repo_integrations_edit',
191 edit_url = request.route_path('repo_integrations_edit',
190 repo_name=c.repo.repo_name,
192 repo_name=c.repo.repo_name,
191 integration=integration.integration_type,
193 integration=integration.integration_type,
192 integration_id=integration.integration_id)
194 integration_id=integration.integration_id)
193 elif c.repo_group:
195 elif c.repo_group:
194 edit_url = request.route_path('repo_group_integrations_edit',
196 edit_url = request.route_path('repo_group_integrations_edit',
195 repo_group_name=c.repo_group.group_name,
197 repo_group_name=c.repo_group.group_name,
196 integration=integration.integration_type,
198 integration=integration.integration_type,
197 integration_id=integration.integration_id)
199 integration_id=integration.integration_id)
198 else:
200 else:
199 edit_url = request.route_path('global_integrations_edit',
201 edit_url = request.route_path('global_integrations_edit',
200 integration=integration.integration_type,
202 integration=integration.integration_type,
201 integration_id=integration.integration_id)
203 integration_id=integration.integration_id)
202 %>
204 %>
203 <div class="grid_edit">
205 <div class="grid_edit">
204 <a href="${edit_url}">${_('Edit')}</a>
206 <a href="${edit_url}">${_('Edit')}</a>
205 </div>
207 </div>
206 <div class="grid_delete">
208 <div class="grid_delete">
207 <a href="${edit_url}"
209 <a href="${edit_url}"
208 class="btn btn-link btn-danger delete_integration_entry"
210 class="btn btn-link btn-danger delete_integration_entry"
209 data-desc="${integration.name}"
211 data-desc="${integration.name}"
210 data-uid="${integration.integration_id}">
212 data-uid="${integration.integration_id}">
211 ${_('Delete')}
213 ${_('Delete')}
212 </a>
214 </a>
213 </div>
215 </div>
214 %endif
216 %endif
215 </td>
217 </td>
216 </tr>
218 </tr>
217 %endfor
219 %endfor
218 <tr id="last-row"></tr>
220 <tr id="last-row"></tr>
219 </tbody>
221 </tbody>
220 </table>
222 </table>
221 <div class="integrations-paginator">
223 <div class="integrations-paginator">
222 <div class="pagination-wh pagination-left">
224 <div class="pagination-wh pagination-left">
223 ${integrations_list.pager('$link_previous ~2~ $link_next')}
225 ${c.integrations_list.pager('$link_previous ~2~ $link_next')}
224 </div>
226 </div>
225 </div>
227 </div>
226 </div>
228 </div>
227 </div>
229 </div>
228 <script type="text/javascript">
230 <script type="text/javascript">
229 var delete_integration = function(entry) {
231 var delete_integration = function(entry) {
230 if (confirm("Confirm to remove this integration: "+$(entry).data('desc'))) {
232 if (confirm("Confirm to remove this integration: "+$(entry).data('desc'))) {
231 var request = $.ajax({
233 var request = $.ajax({
232 type: "POST",
234 type: "POST",
233 url: $(entry).attr('href'),
235 url: $(entry).attr('href'),
234 data: {
236 data: {
235 'delete': 'delete',
237 'delete': 'delete',
236 'csrf_token': CSRF_TOKEN
238 'csrf_token': CSRF_TOKEN
237 },
239 },
238 success: function(){
240 success: function(){
239 location.reload();
241 location.reload();
240 },
242 },
241 error: function(data, textStatus, errorThrown){
243 error: function(data, textStatus, errorThrown){
242 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
244 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
243 }
245 }
244 });
246 });
245 };
247 };
246 };
248 };
247
249
248 $('.delete_integration_entry').on('click', function(e){
250 $('.delete_integration_entry').on('click', function(e){
249 e.preventDefault();
251 e.preventDefault();
250 delete_integration(this);
252 delete_integration(this);
251 });
253 });
252 </script> No newline at end of file
254 </script>
@@ -1,66 +1,66 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3 <%namespace name="widgets" file="/widgets.mako"/>
3 <%namespace name="widgets" file="/widgets.mako"/>
4
4
5 <%def name="breadcrumbs_links()">
5 <%def name="breadcrumbs_links()">
6 %if c.repo:
6 %if c.repo:
7 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
8 &raquo;
8 &raquo;
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
10 %elif c.repo_group:
10 %elif c.repo_group:
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
13 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
14 &raquo;
14 &raquo;
15 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
15 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
18 %else:
18 %else:
19 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
19 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
20 &raquo;
20 &raquo;
21 ${h.link_to(_('Settings'),h.url('admin_settings'))}
21 ${h.link_to(_('Settings'),h.url('admin_settings'))}
22 &raquo;
22 &raquo;
23 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
23 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
24 %endif
24 %endif
25 &raquo;
25 &raquo;
26 ${_('Create new integration')}
26 ${_('Create new integration')}
27 </%def>
27 </%def>
28 <%widgets:panel class_='integrations'>
28 <%widgets:panel class_='integrations'>
29 <%def name="title()">
29 <%def name="title()">
30 %if c.repo:
30 %if c.repo:
31 ${_('Create New Integration for repository: {repo_name}').format(repo_name=c.repo.repo_name)}
31 ${_('Create New Integration for repository: {repo_name}').format(repo_name=c.repo.repo_name)}
32 %elif c.repo_group:
32 %elif c.repo_group:
33 ${_('Create New Integration for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
33 ${_('Create New Integration for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
34 %else:
34 %else:
35 ${_('Create New Global Integration')}
35 ${_('Create New Global Integration')}
36 %endif
36 %endif
37 </%def>
37 </%def>
38
38
39 %for integration, IntegrationType in available_integrations.items():
39 %for integration, IntegrationType in c.available_integrations.items():
40 <%
40 <%
41 if c.repo:
41 if c.repo:
42 create_url = request.route_path('repo_integrations_create',
42 create_url = request.route_path('repo_integrations_create',
43 repo_name=c.repo.repo_name,
43 repo_name=c.repo.repo_name,
44 integration=integration)
44 integration=integration)
45 elif c.repo_group:
45 elif c.repo_group:
46 create_url = request.route_path('repo_group_integrations_create',
46 create_url = request.route_path('repo_group_integrations_create',
47 repo_group_name=c.repo_group.group_name,
47 repo_group_name=c.repo_group.group_name,
48 integration=integration)
48 integration=integration)
49 else:
49 else:
50 create_url = request.route_path('global_integrations_create',
50 create_url = request.route_path('global_integrations_create',
51 integration=integration)
51 integration=integration)
52 %>
52 %>
53 <a href="${create_url}" class="integration-box">
53 <a href="${create_url}" class="integration-box">
54 <%widgets:panel>
54 <%widgets:panel>
55 <h2>
55 <h2>
56 <div class="integration-icon">
56 <div class="integration-icon">
57 ${IntegrationType.icon|n}
57 ${IntegrationType.icon|n}
58 </div>
58 </div>
59 ${IntegrationType.display_name}
59 ${IntegrationType.display_name}
60 </h2>
60 </h2>
61 ${IntegrationType.description or _('No description available')}
61 ${IntegrationType.description or _('No description available')}
62 </%widgets:panel>
62 </%widgets:panel>
63 </a>
63 </a>
64 %endfor
64 %endfor
65 <div style="clear:both"></div>
65 <div style="clear:both"></div>
66 </%widgets:panel>
66 </%widgets:panel>
@@ -1,222 +1,216 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import time
21 import time
22 import pytest
22 import pytest
23
23
24 from rhodecode import events
24 from rhodecode import events
25 from rhodecode.tests.fixture import Fixture
25 from rhodecode.tests.fixture import Fixture
26 from rhodecode.model.db import Session, Integration
26 from rhodecode.model.db import Session, Integration
27 from rhodecode.model.integration import IntegrationModel
27 from rhodecode.model.integration import IntegrationModel
28 from rhodecode.integrations.types.base import IntegrationTypeBase
29
28
30
29
31 class TestDeleteScopesDeletesIntegrations(object):
30 class TestDeleteScopesDeletesIntegrations(object):
32 def test_delete_repo_with_integration_deletes_integration(self,
31 def test_delete_repo_with_integration_deletes_integration(
33 repo_integration_stub):
32 self, repo_integration_stub):
33
34 Session().delete(repo_integration_stub.repo)
34 Session().delete(repo_integration_stub.repo)
35 Session().commit()
35 Session().commit()
36 Session().expire_all()
36 Session().expire_all()
37 integration = Integration.get(repo_integration_stub.integration_id)
37 integration = Integration.get(repo_integration_stub.integration_id)
38 assert integration is None
38 assert integration is None
39
39
40 def test_delete_repo_group_with_integration_deletes_integration(
41 self, repogroup_integration_stub):
40
42
41 def test_delete_repo_group_with_integration_deletes_integration(self,
42 repogroup_integration_stub):
43 Session().delete(repogroup_integration_stub.repo_group)
43 Session().delete(repogroup_integration_stub.repo_group)
44 Session().commit()
44 Session().commit()
45 Session().expire_all()
45 Session().expire_all()
46 integration = Integration.get(repogroup_integration_stub.integration_id)
46 integration = Integration.get(repogroup_integration_stub.integration_id)
47 assert integration is None
47 assert integration is None
48
48
49
49
50 @pytest.fixture
50 @pytest.fixture
51 def integration_repos(request, StubIntegrationType, stub_integration_settings):
51 def integration_repos(request, StubIntegrationType, stub_integration_settings):
52 """
52 """
53 Create repositories and integrations for testing, and destroy them after
53 Create repositories and integrations for testing, and destroy them after
54
54
55 Structure:
55 Structure:
56 root_repo
56 root_repo
57 parent_group/
57 parent_group/
58 parent_repo
58 parent_repo
59 child_group/
59 child_group/
60 child_repo
60 child_repo
61 other_group/
61 other_group/
62 other_repo
62 other_repo
63 """
63 """
64 fixture = Fixture()
64 fixture = Fixture()
65
65
66
66
67 parent_group_id = 'int_test_parent_group_%s' % time.time()
67 parent_group_id = 'int_test_parent_group_%s' % time.time()
68 parent_group = fixture.create_repo_group(parent_group_id)
68 parent_group = fixture.create_repo_group(parent_group_id)
69
69
70 other_group_id = 'int_test_other_group_%s' % time.time()
70 other_group_id = 'int_test_other_group_%s' % time.time()
71 other_group = fixture.create_repo_group(other_group_id)
71 other_group = fixture.create_repo_group(other_group_id)
72
72
73 child_group_id = (
73 child_group_id = (
74 parent_group_id + '/' + 'int_test_child_group_%s' % time.time())
74 parent_group_id + '/' + 'int_test_child_group_%s' % time.time())
75 child_group = fixture.create_repo_group(child_group_id)
75 child_group = fixture.create_repo_group(child_group_id)
76
76
77 parent_repo_id = 'int_test_parent_repo_%s' % time.time()
77 parent_repo_id = 'int_test_parent_repo_%s' % time.time()
78 parent_repo = fixture.create_repo(parent_repo_id, repo_group=parent_group)
78 parent_repo = fixture.create_repo(parent_repo_id, repo_group=parent_group)
79
79
80 child_repo_id = 'int_test_child_repo_%s' % time.time()
80 child_repo_id = 'int_test_child_repo_%s' % time.time()
81 child_repo = fixture.create_repo(child_repo_id, repo_group=child_group)
81 child_repo = fixture.create_repo(child_repo_id, repo_group=child_group)
82
82
83 other_repo_id = 'int_test_other_repo_%s' % time.time()
83 other_repo_id = 'int_test_other_repo_%s' % time.time()
84 other_repo = fixture.create_repo(other_repo_id, repo_group=other_group)
84 other_repo = fixture.create_repo(other_repo_id, repo_group=other_group)
85
85
86 root_repo_id = 'int_test_repo_root_%s' % time.time()
86 root_repo_id = 'int_test_repo_root_%s' % time.time()
87 root_repo = fixture.create_repo(root_repo_id)
87 root_repo = fixture.create_repo(root_repo_id)
88
88
89 integrations = {}
89 integrations = {}
90 for name, repo, repo_group, child_repos_only in [
90 for name, repo, repo_group, child_repos_only in [
91 ('global', None, None, None),
91 ('global', None, None, None),
92 ('root_repos', None, None, True),
92 ('root_repos', None, None, True),
93 ('parent_repo', parent_repo, None, None),
93 ('parent_repo', parent_repo, None, None),
94 ('child_repo', child_repo, None, None),
94 ('child_repo', child_repo, None, None),
95 ('other_repo', other_repo, None, None),
95 ('other_repo', other_repo, None, None),
96 ('root_repo', root_repo, None, None),
96 ('root_repo', root_repo, None, None),
97 ('parent_group', None, parent_group, True),
97 ('parent_group', None, parent_group, True),
98 ('parent_group_recursive', None, parent_group, False),
98 ('parent_group_recursive', None, parent_group, False),
99 ('child_group', None, child_group, True),
99 ('child_group', None, child_group, True),
100 ('child_group_recursive', None, child_group, False),
100 ('child_group_recursive', None, child_group, False),
101 ('other_group', None, other_group, True),
101 ('other_group', None, other_group, True),
102 ('other_group_recursive', None, other_group, False),
102 ('other_group_recursive', None, other_group, False),
103 ]:
103 ]:
104 integrations[name] = IntegrationModel().create(
104 integrations[name] = IntegrationModel().create(
105 StubIntegrationType, settings=stub_integration_settings,
105 StubIntegrationType, settings=stub_integration_settings,
106 enabled=True, name='test %s integration' % name,
106 enabled=True, name='test %s integration' % name,
107 repo=repo, repo_group=repo_group, child_repos_only=child_repos_only)
107 repo=repo, repo_group=repo_group, child_repos_only=child_repos_only)
108
108
109 Session().commit()
109 Session().commit()
110
110
111 def _cleanup():
111 def _cleanup():
112 for integration in integrations.values():
112 for integration in integrations.values():
113 Session.delete(integration)
113 Session.delete(integration)
114
114
115 fixture.destroy_repo(root_repo)
115 fixture.destroy_repo(root_repo)
116 fixture.destroy_repo(child_repo)
116 fixture.destroy_repo(child_repo)
117 fixture.destroy_repo(parent_repo)
117 fixture.destroy_repo(parent_repo)
118 fixture.destroy_repo(other_repo)
118 fixture.destroy_repo(other_repo)
119 fixture.destroy_repo_group(child_group)
119 fixture.destroy_repo_group(child_group)
120 fixture.destroy_repo_group(parent_group)
120 fixture.destroy_repo_group(parent_group)
121 fixture.destroy_repo_group(other_group)
121 fixture.destroy_repo_group(other_group)
122
122
123 request.addfinalizer(_cleanup)
123 request.addfinalizer(_cleanup)
124
124
125 return {
125 return {
126 'integrations': integrations,
126 'integrations': integrations,
127 'repos': {
127 'repos': {
128 'root_repo': root_repo,
128 'root_repo': root_repo,
129 'other_repo': other_repo,
129 'other_repo': other_repo,
130 'parent_repo': parent_repo,
130 'parent_repo': parent_repo,
131 'child_repo': child_repo,
131 'child_repo': child_repo,
132 }
132 }
133 }
133 }
134
134
135
135
136 def test_enabled_integration_repo_scopes(integration_repos):
136 def test_enabled_integration_repo_scopes(integration_repos):
137 integrations = integration_repos['integrations']
137 integrations = integration_repos['integrations']
138 repos = integration_repos['repos']
138 repos = integration_repos['repos']
139
139
140 triggered_integrations = IntegrationModel().get_for_event(
140 triggered_integrations = IntegrationModel().get_for_event(
141 events.RepoEvent(repos['root_repo']))
141 events.RepoEvent(repos['root_repo']))
142
142
143 assert triggered_integrations == [
143 assert triggered_integrations == [
144 integrations['global'],
144 integrations['global'],
145 integrations['root_repos'],
145 integrations['root_repos'],
146 integrations['root_repo'],
146 integrations['root_repo'],
147 ]
147 ]
148
148
149
150 triggered_integrations = IntegrationModel().get_for_event(
149 triggered_integrations = IntegrationModel().get_for_event(
151 events.RepoEvent(repos['other_repo']))
150 events.RepoEvent(repos['other_repo']))
152
151
153 assert triggered_integrations == [
152 assert triggered_integrations == [
154 integrations['global'],
153 integrations['global'],
155 integrations['other_repo'],
154 integrations['other_repo'],
156 integrations['other_group'],
155 integrations['other_group'],
157 integrations['other_group_recursive'],
156 integrations['other_group_recursive'],
158 ]
157 ]
159
158
160
161 triggered_integrations = IntegrationModel().get_for_event(
159 triggered_integrations = IntegrationModel().get_for_event(
162 events.RepoEvent(repos['parent_repo']))
160 events.RepoEvent(repos['parent_repo']))
163
161
164 assert triggered_integrations == [
162 assert triggered_integrations == [
165 integrations['global'],
163 integrations['global'],
166 integrations['parent_repo'],
164 integrations['parent_repo'],
167 integrations['parent_group'],
165 integrations['parent_group'],
168 integrations['parent_group_recursive'],
166 integrations['parent_group_recursive'],
169 ]
167 ]
170
168
171 triggered_integrations = IntegrationModel().get_for_event(
169 triggered_integrations = IntegrationModel().get_for_event(
172 events.RepoEvent(repos['child_repo']))
170 events.RepoEvent(repos['child_repo']))
173
171
174 assert triggered_integrations == [
172 assert triggered_integrations == [
175 integrations['global'],
173 integrations['global'],
176 integrations['child_repo'],
174 integrations['child_repo'],
177 integrations['parent_group_recursive'],
175 integrations['parent_group_recursive'],
178 integrations['child_group'],
176 integrations['child_group'],
179 integrations['child_group_recursive'],
177 integrations['child_group_recursive'],
180 ]
178 ]
181
179
182
180
183 def test_disabled_integration_repo_scopes(integration_repos):
181 def test_disabled_integration_repo_scopes(integration_repos):
184 integrations = integration_repos['integrations']
182 integrations = integration_repos['integrations']
185 repos = integration_repos['repos']
183 repos = integration_repos['repos']
186
184
187 for integration in integrations.values():
185 for integration in integrations.values():
188 integration.enabled = False
186 integration.enabled = False
189 Session().commit()
187 Session().commit()
190
188
191 triggered_integrations = IntegrationModel().get_for_event(
189 triggered_integrations = IntegrationModel().get_for_event(
192 events.RepoEvent(repos['root_repo']))
190 events.RepoEvent(repos['root_repo']))
193
191
194 assert triggered_integrations == []
192 assert triggered_integrations == []
195
193
196
197 triggered_integrations = IntegrationModel().get_for_event(
194 triggered_integrations = IntegrationModel().get_for_event(
198 events.RepoEvent(repos['parent_repo']))
195 events.RepoEvent(repos['parent_repo']))
199
196
200 assert triggered_integrations == []
197 assert triggered_integrations == []
201
198
202
203 triggered_integrations = IntegrationModel().get_for_event(
199 triggered_integrations = IntegrationModel().get_for_event(
204 events.RepoEvent(repos['child_repo']))
200 events.RepoEvent(repos['child_repo']))
205
201
206 assert triggered_integrations == []
202 assert triggered_integrations == []
207
203
208
209 triggered_integrations = IntegrationModel().get_for_event(
204 triggered_integrations = IntegrationModel().get_for_event(
210 events.RepoEvent(repos['other_repo']))
205 events.RepoEvent(repos['other_repo']))
211
206
212 assert triggered_integrations == []
207 assert triggered_integrations == []
213
208
214
209
215
216 def test_enabled_non_repo_integrations(integration_repos):
210 def test_enabled_non_repo_integrations(integration_repos):
217 integrations = integration_repos['integrations']
211 integrations = integration_repos['integrations']
218
212
219 triggered_integrations = IntegrationModel().get_for_event(
213 triggered_integrations = IntegrationModel().get_for_event(
220 events.UserPreCreate({}))
214 events.UserPreCreate({}))
221
215
222 assert triggered_integrations == [integrations['global']]
216 assert triggered_integrations == [integrations['global']]
@@ -1,1832 +1,1832 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import collections
21 import collections
22 import datetime
22 import datetime
23 import hashlib
23 import hashlib
24 import os
24 import os
25 import re
25 import re
26 import pprint
26 import pprint
27 import shutil
27 import shutil
28 import socket
28 import socket
29 import subprocess32
29 import subprocess32
30 import time
30 import time
31 import uuid
31 import uuid
32 import dateutil.tz
32 import dateutil.tz
33
33
34 import mock
34 import mock
35 import pyramid.testing
35 import pyramid.testing
36 import pytest
36 import pytest
37 import colander
37 import colander
38 import requests
38 import requests
39
39
40 import rhodecode
40 import rhodecode
41 from rhodecode.lib.utils2 import AttributeDict
41 from rhodecode.lib.utils2 import AttributeDict
42 from rhodecode.model.changeset_status import ChangesetStatusModel
42 from rhodecode.model.changeset_status import ChangesetStatusModel
43 from rhodecode.model.comment import CommentsModel
43 from rhodecode.model.comment import CommentsModel
44 from rhodecode.model.db import (
44 from rhodecode.model.db import (
45 PullRequest, Repository, RhodeCodeSetting, ChangesetStatus, RepoGroup,
45 PullRequest, Repository, RhodeCodeSetting, ChangesetStatus, RepoGroup,
46 UserGroup, RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi)
46 UserGroup, RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi)
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.model.pull_request import PullRequestModel
48 from rhodecode.model.pull_request import PullRequestModel
49 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo_group import RepoGroupModel
50 from rhodecode.model.repo_group import RepoGroupModel
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52 from rhodecode.model.settings import VcsSettingsModel
52 from rhodecode.model.settings import VcsSettingsModel
53 from rhodecode.model.user_group import UserGroupModel
53 from rhodecode.model.user_group import UserGroupModel
54 from rhodecode.model.integration import IntegrationModel
54 from rhodecode.model.integration import IntegrationModel
55 from rhodecode.integrations import integration_type_registry
55 from rhodecode.integrations import integration_type_registry
56 from rhodecode.integrations.types.base import IntegrationTypeBase
56 from rhodecode.integrations.types.base import IntegrationTypeBase
57 from rhodecode.lib.utils import repo2db_mapper
57 from rhodecode.lib.utils import repo2db_mapper
58 from rhodecode.lib.vcs import create_vcsserver_proxy
58 from rhodecode.lib.vcs import create_vcsserver_proxy
59 from rhodecode.lib.vcs.backends import get_backend
59 from rhodecode.lib.vcs.backends import get_backend
60 from rhodecode.lib.vcs.nodes import FileNode
60 from rhodecode.lib.vcs.nodes import FileNode
61 from rhodecode.tests import (
61 from rhodecode.tests import (
62 login_user_session, get_new_dir, utils, TESTS_TMP_PATH,
62 login_user_session, get_new_dir, utils, TESTS_TMP_PATH,
63 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR2_LOGIN,
63 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR2_LOGIN,
64 TEST_USER_REGULAR_PASS)
64 TEST_USER_REGULAR_PASS)
65 from rhodecode.tests.utils import CustomTestApp, set_anonymous_access, add_test_routes
65 from rhodecode.tests.utils import CustomTestApp, set_anonymous_access, add_test_routes
66 from rhodecode.tests.fixture import Fixture
66 from rhodecode.tests.fixture import Fixture
67
67
68
68
69 def _split_comma(value):
69 def _split_comma(value):
70 return value.split(',')
70 return value.split(',')
71
71
72
72
73 def pytest_addoption(parser):
73 def pytest_addoption(parser):
74 parser.addoption(
74 parser.addoption(
75 '--keep-tmp-path', action='store_true',
75 '--keep-tmp-path', action='store_true',
76 help="Keep the test temporary directories")
76 help="Keep the test temporary directories")
77 parser.addoption(
77 parser.addoption(
78 '--backends', action='store', type=_split_comma,
78 '--backends', action='store', type=_split_comma,
79 default=['git', 'hg', 'svn'],
79 default=['git', 'hg', 'svn'],
80 help="Select which backends to test for backend specific tests.")
80 help="Select which backends to test for backend specific tests.")
81 parser.addoption(
81 parser.addoption(
82 '--dbs', action='store', type=_split_comma,
82 '--dbs', action='store', type=_split_comma,
83 default=['sqlite'],
83 default=['sqlite'],
84 help="Select which database to test for database specific tests. "
84 help="Select which database to test for database specific tests. "
85 "Possible options are sqlite,postgres,mysql")
85 "Possible options are sqlite,postgres,mysql")
86 parser.addoption(
86 parser.addoption(
87 '--appenlight', '--ae', action='store_true',
87 '--appenlight', '--ae', action='store_true',
88 help="Track statistics in appenlight.")
88 help="Track statistics in appenlight.")
89 parser.addoption(
89 parser.addoption(
90 '--appenlight-api-key', '--ae-key',
90 '--appenlight-api-key', '--ae-key',
91 help="API key for Appenlight.")
91 help="API key for Appenlight.")
92 parser.addoption(
92 parser.addoption(
93 '--appenlight-url', '--ae-url',
93 '--appenlight-url', '--ae-url',
94 default="https://ae.rhodecode.com",
94 default="https://ae.rhodecode.com",
95 help="Appenlight service URL, defaults to https://ae.rhodecode.com")
95 help="Appenlight service URL, defaults to https://ae.rhodecode.com")
96 parser.addoption(
96 parser.addoption(
97 '--sqlite-connection-string', action='store',
97 '--sqlite-connection-string', action='store',
98 default='', help="Connection string for the dbs tests with SQLite")
98 default='', help="Connection string for the dbs tests with SQLite")
99 parser.addoption(
99 parser.addoption(
100 '--postgres-connection-string', action='store',
100 '--postgres-connection-string', action='store',
101 default='', help="Connection string for the dbs tests with Postgres")
101 default='', help="Connection string for the dbs tests with Postgres")
102 parser.addoption(
102 parser.addoption(
103 '--mysql-connection-string', action='store',
103 '--mysql-connection-string', action='store',
104 default='', help="Connection string for the dbs tests with MySQL")
104 default='', help="Connection string for the dbs tests with MySQL")
105 parser.addoption(
105 parser.addoption(
106 '--repeat', type=int, default=100,
106 '--repeat', type=int, default=100,
107 help="Number of repetitions in performance tests.")
107 help="Number of repetitions in performance tests.")
108
108
109
109
110 def pytest_configure(config):
110 def pytest_configure(config):
111 # Appy the kombu patch early on, needed for test discovery on Python 2.7.11
111 # Appy the kombu patch early on, needed for test discovery on Python 2.7.11
112 from rhodecode.config import patches
112 from rhodecode.config import patches
113 patches.kombu_1_5_1_python_2_7_11()
113 patches.kombu_1_5_1_python_2_7_11()
114
114
115
115
116 def pytest_collection_modifyitems(session, config, items):
116 def pytest_collection_modifyitems(session, config, items):
117 # nottest marked, compare nose, used for transition from nose to pytest
117 # nottest marked, compare nose, used for transition from nose to pytest
118 remaining = [
118 remaining = [
119 i for i in items if getattr(i.obj, '__test__', True)]
119 i for i in items if getattr(i.obj, '__test__', True)]
120 items[:] = remaining
120 items[:] = remaining
121
121
122
122
123 def pytest_generate_tests(metafunc):
123 def pytest_generate_tests(metafunc):
124 # Support test generation based on --backend parameter
124 # Support test generation based on --backend parameter
125 if 'backend_alias' in metafunc.fixturenames:
125 if 'backend_alias' in metafunc.fixturenames:
126 backends = get_backends_from_metafunc(metafunc)
126 backends = get_backends_from_metafunc(metafunc)
127 scope = None
127 scope = None
128 if not backends:
128 if not backends:
129 pytest.skip("Not enabled for any of selected backends")
129 pytest.skip("Not enabled for any of selected backends")
130 metafunc.parametrize('backend_alias', backends, scope=scope)
130 metafunc.parametrize('backend_alias', backends, scope=scope)
131 elif hasattr(metafunc.function, 'backends'):
131 elif hasattr(metafunc.function, 'backends'):
132 backends = get_backends_from_metafunc(metafunc)
132 backends = get_backends_from_metafunc(metafunc)
133 if not backends:
133 if not backends:
134 pytest.skip("Not enabled for any of selected backends")
134 pytest.skip("Not enabled for any of selected backends")
135
135
136
136
137 def get_backends_from_metafunc(metafunc):
137 def get_backends_from_metafunc(metafunc):
138 requested_backends = set(metafunc.config.getoption('--backends'))
138 requested_backends = set(metafunc.config.getoption('--backends'))
139 if hasattr(metafunc.function, 'backends'):
139 if hasattr(metafunc.function, 'backends'):
140 # Supported backends by this test function, created from
140 # Supported backends by this test function, created from
141 # pytest.mark.backends
141 # pytest.mark.backends
142 backends = metafunc.function.backends.args
142 backends = metafunc.function.backends.args
143 elif hasattr(metafunc.cls, 'backend_alias'):
143 elif hasattr(metafunc.cls, 'backend_alias'):
144 # Support class attribute "backend_alias", this is mainly
144 # Support class attribute "backend_alias", this is mainly
145 # for legacy reasons for tests not yet using pytest.mark.backends
145 # for legacy reasons for tests not yet using pytest.mark.backends
146 backends = [metafunc.cls.backend_alias]
146 backends = [metafunc.cls.backend_alias]
147 else:
147 else:
148 backends = metafunc.config.getoption('--backends')
148 backends = metafunc.config.getoption('--backends')
149 return requested_backends.intersection(backends)
149 return requested_backends.intersection(backends)
150
150
151
151
152 @pytest.fixture(scope='session', autouse=True)
152 @pytest.fixture(scope='session', autouse=True)
153 def activate_example_rcextensions(request):
153 def activate_example_rcextensions(request):
154 """
154 """
155 Patch in an example rcextensions module which verifies passed in kwargs.
155 Patch in an example rcextensions module which verifies passed in kwargs.
156 """
156 """
157 from rhodecode.tests.other import example_rcextensions
157 from rhodecode.tests.other import example_rcextensions
158
158
159 old_extensions = rhodecode.EXTENSIONS
159 old_extensions = rhodecode.EXTENSIONS
160 rhodecode.EXTENSIONS = example_rcextensions
160 rhodecode.EXTENSIONS = example_rcextensions
161
161
162 @request.addfinalizer
162 @request.addfinalizer
163 def cleanup():
163 def cleanup():
164 rhodecode.EXTENSIONS = old_extensions
164 rhodecode.EXTENSIONS = old_extensions
165
165
166
166
167 @pytest.fixture
167 @pytest.fixture
168 def capture_rcextensions():
168 def capture_rcextensions():
169 """
169 """
170 Returns the recorded calls to entry points in rcextensions.
170 Returns the recorded calls to entry points in rcextensions.
171 """
171 """
172 calls = rhodecode.EXTENSIONS.calls
172 calls = rhodecode.EXTENSIONS.calls
173 calls.clear()
173 calls.clear()
174 # Note: At this moment, it is still the empty dict, but that will
174 # Note: At this moment, it is still the empty dict, but that will
175 # be filled during the test run and since it is a reference this
175 # be filled during the test run and since it is a reference this
176 # is enough to make it work.
176 # is enough to make it work.
177 return calls
177 return calls
178
178
179
179
180 @pytest.fixture(scope='session')
180 @pytest.fixture(scope='session')
181 def http_environ_session():
181 def http_environ_session():
182 """
182 """
183 Allow to use "http_environ" in session scope.
183 Allow to use "http_environ" in session scope.
184 """
184 """
185 return http_environ(
185 return http_environ(
186 http_host_stub=http_host_stub())
186 http_host_stub=http_host_stub())
187
187
188
188
189 @pytest.fixture
189 @pytest.fixture
190 def http_host_stub():
190 def http_host_stub():
191 """
191 """
192 Value of HTTP_HOST in the test run.
192 Value of HTTP_HOST in the test run.
193 """
193 """
194 return 'example.com:80'
194 return 'example.com:80'
195
195
196
196
197 @pytest.fixture
197 @pytest.fixture
198 def http_host_only_stub():
198 def http_host_only_stub():
199 """
199 """
200 Value of HTTP_HOST in the test run.
200 Value of HTTP_HOST in the test run.
201 """
201 """
202 return http_host_stub().split(':')[0]
202 return http_host_stub().split(':')[0]
203
203
204
204
205 @pytest.fixture
205 @pytest.fixture
206 def http_environ(http_host_stub):
206 def http_environ(http_host_stub):
207 """
207 """
208 HTTP extra environ keys.
208 HTTP extra environ keys.
209
209
210 User by the test application and as well for setting up the pylons
210 User by the test application and as well for setting up the pylons
211 environment. In the case of the fixture "app" it should be possible
211 environment. In the case of the fixture "app" it should be possible
212 to override this for a specific test case.
212 to override this for a specific test case.
213 """
213 """
214 return {
214 return {
215 'SERVER_NAME': http_host_only_stub(),
215 'SERVER_NAME': http_host_only_stub(),
216 'SERVER_PORT': http_host_stub.split(':')[1],
216 'SERVER_PORT': http_host_stub.split(':')[1],
217 'HTTP_HOST': http_host_stub,
217 'HTTP_HOST': http_host_stub,
218 'HTTP_USER_AGENT': 'rc-test-agent',
218 'HTTP_USER_AGENT': 'rc-test-agent',
219 'REQUEST_METHOD': 'GET'
219 'REQUEST_METHOD': 'GET'
220 }
220 }
221
221
222
222
223 @pytest.fixture(scope='function')
223 @pytest.fixture(scope='function')
224 def app(request, config_stub, pylonsapp, http_environ):
224 def app(request, config_stub, pylonsapp, http_environ):
225 app = CustomTestApp(
225 app = CustomTestApp(
226 pylonsapp,
226 pylonsapp,
227 extra_environ=http_environ)
227 extra_environ=http_environ)
228 if request.cls:
228 if request.cls:
229 request.cls.app = app
229 request.cls.app = app
230 return app
230 return app
231
231
232
232
233 @pytest.fixture(scope='session')
233 @pytest.fixture(scope='session')
234 def app_settings(pylonsapp, pylons_config):
234 def app_settings(pylonsapp, pylons_config):
235 """
235 """
236 Settings dictionary used to create the app.
236 Settings dictionary used to create the app.
237
237
238 Parses the ini file and passes the result through the sanitize and apply
238 Parses the ini file and passes the result through the sanitize and apply
239 defaults mechanism in `rhodecode.config.middleware`.
239 defaults mechanism in `rhodecode.config.middleware`.
240 """
240 """
241 from paste.deploy.loadwsgi import loadcontext, APP
241 from paste.deploy.loadwsgi import loadcontext, APP
242 from rhodecode.config.middleware import (
242 from rhodecode.config.middleware import (
243 sanitize_settings_and_apply_defaults)
243 sanitize_settings_and_apply_defaults)
244 context = loadcontext(APP, 'config:' + pylons_config)
244 context = loadcontext(APP, 'config:' + pylons_config)
245 settings = sanitize_settings_and_apply_defaults(context.config())
245 settings = sanitize_settings_and_apply_defaults(context.config())
246 return settings
246 return settings
247
247
248
248
249 @pytest.fixture(scope='session')
249 @pytest.fixture(scope='session')
250 def db(app_settings):
250 def db(app_settings):
251 """
251 """
252 Initializes the database connection.
252 Initializes the database connection.
253
253
254 It uses the same settings which are used to create the ``pylonsapp`` or
254 It uses the same settings which are used to create the ``pylonsapp`` or
255 ``app`` fixtures.
255 ``app`` fixtures.
256 """
256 """
257 from rhodecode.config.utils import initialize_database
257 from rhodecode.config.utils import initialize_database
258 initialize_database(app_settings)
258 initialize_database(app_settings)
259
259
260
260
261 LoginData = collections.namedtuple('LoginData', ('csrf_token', 'user'))
261 LoginData = collections.namedtuple('LoginData', ('csrf_token', 'user'))
262
262
263
263
264 def _autologin_user(app, *args):
264 def _autologin_user(app, *args):
265 session = login_user_session(app, *args)
265 session = login_user_session(app, *args)
266 csrf_token = rhodecode.lib.auth.get_csrf_token(session)
266 csrf_token = rhodecode.lib.auth.get_csrf_token(session)
267 return LoginData(csrf_token, session['rhodecode_user'])
267 return LoginData(csrf_token, session['rhodecode_user'])
268
268
269
269
270 @pytest.fixture
270 @pytest.fixture
271 def autologin_user(app):
271 def autologin_user(app):
272 """
272 """
273 Utility fixture which makes sure that the admin user is logged in
273 Utility fixture which makes sure that the admin user is logged in
274 """
274 """
275 return _autologin_user(app)
275 return _autologin_user(app)
276
276
277
277
278 @pytest.fixture
278 @pytest.fixture
279 def autologin_regular_user(app):
279 def autologin_regular_user(app):
280 """
280 """
281 Utility fixture which makes sure that the regular user is logged in
281 Utility fixture which makes sure that the regular user is logged in
282 """
282 """
283 return _autologin_user(
283 return _autologin_user(
284 app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
284 app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
285
285
286
286
287 @pytest.fixture(scope='function')
287 @pytest.fixture(scope='function')
288 def csrf_token(request, autologin_user):
288 def csrf_token(request, autologin_user):
289 return autologin_user.csrf_token
289 return autologin_user.csrf_token
290
290
291
291
292 @pytest.fixture(scope='function')
292 @pytest.fixture(scope='function')
293 def xhr_header(request):
293 def xhr_header(request):
294 return {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
294 return {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
295
295
296
296
297 @pytest.fixture
297 @pytest.fixture
298 def real_crypto_backend(monkeypatch):
298 def real_crypto_backend(monkeypatch):
299 """
299 """
300 Switch the production crypto backend on for this test.
300 Switch the production crypto backend on for this test.
301
301
302 During the test run the crypto backend is replaced with a faster
302 During the test run the crypto backend is replaced with a faster
303 implementation based on the MD5 algorithm.
303 implementation based on the MD5 algorithm.
304 """
304 """
305 monkeypatch.setattr(rhodecode, 'is_test', False)
305 monkeypatch.setattr(rhodecode, 'is_test', False)
306
306
307
307
308 @pytest.fixture(scope='class')
308 @pytest.fixture(scope='class')
309 def index_location(request, pylonsapp):
309 def index_location(request, pylonsapp):
310 index_location = pylonsapp.config['app_conf']['search.location']
310 index_location = pylonsapp.config['app_conf']['search.location']
311 if request.cls:
311 if request.cls:
312 request.cls.index_location = index_location
312 request.cls.index_location = index_location
313 return index_location
313 return index_location
314
314
315
315
316 @pytest.fixture(scope='session', autouse=True)
316 @pytest.fixture(scope='session', autouse=True)
317 def tests_tmp_path(request):
317 def tests_tmp_path(request):
318 """
318 """
319 Create temporary directory to be used during the test session.
319 Create temporary directory to be used during the test session.
320 """
320 """
321 if not os.path.exists(TESTS_TMP_PATH):
321 if not os.path.exists(TESTS_TMP_PATH):
322 os.makedirs(TESTS_TMP_PATH)
322 os.makedirs(TESTS_TMP_PATH)
323
323
324 if not request.config.getoption('--keep-tmp-path'):
324 if not request.config.getoption('--keep-tmp-path'):
325 @request.addfinalizer
325 @request.addfinalizer
326 def remove_tmp_path():
326 def remove_tmp_path():
327 shutil.rmtree(TESTS_TMP_PATH)
327 shutil.rmtree(TESTS_TMP_PATH)
328
328
329 return TESTS_TMP_PATH
329 return TESTS_TMP_PATH
330
330
331
331
332 @pytest.fixture
332 @pytest.fixture
333 def test_repo_group(request):
333 def test_repo_group(request):
334 """
334 """
335 Create a temporary repository group, and destroy it after
335 Create a temporary repository group, and destroy it after
336 usage automatically
336 usage automatically
337 """
337 """
338 fixture = Fixture()
338 fixture = Fixture()
339 repogroupid = 'test_repo_group_%s' % int(time.time())
339 repogroupid = 'test_repo_group_%s' % str(time.time()).replace('.', '')
340 repo_group = fixture.create_repo_group(repogroupid)
340 repo_group = fixture.create_repo_group(repogroupid)
341
341
342 def _cleanup():
342 def _cleanup():
343 fixture.destroy_repo_group(repogroupid)
343 fixture.destroy_repo_group(repogroupid)
344
344
345 request.addfinalizer(_cleanup)
345 request.addfinalizer(_cleanup)
346 return repo_group
346 return repo_group
347
347
348
348
349 @pytest.fixture
349 @pytest.fixture
350 def test_user_group(request):
350 def test_user_group(request):
351 """
351 """
352 Create a temporary user group, and destroy it after
352 Create a temporary user group, and destroy it after
353 usage automatically
353 usage automatically
354 """
354 """
355 fixture = Fixture()
355 fixture = Fixture()
356 usergroupid = 'test_user_group_%s' % int(time.time())
356 usergroupid = 'test_user_group_%s' % str(time.time()).replace('.', '')
357 user_group = fixture.create_user_group(usergroupid)
357 user_group = fixture.create_user_group(usergroupid)
358
358
359 def _cleanup():
359 def _cleanup():
360 fixture.destroy_user_group(user_group)
360 fixture.destroy_user_group(user_group)
361
361
362 request.addfinalizer(_cleanup)
362 request.addfinalizer(_cleanup)
363 return user_group
363 return user_group
364
364
365
365
366 @pytest.fixture(scope='session')
366 @pytest.fixture(scope='session')
367 def test_repo(request):
367 def test_repo(request):
368 container = TestRepoContainer()
368 container = TestRepoContainer()
369 request.addfinalizer(container._cleanup)
369 request.addfinalizer(container._cleanup)
370 return container
370 return container
371
371
372
372
373 class TestRepoContainer(object):
373 class TestRepoContainer(object):
374 """
374 """
375 Container for test repositories which are used read only.
375 Container for test repositories which are used read only.
376
376
377 Repositories will be created on demand and re-used during the lifetime
377 Repositories will be created on demand and re-used during the lifetime
378 of this object.
378 of this object.
379
379
380 Usage to get the svn test repository "minimal"::
380 Usage to get the svn test repository "minimal"::
381
381
382 test_repo = TestContainer()
382 test_repo = TestContainer()
383 repo = test_repo('minimal', 'svn')
383 repo = test_repo('minimal', 'svn')
384
384
385 """
385 """
386
386
387 dump_extractors = {
387 dump_extractors = {
388 'git': utils.extract_git_repo_from_dump,
388 'git': utils.extract_git_repo_from_dump,
389 'hg': utils.extract_hg_repo_from_dump,
389 'hg': utils.extract_hg_repo_from_dump,
390 'svn': utils.extract_svn_repo_from_dump,
390 'svn': utils.extract_svn_repo_from_dump,
391 }
391 }
392
392
393 def __init__(self):
393 def __init__(self):
394 self._cleanup_repos = []
394 self._cleanup_repos = []
395 self._fixture = Fixture()
395 self._fixture = Fixture()
396 self._repos = {}
396 self._repos = {}
397
397
398 def __call__(self, dump_name, backend_alias, config=None):
398 def __call__(self, dump_name, backend_alias, config=None):
399 key = (dump_name, backend_alias)
399 key = (dump_name, backend_alias)
400 if key not in self._repos:
400 if key not in self._repos:
401 repo = self._create_repo(dump_name, backend_alias, config)
401 repo = self._create_repo(dump_name, backend_alias, config)
402 self._repos[key] = repo.repo_id
402 self._repos[key] = repo.repo_id
403 return Repository.get(self._repos[key])
403 return Repository.get(self._repos[key])
404
404
405 def _create_repo(self, dump_name, backend_alias, config):
405 def _create_repo(self, dump_name, backend_alias, config):
406 repo_name = '%s-%s' % (backend_alias, dump_name)
406 repo_name = '%s-%s' % (backend_alias, dump_name)
407 backend_class = get_backend(backend_alias)
407 backend_class = get_backend(backend_alias)
408 dump_extractor = self.dump_extractors[backend_alias]
408 dump_extractor = self.dump_extractors[backend_alias]
409 repo_path = dump_extractor(dump_name, repo_name)
409 repo_path = dump_extractor(dump_name, repo_name)
410
410
411 vcs_repo = backend_class(repo_path, config=config)
411 vcs_repo = backend_class(repo_path, config=config)
412 repo2db_mapper({repo_name: vcs_repo})
412 repo2db_mapper({repo_name: vcs_repo})
413
413
414 repo = RepoModel().get_by_repo_name(repo_name)
414 repo = RepoModel().get_by_repo_name(repo_name)
415 self._cleanup_repos.append(repo_name)
415 self._cleanup_repos.append(repo_name)
416 return repo
416 return repo
417
417
418 def _cleanup(self):
418 def _cleanup(self):
419 for repo_name in reversed(self._cleanup_repos):
419 for repo_name in reversed(self._cleanup_repos):
420 self._fixture.destroy_repo(repo_name)
420 self._fixture.destroy_repo(repo_name)
421
421
422
422
423 @pytest.fixture
423 @pytest.fixture
424 def backend(request, backend_alias, pylonsapp, test_repo):
424 def backend(request, backend_alias, pylonsapp, test_repo):
425 """
425 """
426 Parametrized fixture which represents a single backend implementation.
426 Parametrized fixture which represents a single backend implementation.
427
427
428 It respects the option `--backends` to focus the test run on specific
428 It respects the option `--backends` to focus the test run on specific
429 backend implementations.
429 backend implementations.
430
430
431 It also supports `pytest.mark.xfail_backends` to mark tests as failing
431 It also supports `pytest.mark.xfail_backends` to mark tests as failing
432 for specific backends. This is intended as a utility for incremental
432 for specific backends. This is intended as a utility for incremental
433 development of a new backend implementation.
433 development of a new backend implementation.
434 """
434 """
435 if backend_alias not in request.config.getoption('--backends'):
435 if backend_alias not in request.config.getoption('--backends'):
436 pytest.skip("Backend %s not selected." % (backend_alias, ))
436 pytest.skip("Backend %s not selected." % (backend_alias, ))
437
437
438 utils.check_xfail_backends(request.node, backend_alias)
438 utils.check_xfail_backends(request.node, backend_alias)
439 utils.check_skip_backends(request.node, backend_alias)
439 utils.check_skip_backends(request.node, backend_alias)
440
440
441 repo_name = 'vcs_test_%s' % (backend_alias, )
441 repo_name = 'vcs_test_%s' % (backend_alias, )
442 backend = Backend(
442 backend = Backend(
443 alias=backend_alias,
443 alias=backend_alias,
444 repo_name=repo_name,
444 repo_name=repo_name,
445 test_name=request.node.name,
445 test_name=request.node.name,
446 test_repo_container=test_repo)
446 test_repo_container=test_repo)
447 request.addfinalizer(backend.cleanup)
447 request.addfinalizer(backend.cleanup)
448 return backend
448 return backend
449
449
450
450
451 @pytest.fixture
451 @pytest.fixture
452 def backend_git(request, pylonsapp, test_repo):
452 def backend_git(request, pylonsapp, test_repo):
453 return backend(request, 'git', pylonsapp, test_repo)
453 return backend(request, 'git', pylonsapp, test_repo)
454
454
455
455
456 @pytest.fixture
456 @pytest.fixture
457 def backend_hg(request, pylonsapp, test_repo):
457 def backend_hg(request, pylonsapp, test_repo):
458 return backend(request, 'hg', pylonsapp, test_repo)
458 return backend(request, 'hg', pylonsapp, test_repo)
459
459
460
460
461 @pytest.fixture
461 @pytest.fixture
462 def backend_svn(request, pylonsapp, test_repo):
462 def backend_svn(request, pylonsapp, test_repo):
463 return backend(request, 'svn', pylonsapp, test_repo)
463 return backend(request, 'svn', pylonsapp, test_repo)
464
464
465
465
466 @pytest.fixture
466 @pytest.fixture
467 def backend_random(backend_git):
467 def backend_random(backend_git):
468 """
468 """
469 Use this to express that your tests need "a backend.
469 Use this to express that your tests need "a backend.
470
470
471 A few of our tests need a backend, so that we can run the code. This
471 A few of our tests need a backend, so that we can run the code. This
472 fixture is intended to be used for such cases. It will pick one of the
472 fixture is intended to be used for such cases. It will pick one of the
473 backends and run the tests.
473 backends and run the tests.
474
474
475 The fixture `backend` would run the test multiple times for each
475 The fixture `backend` would run the test multiple times for each
476 available backend which is a pure waste of time if the test is
476 available backend which is a pure waste of time if the test is
477 independent of the backend type.
477 independent of the backend type.
478 """
478 """
479 # TODO: johbo: Change this to pick a random backend
479 # TODO: johbo: Change this to pick a random backend
480 return backend_git
480 return backend_git
481
481
482
482
483 @pytest.fixture
483 @pytest.fixture
484 def backend_stub(backend_git):
484 def backend_stub(backend_git):
485 """
485 """
486 Use this to express that your tests need a backend stub
486 Use this to express that your tests need a backend stub
487
487
488 TODO: mikhail: Implement a real stub logic instead of returning
488 TODO: mikhail: Implement a real stub logic instead of returning
489 a git backend
489 a git backend
490 """
490 """
491 return backend_git
491 return backend_git
492
492
493
493
494 @pytest.fixture
494 @pytest.fixture
495 def repo_stub(backend_stub):
495 def repo_stub(backend_stub):
496 """
496 """
497 Use this to express that your tests need a repository stub
497 Use this to express that your tests need a repository stub
498 """
498 """
499 return backend_stub.create_repo()
499 return backend_stub.create_repo()
500
500
501
501
502 class Backend(object):
502 class Backend(object):
503 """
503 """
504 Represents the test configuration for one supported backend
504 Represents the test configuration for one supported backend
505
505
506 Provides easy access to different test repositories based on
506 Provides easy access to different test repositories based on
507 `__getitem__`. Such repositories will only be created once per test
507 `__getitem__`. Such repositories will only be created once per test
508 session.
508 session.
509 """
509 """
510
510
511 invalid_repo_name = re.compile(r'[^0-9a-zA-Z]+')
511 invalid_repo_name = re.compile(r'[^0-9a-zA-Z]+')
512 _master_repo = None
512 _master_repo = None
513 _commit_ids = {}
513 _commit_ids = {}
514
514
515 def __init__(self, alias, repo_name, test_name, test_repo_container):
515 def __init__(self, alias, repo_name, test_name, test_repo_container):
516 self.alias = alias
516 self.alias = alias
517 self.repo_name = repo_name
517 self.repo_name = repo_name
518 self._cleanup_repos = []
518 self._cleanup_repos = []
519 self._test_name = test_name
519 self._test_name = test_name
520 self._test_repo_container = test_repo_container
520 self._test_repo_container = test_repo_container
521 # TODO: johbo: Used as a delegate interim. Not yet sure if Backend or
521 # TODO: johbo: Used as a delegate interim. Not yet sure if Backend or
522 # Fixture will survive in the end.
522 # Fixture will survive in the end.
523 self._fixture = Fixture()
523 self._fixture = Fixture()
524
524
525 def __getitem__(self, key):
525 def __getitem__(self, key):
526 return self._test_repo_container(key, self.alias)
526 return self._test_repo_container(key, self.alias)
527
527
528 def create_test_repo(self, key, config=None):
528 def create_test_repo(self, key, config=None):
529 return self._test_repo_container(key, self.alias, config)
529 return self._test_repo_container(key, self.alias, config)
530
530
531 @property
531 @property
532 def repo(self):
532 def repo(self):
533 """
533 """
534 Returns the "current" repository. This is the vcs_test repo or the
534 Returns the "current" repository. This is the vcs_test repo or the
535 last repo which has been created with `create_repo`.
535 last repo which has been created with `create_repo`.
536 """
536 """
537 from rhodecode.model.db import Repository
537 from rhodecode.model.db import Repository
538 return Repository.get_by_repo_name(self.repo_name)
538 return Repository.get_by_repo_name(self.repo_name)
539
539
540 @property
540 @property
541 def default_branch_name(self):
541 def default_branch_name(self):
542 VcsRepository = get_backend(self.alias)
542 VcsRepository = get_backend(self.alias)
543 return VcsRepository.DEFAULT_BRANCH_NAME
543 return VcsRepository.DEFAULT_BRANCH_NAME
544
544
545 @property
545 @property
546 def default_head_id(self):
546 def default_head_id(self):
547 """
547 """
548 Returns the default head id of the underlying backend.
548 Returns the default head id of the underlying backend.
549
549
550 This will be the default branch name in case the backend does have a
550 This will be the default branch name in case the backend does have a
551 default branch. In the other cases it will point to a valid head
551 default branch. In the other cases it will point to a valid head
552 which can serve as the base to create a new commit on top of it.
552 which can serve as the base to create a new commit on top of it.
553 """
553 """
554 vcsrepo = self.repo.scm_instance()
554 vcsrepo = self.repo.scm_instance()
555 head_id = (
555 head_id = (
556 vcsrepo.DEFAULT_BRANCH_NAME or
556 vcsrepo.DEFAULT_BRANCH_NAME or
557 vcsrepo.commit_ids[-1])
557 vcsrepo.commit_ids[-1])
558 return head_id
558 return head_id
559
559
560 @property
560 @property
561 def commit_ids(self):
561 def commit_ids(self):
562 """
562 """
563 Returns the list of commits for the last created repository
563 Returns the list of commits for the last created repository
564 """
564 """
565 return self._commit_ids
565 return self._commit_ids
566
566
567 def create_master_repo(self, commits):
567 def create_master_repo(self, commits):
568 """
568 """
569 Create a repository and remember it as a template.
569 Create a repository and remember it as a template.
570
570
571 This allows to easily create derived repositories to construct
571 This allows to easily create derived repositories to construct
572 more complex scenarios for diff, compare and pull requests.
572 more complex scenarios for diff, compare and pull requests.
573
573
574 Returns a commit map which maps from commit message to raw_id.
574 Returns a commit map which maps from commit message to raw_id.
575 """
575 """
576 self._master_repo = self.create_repo(commits=commits)
576 self._master_repo = self.create_repo(commits=commits)
577 return self._commit_ids
577 return self._commit_ids
578
578
579 def create_repo(
579 def create_repo(
580 self, commits=None, number_of_commits=0, heads=None,
580 self, commits=None, number_of_commits=0, heads=None,
581 name_suffix=u'', **kwargs):
581 name_suffix=u'', **kwargs):
582 """
582 """
583 Create a repository and record it for later cleanup.
583 Create a repository and record it for later cleanup.
584
584
585 :param commits: Optional. A sequence of dict instances.
585 :param commits: Optional. A sequence of dict instances.
586 Will add a commit per entry to the new repository.
586 Will add a commit per entry to the new repository.
587 :param number_of_commits: Optional. If set to a number, this number of
587 :param number_of_commits: Optional. If set to a number, this number of
588 commits will be added to the new repository.
588 commits will be added to the new repository.
589 :param heads: Optional. Can be set to a sequence of of commit
589 :param heads: Optional. Can be set to a sequence of of commit
590 names which shall be pulled in from the master repository.
590 names which shall be pulled in from the master repository.
591
591
592 """
592 """
593 self.repo_name = self._next_repo_name() + name_suffix
593 self.repo_name = self._next_repo_name() + name_suffix
594 repo = self._fixture.create_repo(
594 repo = self._fixture.create_repo(
595 self.repo_name, repo_type=self.alias, **kwargs)
595 self.repo_name, repo_type=self.alias, **kwargs)
596 self._cleanup_repos.append(repo.repo_name)
596 self._cleanup_repos.append(repo.repo_name)
597
597
598 commits = commits or [
598 commits = commits or [
599 {'message': 'Commit %s of %s' % (x, self.repo_name)}
599 {'message': 'Commit %s of %s' % (x, self.repo_name)}
600 for x in xrange(number_of_commits)]
600 for x in xrange(number_of_commits)]
601 self._add_commits_to_repo(repo.scm_instance(), commits)
601 self._add_commits_to_repo(repo.scm_instance(), commits)
602 if heads:
602 if heads:
603 self.pull_heads(repo, heads)
603 self.pull_heads(repo, heads)
604
604
605 return repo
605 return repo
606
606
607 def pull_heads(self, repo, heads):
607 def pull_heads(self, repo, heads):
608 """
608 """
609 Make sure that repo contains all commits mentioned in `heads`
609 Make sure that repo contains all commits mentioned in `heads`
610 """
610 """
611 vcsmaster = self._master_repo.scm_instance()
611 vcsmaster = self._master_repo.scm_instance()
612 vcsrepo = repo.scm_instance()
612 vcsrepo = repo.scm_instance()
613 vcsrepo.config.clear_section('hooks')
613 vcsrepo.config.clear_section('hooks')
614 commit_ids = [self._commit_ids[h] for h in heads]
614 commit_ids = [self._commit_ids[h] for h in heads]
615 vcsrepo.pull(vcsmaster.path, commit_ids=commit_ids)
615 vcsrepo.pull(vcsmaster.path, commit_ids=commit_ids)
616
616
617 def create_fork(self):
617 def create_fork(self):
618 repo_to_fork = self.repo_name
618 repo_to_fork = self.repo_name
619 self.repo_name = self._next_repo_name()
619 self.repo_name = self._next_repo_name()
620 repo = self._fixture.create_fork(repo_to_fork, self.repo_name)
620 repo = self._fixture.create_fork(repo_to_fork, self.repo_name)
621 self._cleanup_repos.append(self.repo_name)
621 self._cleanup_repos.append(self.repo_name)
622 return repo
622 return repo
623
623
624 def new_repo_name(self, suffix=u''):
624 def new_repo_name(self, suffix=u''):
625 self.repo_name = self._next_repo_name() + suffix
625 self.repo_name = self._next_repo_name() + suffix
626 self._cleanup_repos.append(self.repo_name)
626 self._cleanup_repos.append(self.repo_name)
627 return self.repo_name
627 return self.repo_name
628
628
629 def _next_repo_name(self):
629 def _next_repo_name(self):
630 return u"%s_%s" % (
630 return u"%s_%s" % (
631 self.invalid_repo_name.sub(u'_', self._test_name),
631 self.invalid_repo_name.sub(u'_', self._test_name),
632 len(self._cleanup_repos))
632 len(self._cleanup_repos))
633
633
634 def ensure_file(self, filename, content='Test content\n'):
634 def ensure_file(self, filename, content='Test content\n'):
635 assert self._cleanup_repos, "Avoid writing into vcs_test repos"
635 assert self._cleanup_repos, "Avoid writing into vcs_test repos"
636 commits = [
636 commits = [
637 {'added': [
637 {'added': [
638 FileNode(filename, content=content),
638 FileNode(filename, content=content),
639 ]},
639 ]},
640 ]
640 ]
641 self._add_commits_to_repo(self.repo.scm_instance(), commits)
641 self._add_commits_to_repo(self.repo.scm_instance(), commits)
642
642
643 def enable_downloads(self):
643 def enable_downloads(self):
644 repo = self.repo
644 repo = self.repo
645 repo.enable_downloads = True
645 repo.enable_downloads = True
646 Session().add(repo)
646 Session().add(repo)
647 Session().commit()
647 Session().commit()
648
648
649 def cleanup(self):
649 def cleanup(self):
650 for repo_name in reversed(self._cleanup_repos):
650 for repo_name in reversed(self._cleanup_repos):
651 self._fixture.destroy_repo(repo_name)
651 self._fixture.destroy_repo(repo_name)
652
652
653 def _add_commits_to_repo(self, repo, commits):
653 def _add_commits_to_repo(self, repo, commits):
654 commit_ids = _add_commits_to_repo(repo, commits)
654 commit_ids = _add_commits_to_repo(repo, commits)
655 if not commit_ids:
655 if not commit_ids:
656 return
656 return
657 self._commit_ids = commit_ids
657 self._commit_ids = commit_ids
658
658
659 # Creating refs for Git to allow fetching them from remote repository
659 # Creating refs for Git to allow fetching them from remote repository
660 if self.alias == 'git':
660 if self.alias == 'git':
661 refs = {}
661 refs = {}
662 for message in self._commit_ids:
662 for message in self._commit_ids:
663 # TODO: mikhail: do more special chars replacements
663 # TODO: mikhail: do more special chars replacements
664 ref_name = 'refs/test-refs/{}'.format(
664 ref_name = 'refs/test-refs/{}'.format(
665 message.replace(' ', ''))
665 message.replace(' ', ''))
666 refs[ref_name] = self._commit_ids[message]
666 refs[ref_name] = self._commit_ids[message]
667 self._create_refs(repo, refs)
667 self._create_refs(repo, refs)
668
668
669 def _create_refs(self, repo, refs):
669 def _create_refs(self, repo, refs):
670 for ref_name in refs:
670 for ref_name in refs:
671 repo.set_refs(ref_name, refs[ref_name])
671 repo.set_refs(ref_name, refs[ref_name])
672
672
673
673
674 @pytest.fixture
674 @pytest.fixture
675 def vcsbackend(request, backend_alias, tests_tmp_path, pylonsapp, test_repo):
675 def vcsbackend(request, backend_alias, tests_tmp_path, pylonsapp, test_repo):
676 """
676 """
677 Parametrized fixture which represents a single vcs backend implementation.
677 Parametrized fixture which represents a single vcs backend implementation.
678
678
679 See the fixture `backend` for more details. This one implements the same
679 See the fixture `backend` for more details. This one implements the same
680 concept, but on vcs level. So it does not provide model instances etc.
680 concept, but on vcs level. So it does not provide model instances etc.
681
681
682 Parameters are generated dynamically, see :func:`pytest_generate_tests`
682 Parameters are generated dynamically, see :func:`pytest_generate_tests`
683 for how this works.
683 for how this works.
684 """
684 """
685 if backend_alias not in request.config.getoption('--backends'):
685 if backend_alias not in request.config.getoption('--backends'):
686 pytest.skip("Backend %s not selected." % (backend_alias, ))
686 pytest.skip("Backend %s not selected." % (backend_alias, ))
687
687
688 utils.check_xfail_backends(request.node, backend_alias)
688 utils.check_xfail_backends(request.node, backend_alias)
689 utils.check_skip_backends(request.node, backend_alias)
689 utils.check_skip_backends(request.node, backend_alias)
690
690
691 repo_name = 'vcs_test_%s' % (backend_alias, )
691 repo_name = 'vcs_test_%s' % (backend_alias, )
692 repo_path = os.path.join(tests_tmp_path, repo_name)
692 repo_path = os.path.join(tests_tmp_path, repo_name)
693 backend = VcsBackend(
693 backend = VcsBackend(
694 alias=backend_alias,
694 alias=backend_alias,
695 repo_path=repo_path,
695 repo_path=repo_path,
696 test_name=request.node.name,
696 test_name=request.node.name,
697 test_repo_container=test_repo)
697 test_repo_container=test_repo)
698 request.addfinalizer(backend.cleanup)
698 request.addfinalizer(backend.cleanup)
699 return backend
699 return backend
700
700
701
701
702 @pytest.fixture
702 @pytest.fixture
703 def vcsbackend_git(request, tests_tmp_path, pylonsapp, test_repo):
703 def vcsbackend_git(request, tests_tmp_path, pylonsapp, test_repo):
704 return vcsbackend(request, 'git', tests_tmp_path, pylonsapp, test_repo)
704 return vcsbackend(request, 'git', tests_tmp_path, pylonsapp, test_repo)
705
705
706
706
707 @pytest.fixture
707 @pytest.fixture
708 def vcsbackend_hg(request, tests_tmp_path, pylonsapp, test_repo):
708 def vcsbackend_hg(request, tests_tmp_path, pylonsapp, test_repo):
709 return vcsbackend(request, 'hg', tests_tmp_path, pylonsapp, test_repo)
709 return vcsbackend(request, 'hg', tests_tmp_path, pylonsapp, test_repo)
710
710
711
711
712 @pytest.fixture
712 @pytest.fixture
713 def vcsbackend_svn(request, tests_tmp_path, pylonsapp, test_repo):
713 def vcsbackend_svn(request, tests_tmp_path, pylonsapp, test_repo):
714 return vcsbackend(request, 'svn', tests_tmp_path, pylonsapp, test_repo)
714 return vcsbackend(request, 'svn', tests_tmp_path, pylonsapp, test_repo)
715
715
716
716
717 @pytest.fixture
717 @pytest.fixture
718 def vcsbackend_random(vcsbackend_git):
718 def vcsbackend_random(vcsbackend_git):
719 """
719 """
720 Use this to express that your tests need "a vcsbackend".
720 Use this to express that your tests need "a vcsbackend".
721
721
722 The fixture `vcsbackend` would run the test multiple times for each
722 The fixture `vcsbackend` would run the test multiple times for each
723 available vcs backend which is a pure waste of time if the test is
723 available vcs backend which is a pure waste of time if the test is
724 independent of the vcs backend type.
724 independent of the vcs backend type.
725 """
725 """
726 # TODO: johbo: Change this to pick a random backend
726 # TODO: johbo: Change this to pick a random backend
727 return vcsbackend_git
727 return vcsbackend_git
728
728
729
729
730 @pytest.fixture
730 @pytest.fixture
731 def vcsbackend_stub(vcsbackend_git):
731 def vcsbackend_stub(vcsbackend_git):
732 """
732 """
733 Use this to express that your test just needs a stub of a vcsbackend.
733 Use this to express that your test just needs a stub of a vcsbackend.
734
734
735 Plan is to eventually implement an in-memory stub to speed tests up.
735 Plan is to eventually implement an in-memory stub to speed tests up.
736 """
736 """
737 return vcsbackend_git
737 return vcsbackend_git
738
738
739
739
740 class VcsBackend(object):
740 class VcsBackend(object):
741 """
741 """
742 Represents the test configuration for one supported vcs backend.
742 Represents the test configuration for one supported vcs backend.
743 """
743 """
744
744
745 invalid_repo_name = re.compile(r'[^0-9a-zA-Z]+')
745 invalid_repo_name = re.compile(r'[^0-9a-zA-Z]+')
746
746
747 def __init__(self, alias, repo_path, test_name, test_repo_container):
747 def __init__(self, alias, repo_path, test_name, test_repo_container):
748 self.alias = alias
748 self.alias = alias
749 self._repo_path = repo_path
749 self._repo_path = repo_path
750 self._cleanup_repos = []
750 self._cleanup_repos = []
751 self._test_name = test_name
751 self._test_name = test_name
752 self._test_repo_container = test_repo_container
752 self._test_repo_container = test_repo_container
753
753
754 def __getitem__(self, key):
754 def __getitem__(self, key):
755 return self._test_repo_container(key, self.alias).scm_instance()
755 return self._test_repo_container(key, self.alias).scm_instance()
756
756
757 @property
757 @property
758 def repo(self):
758 def repo(self):
759 """
759 """
760 Returns the "current" repository. This is the vcs_test repo of the last
760 Returns the "current" repository. This is the vcs_test repo of the last
761 repo which has been created.
761 repo which has been created.
762 """
762 """
763 Repository = get_backend(self.alias)
763 Repository = get_backend(self.alias)
764 return Repository(self._repo_path)
764 return Repository(self._repo_path)
765
765
766 @property
766 @property
767 def backend(self):
767 def backend(self):
768 """
768 """
769 Returns the backend implementation class.
769 Returns the backend implementation class.
770 """
770 """
771 return get_backend(self.alias)
771 return get_backend(self.alias)
772
772
773 def create_repo(self, commits=None, number_of_commits=0, _clone_repo=None):
773 def create_repo(self, commits=None, number_of_commits=0, _clone_repo=None):
774 repo_name = self._next_repo_name()
774 repo_name = self._next_repo_name()
775 self._repo_path = get_new_dir(repo_name)
775 self._repo_path = get_new_dir(repo_name)
776 repo_class = get_backend(self.alias)
776 repo_class = get_backend(self.alias)
777 src_url = None
777 src_url = None
778 if _clone_repo:
778 if _clone_repo:
779 src_url = _clone_repo.path
779 src_url = _clone_repo.path
780 repo = repo_class(self._repo_path, create=True, src_url=src_url)
780 repo = repo_class(self._repo_path, create=True, src_url=src_url)
781 self._cleanup_repos.append(repo)
781 self._cleanup_repos.append(repo)
782
782
783 commits = commits or [
783 commits = commits or [
784 {'message': 'Commit %s of %s' % (x, repo_name)}
784 {'message': 'Commit %s of %s' % (x, repo_name)}
785 for x in xrange(number_of_commits)]
785 for x in xrange(number_of_commits)]
786 _add_commits_to_repo(repo, commits)
786 _add_commits_to_repo(repo, commits)
787 return repo
787 return repo
788
788
789 def clone_repo(self, repo):
789 def clone_repo(self, repo):
790 return self.create_repo(_clone_repo=repo)
790 return self.create_repo(_clone_repo=repo)
791
791
792 def cleanup(self):
792 def cleanup(self):
793 for repo in self._cleanup_repos:
793 for repo in self._cleanup_repos:
794 shutil.rmtree(repo.path)
794 shutil.rmtree(repo.path)
795
795
796 def new_repo_path(self):
796 def new_repo_path(self):
797 repo_name = self._next_repo_name()
797 repo_name = self._next_repo_name()
798 self._repo_path = get_new_dir(repo_name)
798 self._repo_path = get_new_dir(repo_name)
799 return self._repo_path
799 return self._repo_path
800
800
801 def _next_repo_name(self):
801 def _next_repo_name(self):
802 return "%s_%s" % (
802 return "%s_%s" % (
803 self.invalid_repo_name.sub('_', self._test_name),
803 self.invalid_repo_name.sub('_', self._test_name),
804 len(self._cleanup_repos))
804 len(self._cleanup_repos))
805
805
806 def add_file(self, repo, filename, content='Test content\n'):
806 def add_file(self, repo, filename, content='Test content\n'):
807 imc = repo.in_memory_commit
807 imc = repo.in_memory_commit
808 imc.add(FileNode(filename, content=content))
808 imc.add(FileNode(filename, content=content))
809 imc.commit(
809 imc.commit(
810 message=u'Automatic commit from vcsbackend fixture',
810 message=u'Automatic commit from vcsbackend fixture',
811 author=u'Automatic')
811 author=u'Automatic')
812
812
813 def ensure_file(self, filename, content='Test content\n'):
813 def ensure_file(self, filename, content='Test content\n'):
814 assert self._cleanup_repos, "Avoid writing into vcs_test repos"
814 assert self._cleanup_repos, "Avoid writing into vcs_test repos"
815 self.add_file(self.repo, filename, content)
815 self.add_file(self.repo, filename, content)
816
816
817
817
818 def _add_commits_to_repo(vcs_repo, commits):
818 def _add_commits_to_repo(vcs_repo, commits):
819 commit_ids = {}
819 commit_ids = {}
820 if not commits:
820 if not commits:
821 return commit_ids
821 return commit_ids
822
822
823 imc = vcs_repo.in_memory_commit
823 imc = vcs_repo.in_memory_commit
824 commit = None
824 commit = None
825
825
826 for idx, commit in enumerate(commits):
826 for idx, commit in enumerate(commits):
827 message = unicode(commit.get('message', 'Commit %s' % idx))
827 message = unicode(commit.get('message', 'Commit %s' % idx))
828
828
829 for node in commit.get('added', []):
829 for node in commit.get('added', []):
830 imc.add(FileNode(node.path, content=node.content))
830 imc.add(FileNode(node.path, content=node.content))
831 for node in commit.get('changed', []):
831 for node in commit.get('changed', []):
832 imc.change(FileNode(node.path, content=node.content))
832 imc.change(FileNode(node.path, content=node.content))
833 for node in commit.get('removed', []):
833 for node in commit.get('removed', []):
834 imc.remove(FileNode(node.path))
834 imc.remove(FileNode(node.path))
835
835
836 parents = [
836 parents = [
837 vcs_repo.get_commit(commit_id=commit_ids[p])
837 vcs_repo.get_commit(commit_id=commit_ids[p])
838 for p in commit.get('parents', [])]
838 for p in commit.get('parents', [])]
839
839
840 operations = ('added', 'changed', 'removed')
840 operations = ('added', 'changed', 'removed')
841 if not any((commit.get(o) for o in operations)):
841 if not any((commit.get(o) for o in operations)):
842 imc.add(FileNode('file_%s' % idx, content=message))
842 imc.add(FileNode('file_%s' % idx, content=message))
843
843
844 commit = imc.commit(
844 commit = imc.commit(
845 message=message,
845 message=message,
846 author=unicode(commit.get('author', 'Automatic')),
846 author=unicode(commit.get('author', 'Automatic')),
847 date=commit.get('date'),
847 date=commit.get('date'),
848 branch=commit.get('branch'),
848 branch=commit.get('branch'),
849 parents=parents)
849 parents=parents)
850
850
851 commit_ids[commit.message] = commit.raw_id
851 commit_ids[commit.message] = commit.raw_id
852
852
853 return commit_ids
853 return commit_ids
854
854
855
855
856 @pytest.fixture
856 @pytest.fixture
857 def reposerver(request):
857 def reposerver(request):
858 """
858 """
859 Allows to serve a backend repository
859 Allows to serve a backend repository
860 """
860 """
861
861
862 repo_server = RepoServer()
862 repo_server = RepoServer()
863 request.addfinalizer(repo_server.cleanup)
863 request.addfinalizer(repo_server.cleanup)
864 return repo_server
864 return repo_server
865
865
866
866
867 class RepoServer(object):
867 class RepoServer(object):
868 """
868 """
869 Utility to serve a local repository for the duration of a test case.
869 Utility to serve a local repository for the duration of a test case.
870
870
871 Supports only Subversion so far.
871 Supports only Subversion so far.
872 """
872 """
873
873
874 url = None
874 url = None
875
875
876 def __init__(self):
876 def __init__(self):
877 self._cleanup_servers = []
877 self._cleanup_servers = []
878
878
879 def serve(self, vcsrepo):
879 def serve(self, vcsrepo):
880 if vcsrepo.alias != 'svn':
880 if vcsrepo.alias != 'svn':
881 raise TypeError("Backend %s not supported" % vcsrepo.alias)
881 raise TypeError("Backend %s not supported" % vcsrepo.alias)
882
882
883 proc = subprocess32.Popen(
883 proc = subprocess32.Popen(
884 ['svnserve', '-d', '--foreground', '--listen-host', 'localhost',
884 ['svnserve', '-d', '--foreground', '--listen-host', 'localhost',
885 '--root', vcsrepo.path])
885 '--root', vcsrepo.path])
886 self._cleanup_servers.append(proc)
886 self._cleanup_servers.append(proc)
887 self.url = 'svn://localhost'
887 self.url = 'svn://localhost'
888
888
889 def cleanup(self):
889 def cleanup(self):
890 for proc in self._cleanup_servers:
890 for proc in self._cleanup_servers:
891 proc.terminate()
891 proc.terminate()
892
892
893
893
894 @pytest.fixture
894 @pytest.fixture
895 def pr_util(backend, request, config_stub):
895 def pr_util(backend, request, config_stub):
896 """
896 """
897 Utility for tests of models and for functional tests around pull requests.
897 Utility for tests of models and for functional tests around pull requests.
898
898
899 It gives an instance of :class:`PRTestUtility` which provides various
899 It gives an instance of :class:`PRTestUtility` which provides various
900 utility methods around one pull request.
900 utility methods around one pull request.
901
901
902 This fixture uses `backend` and inherits its parameterization.
902 This fixture uses `backend` and inherits its parameterization.
903 """
903 """
904
904
905 util = PRTestUtility(backend)
905 util = PRTestUtility(backend)
906
906
907 @request.addfinalizer
907 @request.addfinalizer
908 def cleanup():
908 def cleanup():
909 util.cleanup()
909 util.cleanup()
910
910
911 return util
911 return util
912
912
913
913
914 class PRTestUtility(object):
914 class PRTestUtility(object):
915
915
916 pull_request = None
916 pull_request = None
917 pull_request_id = None
917 pull_request_id = None
918 mergeable_patcher = None
918 mergeable_patcher = None
919 mergeable_mock = None
919 mergeable_mock = None
920 notification_patcher = None
920 notification_patcher = None
921
921
922 def __init__(self, backend):
922 def __init__(self, backend):
923 self.backend = backend
923 self.backend = backend
924
924
925 def create_pull_request(
925 def create_pull_request(
926 self, commits=None, target_head=None, source_head=None,
926 self, commits=None, target_head=None, source_head=None,
927 revisions=None, approved=False, author=None, mergeable=False,
927 revisions=None, approved=False, author=None, mergeable=False,
928 enable_notifications=True, name_suffix=u'', reviewers=None,
928 enable_notifications=True, name_suffix=u'', reviewers=None,
929 title=u"Test", description=u"Description"):
929 title=u"Test", description=u"Description"):
930 self.set_mergeable(mergeable)
930 self.set_mergeable(mergeable)
931 if not enable_notifications:
931 if not enable_notifications:
932 # mock notification side effect
932 # mock notification side effect
933 self.notification_patcher = mock.patch(
933 self.notification_patcher = mock.patch(
934 'rhodecode.model.notification.NotificationModel.create')
934 'rhodecode.model.notification.NotificationModel.create')
935 self.notification_patcher.start()
935 self.notification_patcher.start()
936
936
937 if not self.pull_request:
937 if not self.pull_request:
938 if not commits:
938 if not commits:
939 commits = [
939 commits = [
940 {'message': 'c1'},
940 {'message': 'c1'},
941 {'message': 'c2'},
941 {'message': 'c2'},
942 {'message': 'c3'},
942 {'message': 'c3'},
943 ]
943 ]
944 target_head = 'c1'
944 target_head = 'c1'
945 source_head = 'c2'
945 source_head = 'c2'
946 revisions = ['c2']
946 revisions = ['c2']
947
947
948 self.commit_ids = self.backend.create_master_repo(commits)
948 self.commit_ids = self.backend.create_master_repo(commits)
949 self.target_repository = self.backend.create_repo(
949 self.target_repository = self.backend.create_repo(
950 heads=[target_head], name_suffix=name_suffix)
950 heads=[target_head], name_suffix=name_suffix)
951 self.source_repository = self.backend.create_repo(
951 self.source_repository = self.backend.create_repo(
952 heads=[source_head], name_suffix=name_suffix)
952 heads=[source_head], name_suffix=name_suffix)
953 self.author = author or UserModel().get_by_username(
953 self.author = author or UserModel().get_by_username(
954 TEST_USER_ADMIN_LOGIN)
954 TEST_USER_ADMIN_LOGIN)
955
955
956 model = PullRequestModel()
956 model = PullRequestModel()
957 self.create_parameters = {
957 self.create_parameters = {
958 'created_by': self.author,
958 'created_by': self.author,
959 'source_repo': self.source_repository.repo_name,
959 'source_repo': self.source_repository.repo_name,
960 'source_ref': self._default_branch_reference(source_head),
960 'source_ref': self._default_branch_reference(source_head),
961 'target_repo': self.target_repository.repo_name,
961 'target_repo': self.target_repository.repo_name,
962 'target_ref': self._default_branch_reference(target_head),
962 'target_ref': self._default_branch_reference(target_head),
963 'revisions': [self.commit_ids[r] for r in revisions],
963 'revisions': [self.commit_ids[r] for r in revisions],
964 'reviewers': reviewers or self._get_reviewers(),
964 'reviewers': reviewers or self._get_reviewers(),
965 'title': title,
965 'title': title,
966 'description': description,
966 'description': description,
967 }
967 }
968 self.pull_request = model.create(**self.create_parameters)
968 self.pull_request = model.create(**self.create_parameters)
969 assert model.get_versions(self.pull_request) == []
969 assert model.get_versions(self.pull_request) == []
970
970
971 self.pull_request_id = self.pull_request.pull_request_id
971 self.pull_request_id = self.pull_request.pull_request_id
972
972
973 if approved:
973 if approved:
974 self.approve()
974 self.approve()
975
975
976 Session().add(self.pull_request)
976 Session().add(self.pull_request)
977 Session().commit()
977 Session().commit()
978
978
979 return self.pull_request
979 return self.pull_request
980
980
981 def approve(self):
981 def approve(self):
982 self.create_status_votes(
982 self.create_status_votes(
983 ChangesetStatus.STATUS_APPROVED,
983 ChangesetStatus.STATUS_APPROVED,
984 *self.pull_request.reviewers)
984 *self.pull_request.reviewers)
985
985
986 def close(self):
986 def close(self):
987 PullRequestModel().close_pull_request(self.pull_request, self.author)
987 PullRequestModel().close_pull_request(self.pull_request, self.author)
988
988
989 def _default_branch_reference(self, commit_message):
989 def _default_branch_reference(self, commit_message):
990 reference = '%s:%s:%s' % (
990 reference = '%s:%s:%s' % (
991 'branch',
991 'branch',
992 self.backend.default_branch_name,
992 self.backend.default_branch_name,
993 self.commit_ids[commit_message])
993 self.commit_ids[commit_message])
994 return reference
994 return reference
995
995
996 def _get_reviewers(self):
996 def _get_reviewers(self):
997 return [
997 return [
998 (TEST_USER_REGULAR_LOGIN, ['default1'], False),
998 (TEST_USER_REGULAR_LOGIN, ['default1'], False),
999 (TEST_USER_REGULAR2_LOGIN, ['default2'], False),
999 (TEST_USER_REGULAR2_LOGIN, ['default2'], False),
1000 ]
1000 ]
1001
1001
1002 def update_source_repository(self, head=None):
1002 def update_source_repository(self, head=None):
1003 heads = [head or 'c3']
1003 heads = [head or 'c3']
1004 self.backend.pull_heads(self.source_repository, heads=heads)
1004 self.backend.pull_heads(self.source_repository, heads=heads)
1005
1005
1006 def add_one_commit(self, head=None):
1006 def add_one_commit(self, head=None):
1007 self.update_source_repository(head=head)
1007 self.update_source_repository(head=head)
1008 old_commit_ids = set(self.pull_request.revisions)
1008 old_commit_ids = set(self.pull_request.revisions)
1009 PullRequestModel().update_commits(self.pull_request)
1009 PullRequestModel().update_commits(self.pull_request)
1010 commit_ids = set(self.pull_request.revisions)
1010 commit_ids = set(self.pull_request.revisions)
1011 new_commit_ids = commit_ids - old_commit_ids
1011 new_commit_ids = commit_ids - old_commit_ids
1012 assert len(new_commit_ids) == 1
1012 assert len(new_commit_ids) == 1
1013 return new_commit_ids.pop()
1013 return new_commit_ids.pop()
1014
1014
1015 def remove_one_commit(self):
1015 def remove_one_commit(self):
1016 assert len(self.pull_request.revisions) == 2
1016 assert len(self.pull_request.revisions) == 2
1017 source_vcs = self.source_repository.scm_instance()
1017 source_vcs = self.source_repository.scm_instance()
1018 removed_commit_id = source_vcs.commit_ids[-1]
1018 removed_commit_id = source_vcs.commit_ids[-1]
1019
1019
1020 # TODO: johbo: Git and Mercurial have an inconsistent vcs api here,
1020 # TODO: johbo: Git and Mercurial have an inconsistent vcs api here,
1021 # remove the if once that's sorted out.
1021 # remove the if once that's sorted out.
1022 if self.backend.alias == "git":
1022 if self.backend.alias == "git":
1023 kwargs = {'branch_name': self.backend.default_branch_name}
1023 kwargs = {'branch_name': self.backend.default_branch_name}
1024 else:
1024 else:
1025 kwargs = {}
1025 kwargs = {}
1026 source_vcs.strip(removed_commit_id, **kwargs)
1026 source_vcs.strip(removed_commit_id, **kwargs)
1027
1027
1028 PullRequestModel().update_commits(self.pull_request)
1028 PullRequestModel().update_commits(self.pull_request)
1029 assert len(self.pull_request.revisions) == 1
1029 assert len(self.pull_request.revisions) == 1
1030 return removed_commit_id
1030 return removed_commit_id
1031
1031
1032 def create_comment(self, linked_to=None):
1032 def create_comment(self, linked_to=None):
1033 comment = CommentsModel().create(
1033 comment = CommentsModel().create(
1034 text=u"Test comment",
1034 text=u"Test comment",
1035 repo=self.target_repository.repo_name,
1035 repo=self.target_repository.repo_name,
1036 user=self.author,
1036 user=self.author,
1037 pull_request=self.pull_request)
1037 pull_request=self.pull_request)
1038 assert comment.pull_request_version_id is None
1038 assert comment.pull_request_version_id is None
1039
1039
1040 if linked_to:
1040 if linked_to:
1041 PullRequestModel()._link_comments_to_version(linked_to)
1041 PullRequestModel()._link_comments_to_version(linked_to)
1042
1042
1043 return comment
1043 return comment
1044
1044
1045 def create_inline_comment(
1045 def create_inline_comment(
1046 self, linked_to=None, line_no=u'n1', file_path='file_1'):
1046 self, linked_to=None, line_no=u'n1', file_path='file_1'):
1047 comment = CommentsModel().create(
1047 comment = CommentsModel().create(
1048 text=u"Test comment",
1048 text=u"Test comment",
1049 repo=self.target_repository.repo_name,
1049 repo=self.target_repository.repo_name,
1050 user=self.author,
1050 user=self.author,
1051 line_no=line_no,
1051 line_no=line_no,
1052 f_path=file_path,
1052 f_path=file_path,
1053 pull_request=self.pull_request)
1053 pull_request=self.pull_request)
1054 assert comment.pull_request_version_id is None
1054 assert comment.pull_request_version_id is None
1055
1055
1056 if linked_to:
1056 if linked_to:
1057 PullRequestModel()._link_comments_to_version(linked_to)
1057 PullRequestModel()._link_comments_to_version(linked_to)
1058
1058
1059 return comment
1059 return comment
1060
1060
1061 def create_version_of_pull_request(self):
1061 def create_version_of_pull_request(self):
1062 pull_request = self.create_pull_request()
1062 pull_request = self.create_pull_request()
1063 version = PullRequestModel()._create_version_from_snapshot(
1063 version = PullRequestModel()._create_version_from_snapshot(
1064 pull_request)
1064 pull_request)
1065 return version
1065 return version
1066
1066
1067 def create_status_votes(self, status, *reviewers):
1067 def create_status_votes(self, status, *reviewers):
1068 for reviewer in reviewers:
1068 for reviewer in reviewers:
1069 ChangesetStatusModel().set_status(
1069 ChangesetStatusModel().set_status(
1070 repo=self.pull_request.target_repo,
1070 repo=self.pull_request.target_repo,
1071 status=status,
1071 status=status,
1072 user=reviewer.user_id,
1072 user=reviewer.user_id,
1073 pull_request=self.pull_request)
1073 pull_request=self.pull_request)
1074
1074
1075 def set_mergeable(self, value):
1075 def set_mergeable(self, value):
1076 if not self.mergeable_patcher:
1076 if not self.mergeable_patcher:
1077 self.mergeable_patcher = mock.patch.object(
1077 self.mergeable_patcher = mock.patch.object(
1078 VcsSettingsModel, 'get_general_settings')
1078 VcsSettingsModel, 'get_general_settings')
1079 self.mergeable_mock = self.mergeable_patcher.start()
1079 self.mergeable_mock = self.mergeable_patcher.start()
1080 self.mergeable_mock.return_value = {
1080 self.mergeable_mock.return_value = {
1081 'rhodecode_pr_merge_enabled': value}
1081 'rhodecode_pr_merge_enabled': value}
1082
1082
1083 def cleanup(self):
1083 def cleanup(self):
1084 # In case the source repository is already cleaned up, the pull
1084 # In case the source repository is already cleaned up, the pull
1085 # request will already be deleted.
1085 # request will already be deleted.
1086 pull_request = PullRequest().get(self.pull_request_id)
1086 pull_request = PullRequest().get(self.pull_request_id)
1087 if pull_request:
1087 if pull_request:
1088 PullRequestModel().delete(pull_request, pull_request.author)
1088 PullRequestModel().delete(pull_request, pull_request.author)
1089 Session().commit()
1089 Session().commit()
1090
1090
1091 if self.notification_patcher:
1091 if self.notification_patcher:
1092 self.notification_patcher.stop()
1092 self.notification_patcher.stop()
1093
1093
1094 if self.mergeable_patcher:
1094 if self.mergeable_patcher:
1095 self.mergeable_patcher.stop()
1095 self.mergeable_patcher.stop()
1096
1096
1097
1097
1098 @pytest.fixture
1098 @pytest.fixture
1099 def user_admin(pylonsapp):
1099 def user_admin(pylonsapp):
1100 """
1100 """
1101 Provides the default admin test user as an instance of `db.User`.
1101 Provides the default admin test user as an instance of `db.User`.
1102 """
1102 """
1103 user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1103 user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1104 return user
1104 return user
1105
1105
1106
1106
1107 @pytest.fixture
1107 @pytest.fixture
1108 def user_regular(pylonsapp):
1108 def user_regular(pylonsapp):
1109 """
1109 """
1110 Provides the default regular test user as an instance of `db.User`.
1110 Provides the default regular test user as an instance of `db.User`.
1111 """
1111 """
1112 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
1112 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
1113 return user
1113 return user
1114
1114
1115
1115
1116 @pytest.fixture
1116 @pytest.fixture
1117 def user_util(request, pylonsapp):
1117 def user_util(request, pylonsapp):
1118 """
1118 """
1119 Provides a wired instance of `UserUtility` with integrated cleanup.
1119 Provides a wired instance of `UserUtility` with integrated cleanup.
1120 """
1120 """
1121 utility = UserUtility(test_name=request.node.name)
1121 utility = UserUtility(test_name=request.node.name)
1122 request.addfinalizer(utility.cleanup)
1122 request.addfinalizer(utility.cleanup)
1123 return utility
1123 return utility
1124
1124
1125
1125
1126 # TODO: johbo: Split this up into utilities per domain or something similar
1126 # TODO: johbo: Split this up into utilities per domain or something similar
1127 class UserUtility(object):
1127 class UserUtility(object):
1128
1128
1129 def __init__(self, test_name="test"):
1129 def __init__(self, test_name="test"):
1130 self._test_name = self._sanitize_name(test_name)
1130 self._test_name = self._sanitize_name(test_name)
1131 self.fixture = Fixture()
1131 self.fixture = Fixture()
1132 self.repo_group_ids = []
1132 self.repo_group_ids = []
1133 self.repos_ids = []
1133 self.repos_ids = []
1134 self.user_ids = []
1134 self.user_ids = []
1135 self.user_group_ids = []
1135 self.user_group_ids = []
1136 self.user_repo_permission_ids = []
1136 self.user_repo_permission_ids = []
1137 self.user_group_repo_permission_ids = []
1137 self.user_group_repo_permission_ids = []
1138 self.user_repo_group_permission_ids = []
1138 self.user_repo_group_permission_ids = []
1139 self.user_group_repo_group_permission_ids = []
1139 self.user_group_repo_group_permission_ids = []
1140 self.user_user_group_permission_ids = []
1140 self.user_user_group_permission_ids = []
1141 self.user_group_user_group_permission_ids = []
1141 self.user_group_user_group_permission_ids = []
1142 self.user_permissions = []
1142 self.user_permissions = []
1143
1143
1144 def _sanitize_name(self, name):
1144 def _sanitize_name(self, name):
1145 for char in ['[', ']']:
1145 for char in ['[', ']']:
1146 name = name.replace(char, '_')
1146 name = name.replace(char, '_')
1147 return name
1147 return name
1148
1148
1149 def create_repo_group(
1149 def create_repo_group(
1150 self, owner=TEST_USER_ADMIN_LOGIN, auto_cleanup=True):
1150 self, owner=TEST_USER_ADMIN_LOGIN, auto_cleanup=True):
1151 group_name = "{prefix}_repogroup_{count}".format(
1151 group_name = "{prefix}_repogroup_{count}".format(
1152 prefix=self._test_name,
1152 prefix=self._test_name,
1153 count=len(self.repo_group_ids))
1153 count=len(self.repo_group_ids))
1154 repo_group = self.fixture.create_repo_group(
1154 repo_group = self.fixture.create_repo_group(
1155 group_name, cur_user=owner)
1155 group_name, cur_user=owner)
1156 if auto_cleanup:
1156 if auto_cleanup:
1157 self.repo_group_ids.append(repo_group.group_id)
1157 self.repo_group_ids.append(repo_group.group_id)
1158 return repo_group
1158 return repo_group
1159
1159
1160 def create_repo(self, owner=TEST_USER_ADMIN_LOGIN, parent=None,
1160 def create_repo(self, owner=TEST_USER_ADMIN_LOGIN, parent=None,
1161 auto_cleanup=True, repo_type='hg'):
1161 auto_cleanup=True, repo_type='hg'):
1162 repo_name = "{prefix}_repository_{count}".format(
1162 repo_name = "{prefix}_repository_{count}".format(
1163 prefix=self._test_name,
1163 prefix=self._test_name,
1164 count=len(self.repos_ids))
1164 count=len(self.repos_ids))
1165
1165
1166 repository = self.fixture.create_repo(
1166 repository = self.fixture.create_repo(
1167 repo_name, cur_user=owner, repo_group=parent, repo_type=repo_type)
1167 repo_name, cur_user=owner, repo_group=parent, repo_type=repo_type)
1168 if auto_cleanup:
1168 if auto_cleanup:
1169 self.repos_ids.append(repository.repo_id)
1169 self.repos_ids.append(repository.repo_id)
1170 return repository
1170 return repository
1171
1171
1172 def create_user(self, auto_cleanup=True, **kwargs):
1172 def create_user(self, auto_cleanup=True, **kwargs):
1173 user_name = "{prefix}_user_{count}".format(
1173 user_name = "{prefix}_user_{count}".format(
1174 prefix=self._test_name,
1174 prefix=self._test_name,
1175 count=len(self.user_ids))
1175 count=len(self.user_ids))
1176 user = self.fixture.create_user(user_name, **kwargs)
1176 user = self.fixture.create_user(user_name, **kwargs)
1177 if auto_cleanup:
1177 if auto_cleanup:
1178 self.user_ids.append(user.user_id)
1178 self.user_ids.append(user.user_id)
1179 return user
1179 return user
1180
1180
1181 def create_user_with_group(self):
1181 def create_user_with_group(self):
1182 user = self.create_user()
1182 user = self.create_user()
1183 user_group = self.create_user_group(members=[user])
1183 user_group = self.create_user_group(members=[user])
1184 return user, user_group
1184 return user, user_group
1185
1185
1186 def create_user_group(self, owner=TEST_USER_ADMIN_LOGIN, members=None,
1186 def create_user_group(self, owner=TEST_USER_ADMIN_LOGIN, members=None,
1187 auto_cleanup=True, **kwargs):
1187 auto_cleanup=True, **kwargs):
1188 group_name = "{prefix}_usergroup_{count}".format(
1188 group_name = "{prefix}_usergroup_{count}".format(
1189 prefix=self._test_name,
1189 prefix=self._test_name,
1190 count=len(self.user_group_ids))
1190 count=len(self.user_group_ids))
1191 user_group = self.fixture.create_user_group(
1191 user_group = self.fixture.create_user_group(
1192 group_name, cur_user=owner, **kwargs)
1192 group_name, cur_user=owner, **kwargs)
1193
1193
1194 if auto_cleanup:
1194 if auto_cleanup:
1195 self.user_group_ids.append(user_group.users_group_id)
1195 self.user_group_ids.append(user_group.users_group_id)
1196 if members:
1196 if members:
1197 for user in members:
1197 for user in members:
1198 UserGroupModel().add_user_to_group(user_group, user)
1198 UserGroupModel().add_user_to_group(user_group, user)
1199 return user_group
1199 return user_group
1200
1200
1201 def grant_user_permission(self, user_name, permission_name):
1201 def grant_user_permission(self, user_name, permission_name):
1202 self._inherit_default_user_permissions(user_name, False)
1202 self._inherit_default_user_permissions(user_name, False)
1203 self.user_permissions.append((user_name, permission_name))
1203 self.user_permissions.append((user_name, permission_name))
1204
1204
1205 def grant_user_permission_to_repo_group(
1205 def grant_user_permission_to_repo_group(
1206 self, repo_group, user, permission_name):
1206 self, repo_group, user, permission_name):
1207 permission = RepoGroupModel().grant_user_permission(
1207 permission = RepoGroupModel().grant_user_permission(
1208 repo_group, user, permission_name)
1208 repo_group, user, permission_name)
1209 self.user_repo_group_permission_ids.append(
1209 self.user_repo_group_permission_ids.append(
1210 (repo_group.group_id, user.user_id))
1210 (repo_group.group_id, user.user_id))
1211 return permission
1211 return permission
1212
1212
1213 def grant_user_group_permission_to_repo_group(
1213 def grant_user_group_permission_to_repo_group(
1214 self, repo_group, user_group, permission_name):
1214 self, repo_group, user_group, permission_name):
1215 permission = RepoGroupModel().grant_user_group_permission(
1215 permission = RepoGroupModel().grant_user_group_permission(
1216 repo_group, user_group, permission_name)
1216 repo_group, user_group, permission_name)
1217 self.user_group_repo_group_permission_ids.append(
1217 self.user_group_repo_group_permission_ids.append(
1218 (repo_group.group_id, user_group.users_group_id))
1218 (repo_group.group_id, user_group.users_group_id))
1219 return permission
1219 return permission
1220
1220
1221 def grant_user_permission_to_repo(
1221 def grant_user_permission_to_repo(
1222 self, repo, user, permission_name):
1222 self, repo, user, permission_name):
1223 permission = RepoModel().grant_user_permission(
1223 permission = RepoModel().grant_user_permission(
1224 repo, user, permission_name)
1224 repo, user, permission_name)
1225 self.user_repo_permission_ids.append(
1225 self.user_repo_permission_ids.append(
1226 (repo.repo_id, user.user_id))
1226 (repo.repo_id, user.user_id))
1227 return permission
1227 return permission
1228
1228
1229 def grant_user_group_permission_to_repo(
1229 def grant_user_group_permission_to_repo(
1230 self, repo, user_group, permission_name):
1230 self, repo, user_group, permission_name):
1231 permission = RepoModel().grant_user_group_permission(
1231 permission = RepoModel().grant_user_group_permission(
1232 repo, user_group, permission_name)
1232 repo, user_group, permission_name)
1233 self.user_group_repo_permission_ids.append(
1233 self.user_group_repo_permission_ids.append(
1234 (repo.repo_id, user_group.users_group_id))
1234 (repo.repo_id, user_group.users_group_id))
1235 return permission
1235 return permission
1236
1236
1237 def grant_user_permission_to_user_group(
1237 def grant_user_permission_to_user_group(
1238 self, target_user_group, user, permission_name):
1238 self, target_user_group, user, permission_name):
1239 permission = UserGroupModel().grant_user_permission(
1239 permission = UserGroupModel().grant_user_permission(
1240 target_user_group, user, permission_name)
1240 target_user_group, user, permission_name)
1241 self.user_user_group_permission_ids.append(
1241 self.user_user_group_permission_ids.append(
1242 (target_user_group.users_group_id, user.user_id))
1242 (target_user_group.users_group_id, user.user_id))
1243 return permission
1243 return permission
1244
1244
1245 def grant_user_group_permission_to_user_group(
1245 def grant_user_group_permission_to_user_group(
1246 self, target_user_group, user_group, permission_name):
1246 self, target_user_group, user_group, permission_name):
1247 permission = UserGroupModel().grant_user_group_permission(
1247 permission = UserGroupModel().grant_user_group_permission(
1248 target_user_group, user_group, permission_name)
1248 target_user_group, user_group, permission_name)
1249 self.user_group_user_group_permission_ids.append(
1249 self.user_group_user_group_permission_ids.append(
1250 (target_user_group.users_group_id, user_group.users_group_id))
1250 (target_user_group.users_group_id, user_group.users_group_id))
1251 return permission
1251 return permission
1252
1252
1253 def revoke_user_permission(self, user_name, permission_name):
1253 def revoke_user_permission(self, user_name, permission_name):
1254 self._inherit_default_user_permissions(user_name, True)
1254 self._inherit_default_user_permissions(user_name, True)
1255 UserModel().revoke_perm(user_name, permission_name)
1255 UserModel().revoke_perm(user_name, permission_name)
1256
1256
1257 def _inherit_default_user_permissions(self, user_name, value):
1257 def _inherit_default_user_permissions(self, user_name, value):
1258 user = UserModel().get_by_username(user_name)
1258 user = UserModel().get_by_username(user_name)
1259 user.inherit_default_permissions = value
1259 user.inherit_default_permissions = value
1260 Session().add(user)
1260 Session().add(user)
1261 Session().commit()
1261 Session().commit()
1262
1262
1263 def cleanup(self):
1263 def cleanup(self):
1264 self._cleanup_permissions()
1264 self._cleanup_permissions()
1265 self._cleanup_repos()
1265 self._cleanup_repos()
1266 self._cleanup_repo_groups()
1266 self._cleanup_repo_groups()
1267 self._cleanup_user_groups()
1267 self._cleanup_user_groups()
1268 self._cleanup_users()
1268 self._cleanup_users()
1269
1269
1270 def _cleanup_permissions(self):
1270 def _cleanup_permissions(self):
1271 if self.user_permissions:
1271 if self.user_permissions:
1272 for user_name, permission_name in self.user_permissions:
1272 for user_name, permission_name in self.user_permissions:
1273 self.revoke_user_permission(user_name, permission_name)
1273 self.revoke_user_permission(user_name, permission_name)
1274
1274
1275 for permission in self.user_repo_permission_ids:
1275 for permission in self.user_repo_permission_ids:
1276 RepoModel().revoke_user_permission(*permission)
1276 RepoModel().revoke_user_permission(*permission)
1277
1277
1278 for permission in self.user_group_repo_permission_ids:
1278 for permission in self.user_group_repo_permission_ids:
1279 RepoModel().revoke_user_group_permission(*permission)
1279 RepoModel().revoke_user_group_permission(*permission)
1280
1280
1281 for permission in self.user_repo_group_permission_ids:
1281 for permission in self.user_repo_group_permission_ids:
1282 RepoGroupModel().revoke_user_permission(*permission)
1282 RepoGroupModel().revoke_user_permission(*permission)
1283
1283
1284 for permission in self.user_group_repo_group_permission_ids:
1284 for permission in self.user_group_repo_group_permission_ids:
1285 RepoGroupModel().revoke_user_group_permission(*permission)
1285 RepoGroupModel().revoke_user_group_permission(*permission)
1286
1286
1287 for permission in self.user_user_group_permission_ids:
1287 for permission in self.user_user_group_permission_ids:
1288 UserGroupModel().revoke_user_permission(*permission)
1288 UserGroupModel().revoke_user_permission(*permission)
1289
1289
1290 for permission in self.user_group_user_group_permission_ids:
1290 for permission in self.user_group_user_group_permission_ids:
1291 UserGroupModel().revoke_user_group_permission(*permission)
1291 UserGroupModel().revoke_user_group_permission(*permission)
1292
1292
1293 def _cleanup_repo_groups(self):
1293 def _cleanup_repo_groups(self):
1294 def _repo_group_compare(first_group_id, second_group_id):
1294 def _repo_group_compare(first_group_id, second_group_id):
1295 """
1295 """
1296 Gives higher priority to the groups with the most complex paths
1296 Gives higher priority to the groups with the most complex paths
1297 """
1297 """
1298 first_group = RepoGroup.get(first_group_id)
1298 first_group = RepoGroup.get(first_group_id)
1299 second_group = RepoGroup.get(second_group_id)
1299 second_group = RepoGroup.get(second_group_id)
1300 first_group_parts = (
1300 first_group_parts = (
1301 len(first_group.group_name.split('/')) if first_group else 0)
1301 len(first_group.group_name.split('/')) if first_group else 0)
1302 second_group_parts = (
1302 second_group_parts = (
1303 len(second_group.group_name.split('/')) if second_group else 0)
1303 len(second_group.group_name.split('/')) if second_group else 0)
1304 return cmp(second_group_parts, first_group_parts)
1304 return cmp(second_group_parts, first_group_parts)
1305
1305
1306 sorted_repo_group_ids = sorted(
1306 sorted_repo_group_ids = sorted(
1307 self.repo_group_ids, cmp=_repo_group_compare)
1307 self.repo_group_ids, cmp=_repo_group_compare)
1308 for repo_group_id in sorted_repo_group_ids:
1308 for repo_group_id in sorted_repo_group_ids:
1309 self.fixture.destroy_repo_group(repo_group_id)
1309 self.fixture.destroy_repo_group(repo_group_id)
1310
1310
1311 def _cleanup_repos(self):
1311 def _cleanup_repos(self):
1312 sorted_repos_ids = sorted(self.repos_ids)
1312 sorted_repos_ids = sorted(self.repos_ids)
1313 for repo_id in sorted_repos_ids:
1313 for repo_id in sorted_repos_ids:
1314 self.fixture.destroy_repo(repo_id)
1314 self.fixture.destroy_repo(repo_id)
1315
1315
1316 def _cleanup_user_groups(self):
1316 def _cleanup_user_groups(self):
1317 def _user_group_compare(first_group_id, second_group_id):
1317 def _user_group_compare(first_group_id, second_group_id):
1318 """
1318 """
1319 Gives higher priority to the groups with the most complex paths
1319 Gives higher priority to the groups with the most complex paths
1320 """
1320 """
1321 first_group = UserGroup.get(first_group_id)
1321 first_group = UserGroup.get(first_group_id)
1322 second_group = UserGroup.get(second_group_id)
1322 second_group = UserGroup.get(second_group_id)
1323 first_group_parts = (
1323 first_group_parts = (
1324 len(first_group.users_group_name.split('/'))
1324 len(first_group.users_group_name.split('/'))
1325 if first_group else 0)
1325 if first_group else 0)
1326 second_group_parts = (
1326 second_group_parts = (
1327 len(second_group.users_group_name.split('/'))
1327 len(second_group.users_group_name.split('/'))
1328 if second_group else 0)
1328 if second_group else 0)
1329 return cmp(second_group_parts, first_group_parts)
1329 return cmp(second_group_parts, first_group_parts)
1330
1330
1331 sorted_user_group_ids = sorted(
1331 sorted_user_group_ids = sorted(
1332 self.user_group_ids, cmp=_user_group_compare)
1332 self.user_group_ids, cmp=_user_group_compare)
1333 for user_group_id in sorted_user_group_ids:
1333 for user_group_id in sorted_user_group_ids:
1334 self.fixture.destroy_user_group(user_group_id)
1334 self.fixture.destroy_user_group(user_group_id)
1335
1335
1336 def _cleanup_users(self):
1336 def _cleanup_users(self):
1337 for user_id in self.user_ids:
1337 for user_id in self.user_ids:
1338 self.fixture.destroy_user(user_id)
1338 self.fixture.destroy_user(user_id)
1339
1339
1340
1340
1341 # TODO: Think about moving this into a pytest-pyro package and make it a
1341 # TODO: Think about moving this into a pytest-pyro package and make it a
1342 # pytest plugin
1342 # pytest plugin
1343 @pytest.hookimpl(tryfirst=True, hookwrapper=True)
1343 @pytest.hookimpl(tryfirst=True, hookwrapper=True)
1344 def pytest_runtest_makereport(item, call):
1344 def pytest_runtest_makereport(item, call):
1345 """
1345 """
1346 Adding the remote traceback if the exception has this information.
1346 Adding the remote traceback if the exception has this information.
1347
1347
1348 VCSServer attaches this information as the attribute `_vcs_server_traceback`
1348 VCSServer attaches this information as the attribute `_vcs_server_traceback`
1349 to the exception instance.
1349 to the exception instance.
1350 """
1350 """
1351 outcome = yield
1351 outcome = yield
1352 report = outcome.get_result()
1352 report = outcome.get_result()
1353 if call.excinfo:
1353 if call.excinfo:
1354 _add_vcsserver_remote_traceback(report, call.excinfo.value)
1354 _add_vcsserver_remote_traceback(report, call.excinfo.value)
1355
1355
1356
1356
1357 def _add_vcsserver_remote_traceback(report, exc):
1357 def _add_vcsserver_remote_traceback(report, exc):
1358 vcsserver_traceback = getattr(exc, '_vcs_server_traceback', None)
1358 vcsserver_traceback = getattr(exc, '_vcs_server_traceback', None)
1359
1359
1360 if vcsserver_traceback:
1360 if vcsserver_traceback:
1361 section = 'VCSServer remote traceback ' + report.when
1361 section = 'VCSServer remote traceback ' + report.when
1362 report.sections.append((section, vcsserver_traceback))
1362 report.sections.append((section, vcsserver_traceback))
1363
1363
1364
1364
1365 @pytest.fixture(scope='session')
1365 @pytest.fixture(scope='session')
1366 def testrun():
1366 def testrun():
1367 return {
1367 return {
1368 'uuid': uuid.uuid4(),
1368 'uuid': uuid.uuid4(),
1369 'start': datetime.datetime.utcnow().isoformat(),
1369 'start': datetime.datetime.utcnow().isoformat(),
1370 'timestamp': int(time.time()),
1370 'timestamp': int(time.time()),
1371 }
1371 }
1372
1372
1373
1373
1374 @pytest.fixture(autouse=True)
1374 @pytest.fixture(autouse=True)
1375 def collect_appenlight_stats(request, testrun):
1375 def collect_appenlight_stats(request, testrun):
1376 """
1376 """
1377 This fixture reports memory consumtion of single tests.
1377 This fixture reports memory consumtion of single tests.
1378
1378
1379 It gathers data based on `psutil` and sends them to Appenlight. The option
1379 It gathers data based on `psutil` and sends them to Appenlight. The option
1380 ``--ae`` has te be used to enable this fixture and the API key for your
1380 ``--ae`` has te be used to enable this fixture and the API key for your
1381 application has to be provided in ``--ae-key``.
1381 application has to be provided in ``--ae-key``.
1382 """
1382 """
1383 try:
1383 try:
1384 # cygwin cannot have yet psutil support.
1384 # cygwin cannot have yet psutil support.
1385 import psutil
1385 import psutil
1386 except ImportError:
1386 except ImportError:
1387 return
1387 return
1388
1388
1389 if not request.config.getoption('--appenlight'):
1389 if not request.config.getoption('--appenlight'):
1390 return
1390 return
1391 else:
1391 else:
1392 # Only request the pylonsapp fixture if appenlight tracking is
1392 # Only request the pylonsapp fixture if appenlight tracking is
1393 # enabled. This will speed up a test run of unit tests by 2 to 3
1393 # enabled. This will speed up a test run of unit tests by 2 to 3
1394 # seconds if appenlight is not enabled.
1394 # seconds if appenlight is not enabled.
1395 pylonsapp = request.getfuncargvalue("pylonsapp")
1395 pylonsapp = request.getfuncargvalue("pylonsapp")
1396 url = '{}/api/logs'.format(request.config.getoption('--appenlight-url'))
1396 url = '{}/api/logs'.format(request.config.getoption('--appenlight-url'))
1397 client = AppenlightClient(
1397 client = AppenlightClient(
1398 url=url,
1398 url=url,
1399 api_key=request.config.getoption('--appenlight-api-key'),
1399 api_key=request.config.getoption('--appenlight-api-key'),
1400 namespace=request.node.nodeid,
1400 namespace=request.node.nodeid,
1401 request=str(testrun['uuid']),
1401 request=str(testrun['uuid']),
1402 testrun=testrun)
1402 testrun=testrun)
1403
1403
1404 client.collect({
1404 client.collect({
1405 'message': "Starting",
1405 'message': "Starting",
1406 })
1406 })
1407
1407
1408 server_and_port = pylonsapp.config['vcs.server']
1408 server_and_port = pylonsapp.config['vcs.server']
1409 protocol = pylonsapp.config['vcs.server.protocol']
1409 protocol = pylonsapp.config['vcs.server.protocol']
1410 server = create_vcsserver_proxy(server_and_port, protocol)
1410 server = create_vcsserver_proxy(server_and_port, protocol)
1411 with server:
1411 with server:
1412 vcs_pid = server.get_pid()
1412 vcs_pid = server.get_pid()
1413 server.run_gc()
1413 server.run_gc()
1414 vcs_process = psutil.Process(vcs_pid)
1414 vcs_process = psutil.Process(vcs_pid)
1415 mem = vcs_process.memory_info()
1415 mem = vcs_process.memory_info()
1416 client.tag_before('vcsserver.rss', mem.rss)
1416 client.tag_before('vcsserver.rss', mem.rss)
1417 client.tag_before('vcsserver.vms', mem.vms)
1417 client.tag_before('vcsserver.vms', mem.vms)
1418
1418
1419 test_process = psutil.Process()
1419 test_process = psutil.Process()
1420 mem = test_process.memory_info()
1420 mem = test_process.memory_info()
1421 client.tag_before('test.rss', mem.rss)
1421 client.tag_before('test.rss', mem.rss)
1422 client.tag_before('test.vms', mem.vms)
1422 client.tag_before('test.vms', mem.vms)
1423
1423
1424 client.tag_before('time', time.time())
1424 client.tag_before('time', time.time())
1425
1425
1426 @request.addfinalizer
1426 @request.addfinalizer
1427 def send_stats():
1427 def send_stats():
1428 client.tag_after('time', time.time())
1428 client.tag_after('time', time.time())
1429 with server:
1429 with server:
1430 gc_stats = server.run_gc()
1430 gc_stats = server.run_gc()
1431 for tag, value in gc_stats.items():
1431 for tag, value in gc_stats.items():
1432 client.tag_after(tag, value)
1432 client.tag_after(tag, value)
1433 mem = vcs_process.memory_info()
1433 mem = vcs_process.memory_info()
1434 client.tag_after('vcsserver.rss', mem.rss)
1434 client.tag_after('vcsserver.rss', mem.rss)
1435 client.tag_after('vcsserver.vms', mem.vms)
1435 client.tag_after('vcsserver.vms', mem.vms)
1436
1436
1437 mem = test_process.memory_info()
1437 mem = test_process.memory_info()
1438 client.tag_after('test.rss', mem.rss)
1438 client.tag_after('test.rss', mem.rss)
1439 client.tag_after('test.vms', mem.vms)
1439 client.tag_after('test.vms', mem.vms)
1440
1440
1441 client.collect({
1441 client.collect({
1442 'message': "Finished",
1442 'message': "Finished",
1443 })
1443 })
1444 client.send_stats()
1444 client.send_stats()
1445
1445
1446 return client
1446 return client
1447
1447
1448
1448
1449 class AppenlightClient():
1449 class AppenlightClient():
1450
1450
1451 url_template = '{url}?protocol_version=0.5'
1451 url_template = '{url}?protocol_version=0.5'
1452
1452
1453 def __init__(
1453 def __init__(
1454 self, url, api_key, add_server=True, add_timestamp=True,
1454 self, url, api_key, add_server=True, add_timestamp=True,
1455 namespace=None, request=None, testrun=None):
1455 namespace=None, request=None, testrun=None):
1456 self.url = self.url_template.format(url=url)
1456 self.url = self.url_template.format(url=url)
1457 self.api_key = api_key
1457 self.api_key = api_key
1458 self.add_server = add_server
1458 self.add_server = add_server
1459 self.add_timestamp = add_timestamp
1459 self.add_timestamp = add_timestamp
1460 self.namespace = namespace
1460 self.namespace = namespace
1461 self.request = request
1461 self.request = request
1462 self.server = socket.getfqdn(socket.gethostname())
1462 self.server = socket.getfqdn(socket.gethostname())
1463 self.tags_before = {}
1463 self.tags_before = {}
1464 self.tags_after = {}
1464 self.tags_after = {}
1465 self.stats = []
1465 self.stats = []
1466 self.testrun = testrun or {}
1466 self.testrun = testrun or {}
1467
1467
1468 def tag_before(self, tag, value):
1468 def tag_before(self, tag, value):
1469 self.tags_before[tag] = value
1469 self.tags_before[tag] = value
1470
1470
1471 def tag_after(self, tag, value):
1471 def tag_after(self, tag, value):
1472 self.tags_after[tag] = value
1472 self.tags_after[tag] = value
1473
1473
1474 def collect(self, data):
1474 def collect(self, data):
1475 if self.add_server:
1475 if self.add_server:
1476 data.setdefault('server', self.server)
1476 data.setdefault('server', self.server)
1477 if self.add_timestamp:
1477 if self.add_timestamp:
1478 data.setdefault('date', datetime.datetime.utcnow().isoformat())
1478 data.setdefault('date', datetime.datetime.utcnow().isoformat())
1479 if self.namespace:
1479 if self.namespace:
1480 data.setdefault('namespace', self.namespace)
1480 data.setdefault('namespace', self.namespace)
1481 if self.request:
1481 if self.request:
1482 data.setdefault('request', self.request)
1482 data.setdefault('request', self.request)
1483 self.stats.append(data)
1483 self.stats.append(data)
1484
1484
1485 def send_stats(self):
1485 def send_stats(self):
1486 tags = [
1486 tags = [
1487 ('testrun', self.request),
1487 ('testrun', self.request),
1488 ('testrun.start', self.testrun['start']),
1488 ('testrun.start', self.testrun['start']),
1489 ('testrun.timestamp', self.testrun['timestamp']),
1489 ('testrun.timestamp', self.testrun['timestamp']),
1490 ('test', self.namespace),
1490 ('test', self.namespace),
1491 ]
1491 ]
1492 for key, value in self.tags_before.items():
1492 for key, value in self.tags_before.items():
1493 tags.append((key + '.before', value))
1493 tags.append((key + '.before', value))
1494 try:
1494 try:
1495 delta = self.tags_after[key] - value
1495 delta = self.tags_after[key] - value
1496 tags.append((key + '.delta', delta))
1496 tags.append((key + '.delta', delta))
1497 except Exception:
1497 except Exception:
1498 pass
1498 pass
1499 for key, value in self.tags_after.items():
1499 for key, value in self.tags_after.items():
1500 tags.append((key + '.after', value))
1500 tags.append((key + '.after', value))
1501 self.collect({
1501 self.collect({
1502 'message': "Collected tags",
1502 'message': "Collected tags",
1503 'tags': tags,
1503 'tags': tags,
1504 })
1504 })
1505
1505
1506 response = requests.post(
1506 response = requests.post(
1507 self.url,
1507 self.url,
1508 headers={
1508 headers={
1509 'X-appenlight-api-key': self.api_key},
1509 'X-appenlight-api-key': self.api_key},
1510 json=self.stats,
1510 json=self.stats,
1511 )
1511 )
1512
1512
1513 if not response.status_code == 200:
1513 if not response.status_code == 200:
1514 pprint.pprint(self.stats)
1514 pprint.pprint(self.stats)
1515 print response.headers
1515 print response.headers
1516 print response.text
1516 print response.text
1517 raise Exception('Sending to appenlight failed')
1517 raise Exception('Sending to appenlight failed')
1518
1518
1519
1519
1520 @pytest.fixture
1520 @pytest.fixture
1521 def gist_util(request, pylonsapp):
1521 def gist_util(request, pylonsapp):
1522 """
1522 """
1523 Provides a wired instance of `GistUtility` with integrated cleanup.
1523 Provides a wired instance of `GistUtility` with integrated cleanup.
1524 """
1524 """
1525 utility = GistUtility()
1525 utility = GistUtility()
1526 request.addfinalizer(utility.cleanup)
1526 request.addfinalizer(utility.cleanup)
1527 return utility
1527 return utility
1528
1528
1529
1529
1530 class GistUtility(object):
1530 class GistUtility(object):
1531 def __init__(self):
1531 def __init__(self):
1532 self.fixture = Fixture()
1532 self.fixture = Fixture()
1533 self.gist_ids = []
1533 self.gist_ids = []
1534
1534
1535 def create_gist(self, **kwargs):
1535 def create_gist(self, **kwargs):
1536 gist = self.fixture.create_gist(**kwargs)
1536 gist = self.fixture.create_gist(**kwargs)
1537 self.gist_ids.append(gist.gist_id)
1537 self.gist_ids.append(gist.gist_id)
1538 return gist
1538 return gist
1539
1539
1540 def cleanup(self):
1540 def cleanup(self):
1541 for id_ in self.gist_ids:
1541 for id_ in self.gist_ids:
1542 self.fixture.destroy_gists(str(id_))
1542 self.fixture.destroy_gists(str(id_))
1543
1543
1544
1544
1545 @pytest.fixture
1545 @pytest.fixture
1546 def enabled_backends(request):
1546 def enabled_backends(request):
1547 backends = request.config.option.backends
1547 backends = request.config.option.backends
1548 return backends[:]
1548 return backends[:]
1549
1549
1550
1550
1551 @pytest.fixture
1551 @pytest.fixture
1552 def settings_util(request):
1552 def settings_util(request):
1553 """
1553 """
1554 Provides a wired instance of `SettingsUtility` with integrated cleanup.
1554 Provides a wired instance of `SettingsUtility` with integrated cleanup.
1555 """
1555 """
1556 utility = SettingsUtility()
1556 utility = SettingsUtility()
1557 request.addfinalizer(utility.cleanup)
1557 request.addfinalizer(utility.cleanup)
1558 return utility
1558 return utility
1559
1559
1560
1560
1561 class SettingsUtility(object):
1561 class SettingsUtility(object):
1562 def __init__(self):
1562 def __init__(self):
1563 self.rhodecode_ui_ids = []
1563 self.rhodecode_ui_ids = []
1564 self.rhodecode_setting_ids = []
1564 self.rhodecode_setting_ids = []
1565 self.repo_rhodecode_ui_ids = []
1565 self.repo_rhodecode_ui_ids = []
1566 self.repo_rhodecode_setting_ids = []
1566 self.repo_rhodecode_setting_ids = []
1567
1567
1568 def create_repo_rhodecode_ui(
1568 def create_repo_rhodecode_ui(
1569 self, repo, section, value, key=None, active=True, cleanup=True):
1569 self, repo, section, value, key=None, active=True, cleanup=True):
1570 key = key or hashlib.sha1(
1570 key = key or hashlib.sha1(
1571 '{}{}{}'.format(section, value, repo.repo_id)).hexdigest()
1571 '{}{}{}'.format(section, value, repo.repo_id)).hexdigest()
1572
1572
1573 setting = RepoRhodeCodeUi()
1573 setting = RepoRhodeCodeUi()
1574 setting.repository_id = repo.repo_id
1574 setting.repository_id = repo.repo_id
1575 setting.ui_section = section
1575 setting.ui_section = section
1576 setting.ui_value = value
1576 setting.ui_value = value
1577 setting.ui_key = key
1577 setting.ui_key = key
1578 setting.ui_active = active
1578 setting.ui_active = active
1579 Session().add(setting)
1579 Session().add(setting)
1580 Session().commit()
1580 Session().commit()
1581
1581
1582 if cleanup:
1582 if cleanup:
1583 self.repo_rhodecode_ui_ids.append(setting.ui_id)
1583 self.repo_rhodecode_ui_ids.append(setting.ui_id)
1584 return setting
1584 return setting
1585
1585
1586 def create_rhodecode_ui(
1586 def create_rhodecode_ui(
1587 self, section, value, key=None, active=True, cleanup=True):
1587 self, section, value, key=None, active=True, cleanup=True):
1588 key = key or hashlib.sha1('{}{}'.format(section, value)).hexdigest()
1588 key = key or hashlib.sha1('{}{}'.format(section, value)).hexdigest()
1589
1589
1590 setting = RhodeCodeUi()
1590 setting = RhodeCodeUi()
1591 setting.ui_section = section
1591 setting.ui_section = section
1592 setting.ui_value = value
1592 setting.ui_value = value
1593 setting.ui_key = key
1593 setting.ui_key = key
1594 setting.ui_active = active
1594 setting.ui_active = active
1595 Session().add(setting)
1595 Session().add(setting)
1596 Session().commit()
1596 Session().commit()
1597
1597
1598 if cleanup:
1598 if cleanup:
1599 self.rhodecode_ui_ids.append(setting.ui_id)
1599 self.rhodecode_ui_ids.append(setting.ui_id)
1600 return setting
1600 return setting
1601
1601
1602 def create_repo_rhodecode_setting(
1602 def create_repo_rhodecode_setting(
1603 self, repo, name, value, type_, cleanup=True):
1603 self, repo, name, value, type_, cleanup=True):
1604 setting = RepoRhodeCodeSetting(
1604 setting = RepoRhodeCodeSetting(
1605 repo.repo_id, key=name, val=value, type=type_)
1605 repo.repo_id, key=name, val=value, type=type_)
1606 Session().add(setting)
1606 Session().add(setting)
1607 Session().commit()
1607 Session().commit()
1608
1608
1609 if cleanup:
1609 if cleanup:
1610 self.repo_rhodecode_setting_ids.append(setting.app_settings_id)
1610 self.repo_rhodecode_setting_ids.append(setting.app_settings_id)
1611 return setting
1611 return setting
1612
1612
1613 def create_rhodecode_setting(self, name, value, type_, cleanup=True):
1613 def create_rhodecode_setting(self, name, value, type_, cleanup=True):
1614 setting = RhodeCodeSetting(key=name, val=value, type=type_)
1614 setting = RhodeCodeSetting(key=name, val=value, type=type_)
1615 Session().add(setting)
1615 Session().add(setting)
1616 Session().commit()
1616 Session().commit()
1617
1617
1618 if cleanup:
1618 if cleanup:
1619 self.rhodecode_setting_ids.append(setting.app_settings_id)
1619 self.rhodecode_setting_ids.append(setting.app_settings_id)
1620
1620
1621 return setting
1621 return setting
1622
1622
1623 def cleanup(self):
1623 def cleanup(self):
1624 for id_ in self.rhodecode_ui_ids:
1624 for id_ in self.rhodecode_ui_ids:
1625 setting = RhodeCodeUi.get(id_)
1625 setting = RhodeCodeUi.get(id_)
1626 Session().delete(setting)
1626 Session().delete(setting)
1627
1627
1628 for id_ in self.rhodecode_setting_ids:
1628 for id_ in self.rhodecode_setting_ids:
1629 setting = RhodeCodeSetting.get(id_)
1629 setting = RhodeCodeSetting.get(id_)
1630 Session().delete(setting)
1630 Session().delete(setting)
1631
1631
1632 for id_ in self.repo_rhodecode_ui_ids:
1632 for id_ in self.repo_rhodecode_ui_ids:
1633 setting = RepoRhodeCodeUi.get(id_)
1633 setting = RepoRhodeCodeUi.get(id_)
1634 Session().delete(setting)
1634 Session().delete(setting)
1635
1635
1636 for id_ in self.repo_rhodecode_setting_ids:
1636 for id_ in self.repo_rhodecode_setting_ids:
1637 setting = RepoRhodeCodeSetting.get(id_)
1637 setting = RepoRhodeCodeSetting.get(id_)
1638 Session().delete(setting)
1638 Session().delete(setting)
1639
1639
1640 Session().commit()
1640 Session().commit()
1641
1641
1642
1642
1643 @pytest.fixture
1643 @pytest.fixture
1644 def no_notifications(request):
1644 def no_notifications(request):
1645 notification_patcher = mock.patch(
1645 notification_patcher = mock.patch(
1646 'rhodecode.model.notification.NotificationModel.create')
1646 'rhodecode.model.notification.NotificationModel.create')
1647 notification_patcher.start()
1647 notification_patcher.start()
1648 request.addfinalizer(notification_patcher.stop)
1648 request.addfinalizer(notification_patcher.stop)
1649
1649
1650
1650
1651 @pytest.fixture(scope='session')
1651 @pytest.fixture(scope='session')
1652 def repeat(request):
1652 def repeat(request):
1653 """
1653 """
1654 The number of repetitions is based on this fixture.
1654 The number of repetitions is based on this fixture.
1655
1655
1656 Slower calls may divide it by 10 or 100. It is chosen in a way so that the
1656 Slower calls may divide it by 10 or 100. It is chosen in a way so that the
1657 tests are not too slow in our default test suite.
1657 tests are not too slow in our default test suite.
1658 """
1658 """
1659 return request.config.getoption('--repeat')
1659 return request.config.getoption('--repeat')
1660
1660
1661
1661
1662 @pytest.fixture
1662 @pytest.fixture
1663 def rhodecode_fixtures():
1663 def rhodecode_fixtures():
1664 return Fixture()
1664 return Fixture()
1665
1665
1666
1666
1667 @pytest.fixture
1667 @pytest.fixture
1668 def request_stub():
1668 def request_stub():
1669 """
1669 """
1670 Stub request object.
1670 Stub request object.
1671 """
1671 """
1672 request = pyramid.testing.DummyRequest()
1672 request = pyramid.testing.DummyRequest()
1673 request.scheme = 'https'
1673 request.scheme = 'https'
1674 return request
1674 return request
1675
1675
1676
1676
1677 @pytest.fixture
1677 @pytest.fixture
1678 def context_stub():
1678 def context_stub():
1679 """
1679 """
1680 Stub context object.
1680 Stub context object.
1681 """
1681 """
1682 context = pyramid.testing.DummyResource()
1682 context = pyramid.testing.DummyResource()
1683 return context
1683 return context
1684
1684
1685
1685
1686 @pytest.fixture
1686 @pytest.fixture
1687 def config_stub(request, request_stub):
1687 def config_stub(request, request_stub):
1688 """
1688 """
1689 Set up pyramid.testing and return the Configurator.
1689 Set up pyramid.testing and return the Configurator.
1690 """
1690 """
1691 config = pyramid.testing.setUp(request=request_stub)
1691 config = pyramid.testing.setUp(request=request_stub)
1692 add_test_routes(config)
1692 add_test_routes(config)
1693
1693
1694 @request.addfinalizer
1694 @request.addfinalizer
1695 def cleanup():
1695 def cleanup():
1696 pyramid.testing.tearDown()
1696 pyramid.testing.tearDown()
1697
1697
1698 return config
1698 return config
1699
1699
1700
1700
1701 @pytest.fixture
1701 @pytest.fixture
1702 def StubIntegrationType():
1702 def StubIntegrationType():
1703 class _StubIntegrationType(IntegrationTypeBase):
1703 class _StubIntegrationType(IntegrationTypeBase):
1704 """ Test integration type class """
1704 """ Test integration type class """
1705
1705
1706 key = 'test'
1706 key = 'test'
1707 display_name = 'Test integration type'
1707 display_name = 'Test integration type'
1708 description = 'A test integration type for testing'
1708 description = 'A test integration type for testing'
1709 icon = 'test_icon_html_image'
1709 icon = 'test_icon_html_image'
1710
1710
1711 def __init__(self, settings):
1711 def __init__(self, settings):
1712 super(_StubIntegrationType, self).__init__(settings)
1712 super(_StubIntegrationType, self).__init__(settings)
1713 self.sent_events = [] # for testing
1713 self.sent_events = [] # for testing
1714
1714
1715 def send_event(self, event):
1715 def send_event(self, event):
1716 self.sent_events.append(event)
1716 self.sent_events.append(event)
1717
1717
1718 def settings_schema(self):
1718 def settings_schema(self):
1719 class SettingsSchema(colander.Schema):
1719 class SettingsSchema(colander.Schema):
1720 test_string_field = colander.SchemaNode(
1720 test_string_field = colander.SchemaNode(
1721 colander.String(),
1721 colander.String(),
1722 missing=colander.required,
1722 missing=colander.required,
1723 title='test string field',
1723 title='test string field',
1724 )
1724 )
1725 test_int_field = colander.SchemaNode(
1725 test_int_field = colander.SchemaNode(
1726 colander.Int(),
1726 colander.Int(),
1727 title='some integer setting',
1727 title='some integer setting',
1728 )
1728 )
1729 return SettingsSchema()
1729 return SettingsSchema()
1730
1730
1731
1731
1732 integration_type_registry.register_integration_type(_StubIntegrationType)
1732 integration_type_registry.register_integration_type(_StubIntegrationType)
1733 return _StubIntegrationType
1733 return _StubIntegrationType
1734
1734
1735 @pytest.fixture
1735 @pytest.fixture
1736 def stub_integration_settings():
1736 def stub_integration_settings():
1737 return {
1737 return {
1738 'test_string_field': 'some data',
1738 'test_string_field': 'some data',
1739 'test_int_field': 100,
1739 'test_int_field': 100,
1740 }
1740 }
1741
1741
1742
1742
1743 @pytest.fixture
1743 @pytest.fixture
1744 def repo_integration_stub(request, repo_stub, StubIntegrationType,
1744 def repo_integration_stub(request, repo_stub, StubIntegrationType,
1745 stub_integration_settings):
1745 stub_integration_settings):
1746 integration = IntegrationModel().create(
1746 integration = IntegrationModel().create(
1747 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1747 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1748 name='test repo integration',
1748 name='test repo integration',
1749 repo=repo_stub, repo_group=None, child_repos_only=None)
1749 repo=repo_stub, repo_group=None, child_repos_only=None)
1750
1750
1751 @request.addfinalizer
1751 @request.addfinalizer
1752 def cleanup():
1752 def cleanup():
1753 IntegrationModel().delete(integration)
1753 IntegrationModel().delete(integration)
1754
1754
1755 return integration
1755 return integration
1756
1756
1757
1757
1758 @pytest.fixture
1758 @pytest.fixture
1759 def repogroup_integration_stub(request, test_repo_group, StubIntegrationType,
1759 def repogroup_integration_stub(request, test_repo_group, StubIntegrationType,
1760 stub_integration_settings):
1760 stub_integration_settings):
1761 integration = IntegrationModel().create(
1761 integration = IntegrationModel().create(
1762 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1762 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1763 name='test repogroup integration',
1763 name='test repogroup integration',
1764 repo=None, repo_group=test_repo_group, child_repos_only=True)
1764 repo=None, repo_group=test_repo_group, child_repos_only=True)
1765
1765
1766 @request.addfinalizer
1766 @request.addfinalizer
1767 def cleanup():
1767 def cleanup():
1768 IntegrationModel().delete(integration)
1768 IntegrationModel().delete(integration)
1769
1769
1770 return integration
1770 return integration
1771
1771
1772
1772
1773 @pytest.fixture
1773 @pytest.fixture
1774 def repogroup_recursive_integration_stub(request, test_repo_group,
1774 def repogroup_recursive_integration_stub(request, test_repo_group,
1775 StubIntegrationType, stub_integration_settings):
1775 StubIntegrationType, stub_integration_settings):
1776 integration = IntegrationModel().create(
1776 integration = IntegrationModel().create(
1777 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1777 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1778 name='test recursive repogroup integration',
1778 name='test recursive repogroup integration',
1779 repo=None, repo_group=test_repo_group, child_repos_only=False)
1779 repo=None, repo_group=test_repo_group, child_repos_only=False)
1780
1780
1781 @request.addfinalizer
1781 @request.addfinalizer
1782 def cleanup():
1782 def cleanup():
1783 IntegrationModel().delete(integration)
1783 IntegrationModel().delete(integration)
1784
1784
1785 return integration
1785 return integration
1786
1786
1787
1787
1788 @pytest.fixture
1788 @pytest.fixture
1789 def global_integration_stub(request, StubIntegrationType,
1789 def global_integration_stub(request, StubIntegrationType,
1790 stub_integration_settings):
1790 stub_integration_settings):
1791 integration = IntegrationModel().create(
1791 integration = IntegrationModel().create(
1792 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1792 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1793 name='test global integration',
1793 name='test global integration',
1794 repo=None, repo_group=None, child_repos_only=None)
1794 repo=None, repo_group=None, child_repos_only=None)
1795
1795
1796 @request.addfinalizer
1796 @request.addfinalizer
1797 def cleanup():
1797 def cleanup():
1798 IntegrationModel().delete(integration)
1798 IntegrationModel().delete(integration)
1799
1799
1800 return integration
1800 return integration
1801
1801
1802
1802
1803 @pytest.fixture
1803 @pytest.fixture
1804 def root_repos_integration_stub(request, StubIntegrationType,
1804 def root_repos_integration_stub(request, StubIntegrationType,
1805 stub_integration_settings):
1805 stub_integration_settings):
1806 integration = IntegrationModel().create(
1806 integration = IntegrationModel().create(
1807 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1807 StubIntegrationType, settings=stub_integration_settings, enabled=True,
1808 name='test global integration',
1808 name='test global integration',
1809 repo=None, repo_group=None, child_repos_only=True)
1809 repo=None, repo_group=None, child_repos_only=True)
1810
1810
1811 @request.addfinalizer
1811 @request.addfinalizer
1812 def cleanup():
1812 def cleanup():
1813 IntegrationModel().delete(integration)
1813 IntegrationModel().delete(integration)
1814
1814
1815 return integration
1815 return integration
1816
1816
1817
1817
1818 @pytest.fixture
1818 @pytest.fixture
1819 def local_dt_to_utc():
1819 def local_dt_to_utc():
1820 def _factory(dt):
1820 def _factory(dt):
1821 return dt.replace(tzinfo=dateutil.tz.tzlocal()).astimezone(
1821 return dt.replace(tzinfo=dateutil.tz.tzlocal()).astimezone(
1822 dateutil.tz.tzutc()).replace(tzinfo=None)
1822 dateutil.tz.tzutc()).replace(tzinfo=None)
1823 return _factory
1823 return _factory
1824
1824
1825
1825
1826 @pytest.fixture
1826 @pytest.fixture
1827 def disable_anonymous_user(request, pylonsapp):
1827 def disable_anonymous_user(request, pylonsapp):
1828 set_anonymous_access(False)
1828 set_anonymous_access(False)
1829
1829
1830 @request.addfinalizer
1830 @request.addfinalizer
1831 def cleanup():
1831 def cleanup():
1832 set_anonymous_access(True)
1832 set_anonymous_access(True)
General Comments 0
You need to be logged in to leave comments. Login now