##// END OF EJS Templates
tests: fixed all tests for python3 BIG changes
super-admin -
r5087:0a27bd22 default
parent child Browse files
Show More
@@ -0,0 +1,132 b''
1 # Copyright (C) 2010-2020 RhodeCode GmbH
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
19 """
20 py.test config for test suite for making push/pull operations.
21
22 .. important::
23
24 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
25 to redirect things to stderr instead of stdout.
26 """
27
28 import pytest
29 import logging
30
31 from rhodecode.authentication import AuthenticationPluginRegistry
32 from rhodecode.model.db import Permission, User
33 from rhodecode.model.meta import Session
34 from rhodecode.model.settings import SettingsModel
35 from rhodecode.model.user import UserModel
36
37
38 log = logging.getLogger(__name__)
39
40
41 @pytest.fixture()
42 def enable_auth_plugins(request, baseapp, csrf_token):
43 """
44 Return a factory object that when called, allows to control which
45 authentication plugins are enabled.
46 """
47
48 class AuthPluginManager(object):
49
50 def cleanup(self):
51 self._enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
52
53 def enable(self, plugins_list, override=None):
54 return self._enable_plugins(plugins_list, override)
55
56 def _enable_plugins(self, plugins_list, override=None):
57 override = override or {}
58 params = {
59 'auth_plugins': ','.join(plugins_list),
60 }
61
62 # helper translate some names to others, to fix settings code
63 name_map = {
64 'token': 'authtoken'
65 }
66 log.debug('enable_auth_plugins: enabling following auth-plugins: %s', plugins_list)
67
68 for module in plugins_list:
69 plugin_name = module.partition('#')[-1]
70 if plugin_name in name_map:
71 plugin_name = name_map[plugin_name]
72 enabled_plugin = f'auth_{plugin_name}_enabled'
73 cache_ttl = f'auth_{plugin_name}_cache_ttl'
74
75 # default params that are needed for each plugin,
76 # `enabled` and `cache_ttl`
77 params.update({
78 enabled_plugin: True,
79 cache_ttl: 0
80 })
81 if override.get:
82 params.update(override.get(module, {}))
83
84 validated_params = params
85
86 for k, v in validated_params.items():
87 setting = SettingsModel().create_or_update_setting(k, v)
88 Session().add(setting)
89 Session().commit()
90
91 AuthenticationPluginRegistry.invalidate_auth_plugins_cache(hard=True)
92
93 enabled_plugins = SettingsModel().get_auth_plugins()
94 assert plugins_list == enabled_plugins
95
96 enabler = AuthPluginManager()
97 request.addfinalizer(enabler.cleanup)
98
99 return enabler
100
101
102 @pytest.fixture()
103 def test_user_factory(request, baseapp):
104
105 def user_factory(username='test_user', password='qweqwe', first_name='John', last_name='Testing', **kwargs):
106 usr = UserModel().create_or_update(
107 username=username,
108 password=password,
109 email=f'{username}@rhodecode.org',
110 firstname=first_name, lastname=last_name)
111 Session().commit()
112
113 for k, v in kwargs.items():
114 setattr(usr, k, v)
115 Session().add(usr)
116
117 assert User.get_by_username(username) == usr
118
119 @request.addfinalizer
120 def cleanup():
121 if UserModel().get_user(usr.user_id) is None:
122 return
123
124 perm = Permission.query().all()
125 for p in perm:
126 UserModel().revoke_perm(usr, p)
127
128 UserModel().delete(usr.user_id)
129 Session().commit()
130 return usr
131
132 return user_factory
@@ -0,0 +1,167 b''
1 # Copyright (C) 2010-2020 RhodeCode GmbH
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import pytest
19 from rhodecode.lib import ext_json
20
21
22 def get_backends_from_metafunc(metafunc):
23 requested_backends = set(metafunc.config.getoption('--backends'))
24 backend_mark = metafunc.definition.get_closest_marker('backends')
25 if backend_mark:
26 # Supported backends by this test function, created from
27 # pytest.mark.backends
28 backends = backend_mark.args
29 elif hasattr(metafunc.cls, 'backend_alias'):
30 # Support class attribute "backend_alias", this is mainly
31 # for legacy reasons for tests not yet using pytest.mark.backends
32 backends = [metafunc.cls.backend_alias]
33 else:
34 backends = metafunc.config.getoption('--backends')
35 return requested_backends.intersection(backends)
36
37
38 def pytest_addoption(parser):
39
40 def _parse_json(value):
41 return ext_json.str_json(value) if value else None
42
43 def _split_comma(value):
44 return value.split(',')
45
46 parser.addoption(
47 '--keep-tmp-path', action='store_true',
48 help="Keep the test temporary directories")
49
50 parser.addoption(
51 '--backends', action='store', type=_split_comma,
52 default=['git', 'hg', 'svn'],
53 help="Select which backends to test for backend specific tests.")
54 parser.addoption(
55 '--dbs', action='store', type=_split_comma,
56 default=['sqlite'],
57 help="Select which database to test for database specific tests. "
58 "Possible options are sqlite,postgres,mysql")
59 parser.addoption(
60 '--appenlight', '--ae', action='store_true',
61 help="Track statistics in appenlight.")
62 parser.addoption(
63 '--appenlight-api-key', '--ae-key',
64 help="API key for Appenlight.")
65 parser.addoption(
66 '--appenlight-url', '--ae-url',
67 default="https://ae.rhodecode.com",
68 help="Appenlight service URL, defaults to https://ae.rhodecode.com")
69 parser.addoption(
70 '--sqlite-connection-string', action='store',
71 default='', help="Connection string for the dbs tests with SQLite")
72 parser.addoption(
73 '--postgres-connection-string', action='store',
74 default='', help="Connection string for the dbs tests with Postgres")
75 parser.addoption(
76 '--mysql-connection-string', action='store',
77 default='', help="Connection string for the dbs tests with MySQL")
78 parser.addoption(
79 '--repeat', type=int, default=100,
80 help="Number of repetitions in performance tests.")
81
82 parser.addoption(
83 '--test-loglevel', dest='test_loglevel',
84 help="Set default Logging level for tests, critical(default), error, warn , info, debug")
85 group = parser.getgroup('pylons')
86 group.addoption(
87 '--with-pylons', dest='pyramid_config',
88 help="Set up a Pylons environment with the specified config file.")
89 group.addoption(
90 '--ini-config-override', action='store', type=_parse_json,
91 default=None, dest='pyramid_config_override', help=(
92 "Overrides the .ini file settings. Should be specified in JSON"
93 " format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
94 )
95 )
96 parser.addini(
97 'pyramid_config',
98 "Set up a Pyramid environment with the specified config file.")
99
100 vcsgroup = parser.getgroup('vcs')
101 vcsgroup.addoption(
102 '--without-vcsserver', dest='with_vcsserver', action='store_false',
103 help="Do not start the VCSServer in a background process.")
104 vcsgroup.addoption(
105 '--with-vcsserver-http', dest='vcsserver_config_http',
106 help="Start the HTTP VCSServer with the specified config file.")
107 vcsgroup.addoption(
108 '--vcsserver-protocol', dest='vcsserver_protocol',
109 help="Start the VCSServer with HTTP protocol support.")
110 vcsgroup.addoption(
111 '--vcsserver-config-override', action='store', type=_parse_json,
112 default=None, dest='vcsserver_config_override', help=(
113 "Overrides the .ini file settings for the VCSServer. "
114 "Should be specified in JSON "
115 "format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
116 )
117 )
118 vcsgroup.addoption(
119 '--vcsserver-port', action='store', type=int,
120 default=None, help=(
121 "Allows to set the port of the vcsserver. Useful when testing "
122 "against an already running server and random ports cause "
123 "trouble."))
124 parser.addini(
125 'vcsserver_config_http',
126 "Start the HTTP VCSServer with the specified config file.")
127 parser.addini(
128 'vcsserver_protocol',
129 "Start the VCSServer with HTTP protocol support.")
130
131
132 @pytest.hookimpl(tryfirst=True, hookwrapper=True)
133 def pytest_runtest_makereport(item, call):
134 """
135 Adding the remote traceback if the exception has this information.
136
137 VCSServer attaches this information as the attribute `_vcs_server_traceback`
138 to the exception instance.
139 """
140 outcome = yield
141 report = outcome.get_result()
142
143 if call.excinfo:
144 exc = call.excinfo.value
145 vcsserver_traceback = getattr(exc, '_vcs_server_traceback', None)
146
147 if vcsserver_traceback and report.outcome == 'failed':
148 section = f'VCSServer remote traceback {report.when}'
149 report.sections.append((section, vcsserver_traceback))
150
151
152 def pytest_generate_tests(metafunc):
153
154 # Support test generation based on --backend parameter
155 if 'backend_alias' in metafunc.fixturenames:
156 backends = get_backends_from_metafunc(metafunc)
157 scope = None
158 if not backends:
159 pytest.skip("Not enabled for any of selected backends")
160
161 metafunc.parametrize('backend_alias', backends, scope=scope)
162
163 backend_mark = metafunc.definition.get_closest_marker('backends')
164 if backend_mark:
165 backends = get_backends_from_metafunc(metafunc)
166 if not backends:
167 pytest.skip("Not enabled for any of selected backends")
@@ -0,0 +1,174 b''
1
2 # Copyright (C) 2010-2020 RhodeCode GmbH
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
6 # (only), as published by the Free Software Foundation.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU Affero General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #
16 # This program is dual-licensed. If you wish to learn more about the
17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19
20 """
21 Test suite for making push/pull operations, on specially modified INI files
22
23 .. important::
24
25 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
26 to redirect things to stderr instead of stdout.
27 """
28
29 import pytest
30
31 from rhodecode.model.auth_token import AuthTokenModel
32 from rhodecode.model.db import Repository
33 from rhodecode.model.meta import Session
34 from rhodecode.tests import (GIT_REPO, HG_REPO)
35
36 from rhodecode.tests.vcs_operations import (Command, _check_proper_clone)
37
38
39 @pytest.mark.usefixtures("disable_locking", "disable_anonymous_user")
40 class TestVCSOperations(object):
41 def test_clone_by_auth_token(
42 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
43
44 enable_auth_plugins.enable([
45 'egg:rhodecode-enterprise-ce#token',
46 'egg:rhodecode-enterprise-ce#rhodecode'
47 ])
48
49 user = user_util.create_user()
50 token = user.auth_tokens[1]
51
52 clone_url = rc_web_server.repo_clone_url(
53 HG_REPO, user=user.username, passwd=token)
54
55 stdout, stderr = Command('/tmp').execute(
56 'hg clone', clone_url, tmpdir.strpath)
57
58 _check_proper_clone(stdout, stderr, 'hg')
59
60 def test_clone_by_auth_token_expired(
61 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
62 enable_auth_plugins.enable([
63 'egg:rhodecode-enterprise-ce#token',
64 'egg:rhodecode-enterprise-ce#rhodecode'
65 ])
66
67 user = user_util.create_user()
68 auth_token = AuthTokenModel().create(
69 user.user_id, 'test-token', -10, AuthTokenModel.cls.ROLE_VCS)
70 token = auth_token.api_key
71
72 clone_url = rc_web_server.repo_clone_url(
73 HG_REPO, user=user.username, passwd=token)
74
75 stdout, stderr = Command('/tmp').execute(
76 'hg clone', clone_url, tmpdir.strpath)
77 assert 'abort: authorization failed' in stderr
78
79 msg = 'reason: bad or inactive token.'
80 rc_web_server.assert_message_in_server_logs(msg)
81
82 def test_clone_by_auth_token_bad_role(
83 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
84 enable_auth_plugins.enable([
85 'egg:rhodecode-enterprise-ce#token',
86 'egg:rhodecode-enterprise-ce#rhodecode'
87 ])
88
89 user = user_util.create_user()
90 auth_token = AuthTokenModel().create(
91 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_API)
92 token = auth_token.api_key
93
94 clone_url = rc_web_server.repo_clone_url(
95 HG_REPO, user=user.username, passwd=token)
96
97 stdout, stderr = Command('/tmp').execute(
98 'hg clone', clone_url, tmpdir.strpath)
99 assert 'abort: authorization failed' in stderr
100
101 def test_clone_by_auth_token_user_disabled(
102 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
103 enable_auth_plugins.enable([
104 'egg:rhodecode-enterprise-ce#token',
105 'egg:rhodecode-enterprise-ce#rhodecode'
106 ])
107
108 user = user_util.create_user()
109 user.active = False
110 Session().add(user)
111 Session().commit()
112 token = user.auth_tokens[1]
113
114 clone_url = rc_web_server.repo_clone_url(
115 HG_REPO, user=user.username, passwd=token)
116
117 stdout, stderr = Command('/tmp').execute(
118 'hg clone', clone_url, tmpdir.strpath)
119 assert 'abort: authorization failed' in stderr
120
121 msg = 'reason: account not active.'
122 rc_web_server.assert_message_in_server_logs(msg)
123
124 def test_clone_by_auth_token_with_scope(
125 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
126 enable_auth_plugins.enable([
127 'egg:rhodecode-enterprise-ce#token',
128 'egg:rhodecode-enterprise-ce#rhodecode'
129 ])
130
131 user = user_util.create_user()
132 auth_token = AuthTokenModel().create(
133 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_VCS)
134 token = auth_token.api_key
135
136 # manually set scope
137 auth_token.repo = Repository.get_by_repo_name(HG_REPO)
138 Session().add(auth_token)
139 Session().commit()
140
141 clone_url = rc_web_server.repo_clone_url(
142 HG_REPO, user=user.username, passwd=token)
143
144 stdout, stderr = Command('/tmp').execute(
145 'hg clone', clone_url, tmpdir.strpath)
146 _check_proper_clone(stdout, stderr, 'hg')
147
148 def test_clone_by_auth_token_with_wrong_scope(
149 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
150 enable_auth_plugins.enable([
151 'egg:rhodecode-enterprise-ce#token',
152 'egg:rhodecode-enterprise-ce#rhodecode'
153 ])
154
155 user = user_util.create_user()
156 auth_token = AuthTokenModel().create(
157 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_VCS)
158 token = auth_token.api_key
159
160 # manually set scope
161 auth_token.repo = Repository.get_by_repo_name(GIT_REPO)
162 Session().add(auth_token)
163 Session().commit()
164
165 clone_url = rc_web_server.repo_clone_url(
166 HG_REPO, user=user.username, passwd=token)
167
168 stdout, stderr = Command('/tmp').execute(
169 'hg clone', clone_url, tmpdir.strpath)
170
171 assert 'abort: authorization failed' in stderr
172
173 msg = 'reason: bad or inactive token.'
174 rc_web_server.assert_message_in_server_logs(msg)
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -18,8 +17,14 b''
18 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
19
21 import pytest
20 import pytest # noqa
22 from rhodecode.lib import ext_json
21
22 # keep the imports to have a toplevel conftest.py but still importable from EE edition
23 from rhodecode.tests.conftest_common import ( # noqa
24 pytest_generate_tests,
25 pytest_runtest_makereport,
26 pytest_addoption
27 )
23
28
24
29
25 pytest_plugins = [
30 pytest_plugins = [
@@ -29,121 +34,7 b' pytest_plugins = ['
29
34
30
35
31 def pytest_configure(config):
36 def pytest_configure(config):
32 from rhodecode.config import patches
37 from rhodecode.config import patches # noqa
33
34
35 def pytest_addoption(parser):
36
37 def _parse_json(value):
38 return ext_json.str_json(value) if value else None
39
40 def _split_comma(value):
41 return value.split(',')
42
43 parser.addoption(
44 '--keep-tmp-path', action='store_true',
45 help="Keep the test temporary directories")
46
47 parser.addoption(
48 '--backends', action='store', type=_split_comma,
49 default=['git', 'hg', 'svn'],
50 help="Select which backends to test for backend specific tests.")
51 parser.addoption(
52 '--dbs', action='store', type=_split_comma,
53 default=['sqlite'],
54 help="Select which database to test for database specific tests. "
55 "Possible options are sqlite,postgres,mysql")
56 parser.addoption(
57 '--appenlight', '--ae', action='store_true',
58 help="Track statistics in appenlight.")
59 parser.addoption(
60 '--appenlight-api-key', '--ae-key',
61 help="API key for Appenlight.")
62 parser.addoption(
63 '--appenlight-url', '--ae-url',
64 default="https://ae.rhodecode.com",
65 help="Appenlight service URL, defaults to https://ae.rhodecode.com")
66 parser.addoption(
67 '--sqlite-connection-string', action='store',
68 default='', help="Connection string for the dbs tests with SQLite")
69 parser.addoption(
70 '--postgres-connection-string', action='store',
71 default='', help="Connection string for the dbs tests with Postgres")
72 parser.addoption(
73 '--mysql-connection-string', action='store',
74 default='', help="Connection string for the dbs tests with MySQL")
75 parser.addoption(
76 '--repeat', type=int, default=100,
77 help="Number of repetitions in performance tests.")
78
79 parser.addoption(
80 '--test-loglevel', dest='test_loglevel',
81 help="Set default Logging level for tests, critical(default), error, warn , info, debug")
82 group = parser.getgroup('pylons')
83 group.addoption(
84 '--with-pylons', dest='pyramid_config',
85 help="Set up a Pylons environment with the specified config file.")
86 group.addoption(
87 '--ini-config-override', action='store', type=_parse_json,
88 default=None, dest='pyramid_config_override', help=(
89 "Overrides the .ini file settings. Should be specified in JSON"
90 " format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
91 )
92 )
93 parser.addini(
94 'pyramid_config',
95 "Set up a Pyramid environment with the specified config file.")
96
97 vcsgroup = parser.getgroup('vcs')
98 vcsgroup.addoption(
99 '--without-vcsserver', dest='with_vcsserver', action='store_false',
100 help="Do not start the VCSServer in a background process.")
101 vcsgroup.addoption(
102 '--with-vcsserver-http', dest='vcsserver_config_http',
103 help="Start the HTTP VCSServer with the specified config file.")
104 vcsgroup.addoption(
105 '--vcsserver-protocol', dest='vcsserver_protocol',
106 help="Start the VCSServer with HTTP protocol support.")
107 vcsgroup.addoption(
108 '--vcsserver-config-override', action='store', type=_parse_json,
109 default=None, dest='vcsserver_config_override', help=(
110 "Overrides the .ini file settings for the VCSServer. "
111 "Should be specified in JSON "
112 "format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
113 )
114 )
115 vcsgroup.addoption(
116 '--vcsserver-port', action='store', type=int,
117 default=None, help=(
118 "Allows to set the port of the vcsserver. Useful when testing "
119 "against an already running server and random ports cause "
120 "trouble."))
121 parser.addini(
122 'vcsserver_config_http',
123 "Start the HTTP VCSServer with the specified config file.")
124 parser.addini(
125 'vcsserver_protocol',
126 "Start the VCSServer with HTTP protocol support.")
127
128
129 @pytest.hookimpl(tryfirst=True, hookwrapper=True)
130 def pytest_runtest_makereport(item, call):
131 """
132 Adding the remote traceback if the exception has this information.
133
134 VCSServer attaches this information as the attribute `_vcs_server_traceback`
135 to the exception instance.
136 """
137 outcome = yield
138 report = outcome.get_result()
139
140 if call.excinfo:
141 exc = call.excinfo.value
142 vcsserver_traceback = getattr(exc, '_vcs_server_traceback', None)
143
144 if vcsserver_traceback and report.outcome == 'failed':
145 section = f'VCSServer remote traceback {report.when}'
146 report.sections.append((section, vcsserver_traceback))
147
38
148
39
149 def pytest_collection_modifyitems(session, config, items):
40 def pytest_collection_modifyitems(session, config, items):
@@ -152,7 +43,7 b' def pytest_collection_modifyitems(sessio'
152 i for i in items if getattr(i.obj, '__test__', True)]
43 i for i in items if getattr(i.obj, '__test__', True)]
153 items[:] = remaining
44 items[:] = remaining
154
45
155 # NOTE(marcink): custom test ordering, db tests and vcstests are slowes and should
46 # NOTE(marcink): custom test ordering, db tests and vcstests are slowest and should
156 # be executed at the end for faster test feedback
47 # be executed at the end for faster test feedback
157 def sorter(item):
48 def sorter(item):
158 pos = 0
49 pos = 0
@@ -165,37 +56,3 b' def pytest_collection_modifyitems(sessio'
165 return pos
56 return pos
166
57
167 items.sort(key=sorter)
58 items.sort(key=sorter)
168
169
170 def get_backends_from_metafunc(metafunc):
171 requested_backends = set(metafunc.config.getoption('--backends'))
172 backend_mark = metafunc.definition.get_closest_marker('backends')
173 if backend_mark:
174 # Supported backends by this test function, created from
175 # pytest.mark.backends
176 backends = backend_mark.args
177 elif hasattr(metafunc.cls, 'backend_alias'):
178 # Support class attribute "backend_alias", this is mainly
179 # for legacy reasons for tests not yet using pytest.mark.backends
180 backends = [metafunc.cls.backend_alias]
181 else:
182 backends = metafunc.config.getoption('--backends')
183 return requested_backends.intersection(backends)
184
185
186 def pytest_generate_tests(metafunc):
187
188 # Support test generation based on --backend parameter
189 if 'backend_alias' in metafunc.fixturenames:
190 backends = get_backends_from_metafunc(metafunc)
191 scope = None
192 if not backends:
193 pytest.skip("Not enabled for any of selected backends")
194
195 metafunc.parametrize('backend_alias', backends, scope=scope)
196
197 backend_mark = metafunc.definition.get_closest_marker('backends')
198 if backend_mark:
199 backends = get_backends_from_metafunc(metafunc)
200 if not backends:
201 pytest.skip("Not enabled for any of selected backends")
@@ -13,6 +13,8 b' addopts ='
13 --capture=no
13 --capture=no
14 --show-capture=all
14 --show-capture=all
15
15
16 # --test-loglevel=INFO, show log-level during execution
17
16 markers =
18 markers =
17 vcs_operations: Mark tests depending on a running RhodeCode instance.
19 vcs_operations: Mark tests depending on a running RhodeCode instance.
18 xfail_backends: Mark tests as xfail for given backends.
20 xfail_backends: Mark tests as xfail for given backends.
@@ -23,15 +23,17 b' import datetime'
23
23
24 import pytest
24 import pytest
25
25
26 from rhodecode.lib.str_utils import safe_str
26 from rhodecode.tests import *
27 from rhodecode.tests import *
27 from rhodecode.tests.fixture import FIXTURES
28 from rhodecode.tests.fixture import FIXTURES
28 from rhodecode.model.db import UserLog
29 from rhodecode.model.db import UserLog
29 from rhodecode.model.meta import Session
30 from rhodecode.model.meta import Session
30 from rhodecode.lib.utils2 import safe_unicode
31
31
32
32
33 def route_path(name, params=None, **kwargs):
33 def route_path(name, params=None, **kwargs):
34 import urllib.request, urllib.parse, urllib.error
34 import urllib.request
35 import urllib.parse
36 import urllib.error
35 from rhodecode.apps._base import ADMIN_PREFIX
37 from rhodecode.apps._base import ADMIN_PREFIX
36
38
37 base_url = {
39 base_url = {
@@ -69,7 +71,7 b' class TestAdminController(object):'
69 for row in csv.DictReader(f):
71 for row in csv.DictReader(f):
70 ul = UserLog()
72 ul = UserLog()
71 for k, v in row.items():
73 for k, v in row.items():
72 v = safe_unicode(v)
74 v = safe_str(v)
73 if k == 'action_date':
75 if k == 'action_date':
74 v = strptime(v)
76 v = strptime(v)
75 if k in ['user_id', 'repository_id']:
77 if k in ['user_id', 'repository_id']:
@@ -24,7 +24,9 b' from rhodecode.model.settings import Set'
24
24
25
25
26 def route_path(name, params=None, **kwargs):
26 def route_path(name, params=None, **kwargs):
27 import urllib.request, urllib.parse, urllib.error
27 import urllib.request
28 import urllib.parse
29 import urllib.error
28 from rhodecode.apps._base import ADMIN_PREFIX
30 from rhodecode.apps._base import ADMIN_PREFIX
29
31
30 base_url = {
32 base_url = {
@@ -26,7 +26,9 b' fixture = Fixture()'
26
26
27
27
28 def route_path(name, params=None, **kwargs):
28 def route_path(name, params=None, **kwargs):
29 import urllib.request, urllib.parse, urllib.error
29 import urllib.request
30 import urllib.parse
31 import urllib.error
30 from rhodecode.apps._base import ADMIN_PREFIX
32 from rhodecode.apps._base import ADMIN_PREFIX
31
33
32 base_url = {
34 base_url = {
@@ -50,7 +52,10 b' class TestAdminMainView(TestController):'
50 response = self.app.get(route_path('admin_home'), status=200)
52 response = self.app.get(route_path('admin_home'), status=200)
51 response.mustcontain("Administration area")
53 response.mustcontain("Administration area")
52
54
53 def test_redirect_pull_request_view(self, view):
55 @pytest.mark.parametrize('view', [
56 'pull_requests_global',
57 ])
58 def test_redirect_pull_request_view_global(self, view):
54 self.log_user()
59 self.log_user()
55 self.app.get(
60 self.app.get(
56 route_path(view, pull_request_id='xxxx'),
61 route_path(view, pull_request_id='xxxx'),
@@ -28,7 +28,9 b' from rhodecode.tests import ('
28
28
29
29
30 def route_path(name, params=None, **kwargs):
30 def route_path(name, params=None, **kwargs):
31 import urllib.request, urllib.parse, urllib.error
31 import urllib.request
32 import urllib.parse
33 import urllib.error
32 from rhodecode.apps._base import ADMIN_PREFIX
34 from rhodecode.apps._base import ADMIN_PREFIX
33
35
34 base_url = {
36 base_url = {
@@ -17,7 +17,9 b''
17 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19
19
20 import urllib.request, urllib.parse, urllib.error
20 import urllib.request
21 import urllib.parse
22 import urllib.error
21
23
22 import mock
24 import mock
23 import pytest
25 import pytest
@@ -42,7 +44,9 b' fixture = Fixture()'
42
44
43
45
44 def route_path(name, params=None, **kwargs):
46 def route_path(name, params=None, **kwargs):
45 import urllib.request, urllib.parse, urllib.error
47 import urllib.request
48 import urllib.parse
49 import urllib.error
46
50
47 base_url = {
51 base_url = {
48 'repos': ADMIN_PREFIX + '/repos',
52 'repos': ADMIN_PREFIX + '/repos',
@@ -92,12 +96,14 b' class TestAdminRepos(object):'
92 assert ['hg', 'git', 'svn'] == [x.get('value') for x in assert_response.get_elements('[name=repo_type]')]
96 assert ['hg', 'git', 'svn'] == [x.get('value') for x in assert_response.get_elements('[name=repo_type]')]
93
97
94 @pytest.mark.parametrize(
98 @pytest.mark.parametrize(
95 "suffix", [u'', u'xxa'], ids=['', 'non-ascii'])
99 "suffix", ['', 'xxa'], ids=['', 'non-ascii'])
96 def test_create(self, autologin_user, backend, suffix, csrf_token):
100 def test_create(self, autologin_user, backend, suffix, csrf_token):
97 repo_name_unicode = backend.new_repo_name(suffix=suffix)
101 repo_name_unicode = backend.new_repo_name(suffix=suffix)
98 repo_name = repo_name_unicode.encode('utf8')
102 repo_name = repo_name_unicode
99 description_unicode = u'description for newly created repo' + suffix
103
100 description = description_unicode.encode('utf8')
104 description_unicode = 'description for newly created repo' + suffix
105 description = description_unicode
106
101 response = self.app.post(
107 response = self.app.post(
102 route_path('repo_create'),
108 route_path('repo_create'),
103 fixture._get_repo_create_params(
109 fixture._get_repo_create_params(
@@ -127,20 +133,20 b' class TestAdminRepos(object):'
127 self.assert_repository_is_created_correctly(
133 self.assert_repository_is_created_correctly(
128 repo_name, description, backend)
134 repo_name, description, backend)
129
135
130 @pytest.mark.parametrize("suffix", [u'', u'Δ…Δ‡Δ™'], ids=['', 'non-ascii'])
136 @pytest.mark.parametrize("suffix", ['', '_Δ…Δ‡Δ™'], ids=['', 'non-ascii'])
131 def test_create_in_group(
137 def test_create_in_group(
132 self, autologin_user, backend, suffix, csrf_token):
138 self, autologin_user, backend, suffix, csrf_token):
133 # create GROUP
139 # create GROUP
134 group_name = 'sometest_%s' % backend.alias
140 group_name = f'sometest_{backend.alias}'
135 gr = RepoGroupModel().create(group_name=group_name,
141 gr = RepoGroupModel().create(group_name=group_name,
136 group_description='test',
142 group_description='test',
137 owner=TEST_USER_ADMIN_LOGIN)
143 owner=TEST_USER_ADMIN_LOGIN)
138 Session().commit()
144 Session().commit()
139
145
140 repo_name = u'ingroup' + suffix
146 repo_name = f'ingroup{suffix}'
141 repo_name_full = RepoGroup.url_sep().join(
147 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
142 [group_name, repo_name])
148 description = 'description for newly created repo'
143 description = u'description for newly created repo'
149
144 self.app.post(
150 self.app.post(
145 route_path('repo_create'),
151 route_path('repo_create'),
146 fixture._get_repo_create_params(
152 fixture._get_repo_create_params(
@@ -483,17 +489,15 b' class TestAdminRepos(object):'
483 # repo must not be in filesystem !
489 # repo must not be in filesystem !
484 assert not repo_on_filesystem(repo_name)
490 assert not repo_on_filesystem(repo_name)
485
491
486 def assert_repository_is_created_correctly(
492 def assert_repository_is_created_correctly(self, repo_name, description, backend):
487 self, repo_name, description, backend):
493 url_quoted_repo_name = urllib.parse.quote(repo_name)
488 repo_name_utf8 = safe_str(repo_name)
489
494
490 # run the check page that triggers the flash message
495 # run the check page that triggers the flash message
491 response = self.app.get(
496 response = self.app.get(
492 route_path('repo_creating_check', repo_name=safe_str(repo_name)))
497 route_path('repo_creating_check', repo_name=repo_name))
493 assert response.json == {u'result': True}
498 assert response.json == {'result': True}
494
499
495 flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
500 flash_msg = 'Created repository <a href="/{}">{}</a>'.format(url_quoted_repo_name, repo_name)
496 urllib.parse.quote(repo_name_utf8), repo_name)
497 assert_session_flash(response, flash_msg)
501 assert_session_flash(response, flash_msg)
498
502
499 # test if the repo was created in the database
503 # test if the repo was created in the database
@@ -504,7 +508,7 b' class TestAdminRepos(object):'
504
508
505 # test if the repository is visible in the list ?
509 # test if the repository is visible in the list ?
506 response = self.app.get(
510 response = self.app.get(
507 h.route_path('repo_summary', repo_name=safe_str(repo_name)))
511 h.route_path('repo_summary', repo_name=repo_name))
508 response.mustcontain(repo_name)
512 response.mustcontain(repo_name)
509 response.mustcontain(backend.alias)
513 response.mustcontain(backend.alias)
510
514
@@ -33,7 +33,9 b' fixture = Fixture()'
33
33
34
34
35 def route_path(name, params=None, **kwargs):
35 def route_path(name, params=None, **kwargs):
36 import urllib.request, urllib.parse, urllib.error
36 import urllib.request
37 import urllib.parse
38 import urllib.error
37
39
38 base_url = {
40 base_url = {
39 'repo_groups': ADMIN_PREFIX + '/repo_groups',
41 'repo_groups': ADMIN_PREFIX + '/repo_groups',
@@ -106,7 +108,7 b' class TestAdminRepositoryGroups(object):'
106 'hg_repo_Δ…Δ‡',
108 'hg_repo_Δ…Δ‡',
107 ])
109 ])
108 def test_create(self, autologin_user, repo_group_name, csrf_token):
110 def test_create(self, autologin_user, repo_group_name, csrf_token):
109 repo_group_name_unicode = repo_group_name.decode('utf8')
111 repo_group_name_non_ascii = repo_group_name
110 description = 'description for newly created repo group'
112 description = 'description for newly created repo group'
111
113
112 response = self.app.post(
114 response = self.app.post(
@@ -123,14 +125,14 b' class TestAdminRepositoryGroups(object):'
123 assert_session_flash(
125 assert_session_flash(
124 response,
126 response,
125 'Created repository group <a href="%s">%s</a>' % (
127 'Created repository group <a href="%s">%s</a>' % (
126 repo_gr_url, repo_group_name_unicode))
128 repo_gr_url, repo_group_name_non_ascii))
127
129
128 # # test if the repo group was created in the database
130 # # test if the repo group was created in the database
129 new_repo_group = RepoGroupModel()._get_repo_group(
131 new_repo_group = RepoGroupModel()._get_repo_group(
130 repo_group_name_unicode)
132 repo_group_name_non_ascii)
131 assert new_repo_group is not None
133 assert new_repo_group is not None
132
134
133 assert new_repo_group.group_name == repo_group_name_unicode
135 assert new_repo_group.group_name == repo_group_name_non_ascii
134 assert new_repo_group.group_description == description
136 assert new_repo_group.group_description == description
135
137
136 # test if the repository is visible in the list ?
138 # test if the repository is visible in the list ?
@@ -143,7 +145,7 b' class TestAdminRepositoryGroups(object):'
143 if not is_on_filesystem:
145 if not is_on_filesystem:
144 self.fail('no repo group %s in filesystem' % repo_group_name)
146 self.fail('no repo group %s in filesystem' % repo_group_name)
145
147
146 RepoGroupModel().delete(repo_group_name_unicode)
148 RepoGroupModel().delete(repo_group_name_non_ascii)
147 Session().commit()
149 Session().commit()
148
150
149 @pytest.mark.parametrize('repo_group_name', [
151 @pytest.mark.parametrize('repo_group_name', [
@@ -159,7 +161,7 b' class TestAdminRepositoryGroups(object):'
159
161
160 expected_group_name = '{}/{}'.format(
162 expected_group_name = '{}/{}'.format(
161 parent_group_name, repo_group_name)
163 parent_group_name, repo_group_name)
162 expected_group_name_unicode = expected_group_name.decode('utf8')
164 expected_group_name_non_ascii = expected_group_name
163
165
164 try:
166 try:
165 response = self.app.post(
167 response = self.app.post(
@@ -175,9 +177,9 b' class TestAdminRepositoryGroups(object):'
175 u'Created repository group <a href="%s">%s</a>' % (
177 u'Created repository group <a href="%s">%s</a>' % (
176 h.route_path('repo_group_home',
178 h.route_path('repo_group_home',
177 repo_group_name=expected_group_name),
179 repo_group_name=expected_group_name),
178 expected_group_name_unicode))
180 expected_group_name_non_ascii))
179 finally:
181 finally:
180 RepoGroupModel().delete(expected_group_name_unicode)
182 RepoGroupModel().delete(expected_group_name_non_ascii)
181 Session().commit()
183 Session().commit()
182
184
183 def test_user_with_creation_permissions_cannot_create_subgroups(
185 def test_user_with_creation_permissions_cannot_create_subgroups(
@@ -22,19 +22,20 b' import pytest'
22
22
23 import rhodecode
23 import rhodecode
24 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.lib.utils2 import md5
25 from rhodecode.lib.hash_utils import md5_safe
26 from rhodecode.model.db import RhodeCodeUi
26 from rhodecode.model.db import RhodeCodeUi
27 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
28 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
28 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
29 from rhodecode.tests import assert_session_flash
29 from rhodecode.tests import assert_session_flash
30 from rhodecode.tests.utils import AssertResponse
31
30
32
31
33 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
32 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
34
33
35
34
36 def route_path(name, params=None, **kwargs):
35 def route_path(name, params=None, **kwargs):
37 import urllib.request, urllib.parse, urllib.error
36 import urllib.request
37 import urllib.parse
38 import urllib.error
38 from rhodecode.apps._base import ADMIN_PREFIX
39 from rhodecode.apps._base import ADMIN_PREFIX
39
40
40 base_url = {
41 base_url = {
@@ -233,6 +234,7 b' class TestAdminSettingsGlobal(object):'
233 route_path('admin_settings_global_update'), params=params)
234 route_path('admin_settings_global_update'), params=params)
234
235
235 assert_session_flash(response, 'Updated application settings')
236 assert_session_flash(response, 'Updated application settings')
237
236 app_settings = SettingsModel().get_all_settings()
238 app_settings = SettingsModel().get_all_settings()
237 del settings['csrf_token']
239 del settings['csrf_token']
238 for key, value in settings.items():
240 for key, value in settings.items():
@@ -413,8 +415,9 b' class TestAdminSettingsVcs(object):'
413
415
414 @pytest.fixture()
416 @pytest.fixture()
415 def disable_sql_cache(self, request):
417 def disable_sql_cache(self, request):
418 # patch _do_orm_execute so it returns None similar like if we don't use a cached query
416 patcher = mock.patch(
419 patcher = mock.patch(
417 'rhodecode.lib.caching_query.FromCache.process_query')
420 'rhodecode.lib.caching_query.ORMCache._do_orm_execute', return_value=None)
418 request.addfinalizer(patcher.stop)
421 request.addfinalizer(patcher.stop)
419 patcher.start()
422 patcher.start()
420
423
@@ -428,8 +431,7 b' class TestAdminSettingsVcs(object):'
428 @pytest.fixture(scope='class', autouse=True)
431 @pytest.fixture(scope='class', autouse=True)
429 def cleanup_settings(self, request, baseapp):
432 def cleanup_settings(self, request, baseapp):
430 ui_id = RhodeCodeUi.ui_id
433 ui_id = RhodeCodeUi.ui_id
431 original_ids = list(
434 original_ids = [r.ui_id for r in RhodeCodeUi.query().with_entities(ui_id)]
432 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
433
435
434 @request.addfinalizer
436 @request.addfinalizer
435 def cleanup():
437 def cleanup():
@@ -644,9 +646,9 b' class TestAdminSettingsIssueTracker(obje'
644 }
646 }
645 self.app.post(post_url, post_data, status=302)
647 self.app.post(post_url, post_data, status=302)
646 settings = SettingsModel().get_all_settings()
648 settings = SettingsModel().get_all_settings()
647 self.uid = md5(pattern)
649 self.uid = md5_safe(pattern)
648 assert settings[self.PATTERN_KEY+self.uid] == pattern
650 assert settings[self.PATTERN_KEY+self.uid] == pattern
649 self.another_uid = md5(another_pattern)
651 self.another_uid = md5_safe(another_pattern)
650 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
652 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
651
653
652 @request.addfinalizer
654 @request.addfinalizer
@@ -654,7 +656,7 b' class TestAdminSettingsIssueTracker(obje'
654 defaults = SettingsModel().get_all_settings()
656 defaults = SettingsModel().get_all_settings()
655
657
656 entries = [name for name in defaults if (
658 entries = [name for name in defaults if (
657 (self.uid in name) or (self.another_uid) in name)]
659 (self.uid in name) or (self.another_uid in name))]
658 start = len(self.RC_PREFIX)
660 start = len(self.RC_PREFIX)
659 for del_key in entries:
661 for del_key in entries:
660 # TODO: anderson: get_by_name needs name without prefix
662 # TODO: anderson: get_by_name needs name without prefix
@@ -667,7 +669,7 b' class TestAdminSettingsIssueTracker(obje'
667 self, autologin_user, backend, csrf_token, request):
669 self, autologin_user, backend, csrf_token, request):
668
670
669 old_pattern = 'issuetracker_pat1'
671 old_pattern = 'issuetracker_pat1'
670 old_uid = md5(old_pattern)
672 old_uid = md5_safe(old_pattern)
671
673
672 post_url = route_path('admin_settings_issuetracker_update')
674 post_url = route_path('admin_settings_issuetracker_update')
673 post_data = {
675 post_data = {
@@ -681,7 +683,7 b' class TestAdminSettingsIssueTracker(obje'
681 self.app.post(post_url, post_data, status=302)
683 self.app.post(post_url, post_data, status=302)
682
684
683 new_pattern = 'issuetracker_pat1_edited'
685 new_pattern = 'issuetracker_pat1_edited'
684 self.new_uid = md5(new_pattern)
686 self.new_uid = md5_safe(new_pattern)
685
687
686 post_url = route_path('admin_settings_issuetracker_update')
688 post_url = route_path('admin_settings_issuetracker_update')
687 post_data = {
689 post_data = {
@@ -708,7 +710,7 b' class TestAdminSettingsIssueTracker(obje'
708 self, autologin_user, csrf_token, request, settings_util):
710 self, autologin_user, csrf_token, request, settings_util):
709 prefix = 'issuetracker'
711 prefix = 'issuetracker'
710 pattern = 'issuetracker_pat'
712 pattern = 'issuetracker_pat'
711 self.uid = md5(pattern)
713 self.uid = md5_safe(pattern)
712 pattern_key = '_'.join([prefix, 'pat', self.uid])
714 pattern_key = '_'.join([prefix, 'pat', self.uid])
713 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
715 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
714 desc_key = '_'.join([prefix, 'desc', self.uid])
716 desc_key = '_'.join([prefix, 'desc', self.uid])
@@ -742,7 +744,7 b' class TestAdminSettingsIssueTracker(obje'
742 self, autologin_user, backend, csrf_token, settings_util, xhr_header):
744 self, autologin_user, backend, csrf_token, settings_util, xhr_header):
743
745
744 old_pattern = 'issuetracker_pat_deleted'
746 old_pattern = 'issuetracker_pat_deleted'
745 old_uid = md5(old_pattern)
747 old_uid = md5_safe(old_pattern)
746
748
747 post_url = route_path('admin_settings_issuetracker_update')
749 post_url = route_path('admin_settings_issuetracker_update')
748 post_data = {
750 post_data = {
@@ -30,7 +30,9 b' fixture = Fixture()'
30
30
31
31
32 def route_path(name, params=None, **kwargs):
32 def route_path(name, params=None, **kwargs):
33 import urllib.request, urllib.parse, urllib.error
33 import urllib.request
34 import urllib.parse
35 import urllib.error
34 from rhodecode.apps._base import ADMIN_PREFIX
36 from rhodecode.apps._base import ADMIN_PREFIX
35
37
36 base_url = {
38 base_url = {
@@ -34,7 +34,9 b' fixture = Fixture()'
34
34
35
35
36 def route_path(name, params=None, **kwargs):
36 def route_path(name, params=None, **kwargs):
37 import urllib.request, urllib.parse, urllib.error
37 import urllib.request
38 import urllib.parse
39 import urllib.error
38 from rhodecode.apps._base import ADMIN_PREFIX
40 from rhodecode.apps._base import ADMIN_PREFIX
39
41
40 base_url = {
42 base_url = {
@@ -28,7 +28,9 b' fixture = Fixture()'
28
28
29
29
30 def route_path(name, params=None, **kwargs):
30 def route_path(name, params=None, **kwargs):
31 import urllib.request, urllib.parse, urllib.error
31 import urllib.request
32 import urllib.parse
33 import urllib.error
32 from rhodecode.apps._base import ADMIN_PREFIX
34 from rhodecode.apps._base import ADMIN_PREFIX
33
35
34 base_url = {
36 base_url = {
@@ -27,7 +27,9 b' from rhodecode.apps.file_store import ut'
27
27
28
28
29 def route_path(name, params=None, **kwargs):
29 def route_path(name, params=None, **kwargs):
30 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
31 import urllib.parse
32 import urllib.error
31
33
32 base_url = {
34 base_url = {
33 'upload_file': '/_file_store/upload',
35 'upload_file': '/_file_store/upload',
@@ -59,7 +61,7 b' class TestFileStoreViews(TestController)'
59 status = 200
61 status = 200
60 store = utils.get_file_storage({config_keys.store_path: store_path})
62 store = utils.get_file_storage({config_keys.store_path: store_path})
61 filesystem_file = os.path.join(str(tmpdir), fid)
63 filesystem_file = os.path.join(str(tmpdir), fid)
62 with open(filesystem_file, 'wb') as f:
64 with open(filesystem_file, 'wt') as f:
63 f.write(content)
65 f.write(content)
64
66
65 with open(filesystem_file, 'rb') as f:
67 with open(filesystem_file, 'rb') as f:
@@ -120,7 +122,7 b' class TestFileStoreViews(TestController)'
120 self.log_user()
122 self.log_user()
121 response = self.app.post(
123 response = self.app.post(
122 route_path('upload_file'),
124 route_path('upload_file'),
123 upload_files=[('store_file', 'myfile.txt', 'SOME CONTENT')],
125 upload_files=[('store_file', b'myfile.txt', b'SOME CONTENT')],
124 params={'csrf_token': self.csrf_token},
126 params={'csrf_token': self.csrf_token},
125 status=200)
127 status=200)
126
128
@@ -134,7 +136,7 b' class TestFileStoreViews(TestController)'
134 fid = 'example.txt'
136 fid = 'example.txt'
135
137
136 filesystem_file = os.path.join(str(tmpdir), fid)
138 filesystem_file = os.path.join(str(tmpdir), fid)
137 with open(filesystem_file, 'wb') as f:
139 with open(filesystem_file, 'wt') as f:
138 f.write(content)
140 f.write(content)
139
141
140 with open(filesystem_file, 'rb') as f:
142 with open(filesystem_file, 'rb') as f:
@@ -30,7 +30,8 b' from rhodecode.tests import ('
30
30
31
31
32 def route_path(name, params=None, **kwargs):
32 def route_path(name, params=None, **kwargs):
33 import urllib.request, urllib.parse, urllib.error
33 import urllib.parse
34 import urllib.error
34 from rhodecode.apps._base import ADMIN_PREFIX
35 from rhodecode.apps._base import ADMIN_PREFIX
35
36
36 base_url = {
37 base_url = {
@@ -59,7 +60,7 b' class GistUtility(object):'
59 self._gist_ids = []
60 self._gist_ids = []
60
61
61 def __call__(
62 def __call__(
62 self, f_name, content='some gist', lifetime=-1,
63 self, f_name: bytes, content: bytes = b'some gist', lifetime=-1,
63 description='gist-desc', gist_type='public',
64 description='gist-desc', gist_type='public',
64 acl_level=Gist.GIST_PUBLIC, owner=TEST_USER_ADMIN_LOGIN):
65 acl_level=Gist.GIST_PUBLIC, owner=TEST_USER_ADMIN_LOGIN):
65 gist_mapping = {
66 gist_mapping = {
@@ -94,14 +95,14 b' class TestGistsController(TestController'
94 def test_index_empty(self, create_gist):
95 def test_index_empty(self, create_gist):
95 self.log_user()
96 self.log_user()
96 response = self.app.get(route_path('gists_show'))
97 response = self.app.get(route_path('gists_show'))
97 response.mustcontain('data: [],')
98 response.mustcontain('var gist_data = [];')
98
99
99 def test_index(self, create_gist):
100 def test_index(self, create_gist):
100 self.log_user()
101 self.log_user()
101 g1 = create_gist('gist1')
102 g1 = create_gist(b'gist1')
102 g2 = create_gist('gist2', lifetime=1400)
103 g2 = create_gist(b'gist2', lifetime=1400)
103 g3 = create_gist('gist3', description='gist3-desc')
104 g3 = create_gist(b'gist3', description='gist3-desc')
104 g4 = create_gist('gist4', gist_type='private').gist_access_id
105 g4 = create_gist(b'gist4', gist_type='private').gist_access_id
105 response = self.app.get(route_path('gists_show'))
106 response = self.app.get(route_path('gists_show'))
106
107
107 response.mustcontain(g1.gist_access_id)
108 response.mustcontain(g1.gist_access_id)
@@ -111,13 +112,12 b' class TestGistsController(TestController'
111 response.mustcontain(no=[g4])
112 response.mustcontain(no=[g4])
112
113
113 # Expiration information should be visible
114 # Expiration information should be visible
114 expires_tag = '%s' % h.age_component(
115 expires_tag = str(h.age_component(h.time_to_utcdatetime(g2.gist_expires)))
115 h.time_to_utcdatetime(g2.gist_expires))
116 response.mustcontain(expires_tag.replace('"', '\\"'))
116 response.mustcontain(expires_tag.replace('"', '\\"'))
117
117
118 def test_index_private_gists(self, create_gist):
118 def test_index_private_gists(self, create_gist):
119 self.log_user()
119 self.log_user()
120 gist = create_gist('gist5', gist_type='private')
120 gist = create_gist(b'gist5', gist_type='private')
121 response = self.app.get(route_path('gists_show', params=dict(private=1)))
121 response = self.app.get(route_path('gists_show', params=dict(private=1)))
122
122
123 # and privates
123 # and privates
@@ -125,10 +125,10 b' class TestGistsController(TestController'
125
125
126 def test_index_show_all(self, create_gist):
126 def test_index_show_all(self, create_gist):
127 self.log_user()
127 self.log_user()
128 create_gist('gist1')
128 create_gist(b'gist1')
129 create_gist('gist2', lifetime=1400)
129 create_gist(b'gist2', lifetime=1400)
130 create_gist('gist3', description='gist3-desc')
130 create_gist(b'gist3', description='gist3-desc')
131 create_gist('gist4', gist_type='private')
131 create_gist(b'gist4', gist_type='private')
132
132
133 response = self.app.get(route_path('gists_show', params=dict(all=1)))
133 response = self.app.get(route_path('gists_show', params=dict(all=1)))
134
134
@@ -139,9 +139,9 b' class TestGistsController(TestController'
139
139
140 def test_index_show_all_hidden_from_regular(self, create_gist):
140 def test_index_show_all_hidden_from_regular(self, create_gist):
141 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
141 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
142 create_gist('gist2', gist_type='private')
142 create_gist(b'gist2', gist_type='private')
143 create_gist('gist3', gist_type='private')
143 create_gist(b'gist3', gist_type='private')
144 create_gist('gist4', gist_type='private')
144 create_gist(b'gist4', gist_type='private')
145
145
146 response = self.app.get(route_path('gists_show', params=dict(all=1)))
146 response = self.app.get(route_path('gists_show', params=dict(all=1)))
147
147
@@ -181,7 +181,7 b' class TestGistsController(TestController'
181
181
182 def test_access_expired_gist(self, create_gist):
182 def test_access_expired_gist(self, create_gist):
183 self.log_user()
183 self.log_user()
184 gist = create_gist('never-see-me')
184 gist = create_gist(b'never-see-me')
185 gist.gist_expires = 0 # 1970
185 gist.gist_expires = 0 # 1970
186 Session().add(gist)
186 Session().add(gist)
187 Session().commit()
187 Session().commit()
@@ -269,7 +269,7 b' class TestGistsController(TestController'
269
269
270 def test_delete(self, create_gist):
270 def test_delete(self, create_gist):
271 self.log_user()
271 self.log_user()
272 gist = create_gist('delete-me')
272 gist = create_gist(b'delete-me')
273 response = self.app.post(
273 response = self.app.post(
274 route_path('gist_delete', gist_id=gist.gist_id),
274 route_path('gist_delete', gist_id=gist.gist_id),
275 params={'csrf_token': self.csrf_token})
275 params={'csrf_token': self.csrf_token})
@@ -277,7 +277,7 b' class TestGistsController(TestController'
277
277
278 def test_delete_normal_user_his_gist(self, create_gist):
278 def test_delete_normal_user_his_gist(self, create_gist):
279 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
279 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
280 gist = create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN)
280 gist = create_gist(b'delete-me', owner=TEST_USER_REGULAR_LOGIN)
281
281
282 response = self.app.post(
282 response = self.app.post(
283 route_path('gist_delete', gist_id=gist.gist_id),
283 route_path('gist_delete', gist_id=gist.gist_id),
@@ -286,14 +286,14 b' class TestGistsController(TestController'
286
286
287 def test_delete_normal_user_not_his_own_gist(self, create_gist):
287 def test_delete_normal_user_not_his_own_gist(self, create_gist):
288 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
288 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
289 gist = create_gist('delete-me-2')
289 gist = create_gist(b'delete-me-2')
290
290
291 self.app.post(
291 self.app.post(
292 route_path('gist_delete', gist_id=gist.gist_id),
292 route_path('gist_delete', gist_id=gist.gist_id),
293 params={'csrf_token': self.csrf_token}, status=404)
293 params={'csrf_token': self.csrf_token}, status=404)
294
294
295 def test_show(self, create_gist):
295 def test_show(self, create_gist):
296 gist = create_gist('gist-show-me')
296 gist = create_gist(b'gist-show-me')
297 response = self.app.get(route_path('gist_show', gist_id=gist.gist_access_id))
297 response = self.app.get(route_path('gist_show', gist_id=gist.gist_access_id))
298
298
299 response.mustcontain('added file: gist-show-me<')
299 response.mustcontain('added file: gist-show-me<')
@@ -308,12 +308,12 b' class TestGistsController(TestController'
308 def test_show_without_hg(self, create_gist):
308 def test_show_without_hg(self, create_gist):
309 with mock.patch(
309 with mock.patch(
310 'rhodecode.lib.vcs.settings.ALIASES', ['git']):
310 'rhodecode.lib.vcs.settings.ALIASES', ['git']):
311 gist = create_gist('gist-show-me-again')
311 gist = create_gist(b'gist-show-me-again')
312 self.app.get(
312 self.app.get(
313 route_path('gist_show', gist_id=gist.gist_access_id), status=200)
313 route_path('gist_show', gist_id=gist.gist_access_id), status=200)
314
314
315 def test_show_acl_private(self, create_gist):
315 def test_show_acl_private(self, create_gist):
316 gist = create_gist('gist-show-me-only-when-im-logged-in',
316 gist = create_gist(b'gist-show-me-only-when-im-logged-in',
317 acl_level=Gist.ACL_LEVEL_PRIVATE)
317 acl_level=Gist.ACL_LEVEL_PRIVATE)
318 self.app.get(
318 self.app.get(
319 route_path('gist_show', gist_id=gist.gist_access_id), status=404)
319 route_path('gist_show', gist_id=gist.gist_access_id), status=404)
@@ -331,7 +331,7 b' class TestGistsController(TestController'
331 response.mustcontain('gist-desc')
331 response.mustcontain('gist-desc')
332
332
333 def test_show_as_raw(self, create_gist):
333 def test_show_as_raw(self, create_gist):
334 gist = create_gist('gist-show-me', content='GIST CONTENT')
334 gist = create_gist(b'gist-show-me', content=b'GIST CONTENT')
335 response = self.app.get(
335 response = self.app.get(
336 route_path('gist_show_formatted',
336 route_path('gist_show_formatted',
337 gist_id=gist.gist_access_id, revision='tip',
337 gist_id=gist.gist_access_id, revision='tip',
@@ -339,7 +339,7 b' class TestGistsController(TestController'
339 assert response.text == 'GIST CONTENT'
339 assert response.text == 'GIST CONTENT'
340
340
341 def test_show_as_raw_individual_file(self, create_gist):
341 def test_show_as_raw_individual_file(self, create_gist):
342 gist = create_gist('gist-show-me-raw', content='GIST BODY')
342 gist = create_gist(b'gist-show-me-raw', content=b'GIST BODY')
343 response = self.app.get(
343 response = self.app.get(
344 route_path('gist_show_formatted_path',
344 route_path('gist_show_formatted_path',
345 gist_id=gist.gist_access_id, format='raw',
345 gist_id=gist.gist_access_id, format='raw',
@@ -348,24 +348,24 b' class TestGistsController(TestController'
348
348
349 def test_edit_page(self, create_gist):
349 def test_edit_page(self, create_gist):
350 self.log_user()
350 self.log_user()
351 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
351 gist = create_gist(b'gist-for-edit', content=b'GIST EDIT BODY')
352 response = self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id))
352 response = self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id))
353 response.mustcontain('GIST EDIT BODY')
353 response.mustcontain('GIST EDIT BODY')
354
354
355 def test_edit_page_non_logged_user(self, create_gist):
355 def test_edit_page_non_logged_user(self, create_gist):
356 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
356 gist = create_gist(b'gist-for-edit', content=b'GIST EDIT BODY')
357 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
357 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
358 status=302)
358 status=302)
359
359
360 def test_edit_normal_user_his_gist(self, create_gist):
360 def test_edit_normal_user_his_gist(self, create_gist):
361 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
361 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
362 gist = create_gist('gist-for-edit', owner=TEST_USER_REGULAR_LOGIN)
362 gist = create_gist(b'gist-for-edit', owner=TEST_USER_REGULAR_LOGIN)
363 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id,
363 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id,
364 status=200))
364 status=200))
365
365
366 def test_edit_normal_user_not_his_own_gist(self, create_gist):
366 def test_edit_normal_user_not_his_own_gist(self, create_gist):
367 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
367 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
368 gist = create_gist('delete-me')
368 gist = create_gist(b'delete-me')
369 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
369 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
370 status=404)
370 status=404)
371
371
@@ -375,7 +375,7 b' class TestGistsController(TestController'
375 password = 'test'
375 password = 'test'
376 user = user_util.create_user(
376 user = user_util.create_user(
377 firstname=xss_atack_string, password=password)
377 firstname=xss_atack_string, password=password)
378 create_gist('gist', gist_type='public', owner=user.username)
378 create_gist(b'gist', gist_type='public', owner=user.username)
379 response = self.app.get(route_path('gists_show'))
379 response = self.app.get(route_path('gists_show'))
380 response.mustcontain(xss_escaped_string)
380 response.mustcontain(xss_escaped_string)
381
381
@@ -385,6 +385,6 b' class TestGistsController(TestController'
385 password = 'test'
385 password = 'test'
386 user = user_util.create_user(
386 user = user_util.create_user(
387 lastname=xss_atack_string, password=password)
387 lastname=xss_atack_string, password=password)
388 create_gist('gist', gist_type='public', owner=user.username)
388 create_gist(b'gist', gist_type='public', owner=user.username)
389 response = self.app.get(route_path('gists_show'))
389 response = self.app.get(route_path('gists_show'))
390 response.mustcontain(xss_escaped_string)
390 response.mustcontain(xss_escaped_string)
@@ -18,18 +18,20 b''
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 json
22
23 from . import assert_and_get_repo_list_content
21 from . import assert_and_get_repo_list_content
24 from rhodecode.tests import TestController
22 from rhodecode.tests import TestController
25 from rhodecode.tests.fixture import Fixture
23 from rhodecode.tests.fixture import Fixture
26 from rhodecode.model.db import Repository
24 from rhodecode.model.db import Repository
25 from rhodecode.lib.ext_json import json
26
27
27
28 fixture = Fixture()
28 fixture = Fixture()
29
29
30
30
31 def route_path(name, params=None, **kwargs):
31 def route_path(name, params=None, **kwargs):
32 import urllib.request, urllib.parse, urllib.error
32 import urllib.request
33 import urllib.parse
34 import urllib.error
33
35
34 base_url = {
36 base_url = {
35 'repo_list_data': '/_repos',
37 'repo_list_data': '/_repos',
@@ -17,19 +17,19 b''
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
21 import json
22 import pytest
20 import pytest
23
21
24 from rhodecode.tests import TestController
22 from rhodecode.tests import TestController
25 from rhodecode.tests.fixture import Fixture
23 from rhodecode.tests.fixture import Fixture
26
24 from rhodecode.lib.ext_json import json
27
25
28 fixture = Fixture()
26 fixture = Fixture()
29
27
30
28
31 def route_path(name, params=None, **kwargs):
29 def route_path(name, params=None, **kwargs):
32 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
31 import urllib.parse
32 import urllib.error
33
33
34 base_url = {
34 base_url = {
35 'user_autocomplete_data': '/_users',
35 'user_autocomplete_data': '/_users',
@@ -1,22 +1,4 b''
1
1
2 # Copyright (C) 2016-2020 RhodeCode GmbH
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
6 # (only), as published by the Free Software Foundation.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU Affero General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #
16 # This program is dual-licensed. If you wish to learn more about the
17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # -*- coding: utf-8 -*-
20
2
21 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
22 #
4 #
@@ -36,19 +18,39 b''
36 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
37 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
38
20
39 import json
21
22 # Copyright (C) 2016-2020 RhodeCode GmbH
23 #
24 # This program is free software: you can redistribute it and/or modify
25 # it under the terms of the GNU Affero General Public License, version 3
26 # (only), as published by the Free Software Foundation.
27 #
28 # This program is distributed in the hope that it will be useful,
29 # but WITHOUT ANY WARRANTY; without even the implied warranty of
30 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 # GNU General Public License for more details.
32 #
33 # You should have received a copy of the GNU Affero General Public License
34 # along with this program. If not, see <http://www.gnu.org/licenses/>.
35 #
36 # This program is dual-licensed. If you wish to learn more about the
37 # RhodeCode Enterprise Edition, including its added features, Support services,
38 # and proprietary license terms, please see https://rhodecode.com/licenses/
40
39
41 import pytest
40 import pytest
42
41
43 from rhodecode.tests import TestController
42 from rhodecode.tests import TestController
44 from rhodecode.tests.fixture import Fixture
43 from rhodecode.tests.fixture import Fixture
44 from rhodecode.lib.ext_json import json
45
45
46
46
47 fixture = Fixture()
47 fixture = Fixture()
48
48
49
49
50 def route_path(name, params=None, **kwargs):
50 def route_path(name, params=None, **kwargs):
51 import urllib.request, urllib.parse, urllib.error
51 import urllib.request
52 import urllib.parse
53 import urllib.error
52
54
53 base_url = {
55 base_url = {
54 'user_autocomplete_data': '/_users',
56 'user_autocomplete_data': '/_users',
@@ -163,7 +163,7 b' class TestHomeController(TestController)'
163 'show_version', state, 'bool')
163 'show_version', state, 'bool')
164 Session().add(sett)
164 Session().add(sett)
165 Session().commit()
165 Session().commit()
166 SettingsModel().invalidate_settings_cache()
166 SettingsModel().invalidate_settings_cache(hard=True)
167
167
168 response = self.app.get(route_path('home'))
168 response = self.app.get(route_path('home'))
169 if state is True:
169 if state is True:
@@ -27,7 +27,9 b' from rhodecode.model.db import UserFollo'
27
27
28
28
29 def route_path(name, params=None, **kwargs):
29 def route_path(name, params=None, **kwargs):
30 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
31 import urllib.parse
32 import urllib.error
31
33
32 base_url = {
34 base_url = {
33 'journal': ADMIN_PREFIX + '/journal',
35 'journal': ADMIN_PREFIX + '/journal',
@@ -38,7 +38,9 b" whitelist_view = ['RepoCommitsView:repo_"
38
38
39
39
40 def route_path(name, params=None, **kwargs):
40 def route_path(name, params=None, **kwargs):
41 import urllib.request, urllib.parse, urllib.error
41 import urllib.request
42 import urllib.parse
43 import urllib.error
42 from rhodecode.apps._base import ADMIN_PREFIX
44 from rhodecode.apps._base import ADMIN_PREFIX
43
45
44 base_url = {
46 base_url = {
@@ -160,18 +162,28 b' class TestLoginController(object):'
160 'file:///etc/passwd',
162 'file:///etc/passwd',
161 'ftp://some.ftp.server',
163 'ftp://some.ftp.server',
162 'http://other.domain',
164 'http://other.domain',
163 '/\r\nX-Forwarded-Host: http://example.org',
164 ], ids=no_newline_id_generator)
165 ], ids=no_newline_id_generator)
165 def test_login_bad_came_froms(self, url_came_from):
166 def test_login_bad_came_froms(self, url_came_from):
166 _url = '{}?came_from={}'.format(route_path('login'), url_came_from)
167 _url = '{}?came_from={}'.format(route_path('login'), url_came_from)
167 response = self.app.post(
168 response = self.app.post(
168 _url,
169 _url, {'username': 'test_admin', 'password': 'test12'}, status=302)
169 {'username': 'test_admin', 'password': 'test12'})
170 assert response.status == '302 Found'
170 assert response.status == '302 Found'
171 response = response.follow()
171 response = response.follow()
172 assert response.status == '200 OK'
172 assert response.status == '200 OK'
173 assert response.request.path == '/'
173 assert response.request.path == '/'
174
174
175 @pytest.mark.xfail(reason="newline params changed behaviour in python3")
176 @pytest.mark.parametrize("url_came_from", [
177 '/\r\nX-Forwarded-Host: \rhttp://example.org',
178 ], ids=no_newline_id_generator)
179 def test_login_bad_came_froms_404(self, url_came_from):
180 _url = '{}?came_from={}'.format(route_path('login'), url_came_from)
181 response = self.app.post(
182 _url, {'username': 'test_admin', 'password': 'test12'}, status=302)
183
184 response = response.follow()
185 assert response.status == '404 Not Found'
186
175 def test_login_short_password(self):
187 def test_login_short_password(self):
176 response = self.app.post(route_path('login'),
188 response = self.app.post(route_path('login'),
177 {'username': 'test_admin',
189 {'username': 'test_admin',
@@ -184,7 +196,7 b' class TestLoginController(object):'
184 response = self.app.post(
196 response = self.app.post(
185 route_path('login'),
197 route_path('login'),
186 {'username': user_regular.username,
198 {'username': user_regular.username,
187 'password': u'invalid-non-asci\xe4'.encode('utf8')})
199 'password': 'invalid-non-asci\xe4'.encode('utf8')})
188
200
189 response.mustcontain('invalid user name')
201 response.mustcontain('invalid user name')
190 response.mustcontain('invalid password')
202 response.mustcontain('invalid password')
@@ -486,6 +498,10 b' class TestLoginController(object):'
486 auth_token = user_admin.api_key
498 auth_token = user_admin.api_key
487
499
488 with fixture.anon_access(False):
500 with fixture.anon_access(False):
501 # webtest uses linter to check if response is bytes,
502 # and we use memoryview here as a wrapper, quick turn-off
503 self.app.lint = False
504
489 self.app.get(
505 self.app.get(
490 route_path('repo_commit_raw',
506 route_path('repo_commit_raw',
491 repo_name=HG_REPO, commit_id='tip',
507 repo_name=HG_REPO, commit_id='tip',
@@ -511,6 +527,9 b' class TestLoginController(object):'
511 assert auth_token
527 assert auth_token
512
528
513 with fixture.anon_access(False):
529 with fixture.anon_access(False):
530 # webtest uses linter to check if response is bytes,
531 # and we use memoryview here as a wrapper, quick turn-off
532 self.app.lint = False
514 self.app.get(
533 self.app.get(
515 route_path('repo_commit_raw',
534 route_path('repo_commit_raw',
516 repo_name=HG_REPO, commit_id='tip',
535 repo_name=HG_REPO, commit_id='tip',
@@ -536,6 +555,10 b' class TestLoginController(object):'
536 with mock.patch.dict('rhodecode.CONFIG', whitelist):
555 with mock.patch.dict('rhodecode.CONFIG', whitelist):
537
556
538 with fixture.anon_access(False):
557 with fixture.anon_access(False):
558 # webtest uses linter to check if response is bytes,
559 # and we use memoryview here as a wrapper, quick turn-off
560 self.app.lint = False
561
539 self.app.get(
562 self.app.get(
540 route_path('repo_commit_raw',
563 route_path('repo_commit_raw',
541 repo_name=HG_REPO, commit_id='tip',
564 repo_name=HG_REPO, commit_id='tip',
@@ -552,6 +575,9 b' class TestLoginController(object):'
552 TEST_USER_ADMIN_LOGIN, 'test')
575 TEST_USER_ADMIN_LOGIN, 'test')
553 Session().commit()
576 Session().commit()
554 with fixture.anon_access(False):
577 with fixture.anon_access(False):
578 # webtest uses linter to check if response is bytes,
579 # and we use memoryview here as a wrapper, quick turn-off
580 self.app.lint = False
555 self.app.get(
581 self.app.get(
556 route_path('repo_commit_raw',
582 route_path('repo_commit_raw',
557 repo_name=HG_REPO, commit_id='tip',
583 repo_name=HG_REPO, commit_id='tip',
@@ -572,6 +598,9 b' class TestLoginController(object):'
572 Session().add(new_auth_token)
598 Session().add(new_auth_token)
573 Session().commit()
599 Session().commit()
574 with fixture.anon_access(False):
600 with fixture.anon_access(False):
601 # webtest uses linter to check if response is bytes,
602 # and we use memoryview here as a wrapper, quick turn-off
603 self.app.lint = False
575 self.app.get(
604 self.app.get(
576 route_path('repo_commit_raw',
605 route_path('repo_commit_raw',
577 repo_name=HG_REPO, commit_id='tip',
606 repo_name=HG_REPO, commit_id='tip',
@@ -30,7 +30,9 b' fixture = Fixture()'
30
30
31
31
32 def route_path(name, params=None, **kwargs):
32 def route_path(name, params=None, **kwargs):
33 import urllib.request, urllib.parse, urllib.error
33 import urllib.request
34 import urllib.parse
35 import urllib.error
34 from rhodecode.apps._base import ADMIN_PREFIX
36 from rhodecode.apps._base import ADMIN_PREFIX
35
37
36 base_url = {
38 base_url = {
@@ -34,7 +34,9 b' fixture = Fixture()'
34
34
35
35
36 def route_path(name, params=None, **kwargs):
36 def route_path(name, params=None, **kwargs):
37 import urllib.request, urllib.parse, urllib.error
37 import urllib.request
38 import urllib.parse
39 import urllib.error
38 from rhodecode.apps._base import ADMIN_PREFIX
40 from rhodecode.apps._base import ADMIN_PREFIX
39
41
40 base_url = {
42 base_url = {
@@ -133,7 +135,8 b' class TestNotificationsController(TestCo'
133 u2 = User.get(u2.user_id)
135 u2 = User.get(u2.user_id)
134
136
135 # check DB
137 # check DB
136 get_notif = lambda un: [x.notification for x in un]
138 def get_notif(un):
139 return [x.notification for x in un]
137 assert get_notif(cur_user.notifications) == [notification]
140 assert get_notif(cur_user.notifications) == [notification]
138 assert get_notif(u1.notifications) == [notification]
141 assert get_notif(u1.notifications) == [notification]
139 assert get_notif(u2.notifications) == [notification]
142 assert get_notif(u2.notifications) == [notification]
@@ -28,7 +28,9 b' fixture = Fixture()'
28
28
29
29
30 def route_path(name, params=None, **kwargs):
30 def route_path(name, params=None, **kwargs):
31 import urllib.request, urllib.parse, urllib.error
31 import urllib.request
32 import urllib.parse
33 import urllib.error
32 from rhodecode.apps._base import ADMIN_PREFIX
34 from rhodecode.apps._base import ADMIN_PREFIX
33
35
34 base_url = {
36 base_url = {
@@ -23,7 +23,9 b' from rhodecode.tests import assert_sessi'
23
23
24
24
25 def route_path(name, params=None, **kwargs):
25 def route_path(name, params=None, **kwargs):
26 import urllib.request, urllib.parse, urllib.error
26 import urllib.request
27 import urllib.parse
28 import urllib.error
27
29
28 base_url = {
30 base_url = {
29 'edit_repo_group_advanced':
31 'edit_repo_group_advanced':
@@ -23,7 +23,9 b' from rhodecode.tests.utils import permis'
23
23
24
24
25 def route_path(name, params=None, **kwargs):
25 def route_path(name, params=None, **kwargs):
26 import urllib.request, urllib.parse, urllib.error
26 import urllib.request
27 import urllib.parse
28 import urllib.error
27
29
28 base_url = {
30 base_url = {
29 'edit_repo_group_perms':
31 'edit_repo_group_perms':
@@ -23,7 +23,9 b' from rhodecode.tests import assert_sessi'
23
23
24
24
25 def route_path(name, params=None, **kwargs):
25 def route_path(name, params=None, **kwargs):
26 import urllib.request, urllib.parse, urllib.error
26 import urllib.request
27 import urllib.parse
28 import urllib.error
27
29
28 base_url = {
30 base_url = {
29 'edit_repo_group': '/{repo_group_name}/_edit',
31 'edit_repo_group': '/{repo_group_name}/_edit',
@@ -22,7 +22,9 b' from rhodecode.model.db import Repositor'
22
22
23
23
24 def route_path(name, params=None, **kwargs):
24 def route_path(name, params=None, **kwargs):
25 import urllib.request, urllib.parse, urllib.error
25 import urllib.request
26 import urllib.parse
27 import urllib.error
26
28
27 base_url = {
29 base_url = {
28 'pullrequest_show_all': '/{repo_name}/pull-request',
30 'pullrequest_show_all': '/{repo_name}/pull-request',
@@ -22,7 +22,9 b' from rhodecode.model.db import Repositor'
22
22
23
23
24 def route_path(name, params=None, **kwargs):
24 def route_path(name, params=None, **kwargs):
25 import urllib.request, urllib.parse, urllib.error
25 import urllib.request
26 import urllib.parse
27 import urllib.error
26
28
27 base_url = {
29 base_url = {
28 'bookmarks_home': '/{repo_name}/bookmarks',
30 'bookmarks_home': '/{repo_name}/bookmarks',
@@ -22,7 +22,9 b' from rhodecode.model.db import Repositor'
22
22
23
23
24 def route_path(name, params=None, **kwargs):
24 def route_path(name, params=None, **kwargs):
25 import urllib.request, urllib.parse, urllib.error
25 import urllib.request
26 import urllib.parse
27 import urllib.error
26
28
27 base_url = {
29 base_url = {
28 'branches_home': '/{repo_name}/branches',
30 'branches_home': '/{repo_name}/branches',
@@ -28,7 +28,9 b' MATCH_HASH = re.compile(r\'<span class="c'
28
28
29
29
30 def route_path(name, params=None, **kwargs):
30 def route_path(name, params=None, **kwargs):
31 import urllib.request, urllib.parse, urllib.error
31 import urllib.request
32 import urllib.parse
33 import urllib.error
32
34
33 base_url = {
35 base_url = {
34 'repo_changelog': '/{repo_name}/changelog',
36 'repo_changelog': '/{repo_name}/changelog',
@@ -43,7 +45,7 b' def route_path(name, params=None, **kwar'
43
45
44
46
45 def assert_commits_on_page(response, indexes):
47 def assert_commits_on_page(response, indexes):
46 found_indexes = [int(idx) for idx in MATCH_HASH.findall(response.body)]
48 found_indexes = [int(idx) for idx in MATCH_HASH.findall(response.text)]
47 assert found_indexes == indexes
49 assert found_indexes == indexes
48
50
49
51
@@ -109,8 +111,7 b' class TestChangelogController(TestContro'
109 assert expected_warning in response.text
111 assert expected_warning in response.text
110
112
111 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
113 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
112 def test_changelog_filtered_by_branch_with_merges(
114 def test_changelog_filtered_by_branch_with_merges(self, autologin_user, backend):
113 self, autologin_user, backend):
114
115
115 # Note: The changelog of branch "b" does not contain the commit "a1"
116 # Note: The changelog of branch "b" does not contain the commit "a1"
116 # although this is a parent of commit "b1". And branch "b" has commits
117 # although this is a parent of commit "b1". And branch "b" has commits
@@ -27,7 +27,9 b' from rhodecode.lib import helpers as h'
27
27
28
28
29 def route_path(name, params=None, **kwargs):
29 def route_path(name, params=None, **kwargs):
30 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
31 import urllib.parse
32 import urllib.error
31
33
32 base_url = {
34 base_url = {
33 'repo_commit': '/{repo_name}/changeset/{commit_id}',
35 'repo_commit': '/{repo_name}/changeset/{commit_id}',
@@ -71,7 +73,7 b' class TestRepoCommitCommentsView(TestCon'
71 self.log_user()
73 self.log_user()
72 commit = backend.repo.get_commit('300')
74 commit = backend.repo.get_commit('300')
73 commit_id = commit.raw_id
75 commit_id = commit.raw_id
74 text = u'CommentOnCommit'
76 text = 'CommentOnCommit'
75
77
76 params = {'text': text, 'csrf_token': self.csrf_token,
78 params = {'text': text, 'csrf_token': self.csrf_token,
77 'comment_type': comment_type}
79 'comment_type': comment_type}
@@ -101,7 +103,7 b' class TestRepoCommitCommentsView(TestCon'
101 author, comment_type, h.show_id(commit), backend.repo_name)
103 author, comment_type, h.show_id(commit), backend.repo_name)
102 assert sbj == notification.subject
104 assert sbj == notification.subject
103
105
104 lnk = (u'/{0}/changeset/{1}#comment-{2}'.format(
106 lnk = ('/{0}/changeset/{1}#comment-{2}'.format(
105 backend.repo_name, commit_id, comment_id))
107 backend.repo_name, commit_id, comment_id))
106 assert lnk in notification.body
108 assert lnk in notification.body
107
109
@@ -110,7 +112,7 b' class TestRepoCommitCommentsView(TestCon'
110 self.log_user()
112 self.log_user()
111 commit = backend.repo.get_commit('300')
113 commit = backend.repo.get_commit('300')
112 commit_id = commit.raw_id
114 commit_id = commit.raw_id
113 text = u'CommentOnCommit'
115 text = 'CommentOnCommit'
114 f_path = 'vcs/web/simplevcs/views/repository.py'
116 f_path = 'vcs/web/simplevcs/views/repository.py'
115 line = 'n1'
117 line = 'n1'
116
118
@@ -163,7 +165,7 b' class TestRepoCommitCommentsView(TestCon'
163
165
164 assert sbj == notification.subject
166 assert sbj == notification.subject
165
167
166 lnk = (u'/{0}/changeset/{1}#comment-{2}'.format(
168 lnk = ('/{0}/changeset/{1}#comment-{2}'.format(
167 backend.repo_name, commit_id, comment.comment_id))
169 backend.repo_name, commit_id, comment.comment_id))
168 assert lnk in notification.body
170 assert lnk in notification.body
169 assert 'on line n1' in notification.body
171 assert 'on line n1' in notification.body
@@ -172,7 +174,7 b' class TestRepoCommitCommentsView(TestCon'
172 self.log_user()
174 self.log_user()
173
175
174 commit_id = backend.repo.get_commit('300').raw_id
176 commit_id = backend.repo.get_commit('300').raw_id
175 text = u'@test_regular check CommentOnCommit'
177 text = '@test_regular check CommentOnCommit'
176
178
177 params = {'text': text, 'csrf_token': self.csrf_token}
179 params = {'text': text, 'csrf_token': self.csrf_token}
178 self.app.post(
180 self.app.post(
@@ -193,13 +195,13 b' class TestRepoCommitCommentsView(TestCon'
193 users = [x.username for x in notification.recipients]
195 users = [x.username for x in notification.recipients]
194
196
195 # test_regular gets notification by @mention
197 # test_regular gets notification by @mention
196 assert sorted(users) == [u'test_admin', u'test_regular']
198 assert sorted(users) == ['test_admin', 'test_regular']
197
199
198 def test_create_with_status_change(self, backend):
200 def test_create_with_status_change(self, backend):
199 self.log_user()
201 self.log_user()
200 commit = backend.repo.get_commit('300')
202 commit = backend.repo.get_commit('300')
201 commit_id = commit.raw_id
203 commit_id = commit.raw_id
202 text = u'CommentOnCommit'
204 text = 'CommentOnCommit'
203 f_path = 'vcs/web/simplevcs/views/repository.py'
205 f_path = 'vcs/web/simplevcs/views/repository.py'
204 line = 'n1'
206 line = 'n1'
205
207
@@ -233,14 +235,14 b' class TestRepoCommitCommentsView(TestCon'
233 author, h.show_id(commit), backend.repo_name)
235 author, h.show_id(commit), backend.repo_name)
234 assert sbj == notification.subject
236 assert sbj == notification.subject
235
237
236 lnk = (u'/{0}/changeset/{1}#comment-{2}'.format(
238 lnk = ('/{0}/changeset/{1}#comment-{2}'.format(
237 backend.repo_name, commit_id, comment_id))
239 backend.repo_name, commit_id, comment_id))
238 assert lnk in notification.body
240 assert lnk in notification.body
239
241
240 def test_delete(self, backend):
242 def test_delete(self, backend):
241 self.log_user()
243 self.log_user()
242 commit_id = backend.repo.get_commit('300').raw_id
244 commit_id = backend.repo.get_commit('300').raw_id
243 text = u'CommentOnCommit'
245 text = 'CommentOnCommit'
244
246
245 params = {'text': text, 'csrf_token': self.csrf_token}
247 params = {'text': text, 'csrf_token': self.csrf_token}
246 self.app.post(
248 self.app.post(
@@ -271,7 +273,7 b' class TestRepoCommitCommentsView(TestCon'
271 def test_edit(self, backend):
273 def test_edit(self, backend):
272 self.log_user()
274 self.log_user()
273 commit_id = backend.repo.get_commit('300').raw_id
275 commit_id = backend.repo.get_commit('300').raw_id
274 text = u'CommentOnCommit'
276 text = 'CommentOnCommit'
275
277
276 params = {'text': text, 'csrf_token': self.csrf_token}
278 params = {'text': text, 'csrf_token': self.csrf_token}
277 self.app.post(
279 self.app.post(
@@ -304,7 +306,7 b' class TestRepoCommitCommentsView(TestCon'
304 def test_edit_without_change(self, backend):
306 def test_edit_without_change(self, backend):
305 self.log_user()
307 self.log_user()
306 commit_id = backend.repo.get_commit('300').raw_id
308 commit_id = backend.repo.get_commit('300').raw_id
307 text = u'CommentOnCommit'
309 text = 'CommentOnCommit'
308
310
309 params = {'text': text, 'csrf_token': self.csrf_token}
311 params = {'text': text, 'csrf_token': self.csrf_token}
310 self.app.post(
312 self.app.post(
@@ -336,7 +338,7 b' class TestRepoCommitCommentsView(TestCon'
336 def test_edit_try_edit_already_edited(self, backend):
338 def test_edit_try_edit_already_edited(self, backend):
337 self.log_user()
339 self.log_user()
338 commit_id = backend.repo.get_commit('300').raw_id
340 commit_id = backend.repo.get_commit('300').raw_id
339 text = u'CommentOnCommit'
341 text = 'CommentOnCommit'
340
342
341 params = {'text': text, 'csrf_token': self.csrf_token}
343 params = {'text': text, 'csrf_token': self.csrf_token}
342 self.app.post(
344 self.app.post(
@@ -390,7 +392,7 b' class TestRepoCommitCommentsView(TestCon'
390 def test_edit_forbidden_for_immutable_comments(self, backend):
392 def test_edit_forbidden_for_immutable_comments(self, backend):
391 self.log_user()
393 self.log_user()
392 commit_id = backend.repo.get_commit('300').raw_id
394 commit_id = backend.repo.get_commit('300').raw_id
393 text = u'CommentOnCommit'
395 text = 'CommentOnCommit'
394
396
395 params = {'text': text, 'csrf_token': self.csrf_token, 'version': '0'}
397 params = {'text': text, 'csrf_token': self.csrf_token, 'version': '0'}
396 self.app.post(
398 self.app.post(
@@ -429,7 +431,7 b' class TestRepoCommitCommentsView(TestCon'
429 def test_delete_forbidden_for_immutable_comments(self, backend):
431 def test_delete_forbidden_for_immutable_comments(self, backend):
430 self.log_user()
432 self.log_user()
431 commit_id = backend.repo.get_commit('300').raw_id
433 commit_id = backend.repo.get_commit('300').raw_id
432 text = u'CommentOnCommit'
434 text = 'CommentOnCommit'
433
435
434 params = {'text': text, 'csrf_token': self.csrf_token}
436 params = {'text': text, 'csrf_token': self.csrf_token}
435 self.app.post(
437 self.app.post(
@@ -24,7 +24,9 b' from rhodecode.lib.helpers import _short'
24
24
25
25
26 def route_path(name, params=None, **kwargs):
26 def route_path(name, params=None, **kwargs):
27 import urllib.request, urllib.parse, urllib.error
27 import urllib.request
28 import urllib.parse
29 import urllib.error
28
30
29 base_url = {
31 base_url = {
30 'repo_commit': '/{repo_name}/changeset/{commit_id}',
32 'repo_commit': '/{repo_name}/changeset/{commit_id}',
@@ -55,23 +57,31 b' class TestRepoCommitView(object):'
55
57
56 def test_show_raw(self, backend):
58 def test_show_raw(self, backend):
57 commit_id = self.commit_id[backend.alias]
59 commit_id = self.commit_id[backend.alias]
60 # webtest uses linter to check if response is bytes,
61 # and we use memoryview here as a wrapper, quick turn-off
62 self.app.lint = False
63
58 response = self.app.get(route_path(
64 response = self.app.get(route_path(
59 'repo_commit_raw',
65 'repo_commit_raw',
60 repo_name=backend.repo_name, commit_id=commit_id))
66 repo_name=backend.repo_name, commit_id=commit_id))
61 assert response.text == self.diffs[backend.alias]
67 assert response.body == self.diffs[backend.alias]
62
68
63 def test_show_raw_patch(self, backend):
69 def test_show_raw_patch(self, backend):
64 response = self.app.get(route_path(
70 response = self.app.get(route_path(
65 'repo_commit_patch', repo_name=backend.repo_name,
71 'repo_commit_patch', repo_name=backend.repo_name,
66 commit_id=self.commit_id[backend.alias]))
72 commit_id=self.commit_id[backend.alias]))
67 assert response.text == self.patches[backend.alias]
73 assert response.body == self.patches[backend.alias]
68
74
69 def test_commit_download(self, backend):
75 def test_commit_download(self, backend):
76 # webtest uses linter to check if response is bytes,
77 # and we use memoryview here as a wrapper, quick turn-off
78 self.app.lint = False
79
70 response = self.app.get(route_path(
80 response = self.app.get(route_path(
71 'repo_commit_download',
81 'repo_commit_download',
72 repo_name=backend.repo_name,
82 repo_name=backend.repo_name,
73 commit_id=self.commit_id[backend.alias]))
83 commit_id=self.commit_id[backend.alias]))
74 assert response.text == self.diffs[backend.alias]
84 assert response.body == self.diffs[backend.alias]
75
85
76 def test_single_commit_page_different_ops(self, backend):
86 def test_single_commit_page_different_ops(self, backend):
77 commit_id = {
87 commit_id = {
@@ -257,7 +267,7 b' class TestRepoCommitView(object):'
257 }
267 }
258
268
259 diffs = {
269 diffs = {
260 'hg': r"""diff --git a/README b/README
270 'hg': br"""diff --git a/README b/README
261 new file mode 120000
271 new file mode 120000
262 --- /dev/null
272 --- /dev/null
263 +++ b/README
273 +++ b/README
@@ -265,7 +275,7 b' new file mode 120000'
265 +README.rst
275 +README.rst
266 \ No newline at end of file
276 \ No newline at end of file
267 """,
277 """,
268 'git': r"""diff --git a/README b/README
278 'git': br"""diff --git a/README b/README
269 new file mode 120000
279 new file mode 120000
270 index 0000000..92cacd2
280 index 0000000..92cacd2
271 --- /dev/null
281 --- /dev/null
@@ -274,7 +284,7 b' index 0000000..92cacd2'
274 +README.rst
284 +README.rst
275 \ No newline at end of file
285 \ No newline at end of file
276 """,
286 """,
277 'svn': """Index: README
287 'svn': b"""Index: README
278 ===================================================================
288 ===================================================================
279 diff --git a/README b/README
289 diff --git a/README b/README
280 new file mode 10644
290 new file mode 10644
@@ -287,7 +297,7 b' new file mode 10644'
287 }
297 }
288
298
289 patches = {
299 patches = {
290 'hg': r"""# HG changeset patch
300 'hg': br"""# HG changeset patch
291 # User Marcin Kuzminski <marcin@python-works.com>
301 # User Marcin Kuzminski <marcin@python-works.com>
292 # Date 2014-01-07 12:21:40
302 # Date 2014-01-07 12:21:40
293 # Node ID 2062ec7beeeaf9f44a1c25c41479565040b930b2
303 # Node ID 2062ec7beeeaf9f44a1c25c41479565040b930b2
@@ -296,7 +306,7 b' new file mode 10644'
296 Added a symlink
306 Added a symlink
297
307
298 """ + diffs['hg'],
308 """ + diffs['hg'],
299 'git': r"""From fd627b9e0dd80b47be81af07c4a98518244ed2f7 2014-01-07 12:22:20
309 'git': br"""From fd627b9e0dd80b47be81af07c4a98518244ed2f7 2014-01-07 12:22:20
300 From: Marcin Kuzminski <marcin@python-works.com>
310 From: Marcin Kuzminski <marcin@python-works.com>
301 Date: 2014-01-07 12:22:20
311 Date: 2014-01-07 12:22:20
302 Subject: [PATCH] Added a symlink
312 Subject: [PATCH] Added a symlink
@@ -304,7 +314,7 b' Subject: [PATCH] Added a symlink'
304 ---
314 ---
305
315
306 """ + diffs['git'],
316 """ + diffs['git'],
307 'svn': r"""# SVN changeset patch
317 'svn': br"""# SVN changeset patch
308 # User marcin
318 # User marcin
309 # Date 2014-09-02 12:25:22.071142
319 # Date 2014-09-02 12:25:22.071142
310 # Revision 393
320 # Revision 393
@@ -27,7 +27,9 b' from rhodecode.tests.utils import Assert'
27
27
28
28
29 def route_path(name, params=None, **kwargs):
29 def route_path(name, params=None, **kwargs):
30 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
31 import urllib.parse
32 import urllib.error
31
33
32 base_url = {
34 base_url = {
33 'repo_compare_select': '/{repo_name}/compare',
35 'repo_compare_select': '/{repo_name}/compare',
@@ -65,30 +67,30 b' class TestCompareView(object):'
65 #
67 #
66
68
67 fork = backend.create_repo()
69 fork = backend.create_repo()
70 origin = backend.create_repo()
68
71
69 # prepare fork
72 # prepare fork
70 commit0 = commit_change(
73 commit0 = commit_change(
71 fork.repo_name, filename='file1', content='A',
74 fork.repo_name, filename=b'file1', content=b'A',
72 message='A', vcs_type=backend.alias, parent=None, newfile=True)
75 message='A - Initial Commit', vcs_type=backend.alias, parent=None, newfile=True)
73
76
74 commit1 = commit_change(
77 commit1 = commit_change(
75 fork.repo_name, filename='file1', content='B',
78 fork.repo_name, filename=b'file1', content=b'B',
76 message='B, child of A', vcs_type=backend.alias, parent=commit0)
79 message='B, child of A', vcs_type=backend.alias, parent=commit0)
77
80
78 commit_change( # commit 2
81 commit_change( # commit 2
79 fork.repo_name, filename='file1', content='C',
82 fork.repo_name, filename=b'file1', content=b'C',
80 message='C, child of B', vcs_type=backend.alias, parent=commit1)
83 message='C, child of B', vcs_type=backend.alias, parent=commit1)
81
84
82 commit3 = commit_change(
85 commit3 = commit_change(
83 fork.repo_name, filename='file1', content='D',
86 fork.repo_name, filename=b'file1', content=b'D',
84 message='D, child of A', vcs_type=backend.alias, parent=commit0)
87 message='D, child of A', vcs_type=backend.alias, parent=commit0)
85
88
86 commit4 = commit_change(
89 commit4 = commit_change(
87 fork.repo_name, filename='file1', content='E',
90 fork.repo_name, filename=b'file1', content=b'E',
88 message='E, child of D', vcs_type=backend.alias, parent=commit3)
91 message='E, child of D', vcs_type=backend.alias, parent=commit3)
89
92
90 # prepare origin repository, taking just the history up to D
93 # prepare origin repository, taking just the history up to D
91 origin = backend.create_repo()
92
94
93 origin_repo = origin.scm_instance(cache=False)
95 origin_repo = origin.scm_instance(cache=False)
94 origin_repo.config.clear_section('hooks')
96 origin_repo.config.clear_section('hooks')
@@ -98,7 +100,7 b' class TestCompareView(object):'
98 # Verify test fixture setup
100 # Verify test fixture setup
99 # This does not work for git
101 # This does not work for git
100 if backend.alias != 'git':
102 if backend.alias != 'git':
101 assert 5 == len(fork.scm_instance().commit_ids)
103 assert 5 == len(fork.scm_instance(cache=False).commit_ids)
102 assert 2 == len(origin_repo.commit_ids)
104 assert 2 == len(origin_repo.commit_ids)
103
105
104 # Comparing the revisions
106 # Comparing the revisions
@@ -108,7 +110,8 b' class TestCompareView(object):'
108 source_ref_type="rev", source_ref=commit3.raw_id,
110 source_ref_type="rev", source_ref=commit3.raw_id,
109 target_ref_type="rev", target_ref=commit4.raw_id,
111 target_ref_type="rev", target_ref=commit4.raw_id,
110 params=dict(merge='1', target_repo=fork.repo_name)
112 params=dict(merge='1', target_repo=fork.repo_name)
111 ))
113 ),
114 status=200)
112
115
113 compare_page = ComparePage(response)
116 compare_page = ComparePage(response)
114 compare_page.contains_commits([commit4])
117 compare_page.contains_commits([commit4])
@@ -119,7 +122,7 b' class TestCompareView(object):'
119
122
120 # commit something !
123 # commit something !
121 commit0 = commit_change(
124 commit0 = commit_change(
122 repo1.repo_name, filename='file1', content='line1\n',
125 repo1.repo_name, filename=b'file1', content=b'line1\n',
123 message='commit1', vcs_type=backend.alias, parent=None,
126 message='commit1', vcs_type=backend.alias, parent=None,
124 newfile=True)
127 newfile=True)
125
128
@@ -128,11 +131,11 b' class TestCompareView(object):'
128
131
129 # add two extra commit into fork
132 # add two extra commit into fork
130 commit1 = commit_change(
133 commit1 = commit_change(
131 repo2.repo_name, filename='file1', content='line1\nline2\n',
134 repo2.repo_name, filename=b'file1', content=b'line1\nline2\n',
132 message='commit2', vcs_type=backend.alias, parent=commit0)
135 message='commit2', vcs_type=backend.alias, parent=commit0)
133
136
134 commit2 = commit_change(
137 commit2 = commit_change(
135 repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
138 repo2.repo_name, filename=b'file1', content=b'line1\nline2\nline3\n',
136 message='commit3', vcs_type=backend.alias, parent=commit1)
139 message='commit3', vcs_type=backend.alias, parent=commit1)
137
140
138 commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
141 commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
@@ -167,7 +170,7 b' class TestCompareView(object):'
167
170
168 # commit something !
171 # commit something !
169 commit0 = commit_change(
172 commit0 = commit_change(
170 repo1.repo_name, filename='file1', content='line1\n',
173 repo1.repo_name, filename=b'file1', content=b'line1\n',
171 message='commit1', vcs_type=backend.alias, parent=None,
174 message='commit1', vcs_type=backend.alias, parent=None,
172 newfile=True)
175 newfile=True)
173
176
@@ -176,17 +179,17 b' class TestCompareView(object):'
176
179
177 # now commit something to origin repo
180 # now commit something to origin repo
178 commit_change(
181 commit_change(
179 repo1.repo_name, filename='file2', content='line1file2\n',
182 repo1.repo_name, filename=b'file2', content=b'line1file2\n',
180 message='commit2', vcs_type=backend.alias, parent=commit0,
183 message='commit2', vcs_type=backend.alias, parent=commit0,
181 newfile=True)
184 newfile=True)
182
185
183 # add two extra commit into fork
186 # add two extra commit into fork
184 commit1 = commit_change(
187 commit1 = commit_change(
185 repo2.repo_name, filename='file1', content='line1\nline2\n',
188 repo2.repo_name, filename=b'file1', content=b'line1\nline2\n',
186 message='commit2', vcs_type=backend.alias, parent=commit0)
189 message='commit2', vcs_type=backend.alias, parent=commit0)
187
190
188 commit2 = commit_change(
191 commit2 = commit_change(
189 repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
192 repo2.repo_name, filename=b'file1', content=b'line1\nline2\nline3\n',
190 message='commit3', vcs_type=backend.alias, parent=commit1)
193 message='commit3', vcs_type=backend.alias, parent=commit1)
191
194
192 commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
195 commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
@@ -250,11 +253,11 b' class TestCompareView(object):'
250
253
251 # commit something !
254 # commit something !
252 commit0 = commit_change(
255 commit0 = commit_change(
253 repo1.repo_name, filename='file1', content='line1\n',
256 repo1.repo_name, filename=b'file1', content=b'line1\n',
254 message='commit1', vcs_type=backend.alias, parent=None,
257 message='commit1', vcs_type=backend.alias, parent=None,
255 newfile=True)
258 newfile=True)
256 commit1 = commit_change(
259 commit1 = commit_change(
257 repo1.repo_name, filename='file1', content='line1\nline2\n',
260 repo1.repo_name, filename=b'file1', content=b'line1\nline2\n',
258 message='commit2', vcs_type=backend.alias, parent=commit0)
261 message='commit2', vcs_type=backend.alias, parent=commit0)
259
262
260 # fork this repo
263 # fork this repo
@@ -262,19 +265,16 b' class TestCompareView(object):'
262
265
263 # now make commit3-6
266 # now make commit3-6
264 commit2 = commit_change(
267 commit2 = commit_change(
265 repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
268 repo1.repo_name, filename=b'file1', content=b'line1\nline2\nline3\n',
266 message='commit3', vcs_type=backend.alias, parent=commit1)
269 message='commit3', vcs_type=backend.alias, parent=commit1)
267 commit3 = commit_change(
270 commit3 = commit_change(
268 repo1.repo_name, filename='file1',
271 repo1.repo_name, filename=b'file1',content=b'line1\nline2\nline3\nline4\n',
269 content='line1\nline2\nline3\nline4\n', message='commit4',
272 message='commit4', vcs_type=backend.alias, parent=commit2)
270 vcs_type=backend.alias, parent=commit2)
271 commit4 = commit_change(
273 commit4 = commit_change(
272 repo1.repo_name, filename='file1',
274 repo1.repo_name, filename=b'file1', content=b'line1\nline2\nline3\nline4\nline5\n',
273 content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
275 message='commit5', vcs_type=backend.alias, parent=commit3)
274 vcs_type=backend.alias, parent=commit3)
275 commit_change( # commit 5
276 commit_change( # commit 5
276 repo1.repo_name, filename='file1',
277 repo1.repo_name, filename=b'file1', content=b'line1\nline2\nline3\nline4\nline5\nline6\n',
277 content='line1\nline2\nline3\nline4\nline5\nline6\n',
278 message='commit6', vcs_type=backend.alias, parent=commit4)
278 message='commit6', vcs_type=backend.alias, parent=commit4)
279
279
280 response = self.app.get(
280 response = self.app.get(
@@ -313,11 +313,11 b' class TestCompareView(object):'
313
313
314 # commit something !
314 # commit something !
315 commit0 = commit_change(
315 commit0 = commit_change(
316 repo1.repo_name, filename='file1', content='line1\n',
316 repo1.repo_name, filename=b'file1', content=b'line1\n',
317 message='commit1', vcs_type=backend.alias, parent=None,
317 message='commit1', vcs_type=backend.alias, parent=None,
318 newfile=True)
318 newfile=True)
319 commit1 = commit_change(
319 commit1 = commit_change(
320 repo1.repo_name, filename='file1', content='line1\nline2\n',
320 repo1.repo_name, filename=b'file1', content=b'line1\nline2\n',
321 message='commit2', vcs_type=backend.alias, parent=commit0)
321 message='commit2', vcs_type=backend.alias, parent=commit0)
322
322
323 # fork this repo
323 # fork this repo
@@ -325,19 +325,19 b' class TestCompareView(object):'
325
325
326 # now make commit3-6
326 # now make commit3-6
327 commit2 = commit_change(
327 commit2 = commit_change(
328 repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
328 repo1.repo_name, filename=b'file1', content=b'line1\nline2\nline3\n',
329 message='commit3', vcs_type=backend.alias, parent=commit1)
329 message='commit3', vcs_type=backend.alias, parent=commit1)
330 commit3 = commit_change(
330 commit3 = commit_change(
331 repo1.repo_name, filename='file1',
331 repo1.repo_name, filename=b'file1',
332 content='line1\nline2\nline3\nline4\n', message='commit4',
332 content=b'line1\nline2\nline3\nline4\n', message='commit4',
333 vcs_type=backend.alias, parent=commit2)
333 vcs_type=backend.alias, parent=commit2)
334 commit4 = commit_change(
334 commit4 = commit_change(
335 repo1.repo_name, filename='file1',
335 repo1.repo_name, filename=b'file1',
336 content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
336 content=b'line1\nline2\nline3\nline4\nline5\n', message='commit5',
337 vcs_type=backend.alias, parent=commit3)
337 vcs_type=backend.alias, parent=commit3)
338 commit5 = commit_change(
338 commit5 = commit_change(
339 repo1.repo_name, filename='file1',
339 repo1.repo_name, filename=b'file1',
340 content='line1\nline2\nline3\nline4\nline5\nline6\n',
340 content=b'line1\nline2\nline3\nline4\nline5\nline6\n',
341 message='commit6', vcs_type=backend.alias, parent=commit4)
341 message='commit6', vcs_type=backend.alias, parent=commit4)
342
342
343 response = self.app.get(
343 response = self.app.get(
@@ -399,8 +399,8 b' class TestCompareView(object):'
399 r1_name = repo1.repo_name
399 r1_name = repo1.repo_name
400
400
401 commit0 = commit_change(
401 commit0 = commit_change(
402 repo=r1_name, filename='file1',
402 repo=r1_name, filename=b'file1',
403 content='line1', message='commit1', vcs_type=backend.alias,
403 content=b'line1', message='commit1', vcs_type=backend.alias,
404 newfile=True)
404 newfile=True)
405 assert repo1.scm_instance().commit_ids == [commit0.raw_id]
405 assert repo1.scm_instance().commit_ids == [commit0.raw_id]
406
406
@@ -412,20 +412,20 b' class TestCompareView(object):'
412 r2_name = repo2.repo_name
412 r2_name = repo2.repo_name
413
413
414 commit1 = commit_change(
414 commit1 = commit_change(
415 repo=r2_name, filename='file1-fork',
415 repo=r2_name, filename=b'file1-fork',
416 content='file1-line1-from-fork', message='commit1-fork',
416 content=b'file1-line1-from-fork', message='commit1-fork',
417 vcs_type=backend.alias, parent=repo2.scm_instance()[-1],
417 vcs_type=backend.alias, parent=repo2.scm_instance()[-1],
418 newfile=True)
418 newfile=True)
419
419
420 commit2 = commit_change(
420 commit2 = commit_change(
421 repo=r2_name, filename='file2-fork',
421 repo=r2_name, filename=b'file2-fork',
422 content='file2-line1-from-fork', message='commit2-fork',
422 content=b'file2-line1-from-fork', message='commit2-fork',
423 vcs_type=backend.alias, parent=commit1,
423 vcs_type=backend.alias, parent=commit1,
424 newfile=True)
424 newfile=True)
425
425
426 commit_change( # commit 3
426 commit_change( # commit 3
427 repo=r2_name, filename='file3-fork',
427 repo=r2_name, filename=b'file3-fork',
428 content='file3-line1-from-fork', message='commit3-fork',
428 content=b'file3-line1-from-fork', message='commit3-fork',
429 vcs_type=backend.alias, parent=commit2, newfile=True)
429 vcs_type=backend.alias, parent=commit2, newfile=True)
430
430
431 # compare !
431 # compare !
@@ -446,8 +446,8 b' class TestCompareView(object):'
446 response.mustcontain('No commits in this compare')
446 response.mustcontain('No commits in this compare')
447
447
448 commit0 = commit_change(
448 commit0 = commit_change(
449 repo=r1_name, filename='file2',
449 repo=r1_name, filename=b'file2',
450 content='line1-added-after-fork', message='commit2-parent',
450 content=b'line1-added-after-fork', message='commit2-parent',
451 vcs_type=backend.alias, parent=None, newfile=True)
451 vcs_type=backend.alias, parent=None, newfile=True)
452
452
453 # compare !
453 # compare !
@@ -487,11 +487,10 b' class TestCompareView(object):'
487
487
488 def test_errors_when_comparing_unknown_source_repo(self, backend):
488 def test_errors_when_comparing_unknown_source_repo(self, backend):
489 repo = backend.repo
489 repo = backend.repo
490 badrepo = 'badrepo'
491
490
492 response = self.app.get(
491 self.app.get(
493 route_path('repo_compare',
492 route_path('repo_compare',
494 repo_name=badrepo,
493 repo_name='badrepo',
495 source_ref_type="rev", source_ref='tip',
494 source_ref_type="rev", source_ref='tip',
496 target_ref_type="rev", target_ref='tip',
495 target_ref_type="rev", target_ref='tip',
497 params=dict(merge='1', target_repo=repo.repo_name)
496 params=dict(merge='1', target_repo=repo.repo_name)
@@ -23,7 +23,9 b' from .test_repo_compare import ComparePa'
23
23
24
24
25 def route_path(name, params=None, **kwargs):
25 def route_path(name, params=None, **kwargs):
26 import urllib.request, urllib.parse, urllib.error
26 import urllib.request
27 import urllib.parse
28 import urllib.error
27
29
28 base_url = {
30 base_url = {
29 'repo_compare_select': '/{repo_name}/compare',
31 'repo_compare_select': '/{repo_name}/compare',
@@ -55,8 +57,8 b' class TestCompareView(object):'
55
57
56 # outgoing commits between tags
58 # outgoing commits between tags
57 commit_indexes = {
59 commit_indexes = {
58 'git': [113] + range(115, 121),
60 'git': [113] + list(range(115, 121)),
59 'hg': [112] + range(115, 121),
61 'hg': [112] + list(range(115, 121)),
60 }
62 }
61 repo = backend.repo
63 repo = backend.repo
62 commits = (repo.get_commit(commit_idx=idx)
64 commits = (repo.get_commit(commit_idx=idx)
@@ -29,7 +29,9 b' fixture = Fixture()'
29
29
30
30
31 def route_path(name, params=None, **kwargs):
31 def route_path(name, params=None, **kwargs):
32 import urllib.request, urllib.parse, urllib.error
32 import urllib.request
33 import urllib.parse
34 import urllib.error
33
35
34 base_url = {
36 base_url = {
35 'repo_compare_select': '/{repo_name}/compare',
37 'repo_compare_select': '/{repo_name}/compare',
@@ -151,9 +153,9 b' class TestSideBySideDiff(object):'
151
153
152 @pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)')
154 @pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)')
153 def test_diff_side_by_side_from_0_commit(self, app, backend, backend_stub):
155 def test_diff_side_by_side_from_0_commit(self, app, backend, backend_stub):
154 f_path = 'test_sidebyside_file.py'
156 f_path = b'test_sidebyside_file.py'
155 commit1_content = 'content-25d7e49c18b159446c\n'
157 commit1_content = b'content-25d7e49c18b159446c\n'
156 commit2_content = 'content-603d6c72c46d953420\n'
158 commit2_content = b'content-603d6c72c46d953420\n'
157 repo = backend.create_repo()
159 repo = backend.create_repo()
158
160
159 commit1 = commit_change(
161 commit1 = commit_change(
@@ -185,9 +187,9 b' class TestSideBySideDiff(object):'
185
187
186 @pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)')
188 @pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)')
187 def test_diff_side_by_side_from_0_commit_with_file_filter(self, app, backend, backend_stub):
189 def test_diff_side_by_side_from_0_commit_with_file_filter(self, app, backend, backend_stub):
188 f_path = 'test_sidebyside_file.py'
190 f_path = b'test_sidebyside_file.py'
189 commit1_content = 'content-25d7e49c18b159446c\n'
191 commit1_content = b'content-25d7e49c18b159446c\n'
190 commit2_content = 'content-603d6c72c46d953420\n'
192 commit2_content = b'content-603d6c72c46d953420\n'
191 repo = backend.create_repo()
193 repo = backend.create_repo()
192
194
193 commit1 = commit_change(
195 commit1 = commit_change(
@@ -222,7 +224,7 b' class TestSideBySideDiff(object):'
222 {'message': 'First commit'},
224 {'message': 'First commit'},
223 {'message': 'Second commit'},
225 {'message': 'Second commit'},
224 {'message': 'Commit with binary',
226 {'message': 'Commit with binary',
225 'added': [nodes.FileNode('file.empty', content='')]},
227 'added': [nodes.FileNode(b'file.empty', content=b'')]},
226 ]
228 ]
227 f_path = 'file.empty'
229 f_path = 'file.empty'
228 repo = backend.create_repo(commits=commits)
230 repo = backend.create_repo(commits=commits)
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -24,7 +23,9 b' from rhodecode.tests import TestControll'
24
23
25
24
26 def route_path(name, params=None, **kwargs):
25 def route_path(name, params=None, **kwargs):
27 import urllib.request, urllib.parse, urllib.error
26 import urllib.request
27 import urllib.parse
28 import urllib.error
28
29
29 base_url = {
30 base_url = {
30 'rss_feed_home': '/{repo_name}/feed-rss',
31 'rss_feed_home': '/{repo_name}/feed-rss',
@@ -23,10 +23,11 b' import mock'
23 import pytest
23 import pytest
24
24
25 from rhodecode.apps.repository.tests.test_repo_compare import ComparePage
25 from rhodecode.apps.repository.tests.test_repo_compare import ComparePage
26 from rhodecode.apps.repository.views.repo_files import RepoFilesView
26 from rhodecode.apps.repository.views.repo_files import RepoFilesView, get_archive_name, get_path_sha
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from collections import OrderedDict
28 from collections import OrderedDict
29 from rhodecode.lib.ext_json import json
29 from rhodecode.lib.ext_json import json
30 from rhodecode.lib.str_utils import safe_str
30 from rhodecode.lib.vcs import nodes
31 from rhodecode.lib.vcs import nodes
31
32
32 from rhodecode.lib.vcs.conf import settings
33 from rhodecode.lib.vcs.conf import settings
@@ -46,7 +47,9 b' def get_node_history(backend_type):'
46
47
47
48
48 def route_path(name, params=None, **kwargs):
49 def route_path(name, params=None, **kwargs):
49 import urllib.request, urllib.parse, urllib.error
50 import urllib.request
51 import urllib.parse
52 import urllib.error
50
53
51 base_url = {
54 base_url = {
52 'repo_summary': '/{repo_name}',
55 'repo_summary': '/{repo_name}',
@@ -506,7 +509,7 b' class TestRawFileHandling(object):'
506
509
507 def test_raw_svg_should_not_be_rendered(self, backend):
510 def test_raw_svg_should_not_be_rendered(self, backend):
508 backend.create_repo()
511 backend.create_repo()
509 backend.ensure_file("xss.svg")
512 backend.ensure_file(b"xss.svg")
510 response = self.app.get(
513 response = self.app.get(
511 route_path('repo_file_raw',
514 route_path('repo_file_raw',
512 repo_name=backend.repo_name,
515 repo_name=backend.repo_name,
@@ -523,10 +526,10 b' class TestRepositoryArchival(object):'
523 backend.enable_downloads()
526 backend.enable_downloads()
524 commit = backend.repo.get_commit(commit_idx=173)
527 commit = backend.repo.get_commit(commit_idx=173)
525 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
528 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
529 path_sha = get_path_sha('/')
530 filename = get_archive_name(backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha)
526
531
527 short = commit.short_id + extension
528 fname = commit.raw_id + extension
532 fname = commit.raw_id + extension
529 filename = '%s-%s' % (backend.repo_name, short)
530 response = self.app.get(
533 response = self.app.get(
531 route_path('repo_archivefile',
534 route_path('repo_archivefile',
532 repo_name=backend.repo_name,
535 repo_name=backend.repo_name,
@@ -545,10 +548,10 b' class TestRepositoryArchival(object):'
545 backend.enable_downloads()
548 backend.enable_downloads()
546 commit = backend.repo.get_commit(commit_idx=173)
549 commit = backend.repo.get_commit(commit_idx=173)
547 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
550 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
551 path_sha = get_path_sha('/')
552 filename = get_archive_name(backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha, with_hash=False)
548
553
549 short = 'plain' + extension
550 fname = commit.raw_id + extension
554 fname = commit.raw_id + extension
551 filename = '%s-%s' % (backend.repo_name, short)
552 response = self.app.get(
555 response = self.app.get(
553 route_path('repo_archivefile',
556 route_path('repo_archivefile',
554 repo_name=backend.repo_name,
557 repo_name=backend.repo_name,
@@ -622,7 +625,7 b' class TestFilesDiff(object):'
622 commits = [
625 commits = [
623 {'message': 'First commit'},
626 {'message': 'First commit'},
624 {'message': 'Commit with binary',
627 {'message': 'Commit with binary',
625 'added': [nodes.FileNode('file.bin', content='\0BINARY\0')]},
628 'added': [nodes.FileNode(b'file.bin', content='\0BINARY\0')]},
626 ]
629 ]
627 repo = backend.create_repo(commits=commits)
630 repo = backend.create_repo(commits=commits)
628
631
@@ -899,7 +902,7 b' class TestModifyFilesWithWebInterface(ob'
899
902
900 def test_edit_file_view_not_on_branch(self, backend):
903 def test_edit_file_view_not_on_branch(self, backend):
901 repo = backend.create_repo()
904 repo = backend.create_repo()
902 backend.ensure_file("vcs/nodes.py")
905 backend.ensure_file(b"vcs/nodes.py")
903
906
904 response = self.app.get(
907 response = self.app.get(
905 route_path('repo_files_edit_file',
908 route_path('repo_files_edit_file',
@@ -912,7 +915,7 b' class TestModifyFilesWithWebInterface(ob'
912
915
913 def test_edit_file_view_commit_changes(self, backend, csrf_token):
916 def test_edit_file_view_commit_changes(self, backend, csrf_token):
914 repo = backend.create_repo()
917 repo = backend.create_repo()
915 backend.ensure_file("vcs/nodes.py", content="print 'hello'")
918 backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'")
916
919
917 response = self.app.post(
920 response = self.app.post(
918 route_path('repo_files_update_file',
921 route_path('repo_files_update_file',
@@ -934,7 +937,7 b' class TestModifyFilesWithWebInterface(ob'
934 def test_edit_file_view_commit_changes_default_message(self, backend,
937 def test_edit_file_view_commit_changes_default_message(self, backend,
935 csrf_token):
938 csrf_token):
936 repo = backend.create_repo()
939 repo = backend.create_repo()
937 backend.ensure_file("vcs/nodes.py", content="print 'hello'")
940 backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'")
938
941
939 commit_id = (
942 commit_id = (
940 backend.default_branch_name or
943 backend.default_branch_name or
@@ -967,7 +970,7 b' class TestModifyFilesWithWebInterface(ob'
967
970
968 def test_delete_file_view_not_on_branch(self, backend):
971 def test_delete_file_view_not_on_branch(self, backend):
969 repo = backend.create_repo()
972 repo = backend.create_repo()
970 backend.ensure_file('vcs/nodes.py')
973 backend.ensure_file(b'vcs/nodes.py')
971
974
972 response = self.app.get(
975 response = self.app.get(
973 route_path('repo_files_remove_file',
976 route_path('repo_files_remove_file',
@@ -980,7 +983,7 b' class TestModifyFilesWithWebInterface(ob'
980
983
981 def test_delete_file_view_commit_changes(self, backend, csrf_token):
984 def test_delete_file_view_commit_changes(self, backend, csrf_token):
982 repo = backend.create_repo()
985 repo = backend.create_repo()
983 backend.ensure_file("vcs/nodes.py")
986 backend.ensure_file(b"vcs/nodes.py")
984
987
985 response = self.app.post(
988 response = self.app.post(
986 route_path('repo_files_delete_file',
989 route_path('repo_files_delete_file',
@@ -1015,12 +1018,18 b' class TestFilesViewOtherCases(object):'
1015 'repo_files_add_file',
1018 'repo_files_add_file',
1016 repo_name=repo.repo_name,
1019 repo_name=repo.repo_name,
1017 commit_id=0, f_path='')
1020 commit_id=0, f_path='')
1021 add_new = f'<a class="alert-link" href="{repo_file_add_url}">add a new file</a>'
1022
1023 repo_file_upload_url = route_path(
1024 'repo_files_upload_file',
1025 repo_name=repo.repo_name,
1026 commit_id=0, f_path='')
1027 upload_new = f'<a class="alert-link" href="{repo_file_upload_url}">upload a new file</a>'
1018
1028
1019 assert_session_flash(
1029 assert_session_flash(
1020 response,
1030 response,
1021 'There are no files yet. <a class="alert-link" '
1031 'There are no files yet. Click here to %s or %s.' % (add_new, upload_new)
1022 'href="{}">Click here to add a new file.</a>'
1032 )
1023 .format(repo_file_add_url))
1024
1033
1025 def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms(
1034 def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms(
1026 self, backend_stub, autologin_regular_user):
1035 self, backend_stub, autologin_regular_user):
@@ -1041,12 +1050,12 b' class TestFilesViewOtherCases(object):'
1041 assert_session_flash(response, no_=repo_file_add_url)
1050 assert_session_flash(response, no_=repo_file_add_url)
1042
1051
1043 @pytest.mark.parametrize('file_node', [
1052 @pytest.mark.parametrize('file_node', [
1044 'archive/file.zip',
1053 b'archive/file.zip',
1045 'diff/my-file.txt',
1054 b'diff/my-file.txt',
1046 'render.py',
1055 b'render.py',
1047 'render',
1056 b'render',
1048 'remove_file',
1057 b'remove_file',
1049 'remove_file/to-delete.txt',
1058 b'remove_file/to-delete.txt',
1050 ])
1059 ])
1051 def test_file_names_equal_to_routes_parts(self, backend, file_node):
1060 def test_file_names_equal_to_routes_parts(self, backend, file_node):
1052 backend.create_repo()
1061 backend.create_repo()
@@ -1055,7 +1064,7 b' class TestFilesViewOtherCases(object):'
1055 self.app.get(
1064 self.app.get(
1056 route_path('repo_files',
1065 route_path('repo_files',
1057 repo_name=backend.repo_name,
1066 repo_name=backend.repo_name,
1058 commit_id='tip', f_path=file_node),
1067 commit_id='tip', f_path=safe_str(file_node)),
1059 status=200)
1068 status=200)
1060
1069
1061
1070
@@ -33,7 +33,9 b' fixture = Fixture()'
33
33
34
34
35 def route_path(name, params=None, **kwargs):
35 def route_path(name, params=None, **kwargs):
36 import urllib.request, urllib.parse, urllib.error
36 import urllib.request
37 import urllib.parse
38 import urllib.error
37
39
38 base_url = {
40 base_url = {
39 'repo_summary': '/{repo_name}',
41 'repo_summary': '/{repo_name}',
@@ -19,14 +19,16 b''
19
19
20 import pytest
20 import pytest
21
21
22 from rhodecode.lib.utils2 import md5
22 from rhodecode.lib.hash_utils import md5_safe
23 from rhodecode.model.db import Repository
23 from rhodecode.model.db import Repository
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
25 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
26
26
27
27
28 def route_path(name, params=None, **kwargs):
28 def route_path(name, params=None, **kwargs):
29 import urllib.request, urllib.parse, urllib.error
29 import urllib.request
30 import urllib.parse
31 import urllib.error
30
32
31 base_url = {
33 base_url = {
32 'repo_summary': '/{repo_name}',
34 'repo_summary': '/{repo_name}',
@@ -69,9 +71,9 b' class TestRepoIssueTracker(object):'
69 self.app.post(post_url, post_data, status=302)
71 self.app.post(post_url, post_data, status=302)
70 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
72 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
71 settings = self.settings_model.get_repo_settings()
73 settings = self.settings_model.get_repo_settings()
72 self.uid = md5(pattern)
74 self.uid = md5_safe(pattern)
73 assert settings[self.uid]['pat'] == pattern
75 assert settings[self.uid]['pat'] == pattern
74 self.another_uid = md5(another_pattern)
76 self.another_uid = md5_safe(another_pattern)
75 assert settings[self.another_uid]['pat'] == another_pattern
77 assert settings[self.another_uid]['pat'] == another_pattern
76
78
77 # test pattern
79 # test pattern
@@ -95,7 +97,7 b' class TestRepoIssueTracker(object):'
95 entry_key = 'issuetracker_pat_'
97 entry_key = 'issuetracker_pat_'
96 pattern = 'issuetracker_pat2'
98 pattern = 'issuetracker_pat2'
97 old_pattern = 'issuetracker_pat'
99 old_pattern = 'issuetracker_pat'
98 old_uid = md5(old_pattern)
100 old_uid = md5_safe(old_pattern)
99
101
100 sett = SettingsModel(repo=backend.repo).create_or_update_setting(
102 sett = SettingsModel(repo=backend.repo).create_or_update_setting(
101 entry_key+old_uid, old_pattern, 'unicode')
103 entry_key+old_uid, old_pattern, 'unicode')
@@ -114,7 +116,7 b' class TestRepoIssueTracker(object):'
114 self.app.post(post_url, post_data, status=302)
116 self.app.post(post_url, post_data, status=302)
115 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
117 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
116 settings = self.settings_model.get_repo_settings()
118 settings = self.settings_model.get_repo_settings()
117 self.uid = md5(pattern)
119 self.uid = md5_safe(pattern)
118 assert settings[self.uid]['pat'] == pattern
120 assert settings[self.uid]['pat'] == pattern
119 with pytest.raises(KeyError):
121 with pytest.raises(KeyError):
120 key = settings[old_uid]
122 key = settings[old_uid]
@@ -129,7 +131,7 b' class TestRepoIssueTracker(object):'
129 repo_name = repo.repo_name
131 repo_name = repo.repo_name
130 entry_key = 'issuetracker_pat_'
132 entry_key = 'issuetracker_pat_'
131 pattern = 'issuetracker_pat3'
133 pattern = 'issuetracker_pat3'
132 uid = md5(pattern)
134 uid = md5_safe(pattern)
133 settings_util.create_repo_rhodecode_setting(
135 settings_util.create_repo_rhodecode_setting(
134 repo=backend.repo, name=entry_key+uid,
136 repo=backend.repo, name=entry_key+uid,
135 value=entry_key, type_='unicode', cleanup=False)
137 value=entry_key, type_='unicode', cleanup=False)
@@ -32,7 +32,9 b' fixture = Fixture()'
32
32
33
33
34 def route_path(name, params=None, **kwargs):
34 def route_path(name, params=None, **kwargs):
35 import urllib.request, urllib.parse, urllib.error
35 import urllib.request
36 import urllib.parse
37 import urllib.error
36
38
37 base_url = {
39 base_url = {
38 'edit_repo_maintenance': '/{repo_name}/settings/maintenance',
40 'edit_repo_maintenance': '/{repo_name}/settings/maintenance',
@@ -23,7 +23,9 b' from rhodecode.tests.utils import permis'
23
23
24
24
25 def route_path(name, params=None, **kwargs):
25 def route_path(name, params=None, **kwargs):
26 import urllib.request, urllib.parse, urllib.error
26 import urllib.request
27 import urllib.parse
28 import urllib.error
27
29
28 base_url = {
30 base_url = {
29 'edit_repo_perms': '/{repo_name}/settings/permissions'
31 'edit_repo_perms': '/{repo_name}/settings/permissions'
@@ -36,7 +36,9 b' from rhodecode.tests import ('
36
36
37
37
38 def route_path(name, params=None, **kwargs):
38 def route_path(name, params=None, **kwargs):
39 import urllib.request, urllib.parse, urllib.error
39 import urllib.request
40 import urllib.parse
41 import urllib.error
40
42
41 base_url = {
43 base_url = {
42 'repo_changelog': '/{repo_name}/changelog',
44 'repo_changelog': '/{repo_name}/changelog',
@@ -119,21 +121,21 b' class TestPullrequestsView(object):'
119 def test_show_versions_of_pr(self, backend, csrf_token):
121 def test_show_versions_of_pr(self, backend, csrf_token):
120 commits = [
122 commits = [
121 {'message': 'initial-commit',
123 {'message': 'initial-commit',
122 'added': [FileNode('test-file.txt', 'LINE1\n')]},
124 'added': [FileNode(b'test-file.txt', b'LINE1\n')]},
123
125
124 {'message': 'commit-1',
126 {'message': 'commit-1',
125 'changed': [FileNode('test-file.txt', 'LINE1\nLINE2\n')]},
127 'changed': [FileNode(b'test-file.txt', b'LINE1\nLINE2\n')]},
126 # Above is the initial version of PR that changes a single line
128 # Above is the initial version of PR that changes a single line
127
129
128 # from now on we'll add 3x commit adding a nother line on each step
130 # from now on we'll add 3x commit adding a nother line on each step
129 {'message': 'commit-2',
131 {'message': 'commit-2',
130 'changed': [FileNode('test-file.txt', 'LINE1\nLINE2\nLINE3\n')]},
132 'changed': [FileNode(b'test-file.txt', b'LINE1\nLINE2\nLINE3\n')]},
131
133
132 {'message': 'commit-3',
134 {'message': 'commit-3',
133 'changed': [FileNode('test-file.txt', 'LINE1\nLINE2\nLINE3\nLINE4\n')]},
135 'changed': [FileNode(b'test-file.txt', b'LINE1\nLINE2\nLINE3\nLINE4\n')]},
134
136
135 {'message': 'commit-4',
137 {'message': 'commit-4',
136 'changed': [FileNode('test-file.txt', 'LINE1\nLINE2\nLINE3\nLINE4\nLINE5\n')]},
138 'changed': [FileNode(b'test-file.txt', b'LINE1\nLINE2\nLINE3\nLINE4\nLINE5\n')]},
137 ]
139 ]
138
140
139 commit_ids = backend.create_master_repo(commits)
141 commit_ids = backend.create_master_repo(commits)
@@ -413,14 +415,14 b' class TestPullrequestsView(object):'
413 'csrf_token': csrf_token})
415 'csrf_token': csrf_token})
414
416
415 assert_session_flash(
417 assert_session_flash(
416 response, u'Pull request title & description updated.',
418 response, 'Pull request title & description updated.',
417 category='success')
419 category='success')
418
420
419 pull_request = PullRequest.get(pull_request_id)
421 pull_request = PullRequest.get(pull_request_id)
420 assert pull_request.title == 'New title'
422 assert pull_request.title == 'New title'
421 assert pull_request.description == 'New description'
423 assert pull_request.description == 'New description'
422
424
423 def test_edit_title_description(self, pr_util, csrf_token):
425 def test_edit_title_description_special(self, pr_util, csrf_token):
424 pull_request = pr_util.create_pull_request()
426 pull_request = pr_util.create_pull_request()
425 pull_request_id = pull_request.pull_request_id
427 pull_request_id = pull_request.pull_request_id
426
428
@@ -435,7 +437,7 b' class TestPullrequestsView(object):'
435 'csrf_token': csrf_token})
437 'csrf_token': csrf_token})
436
438
437 assert_session_flash(
439 assert_session_flash(
438 response, u'Pull request title & description updated.',
440 response, 'Pull request title & description updated.',
439 category='success')
441 category='success')
440
442
441 pull_request = PullRequest.get(pull_request_id)
443 pull_request = PullRequest.get(pull_request_id)
@@ -456,7 +458,7 b' class TestPullrequestsView(object):'
456 'description': 'New description',
458 'description': 'New description',
457 'csrf_token': csrf_token}, status=200)
459 'csrf_token': csrf_token}, status=200)
458 assert_session_flash(
460 assert_session_flash(
459 response, u'Cannot update closed pull requests.',
461 response, 'Cannot update closed pull requests.',
460 category='error')
462 category='error')
461
463
462 def test_update_invalid_source_reference(self, pr_util, csrf_token):
464 def test_update_invalid_source_reference(self, pr_util, csrf_token):
@@ -483,7 +485,7 b' class TestPullrequestsView(object):'
483 from rhodecode.lib.vcs.backends.base import MergeFailureReason
485 from rhodecode.lib.vcs.backends.base import MergeFailureReason
484 pull_request = pr_util.create_pull_request(
486 pull_request = pr_util.create_pull_request(
485 approved=True, mergeable=True)
487 approved=True, mergeable=True)
486 unicode_reference = u'branch:invalid-branch:invalid-commit-id'
488 unicode_reference = 'branch:invalid-branch:invalid-commit-id'
487 pull_request.target_ref = unicode_reference
489 pull_request.target_ref = unicode_reference
488 Session().add(pull_request)
490 Session().add(pull_request)
489 Session().commit()
491 Session().commit()
@@ -687,7 +689,7 b' class TestPullrequestsView(object):'
687 ChangesetComment.comment_id == comment_id).first().text
689 ChangesetComment.comment_id == comment_id).first().text
688 assert test_text == text_form_db
690 assert test_text == text_form_db
689
691
690 def test_comment_and_comment_edit(self, pr_util, csrf_token, xhr_header):
692 def test_comment_and_comment_edit_special(self, pr_util, csrf_token, xhr_header):
691 pull_request = pr_util.create_pull_request()
693 pull_request = pr_util.create_pull_request()
692 target_scm = pull_request.target_repo.scm_instance()
694 target_scm = pull_request.target_repo.scm_instance()
693 target_scm_name = target_scm.name
695 target_scm_name = target_scm.name
@@ -867,13 +869,12 b' class TestPullrequestsView(object):'
867 # notifications properly with the new PR
869 # notifications properly with the new PR
868 commits = [
870 commits = [
869 {'message': 'ancestor',
871 {'message': 'ancestor',
870 'added': [FileNode('file_A', content='content_of_ancestor')]},
872 'added': [FileNode(b'file_A', content=b'content_of_ancestor')]},
871 {'message': 'change',
873 {'message': 'change',
872 'added': [FileNode('file_a', content='content_of_change')]},
874 'added': [FileNode(b'file_a', content=b'content_of_change')]},
873 {'message': 'change-child'},
875 {'message': 'change-child'},
874 {'message': 'ancestor-child', 'parents': ['ancestor'],
876 {'message': 'ancestor-child', 'parents': ['ancestor'],
875 'added': [
877 'added': [ FileNode(b'file_B', content=b'content_of_ancestor_child')]},
876 FileNode('file_B', content='content_of_ancestor_child')]},
877 {'message': 'ancestor-child-2'},
878 {'message': 'ancestor-child-2'},
878 ]
879 ]
879 commit_ids = backend.create_master_repo(commits)
880 commit_ids = backend.create_master_repo(commits)
@@ -935,13 +936,13 b' class TestPullrequestsView(object):'
935 def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token):
936 def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token):
936 commits = [
937 commits = [
937 {'message': 'ancestor',
938 {'message': 'ancestor',
938 'added': [FileNode('file_A', content='content_of_ancestor')]},
939 'added': [FileNode(b'file_A', content=b'content_of_ancestor')]},
939 {'message': 'change',
940 {'message': 'change',
940 'added': [FileNode('file_a', content='content_of_change')]},
941 'added': [FileNode(b'file_a', content=b'content_of_change')]},
941 {'message': 'change-child'},
942 {'message': 'change-child'},
942 {'message': 'ancestor-child', 'parents': ['ancestor'],
943 {'message': 'ancestor-child', 'parents': ['ancestor'],
943 'added': [
944 'added': [
944 FileNode('file_B', content='content_of_ancestor_child')]},
945 FileNode(b'file_B', content=b'content_of_ancestor_child')]},
945 {'message': 'ancestor-child-2'},
946 {'message': 'ancestor-child-2'},
946 ]
947 ]
947 commit_ids = backend.create_master_repo(commits)
948 commit_ids = backend.create_master_repo(commits)
@@ -1021,9 +1022,9 b' class TestPullrequestsView(object):'
1021 actions = [log.action for log in user_logs]
1022 actions = [log.action for log in user_logs]
1022 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
1023 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
1023 expected_actions = [
1024 expected_actions = [
1024 u'repo.pull_request.close',
1025 'repo.pull_request.close',
1025 u'repo.pull_request.merge',
1026 'repo.pull_request.merge',
1026 u'repo.pull_request.comment.create'
1027 'repo.pull_request.comment.create'
1027 ]
1028 ]
1028 assert actions == expected_actions
1029 assert actions == expected_actions
1029
1030
@@ -1121,8 +1122,8 b' class TestPullrequestsView(object):'
1121 branch=backend.default_branch_name, commit_id=commit_ids['ancestor'])
1122 branch=backend.default_branch_name, commit_id=commit_ids['ancestor'])
1122
1123
1123 pull_request.revisions = [commit_ids['change']]
1124 pull_request.revisions = [commit_ids['change']]
1124 pull_request.title = u"Test"
1125 pull_request.title = "Test"
1125 pull_request.description = u"Description"
1126 pull_request.description = "Description"
1126 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1127 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1127 pull_request.pull_request_state = PullRequest.STATE_CREATED
1128 pull_request.pull_request_state = PullRequest.STATE_CREATED
1128 Session().add(pull_request)
1129 Session().add(pull_request)
@@ -1175,8 +1176,8 b' class TestPullrequestsView(object):'
1175 branch=backend.default_branch_name, commit_id=commit_ids['ancestor'])
1176 branch=backend.default_branch_name, commit_id=commit_ids['ancestor'])
1176
1177
1177 pull_request.revisions = [commit_ids['change']]
1178 pull_request.revisions = [commit_ids['change']]
1178 pull_request.title = u"Test"
1179 pull_request.title = "Test"
1179 pull_request.description = u"Description"
1180 pull_request.description = "Description"
1180 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1181 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1181 pull_request.pull_request_state = PullRequest.STATE_CREATED
1182 pull_request.pull_request_state = PullRequest.STATE_CREATED
1182
1183
@@ -1242,8 +1243,8 b' class TestPullrequestsView(object):'
1242 commit_ids['feat-commit-1'],
1243 commit_ids['feat-commit-1'],
1243 commit_ids['feat-commit-2']
1244 commit_ids['feat-commit-2']
1244 ]
1245 ]
1245 pull_request.title = u"Test"
1246 pull_request.title = "Test"
1246 pull_request.description = u"Description"
1247 pull_request.description = "Description"
1247 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1248 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1248 pull_request.pull_request_state = PullRequest.STATE_CREATED
1249 pull_request.pull_request_state = PullRequest.STATE_CREATED
1249 Session().add(pull_request)
1250 Session().add(pull_request)
@@ -1292,8 +1293,8 b' class TestPullrequestsView(object):'
1292 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
1293 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
1293 branch=backend.default_branch_name, commit_id=commit_ids['ancestor'])
1294 branch=backend.default_branch_name, commit_id=commit_ids['ancestor'])
1294 pull_request.revisions = [commit_ids['change']]
1295 pull_request.revisions = [commit_ids['change']]
1295 pull_request.title = u"Test"
1296 pull_request.title = "Test"
1296 pull_request.description = u"Description"
1297 pull_request.description = "Description"
1297 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1298 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1298 pull_request.pull_request_state = PullRequest.STATE_CREATED
1299 pull_request.pull_request_state = PullRequest.STATE_CREATED
1299 Session().add(pull_request)
1300 Session().add(pull_request)
@@ -1340,8 +1341,8 b' class TestPullrequestsView(object):'
1340 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
1341 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
1341 branch=backend_git.default_branch_name, commit_id=commit_ids['old-feature'])
1342 branch=backend_git.default_branch_name, commit_id=commit_ids['old-feature'])
1342 pull_request.revisions = [commit_ids['new-feature']]
1343 pull_request.revisions = [commit_ids['new-feature']]
1343 pull_request.title = u"Test"
1344 pull_request.title = "Test"
1344 pull_request.description = u"Description"
1345 pull_request.description = "Description"
1345 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1346 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1346 pull_request.pull_request_state = PullRequest.STATE_CREATED
1347 pull_request.pull_request_state = PullRequest.STATE_CREATED
1347 Session().add(pull_request)
1348 Session().add(pull_request)
@@ -32,7 +32,9 b' fixture = Fixture()'
32
32
33
33
34 def route_path(name, params=None, **kwargs):
34 def route_path(name, params=None, **kwargs):
35 import urllib.request, urllib.parse, urllib.error
35 import urllib.request
36 import urllib.parse
37 import urllib.error
36
38
37 base_url = {
39 base_url = {
38 'edit_repo': '/{repo_name}/settings',
40 'edit_repo': '/{repo_name}/settings',
@@ -19,7 +19,7 b''
19
19
20 import pytest
20 import pytest
21
21
22 from rhodecode.lib.utils2 import safe_unicode, safe_str
22 from rhodecode.lib.str_utils import safe_str
23 from rhodecode.model.db import Repository
23 from rhodecode.model.db import Repository
24 from rhodecode.model.repo import RepoModel
24 from rhodecode.model.repo import RepoModel
25 from rhodecode.tests import (
25 from rhodecode.tests import (
@@ -31,7 +31,9 b' fixture = Fixture()'
31
31
32
32
33 def route_path(name, params=None, **kwargs):
33 def route_path(name, params=None, **kwargs):
34 import urllib.request, urllib.parse, urllib.error
34 import urllib.request
35 import urllib.parse
36 import urllib.error
35
37
36 base_url = {
38 base_url = {
37 'repo_summary_explicit': '/{repo_name}/summary',
39 'repo_summary_explicit': '/{repo_name}/summary',
@@ -40,7 +40,9 b' fixture = Fixture()'
40
40
41
41
42 def route_path(name, params=None, **kwargs):
42 def route_path(name, params=None, **kwargs):
43 import urllib.request, urllib.parse, urllib.error
43 import urllib.request
44 import urllib.parse
45 import urllib.error
44
46
45 base_url = {
47 base_url = {
46 'repo_summary': '/{repo_name}',
48 'repo_summary': '/{repo_name}',
@@ -276,8 +278,8 b' class TestRepoLocation(object):'
276 response = self.app.get(
278 response = self.app.get(
277 route_path('repo_summary', repo_name=safe_str(repo_name)), status=302)
279 route_path('repo_summary', repo_name=safe_str(repo_name)), status=302)
278
280
279 msg = 'The repository `%s` cannot be loaded in filesystem. ' \
281 msg = f'The repository `{repo_name}` cannot be loaded in filesystem. ' \
280 'Please check if it exist, or is not damaged.' % repo_name
282 f'Please check if it exist, or is not damaged.'
281 assert_session_flash(response, msg)
283 assert_session_flash(response, msg)
282
284
283 @pytest.mark.parametrize("suffix", [u'', u'Δ…Δ™Ε‚'], ids=['', 'non-ascii'])
285 @pytest.mark.parametrize("suffix", [u'', u'Δ…Δ™Ε‚'], ids=['', 'non-ascii'])
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -23,7 +22,9 b' from rhodecode.model.db import Repositor'
23
22
24
23
25 def route_path(name, params=None, **kwargs):
24 def route_path(name, params=None, **kwargs):
26 import urllib.request, urllib.parse, urllib.error
25 import urllib.request
26 import urllib.parse
27 import urllib.error
27
28
28 base_url = {
29 base_url = {
29 'tags_home': '/{repo_name}/tags',
30 'tags_home': '/{repo_name}/tags',
@@ -37,7 +37,9 b' fixture = Fixture()'
37
37
38
38
39 def route_path(name, params=None, **kwargs):
39 def route_path(name, params=None, **kwargs):
40 import urllib.request, urllib.parse, urllib.error
40 import urllib.request
41 import urllib.parse
42 import urllib.error
41
43
42 base_url = {
44 base_url = {
43 'repo_summary': '/{repo_name}',
45 'repo_summary': '/{repo_name}',
@@ -27,7 +27,9 b' from rhodecode.tests.utils import Assert'
27
27
28
28
29 def route_path(name, params=None, **kwargs):
29 def route_path(name, params=None, **kwargs):
30 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
31 import urllib.parse
32 import urllib.error
31
33
32 base_url = {
34 base_url = {
33 'edit_repo': '/{repo_name}/settings',
35 'edit_repo': '/{repo_name}/settings',
@@ -179,6 +179,7 b' class TestSearchController(TestControlle'
179 def test_filters_are_not_applied_for_admin_user(self):
179 def test_filters_are_not_applied_for_admin_user(self):
180 self.log_user()
180 self.log_user()
181 with mock.patch('whoosh.searching.Searcher.search') as search_mock:
181 with mock.patch('whoosh.searching.Searcher.search') as search_mock:
182
182 self.app.get(route_path('search'),
183 self.app.get(route_path('search'),
183 {'q': 'test query', 'type': 'commit'})
184 {'q': 'test query', 'type': 'commit'})
184 assert search_mock.call_count == 1
185 assert search_mock.call_count == 1
@@ -35,7 +35,7 b' def dummy_conf_file(tmpdir):'
35 conf.set('app:main', 'ssh.executable.svn', '/usr/bin/svnserve')
35 conf.set('app:main', 'ssh.executable.svn', '/usr/bin/svnserve')
36
36
37 f_path = os.path.join(str(tmpdir), 'ssh_wrapper_test.ini')
37 f_path = os.path.join(str(tmpdir), 'ssh_wrapper_test.ini')
38 with open(f_path, 'wb') as f:
38 with open(f_path, 'wt') as f:
39 conf.write(f)
39 conf.write(f)
40
40
41 return os.path.join(f_path)
41 return os.path.join(f_path)
@@ -18,7 +18,6 b''
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 json
22 import os
21 import os
23
22
24 import mock
23 import mock
@@ -26,7 +25,7 b' import pytest'
26
25
27 from rhodecode.apps.ssh_support.lib.backends.git import GitServer
26 from rhodecode.apps.ssh_support.lib.backends.git import GitServer
28 from rhodecode.apps.ssh_support.tests.conftest import plain_dummy_env, plain_dummy_user
27 from rhodecode.apps.ssh_support.tests.conftest import plain_dummy_env, plain_dummy_user
29
28 from rhodecode.lib.ext_json import json
30
29
31 class GitServerCreator(object):
30 class GitServerCreator(object):
32 root = '/tmp/repo/path/'
31 root = '/tmp/repo/path/'
@@ -39,7 +39,7 b' class TestModDavSvnConfig(object):'
39 def get_repo_group_mocks(cls, count=1):
39 def get_repo_group_mocks(cls, count=1):
40 repo_groups = []
40 repo_groups = []
41 for num in range(0, count):
41 for num in range(0, count):
42 full_path = u'/path/to/RepâGrâúp-°¡ {}'.format(num)
42 full_path = f'/path/to/RepâGrâúp-°¡ {num}'
43 repo_group_mock = mock.MagicMock()
43 repo_group_mock = mock.MagicMock()
44 repo_group_mock.full_path = full_path
44 repo_group_mock.full_path = full_path
45 repo_group_mock.full_path_splitted = full_path.split('/')
45 repo_group_mock.full_path_splitted = full_path.split('/')
@@ -78,7 +78,7 b' class TestModDavSvnConfig(object):'
78 def test_render_mod_dav_svn_config_with_alternative_template(self, tmpdir):
78 def test_render_mod_dav_svn_config_with_alternative_template(self, tmpdir):
79 repo_groups = self.get_repo_group_mocks(count=10)
79 repo_groups = self.get_repo_group_mocks(count=10)
80 test_file_path = os.path.join(str(tmpdir), 'example.mako')
80 test_file_path = os.path.join(str(tmpdir), 'example.mako')
81 with open(test_file_path, 'wb') as f:
81 with open(test_file_path, 'wt') as f:
82 f.write('TEST_EXAMPLE\n')
82 f.write('TEST_EXAMPLE\n')
83
83
84 generated_config = utils._render_mod_dav_svn_config(
84 generated_config = utils._render_mod_dav_svn_config(
@@ -107,11 +107,11 b' class TestModDavSvnConfig(object):'
107
107
108 # Assert that correct configuration directive is present.
108 # Assert that correct configuration directive is present.
109 if list_parent_path:
109 if list_parent_path:
110 assert not re.search('SVNListParentPath\s+Off', generated_config)
110 assert not re.search(r'SVNListParentPath\s+Off', generated_config)
111 assert re.search('SVNListParentPath\s+On', generated_config)
111 assert re.search(r'SVNListParentPath\s+On', generated_config)
112 else:
112 else:
113 assert re.search('SVNListParentPath\s+Off', generated_config)
113 assert re.search(r'SVNListParentPath\s+Off', generated_config)
114 assert not re.search('SVNListParentPath\s+On', generated_config)
114 assert not re.search(r'SVNListParentPath\s+On', generated_config)
115
115
116 if use_ssl:
116 if use_ssl:
117 assert 'RequestHeader edit Destination ^https: http: early' \
117 assert 'RequestHeader edit Destination ^https: http: early' \
@@ -29,7 +29,9 b' fixture = Fixture()'
29
29
30
30
31 def route_path(name, params=None, **kwargs):
31 def route_path(name, params=None, **kwargs):
32 import urllib.request, urllib.parse, urllib.error
32 import urllib.request
33 import urllib.parse
34 import urllib.error
33 from rhodecode.apps._base import ADMIN_PREFIX
35 from rhodecode.apps._base import ADMIN_PREFIX
34
36
35 base_url = {
37 base_url = {
@@ -23,7 +23,9 b' from rhodecode.tests.utils import permis'
23
23
24
24
25 def route_path(name, params=None, **kwargs):
25 def route_path(name, params=None, **kwargs):
26 import urllib.request, urllib.parse, urllib.error
26 import urllib.request
27 import urllib.parse
28 import urllib.error
27 from rhodecode.apps._base import ADMIN_PREFIX
29 from rhodecode.apps._base import ADMIN_PREFIX
28
30
29 base_url = {
31 base_url = {
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -214,11 +213,13 b' def assert_session_flash(response, msg=N'
214 msg = f'msg `{no_}` found in session flash.'
213 msg = f'msg `{no_}` found in session flash.'
215 pytest.fail(safe_str(msg))
214 pytest.fail(safe_str(msg))
216 else:
215 else:
216
217 if msg not in message_text:
217 if msg not in message_text:
218 fail_msg = f'msg `{msg}` not found in ' \
218 fail_msg = f'msg `{msg}` not found in ' \
219 f'session flash: got `{message_text}` (type:{type(message_text)}) instead'
219 f'session flash: got `{message_text}` (type:{type(message_text)}) instead'
220
220
221 pytest.fail(safe_str(fail_msg))
221 pytest.fail(safe_str(fail_msg))
222
222 if category:
223 if category:
223 assert category == message.category
224 assert category == message.category
224
225
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -118,7 +118,7 b' class TestSanitizeVcsSettings(object):'
118 _string_funcs = [
118 _string_funcs = [
119 ('vcs.svn.compatible_version', ''),
119 ('vcs.svn.compatible_version', ''),
120 ('vcs.hooks.protocol', 'http'),
120 ('vcs.hooks.protocol', 'http'),
121 ('vcs.hooks.host', '127.0.0.1'),
121 ('vcs.hooks.host', '*'),
122 ('vcs.scm_app_implementation', 'http'),
122 ('vcs.scm_app_implementation', 'http'),
123 ('vcs.server', ''),
123 ('vcs.server', ''),
124 ('vcs.server.protocol', 'http'),
124 ('vcs.server.protocol', 'http'),
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2012-2020 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -20,13 +19,13 b''
20
19
21 from subprocess import Popen, PIPE
20 from subprocess import Popen, PIPE
22 import os
21 import os
23 import shutil
24 import sys
22 import sys
25 import tempfile
23 import tempfile
26
24
27 import pytest
25 import pytest
28 from sqlalchemy.engine import url
26 from sqlalchemy.engine import url
29
27
28 from rhodecode.lib.str_utils import safe_str, safe_bytes
30 from rhodecode.tests.fixture import TestINI
29 from rhodecode.tests.fixture import TestINI
31
30
32
31
@@ -145,17 +144,19 b' class DBBackend(object):'
145 _env.update(env)
144 _env.update(env)
146 self.p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=_env)
145 self.p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=_env)
147 self.stdout, self.stderr = self.p.communicate()
146 self.stdout, self.stderr = self.p.communicate()
148 sys.stdout.write('COMMAND:'+command+'\n')
147 stdout_str = safe_str(self.stdout)
149 sys.stdout.write(self.stdout)
148 sys.stdout.write(f'COMMAND:{command}\n')
149 sys.stdout.write(stdout_str)
150 return self.stdout, self.stderr
150 return self.stdout, self.stderr
151
151
152 def assert_returncode_success(self):
152 def assert_returncode_success(self):
153 from rich import print as pprint
153 if not self.p.returncode == 0:
154 if not self.p.returncode == 0:
154 print(self.stderr)
155 pprint(safe_str(self.stderr))
155 raise AssertionError('non 0 retcode:{}'.format(self.p.returncode))
156 raise AssertionError(f'non 0 retcode:{self.p.returncode}')
156
157
157 def assert_correct_output(self, stdout, version):
158 def assert_correct_output(self, stdout, version):
158 assert 'UPGRADE FOR STEP {} COMPLETED'.format(version) in stdout
159 assert b'UPGRADE FOR STEP %b COMPLETED' % safe_bytes(version) in stdout
159
160
160 def setup_rhodecode_db(self, ini_params=None, env=None):
161 def setup_rhodecode_db(self, ini_params=None, env=None):
161 if not ini_params:
162 if not ini_params:
@@ -233,11 +234,11 b' class SQLiteDBBackend(DBBackend):'
233 def import_dump(self, dumpname):
234 def import_dump(self, dumpname):
234 dump = os.path.join(self.fixture_store, dumpname)
235 dump = os.path.join(self.fixture_store, dumpname)
235 target = os.path.join(self._basetemp, '{0.db_name}.sqlite'.format(self))
236 target = os.path.join(self._basetemp, '{0.db_name}.sqlite'.format(self))
236 return self.execute('cp -v {} {}'.format(dump, target))
237 return self.execute(f'cp -v {dump} {target}')
237
238
238 def teardown_db(self):
239 def teardown_db(self):
239 return self.execute("rm -rf {}.sqlite".format(
240 target_db = os.path.join(self._basetemp, self.db_name)
240 os.path.join(self._basetemp, self.db_name)))
241 return self.execute(f"rm -rf {target_db}.sqlite")
241
242
242
243
243 class MySQLDBBackend(DBBackend):
244 class MySQLDBBackend(DBBackend):
@@ -273,21 +274,15 b' class PostgresDBBackend(DBBackend):'
273 def setup_db(self):
274 def setup_db(self):
274 # dump schema for tests
275 # dump schema for tests
275 # pg_dump -U postgres -h localhost $TEST_DB_NAME
276 # pg_dump -U postgres -h localhost $TEST_DB_NAME
276 self._db_url = [{'app:main': {
277 self._db_url = [{'app:main': {'sqlalchemy.db1.url': self.connection_string}}]
277 'sqlalchemy.db1.url':
278 cmd = f"PGPASSWORD={self.password} psql -U {self.user} -h localhost -c 'create database '{self.db_name}';'"
278 self.connection_string}}]
279 return self.execute(cmd)
279 return self.execute("PGPASSWORD={} psql -U {} -h localhost "
280 "-c 'create database '{}';'".format(
281 self.password, self.user, self.db_name))
282
280
283 def teardown_db(self):
281 def teardown_db(self):
284 return self.execute("PGPASSWORD={} psql -U {} -h localhost "
282 cmd = f"PGPASSWORD={self.password} psql -U {self.user} -h localhost -c 'drop database if exists '{self.db_name}';'"
285 "-c 'drop database if exists '{}';'".format(
283 return self.execute(cmd)
286 self.password, self.user, self.db_name))
287
284
288 def import_dump(self, dumpname):
285 def import_dump(self, dumpname):
289 dump = os.path.join(self.fixture_store, dumpname)
286 dump = os.path.join(self.fixture_store, dumpname)
290 return self.execute(
287 cmd = f"PGPASSWORD={self.password} psql -U {self.user} -h localhost -d {self.db_name} -1 -f {dump}"
291 "PGPASSWORD={} psql -U {} -h localhost -d {} -1 "
288 return self.execute(cmd)
292 "-f {}".format(
293 self.password, self.user, self.db_name, dump))
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -148,7 +147,7 b' class Fixture(object):'
148 plugin = self._get_plugin()
147 plugin = self._get_plugin()
149 plugin.create_or_update_setting('auth_restriction', auth_restriction)
148 plugin.create_or_update_setting('auth_restriction', auth_restriction)
150 Session().commit()
149 Session().commit()
151 SettingsModel().invalidate_settings_cache()
150 SettingsModel().invalidate_settings_cache(hard=True)
152
151
153 def __exit__(self, exc_type, exc_val, exc_tb):
152 def __exit__(self, exc_type, exc_val, exc_tb):
154
153
@@ -156,7 +155,7 b' class Fixture(object):'
156 plugin.create_or_update_setting(
155 plugin.create_or_update_setting(
157 'auth_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_NONE)
156 'auth_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_NONE)
158 Session().commit()
157 Session().commit()
159 SettingsModel().invalidate_settings_cache()
158 SettingsModel().invalidate_settings_cache(hard=True)
160
159
161 return context()
160 return context()
162
161
@@ -181,14 +180,14 b' class Fixture(object):'
181 plugin = self._get_plugin()
180 plugin = self._get_plugin()
182 plugin.create_or_update_setting('scope_restriction', scope_restriction)
181 plugin.create_or_update_setting('scope_restriction', scope_restriction)
183 Session().commit()
182 Session().commit()
184 SettingsModel().invalidate_settings_cache()
183 SettingsModel().invalidate_settings_cache(hard=True)
185
184
186 def __exit__(self, exc_type, exc_val, exc_tb):
185 def __exit__(self, exc_type, exc_val, exc_tb):
187 plugin = self._get_plugin()
186 plugin = self._get_plugin()
188 plugin.create_or_update_setting(
187 plugin.create_or_update_setting(
189 'scope_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_ALL)
188 'scope_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_ALL)
190 Session().commit()
189 Session().commit()
191 SettingsModel().invalidate_settings_cache()
190 SettingsModel().invalidate_settings_cache(hard=True)
192
191
193 return context()
192 return context()
194
193
@@ -399,7 +398,7 b' class Fixture(object):'
399 'gist_type': GistModel.cls.GIST_PUBLIC,
398 'gist_type': GistModel.cls.GIST_PUBLIC,
400 'lifetime': -1,
399 'lifetime': -1,
401 'acl_level': Gist.ACL_LEVEL_PUBLIC,
400 'acl_level': Gist.ACL_LEVEL_PUBLIC,
402 'gist_mapping': {'filename1.txt': {'content': 'hello world'},}
401 'gist_mapping': {b'filename1.txt': {'content': b'hello world'},}
403 }
402 }
404 form_data.update(kwargs)
403 form_data.update(kwargs)
405 gist = GistModel().create(
404 gist = GistModel().create(
@@ -420,7 +419,7 b' class Fixture(object):'
420 Session().commit()
419 Session().commit()
421
420
422 def load_resource(self, resource_name, strip=False):
421 def load_resource(self, resource_name, strip=False):
423 with open(os.path.join(FIXTURES, resource_name)) as f:
422 with open(os.path.join(FIXTURES, resource_name), 'rb') as f:
424 source = f.read()
423 source = f.read()
425 if strip:
424 if strip:
426 source = source.strip()
425 source = source.strip()
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -30,7 +29,7 b' def vcsserver(request, vcsserver_port, v'
30 """
29 """
31 Session scope VCSServer.
30 Session scope VCSServer.
32
31
33 Tests wich need the VCSServer have to rely on this fixture in order
32 Tests which need the VCSServer have to rely on this fixture in order
34 to ensure it will be running.
33 to ensure it will be running.
35
34
36 For specific needs, the fixture vcsserver_factory can be used. It allows to
35 For specific needs, the fixture vcsserver_factory can be used. It allows to
@@ -58,7 +57,7 b' def vcsserver_factory(tmpdir_factory):'
58 """
57 """
59
58
60 def factory(request, overrides=(), vcsserver_port=None,
59 def factory(request, overrides=(), vcsserver_port=None,
61 log_file=None):
60 log_file=None, workers='2'):
62
61
63 if vcsserver_port is None:
62 if vcsserver_port is None:
64 vcsserver_port = get_available_port()
63 vcsserver_port = get_available_port()
@@ -74,7 +73,7 b' def vcsserver_factory(tmpdir_factory):'
74 basetemp=tmpdir_factory.getbasetemp().strpath,
73 basetemp=tmpdir_factory.getbasetemp().strpath,
75 prefix='test_vcs_')
74 prefix='test_vcs_')
76
75
77 server = RcVCSServer(config_file, log_file)
76 server = RcVCSServer(config_file, log_file, workers)
78 server.start()
77 server.start()
79
78
80 @request.addfinalizer
79 @request.addfinalizer
@@ -100,7 +99,8 b' def ini_config(request, tmpdir_factory, '
100 overrides = [
99 overrides = [
101 {'server:main': {'port': rcserver_port}},
100 {'server:main': {'port': rcserver_port}},
102 {'app:main': {
101 {'app:main': {
103 'vcs.server': 'localhost:%s' % vcsserver_port,
102 'cache_dir': '%(here)s/rc_data',
103 'vcs.server': f'localhost:{vcsserver_port}',
104 # johbo: We will always start the VCSServer on our own based on the
104 # johbo: We will always start the VCSServer on our own based on the
105 # fixtures of the test cases. For the test run it must always be
105 # fixtures of the test cases. For the test run it must always be
106 # off in the INI file.
106 # off in the INI file.
@@ -109,7 +109,7 b' def ini_config(request, tmpdir_factory, '
109 'vcs.server.protocol': 'http',
109 'vcs.server.protocol': 'http',
110 'vcs.scm_app_implementation': 'http',
110 'vcs.scm_app_implementation': 'http',
111 'vcs.hooks.protocol': 'http',
111 'vcs.hooks.protocol': 'http',
112 'vcs.hooks.host': '127.0.0.1',
112 'vcs.hooks.host': '*',
113 }},
113 }},
114
114
115 {'handler_console': {
115 {'handler_console': {
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -57,6 +56,7 b' from rhodecode.model.integration import '
57 from rhodecode.integrations import integration_type_registry
56 from rhodecode.integrations import integration_type_registry
58 from rhodecode.integrations.types.base import IntegrationTypeBase
57 from rhodecode.integrations.types.base import IntegrationTypeBase
59 from rhodecode.lib.utils import repo2db_mapper
58 from rhodecode.lib.utils import repo2db_mapper
59 from rhodecode.lib.str_utils import safe_bytes
60 from rhodecode.lib.hash_utils import sha1_safe
60 from rhodecode.lib.hash_utils import sha1_safe
61 from rhodecode.lib.vcs.backends import get_backend
61 from rhodecode.lib.vcs.backends import get_backend
62 from rhodecode.lib.vcs.nodes import FileNode
62 from rhodecode.lib.vcs.nodes import FileNode
@@ -540,7 +540,7 b' class Backend(object):'
540
540
541 def create_repo(
541 def create_repo(
542 self, commits=None, number_of_commits=0, heads=None,
542 self, commits=None, number_of_commits=0, heads=None,
543 name_suffix=u'', bare=False, **kwargs):
543 name_suffix='', bare=False, **kwargs):
544 """
544 """
545 Create a repository and record it for later cleanup.
545 Create a repository and record it for later cleanup.
546
546
@@ -585,14 +585,14 b' class Backend(object):'
585 self._cleanup_repos.append(self.repo_name)
585 self._cleanup_repos.append(self.repo_name)
586 return repo
586 return repo
587
587
588 def new_repo_name(self, suffix=u''):
588 def new_repo_name(self, suffix=''):
589 self.repo_name = self._next_repo_name() + suffix
589 self.repo_name = self._next_repo_name() + suffix
590 self._cleanup_repos.append(self.repo_name)
590 self._cleanup_repos.append(self.repo_name)
591 return self.repo_name
591 return self.repo_name
592
592
593 def _next_repo_name(self):
593 def _next_repo_name(self):
594 return u"%s_%s" % (
594 return u"%s_%s" % (
595 self.invalid_repo_name.sub(u'_', self._test_name), len(self._cleanup_repos))
595 self.invalid_repo_name.sub('_', self._test_name), len(self._cleanup_repos))
596
596
597 def ensure_file(self, filename, content='Test content\n'):
597 def ensure_file(self, filename, content='Test content\n'):
598 assert self._cleanup_repos, "Avoid writing into vcs_test repos"
598 assert self._cleanup_repos, "Avoid writing into vcs_test repos"
@@ -634,14 +634,98 b' class Backend(object):'
634 repo.set_refs(ref_name, refs[ref_name])
634 repo.set_refs(ref_name, refs[ref_name])
635
635
636
636
637 def vcsbackend_base(request, backend_alias, tests_tmp_path, baseapp, test_repo):
637 class VcsBackend(object):
638 """
639 Represents the test configuration for one supported vcs backend.
640 """
641
642 invalid_repo_name = re.compile(r'[^0-9a-zA-Z]+')
643
644 def __init__(self, alias, repo_path, test_name, test_repo_container):
645 self.alias = alias
646 self._repo_path = repo_path
647 self._cleanup_repos = []
648 self._test_name = test_name
649 self._test_repo_container = test_repo_container
650
651 def __getitem__(self, key):
652 return self._test_repo_container(key, self.alias).scm_instance()
653
654 def __repr__(self):
655 return f'{self.__class__.__name__}(alias={self.alias}, repo={self._repo_path})'
656
657 @property
658 def repo(self):
659 """
660 Returns the "current" repository. This is the vcs_test repo of the last
661 repo which has been created.
662 """
663 Repository = get_backend(self.alias)
664 return Repository(self._repo_path)
665
666 @property
667 def backend(self):
668 """
669 Returns the backend implementation class.
670 """
671 return get_backend(self.alias)
672
673 def create_repo(self, commits=None, number_of_commits=0, _clone_repo=None,
674 bare=False):
675 repo_name = self._next_repo_name()
676 self._repo_path = get_new_dir(repo_name)
677 repo_class = get_backend(self.alias)
678 src_url = None
679 if _clone_repo:
680 src_url = _clone_repo.path
681 repo = repo_class(self._repo_path, create=True, src_url=src_url, bare=bare)
682 self._cleanup_repos.append(repo)
683
684 commits = commits or [
685 {'message': 'Commit %s of %s' % (x, repo_name)}
686 for x in range(number_of_commits)]
687 _add_commits_to_repo(repo, commits)
688 return repo
689
690 def clone_repo(self, repo):
691 return self.create_repo(_clone_repo=repo)
692
693 def cleanup(self):
694 for repo in self._cleanup_repos:
695 shutil.rmtree(repo.path)
696
697 def new_repo_path(self):
698 repo_name = self._next_repo_name()
699 self._repo_path = get_new_dir(repo_name)
700 return self._repo_path
701
702 def _next_repo_name(self):
703
704 return "{}_{}".format(
705 self.invalid_repo_name.sub('_', self._test_name),
706 len(self._cleanup_repos)
707 )
708
709 def add_file(self, repo, filename, content='Test content\n'):
710 imc = repo.in_memory_commit
711 imc.add(FileNode(safe_bytes(filename), content=safe_bytes(content)))
712 imc.commit(
713 message='Automatic commit from vcsbackend fixture',
714 author='Automatic <automatic@rhodecode.com>')
715
716 def ensure_file(self, filename, content='Test content\n'):
717 assert self._cleanup_repos, "Avoid writing into vcs_test repos"
718 self.add_file(self.repo, filename, content)
719
720
721 def vcsbackend_base(request, backend_alias, tests_tmp_path, baseapp, test_repo) -> VcsBackend:
638 if backend_alias not in request.config.getoption('--backends'):
722 if backend_alias not in request.config.getoption('--backends'):
639 pytest.skip("Backend %s not selected." % (backend_alias, ))
723 pytest.skip("Backend %s not selected." % (backend_alias, ))
640
724
641 utils.check_xfail_backends(request.node, backend_alias)
725 utils.check_xfail_backends(request.node, backend_alias)
642 utils.check_skip_backends(request.node, backend_alias)
726 utils.check_skip_backends(request.node, backend_alias)
643
727
644 repo_name = 'vcs_test_%s' % (backend_alias, )
728 repo_name = f'vcs_test_{backend_alias}'
645 repo_path = os.path.join(tests_tmp_path, repo_name)
729 repo_path = os.path.join(tests_tmp_path, repo_name)
646 backend = VcsBackend(
730 backend = VcsBackend(
647 alias=backend_alias,
731 alias=backend_alias,
@@ -691,85 +775,6 b' def vcsbackend_stub(vcsbackend_git):'
691 return vcsbackend_git
775 return vcsbackend_git
692
776
693
777
694 class VcsBackend(object):
695 """
696 Represents the test configuration for one supported vcs backend.
697 """
698
699 invalid_repo_name = re.compile(r'[^0-9a-zA-Z]+')
700
701 def __init__(self, alias, repo_path, test_name, test_repo_container):
702 self.alias = alias
703 self._repo_path = repo_path
704 self._cleanup_repos = []
705 self._test_name = test_name
706 self._test_repo_container = test_repo_container
707
708 def __getitem__(self, key):
709 return self._test_repo_container(key, self.alias).scm_instance()
710
711 @property
712 def repo(self):
713 """
714 Returns the "current" repository. This is the vcs_test repo of the last
715 repo which has been created.
716 """
717 Repository = get_backend(self.alias)
718 return Repository(self._repo_path)
719
720 @property
721 def backend(self):
722 """
723 Returns the backend implementation class.
724 """
725 return get_backend(self.alias)
726
727 def create_repo(self, commits=None, number_of_commits=0, _clone_repo=None,
728 bare=False):
729 repo_name = self._next_repo_name()
730 self._repo_path = get_new_dir(repo_name)
731 repo_class = get_backend(self.alias)
732 src_url = None
733 if _clone_repo:
734 src_url = _clone_repo.path
735 repo = repo_class(self._repo_path, create=True, src_url=src_url, bare=bare)
736 self._cleanup_repos.append(repo)
737
738 commits = commits or [
739 {'message': 'Commit %s of %s' % (x, repo_name)}
740 for x in range(number_of_commits)]
741 _add_commits_to_repo(repo, commits)
742 return repo
743
744 def clone_repo(self, repo):
745 return self.create_repo(_clone_repo=repo)
746
747 def cleanup(self):
748 for repo in self._cleanup_repos:
749 shutil.rmtree(repo.path)
750
751 def new_repo_path(self):
752 repo_name = self._next_repo_name()
753 self._repo_path = get_new_dir(repo_name)
754 return self._repo_path
755
756 def _next_repo_name(self):
757 return "%s_%s" % (
758 self.invalid_repo_name.sub('_', self._test_name),
759 len(self._cleanup_repos))
760
761 def add_file(self, repo, filename, content='Test content\n'):
762 imc = repo.in_memory_commit
763 imc.add(FileNode(filename, content=content))
764 imc.commit(
765 message=u'Automatic commit from vcsbackend fixture',
766 author=u'Automatic <automatic@rhodecode.com>')
767
768 def ensure_file(self, filename, content='Test content\n'):
769 assert self._cleanup_repos, "Avoid writing into vcs_test repos"
770 self.add_file(self.repo, filename, content)
771
772
773 def _add_commits_to_repo(vcs_repo, commits):
778 def _add_commits_to_repo(vcs_repo, commits):
774 commit_ids = {}
779 commit_ids = {}
775 if not commits:
780 if not commits:
@@ -782,11 +787,11 b' def _add_commits_to_repo(vcs_repo, commi'
782 message = str(commit.get('message', 'Commit %s' % idx))
787 message = str(commit.get('message', 'Commit %s' % idx))
783
788
784 for node in commit.get('added', []):
789 for node in commit.get('added', []):
785 imc.add(FileNode(node.path, content=node.content))
790 imc.add(FileNode(safe_bytes(node.path), content=node.content))
786 for node in commit.get('changed', []):
791 for node in commit.get('changed', []):
787 imc.change(FileNode(node.path, content=node.content))
792 imc.change(FileNode(safe_bytes(node.path), content=node.content))
788 for node in commit.get('removed', []):
793 for node in commit.get('removed', []):
789 imc.remove(FileNode(node.path))
794 imc.remove(FileNode(safe_bytes(node.path)))
790
795
791 parents = [
796 parents = [
792 vcs_repo.get_commit(commit_id=commit_ids[p])
797 vcs_repo.get_commit(commit_id=commit_ids[p])
@@ -794,7 +799,7 b' def _add_commits_to_repo(vcs_repo, commi'
794
799
795 operations = ('added', 'changed', 'removed')
800 operations = ('added', 'changed', 'removed')
796 if not any((commit.get(o) for o in operations)):
801 if not any((commit.get(o) for o in operations)):
797 imc.add(FileNode('file_%s' % idx, content=message))
802 imc.add(FileNode(b'file_%b' % safe_bytes(str(idx)), content=safe_bytes(message)))
798
803
799 commit = imc.commit(
804 commit = imc.commit(
800 message=message,
805 message=message,
@@ -877,7 +882,7 b' class PRTestUtility(object):'
877 def create_pull_request(
882 def create_pull_request(
878 self, commits=None, target_head=None, source_head=None,
883 self, commits=None, target_head=None, source_head=None,
879 revisions=None, approved=False, author=None, mergeable=False,
884 revisions=None, approved=False, author=None, mergeable=False,
880 enable_notifications=True, name_suffix=u'', reviewers=None, observers=None,
885 enable_notifications=True, name_suffix='', reviewers=None, observers=None,
881 title=u"Test", description=u"Description"):
886 title=u"Test", description=u"Description"):
882 self.set_mergeable(mergeable)
887 self.set_mergeable(mergeable)
883 if not enable_notifications:
888 if not enable_notifications:
@@ -1002,7 +1007,7 b' class PRTestUtility(object):'
1002 return comment
1007 return comment
1003
1008
1004 def create_inline_comment(
1009 def create_inline_comment(
1005 self, linked_to=None, line_no=u'n1', file_path='file_1'):
1010 self, linked_to=None, line_no='n1', file_path='file_1'):
1006 comment = CommentsModel().create(
1011 comment = CommentsModel().create(
1007 text=u"Test comment",
1012 text=u"Test comment",
1008 repo=self.target_repository.repo_name,
1013 repo=self.target_repository.repo_name,
@@ -245,8 +245,8 b' index e34033e29fa9b3d3366b723beab129cee7'
245 + 'author': 'Joe Doe <joe.doe@example.com>',
245 + 'author': 'Joe Doe <joe.doe@example.com>',
246 + 'date': datetime.datetime(2010, 1, 1, 20),
246 + 'date': datetime.datetime(2010, 1, 1, 20),
247 + 'added': [
247 + 'added': [
248 + FileNode('foobar', content='foobar'),
248 + FileNode(b'foobar', content='foobar'),
249 + FileNode('foobar2', content='foobar2'),
249 + FileNode(b'foobar2', content='foobar2'),
250 + ],
250 + ],
251 + },
251 + },
252 + {
252 + {
@@ -254,10 +254,10 b' index e34033e29fa9b3d3366b723beab129cee7'
254 + 'author': 'Jane Doe <jane.doe@example.com>',
254 + 'author': 'Jane Doe <jane.doe@example.com>',
255 + 'date': datetime.datetime(2010, 1, 1, 21),
255 + 'date': datetime.datetime(2010, 1, 1, 21),
256 + 'added': [
256 + 'added': [
257 + FileNode('foobar3', content='foobar3'),
257 + FileNode(b'foobar3', content='foobar3'),
258 + ],
258 + ],
259 + 'changed': [
259 + 'changed': [
260 + FileNode('foobar', 'FOOBAR'),
260 + FileNode(b'foobar', 'FOOBAR'),
261 + ],
261 + ],
262 + },
262 + },
263 + {
263 + {
@@ -265,9 +265,9 b' index e34033e29fa9b3d3366b723beab129cee7'
265 + 'author': 'Jane Doe <jane.doe@example.com>',
265 + 'author': 'Jane Doe <jane.doe@example.com>',
266 + 'date': datetime.datetime(2010, 1, 1, 22),
266 + 'date': datetime.datetime(2010, 1, 1, 22),
267 + 'changed': [
267 + 'changed': [
268 + FileNode('foobar3', content='FOOBAR\nFOOBAR\nFOOBAR\n'),
268 + FileNode(b'foobar3', content='FOOBAR\nFOOBAR\nFOOBAR\n'),
269 + ],
269 + ],
270 + 'removed': [FileNode('foobar')],
270 + 'removed': [FileNode(b'foobar')],
271 + },
271 + },
272 + ]
272 + ]
273 + return commits
273 + return commits
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -25,7 +25,9 b' from rhodecode.tests.fixture import Fixt'
25
25
26
26
27 def route_path(name, params=None, **kwargs):
27 def route_path(name, params=None, **kwargs):
28 import urllib.request, urllib.parse, urllib.error
28 import urllib.request
29 import urllib.parse
30 import urllib.error
29 from rhodecode.apps._base import ADMIN_PREFIX
31 from rhodecode.apps._base import ADMIN_PREFIX
30
32
31 base_url = {
33 base_url = {
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -18,11 +17,10 b''
18 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
19
21 import base64
22
23 import mock
20 import mock
24 import pytest
21 import pytest
25
22
23 from rhodecode.lib.str_utils import base64_to_str
26 from rhodecode.lib.utils2 import AttributeDict
24 from rhodecode.lib.utils2 import AttributeDict
27 from rhodecode.tests.utils import CustomTestApp
25 from rhodecode.tests.utils import CustomTestApp
28
26
@@ -32,7 +30,7 b' from rhodecode.lib.middleware import sim'
32 from rhodecode.lib.middleware.https_fixup import HttpsFixup
30 from rhodecode.lib.middleware.https_fixup import HttpsFixup
33 from rhodecode.lib.middleware.utils import scm_app_http
31 from rhodecode.lib.middleware.utils import scm_app_http
34 from rhodecode.model.db import User, _hash_key
32 from rhodecode.model.db import User, _hash_key
35 from rhodecode.model.meta import Session
33 from rhodecode.model.meta import Session, cache as db_cache
36 from rhodecode.tests import (
34 from rhodecode.tests import (
37 HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
35 HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
38 from rhodecode.tests.lib.middleware import mock_scm_app
36 from rhodecode.tests.lib.middleware import mock_scm_app
@@ -75,10 +73,13 b' class StubVCSController(simplevcs.Simple'
75
73
76 @pytest.fixture()
74 @pytest.fixture()
77 def vcscontroller(baseapp, config_stub, request_stub):
75 def vcscontroller(baseapp, config_stub, request_stub):
76 from rhodecode.config.middleware import ce_auth_resources
77
78 config_stub.testing_securitypolicy()
78 config_stub.testing_securitypolicy()
79 config_stub.include('rhodecode.authentication')
79 config_stub.include('rhodecode.authentication')
80 config_stub.include('rhodecode.authentication.plugins.auth_rhodecode')
80
81 config_stub.include('rhodecode.authentication.plugins.auth_token')
81 for resource in ce_auth_resources:
82 config_stub.include(resource)
82
83
83 controller = StubVCSController(
84 controller = StubVCSController(
84 baseapp.config.get_settings(), request_stub.registry)
85 baseapp.config.get_settings(), request_stub.registry)
@@ -98,27 +99,45 b' def _remove_default_user_from_query_cach'
98 user = User.get_default_user(cache=True)
99 user = User.get_default_user(cache=True)
99 query = Session().query(User).filter(User.username == user.username)
100 query = Session().query(User).filter(User.username == user.username)
100 query = query.options(
101 query = query.options(
101 FromCache("sql_cache_short", "get_user_%s" % _hash_key(user.username)))
102 FromCache("sql_cache_short", f"get_user_{_hash_key(user.username)}"))
102 query.invalidate()
103
104 db_cache.invalidate(
105 query, {},
106 FromCache("sql_cache_short", f"get_user_{_hash_key(user.username)}"))
107
103 Session().expire(user)
108 Session().expire(user)
104
109
105
110
106 def test_handles_exceptions_during_permissions_checks(
111 def test_handles_exceptions_during_permissions_checks(
107 vcscontroller, disable_anonymous_user):
112 vcscontroller, disable_anonymous_user, enable_auth_plugins, test_user_factory):
108 user_and_pass = '%s:%s' % (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
113
109 auth_password = base64.encodestring(user_and_pass).strip()
114 test_password = 'qweqwe'
115 test_user = test_user_factory(password=test_password, extern_type='headers', extern_name='headers')
116 test_username = test_user.username
117
118 enable_auth_plugins.enable([
119 'egg:rhodecode-enterprise-ce#headers',
120 'egg:rhodecode-enterprise-ce#token',
121 'egg:rhodecode-enterprise-ce#rhodecode'],
122 override={
123 'egg:rhodecode-enterprise-ce#headers': {'auth_headers_header': 'REMOTE_USER'}
124 })
125
126 user_and_pass = f'{test_username}:{test_password}'
127 auth_password = base64_to_str(user_and_pass)
128
110 extra_environ = {
129 extra_environ = {
111 'AUTH_TYPE': 'Basic',
130 'AUTH_TYPE': 'Basic',
112 'HTTP_AUTHORIZATION': 'Basic %s' % auth_password,
131 'HTTP_AUTHORIZATION': f'Basic {auth_password}',
113 'REMOTE_USER': TEST_USER_ADMIN_LOGIN,
132 'REMOTE_USER': test_username,
114 }
133 }
115
134
116 # Verify that things are hooked up correctly
135 # Verify that things are hooked up correctly, we pass user with headers bound auth, and headers filled in
117 vcscontroller.get('/', status=200, extra_environ=extra_environ)
136 vcscontroller.get('/', status=200, extra_environ=extra_environ)
118
137
119 # Simulate trouble during permission checks
138 # Simulate trouble during permission checks
120 with mock.patch('rhodecode.model.db.User.get_by_username',
139 with mock.patch('rhodecode.model.db.User.get_by_username',
121 side_effect=Exception) as get_user:
140 side_effect=Exception('permission_error_test')) as get_user:
122 # Verify that a correct 500 is returned and check that the expected
141 # Verify that a correct 500 is returned and check that the expected
123 # code path was hit.
142 # code path was hit.
124 vcscontroller.get('/', status=500, extra_environ=extra_environ)
143 vcscontroller.get('/', status=500, extra_environ=extra_environ)
@@ -230,7 +249,7 b' class TestShadowRepoExposure(object):'
230 controller.is_shadow_repo = True
249 controller.is_shadow_repo = True
231 controller._action = 'pull'
250 controller._action = 'pull'
232 controller._is_shadow_repo_dir = True
251 controller._is_shadow_repo_dir = True
233 controller.stub_response_body = 'dummy body value'
252 controller.stub_response_body = (b'dummy body value',)
234 controller._get_default_cache_ttl = mock.Mock(
253 controller._get_default_cache_ttl = mock.Mock(
235 return_value=(False, 0))
254 return_value=(False, 0))
236
255
@@ -242,10 +261,10 b' class TestShadowRepoExposure(object):'
242 }
261 }
243
262
244 response = controller(environ_stub, mock.Mock())
263 response = controller(environ_stub, mock.Mock())
245 response_body = ''.join(response)
264 response_body = b''.join(response)
246
265
247 # Assert that we got the response from the wsgi app.
266 # Assert that we got the response from the wsgi app.
248 assert response_body == controller.stub_response_body
267 assert response_body == b''.join(controller.stub_response_body)
249
268
250 def test_pull_on_shadow_repo_that_is_missing(self, baseapp, request_stub):
269 def test_pull_on_shadow_repo_that_is_missing(self, baseapp, request_stub):
251 """
270 """
@@ -258,7 +277,7 b' class TestShadowRepoExposure(object):'
258 controller.is_shadow_repo = True
277 controller.is_shadow_repo = True
259 controller._action = 'pull'
278 controller._action = 'pull'
260 controller._is_shadow_repo_dir = False
279 controller._is_shadow_repo_dir = False
261 controller.stub_response_body = 'dummy body value'
280 controller.stub_response_body = (b'dummy body value',)
262 environ_stub = {
281 environ_stub = {
263 'HTTP_HOST': 'test.example.com',
282 'HTTP_HOST': 'test.example.com',
264 'HTTP_ACCEPT': 'application/mercurial',
283 'HTTP_ACCEPT': 'application/mercurial',
@@ -267,10 +286,10 b' class TestShadowRepoExposure(object):'
267 }
286 }
268
287
269 response = controller(environ_stub, mock.Mock())
288 response = controller(environ_stub, mock.Mock())
270 response_body = ''.join(response)
289 response_body = b''.join(response)
271
290
272 # Assert that we got the response from the wsgi app.
291 # Assert that we got the response from the wsgi app.
273 assert '404 Not Found' in response_body
292 assert b'404 Not Found' in response_body
274
293
275 def test_push_on_shadow_repo_raises(self, baseapp, request_stub):
294 def test_push_on_shadow_repo_raises(self, baseapp, request_stub):
276 """
295 """
@@ -281,7 +300,7 b' class TestShadowRepoExposure(object):'
281 controller._check_ssl = mock.Mock()
300 controller._check_ssl = mock.Mock()
282 controller.is_shadow_repo = True
301 controller.is_shadow_repo = True
283 controller._action = 'push'
302 controller._action = 'push'
284 controller.stub_response_body = 'dummy body value'
303 controller.stub_response_body = (b'dummy body value',)
285 environ_stub = {
304 environ_stub = {
286 'HTTP_HOST': 'test.example.com',
305 'HTTP_HOST': 'test.example.com',
287 'HTTP_ACCEPT': 'application/mercurial',
306 'HTTP_ACCEPT': 'application/mercurial',
@@ -290,11 +309,11 b' class TestShadowRepoExposure(object):'
290 }
309 }
291
310
292 response = controller(environ_stub, mock.Mock())
311 response = controller(environ_stub, mock.Mock())
293 response_body = ''.join(response)
312 response_body = b''.join(response)
294
313
295 assert response_body != controller.stub_response_body
314 assert response_body != controller.stub_response_body
296 # Assert that a 406 error is returned.
315 # Assert that a 406 error is returned.
297 assert '406 Not Acceptable' in response_body
316 assert b'406 Not Acceptable' in response_body
298
317
299 def test_set_repo_names_no_shadow(self, baseapp, request_stub):
318 def test_set_repo_names_no_shadow(self, baseapp, request_stub):
300 """
319 """
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -55,20 +55,20 b' def data():'
55
55
56 def test_reuse_app_no_data(repeat, vcsserver_http_echo_app):
56 def test_reuse_app_no_data(repeat, vcsserver_http_echo_app):
57 app = vcs_http_app(vcsserver_http_echo_app)
57 app = vcs_http_app(vcsserver_http_echo_app)
58 for x in range(repeat / 10):
58 for x in range(repeat // 10):
59 response = app.post('/')
59 response = app.post('/')
60 assert response.status_code == 200
60 assert response.status_code == 200
61
61
62
62
63 def test_reuse_app_with_data(data, repeat, vcsserver_http_echo_app):
63 def test_reuse_app_with_data(data, repeat, vcsserver_http_echo_app):
64 app = vcs_http_app(vcsserver_http_echo_app)
64 app = vcs_http_app(vcsserver_http_echo_app)
65 for x in range(repeat / 10):
65 for x in range(repeat // 10):
66 response = app.post('/', params=data)
66 response = app.post('/', params=data)
67 assert response.status_code == 200
67 assert response.status_code == 200
68
68
69
69
70 def test_create_app_per_request_no_data(repeat, vcsserver_http_echo_app):
70 def test_create_app_per_request_no_data(repeat, vcsserver_http_echo_app):
71 for x in range(repeat / 10):
71 for x in range(repeat // 10):
72 app = vcs_http_app(vcsserver_http_echo_app)
72 app = vcs_http_app(vcsserver_http_echo_app)
73 response = app.post('/')
73 response = app.post('/')
74 assert response.status_code == 200
74 assert response.status_code == 200
@@ -76,7 +76,7 b' def test_create_app_per_request_no_data('
76
76
77 def test_create_app_per_request_with_data(
77 def test_create_app_per_request_with_data(
78 data, repeat, vcsserver_http_echo_app):
78 data, repeat, vcsserver_http_echo_app):
79 for x in range(repeat / 10):
79 for x in range(repeat // 10):
80 app = vcs_http_app(vcsserver_http_echo_app)
80 app = vcs_http_app(vcsserver_http_echo_app)
81 response = app.post('/', params=data)
81 response = app.post('/', params=data)
82 assert response.status_code == 200
82 assert response.status_code == 200
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -82,7 +81,7 b' def test_remote_app_caller():'
82 ('a1', 'a2', 'a3', 'a4', None))
81 ('a1', 'a2', 'a3', 'a4', None))
83 # Note: RemoteAppCaller is expected to return a tuple like the
82 # Note: RemoteAppCaller is expected to return a tuple like the
84 # following one
83 # following one
85 return (['content'], '200 OK', [('Content-Type', 'text/plain')])
84 return ([b'content'], '200 OK', [('Content-Type', 'text/plain')])
86
85
87 wrapper_app = wsgi_app_caller_client.RemoteAppCaller(
86 wrapper_app = wsgi_app_caller_client.RemoteAppCaller(
88 RemoteAppCallerMock(), 'a1', 'a2', arg3='a3', arg4='a4')
87 RemoteAppCallerMock(), 'a1', 'a2', arg3='a3', arg4='a4')
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,5 +1,4 b''
1 import collections
1 import collections
2 # -*- coding: utf-8 -*-
3
2
4 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
5 #
4 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -19,13 +18,13 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
19
21 import os
20 import os
22 from hashlib import sha1
23
21
24 import pytest
22 import pytest
25 from mock import patch
23 from mock import patch
26
24
27 from rhodecode.lib import auth
25 from rhodecode.lib import auth
28 from rhodecode.lib.utils2 import md5
26 from rhodecode.lib.str_utils import safe_bytes
27 from rhodecode.lib.hash_utils import md5_safe, sha1
29 from rhodecode.model.auth_token import AuthTokenModel
28 from rhodecode.model.auth_token import AuthTokenModel
30 from rhodecode.model.db import Session, User
29 from rhodecode.model.db import Session, User
31 from rhodecode.model.repo import RepoModel
30 from rhodecode.model.repo import RepoModel
@@ -638,7 +637,7 b' def test_auth_user_get_cookie_store_for_'
638 expected_data = {
637 expected_data = {
639 'username': user.username,
638 'username': user.username,
640 'user_id': user.user_id,
639 'user_id': user.user_id,
641 'password': md5(user.password),
640 'password': md5_safe(user.password),
642 'is_authenticated': False
641 'is_authenticated': False
643 }
642 }
644 assert auth_user.get_cookie_store() == expected_data
643 assert auth_user.get_cookie_store() == expected_data
@@ -650,7 +649,7 b' def test_auth_user_get_cookie_store_for_'
650 expected_data = {
649 expected_data = {
651 'username': User.DEFAULT_USER,
650 'username': User.DEFAULT_USER,
652 'user_id': default_user.user_id,
651 'user_id': default_user.user_id,
653 'password': md5(default_user.password),
652 'password': md5_safe(default_user.password),
654 'is_authenticated': True
653 'is_authenticated': True
655 }
654 }
656 assert auth_user.get_cookie_store() == expected_data
655 assert auth_user.get_cookie_store() == expected_data
@@ -678,10 +677,10 b' def get_permissions(user, **kwargs):'
678
677
679 class TestGenerateAuthToken(object):
678 class TestGenerateAuthToken(object):
680 def test_salt_is_used_when_specified(self):
679 def test_salt_is_used_when_specified(self):
681 salt = 'abcde'
680 salt = b'abcde'
682 user_name = 'test_user'
681 user_name = 'test_user'
683 result = auth.generate_auth_token(user_name, salt)
682 result = auth.generate_auth_token(user_name, salt)
684 expected_result = sha1(user_name + salt).hexdigest()
683 expected_result = sha1(safe_bytes(user_name) + salt)
685 assert result == expected_result
684 assert result == expected_result
686
685
687 def test_salt_is_geneated_when_not_specified(self):
686 def test_salt_is_geneated_when_not_specified(self):
@@ -690,7 +689,8 b' class TestGenerateAuthToken(object):'
690 with patch.object(auth, 'os') as os_mock:
689 with patch.object(auth, 'os') as os_mock:
691 os_mock.urandom.return_value = random_salt
690 os_mock.urandom.return_value = random_salt
692 result = auth.generate_auth_token(user_name)
691 result = auth.generate_auth_token(user_name)
693 expected_result = sha1(user_name + random_salt).hexdigest()
692
693 expected_result = sha1(safe_bytes(user_name) + random_salt)
694 assert result == expected_result
694 assert result == expected_result
695
695
696
696
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -67,12 +67,12 b' class TestCaches(object):'
67 def test_cache_keygen(self, example_input, example_namespace):
67 def test_cache_keygen(self, example_input, example_namespace):
68 def func_wrapped():
68 def func_wrapped():
69 return 1
69 return 1
70 func = rc_cache.utils.key_generator(None, example_namespace, func_wrapped)
70 func = rc_cache.utils.custom_key_generator(None, example_namespace, func_wrapped)
71 key = func(*example_input)
71 key = func(*example_input)
72 assert key
72 assert key
73
73
74 def test_store_value_in_cache(self):
74 def test_store_value_in_cache(self):
75 cache_region = rc_cache.get_or_create_region('cache_perms')
75 cache_region = rc_cache.get_or_create_region('cache_perms', 'test_cache')
76 # make sure we empty the cache now
76 # make sure we empty the cache now
77 cache_region.delete_multi(cache_region.backend.list_keys())
77 cache_region.delete_multi(cache_region.backend.list_keys())
78
78
@@ -88,7 +88,7 b' class TestCaches(object):'
88 assert len(set(cache_region.backend.list_keys())) == 10
88 assert len(set(cache_region.backend.list_keys())) == 10
89
89
90 def test_store_and_get_value_from_region(self):
90 def test_store_and_get_value_from_region(self):
91 cache_region = rc_cache.get_or_create_region('cache_perms')
91 cache_region = rc_cache.get_or_create_region('cache_perms', 'test_cache')
92 # make sure we empty the cache now
92 # make sure we empty the cache now
93 for key in cache_region.backend.list_keys():
93 for key in cache_region.backend.list_keys():
94 cache_region.delete(key)
94 cache_region.delete(key)
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -40,33 +40,34 b' class TestTokenizeString(object):'
40 def test_tokenize_as_python(self):
40 def test_tokenize_as_python(self):
41 lexer = get_lexer_by_name('python')
41 lexer = get_lexer_by_name('python')
42 tokens = list(tokenize_string(self.python_code, lexer))
42 tokens = list(tokenize_string(self.python_code, lexer))
43 expected_tokens = [
44 ('w', '\n'),
45 ('', ' '),
46 ('kn', 'import'),
47 ('', ' '),
48 ('nn', 'this'),
49 ('w', '\n'),
50 ('w', '\n'),
51 ('', ' '),
52 ('n', 'var'),
53 ('', ' '),
54 ('o', '='),
55 ('', ' '),
56 ('mi', '6'),
57 ('w', '\n'),
58 ('', ' '),
59 ('nb', 'print'),
60 ('p', '('),
61 ('s2', '"'),
62 ('s2', 'this'),
63 ('s2', '"'),
64 ('p', ')'),
65 ('w', '\n'),
66 ('w', '\n'),
67 ('', ' ')
68 ]
43
69
44 assert tokens == [
70 assert tokens == expected_tokens
45 ('', u'\n'),
46 ('', u' '),
47 ('kn', u'import'),
48 ('', u' '),
49 ('nn', u'this'),
50 ('', u'\n'),
51 ('', u'\n'),
52 ('', u' '),
53 ('n', u'var'),
54 ('', u' '),
55 ('o', u'='),
56 ('', u' '),
57 ('mi', u'6'),
58 ('', u'\n'),
59 ('', u' '),
60 ('k', u'print'),
61 ('p', u'('),
62 ('s2', u'"'),
63 ('s2', u'this'),
64 ('s2', u'"'),
65 ('p', u')'),
66 ('', u'\n'),
67 ('', u'\n'),
68 ('', u' ')
69 ]
70
71
71 def test_tokenize_as_text(self):
72 def test_tokenize_as_text(self):
72 lexer = get_lexer_by_name('text')
73 lexer = get_lexer_by_name('text')
@@ -74,7 +75,7 b' class TestTokenizeString(object):'
74
75
75 assert tokens == [
76 assert tokens == [
76 ('',
77 ('',
77 u'\n import this\n\n var = 6\n print("this")\n\n ')
78 '\n import this\n\n var = 6\n print("this")\n\n ')
78 ]
79 ]
79
80
80
81
@@ -86,9 +87,9 b' class TestSplitTokenStream(object):'
86 lines = list(split_token_stream(tokens, content))
87 lines = list(split_token_stream(tokens, content))
87
88
88 assert lines == [
89 assert lines == [
89 [('type1', u'some')],
90 [('type1', 'some')],
90 [('type1', u'text'), ('type2', u'more')],
91 [('type1', 'text'), ('type2', 'more')],
91 [('type2', u'')],
92 [('type2', '')],
92 ]
93 ]
93
94
94 def test_split_token_stream_single(self):
95 def test_split_token_stream_single(self):
@@ -126,7 +127,7 b' class TestSplitTokenStream(object):'
126
127
127 def test_no_tokens_by_content(self):
128 def test_no_tokens_by_content(self):
128 tokens = []
129 tokens = []
129 content = u'\ufeff'
130 content = '\ufeff'
130 lines = list(split_token_stream(tokens, content))
131 lines = list(split_token_stream(tokens, content))
131 assert lines == [
132 assert lines == [
132 [('', content)],
133 [('', content)],
@@ -134,15 +135,15 b' class TestSplitTokenStream(object):'
134
135
135 def test_no_tokens_by_valid_content(self):
136 def test_no_tokens_by_valid_content(self):
136 from pygments.lexers.css import CssLexer
137 from pygments.lexers.css import CssLexer
137 content = u'\ufeff table.dataTable'
138 content = '\ufeff table.dataTable'
138 tokens = tokenize_string(content, CssLexer())
139 tokens = tokenize_string(content, CssLexer())
139
140
140 lines = list(split_token_stream(tokens, content))
141 lines = list(split_token_stream(tokens, content))
141 assert lines == [
142 assert lines == [
142 [('', u' '),
143 [('w', ' '),
143 ('nt', u'table'),
144 ('nt', 'table'),
144 ('p', u'.'),
145 ('p', '.'),
145 ('nc', u'dataTable')],
146 ('nc', 'dataTable')],
146 ]
147 ]
147
148
148
149
@@ -248,59 +249,59 b' class TestRenderTokenStream(object):'
248 '',
249 '',
249 ),
250 ),
250 (
251 (
251 [('', '', u'')],
252 [('', '', '')],
252 '<span></span>',
253 '<span></span>',
253 ),
254 ),
254 (
255 (
255 [('', '', u'text')],
256 [('', '', 'text')],
256 '<span>text</span>',
257 '<span>text</span>',
257 ),
258 ),
258 (
259 (
259 [('A', '', u'')],
260 [('A', '', '')],
260 '<span class="A"></span>',
261 '<span class="A"></span>',
261 ),
262 ),
262 (
263 (
263 [('A', '', u'hello')],
264 [('A', '', 'hello')],
264 '<span class="A">hello</span>',
265 '<span class="A">hello</span>',
265 ),
266 ),
266 (
267 (
267 [('A', '', u'hel'), ('A', '', u'lo')],
268 [('A', '', 'hel'), ('A', '', 'lo')],
268 '<span class="A">hello</span>',
269 '<span class="A">hello</span>',
269 ),
270 ),
270 (
271 (
271 [('A', '', u'two\n'), ('A', '', u'lines')],
272 [('A', '', 'two\n'), ('A', '', 'lines')],
272 '<span class="A">two\nlines</span>',
273 '<span class="A">two\nlines</span>',
273 ),
274 ),
274 (
275 (
275 [('A', '', u'\nthree\n'), ('A', '', u'lines')],
276 [('A', '', '\nthree\n'), ('A', '', 'lines')],
276 '<span class="A">\nthree\nlines</span>',
277 '<span class="A">\nthree\nlines</span>',
277 ),
278 ),
278 (
279 (
279 [('', '', u'\n'), ('A', '', u'line')],
280 [('', '', '\n'), ('A', '', 'line')],
280 '<span>\n</span><span class="A">line</span>',
281 '<span>\n</span><span class="A">line</span>',
281 ),
282 ),
282 (
283 (
283 [('', 'ins', u'\n'), ('A', '', u'line')],
284 [('', 'ins', '\n'), ('A', '', 'line')],
284 '<span><ins>\n</ins></span><span class="A">line</span>',
285 '<span><ins>\n</ins></span><span class="A">line</span>',
285 ),
286 ),
286 (
287 (
287 [('A', '', u'hel'), ('A', 'ins', u'lo')],
288 [('A', '', 'hel'), ('A', 'ins', 'lo')],
288 '<span class="A">hel<ins>lo</ins></span>',
289 '<span class="A">hel<ins>lo</ins></span>',
289 ),
290 ),
290 (
291 (
291 [('A', '', u'hel'), ('A', 'ins', u'l'), ('A', 'ins', u'o')],
292 [('A', '', 'hel'), ('A', 'ins', 'l'), ('A', 'ins', 'o')],
292 '<span class="A">hel<ins>lo</ins></span>',
293 '<span class="A">hel<ins>lo</ins></span>',
293 ),
294 ),
294 (
295 (
295 [('A', '', u'hel'), ('A', 'ins', u'l'), ('A', 'del', u'o')],
296 [('A', '', 'hel'), ('A', 'ins', 'l'), ('A', 'del', 'o')],
296 '<span class="A">hel<ins>l</ins><del>o</del></span>',
297 '<span class="A">hel<ins>l</ins><del>o</del></span>',
297 ),
298 ),
298 (
299 (
299 [('A', '', u'hel'), ('B', '', u'lo')],
300 [('A', '', 'hel'), ('B', '', 'lo')],
300 '<span class="A">hel</span><span class="B">lo</span>',
301 '<span class="A">hel</span><span class="B">lo</span>',
301 ),
302 ),
302 (
303 (
303 [('A', '', u'hel'), ('B', 'ins', u'lo')],
304 [('A', '', 'hel'), ('B', 'ins', 'lo')],
304 '<span class="A">hel</span><span class="B"><ins>lo</ins></span>',
305 '<span class="A">hel</span><span class="B"><ins>lo</ins></span>',
305 ),
306 ),
306 ], ids=no_newline_id_generator)
307 ], ids=no_newline_id_generator)
@@ -310,23 +311,23 b' class TestRenderTokenStream(object):'
310
311
311 @pytest.mark.parametrize('tokenstream,output', [
312 @pytest.mark.parametrize('tokenstream,output', [
312 (
313 (
313 [('A', u'hel'), ('A', u'lo')],
314 [('A', 'hel'), ('A', 'lo')],
314 '<span class="A">hello</span>',
315 '<span class="A">hello</span>',
315 ),
316 ),
316 (
317 (
317 [('A', u'hel'), ('A', u'l'), ('A', u'o')],
318 [('A', 'hel'), ('A', 'l'), ('A', 'o')],
318 '<span class="A">hello</span>',
319 '<span class="A">hello</span>',
319 ),
320 ),
320 (
321 (
321 [('A', u'hel'), ('A', u'l'), ('A', u'o')],
322 [('A', 'hel'), ('A', 'l'), ('A', 'o')],
322 '<span class="A">hello</span>',
323 '<span class="A">hello</span>',
323 ),
324 ),
324 (
325 (
325 [('A', u'hel'), ('B', u'lo')],
326 [('A', 'hel'), ('B', 'lo')],
326 '<span class="A">hel</span><span class="B">lo</span>',
327 '<span class="A">hel</span><span class="B">lo</span>',
327 ),
328 ),
328 (
329 (
329 [('A', u'hel'), ('B', u'lo')],
330 [('A', 'hel'), ('B', 'lo')],
330 '<span class="A">hel</span><span class="B">lo</span>',
331 '<span class="A">hel</span><span class="B">lo</span>',
331 ),
332 ),
332 ])
333 ])
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -28,6 +27,7 b' from rhodecode.lib.diffs import ('
28 DiffProcessor,
27 DiffProcessor,
29 NEW_FILENODE, DEL_FILENODE, MOD_FILENODE, RENAMED_FILENODE,
28 NEW_FILENODE, DEL_FILENODE, MOD_FILENODE, RENAMED_FILENODE,
30 CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE)
29 CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE)
30 from rhodecode.lib.str_utils import safe_bytes
31 from rhodecode.lib.utils2 import AttributeDict
31 from rhodecode.lib.utils2 import AttributeDict
32 from rhodecode.lib.vcs.backends.git import GitCommit
32 from rhodecode.lib.vcs.backends.git import GitCommit
33 from rhodecode.tests.fixture import Fixture, no_newline_id_generator
33 from rhodecode.tests.fixture import Fixture, no_newline_id_generator
@@ -38,134 +38,6 b' from rhodecode.lib.vcs.backends.svn.repo'
38 fixture = Fixture()
38 fixture = Fixture()
39
39
40
40
41 def test_diffprocessor_as_html_with_comments():
42 raw_diff = textwrap.dedent('''
43 diff --git a/setup.py b/setup.py
44 index 5b36422..cfd698e 100755
45 --- a/setup.py
46 +++ b/setup.py
47 @@ -2,7 +2,7 @@
48 #!/usr/bin/python
49 # Setup file for X
50 # Copyright (C) No one
51 -
52 +x
53 try:
54 from setuptools import setup, Extension
55 except ImportError:
56 ''')
57 diff = GitDiff(raw_diff)
58 processor = DiffProcessor(diff)
59 processor.prepare()
60
61 # Note that the cell with the context in line 5 (in the html) has the
62 # no-comment class, which will prevent the add comment icon to be displayed.
63 expected_html = textwrap.dedent('''
64 <table class="code-difftable">
65 <tr class="line context">
66 <td class="add-comment-line"><span class="add-comment-content"></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
67 <td class="lineno old">...</td>
68 <td class="lineno new">...</td>
69 <td class="code no-comment">
70 <pre>@@ -2,7 +2,7 @@
71 </pre>
72 </td>
73 </tr>
74 <tr class="line unmod">
75 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
76 <td id="setuppy_o2" class="lineno old"><a href="#setuppy_o2" class="tooltip"
77 title="Click to select line">2</a></td>
78 <td id="setuppy_n2" class="lineno new"><a href="#setuppy_n2" class="tooltip"
79 title="Click to select line">2</a></td>
80 <td class="code">
81 <pre>#!/usr/bin/python
82 </pre>
83 </td>
84 </tr>
85 <tr class="line unmod">
86 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
87 <td id="setuppy_o3" class="lineno old"><a href="#setuppy_o3" class="tooltip"
88 title="Click to select line">3</a></td>
89 <td id="setuppy_n3" class="lineno new"><a href="#setuppy_n3" class="tooltip"
90 title="Click to select line">3</a></td>
91 <td class="code">
92 <pre># Setup file for X
93 </pre>
94 </td>
95 </tr>
96 <tr class="line unmod">
97 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
98 <td id="setuppy_o4" class="lineno old"><a href="#setuppy_o4" class="tooltip"
99 title="Click to select line">4</a></td>
100 <td id="setuppy_n4" class="lineno new"><a href="#setuppy_n4" class="tooltip"
101 title="Click to select line">4</a></td>
102 <td class="code">
103 <pre># Copyright (C) No one
104 </pre>
105 </td>
106 </tr>
107 <tr class="line del">
108 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
109 <td id="setuppy_o5" class="lineno old"><a href="#setuppy_o5" class="tooltip"
110 title="Click to select line">5</a></td>
111 <td class="lineno new"><a href="#setuppy_n" class="tooltip"
112 title="Click to select line"></a></td>
113 <td class="code">
114 <pre>
115 </pre>
116 </td>
117 </tr>
118 <tr class="line add">
119 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
120 <td class="lineno old"><a href="#setuppy_o" class="tooltip"
121 title="Click to select line"></a></td>
122 <td id="setuppy_n5" class="lineno new"><a href="#setuppy_n5" class="tooltip"
123 title="Click to select line">5</a></td>
124 <td class="code">
125 <pre><ins>x</ins>
126 </pre>
127 </td>
128 </tr>
129 <tr class="line unmod">
130 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
131 <td id="setuppy_o6" class="lineno old"><a href="#setuppy_o6" class="tooltip"
132 title="Click to select line">6</a></td>
133 <td id="setuppy_n6" class="lineno new"><a href="#setuppy_n6" class="tooltip"
134 title="Click to select line">6</a></td>
135 <td class="code">
136 <pre>try:
137 </pre>
138 </td>
139 </tr>
140 <tr class="line unmod">
141 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
142 <td id="setuppy_o7" class="lineno old"><a href="#setuppy_o7" class="tooltip"
143 title="Click to select line">7</a></td>
144 <td id="setuppy_n7" class="lineno new"><a href="#setuppy_n7" class="tooltip"
145 title="Click to select line">7</a></td>
146 <td class="code">
147 <pre> from setuptools import setup, Extension
148 </pre>
149 </td>
150 </tr>
151 <tr class="line unmod">
152 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
153 <td id="setuppy_o8" class="lineno old"><a href="#setuppy_o8" class="tooltip"
154 title="Click to select line">8</a></td>
155 <td id="setuppy_n8" class="lineno new"><a href="#setuppy_n8" class="tooltip"
156 title="Click to select line">8</a></td>
157 <td class="code">
158 <pre>except ImportError:
159 </pre>
160 </td>
161 </tr>
162 </table>
163 ''').strip()
164 html = processor.as_html(enable_comments=True).replace('\t', ' ')
165
166 assert html == expected_html
167
168
169 class TestMixedFilenameEncodings(object):
41 class TestMixedFilenameEncodings(object):
170
42
171 @pytest.fixture(scope="class")
43 @pytest.fixture(scope="class")
@@ -176,7 +48,7 b' class TestMixedFilenameEncodings(object)'
176 @pytest.fixture()
48 @pytest.fixture()
177 def processor(self, raw_diff):
49 def processor(self, raw_diff):
178 diff = MercurialDiff(raw_diff)
50 diff = MercurialDiff(raw_diff)
179 processor = DiffProcessor(diff)
51 processor = DiffProcessor(diff, diff_format='newdiff')
180 return processor
52 return processor
181
53
182 def test_filenames_are_decoded_to_unicode(self, processor):
54 def test_filenames_are_decoded_to_unicode(self, processor):
@@ -207,6 +79,8 b' DIFF_FIXTURES = ['
207 {'added': 0,
79 {'added': 0,
208 'deleted': 0,
80 'deleted': 0,
209 'binary': True,
81 'binary': True,
82 'old_mode': '',
83 'new_mode': '100755',
210 'ops': {NEW_FILENODE: 'new file 100755',
84 'ops': {NEW_FILENODE: 'new file 100755',
211 BIN_FILENODE: 'binary diff hidden'}}),
85 BIN_FILENODE: 'binary diff hidden'}}),
212 ]),
86 ]),
@@ -216,6 +90,8 b' DIFF_FIXTURES = ['
216 {'added': 0,
90 {'added': 0,
217 'deleted': 0,
91 'deleted': 0,
218 'binary': True,
92 'binary': True,
93 'old_mode': '',
94 'new_mode': '',
219 'ops': {MOD_FILENODE: 'modified file',
95 'ops': {MOD_FILENODE: 'modified file',
220 BIN_FILENODE: 'binary diff hidden'}}),
96 BIN_FILENODE: 'binary diff hidden'}}),
221 ]),
97 ]),
@@ -225,6 +101,9 b' DIFF_FIXTURES = ['
225 {'added': 3,
101 {'added': 3,
226 'deleted': 0,
102 'deleted': 0,
227 'binary': False,
103 'binary': False,
104 'old_mode': '100755',
105 'new_mode': '100644',
106 'renamed': ('README.rst', 'README'),
228 'ops': {MOD_FILENODE: 'modified file',
107 'ops': {MOD_FILENODE: 'modified file',
229 RENAMED_FILENODE: 'file renamed from README.rst to README',
108 RENAMED_FILENODE: 'file renamed from README.rst to README',
230 CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
109 CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
@@ -235,6 +114,8 b' DIFF_FIXTURES = ['
235 {'added': 2,
114 {'added': 2,
236 'deleted': 1,
115 'deleted': 1,
237 'binary': False,
116 'binary': False,
117 'old_mode': '',
118 'new_mode': '',
238 'ops': {MOD_FILENODE: 'modified file'}}),
119 'ops': {MOD_FILENODE: 'modified file'}}),
239 ]),
120 ]),
240 ('hg',
121 ('hg',
@@ -243,6 +124,9 b' DIFF_FIXTURES = ['
243 {'added': 3,
124 {'added': 3,
244 'deleted': 0,
125 'deleted': 0,
245 'binary': False,
126 'binary': False,
127 'old_mode': '',
128 'new_mode': '',
129 'renamed': ('README', 'README.rst'),
246 'ops': {MOD_FILENODE: 'modified file',
130 'ops': {MOD_FILENODE: 'modified file',
247 RENAMED_FILENODE: 'file renamed from README to README.rst'}}),
131 RENAMED_FILENODE: 'file renamed from README to README.rst'}}),
248 ]),
132 ]),
@@ -252,6 +136,8 b' DIFF_FIXTURES = ['
252 {'added': 0,
136 {'added': 0,
253 'deleted': 0,
137 'deleted': 0,
254 'binary': True,
138 'binary': True,
139 'old_mode': '',
140 'new_mode': '',
255 'ops': {DEL_FILENODE: 'deleted file',
141 'ops': {DEL_FILENODE: 'deleted file',
256 BIN_FILENODE: 'binary diff hidden'}}),
142 BIN_FILENODE: 'binary diff hidden'}}),
257 ]),
143 ]),
@@ -261,6 +147,8 b' DIFF_FIXTURES = ['
261 {'added': 0,
147 {'added': 0,
262 'deleted': 0,
148 'deleted': 0,
263 'binary': True,
149 'binary': True,
150 'old_mode': '100644',
151 'new_mode': '100755',
264 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
152 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
265 BIN_FILENODE: 'binary diff hidden'}}),
153 BIN_FILENODE: 'binary diff hidden'}}),
266 ]),
154 ]),
@@ -270,6 +158,8 b' DIFF_FIXTURES = ['
270 {'added': 0,
158 {'added': 0,
271 'deleted': 0,
159 'deleted': 0,
272 'binary': True,
160 'binary': True,
161 'old_mode': '100755',
162 'new_mode': '100644',
273 'ops': {CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
163 'ops': {CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
274 ]),
164 ]),
275 ('hg',
165 ('hg',
@@ -278,6 +168,9 b' DIFF_FIXTURES = ['
278 {'added': 0,
168 {'added': 0,
279 'deleted': 0,
169 'deleted': 0,
280 'binary': True,
170 'binary': True,
171 'old_mode': '',
172 'new_mode': '',
173 'renamed': ('file', 'file_renamed'),
281 'ops': {RENAMED_FILENODE: 'file renamed from file to file_renamed'}}),
174 'ops': {RENAMED_FILENODE: 'file renamed from file to file_renamed'}}),
282 ]),
175 ]),
283 ('hg',
176 ('hg',
@@ -286,6 +179,9 b' DIFF_FIXTURES = ['
286 {'added': 0,
179 {'added': 0,
287 'deleted': 0,
180 'deleted': 0,
288 'binary': True,
181 'binary': True,
182 'old_mode': '100644',
183 'new_mode': '100755',
184 'renamed': ('README.rst', 'README'),
289 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
185 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
290 RENAMED_FILENODE: 'file renamed from README.rst to README'}}),
186 RENAMED_FILENODE: 'file renamed from README.rst to README'}}),
291 ]),
187 ]),
@@ -295,43 +191,59 b' DIFF_FIXTURES = ['
295 {'added': 0,
191 {'added': 0,
296 'deleted': 0,
192 'deleted': 0,
297 'binary': True,
193 'binary': True,
194 'new_mode': '100644',
195 'old_mode': '',
298 'ops': {NEW_FILENODE: 'new file 100644',
196 'ops': {NEW_FILENODE: 'new file 100644',
299 BIN_FILENODE: 'binary diff hidden'}}),
197 BIN_FILENODE: 'binary diff hidden'}}),
300 ('js/jquery/hashgrid.js', 'A',
198 ('js/jquery/hashgrid.js', 'A',
301 {'added': 340,
199 {'added': 340,
302 'deleted': 0,
200 'deleted': 0,
303 'binary': False,
201 'binary': False,
202 'new_mode': '100755',
203 'old_mode': '',
304 'ops': {NEW_FILENODE: 'new file 100755'}}),
204 'ops': {NEW_FILENODE: 'new file 100755'}}),
305 ('index.html', 'M',
205 ('index.html', 'M',
306 {'added': 3,
206 {'added': 3,
307 'deleted': 2,
207 'deleted': 2,
308 'binary': False,
208 'binary': False,
209 'new_mode': '',
210 'old_mode': '',
309 'ops': {MOD_FILENODE: 'modified file'}}),
211 'ops': {MOD_FILENODE: 'modified file'}}),
310 ('less/docs.less', 'M',
212 ('less/docs.less', 'M',
311 {'added': 34,
213 {'added': 34,
312 'deleted': 0,
214 'deleted': 0,
313 'binary': False,
215 'binary': False,
216 'new_mode': '',
217 'old_mode': '',
314 'ops': {MOD_FILENODE: 'modified file'}}),
218 'ops': {MOD_FILENODE: 'modified file'}}),
315 ('less/scaffolding.less', 'M',
219 ('less/scaffolding.less', 'M',
316 {'added': 1,
220 {'added': 1,
317 'deleted': 3,
221 'deleted': 3,
318 'binary': False,
222 'binary': False,
223 'new_mode': '',
224 'old_mode': '',
319 'ops': {MOD_FILENODE: 'modified file'}}),
225 'ops': {MOD_FILENODE: 'modified file'}}),
320 ('readme.markdown', 'M',
226 ('readme.markdown', 'M',
321 {'added': 1,
227 {'added': 1,
322 'deleted': 10,
228 'deleted': 10,
323 'binary': False,
229 'binary': False,
230 'new_mode': '',
231 'old_mode': '',
324 'ops': {MOD_FILENODE: 'modified file'}}),
232 'ops': {MOD_FILENODE: 'modified file'}}),
325 ('img/baseline-20px.png', 'D',
233 ('img/baseline-20px.png', 'D',
326 {'added': 0,
234 {'added': 0,
327 'deleted': 0,
235 'deleted': 0,
328 'binary': True,
236 'binary': True,
237 'new_mode': '',
238 'old_mode': '',
329 'ops': {DEL_FILENODE: 'deleted file',
239 'ops': {DEL_FILENODE: 'deleted file',
330 BIN_FILENODE: 'binary diff hidden'}}),
240 BIN_FILENODE: 'binary diff hidden'}}),
331 ('js/global.js', 'D',
241 ('js/global.js', 'D',
332 {'added': 0,
242 {'added': 0,
333 'deleted': 75,
243 'deleted': 75,
334 'binary': False,
244 'binary': False,
245 'new_mode': '',
246 'old_mode': '',
335 'ops': {DEL_FILENODE: 'deleted file'}})
247 'ops': {DEL_FILENODE: 'deleted file'}})
336 ]),
248 ]),
337 ('git',
249 ('git',
@@ -340,6 +252,8 b' DIFF_FIXTURES = ['
340 {'added': 0,
252 {'added': 0,
341 'deleted': 0,
253 'deleted': 0,
342 'binary': True,
254 'binary': True,
255 'old_mode': '100644',
256 'new_mode': '100755',
343 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}})
257 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}})
344 ]),
258 ]),
345 ('git',
259 ('git',
@@ -348,6 +262,8 b' DIFF_FIXTURES = ['
348 {'added': 1,
262 {'added': 1,
349 'deleted': 0,
263 'deleted': 0,
350 'binary': False,
264 'binary': False,
265 'old_mode': '',
266 'new_mode': '100644',
351 'ops': {MOD_FILENODE: 'modified file'}})
267 'ops': {MOD_FILENODE: 'modified file'}})
352 ]),
268 ]),
353 ('git',
269 ('git',
@@ -356,6 +272,9 b' DIFF_FIXTURES = ['
356 {'added': 0,
272 {'added': 0,
357 'deleted': 0,
273 'deleted': 0,
358 'binary': True,
274 'binary': True,
275 'old_mode': '',
276 'new_mode': '',
277 'renamed': ('work-horus.xls', 'file.xls'),
359 'ops': {
278 'ops': {
360 RENAMED_FILENODE: 'file renamed from work-horus.xls to file.xls'}})
279 RENAMED_FILENODE: 'file renamed from work-horus.xls to file.xls'}})
361 ]),
280 ]),
@@ -365,6 +284,8 b' DIFF_FIXTURES = ['
365 {'added': 0,
284 {'added': 0,
366 'deleted': 0,
285 'deleted': 0,
367 'binary': True,
286 'binary': True,
287 'old_mode': '',
288 'new_mode': '',
368 'ops': {MOD_FILENODE: 'modified file',
289 'ops': {MOD_FILENODE: 'modified file',
369 BIN_FILENODE: 'binary diff hidden'}})
290 BIN_FILENODE: 'binary diff hidden'}})
370 ]),
291 ]),
@@ -374,43 +295,59 b' DIFF_FIXTURES = ['
374 {'added': 0,
295 {'added': 0,
375 'deleted': 0,
296 'deleted': 0,
376 'binary': True,
297 'binary': True,
298 'old_mode': '',
299 'new_mode': '100644',
377 'ops': {NEW_FILENODE: 'new file 100644',
300 'ops': {NEW_FILENODE: 'new file 100644',
378 BIN_FILENODE: 'binary diff hidden'}}),
301 BIN_FILENODE: 'binary diff hidden'}}),
379 ('js/jquery/hashgrid.js', 'A',
302 ('js/jquery/hashgrid.js', 'A',
380 {'added': 340,
303 {'added': 340,
381 'deleted': 0,
304 'deleted': 0,
382 'binary': False,
305 'binary': False,
306 'old_mode': '',
307 'new_mode': '100755',
383 'ops': {NEW_FILENODE: 'new file 100755'}}),
308 'ops': {NEW_FILENODE: 'new file 100755'}}),
384 ('index.html', 'M',
309 ('index.html', 'M',
385 {'added': 3,
310 {'added': 3,
386 'deleted': 2,
311 'deleted': 2,
387 'binary': False,
312 'binary': False,
313 'old_mode': '',
314 'new_mode': '100644',
388 'ops': {MOD_FILENODE: 'modified file'}}),
315 'ops': {MOD_FILENODE: 'modified file'}}),
389 ('less/docs.less', 'M',
316 ('less/docs.less', 'M',
390 {'added': 34,
317 {'added': 34,
391 'deleted': 0,
318 'deleted': 0,
392 'binary': False,
319 'binary': False,
320 'old_mode': '',
321 'new_mode': '100644',
393 'ops': {MOD_FILENODE: 'modified file'}}),
322 'ops': {MOD_FILENODE: 'modified file'}}),
394 ('less/scaffolding.less', 'M',
323 ('less/scaffolding.less', 'M',
395 {'added': 1,
324 {'added': 1,
396 'deleted': 3,
325 'deleted': 3,
397 'binary': False,
326 'binary': False,
327 'old_mode': '',
328 'new_mode': '100644',
398 'ops': {MOD_FILENODE: 'modified file'}}),
329 'ops': {MOD_FILENODE: 'modified file'}}),
399 ('readme.markdown', 'M',
330 ('readme.markdown', 'M',
400 {'added': 1,
331 {'added': 1,
401 'deleted': 10,
332 'deleted': 10,
402 'binary': False,
333 'binary': False,
334 'old_mode': '',
335 'new_mode': '100644',
403 'ops': {MOD_FILENODE: 'modified file'}}),
336 'ops': {MOD_FILENODE: 'modified file'}}),
404 ('img/baseline-20px.png', 'D',
337 ('img/baseline-20px.png', 'D',
405 {'added': 0,
338 {'added': 0,
406 'deleted': 0,
339 'deleted': 0,
407 'binary': True,
340 'binary': True,
341 'old_mode': '',
342 'new_mode': '',
408 'ops': {DEL_FILENODE: 'deleted file',
343 'ops': {DEL_FILENODE: 'deleted file',
409 BIN_FILENODE: 'binary diff hidden'}}),
344 BIN_FILENODE: 'binary diff hidden'}}),
410 ('js/global.js', 'D',
345 ('js/global.js', 'D',
411 {'added': 0,
346 {'added': 0,
412 'deleted': 75,
347 'deleted': 75,
413 'binary': False,
348 'binary': False,
349 'old_mode': '',
350 'new_mode': '',
414 'ops': {DEL_FILENODE: 'deleted file'}}),
351 'ops': {DEL_FILENODE: 'deleted file'}}),
415 ]),
352 ]),
416 ('hg',
353 ('hg',
@@ -419,26 +356,36 b' DIFF_FIXTURES = ['
419 {'added': 18,
356 {'added': 18,
420 'deleted': 2,
357 'deleted': 2,
421 'binary': False,
358 'binary': False,
359 'old_mode': '',
360 'new_mode': '100644',
422 'ops': {MOD_FILENODE: 'modified file'}}),
361 'ops': {MOD_FILENODE: 'modified file'}}),
423 ('vcs/backends/git/repository.py', 'M',
362 ('vcs/backends/git/repository.py', 'M',
424 {'added': 46,
363 {'added': 46,
425 'deleted': 15,
364 'deleted': 15,
426 'binary': False,
365 'binary': False,
366 'old_mode': '',
367 'new_mode': '100644',
427 'ops': {MOD_FILENODE: 'modified file'}}),
368 'ops': {MOD_FILENODE: 'modified file'}}),
428 ('vcs/backends/hg.py', 'M',
369 ('vcs/backends/hg.py', 'M',
429 {'added': 22,
370 {'added': 22,
430 'deleted': 3,
371 'deleted': 3,
431 'binary': False,
372 'binary': False,
373 'old_mode': '',
374 'new_mode': '100644',
432 'ops': {MOD_FILENODE: 'modified file'}}),
375 'ops': {MOD_FILENODE: 'modified file'}}),
433 ('vcs/tests/test_git.py', 'M',
376 ('vcs/tests/test_git.py', 'M',
434 {'added': 5,
377 {'added': 5,
435 'deleted': 5,
378 'deleted': 5,
436 'binary': False,
379 'binary': False,
380 'old_mode': '',
381 'new_mode': '100644',
437 'ops': {MOD_FILENODE: 'modified file'}}),
382 'ops': {MOD_FILENODE: 'modified file'}}),
438 ('vcs/tests/test_repository.py', 'M',
383 ('vcs/tests/test_repository.py', 'M',
439 {'added': 174,
384 {'added': 174,
440 'deleted': 2,
385 'deleted': 2,
441 'binary': False,
386 'binary': False,
387 'old_mode': '',
388 'new_mode': '100644',
442 'ops': {MOD_FILENODE: 'modified file'}}),
389 'ops': {MOD_FILENODE: 'modified file'}}),
443 ]),
390 ]),
444 ('hg',
391 ('hg',
@@ -447,6 +394,9 b' DIFF_FIXTURES = ['
447 {'added': 0,
394 {'added': 0,
448 'deleted': 0,
395 'deleted': 0,
449 'binary': True,
396 'binary': True,
397 'old_mode': '',
398 'new_mode': '',
399 'copied': ('file1', 'file2'),
450 'ops': {COPIED_FILENODE: 'file copied from file1 to file2'}}),
400 'ops': {COPIED_FILENODE: 'file copied from file1 to file2'}}),
451 ]),
401 ]),
452 ('hg',
402 ('hg',
@@ -455,6 +405,9 b' DIFF_FIXTURES = ['
455 {'added': 1,
405 {'added': 1,
456 'deleted': 0,
406 'deleted': 0,
457 'binary': False,
407 'binary': False,
408 'old_mode': '',
409 'new_mode': '',
410 'copied': ('file2', 'file3'),
458 'ops': {COPIED_FILENODE: 'file copied from file2 to file3',
411 'ops': {COPIED_FILENODE: 'file copied from file2 to file3',
459 MOD_FILENODE: 'modified file'}}),
412 MOD_FILENODE: 'modified file'}}),
460 ]),
413 ]),
@@ -464,6 +417,9 b' DIFF_FIXTURES = ['
464 {'added': 0,
417 {'added': 0,
465 'deleted': 0,
418 'deleted': 0,
466 'binary': True,
419 'binary': True,
420 'old_mode': '100644',
421 'new_mode': '100755',
422 'copied': ('file3', 'file4'),
467 'ops': {COPIED_FILENODE: 'file copied from file3 to file4',
423 'ops': {COPIED_FILENODE: 'file copied from file3 to file4',
468 CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}}),
424 CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}}),
469 ]),
425 ]),
@@ -473,6 +429,9 b' DIFF_FIXTURES = ['
473 {'added': 2,
429 {'added': 2,
474 'deleted': 1,
430 'deleted': 1,
475 'binary': False,
431 'binary': False,
432 'old_mode': '100755',
433 'new_mode': '100644',
434 'copied': ('file4', 'file5'),
476 'ops': {COPIED_FILENODE: 'file copied from file4 to file5',
435 'ops': {COPIED_FILENODE: 'file copied from file4 to file5',
477 CHMOD_FILENODE: 'modified file chmod 100755 => 100644',
436 CHMOD_FILENODE: 'modified file chmod 100755 => 100644',
478 MOD_FILENODE: 'modified file'}})]),
437 MOD_FILENODE: 'modified file'}})]),
@@ -484,6 +443,9 b' DIFF_FIXTURES = ['
484 {'added': 0,
443 {'added': 0,
485 'deleted': 0,
444 'deleted': 0,
486 'binary': True,
445 'binary': True,
446 'old_mode': '',
447 'new_mode': '',
448 'renamed': ('file_with_ spaces.txt', 'file_with_ two spaces.txt'),
487 'ops': {
449 'ops': {
488 RENAMED_FILENODE: (
450 RENAMED_FILENODE: (
489 'file renamed from file_with_ spaces.txt to file_with_ '
451 'file renamed from file_with_ spaces.txt to file_with_ '
@@ -495,10 +457,12 b' DIFF_FIXTURES = ['
495 {'added': 0,
457 {'added': 0,
496 'deleted': 0,
458 'deleted': 0,
497 'binary': True,
459 'binary': True,
460 'old_mode': '',
461 'new_mode': '',
462 'renamed': ('file_ with update.txt', 'file_changed _.txt'),
498 'ops': {
463 'ops': {
499 RENAMED_FILENODE: (
464 RENAMED_FILENODE: (
500 'file renamed from file_ with update.txt to file_changed'
465 'file renamed from file_ with update.txt to file_changed _.txt')}
501 ' _.txt')}
502 }), ]),
466 }), ]),
503 ('hg',
467 ('hg',
504 'hg_diff_copy_file_with_spaces.diff',
468 'hg_diff_copy_file_with_spaces.diff',
@@ -506,6 +470,9 b' DIFF_FIXTURES = ['
506 {'added': 0,
470 {'added': 0,
507 'deleted': 0,
471 'deleted': 0,
508 'binary': True,
472 'binary': True,
473 'old_mode': '',
474 'new_mode': '',
475 'copied': ('file_changed_without_spaces.txt', 'file_copied_ with spaces.txt'),
509 'ops': {
476 'ops': {
510 COPIED_FILENODE: (
477 COPIED_FILENODE: (
511 'file copied from file_changed_without_spaces.txt to'
478 'file copied from file_changed_without_spaces.txt to'
@@ -520,6 +487,8 b' DIFF_FIXTURES = ['
520 {'added': 0,
487 {'added': 0,
521 'deleted': 0,
488 'deleted': 0,
522 'binary': True,
489 'binary': True,
490 'old_mode': '',
491 'new_mode': '100644',
523 'ops': {NEW_FILENODE: 'new file 100644',
492 'ops': {NEW_FILENODE: 'new file 100644',
524 BIN_FILENODE: 'binary diff hidden'}
493 BIN_FILENODE: 'binary diff hidden'}
525 }),
494 }),
@@ -530,6 +499,8 b' DIFF_FIXTURES = ['
530 {'added': 0,
499 {'added': 0,
531 'deleted': 0,
500 'deleted': 0,
532 'binary': True,
501 'binary': True,
502 'old_mode': '',
503 'new_mode': '100644',
533 'ops': {NEW_FILENODE: 'new file 100644', }
504 'ops': {NEW_FILENODE: 'new file 100644', }
534 }),
505 }),
535 ]),
506 ]),
@@ -540,6 +511,8 b' DIFF_FIXTURES = ['
540 {'added': 0,
511 {'added': 0,
541 'deleted': 0,
512 'deleted': 0,
542 'binary': False,
513 'binary': False,
514 'old_mode': '',
515 'new_mode': '10644',
543 'ops': {NEW_FILENODE: 'new file 10644',
516 'ops': {NEW_FILENODE: 'new file 10644',
544 #TODO(Marcink): depends on binary detection on svn patches
517 #TODO(Marcink): depends on binary detection on svn patches
545 # BIN_FILENODE: 'binary diff hidden'
518 # BIN_FILENODE: 'binary diff hidden'
@@ -553,6 +526,8 b' DIFF_FIXTURES = ['
553 {'added': 0,
526 {'added': 0,
554 'deleted': 0,
527 'deleted': 0,
555 'binary': False,
528 'binary': False,
529 'old_mode': '',
530 'new_mode': '',
556 'ops': {MOD_FILENODE: 'modified file',
531 'ops': {MOD_FILENODE: 'modified file',
557 #TODO(Marcink): depends on binary detection on svn patches
532 #TODO(Marcink): depends on binary detection on svn patches
558 # BIN_FILENODE: 'binary diff hidden'
533 # BIN_FILENODE: 'binary diff hidden'
@@ -562,48 +537,64 b' DIFF_FIXTURES = ['
562 {'added': 89,
537 {'added': 89,
563 'deleted': 34,
538 'deleted': 34,
564 'binary': False,
539 'binary': False,
540 'old_mode': '',
541 'new_mode': '',
565 'ops': {MOD_FILENODE: 'modified file'}
542 'ops': {MOD_FILENODE: 'modified file'}
566 }),
543 }),
567 ('trunk/doc/source/en/tsvn_ch04.xml', 'M',
544 ('trunk/doc/source/en/tsvn_ch04.xml', 'M',
568 {'added': 66,
545 {'added': 66,
569 'deleted': 21,
546 'deleted': 21,
570 'binary': False,
547 'binary': False,
548 'old_mode': '',
549 'new_mode': '',
571 'ops': {MOD_FILENODE: 'modified file'}
550 'ops': {MOD_FILENODE: 'modified file'}
572 }),
551 }),
573 ('trunk/src/Changelog.txt', 'M',
552 ('trunk/src/Changelog.txt', 'M',
574 {'added': 2,
553 {'added': 2,
575 'deleted': 0,
554 'deleted': 0,
576 'binary': False,
555 'binary': False,
556 'old_mode': '',
557 'new_mode': '',
577 'ops': {MOD_FILENODE: 'modified file'}
558 'ops': {MOD_FILENODE: 'modified file'}
578 }),
559 }),
579 ('trunk/src/Resources/TortoiseProcENG.rc', 'M',
560 ('trunk/src/Resources/TortoiseProcENG.rc', 'M',
580 {'added': 19,
561 {'added': 19,
581 'deleted': 13,
562 'deleted': 13,
582 'binary': False,
563 'binary': False,
564 'old_mode': '',
565 'new_mode': '',
583 'ops': {MOD_FILENODE: 'modified file'}
566 'ops': {MOD_FILENODE: 'modified file'}
584 }),
567 }),
585 ('trunk/src/TortoiseProc/SetOverlayPage.cpp', 'M',
568 ('trunk/src/TortoiseProc/SetOverlayPage.cpp', 'M',
586 {'added': 16,
569 {'added': 16,
587 'deleted': 1,
570 'deleted': 1,
588 'binary': False,
571 'binary': False,
572 'old_mode': '',
573 'new_mode': '',
589 'ops': {MOD_FILENODE: 'modified file'}
574 'ops': {MOD_FILENODE: 'modified file'}
590 }),
575 }),
591 ('trunk/src/TortoiseProc/SetOverlayPage.h', 'M',
576 ('trunk/src/TortoiseProc/SetOverlayPage.h', 'M',
592 {'added': 3,
577 {'added': 3,
593 'deleted': 0,
578 'deleted': 0,
594 'binary': False,
579 'binary': False,
580 'old_mode': '',
581 'new_mode': '',
595 'ops': {MOD_FILENODE: 'modified file'}
582 'ops': {MOD_FILENODE: 'modified file'}
596 }),
583 }),
597 ('trunk/src/TortoiseProc/resource.h', 'M',
584 ('trunk/src/TortoiseProc/resource.h', 'M',
598 {'added': 2,
585 {'added': 2,
599 'deleted': 0,
586 'deleted': 0,
600 'binary': False,
587 'binary': False,
588 'old_mode': '',
589 'new_mode': '',
601 'ops': {MOD_FILENODE: 'modified file'}
590 'ops': {MOD_FILENODE: 'modified file'}
602 }),
591 }),
603 ('trunk/src/TortoiseShell/ShellCache.h', 'M',
592 ('trunk/src/TortoiseShell/ShellCache.h', 'M',
604 {'added': 50,
593 {'added': 50,
605 'deleted': 1,
594 'deleted': 1,
606 'binary': False,
595 'binary': False,
596 'old_mode': '',
597 'new_mode': '',
607 'ops': {MOD_FILENODE: 'modified file'}
598 'ops': {MOD_FILENODE: 'modified file'}
608 }),
599 }),
609 ]),
600 ]),
@@ -621,6 +612,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
621 'added': 1,
612 'added': 1,
622 'deleted': 0,
613 'deleted': 0,
623 'binary': False,
614 'binary': False,
615 'old_mode': '',
616 'new_mode': '100644',
624 'ops': {NEW_FILENODE: 'new file 100644', }
617 'ops': {NEW_FILENODE: 'new file 100644', }
625 },
618 },
626 '@@ -0,0 +1 @@\n+test_content b\n' # diff
619 '@@ -0,0 +1 @@\n+test_content b\n' # diff
@@ -637,6 +630,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
637 'added': 1,
630 'added': 1,
638 'deleted': 0,
631 'deleted': 0,
639 'binary': False,
632 'binary': False,
633 'old_mode': '',
634 'new_mode': '100644',
640 'ops': {NEW_FILENODE: 'new file 100644', }
635 'ops': {NEW_FILENODE: 'new file 100644', }
641 },
636 },
642 '@@ -0,0 +1 @@\n+test_content b\n' # diff
637 '@@ -0,0 +1 @@\n+test_content b\n' # diff
@@ -648,6 +643,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
648 'added': 1,
643 'added': 1,
649 'deleted': 0,
644 'deleted': 0,
650 'binary': False,
645 'binary': False,
646 'old_mode': '',
647 'new_mode': '100644',
651 'ops': {NEW_FILENODE: 'new file 100644', }
648 'ops': {NEW_FILENODE: 'new file 100644', }
652 },
649 },
653 '@@ -0,0 +1 @@\n+test_content c\n' # diff
650 '@@ -0,0 +1 @@\n+test_content c\n' # diff
@@ -664,6 +661,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
664 'added': 1,
661 'added': 1,
665 'deleted': 0,
662 'deleted': 0,
666 'binary': False,
663 'binary': False,
664 'old_mode': '',
665 'new_mode': '100644',
667 'ops': {NEW_FILENODE: 'new file 100644', }
666 'ops': {NEW_FILENODE: 'new file 100644', }
668 },
667 },
669 '@@ -0,0 +1 @@\n+test_content b\n\n' # diff
668 '@@ -0,0 +1 @@\n+test_content b\n\n' # diff
@@ -675,6 +674,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
675 'added': 1,
674 'added': 1,
676 'deleted': 0,
675 'deleted': 0,
677 'binary': False,
676 'binary': False,
677 'old_mode': '',
678 'new_mode': '100644',
678 'ops': {NEW_FILENODE: 'new file 100644', }
679 'ops': {NEW_FILENODE: 'new file 100644', }
679 },
680 },
680 '@@ -0,0 +1 @@\n+test_content c\n' # diff
681 '@@ -0,0 +1 @@\n+test_content c\n' # diff
@@ -691,6 +692,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
691 'added': 1,
692 'added': 1,
692 'deleted': 0,
693 'deleted': 0,
693 'binary': False,
694 'binary': False,
695 'old_mode': '',
696 'new_mode': '100644',
694 'ops': {NEW_FILENODE: 'new file 100644', }
697 'ops': {NEW_FILENODE: 'new file 100644', }
695 },
698 },
696 '@@ -0,0 +1,1 @@\n+file\n' # diff
699 '@@ -0,0 +1,1 @@\n+file\n' # diff
@@ -702,6 +705,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
702 'added': 1,
705 'added': 1,
703 'deleted': 0,
706 'deleted': 0,
704 'binary': False,
707 'binary': False,
708 'old_mode': '',
709 'new_mode': '100644',
705 'ops': {NEW_FILENODE: 'new file 100644', }
710 'ops': {NEW_FILENODE: 'new file 100644', }
706 },
711 },
707 '@@ -0,0 +1,1 @@\n+another line\n' # diff
712 '@@ -0,0 +1,1 @@\n+another line\n' # diff
@@ -713,6 +718,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
713 'added': 1,
718 'added': 1,
714 'deleted': 0,
719 'deleted': 0,
715 'binary': False,
720 'binary': False,
721 'old_mode': '',
722 'new_mode': '100644',
716 'ops': {NEW_FILENODE: 'new file 100644', }
723 'ops': {NEW_FILENODE: 'new file 100644', }
717 },
724 },
718 '@@ -0,0 +1,1 @@\n+newline\n' # diff
725 '@@ -0,0 +1,1 @@\n+newline\n' # diff
@@ -724,6 +731,8 b' DIFF_FIXTURES_WITH_CONTENT = ['
724 'added': 1,
731 'added': 1,
725 'deleted': 0,
732 'deleted': 0,
726 'binary': False,
733 'binary': False,
734 'old_mode': '',
735 'new_mode': '100644',
727 'ops': {NEW_FILENODE: 'new file 100644', }
736 'ops': {NEW_FILENODE: 'new file 100644', }
728 },
737 },
729 '@@ -0,0 +1,1 @@\n+fil4\n\\ No newline at end of file' # diff
738 '@@ -0,0 +1,1 @@\n+fil4\n\\ No newline at end of file' # diff
@@ -741,28 +750,46 b' diff_class = {'
741 }
750 }
742
751
743
752
744 @pytest.fixture(params=DIFF_FIXTURES)
753 @pytest.mark.parametrize('vcs_type, diff_file, expected_data', DIFF_FIXTURES)
745 def diff_fixture(request):
754 def test_diff_lib(vcs_type, diff_file, expected_data):
746 vcs, diff_fixture, expected = request.param
755 diff_txt = fixture.load_resource(diff_file)
747 diff_txt = fixture.load_resource(diff_fixture)
756 diff = diff_class[vcs_type](diff_txt)
748 diff = diff_class[vcs](diff_txt)
749 return diff, expected
750
757
751
758 diff_proc = DiffProcessor(diff, diff_format='newdiff')
752 def test_diff_lib(diff_fixture):
753 diff, expected_data = diff_fixture
754 diff_proc = DiffProcessor(diff)
755 diff_proc_d = diff_proc.prepare()
759 diff_proc_d = diff_proc.prepare()
756 data = [(x['filename'], x['operation'], x['stats']) for x in diff_proc_d]
760 data = [(x['filename'], x['operation'], x['stats'])
761 for x in diff_proc_d]
757 assert expected_data == data
762 assert expected_data == data
758
763
759
764
760 @pytest.fixture(params=DIFF_FIXTURES_WITH_CONTENT)
765 @pytest.mark.parametrize('vcs_type, diff_file, expected_data', DIFF_FIXTURES_WITH_CONTENT)
761 def diff_fixture_w_content(request):
766 def test_diff_lib_newlines(vcs_type, diff_file, expected_data):
762 vcs, diff_fixture, expected = request.param
767 diff_txt = fixture.load_resource(diff_file)
763 diff_txt = fixture.load_resource(diff_fixture)
768 diff = diff_class[vcs_type](diff_txt)
764 diff = diff_class[vcs](diff_txt)
769
765 return diff, expected
770 diff_proc = DiffProcessor(diff, diff_format='newdiff')
771 diff_proc_d = diff_proc.prepare()
772 data = [(x['filename'], x['operation'], x['stats'], x['raw_diff'])
773 for x in diff_proc_d]
774 assert expected_data == data
775
776
777 @pytest.mark.parametrize('input_str', [
778 b'',
779 b'\n',
780 b'\n\n',
781 b'First\n+second',
782 b'First\n+second\n',
783
784 b'\n\n\n Multi \n\n\n',
785 b'\n\n\n Multi beginning',
786 b'Multi end \n\n\n',
787 b'Multi end',
788 b'@@ -0,0 +1 @@\n+test_content \n\n b\n'
789 ], ids=no_newline_id_generator)
790 def test_splitlines(input_str):
791 result = DiffProcessor.diff_splitter(input_str)
792 assert list(result) == input_str.splitlines(True)
766
793
767
794
768 def test_diff_over_limit(request):
795 def test_diff_over_limit(request):
@@ -772,8 +799,8 b' def test_diff_over_limit(request):'
772
799
773 raw_diff = fixture.load_resource('large_diff.diff')
800 raw_diff = fixture.load_resource('large_diff.diff')
774 vcs_diff = GitDiff(raw_diff)
801 vcs_diff = GitDiff(raw_diff)
775 diff_processor = DiffProcessor(
802 diff_processor = DiffProcessor(vcs_diff, diff_format='newdiff',
776 vcs_diff, format='newdiff', diff_limit=diff_limit, file_limit=file_limit,
803 diff_limit=diff_limit, file_limit=file_limit,
777 show_full_diff=False)
804 show_full_diff=False)
778
805
779 _parsed = diff_processor.prepare()
806 _parsed = diff_processor.prepare()
@@ -796,30 +823,3 b' def test_diff_over_limit(request):'
796
823
797 assert diffset.files[1].patch['filename'] == 'README.md'
824 assert diffset.files[1].patch['filename'] == 'README.md'
798 assert diffset.files[1].limited_diff is False
825 assert diffset.files[1].limited_diff is False
799
800
801 def test_diff_lib_newlines(diff_fixture_w_content):
802 diff, expected_data = diff_fixture_w_content
803 diff_proc = DiffProcessor(diff)
804 diff_proc_d = diff_proc.prepare()
805 data = [(x['filename'], x['operation'], x['stats'], x['raw_diff'])
806 for x in diff_proc_d]
807 assert expected_data == data
808
809
810 @pytest.mark.parametrize('input_str', [
811 '',
812 '\n',
813 '\n\n',
814 'First\n+second',
815 'First\n+second\n',
816
817 '\n\n\n Multi \n\n\n',
818 '\n\n\n Multi beginning',
819 'Multi end \n\n\n',
820 'Multi end',
821 '@@ -0,0 +1 @@\n+test_content \n\n b\n'
822 ], ids=no_newline_id_generator)
823 def test_splitlines(input_str):
824 result = DiffProcessor.diff_splitter(input_str)
825 assert list(result) == input_str.splitlines(True)
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -27,6 +26,7 b' import textwrap'
27 import pytest
26 import pytest
28
27
29 from rhodecode.lib import diffs
28 from rhodecode.lib import diffs
29 from rhodecode.lib.str_utils import safe_bytes
30 from rhodecode.lib.vcs.backends.git.diff import GitDiff
30 from rhodecode.lib.vcs.backends.git.diff import GitDiff
31
31
32
32
@@ -40,13 +40,13 b' def test_context_of_an_old_line_number(d'
40 context = diff_processor.get_context_of_line(
40 context = diff_processor.get_context_of_line(
41 path='file.txt', diff_line=diffs.DiffLineNumber(old=7, new=None))
41 path='file.txt', diff_line=diffs.DiffLineNumber(old=7, new=None))
42 expected_context = [
42 expected_context = [
43 ('unmod', 'line04\n'),
43 ('unmod', b'line04\n'),
44 ('unmod', 'line05\n'),
44 ('unmod', b'line05\n'),
45 ('unmod', 'line06\n'),
45 ('unmod', b'line06\n'),
46 ('unmod', 'line07\n'),
46 ('unmod', b'line07\n'),
47 ('add', 'line07a Add after line07\n'),
47 ('add', b'line07a Add after line07\n'),
48 ('unmod', 'line08\n'),
48 ('unmod', b'line08\n'),
49 ('unmod', 'line09\n'),
49 ('unmod', b'line09\n'),
50 ]
50 ]
51 assert context == expected_context
51 assert context == expected_context
52
52
@@ -55,13 +55,13 b' def test_context_of_a_new_line_number(di'
55 context = diff_processor.get_context_of_line(
55 context = diff_processor.get_context_of_line(
56 path='file.txt', diff_line=diffs.DiffLineNumber(old=None, new=8))
56 path='file.txt', diff_line=diffs.DiffLineNumber(old=None, new=8))
57 expected_context = [
57 expected_context = [
58 ('unmod', 'line05\n'),
58 ('unmod', b'line05\n'),
59 ('unmod', 'line06\n'),
59 ('unmod', b'line06\n'),
60 ('unmod', 'line07\n'),
60 ('unmod', b'line07\n'),
61 ('add', 'line07a Add after line07\n'),
61 ('add', b'line07a Add after line07\n'),
62 ('unmod', 'line08\n'),
62 ('unmod', b'line08\n'),
63 ('unmod', 'line09\n'),
63 ('unmod', b'line09\n'),
64 ('unmod', 'line10\n'),
64 ('unmod', b'line10\n'),
65 ]
65 ]
66 assert context == expected_context
66 assert context == expected_context
67
67
@@ -72,11 +72,11 b' def test_context_of_an_invisible_line_be'
72 context = diff_processor.get_context_of_line(
72 context = diff_processor.get_context_of_line(
73 path='file.txt', diff_line=diffs.DiffLineNumber(old=None, new=3))
73 path='file.txt', diff_line=diffs.DiffLineNumber(old=None, new=3))
74 expected_context = [
74 expected_context = [
75 ('unmod', 'line02\n'),
75 ('unmod', b'line02\n'),
76 ('unmod', 'line03\n'),
76 ('unmod', b'line03\n'),
77 ('unmod', 'line04\n'),
77 ('unmod', b'line04\n'),
78 ('unmod', 'line05\n'),
78 ('unmod', b'line05\n'),
79 ('unmod', 'line06\n'),
79 ('unmod', b'line06\n'),
80 ]
80 ]
81 assert context == expected_context
81 assert context == expected_context
82
82
@@ -87,11 +87,11 b' def test_context_of_an_invisible_line_en'
87 context = diff_processor.get_context_of_line(
87 context = diff_processor.get_context_of_line(
88 path='file.txt', diff_line=diffs.DiffLineNumber(old=12, new=None))
88 path='file.txt', diff_line=diffs.DiffLineNumber(old=12, new=None))
89 expected_context = [
89 expected_context = [
90 ('unmod', 'line09\n'),
90 ('unmod', b'line09\n'),
91 ('unmod', 'line10\n'),
91 ('unmod', b'line10\n'),
92 ('unmod', 'line11\n'),
92 ('unmod', b'line11\n'),
93 ('unmod', 'line12\n'),
93 ('unmod', b'line12\n'),
94 ('unmod', 'line13\n'),
94 ('unmod', b'line13\n'),
95 ]
95 ]
96 assert context == expected_context
96 assert context == expected_context
97
97
@@ -101,11 +101,11 b' def test_context_of_an_incomplete_hunk_i'
101 context = diff_processor.get_context_of_line(
101 context = diff_processor.get_context_of_line(
102 path='file.txt', diff_line=diffs.DiffLineNumber(old=None, new=2))
102 path='file.txt', diff_line=diffs.DiffLineNumber(old=None, new=2))
103 expected_context = [
103 expected_context = [
104 ('unmod', 'line01\n'),
104 ('unmod', b'line01\n'),
105 ('add', 'line01a Add line after line01\n'),
105 ('add', b'line01a Add line after line01\n'),
106 ('unmod', 'line02\n'),
106 ('unmod', b'line02\n'),
107 ('unmod', 'line03\n'),
107 ('unmod', b'line03\n'),
108 ('unmod', 'line04\n'),
108 ('unmod', b'line04\n'),
109 ]
109 ]
110 assert context == expected_context
110 assert context == expected_context
111
111
@@ -115,11 +115,11 b' def test_context_of_an_incomplete_hunk_i'
115 context = diff_processor.get_context_of_line(
115 context = diff_processor.get_context_of_line(
116 path='file.txt', diff_line=diffs.DiffLineNumber(old=None, new=80))
116 path='file.txt', diff_line=diffs.DiffLineNumber(old=None, new=80))
117 expected_context = [
117 expected_context = [
118 ('unmod', 'line36\n'),
118 ('unmod', b'line36\n'),
119 ('unmod', 'line37\n'),
119 ('unmod', b'line37\n'),
120 ('unmod', 'line38\n'),
120 ('unmod', b'line38\n'),
121 ('add', 'line38a Add line after line38\n'),
121 ('add', b'line38a Add line after line38\n'),
122 ('unmod', 'line39\n'),
122 ('unmod', b'line39\n'),
123 ]
123 ]
124 assert context == expected_context
124 assert context == expected_context
125
125
@@ -131,7 +131,7 b' def test_context_of_an_incomplete_hunk_i'
131 def test_appends_newline_for_each_context_line(diff_processor):
131 def test_appends_newline_for_each_context_line(diff_processor):
132 context = diff_processor.get_context_of_line(
132 context = diff_processor.get_context_of_line(
133 path='file_b', diff_line=diffs.DiffLineNumber(old=None, new=1))
133 path='file_b', diff_line=diffs.DiffLineNumber(old=None, new=1))
134 assert context == [('add', 'test_content\n')]
134 assert context == [('add', b'test_content\n')]
135
135
136
136
137 def test_context_of_a_missing_line_raises(diff_processor):
137 def test_context_of_a_missing_line_raises(diff_processor):
@@ -151,13 +151,13 b' def test_context_of_a_missing_file_raise'
151
151
152 def test_find_context_with_full_context(diff_processor):
152 def test_find_context_with_full_context(diff_processor):
153 context_of_line_7 = [
153 context_of_line_7 = [
154 ('unmod', 'line05\n'),
154 ('unmod', b'line05\n'),
155 ('unmod', 'line06\n'),
155 ('unmod', b'line06\n'),
156 ('unmod', 'line07\n'),
156 ('unmod', b'line07\n'),
157 ('add', 'line07a Add after line07\n'),
157 ('add', b'line07a Add after line07\n'),
158 ('unmod', 'line08\n'),
158 ('unmod', b'line08\n'),
159 ('unmod', 'line09\n'),
159 ('unmod', b'line09\n'),
160 ('unmod', 'line10\n'),
160 ('unmod', b'line10\n'),
161 ]
161 ]
162 found_line = diff_processor.find_context(
162 found_line = diff_processor.find_context(
163 'file.txt', context_of_line_7, offset=3)
163 'file.txt', context_of_line_7, offset=3)
@@ -167,13 +167,13 b' def test_find_context_with_full_context('
167 @pytest.mark.parametrize('diff_fixture', ['change-duplicated.diff'])
167 @pytest.mark.parametrize('diff_fixture', ['change-duplicated.diff'])
168 def test_find_context_multiple_times(diff_processor):
168 def test_find_context_multiple_times(diff_processor):
169 context = [
169 context = [
170 ('unmod', 'line04\n'),
170 ('unmod', b'line04\n'),
171 ('unmod', 'line05\n'),
171 ('unmod', b'line05\n'),
172 ('unmod', 'line06\n'),
172 ('unmod', b'line06\n'),
173 ('add', 'line06a add line\n'),
173 ('add', b'line06a add line\n'),
174 ('unmod', 'line07\n'),
174 ('unmod', b'line07\n'),
175 ('unmod', 'line08\n'),
175 ('unmod', b'line08\n'),
176 ('unmod', 'line09\n'),
176 ('unmod', b'line09\n'),
177 ]
177 ]
178 found_line = diff_processor.find_context('file.txt', context, offset=3)
178 found_line = diff_processor.find_context('file.txt', context, offset=3)
179 assert found_line == [
179 assert found_line == [
@@ -185,13 +185,13 b' def test_find_context_multiple_times(dif'
185 @pytest.mark.parametrize('offset', [20, -20, -1, 7])
185 @pytest.mark.parametrize('offset', [20, -20, -1, 7])
186 def test_find_context_offset_param_raises(diff_processor, offset):
186 def test_find_context_offset_param_raises(diff_processor, offset):
187 context_of_line_7 = [
187 context_of_line_7 = [
188 ('unmod', 'line04\n'),
188 ('unmod', b'line04\n'),
189 ('unmod', 'line05\n'),
189 ('unmod', b'line05\n'),
190 ('unmod', 'line06\n'),
190 ('unmod', b'line06\n'),
191 ('unmod', 'line07\n'),
191 ('unmod', b'line07\n'),
192 ('add', 'line07a Add after line07\n'),
192 ('add', b'line07a Add after line07\n'),
193 ('unmod', 'line08\n'),
193 ('unmod', b'line08\n'),
194 ('unmod', 'line09\n'),
194 ('unmod', b'line09\n'),
195 ]
195 ]
196 with pytest.raises(ValueError):
196 with pytest.raises(ValueError):
197 diff_processor.find_context(
197 diff_processor.find_context(
@@ -200,10 +200,10 b' def test_find_context_offset_param_raise'
200
200
201 def test_find_context_beginning_of_chunk(diff_processor):
201 def test_find_context_beginning_of_chunk(diff_processor):
202 context_of_first_line = [
202 context_of_first_line = [
203 ('unmod', 'line02\n'),
203 ('unmod', b'line02\n'),
204 ('unmod', 'line03\n'),
204 ('unmod', b'line03\n'),
205 ('unmod', 'line04\n'),
205 ('unmod', b'line04\n'),
206 ('unmod', 'line05\n'),
206 ('unmod', b'line05\n'),
207 ]
207 ]
208 found_line = diff_processor.find_context(
208 found_line = diff_processor.find_context(
209 'file.txt', context_of_first_line, offset=0)
209 'file.txt', context_of_first_line, offset=0)
@@ -213,13 +213,13 b' def test_find_context_beginning_of_chunk'
213 @pytest.mark.parametrize('diff_fixture', ['change-in-beginning.diff'])
213 @pytest.mark.parametrize('diff_fixture', ['change-in-beginning.diff'])
214 def test_find_context_beginning_of_file(diff_processor):
214 def test_find_context_beginning_of_file(diff_processor):
215 context_of_first_line = [
215 context_of_first_line = [
216 ('add', 'line01a Add line after line01\n'),
216 ('add', b'line01a Add line after line01\n'),
217 ('unmod', 'line02\n'),
217 ('unmod', b'line02\n'),
218 ('unmod', 'line03\n'),
218 ('unmod', b'line03\n'),
219 ('unmod', 'line04\n'),
219 ('unmod', b'line04\n'),
220 ('unmod', 'line05\n'),
220 ('unmod', b'line05\n'),
221 ('unmod', 'line06\n'),
221 ('unmod', b'line06\n'),
222 ('unmod', 'line07\n'),
222 ('unmod', b'line07\n'),
223 ]
223 ]
224 found_line = diff_processor.find_context(
224 found_line = diff_processor.find_context(
225 'file.txt', context_of_first_line, offset=3)
225 'file.txt', context_of_first_line, offset=3)
@@ -228,10 +228,10 b' def test_find_context_beginning_of_file('
228
228
229 def test_find_context_end_of_chunk(diff_processor):
229 def test_find_context_end_of_chunk(diff_processor):
230 context_of_last_line = [
230 context_of_last_line = [
231 ('unmod', 'line10\n'),
231 ('unmod', b'line10\n'),
232 ('unmod', 'line11\n'),
232 ('unmod', b'line11\n'),
233 ('unmod', 'line12\n'),
233 ('unmod', b'line12\n'),
234 ('unmod', 'line13\n'),
234 ('unmod', b'line13\n'),
235 ]
235 ]
236 found_line = diff_processor.find_context(
236 found_line = diff_processor.find_context(
237 'file.txt', context_of_last_line, offset=3)
237 'file.txt', context_of_last_line, offset=3)
@@ -242,7 +242,7 b' def test_find_context_end_of_chunk(diff_'
242 def diff_processor(request, diff_fixture):
242 def diff_processor(request, diff_fixture):
243 raw_diff = diffs_store[diff_fixture]
243 raw_diff = diffs_store[diff_fixture]
244 diff = GitDiff(raw_diff)
244 diff = GitDiff(raw_diff)
245 processor = diffs.DiffProcessor(diff)
245 processor = diffs.DiffProcessor(diff, diff_format='newdiff')
246 processor.prepare()
246 processor.prepare()
247 return processor
247 return processor
248
248
@@ -252,7 +252,7 b' def diff_fixture():'
252 return 'default.diff'
252 return 'default.diff'
253
253
254
254
255 diff_default = textwrap.dedent("""
255 diff_default: bytes = safe_bytes(textwrap.dedent("""
256 diff --git a/file.txt b/file.txt
256 diff --git a/file.txt b/file.txt
257 index 76e4f2e..6f8738f 100644
257 index 76e4f2e..6f8738f 100644
258 --- a/file.txt
258 --- a/file.txt
@@ -271,10 +271,10 b' diff_default = textwrap.dedent("""'
271 line11
271 line11
272 line12
272 line12
273 line13
273 line13
274 """)
274 """))
275
275
276
276
277 diff_beginning = textwrap.dedent("""
277 diff_beginning: bytes = safe_bytes(textwrap.dedent("""
278 diff --git a/file.txt b/file.txt
278 diff --git a/file.txt b/file.txt
279 index 76e4f2e..47d39f4 100644
279 index 76e4f2e..47d39f4 100644
280 --- a/file.txt
280 --- a/file.txt
@@ -288,10 +288,10 b' diff_beginning = textwrap.dedent("""'
288 line05
288 line05
289 line06
289 line06
290 line07
290 line07
291 """)
291 """))
292
292
293
293
294 diff_end = textwrap.dedent("""
294 diff_end: bytes = safe_bytes(textwrap.dedent("""
295 diff --git a/file.txt b/file.txt
295 diff --git a/file.txt b/file.txt
296 index 76e4f2e..b1304db 100644
296 index 76e4f2e..b1304db 100644
297 --- a/file.txt
297 --- a/file.txt
@@ -305,10 +305,10 b' diff_end = textwrap.dedent("""'
305 line38
305 line38
306 +line38a Add line after line38
306 +line38a Add line after line38
307 line39
307 line39
308 """)
308 """))
309
309
310
310
311 diff_duplicated_change = textwrap.dedent("""
311 diff_duplicated_change: bytes = safe_bytes(textwrap.dedent("""
312 diff --git a/file.txt b/file.txt
312 diff --git a/file.txt b/file.txt
313 index 76e4f2e..55c2781 100644
313 index 76e4f2e..55c2781 100644
314 --- a/file.txt
314 --- a/file.txt
@@ -341,10 +341,10 b' diff_duplicated_change = textwrap.dedent'
341 line10
341 line10
342 line11
342 line11
343 line12
343 line12
344 """)
344 """))
345
345
346
346
347 diff_single_line = textwrap.dedent("""
347 diff_single_line: bytes = safe_bytes(textwrap.dedent("""
348 diff --git a/file_b b/file_b
348 diff --git a/file_b b/file_b
349 new file mode 100644
349 new file mode 100644
350 index 00000000..915e94ff
350 index 00000000..915e94ff
@@ -352,10 +352,10 b' diff_single_line = textwrap.dedent("""'
352 +++ b/file_b
352 +++ b/file_b
353 @@ -0,0 +1 @@
353 @@ -0,0 +1 @@
354 +test_content
354 +test_content
355 """)
355 """))
356
356
357
357
358 diff_single_line_two_files = textwrap.dedent("""
358 diff_single_line_two_files: bytes = safe_bytes(textwrap.dedent("""
359 diff --git a/file_b b/file_b
359 diff --git a/file_b b/file_b
360 new file mode 100644
360 new file mode 100644
361 index 00000000..915e94ff
361 index 00000000..915e94ff
@@ -370,7 +370,7 b' diff_single_line_two_files = textwrap.de'
370 +++ b/file_c
370 +++ b/file_c
371 @@ -0,0 +1 @@
371 @@ -0,0 +1 @@
372 +test_content
372 +test_content
373 """)
373 """))
374
374
375
375
376 diffs_store = {
376 diffs_store = {
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -83,23 +82,35 b' def hook_extras(user_regular, repo_stub)'
83 return extras
82 return extras
84
83
85
84
85 class ExtensionMock(mock.Mock):
86
87 def __repr__(self):
88 return f'ExtensionMock({self._mock_name})'
89
90 @property
91 def output(self):
92 return 'MOCK'
93
94 @property
95 def status(self):
96 return 0
97
98
86 @pytest.mark.parametrize('func, extension, event', [
99 @pytest.mark.parametrize('func, extension, event', [
87 (hooks_base.pre_push, 'pre_push_extension', 'RepoPrePushEvent'),
100 (hooks_base.pre_push, 'pre_push_extension', 'RepoPrePushEvent'),
88 (hooks_base.post_push, 'post_pull_extension', 'RepoPushEvent'),
101 (hooks_base.post_push, 'post_push_extension', 'RepoPushEvent'),
89 (hooks_base.pre_pull, 'pre_pull_extension', 'RepoPrePullEvent'),
102 (hooks_base.pre_pull, 'pre_pull_extension', 'RepoPrePullEvent'),
90 (hooks_base.post_pull, 'post_push_extension', 'RepoPullEvent'),
103 (hooks_base.post_pull, 'post_pull_extension', 'RepoPullEvent'),
91 ])
104 ])
92 def test_hooks_propagate(func, extension, event, hook_extras):
105 def test_hooks_propagate(func, extension, event, hook_extras):
93 """
106 """
94 Tests that our hook code propagates to rhodecode extensions and triggers
107 Tests that our hook code propagates to rhodecode extensions and triggers
95 the appropriate event.
108 the appropriate event.
96 """
109 """
97 class ExtensionMock(mock.Mock):
98 @property
99 def output(self):
100 return 'MOCK'
101
110
102 extension_mock = ExtensionMock()
111 extension_mock = ExtensionMock()
112 extension_mock._mock_name = extension
113
103 events_mock = mock.Mock()
114 events_mock = mock.Mock()
104 patches = {
115 patches = {
105 'Repository': mock.Mock(),
116 'Repository': mock.Mock(),
@@ -115,15 +126,15 b' def test_hooks_propagate(func, extension'
115 func(hook_extras)
126 func(hook_extras)
116
127
117 # Assert that extensions are called and event was fired.
128 # Assert that extensions are called and event was fired.
118 extension_mock.called_once()
129 extension_mock.assert_called_once()
119 assert_called_with_mock(events_mock.trigger, event)
130 assert_called_with_mock(events_mock.trigger, event)
120
131
121
132
122 @pytest.mark.parametrize('func, extension, event', [
133 @pytest.mark.parametrize('func, extension, event', [
123 (hooks_base.pre_push, 'pre_push_extension', 'RepoPrePushEvent'),
134 (hooks_base.pre_push, 'pre_push_extension', 'RepoPrePushEvent'),
124 (hooks_base.post_push, 'post_pull_extension', 'RepoPushEvent'),
135 (hooks_base.post_push, 'post_push_extension', 'RepoPushEvent'),
125 (hooks_base.pre_pull, 'pre_pull_extension', 'RepoPrePullEvent'),
136 (hooks_base.pre_pull, 'pre_pull_extension', 'RepoPrePullEvent'),
126 (hooks_base.post_pull, 'post_push_extension', 'RepoPullEvent'),
137 (hooks_base.post_pull, 'post_pull_extension', 'RepoPullEvent'),
127 ])
138 ])
128 def test_hooks_propagates_not_on_shadow(func, extension, event, hook_extras):
139 def test_hooks_propagates_not_on_shadow(func, extension, event, hook_extras):
129 """
140 """
@@ -131,7 +142,10 b' def test_hooks_propagates_not_on_shadow('
131 internal hooks code but not external ones like rhodecode extensions or
142 internal hooks code but not external ones like rhodecode extensions or
132 trigger an event.
143 trigger an event.
133 """
144 """
134 extension_mock = mock.Mock()
145
146 extension_mock = ExtensionMock()
147 extension_mock._mock_name = extension
148
135 events_mock = mock.Mock()
149 events_mock = mock.Mock()
136 patches = {
150 patches = {
137 'Repository': mock.Mock(),
151 'Repository': mock.Mock(),
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -18,15 +17,19 b''
18 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
19
21 import json
22 import logging
20 import logging
23 import io
21 import io
24
22
25 import mock
23 import mock
24 import msgpack
26 import pytest
25 import pytest
27
26
28 from rhodecode.lib import hooks_daemon
27 from rhodecode.lib import hooks_daemon
28 from rhodecode.lib.str_utils import safe_bytes
29 from rhodecode.tests.utils import assert_message_in_log
29 from rhodecode.tests.utils import assert_message_in_log
30 from rhodecode.lib.ext_json import json
31
32 test_proto = hooks_daemon.HooksHttpHandler.MSGPACK_HOOKS_PROTO
30
33
31
34
32 class TestDummyHooksCallbackDaemon(object):
35 class TestDummyHooksCallbackDaemon(object):
@@ -73,7 +76,10 b' class TestHooksHttpHandler(object):'
73 hooks_daemon.Hooks, data['method'], create=True, return_value=1)
76 hooks_daemon.Hooks, data['method'], create=True, return_value=1)
74
77
75 with hooks_patcher as hooks_mock:
78 with hooks_patcher as hooks_mock:
76 MockServer(hooks_daemon.HooksHttpHandler, request)
79 handler = hooks_daemon.HooksHttpHandler
80 handler.DEFAULT_HOOKS_PROTO = test_proto
81 handler.wbufsize = 10240
82 MockServer(handler, request)
77
83
78 hooks_mock.assert_called_once_with(data['extras'])
84 hooks_mock.assert_called_once_with(data['extras'])
79
85
@@ -84,33 +90,51 b' class TestHooksHttpHandler(object):'
84 'first': 'one',
90 'first': 'one',
85 'second': 2
91 'second': 2
86 }
92 }
93 extras = {}
94
95 # patching our _read to return test method and proto used
87 read_patcher = mock.patch.object(
96 read_patcher = mock.patch.object(
88 hooks_daemon.HooksHttpHandler, '_read_request',
97 hooks_daemon.HooksHttpHandler, '_read_request',
89 return_value=(rpc_method, {}))
98 return_value=(test_proto, rpc_method, extras))
99
100 # patch Hooks instance to return hook_result data on 'test' call
90 hooks_patcher = mock.patch.object(
101 hooks_patcher = mock.patch.object(
91 hooks_daemon.Hooks, rpc_method, create=True,
102 hooks_daemon.Hooks, rpc_method, create=True,
92 return_value=hook_result)
103 return_value=hook_result)
93
104
94 with read_patcher, hooks_patcher:
105 with read_patcher, hooks_patcher:
95 server = MockServer(hooks_daemon.HooksHttpHandler, request)
106 handler = hooks_daemon.HooksHttpHandler
107 handler.DEFAULT_HOOKS_PROTO = test_proto
108 handler.wbufsize = 10240
109 server = MockServer(handler, request)
96
110
97 expected_result = json.dumps(hook_result)
111 expected_result = hooks_daemon.HooksHttpHandler.serialize_data(hook_result)
98 assert server.request.output_stream.buflist[-1] == expected_result
112
113 server.request.output_stream.seek(0)
114 assert server.request.output_stream.readlines()[-1] == expected_result
99
115
100 def test_exception_is_returned_in_response(self):
116 def test_exception_is_returned_in_response(self):
101 request = self._generate_post_request({})
117 request = self._generate_post_request({})
102 rpc_method = 'test'
118 rpc_method = 'test'
119
103 read_patcher = mock.patch.object(
120 read_patcher = mock.patch.object(
104 hooks_daemon.HooksHttpHandler, '_read_request',
121 hooks_daemon.HooksHttpHandler, '_read_request',
105 return_value=(rpc_method, {}))
122 return_value=(test_proto, rpc_method, {}))
123
106 hooks_patcher = mock.patch.object(
124 hooks_patcher = mock.patch.object(
107 hooks_daemon.Hooks, rpc_method, create=True,
125 hooks_daemon.Hooks, rpc_method, create=True,
108 side_effect=Exception('Test exception'))
126 side_effect=Exception('Test exception'))
109
127
110 with read_patcher, hooks_patcher:
128 with read_patcher, hooks_patcher:
111 server = MockServer(hooks_daemon.HooksHttpHandler, request)
129 handler = hooks_daemon.HooksHttpHandler
130 handler.DEFAULT_HOOKS_PROTO = test_proto
131 handler.wbufsize = 10240
132 server = MockServer(handler, request)
112
133
113 org_exc = json.loads(server.request.output_stream.buflist[-1])
134 server.request.output_stream.seek(0)
135 data = server.request.output_stream.readlines()
136 msgpack_data = b''.join(data[5:])
137 org_exc = hooks_daemon.HooksHttpHandler.deserialize_data(msgpack_data)
114 expected_result = {
138 expected_result = {
115 'exception': 'Exception',
139 'exception': 'Exception',
116 'exception_traceback': org_exc['exception_traceback'],
140 'exception_traceback': org_exc['exception_traceback'],
@@ -125,17 +149,22 b' class TestHooksHttpHandler(object):'
125 fake_date = '1/Nov/2015 00:00:00'
149 fake_date = '1/Nov/2015 00:00:00'
126 date_patcher = mock.patch.object(
150 date_patcher = mock.patch.object(
127 handler, 'log_date_time_string', return_value=fake_date)
151 handler, 'log_date_time_string', return_value=fake_date)
152
128 with date_patcher, caplog.at_level(logging.DEBUG):
153 with date_patcher, caplog.at_level(logging.DEBUG):
129 handler.log_message('Some message %d, %s', 123, 'string')
154 handler.log_message('Some message %d, %s', 123, 'string')
130
155
131 expected_message = "HOOKS: {} - - [{}] Some message 123, string".format(ip_port, fake_date)
156 expected_message = f"HOOKS: {ip_port} - - [{fake_date}] Some message 123, string"
132 assert_message_in_log(
157 assert_message_in_log(
133 caplog.records, expected_message,
158 caplog.records, expected_message,
134 levelno=logging.DEBUG, module='hooks_daemon')
159 levelno=logging.DEBUG, module='hooks_daemon')
135
160
136 def _generate_post_request(self, data):
161 def _generate_post_request(self, data, proto=test_proto):
162 if proto == hooks_daemon.HooksHttpHandler.MSGPACK_HOOKS_PROTO:
163 payload = msgpack.packb(data)
164 else:
137 payload = json.dumps(data)
165 payload = json.dumps(data)
138 return 'POST / HTTP/1.0\nContent-Length: {}\n\n{}'.format(
166
167 return b'POST / HTTP/1.0\nContent-Length: %d\n\n%b' % (
139 len(payload), payload)
168 len(payload), payload)
140
169
141
170
@@ -190,9 +219,9 b' class TestHttpHooksCallbackDaemon(object'
190 assert daemon._daemon == tcp_server
219 assert daemon._daemon == tcp_server
191
220
192 _, port = tcp_server.server_address
221 _, port = tcp_server.server_address
193 expected_uri = '{}:{}'.format('127.0.0.1', port)
222
194 msg = 'HOOKS: {} Preparing HTTP callback daemon registering ' \
223 msg = f"HOOKS: 127.0.0.1:{port} Preparing HTTP callback daemon registering " \
195 'hook object: rhodecode.lib.hooks_daemon.HooksHttpHandler'.format(expected_uri)
224 f"hook object: <class 'rhodecode.lib.hooks_daemon.HooksHttpHandler'>"
196 assert_message_in_log(
225 assert_message_in_log(
197 caplog.records, msg, levelno=logging.DEBUG, module='hooks_daemon')
226 caplog.records, msg, levelno=logging.DEBUG, module='hooks_daemon')
198
227
@@ -205,8 +234,8 b' class TestHttpHooksCallbackDaemon(object'
205 expected_uri = '{}:{}'.format('127.0.0.1', port)
234 expected_uri = '{}:{}'.format('127.0.0.1', port)
206 assert daemon.hooks_uri == expected_uri
235 assert daemon.hooks_uri == expected_uri
207
236
208 msg = 'HOOKS: {} Preparing HTTP callback daemon registering ' \
237 msg = f"HOOKS: 127.0.0.1:{port} Preparing HTTP callback daemon registering " \
209 'hook object: rhodecode.lib.hooks_daemon.HooksHttpHandler'.format(expected_uri)
238 f"hook object: <class 'rhodecode.lib.hooks_daemon.HooksHttpHandler'>"
210 assert_message_in_log(
239 assert_message_in_log(
211 caplog.records, msg,
240 caplog.records, msg,
212 levelno=logging.DEBUG, module='hooks_daemon')
241 levelno=logging.DEBUG, module='hooks_daemon')
@@ -318,16 +347,19 b' class TestPrepareHooksDaemon(object):'
318
347
319
348
320 class MockRequest(object):
349 class MockRequest(object):
350
321 def __init__(self, request):
351 def __init__(self, request):
322 self.request = request
352 self.request = request
323 self.input_stream = io.StringIO(b'{}'.format(self.request))
353 self.input_stream = io.BytesIO(safe_bytes(self.request))
324 self.output_stream = io.StringIO()
354 self.output_stream = io.BytesIO() # make it un-closable for testing invesitagion
355 self.output_stream.close = lambda: None
325
356
326 def makefile(self, mode, *args, **kwargs):
357 def makefile(self, mode, *args, **kwargs):
327 return self.output_stream if mode == 'wb' else self.input_stream
358 return self.output_stream if mode == 'wb' else self.input_stream
328
359
329
360
330 class MockServer(object):
361 class MockServer(object):
362
331 def __init__(self, handler_cls, request):
363 def __init__(self, handler_cls, request):
332 ip_port = ('0.0.0.0', 8888)
364 ip_port = ('0.0.0.0', 8888)
333 self.request = MockRequest(request)
365 self.request = MockRequest(request)
@@ -339,4 +371,5 b' class MockServer(object):'
339 def tcp_server():
371 def tcp_server():
340 server = mock.Mock()
372 server = mock.Mock()
341 server.server_address = ('127.0.0.1', 8881)
373 server.server_address = ('127.0.0.1', 8881)
374 server.wbufsize = 1024
342 return server
375 return server
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -90,7 +90,7 b' def test_mutation_types_with_nullable(en'
90
90
91 assert engine.execute(
91 assert engine.execute(
92 "select * from some_table where name = 'nulls';").first() == (
92 "select * from some_table where name = 'nulls';").first() == (
93 (u'nulls', None, None, None)
93 ('nulls', None, None, None)
94 )
94 )
95 ret_nulls = session.query(DummyModel).get('nulls')
95 ret_nulls = session.query(DummyModel).get('nulls')
96 assert ret_nulls.json_list == []
96 assert ret_nulls.json_list == []
@@ -99,7 +99,7 b' def test_mutation_types_with_nullable(en'
99
99
100 assert engine.execute(
100 assert engine.execute(
101 "select * from some_table where name = 'stuff';").first() == (
101 "select * from some_table where name = 'stuff';").first() == (
102 (u'stuff', u'[1, 2, 3]', u'{"a": 5}', u'9')
102 ('stuff', '[1,2,3]', '{"a":5}', '9')
103 )
103 )
104 ret_stuff = session.query(DummyModel).get('stuff')
104 ret_stuff = session.query(DummyModel).get('stuff')
105 assert ret_stuff.json_list == [1, 2, 3]
105 assert ret_stuff.json_list == [1, 2, 3]
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -142,7 +141,8 b' def test_age(age_args, expected, kw, bas'
142 from rhodecode.lib.utils2 import age
141 from rhodecode.lib.utils2 import age
143 from dateutil import relativedelta
142 from dateutil import relativedelta
144 n = datetime.datetime(year=2012, month=5, day=17)
143 n = datetime.datetime(year=2012, month=5, day=17)
145 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
144 def delt(*args, **kwargs):
145 return relativedelta.relativedelta(*args, **kwargs)
146
146
147 def translate(elem):
147 def translate(elem):
148 return elem.interpolate()
148 return elem.interpolate()
@@ -174,7 +174,8 b' def test_age_in_future(age_args, expecte'
174 from rhodecode.lib.utils2 import age
174 from rhodecode.lib.utils2 import age
175 from dateutil import relativedelta
175 from dateutil import relativedelta
176 n = datetime.datetime(year=2012, month=5, day=17)
176 n = datetime.datetime(year=2012, month=5, day=17)
177 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
177 def delt(*args, **kwargs):
178 return relativedelta.relativedelta(*args, **kwargs)
178
179
179 def translate(elem):
180 def translate(elem):
180 return elem.interpolate()
181 return elem.interpolate()
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -69,7 +68,7 b' def test_markdown_inline_html():'
69 xss_md = '\n'.join(['> <a name="n"',
68 xss_md = '\n'.join(['> <a name="n"',
70 '> onload="javascript:alert()" href="https://rhodecode.com">link</a>'])
69 '> onload="javascript:alert()" href="https://rhodecode.com">link</a>'])
71 rendered_html = MarkupRenderer.markdown(xss_md)
70 rendered_html = MarkupRenderer.markdown(xss_md)
72 assert '<a href="https://rhodecode.com" name="n">link</a>' in rendered_html
71 assert '<a name="n" href="https://rhodecode.com">link</a>' in rendered_html
73
72
74
73
75 def test_markdown_bleach_renders_correct():
74 def test_markdown_bleach_renders_correct():
@@ -301,7 +300,10 b' console.log(s);'
301
300
302 ```python
301 ```python
303 s = "Python syntax highlighting"
302 s = "Python syntax highlighting"
304 print s
303 print(s)
304
305 class Orm(object):
306 pass
305 ```
307 ```
306
308
307 ```
309 ```
@@ -317,7 +319,10 b' alert(s);'
317
319
318 ```python
320 ```python
319 s = "Python syntax highlighting"
321 s = "Python syntax highlighting"
320 print s
322 print(s)
323
324 class Orm(object):
325 pass
321 ```
326 ```
322
327
323 ```
328 ```
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,4 b''
1 import py.test
1
2
2
3 from rhodecode.lib.system_info import get_system_info
3 from rhodecode.lib.system_info import get_system_info
4
4
@@ -6,10 +6,3 b' from rhodecode.lib.system_info import ge'
6 def test_system_info(app):
6 def test_system_info(app):
7 info = get_system_info({})
7 info = get_system_info({})
8 assert info['load']['value']['15_min'] != 'NOT AVAILABLE'
8 assert info['load']['value']['15_min'] != 'NOT AVAILABLE'
9
10
11 def test_system_info_without_psutil(monkeypatch, app):
12 import rhodecode.lib.system_info
13 monkeypatch.setattr(rhodecode.lib.system_info, 'psutil', None)
14 info = get_system_info({})
15 assert info['load']['value']['15_min'] == 'NOT AVAILABLE'
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -18,7 +17,6 b''
18 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
19
21 import json
22 import multiprocessing
20 import multiprocessing
23 import os
21 import os
24
22
@@ -28,16 +26,16 b' import pytest'
28
26
29 from rhodecode.lib import caching_query
27 from rhodecode.lib import caching_query
30 from rhodecode.lib import utils
28 from rhodecode.lib import utils
31 from rhodecode.lib.utils2 import md5
29 from rhodecode.lib.str_utils import safe_bytes
32 from rhodecode.model import settings
30 from rhodecode.model import settings
33 from rhodecode.model import db
31 from rhodecode.model import db
34 from rhodecode.model import meta
32 from rhodecode.model import meta
35 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.repo_group import RepoGroupModel
37 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.settings import UiSetting, SettingsModel
35 from rhodecode.model.settings import UiSetting, SettingsModel
39 from rhodecode.tests.fixture import Fixture
36 from rhodecode.tests.fixture import Fixture
40
37 from rhodecode_tools.lib.hash_utils import md5_safe
38 from rhodecode.lib.ext_json import json
41
39
42 fixture = Fixture()
40 fixture = Fixture()
43
41
@@ -67,11 +65,17 b' def disable_hooks(request, hooks):'
67 # Invalidate cache
65 # Invalidate cache
68 ui_settings = session.query(db.RhodeCodeUi).options(
66 ui_settings = session.query(db.RhodeCodeUi).options(
69 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
67 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
70 ui_settings.invalidate()
68
69 meta.cache.invalidate(
70 ui_settings, {},
71 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
71
72
72 ui_settings = session.query(db.RhodeCodeUi).options(
73 ui_settings = session.query(db.RhodeCodeUi).options(
73 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
74 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
74 ui_settings.invalidate()
75
76 meta.cache.invalidate(
77 ui_settings, {},
78 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
75
79
76 @request.addfinalizer
80 @request.addfinalizer
77 def rollback():
81 def rollback():
@@ -110,7 +114,7 b' def test_make_db_config_hg_hooks(baseapp'
110 config = utils.make_db_config()
114 config = utils.make_db_config()
111 hooks = extract_hooks(config)
115 hooks = extract_hooks(config)
112
116
113 assert set(hooks.iterkeys()).intersection(HG_HOOKS) == set(expected_hooks)
117 assert set(hooks.keys()).intersection(HG_HOOKS) == set(expected_hooks)
114
118
115
119
116 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
120 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
@@ -180,11 +184,19 b' def _stub_git_repo(repo_path):'
180 repo_path.ensure('.git', dir=True)
184 repo_path.ensure('.git', dir=True)
181
185
182
186
183 @pytest.mark.parametrize('str_class', [str, bytes], ids=['str', 'bytes'])
187 def test_get_dirpaths_returns_all_paths_on_str(tmpdir):
184 def test_get_dirpaths_returns_all_paths(tmpdir, str_class):
185 tmpdir.ensure('test-file')
188 tmpdir.ensure('test-file')
186 dirpaths = utils._get_dirpaths(str_class(tmpdir))
189 tmpdir.ensure('test-file-1')
187 assert dirpaths == ['test-file']
190 tmp_path = str(tmpdir)
191 dirpaths = utils.get_dirpaths(tmp_path)
192 assert list(sorted(dirpaths)) == ['test-file', 'test-file-1']
193
194
195 def test_get_dirpaths_returns_all_paths_on_bytes(tmpdir):
196 tmpdir.ensure('test-file-bytes')
197 tmp_path = str(tmpdir)
198 dirpaths = utils.get_dirpaths(safe_bytes(tmp_path))
199 assert list(sorted(dirpaths)) == [b'test-file-bytes']
188
200
189
201
190 def test_get_dirpaths_returns_all_paths_bytes(
202 def test_get_dirpaths_returns_all_paths_bytes(
@@ -192,7 +204,7 b' def test_get_dirpaths_returns_all_paths_'
192 if platform_encodes_filenames:
204 if platform_encodes_filenames:
193 pytest.skip("This platform seems to encode filenames.")
205 pytest.skip("This platform seems to encode filenames.")
194 tmpdir.ensure('repo-a-umlaut-\xe4')
206 tmpdir.ensure('repo-a-umlaut-\xe4')
195 dirpaths = utils._get_dirpaths(str(tmpdir))
207 dirpaths = utils.get_dirpaths(str(tmpdir))
196 assert dirpaths == ['repo-a-umlaut-\xe4']
208 assert dirpaths == ['repo-a-umlaut-\xe4']
197
209
198
210
@@ -201,8 +213,8 b' def test_get_dirpaths_skips_paths_it_can'
201 if platform_encodes_filenames:
213 if platform_encodes_filenames:
202 pytest.skip("This platform seems to encode filenames.")
214 pytest.skip("This platform seems to encode filenames.")
203 path_with_latin1 = 'repo-a-umlaut-\xe4'
215 path_with_latin1 = 'repo-a-umlaut-\xe4'
204 tmpdir.ensure(path_with_latin1)
216 tmp_path = str(tmpdir.ensure(path_with_latin1))
205 dirpaths = utils._get_dirpaths(unicode(tmpdir))
217 dirpaths = utils.get_dirpaths(tmp_path)
206 assert dirpaths == []
218 assert dirpaths == []
207
219
208
220
@@ -266,7 +278,8 b' def test_repo2db_mapper_installs_hooks_f'
266
278
267
279
268 class TestPasswordChanged(object):
280 class TestPasswordChanged(object):
269 def setup(self):
281
282 def setup_method(self):
270 self.session = {
283 self.session = {
271 'rhodecode_user': {
284 'rhodecode_user': {
272 'password': '0cc175b9c0f1b6a831c399e269772661'
285 'password': '0cc175b9c0f1b6a831c399e269772661'
@@ -282,7 +295,7 b' class TestPasswordChanged(object):'
282 assert result is False
295 assert result is False
283
296
284 def test_returns_false_if_password_was_not_changed(self):
297 def test_returns_false_if_password_was_not_changed(self):
285 self.session['rhodecode_user']['password'] = md5(
298 self.session['rhodecode_user']['password'] = md5_safe(
286 self.auth_user.password)
299 self.auth_user.password)
287 result = utils.password_changed(self.auth_user, self.session)
300 result = utils.password_changed(self.auth_user, self.session)
288 assert result is False
301 assert result is False
@@ -406,7 +419,7 b' class TestConfigDataFromDb(object):'
406
419
407 class TestIsDirWritable(object):
420 class TestIsDirWritable(object):
408 def test_returns_false_when_not_writable(self):
421 def test_returns_false_when_not_writable(self):
409 with mock.patch('__builtin__.open', side_effect=OSError):
422 with mock.patch('builtins.open', side_effect=OSError):
410 assert not utils._is_dir_writable('/stub-path')
423 assert not utils._is_dir_writable('/stub-path')
411
424
412 def test_returns_true_when_writable(self, tmpdir):
425 def test_returns_true_when_writable(self, tmpdir):
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -18,10 +17,12 b''
18 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
19
21 import json
20
22
21
23 import requests
22 import requests
24
23
24 from rhodecode.lib.ext_json import sjson as json
25
25
26
26 class ApiError(Exception):
27 class ApiError(Exception):
27 """Error when accessing the API."""
28 """Error when accessing the API."""
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -35,7 +34,9 b' import subprocess'
35 import sys
34 import sys
36 import time
35 import time
37 import traceback
36 import traceback
38 import urllib.request, urllib.parse, urllib.error
37 import urllib.request
38 import urllib.parse
39 import urllib.error
39
40
40 PROFILING_INTERVAL = 5
41 PROFILING_INTERVAL = 5
41 RC_WEBSITE = "http://localhost:5001/"
42 RC_WEBSITE = "http://localhost:5001/"
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -60,9 +60,9 b' class TestGistSchema(object):'
60 lifetime_options=[1, 2, 3]
60 lifetime_options=[1, 2, 3]
61 )
61 )
62 nodes = [{
62 nodes = [{
63 'filename': 'foobar',
63 'filename': b'foobar',
64 'filename_org': 'foobar',
64 'filename_org': b'foobar',
65 'content': 'content',
65 'content': b'content',
66 'mimetype': 'xx'
66 'mimetype': 'xx'
67 }]
67 }]
68 schema_data = schema.deserialize(dict(
68 schema_data = schema.deserialize(dict(
@@ -80,9 +80,9 b' class TestGistSchema(object):'
80 convert_nodes=True
80 convert_nodes=True
81 )
81 )
82 nodes = [{
82 nodes = [{
83 'filename': 'foobar',
83 'filename': b'foobar',
84 'filename_org': None,
84 'filename_org': None,
85 'content': 'content',
85 'content': b'content',
86 'mimetype': 'xx'
86 'mimetype': 'xx'
87 }]
87 }]
88 schema_data = schema.deserialize(dict(
88 schema_data = schema.deserialize(dict(
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -116,7 +115,8 b' def assert_inline_comments_order(query):'
116 """
115 """
117 Sorting by ID will make sure that the latest comments are at the bottom.
116 Sorting by ID will make sure that the latest comments are at the bottom.
118 """
117 """
119 order_by = query._order_by
118
119 order_by = query._order_by_clauses
120 assert order_by
120 assert order_by
121 assert len(order_by) == 1
121 assert len(order_by) == 1
122 assert str(order_by[0]) == 'changeset_comments.comment_id ASC'
122 assert str(order_by[0]) == 'changeset_comments.comment_id ASC'
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -33,15 +32,14 b' class TestModelReprImplementation(object'
33
32
34 def test_repr_without_id(self, DBModel, klass, id_attr):
33 def test_repr_without_id(self, DBModel, klass, id_attr):
35 instance = DBModel()
34 instance = DBModel()
36 expected_repr = '<DB:%s at %#x>' % (klass, id(instance))
35 expected_repr = f'<DB:{klass} at {id(instance)}>'
37 assert repr(instance) == expected_repr
36 assert repr(instance) == expected_repr
38
37
39 def test_repr_with_id(self, DBModel, klass, id_attr):
38 def test_repr_with_id(self, DBModel, klass, id_attr):
40 test_id = random.randint(1, 10)
39 test_id = random.randint(1, 10)
41 instance = DBModel()
40 instance = DBModel()
42 setattr(instance, id_attr, test_id)
41 setattr(instance, id_attr, test_id)
43 expected_repr = (
42 expected_repr = f'<DB:{klass} #{test_id}>'
44 '<DB:%s #%d>' % (klass, test_id))
45 assert repr(instance) == expected_repr
43 assert repr(instance) == expected_repr
46
44
47
45
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -35,8 +34,8 b' class TestGistModel(object):'
35 return_value=repo.scm_instance())
34 return_value=repo.scm_instance())
36 with create_repo_patch as create_repo_mock:
35 with create_repo_patch as create_repo_mock:
37 gist_mapping = {
36 gist_mapping = {
38 'filename.txt': {
37 b'filename.txt': {
39 'content': 'Test content'
38 'content': b'Test content'
40 }
39 }
41 }
40 }
42 model.create('Test description', owner, gist_mapping)
41 model.create('Test description', owner, gist_mapping)
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -41,20 +40,20 b' class TestNotifications(object):'
41 def create_users(self, request, app):
40 def create_users(self, request, app):
42 Session.remove()
41 Session.remove()
43 self.u1 = UserModel().create_or_update(
42 self.u1 = UserModel().create_or_update(
44 username=u'u1', password=u'qweqwe',
43 username='u1', password='qweqwe',
45 email=u'u1@rhodecode.org', firstname=u'u1', lastname=u'u1')
44 email='u1@rhodecode.org', firstname='u1', lastname='u1')
46 Session().commit()
45 Session().commit()
47 self.u1 = self.u1.user_id
46 self.u1 = self.u1.user_id
48
47
49 self.u2 = UserModel().create_or_update(
48 self.u2 = UserModel().create_or_update(
50 username=u'u2', password=u'qweqwe',
49 username='u2', password='qweqwe',
51 email=u'u2@rhodecode.org', firstname=u'u2', lastname=u'u2')
50 email='u2@rhodecode.org', firstname='u2', lastname='u2')
52 Session().commit()
51 Session().commit()
53 self.u2 = self.u2.user_id
52 self.u2 = self.u2.user_id
54
53
55 self.u3 = UserModel().create_or_update(
54 self.u3 = UserModel().create_or_update(
56 username=u'u3', password=u'qweqwe',
55 username='u3', password='qweqwe',
57 email=u'u3@rhodecode.org', firstname=u'u3', lastname=u'u3')
56 email='u3@rhodecode.org', firstname='u3', lastname='u3')
58 Session().commit()
57 Session().commit()
59 self.u3 = self.u3.user_id
58 self.u3 = self.u3.user_id
60 self.destroy_users.add('u1')
59 self.destroy_users.add('u1')
@@ -73,8 +72,8 b' class TestNotifications(object):'
73 def test_create_notification(self):
72 def test_create_notification(self):
74 usrs = [self.u1, self.u2]
73 usrs = [self.u1, self.u2]
75 notification = NotificationModel().create(
74 notification = NotificationModel().create(
76 created_by=self.u1, notification_subject=u'subj',
75 created_by=self.u1, notification_subject='subj',
77 notification_body=u'hi there', recipients=usrs)
76 notification_body='hi there', recipients=usrs)
78 Session().commit()
77 Session().commit()
79 u1 = User.get(self.u1)
78 u1 = User.get(self.u1)
80 u2 = User.get(self.u2)
79 u2 = User.get(self.u2)
@@ -93,32 +92,31 b' class TestNotifications(object):'
93 def test_create_notification_fails_for_invalid_recipients(self):
92 def test_create_notification_fails_for_invalid_recipients(self):
94 with pytest.raises(Exception):
93 with pytest.raises(Exception):
95 NotificationModel().create(
94 NotificationModel().create(
96 created_by=self.u1, notification_subject=u'subj',
95 created_by=self.u1, notification_subject='subj',
97 notification_body=u'hi there', recipients=['bad_user_id'])
96 notification_body='hi there', recipients=['bad_user_id'])
98
97
99 with pytest.raises(Exception):
98 with pytest.raises(Exception):
100 NotificationModel().create(
99 NotificationModel().create(
101 created_by=self.u1, notification_subject=u'subj',
100 created_by=self.u1, notification_subject='subj',
102 notification_body=u'hi there', recipients=[])
101 notification_body='hi there', recipients=[])
103
102
104 def test_user_notifications(self):
103 def test_user_notifications(self):
105 notification1 = NotificationModel().create(
104 notification1 = NotificationModel().create(
106 created_by=self.u1, notification_subject=u'subj',
105 created_by=self.u1, notification_subject='subj',
107 notification_body=u'hi there1', recipients=[self.u3])
106 notification_body='hi there1', recipients=[self.u3])
108 Session().commit()
107 Session().commit()
109 notification2 = NotificationModel().create(
108 notification2 = NotificationModel().create(
110 created_by=self.u1, notification_subject=u'subj',
109 created_by=self.u1, notification_subject='subj',
111 notification_body=u'hi there2', recipients=[self.u3])
110 notification_body='hi there2', recipients=[self.u3])
112 Session().commit()
111 Session().commit()
113 u3 = Session().query(User).get(self.u3)
112 u3 = Session().query(User).get(self.u3)
114
113
115 assert sorted([x.notification for x in u3.notifications]) ==\
114 assert [x.notification for x in u3.notifications] == [notification2, notification1]
116 sorted([notification2, notification1])
117
115
118 def test_delete_notifications(self):
116 def test_delete_notifications(self):
119 notification = NotificationModel().create(
117 notification = NotificationModel().create(
120 created_by=self.u1, notification_subject=u'title',
118 created_by=self.u1, notification_subject='title',
121 notification_body=u'hi there3',
119 notification_body='hi there3',
122 recipients=[self.u3, self.u1, self.u2])
120 recipients=[self.u3, self.u1, self.u2])
123 Session().commit()
121 Session().commit()
124 notifications = Notification.query().all()
122 notifications = Notification.query().all()
@@ -136,8 +134,8 b' class TestNotifications(object):'
136
134
137 def test_delete_association(self):
135 def test_delete_association(self):
138 notification = NotificationModel().create(
136 notification = NotificationModel().create(
139 created_by=self.u1, notification_subject=u'title',
137 created_by=self.u1, notification_subject='title',
140 notification_body=u'hi there3',
138 notification_body='hi there3',
141 recipients=[self.u3, self.u1, self.u2])
139 recipients=[self.u3, self.u1, self.u2])
142 Session().commit()
140 Session().commit()
143
141
@@ -180,8 +178,8 b' class TestNotifications(object):'
180
178
181 def test_notification_counter(self):
179 def test_notification_counter(self):
182 NotificationModel().create(
180 NotificationModel().create(
183 created_by=self.u1, notification_subject=u'title',
181 created_by=self.u1, notification_subject='title',
184 notification_body=u'hi there_delete', recipients=[self.u3, self.u1])
182 notification_body='hi there_delete', recipients=[self.u3, self.u1])
185 Session().commit()
183 Session().commit()
186
184
187 # creator has it's own notification marked as read
185 # creator has it's own notification marked as read
@@ -190,8 +188,8 b' class TestNotifications(object):'
190 assert NotificationModel().get_unread_cnt_for_user(self.u3) == 1
188 assert NotificationModel().get_unread_cnt_for_user(self.u3) == 1
191
189
192 NotificationModel().create(
190 NotificationModel().create(
193 created_by=self.u1, notification_subject=u'title',
191 created_by=self.u1, notification_subject='title',
194 notification_body=u'hi there3',
192 notification_body='hi there3',
195 recipients=[self.u3, self.u1, self.u2])
193 recipients=[self.u3, self.u1, self.u2])
196 Session().commit()
194 Session().commit()
197 # creator has it's own notification marked as read
195 # creator has it's own notification marked as read
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -23,7 +22,6 b' import pytest'
23 import textwrap
22 import textwrap
24
23
25 import rhodecode
24 import rhodecode
26 from rhodecode.lib.utils2 import safe_unicode
27 from rhodecode.lib.vcs.backends import get_backend
25 from rhodecode.lib.vcs.backends import get_backend
28 from rhodecode.lib.vcs.backends.base import (
26 from rhodecode.lib.vcs.backends.base import (
29 MergeResponse, MergeFailureReason, Reference)
27 MergeResponse, MergeFailureReason, Reference)
@@ -34,7 +32,7 b' from rhodecode.model.db import PullReque'
34 from rhodecode.model.pull_request import PullRequestModel
32 from rhodecode.model.pull_request import PullRequestModel
35 from rhodecode.model.user import UserModel
33 from rhodecode.model.user import UserModel
36 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
34 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
37
35 from rhodecode.lib.str_utils import safe_str
38
36
39 pytestmark = [
37 pytestmark = [
40 pytest.mark.backends("git", "hg"),
38 pytest.mark.backends("git", "hg"),
@@ -299,10 +297,10 b' class TestPullRequestModel(object):'
299 u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}'
297 u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}'
300 u'\n\n {pr_title}'.format(
298 u'\n\n {pr_title}'.format(
301 pr_id=pull_request.pull_request_id,
299 pr_id=pull_request.pull_request_id,
302 source_repo=safe_unicode(
300 source_repo=safe_str(
303 pull_request.source_repo.scm_instance().name),
301 pull_request.source_repo.scm_instance().name),
304 source_ref_name=pull_request.source_ref_parts.name,
302 source_ref_name=pull_request.source_ref_parts.name,
305 pr_title=safe_unicode(pull_request.title)
303 pr_title=safe_str(pull_request.title)
306 )
304 )
307 )
305 )
308 self.merge_mock.assert_called_with(
306 self.merge_mock.assert_called_with(
@@ -343,10 +341,10 b' class TestPullRequestModel(object):'
343 u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}'
341 u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}'
344 u'\n\n {pr_title}'.format(
342 u'\n\n {pr_title}'.format(
345 pr_id=pull_request.pull_request_id,
343 pr_id=pull_request.pull_request_id,
346 source_repo=safe_unicode(
344 source_repo=safe_str(
347 pull_request.source_repo.scm_instance().name),
345 pull_request.source_repo.scm_instance().name),
348 source_ref_name=pull_request.source_ref_parts.name,
346 source_ref_name=pull_request.source_ref_parts.name,
349 pr_title=safe_unicode(pull_request.title)
347 pr_title=safe_str(pull_request.title)
350 )
348 )
351 )
349 )
352 self.merge_mock.assert_called_with(
350 self.merge_mock.assert_called_with(
@@ -382,10 +380,10 b' class TestPullRequestModel(object):'
382 u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}'
380 u'Merge pull request !{pr_id} from {source_repo} {source_ref_name}'
383 u'\n\n {pr_title}'.format(
381 u'\n\n {pr_title}'.format(
384 pr_id=pull_request.pull_request_id,
382 pr_id=pull_request.pull_request_id,
385 source_repo=safe_unicode(
383 source_repo=safe_str(
386 pull_request.source_repo.scm_instance().name),
384 pull_request.source_repo.scm_instance().name),
387 source_ref_name=pull_request.source_ref_parts.name,
385 source_ref_name=pull_request.source_ref_parts.name,
388 pr_title=safe_unicode(pull_request.title)
386 pr_title=safe_str(pull_request.title)
389 )
387 )
390 )
388 )
391 self.merge_mock.assert_called_with(
389 self.merge_mock.assert_called_with(
@@ -423,7 +421,7 b' class TestPullRequestModel(object):'
423 diff = PullRequestModel()._get_diff_from_pr_or_version(
421 diff = PullRequestModel()._get_diff_from_pr_or_version(
424 source_repo, source_ref_id, target_ref_id,
422 source_repo, source_ref_id, target_ref_id,
425 hide_whitespace_changes=False, diff_context=6)
423 hide_whitespace_changes=False, diff_context=6)
426 assert 'file_1' in diff.raw
424 assert b'file_1' in diff.raw.tobytes()
427
425
428 def test_generate_title_returns_unicode(self):
426 def test_generate_title_returns_unicode(self):
429 title = PullRequestModel().generate_pullrequest_title(
427 title = PullRequestModel().generate_pullrequest_title(
@@ -431,7 +429,7 b' class TestPullRequestModel(object):'
431 source_ref='source-ref-dummy',
429 source_ref='source-ref-dummy',
432 target='target-dummy',
430 target='target-dummy',
433 )
431 )
434 assert type(title) == unicode
432 assert type(title) == str
435
433
436 @pytest.mark.parametrize('title, has_wip', [
434 @pytest.mark.parametrize('title, has_wip', [
437 ('hello', False),
435 ('hello', False),
@@ -607,8 +605,8 b' class TestUpdateCommentHandling(object):'
607 def test_comment_stays_unflagged_on_unchanged_diff(self, pr_util):
605 def test_comment_stays_unflagged_on_unchanged_diff(self, pr_util):
608 commits = [
606 commits = [
609 {'message': 'a'},
607 {'message': 'a'},
610 {'message': 'b', 'added': [FileNode('file_b', 'test_content\n')]},
608 {'message': 'b', 'added': [FileNode(b'file_b', b'test_content\n')]},
611 {'message': 'c', 'added': [FileNode('file_c', 'test_content\n')]},
609 {'message': 'c', 'added': [FileNode(b'file_c', b'test_content\n')]},
612 ]
610 ]
613 pull_request = pr_util.create_pull_request(
611 pull_request = pr_util.create_pull_request(
614 commits=commits, target_head='a', source_head='b', revisions=['b'])
612 commits=commits, target_head='a', source_head='b', revisions=['b'])
@@ -618,13 +616,12 b' class TestUpdateCommentHandling(object):'
618 assert_inline_comments(pull_request, visible=1, outdated=0)
616 assert_inline_comments(pull_request, visible=1, outdated=0)
619
617
620 def test_comment_stays_unflagged_on_change_above(self, pr_util):
618 def test_comment_stays_unflagged_on_change_above(self, pr_util):
621 original_content = ''.join(
619 original_content = b''.join((b'line %d\n' % x for x in range(1, 11)))
622 ['line {}\n'.format(x) for x in range(1, 11)])
620 updated_content = b'new_line_at_top\n' + original_content
623 updated_content = 'new_line_at_top\n' + original_content
624 commits = [
621 commits = [
625 {'message': 'a'},
622 {'message': 'a'},
626 {'message': 'b', 'added': [FileNode('file_b', original_content)]},
623 {'message': 'b', 'added': [FileNode(b'file_b', original_content)]},
627 {'message': 'c', 'changed': [FileNode('file_b', updated_content)]},
624 {'message': 'c', 'changed': [FileNode(b'file_b', updated_content)]},
628 ]
625 ]
629 pull_request = pr_util.create_pull_request(
626 pull_request = pr_util.create_pull_request(
630 commits=commits, target_head='a', source_head='b', revisions=['b'])
627 commits=commits, target_head='a', source_head='b', revisions=['b'])
@@ -638,12 +635,12 b' class TestUpdateCommentHandling(object):'
638 assert comment.line_no == u'n9'
635 assert comment.line_no == u'n9'
639
636
640 def test_comment_stays_unflagged_on_change_below(self, pr_util):
637 def test_comment_stays_unflagged_on_change_below(self, pr_util):
641 original_content = ''.join(['line {}\n'.format(x) for x in range(10)])
638 original_content = b''.join([b'line %d\n' % x for x in range(10)])
642 updated_content = original_content + 'new_line_at_end\n'
639 updated_content = original_content + b'new_line_at_end\n'
643 commits = [
640 commits = [
644 {'message': 'a'},
641 {'message': 'a'},
645 {'message': 'b', 'added': [FileNode('file_b', original_content)]},
642 {'message': 'b', 'added': [FileNode(b'file_b', original_content)]},
646 {'message': 'c', 'changed': [FileNode('file_b', updated_content)]},
643 {'message': 'c', 'changed': [FileNode(b'file_b', updated_content)]},
647 ]
644 ]
648 pull_request = pr_util.create_pull_request(
645 pull_request = pr_util.create_pull_request(
649 commits=commits, target_head='a', source_head='b', revisions=['b'])
646 commits=commits, target_head='a', source_head='b', revisions=['b'])
@@ -654,17 +651,17 b' class TestUpdateCommentHandling(object):'
654
651
655 @pytest.mark.parametrize('line_no', ['n4', 'o4', 'n10', 'o9'])
652 @pytest.mark.parametrize('line_no', ['n4', 'o4', 'n10', 'o9'])
656 def test_comment_flagged_on_change_around_context(self, pr_util, line_no):
653 def test_comment_flagged_on_change_around_context(self, pr_util, line_no):
657 base_lines = ['line {}\n'.format(x) for x in range(1, 13)]
654 base_lines = [b'line %d\n' % x for x in range(1, 13)]
658 change_lines = list(base_lines)
655 change_lines = list(base_lines)
659 change_lines.insert(6, 'line 6a added\n')
656 change_lines.insert(6, b'line 6a added\n')
660
657
661 # Changes on the last line of sight
658 # Changes on the last line of sight
662 update_lines = list(change_lines)
659 update_lines = list(change_lines)
663 update_lines[0] = 'line 1 changed\n'
660 update_lines[0] = b'line 1 changed\n'
664 update_lines[-1] = 'line 12 changed\n'
661 update_lines[-1] = b'line 12 changed\n'
665
662
666 def file_b(lines):
663 def file_b(lines):
667 return FileNode('file_b', ''.join(lines))
664 return FileNode(b'file_b', b''.join(lines))
668
665
669 commits = [
666 commits = [
670 {'message': 'a', 'added': [file_b(base_lines)]},
667 {'message': 'a', 'added': [file_b(base_lines)]},
@@ -681,14 +678,14 b' class TestUpdateCommentHandling(object):'
681 assert_inline_comments(pull_request, visible=0, outdated=1)
678 assert_inline_comments(pull_request, visible=0, outdated=1)
682
679
683 @pytest.mark.parametrize("change, content", [
680 @pytest.mark.parametrize("change, content", [
684 ('changed', 'changed\n'),
681 ('changed', b'changed\n'),
685 ('removed', ''),
682 ('removed', b''),
686 ], ids=['changed', 'removed'])
683 ], ids=['changed', b'removed'])
687 def test_comment_flagged_on_change(self, pr_util, change, content):
684 def test_comment_flagged_on_change(self, pr_util, change, content):
688 commits = [
685 commits = [
689 {'message': 'a'},
686 {'message': 'a'},
690 {'message': 'b', 'added': [FileNode('file_b', 'test_content\n')]},
687 {'message': 'b', 'added': [FileNode(b'file_b', b'test_content\n')]},
691 {'message': 'c', change: [FileNode('file_b', content)]},
688 {'message': 'c', change: [FileNode(b'file_b', content)]},
692 ]
689 ]
693 pull_request = pr_util.create_pull_request(
690 pull_request = pr_util.create_pull_request(
694 commits=commits, target_head='a', source_head='b', revisions=['b'])
691 commits=commits, target_head='a', source_head='b', revisions=['b'])
@@ -706,9 +703,9 b' class TestUpdateChangedFiles(object):'
706 commits = [
703 commits = [
707 {'message': 'a'},
704 {'message': 'a'},
708 {'message': 'b',
705 {'message': 'b',
709 'added': [FileNode('file_b', 'test_content b\n')]},
706 'added': [FileNode(b'file_b', b'test_content b\n')]},
710 {'message': 'c',
707 {'message': 'c',
711 'added': [FileNode('file_c', 'test_content c\n')]},
708 'added': [FileNode(b'file_c', b'test_content c\n')]},
712 ]
709 ]
713 # open a PR from a to b, adding file_b
710 # open a PR from a to b, adding file_b
714 pull_request = pr_util.create_pull_request(
711 pull_request = pr_util.create_pull_request(
@@ -728,11 +725,11 b' class TestUpdateChangedFiles(object):'
728 commits = [
725 commits = [
729 {'message': 'a'},
726 {'message': 'a'},
730 {'message': 'b',
727 {'message': 'b',
731 'added': [FileNode('file_b', 'test_content b\n')]},
728 'added': [FileNode(b'file_b', b'test_content b\n')]},
732 {'message': 'c',
729 {'message': 'c',
733 'changed': [FileNode('file_b', 'test_content b modified\n')]},
730 'changed': [FileNode(b'file_b', b'test_content b modified\n')]},
734 {'message': 'd',
731 {'message': 'd',
735 'changed': [FileNode('file_b', 'test_content b\n')]},
732 'changed': [FileNode(b'file_b', b'test_content b\n')]},
736 ]
733 ]
737 # open a PR from a to b, adding file_b
734 # open a PR from a to b, adding file_b
738 pull_request = pr_util.create_pull_request(
735 pull_request = pr_util.create_pull_request(
@@ -762,13 +759,13 b' class TestUpdateChangedFiles(object):'
762 commits = [
759 commits = [
763 {'message': 'a'},
760 {'message': 'a'},
764 {'message': 'b', 'added': [
761 {'message': 'b', 'added': [
765 FileNode('file_a', 'test_content a\n'),
762 FileNode(b'file_a', b'test_content a\n'),
766 FileNode('file_b', 'test_content b\n'),
763 FileNode(b'file_b', b'test_content b\n'),
767 FileNode('file_c', 'test_content c\n')]},
764 FileNode(b'file_c', b'test_content c\n')]},
768 {'message': 'c', 'changed': [
765 {'message': 'c', 'changed': [
769 FileNode('file_a', 'test_content a changed\n'),
766 FileNode(b'file_a', b'test_content a changed\n'),
770 FileNode('file_b', 'test_content b changed\n'),
767 FileNode(b'file_b', b'test_content b changed\n'),
771 FileNode('file_c', 'test_content c changed\n')]},
768 FileNode(b'file_c', b'test_content c changed\n')]},
772 ]
769 ]
773 # open a PR from a to b, changing 3 files
770 # open a PR from a to b, changing 3 files
774 pull_request = pr_util.create_pull_request(
771 pull_request = pr_util.create_pull_request(
@@ -787,13 +784,13 b' class TestUpdateChangedFiles(object):'
787 commits = [
784 commits = [
788 {'message': 'a'},
785 {'message': 'a'},
789 {'message': 'b', 'added': [
786 {'message': 'b', 'added': [
790 FileNode('file_a', 'test_content a\n'),
787 FileNode(b'file_a', b'test_content a\n'),
791 FileNode('file_b', 'test_content b\n'),
788 FileNode(b'file_b', b'test_content b\n'),
792 FileNode('file_c', 'test_content c\n')]},
789 FileNode(b'file_c', b'test_content c\n')]},
793 {'message': 'c', 'removed': [
790 {'message': 'c', 'removed': [
794 FileNode('file_a', 'test_content a changed\n'),
791 FileNode(b'file_a', b'test_content a changed\n'),
795 FileNode('file_b', 'test_content b changed\n'),
792 FileNode(b'file_b', b'test_content b changed\n'),
796 FileNode('file_c', 'test_content c changed\n')]},
793 FileNode(b'file_c', b'test_content c changed\n')]},
797 ]
794 ]
798 # open a PR from a to b, removing 3 files
795 # open a PR from a to b, removing 3 files
799 pull_request = pr_util.create_pull_request(
796 pull_request = pr_util.create_pull_request(
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -47,8 +46,8 b' class TestGetDiffForPrOrVersion(object):'
47 def _prepare_pull_request(self, pr_util):
46 def _prepare_pull_request(self, pr_util):
48 commits = [
47 commits = [
49 {'message': 'a'},
48 {'message': 'a'},
50 {'message': 'b', 'added': [FileNode('file_b', 'test_content\n')]},
49 {'message': 'b', 'added': [FileNode(b'file_b', 'test_content\n')]},
51 {'message': 'c', 'added': [FileNode('file_c', 'test_content\n')]},
50 {'message': 'c', 'added': [FileNode(b'file_c', 'test_content\n')]},
52 ]
51 ]
53 pull_request = pr_util.create_pull_request(
52 pull_request = pr_util.create_pull_request(
54 commits=commits, target_head='a', source_head='c',
53 commits=commits, target_head='a', source_head='c',
@@ -62,7 +61,7 b' class TestGetDiffForPrOrVersion(object):'
62 diff = PullRequestModel()._get_diff_from_pr_or_version(
61 diff = PullRequestModel()._get_diff_from_pr_or_version(
63 source_repo, source_ref_id, target_ref_id,
62 source_repo, source_ref_id, target_ref_id,
64 hide_whitespace_changes=False, diff_context=6)
63 hide_whitespace_changes=False, diff_context=6)
65 assert 'file_b' in diff.raw
64 assert b'file_b' in diff.raw.tobytes()
66
65
67 def assert_commit_cannot_be_accessed(
66 def assert_commit_cannot_be_accessed(
68 self, removed_commit_id, pull_request):
67 self, removed_commit_id, pull_request):
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -40,40 +39,39 b' class CommitUtility:'
40 def __init__(self, vcsbackend):
39 def __init__(self, vcsbackend):
41 self.vcsbackend = vcsbackend
40 self.vcsbackend = vcsbackend
42
41
43 def commit_with_files(self, filenames):
42 def commit_with_files(self, filenames: list[bytes]):
44 commits = [
43 commits = [
45 {'message': 'Adding all requested files',
44 {'message': 'Adding all requested files',
46 'added': [
45 'added': [
47 nodes.FileNode(filename, content='')
46 nodes.FileNode(filename, content=b'') for filename in filenames
48 for filename in filenames
49 ]}]
47 ]}]
50 repo = self.vcsbackend.create_repo(commits=commits)
48 repo = self.vcsbackend.create_repo(commits=commits)
51 return repo.get_commit()
49 return repo.get_commit()
52
50
53
51
54 def test_no_matching_file_returns_none(commit_util):
52 def test_no_matching_file_returns_none(commit_util):
55 commit = commit_util.commit_with_files(['LIESMICH'])
53 commit = commit_util.commit_with_files([b'LIESMICH'])
56 finder = ReadmeFinder(default_renderer='rst')
54 finder = ReadmeFinder(default_renderer='rst')
57 filenode = finder.search(commit)
55 filenode = finder.search(commit)
58 assert filenode is None
56 assert filenode is None
59
57
60
58
61 def test_matching_file_returns_the_file_name(commit_util):
59 def test_matching_file_returns_the_file_name(commit_util):
62 commit = commit_util.commit_with_files(['README'])
60 commit = commit_util.commit_with_files([b'README'])
63 finder = ReadmeFinder(default_renderer='rst')
61 finder = ReadmeFinder(default_renderer='rst')
64 filenode = finder.search(commit)
62 filenode = finder.search(commit)
65 assert filenode.path == 'README'
63 assert filenode.path == 'README'
66
64
67
65
68 def test_matching_file_with_extension(commit_util):
66 def test_matching_file_with_extension(commit_util):
69 commit = commit_util.commit_with_files(['README.rst'])
67 commit = commit_util.commit_with_files([b'README.rst'])
70 finder = ReadmeFinder(default_renderer='rst')
68 finder = ReadmeFinder(default_renderer='rst')
71 filenode = finder.search(commit)
69 filenode = finder.search(commit)
72 assert filenode.path == 'README.rst'
70 assert filenode.path == 'README.rst'
73
71
74
72
75 def test_prefers_readme_without_extension(commit_util):
73 def test_prefers_readme_without_extension(commit_util):
76 commit = commit_util.commit_with_files(['README.rst', 'Readme'])
74 commit = commit_util.commit_with_files([b'README.rst', b'Readme'])
77 finder = ReadmeFinder()
75 finder = ReadmeFinder()
78 filenode = finder.search(commit)
76 filenode = finder.search(commit)
79 assert filenode.path == 'Readme'
77 assert filenode.path == 'Readme'
@@ -84,23 +82,21 b' def test_prefers_readme_without_extensio'
84 ('markdown', 'readme.md'),
82 ('markdown', 'readme.md'),
85 ])
83 ])
86 def test_prefers_renderer_extensions(commit_util, renderer, expected):
84 def test_prefers_renderer_extensions(commit_util, renderer, expected):
87 commit = commit_util.commit_with_files(
85 commit = commit_util.commit_with_files([b'readme.rst', b'readme.md', b'readme.txt'])
88 ['readme.rst', 'readme.md', 'readme.txt'])
89 finder = ReadmeFinder(default_renderer=renderer)
86 finder = ReadmeFinder(default_renderer=renderer)
90 filenode = finder.search(commit)
87 filenode = finder.search(commit)
91 assert filenode.path == expected
88 assert filenode.path == expected
92
89
93
90
94 def test_finds_readme_in_subdirectory(commit_util):
91 def test_finds_readme_in_subdirectory(commit_util):
95 commit = commit_util.commit_with_files(['doc/README.rst', 'LIESMICH'])
92 commit = commit_util.commit_with_files([b'doc/README.rst', b'LIESMICH'])
96 finder = ReadmeFinder()
93 finder = ReadmeFinder()
97 filenode = finder.search(commit)
94 filenode = finder.search(commit)
98 assert filenode.path == 'doc/README.rst'
95 assert filenode.path == 'doc/README.rst'
99
96
100
97
101 def test_prefers_subdirectory_with_priority(commit_util):
98 def test_prefers_subdirectory_with_priority(commit_util):
102 commit = commit_util.commit_with_files(
99 commit = commit_util.commit_with_files([b'Doc/Readme.rst', b'Docs/Readme.rst'])
103 ['Doc/Readme.rst', 'Docs/Readme.rst'])
104 finder = ReadmeFinder()
100 finder = ReadmeFinder()
105 filenode = finder.search(commit)
101 filenode = finder.search(commit)
106 assert filenode.path == 'Doc/Readme.rst'
102 assert filenode.path == 'Doc/Readme.rst'
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -168,7 +167,7 b' def assert_contains_only_unicode(structu'
168 @pytest.mark.backends("hg", "git")
167 @pytest.mark.backends("hg", "git")
169 def test_get_non_unicode_reference(backend):
168 def test_get_non_unicode_reference(backend):
170 model = scm.ScmModel()
169 model = scm.ScmModel()
171 non_unicode_list = ["AdΔ±nΔ±".decode("cp1254")]
170 non_unicode_list = ["AdΔ±nΔ±".encode("cp1254")]
172
171
173 def scm_instance():
172 def scm_instance():
174 return Mock(
173 return Mock(
@@ -179,11 +178,11 b' def test_get_non_unicode_reference(backe'
179 choices, __ = model.get_repo_landing_revs(translator=lambda s: s, repo=repo)
178 choices, __ = model.get_repo_landing_revs(translator=lambda s: s, repo=repo)
180 if backend.alias == 'hg':
179 if backend.alias == 'hg':
181 valid_choices = [
180 valid_choices = [
182 'rev:tip', u'branch:Ad\xc4\xb1n\xc4\xb1',
181 'rev:tip', 'branch:Ad\xc4\xb1n\xc4\xb1',
183 u'book:Ad\xc4\xb1n\xc4\xb1', u'tag:Ad\xc4\xb1n\xc4\xb1']
182 'book:Ad\xc4\xb1n\xc4\xb1', 'tag:Ad\xc4\xb1n\xc4\xb1']
184 else:
183 else:
185 valid_choices = [
184 valid_choices = [
186 'rev:tip', u'branch:Ad\xc4\xb1n\xc4\xb1',
185 'rev:tip', 'branch:Ad\xc4\xb1n\xc4\xb1',
187 u'tag:Ad\xc4\xb1n\xc4\xb1']
186 'tag:Ad\xc4\xb1n\xc4\xb1']
188
187
189 assert choices == valid_choices
188 assert choices == valid_choices
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -21,7 +20,6 b''
21 import pytest
20 import pytest
22 import mock
21 import mock
23
22
24 from rhodecode.lib.utils2 import safe_unicode
25 from rhodecode.model.db import (
23 from rhodecode.model.db import (
26 true, User, UserGroup, UserGroupMember, UserEmailMap, Permission, UserIpMap)
24 true, User, UserGroup, UserGroupMember, UserEmailMap, Permission, UserIpMap)
27 from rhodecode.model.meta import Session
25 from rhodecode.model.meta import Session
@@ -30,6 +28,8 b' from rhodecode.model.user_group import U'
30 from rhodecode.model.repo import RepoModel
28 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo_group import RepoGroupModel
29 from rhodecode.model.repo_group import RepoGroupModel
32 from rhodecode.tests.fixture import Fixture
30 from rhodecode.tests.fixture import Fixture
31 from rhodecode.lib.str_utils import safe_str
32
33
33
34 fixture = Fixture()
34 fixture = Fixture()
35
35
@@ -69,7 +69,7 b' class TestGetUsers(object):'
69 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
69 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
70 assert len(fake_users) == 2
70 assert len(fake_users) == 2
71 for user in fake_users:
71 for user in fake_users:
72 assert user['last_name'] == safe_unicode('Fake ΓΌnicode user')
72 assert user['last_name'] == safe_str('Fake ΓΌnicode user')
73
73
74 def test_returns_user_filtered_by_first_name(self, backend, user_util):
74 def test_returns_user_filtered_by_first_name(self, backend, user_util):
75 created_users = []
75 created_users = []
@@ -88,7 +88,7 b' class TestGetUsers(object):'
88 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
88 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
89 assert len(fake_users) == 2
89 assert len(fake_users) == 2
90 for user in fake_users:
90 for user in fake_users:
91 assert user['first_name'] == safe_unicode('Fake ΓΌnicode user')
91 assert user['first_name'] == safe_str('Fake ΓΌnicode user')
92
92
93 def test_returns_user_filtered_by_username(self, backend, user_util):
93 def test_returns_user_filtered_by_username(self, backend, user_util):
94 created_users = []
94 created_users = []
@@ -122,10 +122,10 b' class TestGetUsers(object):'
122 @pytest.fixture()
122 @pytest.fixture()
123 def test_user(request, baseapp):
123 def test_user(request, baseapp):
124 usr = UserModel().create_or_update(
124 usr = UserModel().create_or_update(
125 username=u'test_user',
125 username='test_user',
126 password=u'qweqwe',
126 password='qweqwe',
127 email=u'main_email@rhodecode.org',
127 email='main_email@rhodecode.org',
128 firstname=u'u1', lastname=u'u1')
128 firstname='u1', lastname=u'u1')
129 Session().commit()
129 Session().commit()
130 assert User.get_by_username(u'test_user') == usr
130 assert User.get_by_username(u'test_user') == usr
131
131
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -62,6 +62,9 b' use = egg:gunicorn#main'
62 ; The maximum number of simultaneous clients per worker. Valid only for gevent
62 ; The maximum number of simultaneous clients per worker. Valid only for gevent
63 #worker_connections = 10
63 #worker_connections = 10
64
64
65 ; The maximum number of pending connections worker will queue to handle
66 #backlog = 64
67
65 ; Max number of requests that worker will handle before being gracefully restarted.
68 ; Max number of requests that worker will handle before being gracefully restarted.
66 ; Prevents memory leaks, jitter adds variability so not all workers are restarted at once.
69 ; Prevents memory leaks, jitter adds variability so not all workers are restarted at once.
67 #max_requests = 1000
70 #max_requests = 1000
@@ -76,6 +79,40 b' use = egg:gunicorn#main'
76 ; 0 for unlimited
79 ; 0 for unlimited
77 #limit_request_line = 0
80 #limit_request_line = 0
78
81
82 ; Limit the number of HTTP headers fields in a request.
83 ; By default this value is 100 and can't be larger than 32768.
84 #limit_request_fields = 32768
85
86 ; Limit the allowed size of an HTTP request header field.
87 ; Value is a positive number or 0.
88 ; Setting it to 0 will allow unlimited header field sizes.
89 #limit_request_field_size = 0
90
91 ; Timeout for graceful workers restart.
92 ; After receiving a restart signal, workers have this much time to finish
93 ; serving requests. Workers still alive after the timeout (starting from the
94 ; receipt of the restart signal) are force killed.
95 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
96 #graceful_timeout = 3600
97
98 # The number of seconds to wait for requests on a Keep-Alive connection.
99 # Generally set in the 1-5 seconds range.
100 #keepalive = 2
101
102 ; Maximum memory usage that each worker can use before it will receive a
103 ; graceful restart signal 0 = memory monitoring is disabled
104 ; Examples: 268435456 (256MB), 536870912 (512MB)
105 ; 1073741824 (1GB), 2147483648 (2GB), 4294967296 (4GB)
106 #memory_max_usage = 0
107
108 ; How often in seconds to check for memory usage for each gunicorn worker
109 #memory_usage_check_interval = 60
110
111 ; Threshold value for which we don't recycle worker if GarbageCollection
112 ; frees up enough resources. Before each restart we try to run GC on worker
113 ; in case we get enough free memory after that, restart will not happen.
114 #memory_usage_recovery_threshold = 0.8
115
79
116
80 ; Prefix middleware for RhodeCode.
117 ; Prefix middleware for RhodeCode.
81 ; recommended when using proxy setup.
118 ; recommended when using proxy setup.
@@ -108,19 +145,9 b' use = egg:rhodecode-enterprise-ce'
108 ; enable proxy prefix middleware, defined above
145 ; enable proxy prefix middleware, defined above
109 #filter-with = proxy-prefix
146 #filter-with = proxy-prefix
110
147
111
148 ; encryption key used to encrypt social plugin tokens,
112 ## RHODECODE PLUGINS ##
149 ; remote_urls with credentials etc, if not set it defaults to
113 rhodecode.includes = rhodecode.api
150 ; `beaker.session.secret`
114
115 # api prefix url
116 rhodecode.api.url = /_admin/api
117
118
119 ## END RHODECODE PLUGINS ##
120
121 ## encryption key used to encrypt social plugin tokens,
122 ## remote_urls with credentials etc, if not set it defaults to
123 ## `beaker.session.secret`
124 #rhodecode.encrypted_values.secret =
151 #rhodecode.encrypted_values.secret =
125
152
126 ; decryption strict mode (enabled by default). It controls if decryption raises
153 ; decryption strict mode (enabled by default). It controls if decryption raises
@@ -146,12 +173,6 b' lang = en'
146 ; Settings this to true could lead to very long startup time.
173 ; Settings this to true could lead to very long startup time.
147 startup.import_repos = true
174 startup.import_repos = true
148
175
149 ; Uncomment and set this path to use archive download cache.
150 ; Once enabled, generated archives will be cached at this location
151 ; and served from the cache during subsequent requests for the same archive of
152 ; the repository.
153 #archive_cache_dir = /tmp/tarballcache
154
155 ; URL at which the application is running. This is used for Bootstrapping
176 ; URL at which the application is running. This is used for Bootstrapping
156 ; requests in context when no web request is available. Used in ishell, or
177 ; requests in context when no web request is available. Used in ishell, or
157 ; SSH calls. Set this for events to receive proper url for SSH calls.
178 ; SSH calls. Set this for events to receive proper url for SSH calls.
@@ -160,8 +181,14 b' app.base_url = http://rhodecode.local'
160 ; Unique application ID. Should be a random unique string for security.
181 ; Unique application ID. Should be a random unique string for security.
161 app_instance_uuid = rc-production
182 app_instance_uuid = rc-production
162
183
163 ## cut off limit for large diffs (size in bytes)
184 ; Cut off limit for large diffs (size in bytes). If overall diff size on
185 ; commit, or pull request exceeds this limit this diff will be displayed
186 ; partially. E.g 512000 == 512Kb
164 cut_off_limit_diff = 1024000
187 cut_off_limit_diff = 1024000
188
189 ; Cut off limit for large files inside diffs (size in bytes). Each individual
190 ; file inside diff which exceeds this limit will be displayed partially.
191 ; E.g 128000 == 128Kb
165 cut_off_limit_file = 256000
192 cut_off_limit_file = 256000
166
193
167 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
194 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
@@ -259,16 +286,20 b' allow_repo_location_change = true'
259 ; allows to setup custom hooks in settings page
286 ; allows to setup custom hooks in settings page
260 allow_custom_hooks_settings = true
287 allow_custom_hooks_settings = true
261
288
262 ## generated license token, goto license page in RhodeCode settings to obtain
289 ; Generated license token required for EE edition license.
263 ## new token
290 ; New generated token value can be found in Admin > settings > license page.
264 license_token = abra-cada-bra1-rce3
291 license_token = abra-cada-bra1-rce3
265
292
266 ## supervisor connection uri, for managing supervisor and logs.
293 ; This flag hides sensitive information on the license page such as token, and license data
294 license.hide_license_info = false
295
296 ; supervisor connection uri, for managing supervisor and logs.
267 supervisor.uri =
297 supervisor.uri =
268 ## supervisord group name/id we only want this RC instance to handle
298
299 ; supervisord group name/id we only want this RC instance to handle
269 supervisor.group_id = dev
300 supervisor.group_id = dev
270
301
271 ## Display extended labs settings
302 ; Display extended labs settings
272 labs_settings_active = true
303 labs_settings_active = true
273
304
274 ; Custom exception store path, defaults to TMPDIR
305 ; Custom exception store path, defaults to TMPDIR
@@ -295,6 +326,20 b' file_store.backend = local'
295 ; path to store the uploaded binaries
326 ; path to store the uploaded binaries
296 file_store.storage_path = %(here)s/data/file_store
327 file_store.storage_path = %(here)s/data/file_store
297
328
329 ; Uncomment and set this path to control settings for archive download cache.
330 ; Generated repo archives will be cached at this location
331 ; and served from the cache during subsequent requests for the same archive of
332 ; the repository. This path is important to be shared across filesystems and with
333 ; RhodeCode and vcsserver
334
335 ; Default is $cache_dir/archive_cache if not set
336 archive_cache.store_dir = /tmp/rc-test-data/archive_cache
337
338 ; The limit in GB sets how much data we cache before recycling last used, defaults to 10 gb
339 archive_cache.cache_size_gb = 10
340
341 ; By default cache uses sharding technique, this specifies how many shards are there
342 archive_cache.cache_shards = 10
298
343
299 ; #############
344 ; #############
300 ; CELERY CONFIG
345 ; CELERY CONFIG
@@ -325,35 +370,108 b' celery.task_always_eager = false'
325
370
326 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
371 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
327 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
372 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
328 cache_dir = %(here)s/data
373 cache_dir = %(here)s/rc-test-data
329
330 ## locking and default file storage for Beaker. Putting this into a ramdisk
331 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
332 beaker.cache.data_dir = %(here)s/rc/data/cache/beaker_data
333 beaker.cache.lock_dir = %(here)s/rc/data/cache/beaker_lock
334
335 beaker.cache.regions = long_term
336
337 beaker.cache.long_term.type = memory
338 beaker.cache.long_term.expire = 36000
339 beaker.cache.long_term.key_length = 256
340
374
341
375 ; *********************************************
342 #####################################
376 ; `sql_cache_short` cache for heavy SQL queries
343 ### DOGPILE CACHE ####
377 ; Only supported backend is `memory_lru`
344 #####################################
378 ; *********************************************
345
346 ## permission tree cache settings
347 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
348 rc_cache.cache_perms.expiration_time = 0
349 rc_cache.cache_perms.arguments.filename = /tmp/rc_cache_1
350
351
352 ## cache settings for SQL queries
353 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
379 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
354 rc_cache.sql_cache_short.expiration_time = 0
380 rc_cache.sql_cache_short.expiration_time = 0
355
381
356
382
383 ; *****************************************************
384 ; `cache_repo_longterm` cache for repo object instances
385 ; Only supported backend is `memory_lru`
386 ; *****************************************************
387 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
388 ; by default we use 30 Days, cache is still invalidated on push
389 rc_cache.cache_repo_longterm.expiration_time = 2592000
390 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
391 rc_cache.cache_repo_longterm.max_size = 10000
392
393
394 ; *********************************************
395 ; `cache_general` cache for general purpose use
396 ; for simplicity use rc.file_namespace backend,
397 ; for performance and scale use rc.redis
398 ; *********************************************
399 rc_cache.cache_general.backend = dogpile.cache.rc.file_namespace
400 rc_cache.cache_general.expiration_time = 43200
401 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
402 rc_cache.cache_general.arguments.filename = %(here)s/cache-backend/cache_general_db
403
404 ; alternative `cache_general` redis backend with distributed lock
405 #rc_cache.cache_general.backend = dogpile.cache.rc.redis
406 #rc_cache.cache_general.expiration_time = 300
407
408 ; redis_expiration_time needs to be greater then expiration_time
409 #rc_cache.cache_general.arguments.redis_expiration_time = 7200
410
411 #rc_cache.cache_general.arguments.host = localhost
412 #rc_cache.cache_general.arguments.port = 6379
413 #rc_cache.cache_general.arguments.db = 0
414 #rc_cache.cache_general.arguments.socket_timeout = 30
415 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
416 #rc_cache.cache_general.arguments.distributed_lock = true
417
418 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
419 #rc_cache.cache_general.arguments.lock_auto_renewal = true
420
421 ; *************************************************
422 ; `cache_perms` cache for permission tree, auth TTL
423 ; for simplicity use rc.file_namespace backend,
424 ; for performance and scale use rc.redis
425 ; *************************************************
426 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
427 rc_cache.cache_perms.expiration_time = 0
428 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
429 rc_cache.cache_perms.arguments.filename = %(here)s/cache-backend/cache_perms_db
430
431 ; alternative `cache_perms` redis backend with distributed lock
432 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
433 #rc_cache.cache_perms.expiration_time = 300
434
435 ; redis_expiration_time needs to be greater then expiration_time
436 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
437
438 #rc_cache.cache_perms.arguments.host = localhost
439 #rc_cache.cache_perms.arguments.port = 6379
440 #rc_cache.cache_perms.arguments.db = 0
441 #rc_cache.cache_perms.arguments.socket_timeout = 30
442 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
443 #rc_cache.cache_perms.arguments.distributed_lock = true
444
445 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
446 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
447
448 ; ***************************************************
449 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
450 ; for simplicity use rc.file_namespace backend,
451 ; for performance and scale use rc.redis
452 ; ***************************************************
453 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
454 rc_cache.cache_repo.expiration_time = 2592000
455 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
456 rc_cache.cache_repo.arguments.filename = %(here)s/cache-backend/cache_repo_db
457
458 ; alternative `cache_repo` redis backend with distributed lock
459 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
460 #rc_cache.cache_repo.expiration_time = 2592000
461
462 ; redis_expiration_time needs to be greater then expiration_time
463 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
464
465 #rc_cache.cache_repo.arguments.host = localhost
466 #rc_cache.cache_repo.arguments.port = 6379
467 #rc_cache.cache_repo.arguments.db = 1
468 #rc_cache.cache_repo.arguments.socket_timeout = 30
469 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
470 #rc_cache.cache_repo.arguments.distributed_lock = true
471
472 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
473 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
474
357 ; ##############
475 ; ##############
358 ; BEAKER SESSION
476 ; BEAKER SESSION
359 ; ##############
477 ; ##############
@@ -362,7 +480,7 b' rc_cache.sql_cache_short.expiration_time'
362 ; types are file, ext:redis, ext:database, ext:memcached, and memory (default if not specified).
480 ; types are file, ext:redis, ext:database, ext:memcached, and memory (default if not specified).
363 ; Fastest ones are Redis and ext:database
481 ; Fastest ones are Redis and ext:database
364 beaker.session.type = file
482 beaker.session.type = file
365 beaker.session.data_dir = %(here)s/rc/data/sessions/data
483 beaker.session.data_dir = %(here)s/rc-tests/data/sessions
366
484
367 ; Redis based sessions
485 ; Redis based sessions
368 #beaker.session.type = ext:redis
486 #beaker.session.type = ext:redis
@@ -378,7 +496,7 b' beaker.session.data_dir = %(here)s/rc/da'
378
496
379 beaker.session.key = rhodecode
497 beaker.session.key = rhodecode
380 beaker.session.secret = test-rc-uytcxaz
498 beaker.session.secret = test-rc-uytcxaz
381 beaker.session.lock_dir = %(here)s/rc/data/sessions/lock
499 beaker.session.lock_dir = %(here)s/data/sessions/lock
382
500
383 ; Secure encrypted cookie. Requires AES and AES python libraries
501 ; Secure encrypted cookie. Requires AES and AES python libraries
384 ; you must disable beaker.session.secret to use this
502 ; you must disable beaker.session.secret to use this
@@ -476,7 +594,8 b' sqlalchemy.db1.pool_recycle = 3600'
476 ; VCS CONFIG
594 ; VCS CONFIG
477 ; ##########
595 ; ##########
478 vcs.server.enable = true
596 vcs.server.enable = true
479 vcs.server = localhost:9901
597 #vcs.server = localhost:9901
598 vcs.server = vcsserver:10010
480
599
481 ; Web server connectivity protocol, responsible for web based VCS operations
600 ; Web server connectivity protocol, responsible for web based VCS operations
482 ; Available protocols are:
601 ; Available protocols are:
@@ -491,8 +610,9 b' vcs.scm_app_implementation = http'
491 ; `http` - use http-rpc backend (default)
610 ; `http` - use http-rpc backend (default)
492 vcs.hooks.protocol = http
611 vcs.hooks.protocol = http
493
612
494 ; Host on which this instance is listening for hooks. If vcsserver is in other location
613 ; Host on which this instance is listening for hooks. vcsserver will call this host to pull/push hooks so it should be
495 ; this should be adjusted.
614 ; accessible via network.
615 ; Use vcs.hooks.host = "*" to bind to current hostname (for Docker)
496 vcs.hooks.host = *
616 vcs.hooks.host = *
497
617
498 ; Start VCSServer with this instance as a subprocess, useful for development
618 ; Start VCSServer with this instance as a subprocess, useful for development
@@ -617,7 +737,7 b' custom.conf = 1'
617 ; #####################
737 ; #####################
618
738
619 [loggers]
739 [loggers]
620 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
740 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper, dogpile
621
741
622 [handlers]
742 [handlers]
623 keys = console, console_sql
743 keys = console, console_sql
@@ -651,6 +771,12 b' handlers ='
651 qualname = beaker.container
771 qualname = beaker.container
652 propagate = 1
772 propagate = 1
653
773
774 [logger_dogpile]
775 level = INFO
776 handlers = console
777 qualname = dogpile
778 propagate = 1
779
654 [logger_rhodecode]
780 [logger_rhodecode]
655 level = DEBUG
781 level = DEBUG
656 handlers =
782 handlers =
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -28,8 +27,12 b' watch -n1 ./rhodecode/tests/mem_watch'
28
27
29
28
30 import cookielib
29 import cookielib
31 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
32 import urllib.request, urllib.error, urllib.parse
31 import urllib.parse
32 import urllib.error
33 import urllib.request
34 import urllib.error
35 import urllib.parse
33 import time
36 import time
34 import os
37 import os
35 import sys
38 import sys
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -58,7 +57,7 b' def assert_no_running_instance(url):'
58 class ServerBase(object):
57 class ServerBase(object):
59 _args = []
58 _args = []
60 log_file_name = 'NOT_DEFINED.log'
59 log_file_name = 'NOT_DEFINED.log'
61 status_url_tmpl = 'http://{host}:{port}'
60 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
62
61
63 def __init__(self, config_file, log_file):
62 def __init__(self, config_file, log_file):
64 self.config_file = config_file
63 self.config_file = config_file
@@ -99,6 +98,10 b' class ServerBase(object):'
99 with open(self.log_file) as f:
98 with open(self.log_file) as f:
100 return f.read()
99 return f.read()
101
100
101 def assert_message_in_server_logs(self, message):
102 server_logs = self.get_rc_log()
103 assert message in server_logs
104
102 def wait_until_ready(self, timeout=30):
105 def wait_until_ready(self, timeout=30):
103 host = self._config['host']
106 host = self._config['host']
104 port = self._config['port']
107 port = self._config['port']
@@ -140,10 +143,15 b' class RcVCSServer(ServerBase):'
140 log_file_name = 'rc-vcsserver.log'
143 log_file_name = 'rc-vcsserver.log'
141 status_url_tmpl = 'http://{host}:{port}/status'
144 status_url_tmpl = 'http://{host}:{port}/status'
142
145
143 def __init__(self, config_file, log_file=None):
146 def __init__(self, config_file, log_file=None, workers='2'):
144 super(RcVCSServer, self).__init__(config_file, log_file)
147 super(RcVCSServer, self).__init__(config_file, log_file)
145 self._args = [
148 self._args = [
146 'gunicorn', '--bind', self.bind_addr,
149 'gunicorn',
150 '--bind', self.bind_addr,
151 '--worker-class', 'gevent',
152 '--backlog', '16',
153 '--timeout', '300',
154 '--workers', workers,
147 '--paste', self.config_file]
155 '--paste', self.config_file]
148
156
149 def start(self):
157 def start(self):
@@ -172,10 +180,15 b' class RcWebServer(ServerBase):'
172 log_file_name = 'rc-web.log'
180 log_file_name = 'rc-web.log'
173 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
181 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
174
182
175 def __init__(self, config_file, log_file=None):
183 def __init__(self, config_file, log_file=None, workers='1'):
176 super(RcWebServer, self).__init__(config_file, log_file)
184 super(RcWebServer, self).__init__(config_file, log_file)
177 self._args = [
185 self._args = [
178 'gunicorn', '--bind', self.bind_addr, '--worker-class', 'gevent',
186 'gunicorn',
187 '--bind', self.bind_addr,
188 '--worker-class', 'gevent',
189 '--backlog', '16',
190 '--timeout', '300',
191 '--workers', workers,
179 '--paste', self.config_file]
192 '--paste', self.config_file]
180
193
181 def start(self):
194 def start(self):
@@ -204,5 +217,5 b' class RcWebServer(ServerBase):'
204 'cloned_repo': repo_name,
217 'cloned_repo': repo_name,
205 }
218 }
206 params.update(**kwargs)
219 params.update(**kwargs)
207 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
220 _url = f"http://{params['user']}:{params['passwd']}@{params['host']}/{params['cloned_repo']}"
208 return _url
221 return _url
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -78,28 +77,25 b' class CustomTestResponse(TestResponse):'
78 else:
77 else:
79 no = []
78 no = []
80 if kw:
79 if kw:
81 raise TypeError(
80 raise TypeError(f"The only keyword argument allowed is 'no' got {kw}")
82 "The only keyword argument allowed is 'no' got %s" % kw)
83
81
84 f = self._save_output(str(self))
82 f = self._save_output(str(self))
85
83
86 for s in strings:
84 for s in strings:
87 if s not in self:
85 if s not in self:
88 print_stderr("Actual response (no %r):" % s)
86 print_stderr(f"Actual response (no {s!r}):")
89 print_stderr("body output saved as `%s`" % f)
87 print_stderr(f"body output saved as `{f}`")
90 if print_body:
88 if print_body:
91 print_stderr(str(self))
89 print_stderr(str(self))
92 raise IndexError(
90 raise IndexError(f"Body does not contain string {s!r}, body output saved as {f}")
93 "Body does not contain string %r, body output saved as %s" % (s, f))
94
91
95 for no_s in no:
92 for no_s in no:
96 if no_s in self:
93 if no_s in self:
97 print_stderr("Actual response (has %r)" % no_s)
94 print_stderr(f"Actual response (has {no_s!r})")
98 print_stderr("body output saved as `%s`" % f)
95 print_stderr(f"body output saved as `{f}`")
99 if print_body:
96 if print_body:
100 print_stderr(str(self))
97 print_stderr(str(self))
101 raise IndexError(
98 raise IndexError(f"Body contains bad string {no_s!r}, body output saved as {f}")
102 "Body contains bad string %r, body output saved as %s" % (no_s, f))
103
99
104 def assert_response(self):
100 def assert_response(self):
105 return AssertResponse(self)
101 return AssertResponse(self)
@@ -166,6 +162,10 b' class CustomTestApp(TestApp):'
166 def _pyramid_settings(self):
162 def _pyramid_settings(self):
167 return self._pyramid_registry.settings
163 return self._pyramid_registry.settings
168
164
165 def do_request(self, req, status=None, expect_errors=None):
166 # you can put custom code here
167 return super().do_request(req, status, expect_errors)
168
169
169
170 def set_anonymous_access(enabled):
170 def set_anonymous_access(enabled):
171 """(Dis)allows anonymous access depending on parameter `enabled`"""
171 """(Dis)allows anonymous access depending on parameter `enabled`"""
@@ -406,12 +406,12 b' def wait_for_url(url, timeout=10):'
406 pytest.fail(f"Timeout while waiting for URL {url}")
406 pytest.fail(f"Timeout while waiting for URL {url}")
407
407
408
408
409 def is_url_reachable(url: str, log_exc: bool = True) -> bool:
409 def is_url_reachable(url: str, log_exc: bool = False) -> bool:
410 try:
410 try:
411 urllib.request.urlopen(url)
411 urllib.request.urlopen(url)
412 except urllib.error.URLError:
412 except urllib.error.URLError:
413 if log_exc:
413 if log_exc:
414 log.exception('URL `{}` reach error'.format(url))
414 log.exception(f'URL `{url}` reach error')
415 return False
415 return False
416 return True
416 return True
417
417
@@ -425,7 +425,7 b' def repo_on_filesystem(repo_name):'
425
425
426
426
427 def commit_change(
427 def commit_change(
428 repo, filename, content, message, vcs_type, parent=None, newfile=False):
428 repo, filename: bytes, content: bytes, message, vcs_type, parent=None, newfile=False):
429 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
429 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
430
430
431 repo = Repository.get_by_repo_name(repo)
431 repo = Repository.get_by_repo_name(repo)
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -24,6 +23,7 b' import datetime'
24
23
25 import pytest
24 import pytest
26
25
26 from rhodecode.lib.str_utils import safe_bytes
27 from rhodecode.lib.vcs.backends import get_backend
27 from rhodecode.lib.vcs.backends import get_backend
28 from rhodecode.lib.vcs.backends.base import Config
28 from rhodecode.lib.vcs.backends.base import Config
29 from rhodecode.lib.vcs.nodes import FileNode
29 from rhodecode.lib.vcs.nodes import FileNode
@@ -137,17 +137,25 b' def _add_commits_to_repo(repo, commits):'
137
137
138 for commit in commits:
138 for commit in commits:
139 for node in commit.get('added', []):
139 for node in commit.get('added', []):
140 imc.add(FileNode(node.path, content=node.content))
140 if not isinstance(node, FileNode):
141 node = FileNode(safe_bytes(node.path), content=node.content)
142 imc.add(node)
143
141 for node in commit.get('changed', []):
144 for node in commit.get('changed', []):
142 imc.change(FileNode(node.path, content=node.content))
145 if not isinstance(node, FileNode):
146 node = FileNode(safe_bytes(node.path), content=node.content)
147 imc.change(node)
148
143 for node in commit.get('removed', []):
149 for node in commit.get('removed', []):
144 imc.remove(FileNode(node.path))
150 imc.remove(FileNode(safe_bytes(node.path)))
145
151
146 tip = imc.commit(
152 tip = imc.commit(
147 message=str(commit['message']),
153 message=str(commit['message']),
148 author=str(commit['author']),
154 author=str(commit['author']),
149 date=commit['date'],
155 date=commit['date'],
150 branch=commit.get('branch'))
156 branch=commit.get('branch')
157 )
158
151 return tip
159 return tip
152
160
153
161
@@ -167,7 +175,7 b' def vcs_repo(request, backend_alias):'
167 @pytest.fixture()
175 @pytest.fixture()
168 def generate_repo_with_commits(vcs_repo):
176 def generate_repo_with_commits(vcs_repo):
169 """
177 """
170 Creates a fabric to generate N comits with some file nodes on a randomly
178 Creates a fabric to generate N commits with some file nodes on a randomly
171 generated repository
179 generated repository
172 """
180 """
173
181
@@ -179,11 +187,11 b' def generate_repo_with_commits(vcs_repo)'
179 'author': 'Joe Doe <joe.doe@example.com>',
187 'author': 'Joe Doe <joe.doe@example.com>',
180 'date': start_date + datetime.timedelta(hours=12 * x),
188 'date': start_date + datetime.timedelta(hours=12 * x),
181 'added': [
189 'added': [
182 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
190 FileNode(b'file_%d.txt' % x, content=b'Foobar %d' % x),
183 ],
191 ],
184 'modified': [
192 'modified': [
185 FileNode('file_%d.txt' % x,
193 FileNode(b'file_%d.txt' % x,
186 content='Foobar %d modified' % (x-1)),
194 content=b'Foobar %d modified' % (x-1)),
187 ]
195 ]
188 }
196 }
189
197
@@ -229,24 +237,24 b' class BackendTestMixin(object):'
229 def _get_commits(cls):
237 def _get_commits(cls):
230 commits = [
238 commits = [
231 {
239 {
232 'message': u'Initial commit',
240 'message': 'Initial commit',
233 'author': u'Joe Doe <joe.doe@example.com>',
241 'author': 'Joe Doe <joe.doe@example.com>',
234 'date': datetime.datetime(2010, 1, 1, 20),
242 'date': datetime.datetime(2010, 1, 1, 20),
235 'added': [
243 'added': [
236 FileNode('foobar', content='Foobar'),
244 FileNode(b'foobar', content=b'Foobar'),
237 FileNode('foobar2', content='Foobar II'),
245 FileNode(b'foobar2', content=b'Foobar II'),
238 FileNode('foo/bar/baz', content='baz here!'),
246 FileNode(b'foo/bar/baz', content=b'baz here!'),
239 ],
247 ],
240 },
248 },
241 {
249 {
242 'message': u'Changes...',
250 'message': 'Changes...',
243 'author': u'Jane Doe <jane.doe@example.com>',
251 'author': 'Jane Doe <jane.doe@example.com>',
244 'date': datetime.datetime(2010, 1, 1, 21),
252 'date': datetime.datetime(2010, 1, 1, 21),
245 'added': [
253 'added': [
246 FileNode('some/new.txt', content='news...'),
254 FileNode(b'some/new.txt', content=b'news...'),
247 ],
255 ],
248 'changed': [
256 'changed': [
249 FileNode('foobar', 'Foobar I'),
257 FileNode(b'foobar', b'Foobar I'),
250 ],
258 ],
251 'removed': [],
259 'removed': [],
252 },
260 },
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -22,30 +21,29 b' import datetime'
22 import os
21 import os
23 import shutil
22 import shutil
24 import tarfile
23 import tarfile
25 import tempfile
26 import zipfile
24 import zipfile
27 import io
25 import io
28
26
29 import mock
27 import mock
30 import pytest
28 import pytest
31
29
30 import rhodecode
31 from rhodecode.lib.rc_cache.archive_cache import get_archival_config
32 from rhodecode.lib.str_utils import ascii_bytes
32 from rhodecode.lib.vcs.backends import base
33 from rhodecode.lib.vcs.backends import base
33 from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError, VCSError
34 from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError, VCSError
34 from rhodecode.lib.vcs.nodes import FileNode
35 from rhodecode.lib.vcs.nodes import FileNode
35 from rhodecode.tests.vcs.conftest import BackendTestMixin
36 from rhodecode.tests.vcs.conftest import BackendTestMixin
36
37
37
38
39 @pytest.fixture()
40 def d_cache_config():
41 return get_archival_config(config=rhodecode.CONFIG)
42
43
38 @pytest.mark.usefixtures("vcs_repository_support")
44 @pytest.mark.usefixtures("vcs_repository_support")
39 class TestArchives(BackendTestMixin):
45 class TestArchives(BackendTestMixin):
40
46
41 @pytest.fixture(autouse=True)
42 def tempfile(self, request):
43 self.temp_file = tempfile.mkstemp()[1]
44
45 @request.addfinalizer
46 def cleanup():
47 os.remove(self.temp_file)
48
49 @classmethod
47 @classmethod
50 def _get_commits(cls):
48 def _get_commits(cls):
51 start_date = datetime.datetime(2010, 1, 1, 20)
49 start_date = datetime.datetime(2010, 1, 1, 20)
@@ -54,9 +52,9 b' class TestArchives(BackendTestMixin):'
54 'author': 'Joe Doe <joe.doe@example.com>',
52 'author': 'Joe Doe <joe.doe@example.com>',
55 'date': start_date + datetime.timedelta(hours=12),
53 'date': start_date + datetime.timedelta(hours=12),
56 'added': [
54 'added': [
57 FileNode('executable_0o100755', '...', mode=0o100755),
55 FileNode(b'executable_0o100755', b'mode_755', mode=0o100755),
58 FileNode('executable_0o100500', '...', mode=0o100500),
56 FileNode(b'executable_0o100500', b'mode_500', mode=0o100500),
59 FileNode('not_executable', '...', mode=0o100644),
57 FileNode(b'not_executable', b'mode_644', mode=0o100644),
60 ],
58 ],
61 }
59 }
62 for x in range(5):
60 for x in range(5):
@@ -65,22 +63,27 b' class TestArchives(BackendTestMixin):'
65 'author': 'Joe Doe <joe.doe@example.com>',
63 'author': 'Joe Doe <joe.doe@example.com>',
66 'date': start_date + datetime.timedelta(hours=12 * x),
64 'date': start_date + datetime.timedelta(hours=12 * x),
67 'added': [
65 'added': [
68 FileNode('%d/file_%d.txt' % (x, x), content='Foobar %d' % x),
66 FileNode(b'%d/file_%d.txt' % (x, x), content=b'Foobar %d' % x),
69 ],
67 ],
70 }
68 }
71
69
72 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
70 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
73 def test_archive_tar(self, compressor):
71 def test_archive_tar(self, compressor, tmpdir, tmp_path, d_cache_config):
74 self.tip.archive_repo(
72
75 self.temp_file, kind='t{}'.format(compressor), archive_dir_name='repo')
73 archive_node = tmp_path / 'archive-node'
76 out_dir = tempfile.mkdtemp()
74 archive_node.touch()
77 out_file = tarfile.open(self.temp_file, 'r|{}'.format(compressor))
75
76 archive_lnk = self.tip.archive_repo(
77 str(archive_node), kind=f't{compressor}', archive_dir_name='repo', cache_config=d_cache_config)
78
79 out_dir = tmpdir
80 out_file = tarfile.open(str(archive_lnk), f'r|{compressor}')
78 out_file.extractall(out_dir)
81 out_file.extractall(out_dir)
79 out_file.close()
82 out_file.close()
80
83
81 for x in range(5):
84 for x in range(5):
82 node_path = '%d/file_%d.txt' % (x, x)
85 node_path = '%d/file_%d.txt' % (x, x)
83 with open(os.path.join(out_dir, 'repo/' + node_path)) as f:
86 with open(os.path.join(out_dir, 'repo/' + node_path), 'rb') as f:
84 file_content = f.read()
87 file_content = f.read()
85 assert file_content == self.tip.get_node(node_path).content
88 assert file_content == self.tip.get_node(node_path).content
86
89
@@ -88,53 +91,72 b' class TestArchives(BackendTestMixin):'
88
91
89 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
92 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
90 def test_archive_tar_symlink(self, compressor):
93 def test_archive_tar_symlink(self, compressor):
91 return False
94 pytest.skip('Not supported')
92
95
93 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
96 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
94 def test_archive_tar_file_modes(self, compressor):
97 def test_archive_tar_file_modes(self, compressor, tmpdir, tmp_path, d_cache_config):
95 self.tip.archive_repo(
98 archive_node = tmp_path / 'archive-node'
96 self.temp_file, kind='t{}'.format(compressor), archive_dir_name='repo')
99 archive_node.touch()
97 out_dir = tempfile.mkdtemp()
100
98 out_file = tarfile.open(self.temp_file, 'r|{}'.format(compressor))
101 archive_lnk = self.tip.archive_repo(
102 str(archive_node), kind='t{}'.format(compressor), archive_dir_name='repo', cache_config=d_cache_config)
103
104 out_dir = tmpdir
105 out_file = tarfile.open(str(archive_lnk), 'r|{}'.format(compressor))
99 out_file.extractall(out_dir)
106 out_file.extractall(out_dir)
100 out_file.close()
107 out_file.close()
101 dest = lambda inp: os.path.join(out_dir, 'repo/' + inp)
102
108
103 assert oct(os.stat(dest('not_executable')).st_mode) == '0100644'
109 def dest(inp):
110 return os.path.join(out_dir, "repo/" + inp)
111
112 assert oct(os.stat(dest('not_executable')).st_mode) == '0o100644'
104
113
105 def test_archive_zip(self):
114 def test_archive_zip(self, tmp_path, d_cache_config):
106 self.tip.archive_repo(self.temp_file, kind='zip', archive_dir_name='repo')
115 archive_node = tmp_path / 'archive-node'
107 out = zipfile.ZipFile(self.temp_file)
116 archive_node.touch()
117
118 archive_lnk = self.tip.archive_repo(str(archive_node), kind='zip',
119 archive_dir_name='repo', cache_config=d_cache_config)
120 zip_file = zipfile.ZipFile(str(archive_lnk))
108
121
109 for x in range(5):
122 for x in range(5):
110 node_path = '%d/file_%d.txt' % (x, x)
123 node_path = '%d/file_%d.txt' % (x, x)
111 decompressed = io.StringIO()
124 data = zip_file.read(f'repo/{node_path}')
112 decompressed.write(out.read('repo/' + node_path))
125
126 decompressed = io.BytesIO()
127 decompressed.write(data)
113 assert decompressed.getvalue() == \
128 assert decompressed.getvalue() == \
114 self.tip.get_node(node_path).content
129 self.tip.get_node(node_path).content
115 decompressed.close()
130 decompressed.close()
116
131
117 def test_archive_zip_with_metadata(self):
132 def test_archive_zip_with_metadata(self, tmp_path, d_cache_config):
118 self.tip.archive_repo(self.temp_file, kind='zip',
133 archive_node = tmp_path / 'archive-node'
119 archive_dir_name='repo', write_metadata=True)
134 archive_node.touch()
120
135
121 out = zipfile.ZipFile(self.temp_file)
136 archive_lnk = self.tip.archive_repo(str(archive_node), kind='zip',
122 metafile = out.read('repo/.archival.txt')
137 archive_dir_name='repo', write_metadata=True, cache_config=d_cache_config)
123
138
124 raw_id = self.tip.raw_id
139 zip_file = zipfile.ZipFile(str(archive_lnk))
125 assert 'commit_id:%s' % raw_id in metafile
140 metafile = zip_file.read('repo/.archival.txt')
141
142 raw_id = ascii_bytes(self.tip.raw_id)
143 assert b'commit_id:%b' % raw_id in metafile
126
144
127 for x in range(5):
145 for x in range(5):
128 node_path = '%d/file_%d.txt' % (x, x)
146 node_path = '%d/file_%d.txt' % (x, x)
129 decompressed = io.StringIO()
147 data = zip_file.read(f'repo/{node_path}')
130 decompressed.write(out.read('repo/' + node_path))
148 decompressed = io.BytesIO()
149 decompressed.write(data)
131 assert decompressed.getvalue() == \
150 assert decompressed.getvalue() == \
132 self.tip.get_node(node_path).content
151 self.tip.get_node(node_path).content
133 decompressed.close()
152 decompressed.close()
134
153
135 def test_archive_wrong_kind(self):
154 def test_archive_wrong_kind(self, tmp_path, d_cache_config):
155 archive_node = tmp_path / 'archive-node'
156 archive_node.touch()
157
136 with pytest.raises(ImproperArchiveTypeError):
158 with pytest.raises(ImproperArchiveTypeError):
137 self.tip.archive_repo(self.temp_file, kind='wrong kind')
159 self.tip.archive_repo(str(archive_node), kind='wrong kind', cache_config=d_cache_config)
138
160
139
161
140 @pytest.fixture()
162 @pytest.fixture()
@@ -144,15 +166,14 b' def base_commit():'
144 """
166 """
145 commit = base.BaseCommit()
167 commit = base.BaseCommit()
146 commit.repository = mock.Mock()
168 commit.repository = mock.Mock()
147 commit.repository.name = u'fake_repo'
169 commit.repository.name = 'fake_repo'
148 commit.short_id = 'fake_id'
170 commit.short_id = 'fake_id'
149 return commit
171 return commit
150
172
151
173
152 @pytest.mark.parametrize("prefix", [u"unicode-prefix", u"Ünïcâdë"])
174 def test_validate_archive_prefix_enforces_non_ascii_as_prefix(base_commit):
153 def test_validate_archive_prefix_enforces_bytes_as_prefix(prefix, base_commit):
175 with pytest.raises(VCSError):
154 with pytest.raises(ValueError):
176 base_commit._validate_archive_prefix("Ünïcâdë")
155 base_commit._validate_archive_prefix(prefix)
156
177
157
178
158 def test_validate_archive_prefix_empty_prefix(base_commit):
179 def test_validate_archive_prefix_empty_prefix(base_commit):
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -54,9 +53,9 b' class TestBranches(BackendTestMixin):'
54 # This check must not be removed to ensure the 'branches' LazyProperty
53 # This check must not be removed to ensure the 'branches' LazyProperty
55 # gets hit *before* the new 'foobar' branch got created:
54 # gets hit *before* the new 'foobar' branch got created:
56 assert 'foobar' not in self.repo.branches
55 assert 'foobar' not in self.repo.branches
57 self.imc.add(FileNode(
56 self.imc.add(
58 'docs/index.txt',
57 FileNode(b'docs/index.txt', content=b'Documentation\n')
59 content='Documentation\n'))
58 )
60 foobar_tip = self.imc.commit(
59 foobar_tip = self.imc.commit(
61 message=u'New branch: foobar',
60 message=u'New branch: foobar',
62 author=u'joe <joe@rhodecode.com>',
61 author=u'joe <joe@rhodecode.com>',
@@ -68,9 +67,10 b' class TestBranches(BackendTestMixin):'
68 @pytest.mark.backends("git", "hg")
67 @pytest.mark.backends("git", "hg")
69 def test_new_head(self):
68 def test_new_head(self):
70 tip = self.repo.get_commit()
69 tip = self.repo.get_commit()
71 self.imc.add(FileNode(
70 self.imc.add(
72 'docs/index.txt',
71 FileNode(b'docs/index.txt',
73 content='Documentation\n'))
72 content=b'Documentation\n')
73 )
74 foobar_tip = self.imc.commit(
74 foobar_tip = self.imc.commit(
75 message=u'New branch: foobar',
75 message=u'New branch: foobar',
76 author=u'joe <joe@rhodecode.com>',
76 author=u'joe <joe@rhodecode.com>',
@@ -78,8 +78,8 b' class TestBranches(BackendTestMixin):'
78 parents=[tip],
78 parents=[tip],
79 )
79 )
80 self.imc.change(FileNode(
80 self.imc.change(FileNode(
81 'docs/index.txt',
81 b'docs/index.txt',
82 content='Documentation\nand more...\n'))
82 content=b'Documentation\nand more...\n'))
83 newtip = self.imc.commit(
83 newtip = self.imc.commit(
84 message=u'At default branch',
84 message=u'At default branch',
85 author=u'joe <joe@rhodecode.com>',
85 author=u'joe <joe@rhodecode.com>',
@@ -99,7 +99,7 b' class TestBranches(BackendTestMixin):'
99
99
100 @pytest.mark.backends("git", "hg")
100 @pytest.mark.backends("git", "hg")
101 def test_branch_with_slash_in_name(self):
101 def test_branch_with_slash_in_name(self):
102 self.imc.add(FileNode('extrafile', content='Some data\n'))
102 self.imc.add(FileNode(b'extrafile', content=b'Some data\n'))
103 self.imc.commit(
103 self.imc.commit(
104 u'Branch with a slash!', author=u'joe <joe@rhodecode.com>',
104 u'Branch with a slash!', author=u'joe <joe@rhodecode.com>',
105 branch='issue/123')
105 branch='issue/123')
@@ -107,11 +107,11 b' class TestBranches(BackendTestMixin):'
107
107
108 @pytest.mark.backends("git", "hg")
108 @pytest.mark.backends("git", "hg")
109 def test_branch_with_slash_in_name_and_similar_without(self):
109 def test_branch_with_slash_in_name_and_similar_without(self):
110 self.imc.add(FileNode('extrafile', content='Some data\n'))
110 self.imc.add(FileNode(b'extrafile', content=b'Some data\n'))
111 self.imc.commit(
111 self.imc.commit(
112 u'Branch with a slash!', author=u'joe <joe@rhodecode.com>',
112 u'Branch with a slash!', author=u'joe <joe@rhodecode.com>',
113 branch='issue/123')
113 branch='issue/123')
114 self.imc.add(FileNode('extrafile II', content='Some data\n'))
114 self.imc.add(FileNode(b'extrafile II', content=b'Some data\n'))
115 self.imc.commit(
115 self.imc.commit(
116 u'Branch without a slash...', author=u'joe <joe@rhodecode.com>',
116 u'Branch without a slash...', author=u'joe <joe@rhodecode.com>',
117 branch='123')
117 branch='123')
@@ -138,10 +138,10 b' class TestSvnBranches(object):'
138 'branches/argparse',
138 'branches/argparse',
139 'trunk',
139 'trunk',
140 ]
140 ]
141 assert repo.branches.keys() == expected_branches
141 assert list(repo.branches.keys()) == expected_branches
142
142
143 def test_discovers_ordered_tags(self, vcsbackend_svn):
143 def test_discovers_ordered_tags(self, vcsbackend_svn):
144 repo = vcsbackend_svn['svn-simple-layout']
144 repo = vcsbackend_svn['svn-simple-layout']
145 expected_tags = [
145 expected_tags = [
146 'tags/v0.1', 'tags/v0.2', 'tags/v0.3', 'tags/v0.5']
146 'tags/v0.1', 'tags/v0.2', 'tags/v0.3', 'tags/v0.5']
147 assert repo.tags.keys() == expected_tags
147 assert list(repo.tags.keys()) == expected_tags
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -23,6 +22,7 b' import time'
23
22
24 import pytest
23 import pytest
25
24
25 from rhodecode.lib.str_utils import safe_bytes
26 from rhodecode.lib.vcs.backends.base import (
26 from rhodecode.lib.vcs.backends.base import (
27 CollectionGenerator, FILEMODE_DEFAULT, EmptyCommit)
27 CollectionGenerator, FILEMODE_DEFAULT, EmptyCommit)
28 from rhodecode.lib.vcs.exceptions import (
28 from rhodecode.lib.vcs.exceptions import (
@@ -62,7 +62,8 b' class TestCommitsInNonEmptyRepo(BackendT'
62 'author': 'Joe Doe <joe.doe@example.com>',
62 'author': 'Joe Doe <joe.doe@example.com>',
63 'date': start_date + datetime.timedelta(hours=12 * x),
63 'date': start_date + datetime.timedelta(hours=12 * x),
64 'added': [
64 'added': [
65 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
65 FileNode(b'file_%d.txt' % x,
66 content=b'Foobar %d' % x),
66 ],
67 ],
67 }
68 }
68
69
@@ -72,42 +73,39 b' class TestCommitsInNonEmptyRepo(BackendT'
72
73
73 @pytest.mark.backends("git", "hg")
74 @pytest.mark.backends("git", "hg")
74 def test_new_branch(self):
75 def test_new_branch(self):
75 self.imc.add(FileNode('docs/index.txt',
76 self.imc.add(FileNode(b'docs/index.txt', content=b'Documentation\n'))
76 content='Documentation\n'))
77 foobar_tip = self.imc.commit(
77 foobar_tip = self.imc.commit(
78 message=u'New branch: foobar',
78 message='New branch: foobar',
79 author=u'joe <joe@rhodecode.com>',
79 author='joe <joe@rhodecode.com>',
80 branch='foobar',
80 branch='foobar',
81 )
81 )
82 assert 'foobar' in self.repo.branches
82 assert 'foobar' in self.repo.branches
83 assert foobar_tip.branch == 'foobar'
83 assert foobar_tip.branch == 'foobar'
84 # 'foobar' should be the only branch that contains the new commit
84 # 'foobar' should be the only branch that contains the new commit
85 branch = self.repo.branches.values()
85 branch = list(self.repo.branches.values())
86 assert branch[0] != branch[1]
86 assert branch[0] != branch[1]
87
87
88 @pytest.mark.backends("git", "hg")
88 @pytest.mark.backends("git", "hg")
89 def test_new_head_in_default_branch(self):
89 def test_new_head_in_default_branch(self):
90 tip = self.repo.get_commit()
90 tip = self.repo.get_commit()
91 self.imc.add(FileNode('docs/index.txt',
91 self.imc.add(FileNode(b'docs/index.txt', content=b'Documentation\n'))
92 content='Documentation\n'))
93 foobar_tip = self.imc.commit(
92 foobar_tip = self.imc.commit(
94 message=u'New branch: foobar',
93 message='New branch: foobar',
95 author=u'joe <joe@rhodecode.com>',
94 author='joe <joe@rhodecode.com>',
96 branch='foobar',
95 branch='foobar',
97 parents=[tip],
96 parents=[tip],
98 )
97 )
99 self.imc.change(FileNode('docs/index.txt',
98 self.imc.change(FileNode(b'docs/index.txt', content=b'Documentation\nand more...\n'))
100 content='Documentation\nand more...\n'))
101 newtip = self.imc.commit(
99 newtip = self.imc.commit(
102 message=u'At default branch',
100 message='At default branch',
103 author=u'joe <joe@rhodecode.com>',
101 author='joe <joe@rhodecode.com>',
104 branch=foobar_tip.branch,
102 branch=foobar_tip.branch,
105 parents=[foobar_tip],
103 parents=[foobar_tip],
106 )
104 )
107
105
108 newest_tip = self.imc.commit(
106 newest_tip = self.imc.commit(
109 message=u'Merged with %s' % foobar_tip.raw_id,
107 message='Merged with %s' % foobar_tip.raw_id,
110 author=u'joe <joe@rhodecode.com>',
108 author='joe <joe@rhodecode.com>',
111 branch=self.backend_class.DEFAULT_BRANCH_NAME,
109 branch=self.backend_class.DEFAULT_BRANCH_NAME,
112 parents=[newtip, foobar_tip],
110 parents=[newtip, foobar_tip],
113 )
111 )
@@ -131,31 +129,31 b' class TestCommitsInNonEmptyRepo(BackendT'
131 TEST_BRANCH = 'docs'
129 TEST_BRANCH = 'docs'
132 org_tip = self.repo.get_commit()
130 org_tip = self.repo.get_commit()
133
131
134 self.imc.add(FileNode('readme.txt', content='Document\n'))
132 self.imc.add(FileNode(b'readme.txt', content=b'Document\n'))
135 initial = self.imc.commit(
133 initial = self.imc.commit(
136 message=u'Initial commit',
134 message='Initial commit',
137 author=u'joe <joe@rhodecode.com>',
135 author='joe <joe@rhodecode.com>',
138 parents=[org_tip],
136 parents=[org_tip],
139 branch=DEFAULT_BRANCH,)
137 branch=DEFAULT_BRANCH,)
140
138
141 self.imc.add(FileNode('newdoc.txt', content='foobar\n'))
139 self.imc.add(FileNode(b'newdoc.txt', content=b'foobar\n'))
142 docs_branch_commit1 = self.imc.commit(
140 docs_branch_commit1 = self.imc.commit(
143 message=u'New branch: docs',
141 message='New branch: docs',
144 author=u'joe <joe@rhodecode.com>',
142 author='joe <joe@rhodecode.com>',
145 parents=[initial],
143 parents=[initial],
146 branch=TEST_BRANCH,)
144 branch=TEST_BRANCH,)
147
145
148 self.imc.add(FileNode('newdoc2.txt', content='foobar2\n'))
146 self.imc.add(FileNode(b'newdoc2.txt', content=b'foobar2\n'))
149 docs_branch_commit2 = self.imc.commit(
147 docs_branch_commit2 = self.imc.commit(
150 message=u'New branch: docs2',
148 message='New branch: docs2',
151 author=u'joe <joe@rhodecode.com>',
149 author='joe <joe@rhodecode.com>',
152 parents=[docs_branch_commit1],
150 parents=[docs_branch_commit1],
153 branch=TEST_BRANCH,)
151 branch=TEST_BRANCH,)
154
152
155 self.imc.add(FileNode('newfile', content='hello world\n'))
153 self.imc.add(FileNode(b'newfile', content=b'hello world\n'))
156 self.imc.commit(
154 self.imc.commit(
157 message=u'Back in default branch',
155 message='Back in default branch',
158 author=u'joe <joe@rhodecode.com>',
156 author='joe <joe@rhodecode.com>',
159 parents=[initial],
157 parents=[initial],
160 branch=DEFAULT_BRANCH,)
158 branch=DEFAULT_BRANCH,)
161
159
@@ -210,11 +208,12 b' class TestCommits(BackendTestMixin):'
210 start_date = datetime.datetime(2010, 1, 1, 20)
208 start_date = datetime.datetime(2010, 1, 1, 20)
211 for x in range(5):
209 for x in range(5):
212 yield {
210 yield {
213 'message': u'Commit %d' % x,
211 'message': 'Commit %d' % x,
214 'author': u'Joe Doe <joe.doe@example.com>',
212 'author': 'Joe Doe <joe.doe@example.com>',
215 'date': start_date + datetime.timedelta(hours=12 * x),
213 'date': start_date + datetime.timedelta(hours=12 * x),
216 'added': [
214 'added': [
217 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
215 FileNode(b'file_%d.txt' % x,
216 content=b'Foobar %d' % x)
218 ],
217 ],
219 }
218 }
220
219
@@ -269,24 +268,24 b' class TestCommits(BackendTestMixin):'
269
268
270 def test_author(self):
269 def test_author(self):
271 tip = self.repo.get_commit()
270 tip = self.repo.get_commit()
272 assert_text_equal(tip.author, u'Joe Doe <joe.doe@example.com>')
271 assert_text_equal(tip.author, 'Joe Doe <joe.doe@example.com>')
273
272
274 def test_author_name(self):
273 def test_author_name(self):
275 tip = self.repo.get_commit()
274 tip = self.repo.get_commit()
276 assert_text_equal(tip.author_name, u'Joe Doe')
275 assert_text_equal(tip.author_name, 'Joe Doe')
277
276
278 def test_author_email(self):
277 def test_author_email(self):
279 tip = self.repo.get_commit()
278 tip = self.repo.get_commit()
280 assert_text_equal(tip.author_email, u'joe.doe@example.com')
279 assert_text_equal(tip.author_email, 'joe.doe@example.com')
281
280
282 def test_message(self):
281 def test_message(self):
283 tip = self.repo.get_commit()
282 tip = self.repo.get_commit()
284 assert_text_equal(tip.message, u'Commit 4')
283 assert_text_equal(tip.message, 'Commit 4')
285
284
286 def test_diff(self):
285 def test_diff(self):
287 tip = self.repo.get_commit()
286 tip = self.repo.get_commit()
288 diff = tip.diff()
287 diff = tip.diff()
289 assert "+Foobar 4" in diff.raw
288 assert b"+Foobar 4" in diff.raw.tobytes()
290
289
291 def test_prev(self):
290 def test_prev(self):
292 tip = self.repo.get_commit()
291 tip = self.repo.get_commit()
@@ -490,8 +489,8 b' class TestCommits(BackendTestMixin):'
490 assert commit2 == commit2
489 assert commit2 == commit2
491 assert commit1 != commit2
490 assert commit1 != commit2
492 assert commit2 != commit1
491 assert commit2 != commit1
493 assert commit1 != None
492 assert commit1 is not None
494 assert None != commit1
493 assert commit2 is not None
495 assert 1 != commit1
494 assert 1 != commit1
496 assert 'string' != commit1
495 assert 'string' != commit1
497
496
@@ -514,53 +513,49 b' class TestCommitsChanges(BackendTestMixi'
514 def _get_commits(cls):
513 def _get_commits(cls):
515 return [
514 return [
516 {
515 {
517 'message': u'Initial',
516 'message': 'Initial',
518 'author': u'Joe Doe <joe.doe@example.com>',
517 'author': 'Joe Doe <joe.doe@example.com>',
519 'date': datetime.datetime(2010, 1, 1, 20),
518 'date': datetime.datetime(2010, 1, 1, 20),
520 'added': [
519 'added': [
521 FileNode('foo/bar', content='foo'),
520 FileNode(b'foo/bar', content=b'foo'),
522 FileNode('foo/baΕ‚', content='foo'),
521 FileNode(safe_bytes('foo/baΕ‚'), content=b'foo'),
523 FileNode('foobar', content='foo'),
522 FileNode(b'foobar', content=b'foo'),
524 FileNode('qwe', content='foo'),
523 FileNode(b'qwe', content=b'foo'),
525 ],
524 ],
526 },
525 },
527 {
526 {
528 'message': u'Massive changes',
527 'message': 'Massive changes',
529 'author': u'Joe Doe <joe.doe@example.com>',
528 'author': 'Joe Doe <joe.doe@example.com>',
530 'date': datetime.datetime(2010, 1, 1, 22),
529 'date': datetime.datetime(2010, 1, 1, 22),
531 'added': [FileNode('fallout', content='War never changes')],
530 'added': [FileNode(b'fallout', content=b'War never changes')],
532 'changed': [
531 'changed': [
533 FileNode('foo/bar', content='baz'),
532 FileNode(b'foo/bar', content=b'baz'),
534 FileNode('foobar', content='baz'),
533 FileNode(b'foobar', content=b'baz'),
535 ],
534 ],
536 'removed': [FileNode('qwe')],
535 'removed': [FileNode(b'qwe')],
537 },
536 },
538 ]
537 ]
539
538
540 def test_initial_commit(self, local_dt_to_utc):
539 def test_initial_commit(self, local_dt_to_utc):
541 commit = self.repo.get_commit(commit_idx=0)
540 commit = self.repo.get_commit(commit_idx=0)
542 assert set(commit.added) == set([
541 assert set(commit.added) == {
543 commit.get_node('foo/bar'),
542 commit.get_node('foo/bar'),
544 commit.get_node('foo/baΕ‚'),
543 commit.get_node('foo/baΕ‚'),
545 commit.get_node('foobar'),
544 commit.get_node('foobar'),
546 commit.get_node('qwe'),
545 commit.get_node('qwe')
547 ])
546 }
548 assert set(commit.changed) == set()
547 assert set(commit.changed) == set()
549 assert set(commit.removed) == set()
548 assert set(commit.removed) == set()
550 assert set(commit.affected_files) == set(
549 assert set(commit.affected_files) == {'foo/bar', 'foo/baΕ‚', 'foobar', 'qwe'}
551 ['foo/bar', 'foo/baΕ‚', 'foobar', 'qwe'])
552 assert commit.date == local_dt_to_utc(
550 assert commit.date == local_dt_to_utc(
553 datetime.datetime(2010, 1, 1, 20, 0))
551 datetime.datetime(2010, 1, 1, 20, 0))
554
552
555 def test_head_added(self):
553 def test_head_added(self):
556 commit = self.repo.get_commit()
554 commit = self.repo.get_commit()
557 assert isinstance(commit.added, AddedFileNodesGenerator)
555 assert isinstance(commit.added, AddedFileNodesGenerator)
558 assert set(commit.added) == set([commit.get_node('fallout')])
556 assert set(commit.added) == {commit.get_node('fallout')}
559 assert isinstance(commit.changed, ChangedFileNodesGenerator)
557 assert isinstance(commit.changed, ChangedFileNodesGenerator)
560 assert set(commit.changed) == set([
558 assert set(commit.changed) == {commit.get_node('foo/bar'), commit.get_node('foobar')}
561 commit.get_node('foo/bar'),
562 commit.get_node('foobar'),
563 ])
564 assert isinstance(commit.removed, RemovedFileNodesGenerator)
559 assert isinstance(commit.removed, RemovedFileNodesGenerator)
565 assert len(commit.removed) == 1
560 assert len(commit.removed) == 1
566 assert list(commit.removed)[0].path == 'qwe'
561 assert list(commit.removed)[0].path == 'qwe'
@@ -572,7 +567,7 b' class TestCommitsChanges(BackendTestMixi'
572 def test_get_filemode_non_ascii(self):
567 def test_get_filemode_non_ascii(self):
573 commit = self.repo.get_commit()
568 commit = self.repo.get_commit()
574 assert FILEMODE_DEFAULT == commit.get_file_mode('foo/baΕ‚')
569 assert FILEMODE_DEFAULT == commit.get_file_mode('foo/baΕ‚')
575 assert FILEMODE_DEFAULT == commit.get_file_mode(u'foo/baΕ‚')
570 assert FILEMODE_DEFAULT == commit.get_file_mode('foo/baΕ‚')
576
571
577 def test_get_path_history(self):
572 def test_get_path_history(self):
578 commit = self.repo.get_commit()
573 commit = self.repo.get_commit()
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -34,6 +33,7 b' class TestGetDiffValidation:'
34
33
35 def test_raises_if_commits_not_of_this_repository(self, vcsbackend):
34 def test_raises_if_commits_not_of_this_repository(self, vcsbackend):
36 repo = vcsbackend.repo
35 repo = vcsbackend.repo
36
37 target_repo = vcsbackend.create_repo(number_of_commits=1)
37 target_repo = vcsbackend.create_repo(number_of_commits=1)
38 repo_commit = repo[0]
38 repo_commit = repo[0]
39 wrong_commit = target_repo[0]
39 wrong_commit = target_repo[0]
@@ -81,8 +81,8 b' class TestRepositoryGetDiff(BackendTestM'
81 'author': 'Joe Doe <joe.doe@example.com>',
81 'author': 'Joe Doe <joe.doe@example.com>',
82 'date': datetime.datetime(2010, 1, 1, 20),
82 'date': datetime.datetime(2010, 1, 1, 20),
83 'added': [
83 'added': [
84 FileNode('foobar', content='foobar'),
84 FileNode(b'foobar', content=b'foobar'),
85 FileNode('foobar2', content='foobar2'),
85 FileNode(b'foobar2', content=b'foobar2'),
86 ],
86 ],
87 },
87 },
88 {
88 {
@@ -90,10 +90,10 b' class TestRepositoryGetDiff(BackendTestM'
90 'author': 'Jane Doe <jane.doe@example.com>',
90 'author': 'Jane Doe <jane.doe@example.com>',
91 'date': datetime.datetime(2010, 1, 1, 21),
91 'date': datetime.datetime(2010, 1, 1, 21),
92 'added': [
92 'added': [
93 FileNode('foobar3', content='foobar3'),
93 FileNode(b'foobar3', content=b'foobar3'),
94 ],
94 ],
95 'changed': [
95 'changed': [
96 FileNode('foobar', 'FOOBAR'),
96 FileNode(b'foobar', b'FOOBAR'),
97 ],
97 ],
98 },
98 },
99 {
99 {
@@ -101,16 +101,16 b' class TestRepositoryGetDiff(BackendTestM'
101 'author': 'Jane Doe <jane.doe@example.com>',
101 'author': 'Jane Doe <jane.doe@example.com>',
102 'date': datetime.datetime(2010, 1, 1, 22),
102 'date': datetime.datetime(2010, 1, 1, 22),
103 'changed': [
103 'changed': [
104 FileNode('foobar3', content='FOOBAR\nFOOBAR\nFOOBAR\n'),
104 FileNode(b'foobar3', content=b'FOOBAR\nFOOBAR\nFOOBAR\n'),
105 ],
105 ],
106 'removed': [FileNode('foobar')],
106 'removed': [FileNode(b'foobar')],
107 },
107 },
108 {
108 {
109 'message': 'Whitespace changes',
109 'message': 'Whitespace changes',
110 'author': 'Jane Doe <jane.doe@example.com>',
110 'author': 'Jane Doe <jane.doe@example.com>',
111 'date': datetime.datetime(2010, 1, 1, 23),
111 'date': datetime.datetime(2010, 1, 1, 23),
112 'changed': [
112 'changed': [
113 FileNode('foobar3', content='FOOBAR \nFOOBAR\nFOOBAR\n'),
113 FileNode(b'foobar3', content=b'FOOBAR \nFOOBAR\nFOOBAR\n'),
114 ],
114 ],
115 },
115 },
116 ]
116 ]
@@ -119,39 +119,39 b' class TestRepositoryGetDiff(BackendTestM'
119 def test_initial_commit_diff(self):
119 def test_initial_commit_diff(self):
120 initial_commit = self.repo[0]
120 initial_commit = self.repo[0]
121 diff = self.repo.get_diff(self.repo.EMPTY_COMMIT, initial_commit)
121 diff = self.repo.get_diff(self.repo.EMPTY_COMMIT, initial_commit)
122 assert diff.raw == self.first_commit_diffs[self.repo.alias]
122 assert diff.raw.tobytes() == self.first_commit_diffs[self.repo.alias]
123
123
124 def test_second_commit_diff(self):
124 def test_second_commit_diff(self):
125 diff = self.repo.get_diff(self.repo[0], self.repo[1])
125 diff = self.repo.get_diff(self.repo[0], self.repo[1])
126 assert diff.raw == self.second_commit_diffs[self.repo.alias]
126 assert diff.raw.tobytes() == self.second_commit_diffs[self.repo.alias]
127
127
128 def test_third_commit_diff(self):
128 def test_third_commit_diff(self):
129 diff = self.repo.get_diff(self.repo[1], self.repo[2])
129 diff = self.repo.get_diff(self.repo[1], self.repo[2])
130 assert diff.raw == self.third_commit_diffs[self.repo.alias]
130 assert diff.raw.tobytes() == self.third_commit_diffs[self.repo.alias]
131
131
132 def test_ignore_whitespace(self):
132 def test_ignore_whitespace(self):
133 diff = self.repo.get_diff(
133 diff = self.repo.get_diff(
134 self.repo[2], self.repo[3], ignore_whitespace=True)
134 self.repo[2], self.repo[3], ignore_whitespace=True)
135 assert '@@' not in diff.raw
135 assert b'@@' not in diff.raw.tobytes()
136
136
137 def test_only_one_file(self):
137 def test_only_one_file(self):
138 diff = self.repo.get_diff(
138 diff = self.repo.get_diff(
139 self.repo.EMPTY_COMMIT, self.repo[0], path='foobar')
139 self.repo.EMPTY_COMMIT, self.repo[0], path='foobar')
140 assert 'foobar2' not in diff.raw
140 assert b'foobar2' not in diff.raw.tobytes()
141
141
142 def test_context_parameter(self):
142 def test_context_parameter(self):
143 first_commit = self.repo.get_commit(commit_idx=0)
143 first_commit = self.repo.get_commit(commit_idx=0)
144 diff = self.repo.get_diff(
144 diff = self.repo.get_diff(
145 self.repo.EMPTY_COMMIT, first_commit, context=2)
145 self.repo.EMPTY_COMMIT, first_commit, context=2)
146 assert diff.raw == self.first_commit_diffs[self.repo.alias]
146 assert diff.raw.tobytes() == self.first_commit_diffs[self.repo.alias]
147
147
148 def test_context_only_one_file(self):
148 def test_context_only_one_file(self):
149 diff = self.repo.get_diff(
149 diff = self.repo.get_diff(
150 self.repo.EMPTY_COMMIT, self.repo[0], path='foobar', context=2)
150 self.repo.EMPTY_COMMIT, self.repo[0], path='foobar', context=2)
151 assert diff.raw == self.first_commit_one_file[self.repo.alias]
151 assert diff.raw.tobytes() == self.first_commit_one_file[self.repo.alias]
152
152
153 first_commit_diffs = {
153 first_commit_diffs = {
154 'git': r"""diff --git a/foobar b/foobar
154 'git': br"""diff --git a/foobar b/foobar
155 new file mode 100644
155 new file mode 100644
156 index 0000000..f6ea049
156 index 0000000..f6ea049
157 --- /dev/null
157 --- /dev/null
@@ -168,7 +168,7 b' index 0000000..e8c9d6b'
168 +foobar2
168 +foobar2
169 \ No newline at end of file
169 \ No newline at end of file
170 """,
170 """,
171 'hg': r"""diff --git a/foobar b/foobar
171 'hg': br"""diff --git a/foobar b/foobar
172 new file mode 100644
172 new file mode 100644
173 --- /dev/null
173 --- /dev/null
174 +++ b/foobar
174 +++ b/foobar
@@ -183,7 +183,7 b' new file mode 100644'
183 +foobar2
183 +foobar2
184 \ No newline at end of file
184 \ No newline at end of file
185 """,
185 """,
186 'svn': """Index: foobar
186 'svn': b"""Index: foobar
187 ===================================================================
187 ===================================================================
188 diff --git a/foobar b/foobar
188 diff --git a/foobar b/foobar
189 new file mode 10644
189 new file mode 10644
@@ -205,7 +205,7 b' new file mode 10644'
205 }
205 }
206
206
207 second_commit_diffs = {
207 second_commit_diffs = {
208 'git': r"""diff --git a/foobar b/foobar
208 'git': br"""diff --git a/foobar b/foobar
209 index f6ea049..389865b 100644
209 index f6ea049..389865b 100644
210 --- a/foobar
210 --- a/foobar
211 +++ b/foobar
211 +++ b/foobar
@@ -223,7 +223,7 b' index 0000000..c11c37d'
223 +foobar3
223 +foobar3
224 \ No newline at end of file
224 \ No newline at end of file
225 """,
225 """,
226 'hg': r"""diff --git a/foobar b/foobar
226 'hg': br"""diff --git a/foobar b/foobar
227 --- a/foobar
227 --- a/foobar
228 +++ b/foobar
228 +++ b/foobar
229 @@ -1,1 +1,1 @@
229 @@ -1,1 +1,1 @@
@@ -239,7 +239,7 b' new file mode 100644'
239 +foobar3
239 +foobar3
240 \ No newline at end of file
240 \ No newline at end of file
241 """,
241 """,
242 'svn': """Index: foobar
242 'svn': b"""Index: foobar
243 ===================================================================
243 ===================================================================
244 diff --git a/foobar b/foobar
244 diff --git a/foobar b/foobar
245 --- a/foobar\t(revision 1)
245 --- a/foobar\t(revision 1)
@@ -262,7 +262,7 b' new file mode 10644'
262 }
262 }
263
263
264 third_commit_diffs = {
264 third_commit_diffs = {
265 'git': r"""diff --git a/foobar b/foobar
265 'git': br"""diff --git a/foobar b/foobar
266 deleted file mode 100644
266 deleted file mode 100644
267 index 389865b..0000000
267 index 389865b..0000000
268 --- a/foobar
268 --- a/foobar
@@ -281,7 +281,7 b' index c11c37d..f932447 100644'
281 +FOOBAR
281 +FOOBAR
282 +FOOBAR
282 +FOOBAR
283 """,
283 """,
284 'hg': r"""diff --git a/foobar b/foobar
284 'hg': br"""diff --git a/foobar b/foobar
285 deleted file mode 100644
285 deleted file mode 100644
286 --- a/foobar
286 --- a/foobar
287 +++ /dev/null
287 +++ /dev/null
@@ -298,7 +298,7 b' diff --git a/foobar3 b/foobar3'
298 +FOOBAR
298 +FOOBAR
299 +FOOBAR
299 +FOOBAR
300 """,
300 """,
301 'svn': """Index: foobar
301 'svn': b"""Index: foobar
302 ===================================================================
302 ===================================================================
303 diff --git a/foobar b/foobar
303 diff --git a/foobar b/foobar
304 deleted file mode 10644
304 deleted file mode 10644
@@ -322,7 +322,7 b' diff --git a/foobar3 b/foobar3'
322 }
322 }
323
323
324 first_commit_one_file = {
324 first_commit_one_file = {
325 'git': r"""diff --git a/foobar b/foobar
325 'git': br"""diff --git a/foobar b/foobar
326 new file mode 100644
326 new file mode 100644
327 index 0000000..f6ea049
327 index 0000000..f6ea049
328 --- /dev/null
328 --- /dev/null
@@ -331,7 +331,7 b' index 0000000..f6ea049'
331 +foobar
331 +foobar
332 \ No newline at end of file
332 \ No newline at end of file
333 """,
333 """,
334 'hg': r"""diff --git a/foobar b/foobar
334 'hg': br"""diff --git a/foobar b/foobar
335 new file mode 100644
335 new file mode 100644
336 --- /dev/null
336 --- /dev/null
337 +++ b/foobar
337 +++ b/foobar
@@ -339,7 +339,7 b' new file mode 100644'
339 +foobar
339 +foobar
340 \ No newline at end of file
340 \ No newline at end of file
341 """,
341 """,
342 'svn': """Index: foobar
342 'svn': b"""Index: foobar
343 ===================================================================
343 ===================================================================
344 diff --git a/foobar b/foobar
344 diff --git a/foobar b/foobar
345 new file mode 10644
345 new file mode 10644
@@ -363,9 +363,9 b' class TestSvnGetDiff(object):'
363 commit1 = repo[-2]
363 commit1 = repo[-2]
364 commit2 = repo[-1]
364 commit2 = repo[-1]
365 diff = repo.get_diff(commit1, commit2, path=path, path1=path1)
365 diff = repo.get_diff(commit1, commit2, path=path, path1=path1)
366 assert diff.raw == self.expected_diff_v_0_2
366 assert diff.raw.tobytes() == self.expected_diff_v_0_2
367
367
368 expected_diff_v_0_2 = '''Index: example.py
368 expected_diff_v_0_2 = b'''Index: example.py
369 ===================================================================
369 ===================================================================
370 diff --git a/example.py b/example.py
370 diff --git a/example.py b/example.py
371 --- a/example.py\t(revision 25)
371 --- a/example.py\t(revision 25)
@@ -390,7 +390,7 b' diff --git a/example.py b/example.py'
390 diff = repo.get_diff(repo[0], repo[1])
390 diff = repo.get_diff(repo[0], repo[1])
391 # TODO: johbo: Think about supporting svn directory nodes
391 # TODO: johbo: Think about supporting svn directory nodes
392 # a little bit better, source is here like a file
392 # a little bit better, source is here like a file
393 expected_diff = """Index: source
393 expected_diff = b"""Index: source
394 ===================================================================
394 ===================================================================
395 diff --git a/source b/source
395 diff --git a/source b/source
396 deleted file mode 10644
396 deleted file mode 10644
@@ -403,7 +403,7 b' new file mode 10644'
403 --- /dev/null\t(revision 0)
403 --- /dev/null\t(revision 0)
404 +++ b/target/file\t(revision 2)
404 +++ b/target/file\t(revision 2)
405 """
405 """
406 assert diff.raw == expected_diff
406 assert diff.raw.tobytes() == expected_diff
407
407
408
408
409 @pytest.mark.usefixtures("vcs_repository_support")
409 @pytest.mark.usefixtures("vcs_repository_support")
@@ -412,8 +412,8 b' class TestGetDiffBinary(BackendTestMixin'
412 recreate_repo_per_test = False
412 recreate_repo_per_test = False
413
413
414 # Note: "Fake" PNG files, has the correct magic as prefix
414 # Note: "Fake" PNG files, has the correct magic as prefix
415 BINARY = """\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00"""
415 BINARY = b"""\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00"""
416 BINARY2 = """\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x01\x00\x00"""
416 BINARY2 = b"""\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x01\x00\x00"""
417
417
418 @staticmethod
418 @staticmethod
419 def _get_commits():
419 def _get_commits():
@@ -423,21 +423,21 b' class TestGetDiffBinary(BackendTestMixin'
423 'author': 'Joe Doe <joe.deo@example.com>',
423 'author': 'Joe Doe <joe.deo@example.com>',
424 'date': datetime.datetime(2010, 1, 1, 20),
424 'date': datetime.datetime(2010, 1, 1, 20),
425 'added': [
425 'added': [
426 FileNode('image.png', content=TestGetDiffBinary.BINARY),
426 FileNode(b'image.png', content=TestGetDiffBinary.BINARY),
427 ]},
427 ]},
428 {
428 {
429 'message': 'Modify image.png',
429 'message': 'Modify image.png',
430 'author': 'Joe Doe <joe.deo@example.com>',
430 'author': 'Joe Doe <joe.deo@example.com>',
431 'date': datetime.datetime(2010, 1, 1, 21),
431 'date': datetime.datetime(2010, 1, 1, 21),
432 'changed': [
432 'changed': [
433 FileNode('image.png', content=TestGetDiffBinary.BINARY2),
433 FileNode(b'image.png', content=TestGetDiffBinary.BINARY2),
434 ]},
434 ]},
435 {
435 {
436 'message': 'Remove image.png',
436 'message': 'Remove image.png',
437 'author': 'Joe Doe <joe.deo@example.com>',
437 'author': 'Joe Doe <joe.deo@example.com>',
438 'date': datetime.datetime(2010, 1, 1, 21),
438 'date': datetime.datetime(2010, 1, 1, 21),
439 'removed': [
439 'removed': [
440 FileNode('image.png'),
440 FileNode(b'image.png'),
441 ]},
441 ]},
442 ]
442 ]
443 return commits
443 return commits
@@ -446,7 +446,7 b' class TestGetDiffBinary(BackendTestMixin'
446 diff = self.repo.get_diff(self.repo.EMPTY_COMMIT, self.repo[0])
446 diff = self.repo.get_diff(self.repo.EMPTY_COMMIT, self.repo[0])
447
447
448 expected = {
448 expected = {
449 'git': """diff --git a/image.png b/image.png
449 'git': b"""diff --git a/image.png b/image.png
450 new file mode 100644
450 new file mode 100644
451 index 0000000000000000000000000000000000000000..28380fd4a25c58be1b68b523ba2a314f4459ee9c
451 index 0000000000000000000000000000000000000000..28380fd4a25c58be1b68b523ba2a314f4459ee9c
452 GIT binary patch
452 GIT binary patch
@@ -457,15 +457,15 b' literal 0'
457 Hc$@<O00001
457 Hc$@<O00001
458
458
459 """,
459 """,
460 'hg': """diff --git a/image.png b/image.png
460 'hg': b"""diff --git a/image.png b/image.png
461 new file mode 100644
461 new file mode 100644
462 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..28380fd4a25c58be1b68b523ba2a314f4459ee9c
462 index 0000000000000000000000000000000000000000..28380fd4a25c58be1b68b523ba2a314f4459ee9c
463 GIT binary patch
463 GIT binary patch
464 literal 19
464 literal 19
465 Yc%17D@N?(olHy`uVBq!ia0vp^03%2O-T(jq
465 Yc%17D@N?(olHy`uVBq!ia0vp^03%2O-T(jq
466
466
467 """,
467 """,
468 'svn': """===================================================================
468 'svn': b"""===================================================================
469 Cannot display: file marked as a binary type.
469 Cannot display: file marked as a binary type.
470 svn:mime-type = application/octet-stream
470 svn:mime-type = application/octet-stream
471 Index: image.png
471 Index: image.png
@@ -476,13 +476,13 b' new file mode 10644'
476 +++ b/image.png\t(revision 1)
476 +++ b/image.png\t(revision 1)
477 """,
477 """,
478 }
478 }
479 assert diff.raw == expected[self.repo.alias]
479 assert diff.raw.tobytes() == expected[self.repo.alias]
480
480
481 def test_update_a_binary_file(self):
481 def test_update_a_binary_file(self):
482 diff = self.repo.get_diff(self.repo[0], self.repo[1])
482 diff = self.repo.get_diff(self.repo[0], self.repo[1])
483
483
484 expected = {
484 expected = {
485 'git': """diff --git a/image.png b/image.png
485 'git': b"""diff --git a/image.png b/image.png
486 index 28380fd4a25c58be1b68b523ba2a314f4459ee9c..1008a77cd372386a1c24fbd96019333f67ad0065 100644
486 index 28380fd4a25c58be1b68b523ba2a314f4459ee9c..1008a77cd372386a1c24fbd96019333f67ad0065 100644
487 GIT binary patch
487 GIT binary patch
488 literal 19
488 literal 19
@@ -492,14 +492,14 b' literal 19'
492 Yc%17D@N?(olHy`uVBq!ia0vp^03%2O-T(jq
492 Yc%17D@N?(olHy`uVBq!ia0vp^03%2O-T(jq
493
493
494 """,
494 """,
495 'hg': """diff --git a/image.png b/image.png
495 'hg': b"""diff --git a/image.png b/image.png
496 index 28380fd4a25c58be1b68b523ba2a314f4459ee9c..1008a77cd372386a1c24fbd96019333f67ad0065
496 index 28380fd4a25c58be1b68b523ba2a314f4459ee9c..1008a77cd372386a1c24fbd96019333f67ad0065
497 GIT binary patch
497 GIT binary patch
498 literal 19
498 literal 19
499 ac%17D@N?(olHy`uVBq!ia0y~$U;qFkO9I~j
499 ac%17D@N?(olHy`uVBq!ia0y~$U;qFkO9I~j
500
500
501 """,
501 """,
502 'svn': """===================================================================
502 'svn': b"""===================================================================
503 Cannot display: file marked as a binary type.
503 Cannot display: file marked as a binary type.
504 svn:mime-type = application/octet-stream
504 svn:mime-type = application/octet-stream
505 Index: image.png
505 Index: image.png
@@ -509,13 +509,13 b' diff --git a/image.png b/image.png'
509 +++ b/image.png\t(revision 2)
509 +++ b/image.png\t(revision 2)
510 """,
510 """,
511 }
511 }
512 assert diff.raw == expected[self.repo.alias]
512 assert diff.raw.tobytes() == expected[self.repo.alias]
513
513
514 def test_remove_a_binary_file(self):
514 def test_remove_a_binary_file(self):
515 diff = self.repo.get_diff(self.repo[1], self.repo[2])
515 diff = self.repo.get_diff(self.repo[1], self.repo[2])
516
516
517 expected = {
517 expected = {
518 'git': """diff --git a/image.png b/image.png
518 'git': b"""diff --git a/image.png b/image.png
519 deleted file mode 100644
519 deleted file mode 100644
520 index 1008a77cd372386a1c24fbd96019333f67ad0065..0000000000000000000000000000000000000000
520 index 1008a77cd372386a1c24fbd96019333f67ad0065..0000000000000000000000000000000000000000
521 GIT binary patch
521 GIT binary patch
@@ -526,15 +526,15 b' literal 19'
526 ac%17D@N?(olHy`uVBq!ia0y~$U;qFkO9I~j
526 ac%17D@N?(olHy`uVBq!ia0y~$U;qFkO9I~j
527
527
528 """,
528 """,
529 'hg': """diff --git a/image.png b/image.png
529 'hg': b"""diff --git a/image.png b/image.png
530 deleted file mode 100644
530 deleted file mode 100644
531 index 1008a77cd372386a1c24fbd96019333f67ad0065..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
531 index 1008a77cd372386a1c24fbd96019333f67ad0065..0000000000000000000000000000000000000000
532 GIT binary patch
532 GIT binary patch
533 literal 0
533 literal 0
534 Hc$@<O00001
534 Hc$@<O00001
535
535
536 """,
536 """,
537 'svn': """===================================================================
537 'svn': b"""===================================================================
538 Cannot display: file marked as a binary type.
538 Cannot display: file marked as a binary type.
539 svn:mime-type = application/octet-stream
539 svn:mime-type = application/octet-stream
540 Index: image.png
540 Index: image.png
@@ -545,4 +545,4 b' deleted file mode 10644'
545 +++ /dev/null\t(revision 3)
545 +++ /dev/null\t(revision 3)
546 """,
546 """,
547 }
547 }
548 assert diff.raw == expected[self.repo.alias]
548 assert diff.raw.tobytes() == expected[self.repo.alias]
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -21,6 +20,8 b''
21 import datetime
20 import datetime
22
21
23 import pytest
22 import pytest
23
24 from rhodecode.lib.str_utils import safe_bytes
24 from rhodecode.lib.vcs.nodes import FileNode
25 from rhodecode.lib.vcs.nodes import FileNode
25 from rhodecode.tests.vcs.conftest import BackendTestMixin
26 from rhodecode.tests.vcs.conftest import BackendTestMixin
26
27
@@ -28,13 +29,13 b' from rhodecode.tests.vcs.conftest import'
28 @pytest.mark.usefixtures("vcs_repository_support")
29 @pytest.mark.usefixtures("vcs_repository_support")
29 class TestFileNodeUnicodePath(BackendTestMixin):
30 class TestFileNodeUnicodePath(BackendTestMixin):
30
31
31 fname = 'Δ…Ε›Γ°Δ…Δ™Ε‚Δ…Δ‡.txt'
32 fname = safe_bytes('Δ…Ε›Γ°Δ…Δ™Ε‚Δ…Δ‡.txt')
32 ufname = fname
33 ufname = fname
33
34
34 @classmethod
35 @classmethod
35 def _get_commits(cls):
36 def _get_commits(cls):
36 nodes = [
37 nodes = [
37 FileNode(cls.fname, content='Foobar'),
38 FileNode(cls.fname, content=b'Foobar'),
38 ]
39 ]
39
40
40 commits = [
41 commits = [
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -39,7 +38,7 b' class TestGetitem(BackendTestMixin):'
39 'author': 'Joe Doe <joe.doe@example.com>',
38 'author': 'Joe Doe <joe.doe@example.com>',
40 'date': start_date + datetime.timedelta(hours=12 * x),
39 'date': start_date + datetime.timedelta(hours=12 * x),
41 'added': [
40 'added': [
42 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
41 FileNode(b'file_%d.txt' % x, content='Foobar %d' % x),
43 ],
42 ],
44 }
43 }
45
44
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -36,7 +35,7 b' class TestGetslice(BackendTestMixin):'
36 'author': 'Joe Doe <joe.doe@example.com>',
35 'author': 'Joe Doe <joe.doe@example.com>',
37 'date': start_date + datetime.timedelta(hours=12 * x),
36 'date': start_date + datetime.timedelta(hours=12 * x),
38 'added': [
37 'added': [
39 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
38 FileNode(b'file_%d.txt' % x, content='Foobar %d' % x),
40 ],
39 ],
41 }
40 }
42
41
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -41,6 +40,29 b' from rhodecode.tests.vcs.conftest import'
41 pytestmark = pytest.mark.backends("git")
40 pytestmark = pytest.mark.backends("git")
42
41
43
42
43 DIFF_FROM_REMOTE = br"""diff --git a/foobar b/foobar
44 new file mode 100644
45 index 0000000..f6ea049
46 --- /dev/null
47 +++ b/foobar
48 @@ -0,0 +1 @@
49 +foobar
50 \ No newline at end of file
51 diff --git a/foobar2 b/foobar2
52 new file mode 100644
53 index 0000000..e8c9d6b
54 --- /dev/null
55 +++ b/foobar2
56 @@ -0,0 +1 @@
57 +foobar2
58 \ No newline at end of file
59 """
60
61
62 def callable_get_diff(*args, **kwargs):
63 return DIFF_FROM_REMOTE
64
65
44 class TestGitRepository(object):
66 class TestGitRepository(object):
45
67
46 @pytest.fixture(autouse=True)
68 @pytest.fixture(autouse=True)
@@ -253,7 +275,7 b' TODO: To be written...'
253 """
275 """
254 node = commit10.get_node('README.rst')
276 node = commit10.get_node('README.rst')
255 assert node.kind == NodeKind.FILE
277 assert node.kind == NodeKind.FILE
256 assert node.content == README
278 assert node.str_content == README
257
279
258 def test_head(self):
280 def test_head(self):
259 assert self.repo.head == self.repo.get_commit().raw_id
281 assert self.repo.head == self.repo.get_commit().raw_id
@@ -420,7 +442,7 b' TODO: To be written...'
420
442
421 def test_local_merge_raises_exception_on_conflict(self, vcsbackend_git):
443 def test_local_merge_raises_exception_on_conflict(self, vcsbackend_git):
422 target_repo = vcsbackend_git.create_repo(number_of_commits=1)
444 target_repo = vcsbackend_git.create_repo(number_of_commits=1)
423 vcsbackend_git.ensure_file('README', 'I will conflict with you!!!')
445 vcsbackend_git.ensure_file(b'README', b'I will conflict with you!!!')
424
446
425 target_repo._local_fetch(self.repo.path, 'master')
447 target_repo._local_fetch(self.repo.path, 'master')
426 with pytest.raises(RepositoryError):
448 with pytest.raises(RepositoryError):
@@ -971,11 +993,12 b' class TestGitCommit(object):'
971 for commit in self.repo:
993 for commit in self.repo:
972 assert type(commit.author) == str
994 assert type(commit.author) == str
973
995
974 def test_repo_files_content_is_unicode(self):
996 def test_repo_files_content_types(self):
975 commit = self.repo.get_commit()
997 commit = self.repo.get_commit()
976 for node in commit.get_node('/'):
998 for node in commit.get_node('/'):
977 if node.is_file():
999 if node.is_file():
978 assert type(node.content) == str
1000 assert type(node.content) == bytes
1001 assert type(node.str_content) == str
979
1002
980 def test_wrong_path(self):
1003 def test_wrong_path(self):
981 # There is 'setup.py' in the root dir but not there:
1004 # There is 'setup.py' in the root dir but not there:
@@ -1041,11 +1064,9 b' class TestGitSpecificWithRepo(BackendTes'
1041 'author': 'Joe Doe <joe.doe@example.com>',
1064 'author': 'Joe Doe <joe.doe@example.com>',
1042 'date': datetime.datetime(2010, 1, 1, 20),
1065 'date': datetime.datetime(2010, 1, 1, 20),
1043 'added': [
1066 'added': [
1044 FileNode('foobar/static/js/admin/base.js', content='base'),
1067 FileNode(b'foobar/static/js/admin/base.js', content=b'base'),
1045 FileNode(
1068 FileNode(b'foobar/static/admin', content=b'admin', mode=0o120000), # this is a link
1046 'foobar/static/admin', content='admin',
1069 FileNode(b'foo', content=b'foo'),
1047 mode=0o120000), # this is a link
1048 FileNode('foo', content='foo'),
1049 ],
1070 ],
1050 },
1071 },
1051 {
1072 {
@@ -1053,7 +1074,7 b' class TestGitSpecificWithRepo(BackendTes'
1053 'author': 'Joe Doe <joe.doe@example.com>',
1074 'author': 'Joe Doe <joe.doe@example.com>',
1054 'date': datetime.datetime(2010, 1, 1, 22),
1075 'date': datetime.datetime(2010, 1, 1, 22),
1055 'added': [
1076 'added': [
1056 FileNode('foo2', content='foo2'),
1077 FileNode(b'foo2', content=b'foo2'),
1057 ],
1078 ],
1058 },
1079 },
1059 ]
1080 ]
@@ -1061,17 +1082,18 b' class TestGitSpecificWithRepo(BackendTes'
1061 def test_paths_slow_traversing(self):
1082 def test_paths_slow_traversing(self):
1062 commit = self.repo.get_commit()
1083 commit = self.repo.get_commit()
1063 assert commit.get_node('foobar').get_node('static').get_node('js')\
1084 assert commit.get_node('foobar').get_node('static').get_node('js')\
1064 .get_node('admin').get_node('base.js').content == 'base'
1085 .get_node('admin').get_node('base.js').content == b'base'
1065
1086
1066 def test_paths_fast_traversing(self):
1087 def test_paths_fast_traversing(self):
1067 commit = self.repo.get_commit()
1088 commit = self.repo.get_commit()
1068 assert commit.get_node('foobar/static/js/admin/base.js').content == 'base'
1089 assert commit.get_node('foobar/static/js/admin/base.js').content == b'base'
1069
1090
1070 def test_get_diff_runs_git_command_with_hashes(self):
1091 def test_get_diff_runs_git_command_with_hashes(self):
1071 comm1 = self.repo[0]
1092 comm1 = self.repo[0]
1072 comm2 = self.repo[1]
1093 comm2 = self.repo[1]
1073
1094
1074 with mock.patch.object(self.repo, '_remote') as remote_mock:
1095 with mock.patch.object(self.repo, '_remote', return_value=mock.Mock()) as remote_mock:
1096 remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff)
1075 self.repo.get_diff(comm1, comm2)
1097 self.repo.get_diff(comm1, comm2)
1076
1098
1077 remote_mock.diff.assert_called_once_with(
1099 remote_mock.diff.assert_called_once_with(
@@ -1080,8 +1102,11 b' class TestGitSpecificWithRepo(BackendTes'
1080
1102
1081 def test_get_diff_runs_git_command_with_str_hashes(self):
1103 def test_get_diff_runs_git_command_with_str_hashes(self):
1082 comm2 = self.repo[1]
1104 comm2 = self.repo[1]
1083 with mock.patch.object(self.repo, '_remote') as remote_mock:
1105
1106 with mock.patch.object(self.repo, '_remote', return_value=mock.Mock()) as remote_mock:
1107 remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff)
1084 self.repo.get_diff(self.repo.EMPTY_COMMIT, comm2)
1108 self.repo.get_diff(self.repo.EMPTY_COMMIT, comm2)
1109
1085 remote_mock.diff.assert_called_once_with(
1110 remote_mock.diff.assert_called_once_with(
1086 self.repo.EMPTY_COMMIT.raw_id, comm2.raw_id,
1111 self.repo.EMPTY_COMMIT.raw_id, comm2.raw_id,
1087 file_filter=None, opt_ignorews=False, context=3)
1112 file_filter=None, opt_ignorews=False, context=3)
@@ -1089,8 +1114,11 b' class TestGitSpecificWithRepo(BackendTes'
1089 def test_get_diff_runs_git_command_with_path_if_its_given(self):
1114 def test_get_diff_runs_git_command_with_path_if_its_given(self):
1090 comm1 = self.repo[0]
1115 comm1 = self.repo[0]
1091 comm2 = self.repo[1]
1116 comm2 = self.repo[1]
1092 with mock.patch.object(self.repo, '_remote') as remote_mock:
1117
1118 with mock.patch.object(self.repo, '_remote', return_value=mock.Mock()) as remote_mock:
1119 remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff)
1093 self.repo.get_diff(comm1, comm2, 'foo')
1120 self.repo.get_diff(comm1, comm2, 'foo')
1121
1094 remote_mock.diff.assert_called_once_with(
1122 remote_mock.diff.assert_called_once_with(
1095 self.repo._lookup_commit(0), comm2.raw_id,
1123 self.repo._lookup_commit(0), comm2.raw_id,
1096 file_filter='foo', opt_ignorews=False, context=3)
1124 file_filter='foo', opt_ignorews=False, context=3)
@@ -1107,9 +1135,9 b' class TestGitRegression(BackendTestMixin'
1107 'author': 'Joe Doe <joe.doe@example.com>',
1135 'author': 'Joe Doe <joe.doe@example.com>',
1108 'date': datetime.datetime(2010, 1, 1, 20),
1136 'date': datetime.datetime(2010, 1, 1, 20),
1109 'added': [
1137 'added': [
1110 FileNode('bot/__init__.py', content='base'),
1138 FileNode(b'bot/__init__.py', content=b'base'),
1111 FileNode('bot/templates/404.html', content='base'),
1139 FileNode(b'bot/templates/404.html', content=b'base'),
1112 FileNode('bot/templates/500.html', content='base'),
1140 FileNode(b'bot/templates/500.html', content=b'base'),
1113 ],
1141 ],
1114 },
1142 },
1115 {
1143 {
@@ -1117,14 +1145,12 b' class TestGitRegression(BackendTestMixin'
1117 'author': 'Joe Doe <joe.doe@example.com>',
1145 'author': 'Joe Doe <joe.doe@example.com>',
1118 'date': datetime.datetime(2010, 1, 1, 22),
1146 'date': datetime.datetime(2010, 1, 1, 22),
1119 'added': [
1147 'added': [
1120 FileNode('bot/build/migrations/1.py', content='foo2'),
1148 FileNode(b'bot/build/migrations/1.py', content=b'foo2'),
1121 FileNode('bot/build/migrations/2.py', content='foo2'),
1149 FileNode(b'bot/build/migrations/2.py', content=b'foo2'),
1122 FileNode(
1150 FileNode(b'bot/build/static/templates/f.html', content=b'foo2'),
1123 'bot/build/static/templates/f.html', content='foo2'),
1151 FileNode(b'bot/build/static/templates/f1.html', content=b'foo2'),
1124 FileNode(
1152 FileNode(b'bot/build/templates/err.html', content=b'foo2'),
1125 'bot/build/static/templates/f1.html', content='foo2'),
1153 FileNode(b'bot/build/templates/err2.html', content=b'foo2'),
1126 FileNode('bot/build/templates/err.html', content='foo2'),
1127 FileNode('bot/build/templates/err2.html', content='foo2'),
1128 ],
1154 ],
1129 },
1155 },
1130 ]
1156 ]
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -23,6 +22,7 b' import os'
23 import mock
22 import mock
24 import pytest
23 import pytest
25
24
25 from rhodecode.lib.str_utils import safe_bytes
26 from rhodecode.lib.utils import make_db_config
26 from rhodecode.lib.utils import make_db_config
27 from rhodecode.lib.vcs import backends
27 from rhodecode.lib.vcs import backends
28 from rhodecode.lib.vcs.backends.base import (
28 from rhodecode.lib.vcs.backends.base import (
@@ -84,23 +84,23 b' class TestMercurialRepository(object):'
84
84
85 def test_unicode_path_repo(self):
85 def test_unicode_path_repo(self):
86 with pytest.raises(VCSError):
86 with pytest.raises(VCSError):
87 MercurialRepository(u'iShouldFail')
87 MercurialRepository('iShouldFail')
88
88
89 def test_unicode_commit_id(self):
89 def test_unicode_commit_id(self):
90 with pytest.raises(CommitDoesNotExistError):
90 with pytest.raises(CommitDoesNotExistError):
91 self.repo.get_commit(u'unicode-commit-id')
91 self.repo.get_commit('unicode-commit-id')
92 with pytest.raises(CommitDoesNotExistError):
92 with pytest.raises(CommitDoesNotExistError):
93 self.repo.get_commit(u'unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-commit-id')
93 self.repo.get_commit('unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-commit-id')
94
94
95 def test_unicode_bookmark(self):
95 def test_unicode_bookmark(self):
96 self.repo.bookmark(u'unicode-bookmark')
96 self.repo.bookmark('unicode-bookmark')
97 self.repo.bookmark(u'unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-bookmark')
97 self.repo.bookmark('unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-bookmark')
98
98
99 def test_unicode_branch(self):
99 def test_unicode_branch(self):
100 with pytest.raises(KeyError):
100 with pytest.raises(KeyError):
101 self.repo.branches[u'unicode-branch']
101 assert self.repo.branches['unicode-branch']
102 with pytest.raises(KeyError):
102 with pytest.raises(KeyError):
103 self.repo.branches[u'unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-branch']
103 assert self.repo.branches['unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-branch']
104
104
105 def test_repo_clone(self):
105 def test_repo_clone(self):
106 if os.path.exists(TEST_HG_REPO_CLONE):
106 if os.path.exists(TEST_HG_REPO_CLONE):
@@ -141,27 +141,17 b' class TestMercurialRepository(object):'
141 def test_commit_ids(self):
141 def test_commit_ids(self):
142 # there are 21 commits at bitbucket now
142 # there are 21 commits at bitbucket now
143 # so we can assume they would be available from now on
143 # so we can assume they would be available from now on
144 subset = set([
144 subset = {'b986218ba1c9b0d6a259fac9b050b1724ed8e545', '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
145 'b986218ba1c9b0d6a259fac9b050b1724ed8e545',
145 '6cba7170863a2411822803fa77a0a264f1310b35', '56349e29c2af3ac913b28bde9a2c6154436e615b',
146 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
146 '2dda4e345facb0ccff1a191052dd1606dba6781d', '6fff84722075f1607a30f436523403845f84cd9e',
147 '6cba7170863a2411822803fa77a0a264f1310b35',
147 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7', '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
148 '56349e29c2af3ac913b28bde9a2c6154436e615b',
148 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c', 'be90031137367893f1c406e0a8683010fd115b79',
149 '2dda4e345facb0ccff1a191052dd1606dba6781d',
149 'db8e58be770518cbb2b1cdfa69146e47cd481481', '84478366594b424af694a6c784cb991a16b87c21',
150 '6fff84722075f1607a30f436523403845f84cd9e',
150 '17f8e105dddb9f339600389c6dc7175d395a535c', '20a662e756499bde3095ffc9bc0643d1def2d0eb',
151 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
151 '2e319b85e70a707bba0beff866d9f9de032aa4f9', '786facd2c61deb9cf91e9534735124fb8fc11842',
152 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
152 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8', 'aa6a0de05b7612707db567078e130a6cd114a9a7',
153 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
154 'be90031137367893f1c406e0a8683010fd115b79',
155 'db8e58be770518cbb2b1cdfa69146e47cd481481',
156 '84478366594b424af694a6c784cb991a16b87c21',
157 '17f8e105dddb9f339600389c6dc7175d395a535c',
158 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
159 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
160 '786facd2c61deb9cf91e9534735124fb8fc11842',
161 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
162 'aa6a0de05b7612707db567078e130a6cd114a9a7',
163 'eada5a770da98ab0dd7325e29d00e0714f228d09'
153 'eada5a770da98ab0dd7325e29d00e0714f228d09'
164 ])
154 }
165 assert subset.issubset(set(self.repo.commit_ids))
155 assert subset.issubset(set(self.repo.commit_ids))
166
156
167 # check if we have the proper order of commits
157 # check if we have the proper order of commits
@@ -301,7 +291,7 b' TODO: To be written...'
301 """
291 """
302 node = commit10.get_node('README.rst')
292 node = commit10.get_node('README.rst')
303 assert node.kind == NodeKind.FILE
293 assert node.kind == NodeKind.FILE
304 assert node.content == README
294 assert node.str_content == README
305
295
306 def test_local_clone(self):
296 def test_local_clone(self):
307 clone_path = next(REPO_PATH_GENERATOR)
297 clone_path = next(REPO_PATH_GENERATOR)
@@ -583,10 +573,10 b' TODO: To be written...'
583 vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1')
573 vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1')
584 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
574 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
585 imc = source_repo.in_memory_commit
575 imc = source_repo.in_memory_commit
586 imc.add(FileNode('file_x', content=source_repo.name))
576 imc.add(FileNode(b'file_x', content=source_repo.name))
587 imc.commit(
577 imc.commit(
588 message=u'Automatic commit from repo merge test',
578 message='Automatic commit from repo merge test',
589 author=u'Automatic <automatic@rhodecode.com>')
579 author='Automatic <automatic@rhodecode.com>')
590 target_commit = target_repo.get_commit()
580 target_commit = target_repo.get_commit()
591 source_commit = source_repo.get_commit()
581 source_commit = source_repo.get_commit()
592 default_branch = target_repo.DEFAULT_BRANCH_NAME
582 default_branch = target_repo.DEFAULT_BRANCH_NAME
@@ -627,10 +617,10 b' TODO: To be written...'
627 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
617 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
628 source_repo = vcsbackend_hg.clone_repo(target_repo)
618 source_repo = vcsbackend_hg.clone_repo(target_repo)
629 imc = source_repo.in_memory_commit
619 imc = source_repo.in_memory_commit
630 imc.add(FileNode('file_x', content=source_repo.name))
620 imc.add(FileNode(b'file_x', content=source_repo.name))
631 imc.commit(
621 imc.commit(
632 message=u'Automatic commit from repo merge test',
622 message='Automatic commit from repo merge test',
633 author=u'Automatic <automatic@rhodecode.com>')
623 author='Automatic <automatic@rhodecode.com>')
634 target_commit = target_repo.get_commit()
624 target_commit = target_repo.get_commit()
635 source_commit = source_repo.get_commit()
625 source_commit = source_repo.get_commit()
636 default_branch = target_repo.DEFAULT_BRANCH_NAME
626 default_branch = target_repo.DEFAULT_BRANCH_NAME
@@ -665,11 +655,11 b' TODO: To be written...'
665
655
666 # add an extra head to the target repo
656 # add an extra head to the target repo
667 imc = target_repo.in_memory_commit
657 imc = target_repo.in_memory_commit
668 imc.add(FileNode('file_x', content='foo'))
658 imc.add(FileNode(b'file_x', content='foo'))
669 commits = list(target_repo.get_commits())
659 commits = list(target_repo.get_commits())
670 imc.commit(
660 imc.commit(
671 message=u'Automatic commit from repo merge test',
661 message='Automatic commit from repo merge test',
672 author=u'Automatic <automatic@rhodecode.com>', parents=commits[0:1])
662 author='Automatic <automatic@rhodecode.com>', parents=commits[0:1])
673
663
674 target_commit = target_repo.get_commit()
664 target_commit = target_repo.get_commit()
675 source_commit = source_repo.get_commit()
665 source_commit = source_repo.get_commit()
@@ -698,11 +688,13 b' TODO: To be written...'
698 source_repo = vcsbackend_hg.clone_repo(target_repo)
688 source_repo = vcsbackend_hg.clone_repo(target_repo)
699 vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1')
689 vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1')
700 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
690 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
691
701 imc = source_repo.in_memory_commit
692 imc = source_repo.in_memory_commit
702 imc.add(FileNode('file_x', content=source_repo.name))
693 imc.add(FileNode(b'file_x', content=safe_bytes(source_repo.name)))
703 imc.commit(
694 imc.commit(
704 message=u'Automatic commit from repo merge test',
695 message='Automatic commit from repo merge test',
705 author=u'Automatic <automatic@rhodecode.com>')
696 author='Automatic <automatic@rhodecode.com>')
697
706 target_commit = target_repo.get_commit()
698 target_commit = target_repo.get_commit()
707 source_commit = source_repo.get_commit()
699 source_commit = source_repo.get_commit()
708
700
@@ -1090,11 +1082,12 b' class TestMercurialCommit(object):'
1090 for cm in self.repo:
1082 for cm in self.repo:
1091 assert type(cm.author) == str
1083 assert type(cm.author) == str
1092
1084
1093 def test_repo_files_content_is_unicode(self):
1085 def test_repo_files_content_type(self):
1094 test_commit = self.repo.get_commit(commit_idx=100)
1086 test_commit = self.repo.get_commit(commit_idx=100)
1095 for node in test_commit.get_node('/'):
1087 for node in test_commit.get_node('/'):
1096 if node.is_file():
1088 if node.is_file():
1097 assert type(node.content) == str
1089 assert type(node.content) == bytes
1090 assert type(node.str_content) == str
1098
1091
1099 def test_wrong_path(self):
1092 def test_wrong_path(self):
1100 # There is 'setup.py' in the root dir but not there:
1093 # There is 'setup.py' in the root dir but not there:
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -25,7 +24,7 b' import datetime'
25
24
26 import pytest
25 import pytest
27
26
28 from rhodecode.lib.utils2 import safe_unicode
27 from rhodecode.lib.str_utils import safe_bytes, safe_str
29 from rhodecode.lib.vcs.exceptions import (
28 from rhodecode.lib.vcs.exceptions import (
30 EmptyRepositoryError, NodeAlreadyAddedError, NodeAlreadyExistsError,
29 EmptyRepositoryError, NodeAlreadyAddedError, NodeAlreadyExistsError,
31 NodeAlreadyRemovedError, NodeAlreadyChangedError, NodeDoesNotExistError,
30 NodeAlreadyRemovedError, NodeAlreadyChangedError, NodeDoesNotExistError,
@@ -37,20 +36,18 b' from rhodecode.tests.vcs.conftest import'
37 @pytest.fixture()
36 @pytest.fixture()
38 def nodes():
37 def nodes():
39 nodes = [
38 nodes = [
40 FileNode('foobar', content='Foo & bar'),
39 FileNode(b'foobar', content=b'Foo & bar'),
41 FileNode('foobar2', content='Foo & bar, doubled!'),
40 FileNode(b'foobar2', content=b'Foo & bar, doubled!'),
42 FileNode('foo bar with spaces', content=''),
41 FileNode(b'foo bar with spaces', content=b''),
43 FileNode('foo/bar/baz', content='Inside'),
42 FileNode(b'foo/bar/baz', content=b'Inside'),
44 FileNode(
43 FileNode(b'foo/bar/file.bin', content=(
45 'foo/bar/file.bin',
44 b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1\x00\x00\x00\x00\x00\x00'
46 content=(
45 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x03\x00\xfe'
47 '\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1\x00\x00\x00\x00\x00\x00'
46 b'\xff\t\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
48 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x03\x00\xfe'
47 b'\x01\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00'
49 '\xff\t\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
48 b'\x00\x18\x00\x00\x00\x01\x00\x00\x00\xfe\xff\xff\xff\x00\x00'
50 '\x01\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00'
49 b'\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff'
51 '\x00\x18\x00\x00\x00\x01\x00\x00\x00\xfe\xff\xff\xff\x00\x00'
50 b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
52 '\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff'
53 '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
54 )
51 )
55 ),
52 ),
56 ]
53 ]
@@ -77,94 +74,98 b' class TestInMemoryCommit(BackendTestMixi'
77 self.imc.add(node)
74 self.imc.add(node)
78
75
79 self.commit()
76 self.commit()
80 self.assert_succesful_commit(nodes)
77 self.assert_successful_commit(nodes)
81
78
82 @pytest.mark.backends("hg")
79 @pytest.mark.backends("hg")
83 def test_add_on_branch_hg(self, nodes):
80 def test_add_on_branch_hg(self, nodes):
84 for node in nodes:
81 for node in nodes:
85 self.imc.add(node)
82 self.imc.add(node)
86 self.commit(branch=u'stable')
83 self.commit(branch='stable')
87 self.assert_succesful_commit(nodes)
84 self.assert_successful_commit(nodes)
88
85
89 @pytest.mark.backends("git")
86 @pytest.mark.backends("git")
90 def test_add_on_branch_git(self, nodes):
87 def test_add_on_branch_git(self, nodes):
91 for node in nodes:
88 for node in nodes:
92 self.imc.add(node)
89 self.imc.add(node)
93 self.commit(branch=u'stable')
90 self.commit(branch='stable')
94 self.assert_succesful_commit(nodes)
91 self.assert_successful_commit(nodes)
95
92
96 def test_add_in_bulk(self, nodes):
93 def test_add_in_bulk(self, nodes):
97 self.imc.add(*nodes)
94 self.imc.add(*nodes)
98
95
99 self.commit()
96 self.commit()
100 self.assert_succesful_commit(nodes)
97 self.assert_successful_commit(nodes)
101
98
102 def test_add_non_ascii_files(self):
99 def test_add_non_ascii_files(self):
103 nodes = [
100 nodes = [
104 FileNode('ΕΌΓ³Ε‚wik/zwierzΔ…tko_utf8_str', content='Δ‡Δ‡Δ‡Δ‡'),
101 FileNode(safe_bytes('ΕΌΓ³Ε‚wik/zwierzΔ…tko_utf8_str'),
105 FileNode(u'ΕΌΓ³Ε‚wik/zwierzΔ…tko_unicode', content=u'Δ‡Δ‡Δ‡Δ‡'),
102 content=safe_bytes('Δ‡Δ‡Δ‡Δ‡')),
103 FileNode(safe_bytes('ΕΌΓ³Ε‚wik/zwierzΔ…tko_unicode'),
104 content=safe_bytes('Δ‡Δ‡Δ‡Δ‡')),
106 ]
105 ]
107
106
108 for node in nodes:
107 for node in nodes:
109 self.imc.add(node)
108 self.imc.add(node)
110
109
111 self.commit()
110 self.commit()
112 self.assert_succesful_commit(nodes)
111 self.assert_successful_commit(nodes)
113
112
114 def commit(self, branch=None):
113 def commit(self, branch=None):
115 self.old_commit_count = len(self.repo.commit_ids)
114 self.old_commit_count = len(self.repo.commit_ids)
116 self.commit_message = u'Test commit with unicode: ΕΌΓ³Ε‚wik'
115 self.commit_message = 'Test commit with unicode: ΕΌΓ³Ε‚wik'
117 self.commit_author = u'{} <foo@email.com>'.format(self.__class__.__name__)
116 self.commit_author = f'{self.__class__.__name__} <foo@email.com>'
118 self.commit = self.imc.commit(
117 self.commit = self.imc.commit(
119 message=self.commit_message, author=self.commit_author,
118 message=self.commit_message, author=self.commit_author,
120 branch=branch)
119 branch=branch)
121
120
122 def test_add_actually_adds_all_nodes_at_second_commit_too(self):
121 def test_add_actually_adds_all_nodes_at_second_commit_too(self):
123 to_add = [
122 to_add = [
124 FileNode('foo/bar/image.png', content='\0'),
123 FileNode(b'foo/bar/image.png', content=b'\0'),
125 FileNode('foo/README.txt', content='readme!'),
124 FileNode(b'foo/README.txt', content=b'readme!'),
126 ]
125 ]
127 self.imc.add(*to_add)
126 self.imc.add(*to_add)
128 commit = self.imc.commit(u'Initial', u'joe doe <joe.doe@example.com>')
127 commit = self.imc.commit('Initial', 'joe doe <joe.doe@example.com>')
129 assert isinstance(commit.get_node('foo'), DirNode)
128 assert isinstance(commit.get_node('foo'), DirNode)
130 assert isinstance(commit.get_node('foo/bar'), DirNode)
129 assert isinstance(commit.get_node('foo/bar'), DirNode)
131 self.assert_nodes_in_commit(commit, to_add)
130 self.assert_nodes_in_commit(commit, to_add)
132
131
133 # commit some more files again
132 # commit some more files again
134 to_add = [
133 to_add = [
135 FileNode('foo/bar/foobaz/bar', content='foo'),
134 FileNode(b'foo/bar/foobaz/bar', content=b'foo'),
136 FileNode('foo/bar/another/bar', content='foo'),
135 FileNode(b'foo/bar/another/bar', content=b'foo'),
137 FileNode('foo/baz.txt', content='foo'),
136 FileNode(b'foo/baz.txt', content=b'foo'),
138 FileNode('foobar/foobaz/file', content='foo'),
137 FileNode(b'foobar/foobaz/file', content=b'foo'),
139 FileNode('foobar/barbaz', content='foo'),
138 FileNode(b'foobar/barbaz', content=b'foo'),
140 ]
139 ]
141 self.imc.add(*to_add)
140 self.imc.add(*to_add)
142 commit = self.imc.commit(u'Another', u'joe doe <joe.doe@example.com>')
141 commit = self.imc.commit('Another', 'joe doe <joe.doe@example.com>')
143 self.assert_nodes_in_commit(commit, to_add)
142 self.assert_nodes_in_commit(commit, to_add)
144
143
145 def test_add_raise_already_added(self):
144 def test_add_raise_already_added(self):
146 node = FileNode('foobar', content='baz')
145 node = FileNode(b'foobar', content=b'baz')
147 self.imc.add(node)
146 self.imc.add(node)
148 with pytest.raises(NodeAlreadyAddedError):
147 with pytest.raises(NodeAlreadyAddedError):
149 self.imc.add(node)
148 self.imc.add(node)
150
149
151 def test_check_integrity_raise_already_exist(self):
150 def test_check_integrity_raise_already_exist(self):
152 node = FileNode('foobar', content='baz')
151 node = FileNode(b'foobar', content=b'baz')
153 self.imc.add(node)
152 self.imc.add(node)
154 self.imc.commit(message=u'Added foobar', author=u'{} <foo@bar.com>'.format(self))
153 self.imc.commit(message='Added foobar',
154 author='Some Name <foo@bar.com>')
155 self.imc.add(node)
155 self.imc.add(node)
156 with pytest.raises(NodeAlreadyExistsError):
156 with pytest.raises(NodeAlreadyExistsError):
157 self.imc.commit(message='new message', author=u'{} <foo@bar.com>'.format(self))
157 self.imc.commit(message='new message',
158 author='Some Name <foo@bar.com>')
158
159
159 def test_change(self):
160 def test_change(self):
160 self.imc.add(FileNode('foo/bar/baz', content='foo'))
161 self.imc.add(FileNode(b'foo/bar/baz', content=b'foo'))
161 self.imc.add(FileNode('foo/fbar', content='foobar'))
162 self.imc.add(FileNode(b'foo/fbar', content=b'foobar'))
162 tip = self.imc.commit(u'Initial', u'joe doe <joe.doe@example.com>')
163 tip = self.imc.commit('Initial', 'joe doe <joe.doe@example.com>')
163
164
164 # Change node's content
165 # Change node's content
165 node = FileNode('foo/bar/baz', content='My **changed** content')
166 node = FileNode(b'foo/bar/baz', content=b'My **changed** content')
166 self.imc.change(node)
167 self.imc.change(node)
167 self.imc.commit(u'Changed %s' % node.path, u'joe doe <joe.doe@example.com>')
168 self.imc.commit('Changed %s' % node.path, 'joe doe <joe.doe@example.com>')
168
169
169 newtip = self.repo.get_commit()
170 newtip = self.repo.get_commit()
170 assert tip != newtip
171 assert tip != newtip
@@ -173,25 +174,28 b' class TestInMemoryCommit(BackendTestMixi'
173
174
174 def test_change_non_ascii(self):
175 def test_change_non_ascii(self):
175 to_add = [
176 to_add = [
176 FileNode('ΕΌΓ³Ε‚wik/zwierzΔ…tko', content='Δ‡Δ‡Δ‡Δ‡'),
177 FileNode(safe_bytes('ΕΌΓ³Ε‚wik/zwierzΔ…tko'),
177 FileNode(u'ΕΌΓ³Ε‚wik/zwierzΔ…tko_uni', content=u'Δ‡Δ‡Δ‡Δ‡'),
178 content=safe_bytes('Δ‡Δ‡Δ‡Δ‡')),
179 FileNode(safe_bytes('ΕΌΓ³Ε‚wik/zwierzΔ…tko_uni'),
180 content=safe_bytes('Δ‡Δ‡Δ‡Δ‡')),
178 ]
181 ]
179 for node in to_add:
182 for node in to_add:
180 self.imc.add(node)
183 self.imc.add(node)
181
184
182 tip = self.imc.commit(u'Initial', u'joe doe <joe.doe@example.com>')
185 tip = self.imc.commit('Initial', 'joe doe <joe.doe@example.com>')
183
186
184 # Change node's content
187 # Change node's content
185 node = FileNode('ΕΌΓ³Ε‚wik/zwierzΔ…tko', content='My **changed** content')
188 node = FileNode(safe_bytes('ΕΌΓ³Ε‚wik/zwierzΔ…tko'),
189 content=b'My **changed** content')
186 self.imc.change(node)
190 self.imc.change(node)
187 self.imc.commit(u'Changed %s' % safe_unicode(node.path),
191 self.imc.commit('Changed %s' % safe_str(node.path),
188 author=u'joe doe <joe.doe@example.com>')
192 author='joe doe <joe.doe@example.com>')
189
193
190 node_uni = FileNode(
194 node_uni = FileNode(safe_bytes('ΕΌΓ³Ε‚wik/zwierzΔ…tko_uni'),
191 u'ΕΌΓ³Ε‚wik/zwierzΔ…tko_uni', content=u'My **changed** content')
195 content=b'My **changed** content')
192 self.imc.change(node_uni)
196 self.imc.change(node_uni)
193 self.imc.commit(u'Changed %s' % safe_unicode(node_uni.path),
197 self.imc.commit('Changed %s' % safe_str(node_uni.path),
194 author=u'joe doe <joe.doe@example.com>')
198 author='joe doe <joe.doe@example.com>')
195
199
196 newtip = self.repo.get_commit()
200 newtip = self.repo.get_commit()
197 assert tip != newtip
201 assert tip != newtip
@@ -200,24 +204,24 b' class TestInMemoryCommit(BackendTestMixi'
200 self.assert_nodes_in_commit(newtip, (node, node_uni))
204 self.assert_nodes_in_commit(newtip, (node, node_uni))
201
205
202 def test_change_raise_empty_repository(self):
206 def test_change_raise_empty_repository(self):
203 node = FileNode('foobar')
207 node = FileNode(b'foobar')
204 with pytest.raises(EmptyRepositoryError):
208 with pytest.raises(EmptyRepositoryError):
205 self.imc.change(node)
209 self.imc.change(node)
206
210
207 def test_check_integrity_change_raise_node_does_not_exist(self):
211 def test_check_integrity_change_raise_node_does_not_exist(self):
208 node = FileNode('foobar', content='baz')
212 node = FileNode(b'foobar', content=b'baz')
209 self.imc.add(node)
213 self.imc.add(node)
210 self.imc.commit(message=u'Added foobar', author=u'{} <foo@bar.com>'.format(self))
214 self.imc.commit(message='Added foobar', author='Some Name <foo@bar.com>')
211 node = FileNode('not-foobar', content='')
215 node = FileNode(b'not-foobar', content=b'')
212 self.imc.change(node)
216 self.imc.change(node)
213 with pytest.raises(NodeDoesNotExistError):
217 with pytest.raises(NodeDoesNotExistError):
214 self.imc.commit(message='Changed not existing node', author=u'{} <foo@bar.com>'.format(self))
218 self.imc.commit(message='Changed not existing node', author='Some Name <foo@bar.com>')
215
219
216 def test_change_raise_node_already_changed(self):
220 def test_change_raise_node_already_changed(self):
217 node = FileNode('foobar', content='baz')
221 node = FileNode(b'foobar', content=b'baz')
218 self.imc.add(node)
222 self.imc.add(node)
219 self.imc.commit(message=u'Added foobar', author=u'{} <foo@bar.com>'.format(self))
223 self.imc.commit(message='Added foobar', author='Some Nam <foo@bar.com>')
220 node = FileNode('foobar', content='more baz')
224 node = FileNode(b'foobar', content=b'more baz')
221 self.imc.change(node)
225 self.imc.change(node)
222 with pytest.raises(NodeAlreadyChangedError):
226 with pytest.raises(NodeAlreadyChangedError):
223 self.imc.change(node)
227 self.imc.change(node)
@@ -225,18 +229,18 b' class TestInMemoryCommit(BackendTestMixi'
225 def test_check_integrity_change_raise_node_not_changed(self, nodes):
229 def test_check_integrity_change_raise_node_not_changed(self, nodes):
226 self.test_add(nodes) # Performs first commit
230 self.test_add(nodes) # Performs first commit
227
231
228 node = FileNode(nodes[0].path, content=nodes[0].content)
232 node = FileNode(nodes[0].bytes_path, content=nodes[0].content)
229 self.imc.change(node)
233 self.imc.change(node)
230 with pytest.raises(NodeNotChangedError):
234 with pytest.raises(NodeNotChangedError):
231 self.imc.commit(
235 self.imc.commit(
232 message=u'Trying to mark node as changed without touching it',
236 message='Trying to mark node as changed without touching it',
233 author=u'{} <foo@bar.com>'.format(self))
237 author='Some Name <foo@bar.com>')
234
238
235 def test_change_raise_node_already_removed(self):
239 def test_change_raise_node_already_removed(self):
236 node = FileNode('foobar', content='baz')
240 node = FileNode(b'foobar', content=b'baz')
237 self.imc.add(node)
241 self.imc.add(node)
238 self.imc.commit(message=u'Added foobar', author=u'{} <foo@bar.com>'.format(self))
242 self.imc.commit(message='Added foobar', author='Some Name <foo@bar.com>')
239 self.imc.remove(FileNode('foobar'))
243 self.imc.remove(FileNode(b'foobar'))
240 with pytest.raises(NodeAlreadyRemovedError):
244 with pytest.raises(NodeAlreadyRemovedError):
241 self.imc.change(node)
245 self.imc.change(node)
242
246
@@ -247,8 +251,7 b' class TestInMemoryCommit(BackendTestMixi'
247 node = nodes[0]
251 node = nodes[0]
248 assert node.content == tip.get_node(node.path).content
252 assert node.content == tip.get_node(node.path).content
249 self.imc.remove(node)
253 self.imc.remove(node)
250 self.imc.commit(
254 self.imc.commit(message=f'Removed {node.path}', author='Some Name <foo@bar.com>')
251 message=u'Removed %s' % node.path, author=u'{} <foo@bar.com>'.format(self))
252
255
253 newtip = self.repo.get_commit()
256 newtip = self.repo.get_commit()
254 assert tip != newtip
257 assert tip != newtip
@@ -257,12 +260,12 b' class TestInMemoryCommit(BackendTestMixi'
257 newtip.get_node(node.path)
260 newtip.get_node(node.path)
258
261
259 def test_remove_last_file_from_directory(self):
262 def test_remove_last_file_from_directory(self):
260 node = FileNode('omg/qwe/foo/bar', content='foobar')
263 node = FileNode(b'omg/qwe/foo/bar', content=b'foobar')
261 self.imc.add(node)
264 self.imc.add(node)
262 self.imc.commit(u'added', author=u'joe doe <joe@doe.com>')
265 self.imc.commit('added', author='joe doe <joe@doe.com>')
263
266
264 self.imc.remove(node)
267 self.imc.remove(node)
265 tip = self.imc.commit(u'removed', u'joe doe <joe@doe.com>')
268 tip = self.imc.commit('removed', 'joe doe <joe@doe.com>')
266 with pytest.raises(NodeDoesNotExistError):
269 with pytest.raises(NodeDoesNotExistError):
267 tip.get_node('omg/qwe/foo/bar')
270 tip.get_node('omg/qwe/foo/bar')
268
271
@@ -271,22 +274,22 b' class TestInMemoryCommit(BackendTestMixi'
271 with pytest.raises(NodeDoesNotExistError):
274 with pytest.raises(NodeDoesNotExistError):
272 self.imc.commit(
275 self.imc.commit(
273 message='Trying to remove node at empty repository',
276 message='Trying to remove node at empty repository',
274 author=u'{} <foo@bar.com>'.format(self))
277 author='Some Name <foo@bar.com>')
275
278
276 def test_check_integrity_remove_raise_node_does_not_exist(self, nodes):
279 def test_check_integrity_remove_raise_node_does_not_exist(self, nodes):
277 self.test_add(nodes) # Performs first commit
280 self.test_add(nodes) # Performs first commit
278
281
279 node = FileNode('no-such-file')
282 node = FileNode(b'no-such-file')
280 self.imc.remove(node)
283 self.imc.remove(node)
281 with pytest.raises(NodeDoesNotExistError):
284 with pytest.raises(NodeDoesNotExistError):
282 self.imc.commit(
285 self.imc.commit(
283 message=u'Trying to remove not existing node',
286 message='Trying to remove not existing node',
284 author=u'{} <foo@bar.com>'.format(self))
287 author='Some Name <foo@bar.com>')
285
288
286 def test_remove_raise_node_already_removed(self, nodes):
289 def test_remove_raise_node_already_removed(self, nodes):
287 self.test_add(nodes) # Performs first commit
290 self.test_add(nodes) # Performs first commit
288
291
289 node = FileNode(nodes[0].path)
292 node = FileNode(nodes[0].bytes_path)
290 self.imc.remove(node)
293 self.imc.remove(node)
291 with pytest.raises(NodeAlreadyRemovedError):
294 with pytest.raises(NodeAlreadyRemovedError):
292 self.imc.remove(node)
295 self.imc.remove(node)
@@ -294,15 +297,15 b' class TestInMemoryCommit(BackendTestMixi'
294 def test_remove_raise_node_already_changed(self, nodes):
297 def test_remove_raise_node_already_changed(self, nodes):
295 self.test_add(nodes) # Performs first commit
298 self.test_add(nodes) # Performs first commit
296
299
297 node = FileNode(nodes[0].path, content='Bending time')
300 node = FileNode(nodes[0].bytes_path, content=b'Bending time')
298 self.imc.change(node)
301 self.imc.change(node)
299 with pytest.raises(NodeAlreadyChangedError):
302 with pytest.raises(NodeAlreadyChangedError):
300 self.imc.remove(node)
303 self.imc.remove(node)
301
304
302 def test_reset(self):
305 def test_reset(self):
303 self.imc.add(FileNode('foo', content='bar'))
306 self.imc.add(FileNode(b'foo', content=b'bar'))
304 # self.imc.change(FileNode('baz', content='new'))
307 # self.imc.change(FileNode(b'baz', content='new'))
305 # self.imc.remove(FileNode('qwe'))
308 # self.imc.remove(FileNode(b'qwe'))
306 self.imc.reset()
309 self.imc.reset()
307 assert not any((self.imc.added, self.imc.changed, self.imc.removed))
310 assert not any((self.imc.added, self.imc.changed, self.imc.removed))
308
311
@@ -310,11 +313,11 b' class TestInMemoryCommit(BackendTestMixi'
310 N = 3 # number of commits to perform
313 N = 3 # number of commits to perform
311 last = None
314 last = None
312 for x in range(N):
315 for x in range(N):
313 fname = 'file%s' % str(x).rjust(5, '0')
316 fname = safe_bytes('file%s' % str(x).rjust(5, '0'))
314 content = 'foobar\n' * x
317 content = safe_bytes('foobar\n' * x)
315 node = FileNode(fname, content=content)
318 node = FileNode(fname, content=content)
316 self.imc.add(node)
319 self.imc.add(node)
317 commit = self.imc.commit(u"Commit no. %s" % (x + 1), author=u'vcs <foo@bar.com>')
320 commit = self.imc.commit("Commit no. %s" % (x + 1), author='Vcs User <foo@bar.com>')
318 assert last != commit
321 assert last != commit
319 last = commit
322 last = commit
320
323
@@ -326,16 +329,16 b' class TestInMemoryCommit(BackendTestMixi'
326 assert len(repo.commit_ids) == N
329 assert len(repo.commit_ids) == N
327
330
328 def test_date_attr(self, local_dt_to_utc):
331 def test_date_attr(self, local_dt_to_utc):
329 node = FileNode('foobar.txt', content='Foobared!')
332 node = FileNode(b'foobar.txt', content=b'Foobared!')
330 self.imc.add(node)
333 self.imc.add(node)
331 date = datetime.datetime(1985, 1, 30, 1, 45)
334 date = datetime.datetime(1985, 1, 30, 1, 45)
332 commit = self.imc.commit(
335 commit = self.imc.commit(
333 u"Committed at time when I was born ;-)",
336 "Committed at time when I was born ;-)",
334 author=u'{} <foo@bar.com>'.format(self), date=date)
337 author='Test User <foo@bar.com>', date=date)
335
338
336 assert commit.date == local_dt_to_utc(date)
339 assert commit.date == local_dt_to_utc(date)
337
340
338 def assert_succesful_commit(self, added_nodes):
341 def assert_successful_commit(self, added_nodes):
339 newtip = self.repo.get_commit()
342 newtip = self.repo.get_commit()
340 assert self.commit == newtip
343 assert self.commit == newtip
341 assert self.old_commit_count + 1 == len(self.repo.commit_ids)
344 assert self.old_commit_count + 1 == len(self.repo.commit_ids)
@@ -346,4 +349,5 b' class TestInMemoryCommit(BackendTestMixi'
346
349
347 def assert_nodes_in_commit(self, commit, nodes):
350 def assert_nodes_in_commit(self, commit, nodes):
348 for node in nodes:
351 for node in nodes:
352 assert commit.get_node(node.path).path == node.path
349 assert commit.get_node(node.path).content == node.content
353 assert commit.get_node(node.path).content == node.content
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -22,6 +21,7 b' import stat'
22
21
23 import pytest
22 import pytest
24
23
24 from rhodecode.lib.str_utils import safe_bytes
25 from rhodecode.lib.vcs.nodes import DirNode
25 from rhodecode.lib.vcs.nodes import DirNode
26 from rhodecode.lib.vcs.nodes import FileNode
26 from rhodecode.lib.vcs.nodes import FileNode
27 from rhodecode.lib.vcs.nodes import Node
27 from rhodecode.lib.vcs.nodes import Node
@@ -34,29 +34,29 b' from rhodecode.tests.vcs.conftest import'
34 def binary_filenode():
34 def binary_filenode():
35 def node_maker(filename):
35 def node_maker(filename):
36 data = (
36 data = (
37 "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00"
37 b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00"
38 "\x10\x08\x06\x00\x00\x00\x1f??a\x00\x00\x00\x04gAMA\x00\x00\xaf?7"
38 b"\x10\x08\x06\x00\x00\x00\x1f??a\x00\x00\x00\x04gAMA\x00\x00\xaf?7"
39 "\x05\x8a?\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq?e<\x00"
39 b"\x05\x8a?\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq?e<\x00"
40 "\x00\x025IDAT8?\xa5\x93?K\x94Q\x14\x87\x9f\xf7?Q\x1bs4?\x03\x9a"
40 b"\x00\x025IDAT8?\xa5\x93?K\x94Q\x14\x87\x9f\xf7?Q\x1bs4?\x03\x9a"
41 "\xa8?B\x02\x8b$\x10[U;i\x13?6h?&h[?\"\x14j?\xa2M\x7fB\x14F\x9aQ?&"
41 b"\xa8?B\x02\x8b$\x10[U;i\x13?6h?&h[?\"\x14j?\xa2M\x7fB\x14F\x9aQ?&"
42 "\x842?\x0b\x89\"\x82??!?\x9c!\x9c2l??{N\x8bW\x9dY\xb4\t/\x1c?="
42 b"\x842?\x0b\x89\"\x82??!?\x9c!\x9c2l??{N\x8bW\x9dY\xb4\t/\x1c?="
43 "\x9b?}????\xa9*;9!?\x83\x91?[?\\v*?D\x04\'`EpNp\xa2X\'U?pVq\"Sw."
43 b"\x9b?}????\xa9*;9!?\x83\x91?[?\\v*?D\x04\'`EpNp\xa2X\'U?pVq\"Sw."
44 "\x1e?\x08\x01D?jw????\xbc??7{|\x9b?\x89$\x01??W@\x15\x9c\x05q`Lt/"
44 b"\x1e?\x08\x01D?jw????\xbc??7{|\x9b?\x89$\x01??W@\x15\x9c\x05q`Lt/"
45 "\x97?\x94\xa1d?\x18~?\x18?\x18W[%\xb0?\x83??\x14\x88\x8dB?\xa6H"
45 b"\x97?\x94\xa1d?\x18~?\x18?\x18W[%\xb0?\x83??\x14\x88\x8dB?\xa6H"
46 "\tL\tl\x19>/\x01`\xac\xabx?\x9cl\nx\xb0\x98\x07\x95\x88D$\"q["
46 b"\tL\tl\x19>/\x01`\xac\xabx?\x9cl\nx\xb0\x98\x07\x95\x88D$\"q["
47 "\x19?d\x00(o\n\xa0??\x7f\xb9\xa4?\x1bF\x1f\x8e\xac\xa8?j??eUU}?.?"
47 b"\x19?d\x00(o\n\xa0??\x7f\xb9\xa4?\x1bF\x1f\x8e\xac\xa8?j??eUU}?.?"
48 "\x9f\x8cE??x\x94??\r\xbdtoJU5\"0N\x10U?\x00??V\t\x02\x9f\x81?U?"
48 b"\x9f\x8cE??x\x94??\r\xbdtoJU5\"0N\x10U?\x00??V\t\x02\x9f\x81?U?"
49 "\x00\x9eM\xae2?r\x9b7\x83\x82\x8aP3????.?&\"?\xb7ZP \x0c<?O"
49 b"\x00\x9eM\xae2?r\x9b7\x83\x82\x8aP3????.?&\"?\xb7ZP \x0c<?O"
50 "\xa5\t}\xb8?\x99\xa6?\x87?\x1di|/\xa0??0\xbe\x1fp?d&\x1a\xad"
50 b"\xa5\t}\xb8?\x99\xa6?\x87?\x1di|/\xa0??0\xbe\x1fp?d&\x1a\xad"
51 "\x95\x8a\x07?\t*\x10??b:?d?.\x13C\x8a?\x12\xbe\xbf\x8e?{???"
51 b"\x95\x8a\x07?\t*\x10??b:?d?.\x13C\x8a?\x12\xbe\xbf\x8e?{???"
52 "\x08?\x80\xa7\x13+d\x13>J?\x80\x15T\x95\x9a\x00??S\x8c\r?\xa1"
52 b"\x08?\x80\xa7\x13+d\x13>J?\x80\x15T\x95\x9a\x00??S\x8c\r?\xa1"
53 "\x03\x07?\x96\x9b\xa7\xab=E??\xa4\xb3?\x19q??B\x91=\x8d??k?J"
53 b"\x03\x07?\x96\x9b\xa7\xab=E??\xa4\xb3?\x19q??B\x91=\x8d??k?J"
54 "\x0bV\"??\xf7x?\xa1\x00?\\.\x87\x87???\x02F@D\x99],??\x10#?X"
54 b"\x0bV\"??\xf7x?\xa1\x00?\\.\x87\x87???\x02F@D\x99],??\x10#?X"
55 "\xb7=\xb9\x10?Z\x1by???cI??\x1ag?\x92\xbc?T?t[\x92\x81?<_\x17~"
55 b"\xb7=\xb9\x10?Z\x1by???cI??\x1ag?\x92\xbc?T?t[\x92\x81?<_\x17~"
56 "\x92\x88?H%?\x10Q\x02\x9f\n\x81qQ\x0bm?\x1bX?\xb1AK\xa6\x9e\xb9?u"
56 b"\x92\x88?H%?\x10Q\x02\x9f\n\x81qQ\x0bm?\x1bX?\xb1AK\xa6\x9e\xb9?u"
57 "\xb2?1\xbe|/\x92M@\xa2!F?\xa9>\"\r<DT?>\x92\x8e?>\x9a9Qv\x127?a"
57 b"\xb2?1\xbe|/\x92M@\xa2!F?\xa9>\"\r<DT?>\x92\x8e?>\x9a9Qv\x127?a"
58 "\xac?Y?8?:??]X???9\x80\xb7?u?\x0b#BZ\x8d=\x1d?p\x00\x00\x00\x00"
58 b"\xac?Y?8?:??]X???9\x80\xb7?u?\x0b#BZ\x8d=\x1d?p\x00\x00\x00\x00"
59 "IEND\xaeB`\x82")
59 b"IEND\xaeB`\x82")
60 return FileNode(filename, content=data)
60 return FileNode(filename, content=data)
61 return node_maker
61 return node_maker
62
62
@@ -68,8 +68,9 b' class TestNodeBasics:'
68 "kind", [NodeKind.FILE, NodeKind.DIR], ids=["FILE", "DIR"])
68 "kind", [NodeKind.FILE, NodeKind.DIR], ids=["FILE", "DIR"])
69 def test_init_wrong_paths(self, path, kind):
69 def test_init_wrong_paths(self, path, kind):
70 """
70 """
71 Cannot innitialize Node objects with path with slash at the beginning.
71 Cannot initialize Node objects with path with slash at the beginning.
72 """
72 """
73 path = safe_bytes(path)
73 with pytest.raises(NodeError):
74 with pytest.raises(NodeError):
74 Node(path, kind)
75 Node(path, kind)
75
76
@@ -77,47 +78,49 b' class TestNodeBasics:'
77 @pytest.mark.parametrize(
78 @pytest.mark.parametrize(
78 "kind", [NodeKind.FILE, NodeKind.DIR], ids=["FILE", "DIR"])
79 "kind", [NodeKind.FILE, NodeKind.DIR], ids=["FILE", "DIR"])
79 def test_name(self, path, kind):
80 def test_name(self, path, kind):
81 path = safe_bytes(path)
80 node = Node(path, kind)
82 node = Node(path, kind)
81 assert node.name == 'path'
83 assert node.name == 'path'
82
84
83 def test_name_root(self):
85 def test_name_root(self):
84 node = Node('', NodeKind.DIR)
86 node = Node(b'', NodeKind.DIR)
85 assert node.name == ''
87 assert node.name == ''
86
88
87 def test_root_node_cannot_be_file(self):
89 def test_root_node_cannot_be_file(self):
88 with pytest.raises(NodeError):
90 with pytest.raises(NodeError):
89 Node('', NodeKind.FILE)
91 Node(b'', NodeKind.FILE)
90
92
91 def test_kind_setter(self):
93 def test_kind_setter(self):
92 node = Node('', NodeKind.DIR)
94 node = Node(b'', NodeKind.DIR)
93 with pytest.raises(NodeError):
95 with pytest.raises(NodeError):
94 node.kind = NodeKind.FILE
96 node.kind = NodeKind.FILE
95
97
96 def test_compare_equal(self):
98 def test_compare_equal(self):
97 node1 = FileNode('test', content='')
99 node1 = FileNode(b'test', content=b'')
98 node2 = FileNode('test', content='')
100 node2 = FileNode(b'test', content=b'')
99 assert node1 == node2
101 assert node1 == node2
100 assert not node1 != node2
102 assert not node1 != node2
101
103
102 def test_compare_unequal(self):
104 def test_compare_unequal(self):
103 node1 = FileNode('test', content='a')
105 node1 = FileNode(b'test', content=b'a')
104 node2 = FileNode('test', content='b')
106 node2 = FileNode(b'test', content=b'b')
105 assert node1 != node2
107 assert node1 != node2
106 assert not node1 == node2
108 assert not node1 == node2
107
109
108 @pytest.mark.parametrize("node_path, expected_parent_path", [
110 @pytest.mark.parametrize("node_path, expected_parent_path", [
109 ('', ''),
111 ('', b''),
110 ('some/path/', 'some/'),
112 ('some/path/', b'some/'),
111 ('some/longer/path/', 'some/longer/'),
113 ('some/longer/path/', b'some/longer/'),
112 ])
114 ])
113 def test_parent_path_new(self, node_path, expected_parent_path):
115 def test_parent_path_new(self, node_path, expected_parent_path):
114 """
116 """
115 Tests if node's parent path are properly computed.
117 Tests if node's parent path are properly computed.
116 """
118 """
119 node_path = safe_bytes(node_path)
117 node = Node(node_path, NodeKind.DIR)
120 node = Node(node_path, NodeKind.DIR)
118 parent_path = node.get_parent_path()
121 parent_path = node.get_parent_path()
119 assert (parent_path.endswith('/') or
122 assert (parent_path.endswith(b'/') or
120 node.is_root() and parent_path == '')
123 node.is_root() and parent_path == b'')
121 assert parent_path == expected_parent_path
124 assert parent_path == expected_parent_path
122
125
123 '''
126 '''
@@ -134,34 +137,34 b' class TestNodeBasics:'
134 '''
137 '''
135
138
136 def test_is_file(self):
139 def test_is_file(self):
137 node = Node('any', NodeKind.FILE)
140 node = Node(b'any', NodeKind.FILE)
138 assert node.is_file()
141 assert node.is_file()
139
142
140 node = FileNode('any')
143 node = FileNode(b'any')
141 assert node.is_file()
144 assert node.is_file()
142 with pytest.raises(AttributeError):
145 with pytest.raises(AttributeError):
143 node.nodes
146 node.nodes # noqa
144
147
145 def test_is_dir(self):
148 def test_is_dir(self):
146 node = Node('any_dir', NodeKind.DIR)
149 node = Node(b'any_dir', NodeKind.DIR)
147 assert node.is_dir()
150 assert node.is_dir()
148
151
149 node = DirNode('any_dir')
152 node = DirNode(b'any_dir')
150
153
151 assert node.is_dir()
154 assert node.is_dir()
152 with pytest.raises(NodeError):
155 with pytest.raises(NodeError):
153 node.content
156 node.content # noqa
154
157
155 def test_dir_node_iter(self):
158 def test_dir_node_iter(self):
156 nodes = [
159 nodes = [
157 DirNode('docs'),
160 DirNode(b'docs'),
158 DirNode('tests'),
161 DirNode(b'tests'),
159 FileNode('bar'),
162 FileNode(b'bar'),
160 FileNode('foo'),
163 FileNode(b'foo'),
161 FileNode('readme.txt'),
164 FileNode(b'readme.txt'),
162 FileNode('setup.py'),
165 FileNode(b'setup.py'),
163 ]
166 ]
164 dirnode = DirNode('', nodes=nodes)
167 dirnode = DirNode(b'', nodes=nodes)
165 for node in dirnode:
168 for node in dirnode:
166 assert node == dirnode.get_node(node.path)
169 assert node == dirnode.get_node(node.path)
167
170
@@ -169,15 +172,15 b' class TestNodeBasics:'
169 """
172 """
170 Without link to commit nodes should raise NodeError.
173 Without link to commit nodes should raise NodeError.
171 """
174 """
172 node = FileNode('anything')
175 node = FileNode(b'anything')
173 with pytest.raises(NodeError):
176 with pytest.raises(NodeError):
174 node.state
177 node.state # noqa
175 node = DirNode('anything')
178 node = DirNode(b'anything')
176 with pytest.raises(NodeError):
179 with pytest.raises(NodeError):
177 node.state
180 node.state # noqa
178
181
179 def test_file_node_stat(self):
182 def test_file_node_stat(self):
180 node = FileNode('foobar', 'empty... almost')
183 node = FileNode(b'foobar', b'empty... almost')
181 mode = node.mode # default should be 0100644
184 mode = node.mode # default should be 0100644
182 assert mode & stat.S_IRUSR
185 assert mode & stat.S_IRUSR
183 assert mode & stat.S_IWUSR
186 assert mode & stat.S_IWUSR
@@ -190,29 +193,29 b' class TestNodeBasics:'
190 assert not mode & stat.S_IXOTH
193 assert not mode & stat.S_IXOTH
191
194
192 def test_file_node_is_executable(self):
195 def test_file_node_is_executable(self):
193 node = FileNode('foobar', 'empty... almost', mode=0o100755)
196 node = FileNode(b'foobar', b'empty... almost', mode=0o100755)
194 assert node.is_executable
197 assert node.is_executable
195
198
196 node = FileNode('foobar', 'empty... almost', mode=0o100500)
199 node = FileNode(b'foobar', b'empty... almost', mode=0o100500)
197 assert node.is_executable
200 assert node.is_executable
198
201
199 node = FileNode('foobar', 'empty... almost', mode=0o100644)
202 node = FileNode(b'foobar', b'empty... almost', mode=0o100644)
200 assert not node.is_executable
203 assert not node.is_executable
201
204
202 def test_file_node_is_not_symlink(self):
205 def test_file_node_is_not_symlink(self):
203 node = FileNode('foobar', 'empty...')
206 node = FileNode(b'foobar', b'empty...')
204 assert not node.is_link()
207 assert not node.is_link()
205
208
206 def test_mimetype(self):
209 def test_mimetype(self):
207 py_node = FileNode('test.py')
210 py_node = FileNode(b'test.py')
208 tar_node = FileNode('test.tar.gz')
211 tar_node = FileNode(b'test.tar.gz')
209
212
210 ext = 'CustomExtension'
213 ext = 'CustomExtension'
211
214
212 my_node2 = FileNode('myfile2')
215 my_node2 = FileNode(b'myfile2')
213 my_node2._mimetype = [ext]
216 my_node2._mimetype = [ext]
214
217
215 my_node3 = FileNode('myfile3')
218 my_node3 = FileNode(b'myfile3')
216 my_node3._mimetype = [ext, ext]
219 my_node3._mimetype = [ext, ext]
217
220
218 assert py_node.mimetype == 'text/x-python'
221 assert py_node.mimetype == 'text/x-python'
@@ -229,20 +232,20 b' class TestNodeBasics:'
229
232
230 def test_lines_counts(self):
233 def test_lines_counts(self):
231 lines = [
234 lines = [
232 'line1\n',
235 b'line1\n',
233 'line2\n',
236 b'line2\n',
234 'line3\n',
237 b'line3\n',
235 '\n',
238 b'\n',
236 '\n',
239 b'\n',
237 'line4\n',
240 b'line4\n',
238 ]
241 ]
239 py_node = FileNode('test.py', ''.join(lines))
242 py_node = FileNode(b'test.py', b''.join(lines))
240
243
241 assert (len(lines), len(lines)) == py_node.lines()
244 assert (len(lines), len(lines)) == py_node.lines()
242 assert (len(lines), len(lines) - 2) == py_node.lines(count_empty=True)
245 assert (len(lines), len(lines) - 2) == py_node.lines(count_empty=True)
243
246
244 def test_lines_no_newline(self):
247 def test_lines_no_newline(self):
245 py_node = FileNode('test.py', 'oneline')
248 py_node = FileNode(b'test.py', b'oneline')
246
249
247 assert (1, 1) == py_node.lines()
250 assert (1, 1) == py_node.lines()
248 assert (1, 1) == py_node.lines(count_empty=True)
251 assert (1, 1) == py_node.lines(count_empty=True)
@@ -251,15 +254,15 b' class TestNodeBasics:'
251 class TestNodeContent(object):
254 class TestNodeContent(object):
252
255
253 def test_if_binary(self, binary_filenode):
256 def test_if_binary(self, binary_filenode):
254 filenode = binary_filenode('calendar.jpg')
257 filenode = binary_filenode(b'calendar.jpg')
255 assert filenode.is_binary
258 assert filenode.is_binary
256
259
257 def test_binary_line_counts(self, binary_filenode):
260 def test_binary_line_counts(self, binary_filenode):
258 tar_node = binary_filenode('archive.tar.gz')
261 tar_node = binary_filenode(b'archive.tar.gz')
259 assert (0, 0) == tar_node.lines(count_empty=True)
262 assert (0, 0) == tar_node.lines(count_empty=True)
260
263
261 def test_binary_mimetype(self, binary_filenode):
264 def test_binary_mimetype(self, binary_filenode):
262 tar_node = binary_filenode('archive.tar.gz')
265 tar_node = binary_filenode(b'archive.tar.gz')
263 assert tar_node.mimetype == 'application/x-tar'
266 assert tar_node.mimetype == 'application/x-tar'
264
267
265
268
@@ -271,5 +274,5 b' class TestNodesCommits(BackendTestMixin)'
271 last_commit = repo.get_commit()
274 last_commit = repo.get_commit()
272
275
273 for x in range(3):
276 for x in range(3):
274 node = last_commit.get_node('file_%s.txt' % x)
277 node = last_commit.get_node(f'file_{x}.txt')
275 assert node.last_commit == repo[x]
278 assert node.last_commit == repo[x]
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -56,7 +55,7 b' class TestRepositoryBase(BackendTestMixi'
56 self.Backend(path)
55 self.Backend(path)
57
56
58 def test_has_commits_attribute(self):
57 def test_has_commits_attribute(self):
59 self.repo.commit_ids
58 assert self.repo.commit_ids
60
59
61 def test_name(self):
60 def test_name(self):
62 assert self.repo.name.startswith('vcs-test')
61 assert self.repo.name.startswith('vcs-test')
@@ -81,11 +80,20 b' class TestRepositoryBase(BackendTestMixi'
81 def test_bookmarks(self):
80 def test_bookmarks(self):
82 assert len(self.repo.bookmarks) == 0
81 assert len(self.repo.bookmarks) == 0
83
82
84 # TODO: Cover two cases: Local repo path, remote URL
83 def test_check_url_on_path(self):
85 def test_check_url(self):
86 config = Config()
84 config = Config()
87 assert self.Backend.check_url(self.repo.path, config)
85 assert self.Backend.check_url(self.repo.path, config)
88
86
87 def test_check_url_on_remote_url(self):
88 config = Config()
89 url = {
90 'hg': 'https://code.rhodecode.com/rhodecode-vcsserver',
91 'svn': 'https://code.rhodecode.com/svn-doc',
92 'git': 'https://code.rhodecode.com/appenlight',
93 }[self.repo.alias]
94
95 assert self.Backend.check_url(url, config)
96
89 def test_check_url_invalid(self):
97 def test_check_url_invalid(self):
90 config = Config()
98 config = Config()
91 with pytest.raises(URLError):
99 with pytest.raises(URLError):
@@ -189,7 +197,7 b' class TestRepositoryCompare:'
189 source_repo = vcsbackend.clone_repo(target_repo)
197 source_repo = vcsbackend.clone_repo(target_repo)
190 assert target_repo != source_repo
198 assert target_repo != source_repo
191
199
192 vcsbackend.add_file(source_repo, 'newfile', 'somecontent')
200 vcsbackend.add_file(source_repo, b'newfile', b'somecontent')
193 source_commit = source_repo.get_commit()
201 source_commit = source_repo.get_commit()
194
202
195 target_repo.compare(
203 target_repo.compare(
@@ -232,7 +240,7 b' class TestRepositoryGetCommonAncestor:'
232 source_repo = vcsbackend.clone_repo(target_repo)
240 source_repo = vcsbackend.clone_repo(target_repo)
233 assert target_repo != source_repo
241 assert target_repo != source_repo
234
242
235 vcsbackend.add_file(source_repo, 'newfile', 'somecontent')
243 vcsbackend.add_file(source_repo, b'newfile', b'somecontent')
236 source_commit = source_repo.get_commit()
244 source_commit = source_repo.get_commit()
237
245
238 expected_ancestor = target_repo[4].raw_id
246 expected_ancestor = target_repo[4].raw_id
@@ -273,10 +281,10 b' class TestRepositoryMerge(object):'
273 def prepare_for_success(self, vcsbackend):
281 def prepare_for_success(self, vcsbackend):
274 self.target_repo = vcsbackend.create_repo(number_of_commits=1)
282 self.target_repo = vcsbackend.create_repo(number_of_commits=1)
275 self.source_repo = vcsbackend.clone_repo(self.target_repo)
283 self.source_repo = vcsbackend.clone_repo(self.target_repo)
276 vcsbackend.add_file(self.target_repo, 'README_MERGE1', 'Version 1')
284 vcsbackend.add_file(self.target_repo, b'README_MERGE1', b'Version 1')
277 vcsbackend.add_file(self.source_repo, 'README_MERGE2', 'Version 2')
285 vcsbackend.add_file(self.source_repo, b'README_MERGE2', b'Version 2')
278 imc = self.source_repo.in_memory_commit
286 imc = self.source_repo.in_memory_commit
279 imc.add(FileNode('file_x', content=self.source_repo.name))
287 imc.add(FileNode(b'file_x', content=self.source_repo.name))
280 imc.commit(
288 imc.commit(
281 message=u'Automatic commit from repo merge test',
289 message=u'Automatic commit from repo merge test',
282 author=u'Automatic <automatic@rhodecode.com>')
290 author=u'Automatic <automatic@rhodecode.com>')
@@ -292,8 +300,8 b' class TestRepositoryMerge(object):'
292 def prepare_for_conflict(self, vcsbackend):
300 def prepare_for_conflict(self, vcsbackend):
293 self.target_repo = vcsbackend.create_repo(number_of_commits=1)
301 self.target_repo = vcsbackend.create_repo(number_of_commits=1)
294 self.source_repo = vcsbackend.clone_repo(self.target_repo)
302 self.source_repo = vcsbackend.clone_repo(self.target_repo)
295 vcsbackend.add_file(self.target_repo, 'README_MERGE', 'Version 1')
303 vcsbackend.add_file(self.target_repo, b'README_MERGE', b'Version 1')
296 vcsbackend.add_file(self.source_repo, 'README_MERGE', 'Version 2')
304 vcsbackend.add_file(self.source_repo, b'README_MERGE', b'Version 2')
297 self.target_commit = self.target_repo.get_commit()
305 self.target_commit = self.target_repo.get_commit()
298 self.source_commit = self.source_repo.get_commit()
306 self.source_commit = self.source_repo.get_commit()
299 # This only works for Git and Mercurial
307 # This only works for Git and Mercurial
@@ -363,10 +371,10 b' class TestRepositoryMerge(object):'
363
371
364 # Multiple merges may differ in their commit id. Therefore we set the
372 # Multiple merges may differ in their commit id. Therefore we set the
365 # commit id to `None` before comparing the merge responses.
373 # commit id to `None` before comparing the merge responses.
366 new_merge_ref = merge_response.merge_ref._replace(commit_id=None)
374 new_merge_ref = merge_response.merge_ref.commit_id = None
367 merge_response.merge_ref = new_merge_ref
375 merge_response.merge_ref = new_merge_ref
368
376
369 new_update_merge_ref = merge_response_update.merge_ref._replace(commit_id=None)
377 new_update_merge_ref = merge_response_update.merge_ref.commit_id = None
370 merge_response_update.merge_ref = new_update_merge_ref
378 merge_response_update.merge_ref = new_update_merge_ref
371
379
372 assert merge_response == merge_response_update
380 assert merge_response == merge_response_update
@@ -480,8 +488,8 b' class TestRepositoryStrip(BackendTestMix'
480 'date': datetime.datetime(2010, 1, 1, 20),
488 'date': datetime.datetime(2010, 1, 1, 20),
481 'branch': 'master',
489 'branch': 'master',
482 'added': [
490 'added': [
483 FileNode('foobar', content='foobar'),
491 FileNode(b'foobar', content='foobar'),
484 FileNode('foobar2', content='foobar2'),
492 FileNode(b'foobar2', content='foobar2'),
485 ],
493 ],
486 },
494 },
487 ]
495 ]
@@ -492,7 +500,7 b' class TestRepositoryStrip(BackendTestMix'
492 'date': datetime.datetime(2010, 1, 1, 21, x),
500 'date': datetime.datetime(2010, 1, 1, 21, x),
493 'branch': 'master',
501 'branch': 'master',
494 'changed': [
502 'changed': [
495 FileNode('foobar', 'FOOBAR - %s' % x),
503 FileNode(b'foobar', 'FOOBAR - %s' % x),
496 ],
504 ],
497 }
505 }
498 commits.append(commit_data)
506 commits.append(commit_data)
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -23,6 +22,7 b' import os'
23 import mock
22 import mock
24 import pytest
23 import pytest
25
24
25 from rhodecode.lib.str_utils import safe_bytes
26 from rhodecode.tests import SVN_REPO, TEST_DIR, TESTS_TMP_PATH
26 from rhodecode.tests import SVN_REPO, TEST_DIR, TESTS_TMP_PATH
27 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
27 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
28 from rhodecode.lib.vcs.conf import settings
28 from rhodecode.lib.vcs.conf import settings
@@ -83,8 +83,8 b' def test_commit_author(head):'
83
83
84
84
85 @pytest.mark.parametrize("filename, content, mime_type", [
85 @pytest.mark.parametrize("filename, content, mime_type", [
86 ('test.txt', 'Text content\n', None),
86 (b'test.txt', b'Text content\n', None),
87 ('test.bin', '\0 binary \0', 'application/octet-stream'),
87 (b'test.bin', b'\0 binary \0', 'application/octet-stream'),
88 ], ids=['text', 'binary'])
88 ], ids=['text', 'binary'])
89 def test_sets_mime_type_correctly(vcsbackend, filename, content, mime_type):
89 def test_sets_mime_type_correctly(vcsbackend, filename, content, mime_type):
90 repo = vcsbackend.create_repo()
90 repo = vcsbackend.create_repo()
@@ -132,12 +132,14 b' def test_topnode_files_attribute(head):'
132 topnode.files
132 topnode.files
133
133
134
134
135
136
135 @pytest.mark.parametrize("filename, content, branch, mime_type", [
137 @pytest.mark.parametrize("filename, content, branch, mime_type", [
136 (u'branches/plain/test.txt', 'Text content\n', 'plain', None),
138 ('branches/plain/test.txt', b'Text content\n', 'plain', None),
137 (u'branches/uniΓ§ΓΆβˆ‚e/test.bin', '\0 binary \0', u'uniΓ§ΓΆβˆ‚e',
139 ('branches/uniΓ§ΓΆβˆ‚e/test.bin', b'\0 binary \0', 'uniΓ§ΓΆβˆ‚e', 'application/octet-stream'),
138 'application/octet-stream'),
139 ], ids=['text', 'binary'])
140 ], ids=['text', 'binary'])
140 def test_unicode_refs(vcsbackend, filename, content, branch, mime_type):
141 def test_unicode_refs(vcsbackend, filename, content, branch, mime_type):
142 filename = safe_bytes(filename)
141 repo = vcsbackend.create_repo()
143 repo = vcsbackend.create_repo()
142 vcsbackend.ensure_file(filename, content)
144 vcsbackend.ensure_file(filename, content)
143 with mock.patch(("rhodecode.lib.vcs.backends.svn.repository"
145 with mock.patch(("rhodecode.lib.vcs.backends.svn.repository"
@@ -184,3 +186,10 b' class TestSVNCommit(object):'
184 node_ids = [commit.raw_id for commit in node.history]
186 node_ids = [commit.raw_id for commit in node.history]
185 assert ['18',
187 assert ['18',
186 '8'] == node_ids
188 '8'] == node_ids
189
190 def test_repo_files_content_type(self):
191 test_commit = self.repo.get_commit(commit_idx=100)
192 for node in test_commit.get_node('/'):
193 if node.is_file():
194 assert type(node.content) == bytes
195 assert type(node.str_content) == str
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -24,6 +23,7 b' import subprocess'
24
23
25 import pytest
24 import pytest
26
25
26 from rhodecode.lib.vcs.backends.git import GitRepository
27 from rhodecode.lib.vcs.exceptions import VCSError
27 from rhodecode.lib.vcs.exceptions import VCSError
28 from rhodecode.lib.vcs.utils import author_email, author_name
28 from rhodecode.lib.vcs.utils import author_email, author_name
29 from rhodecode.lib.vcs.utils.helpers import get_scm
29 from rhodecode.lib.vcs.utils.helpers import get_scm
@@ -85,9 +85,10 b' class TestGetScm(object):'
85
85
86 def test_get_two_scms_for_path(self, tmpdir):
86 def test_get_two_scms_for_path(self, tmpdir):
87 multialias_repo_path = str(tmpdir)
87 multialias_repo_path = str(tmpdir)
88 git_default_branch = GitRepository.DEFAULT_BRANCH_NAME
88
89
89 subprocess.check_call(['hg', 'init', multialias_repo_path])
90 subprocess.check_call(['hg', 'init', multialias_repo_path])
90 subprocess.check_call(['git', 'init', multialias_repo_path])
91 subprocess.check_call(['git', '-c', f'init.defaultBranch={git_default_branch}', 'init', multialias_repo_path])
91
92
92 with pytest.raises(VCSError):
93 with pytest.raises(VCSError):
93 get_scm(multialias_repo_path)
94 get_scm(multialias_repo_path)
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -67,7 +66,7 b' class SCMFetcher(object):'
67 self.alias = alias
66 self.alias = alias
68 self.test_repo_path = test_repo_path
67 self.test_repo_path = test_repo_path
69
68
70 def setup(self):
69 def setup_method(self):
71 if not os.path.isdir(self.test_repo_path):
70 if not os.path.isdir(self.test_repo_path):
72 self.fetch_repo()
71 self.fetch_repo()
73
72
@@ -100,7 +99,7 b' def get_normalized_path(path):'
100 m = matcher.match(name)
99 m = matcher.match(name)
101 if not m:
100 if not m:
102 # Haven't append number yet so return first
101 # Haven't append number yet so return first
103 newname = '%s-00000' % name
102 newname = f'{name}-00000'
104 newpath = os.path.join(dir, newname)
103 newpath = os.path.join(dir, newname)
105 if ext:
104 if ext:
106 newpath = '.'.join((newpath, ext))
105 newpath = '.'.join((newpath, ext))
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -95,33 +94,31 b' def _add_files(vcs, dest, clone_url=None'
95 author_str = 'Marcin KuΕΊminski <me@email.com>'
94 author_str = 'Marcin KuΕΊminski <me@email.com>'
96
95
97 for i in range(kwargs.get('files_no', 3)):
96 for i in range(kwargs.get('files_no', 3)):
98 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
97 cmd = f"""echo 'added_line{i}' >> {added_file}"""
99 Command(cwd).execute(cmd)
98 Command(cwd).execute(cmd)
100
99
101 if vcs == 'hg':
100 if vcs == 'hg':
102 cmd = """hg commit -m 'committed new %s' -u '%s' %s """ % (
101 cmd = f"""hg commit -m 'committed new {i}' -u '{author_str}' {added_file} """
103 i, author_str, added_file
104 )
105 elif vcs == 'git':
102 elif vcs == 'git':
106 cmd = """%s && git commit -m 'committed new %s' %s""" % (
103 cmd = f"""{git_ident} && git commit -m 'committed new {i}' {added_file}"""
107 git_ident, i, added_file)
108 Command(cwd).execute(cmd)
104 Command(cwd).execute(cmd)
109
105
110 for tag in tags:
106 for tag in tags:
111 if vcs == 'hg':
107 if vcs == 'hg':
112 Command(cwd).execute(
108 Command(cwd).execute(
113 'hg tag -m "{}" -u "{}" '.format(tag['commit'], author_str), tag['name'])
109 f"""hg tag -m "{tag['commit']}" -u "{author_str}" """,
110 tag['name'])
114 elif vcs == 'git':
111 elif vcs == 'git':
115 if tag['commit']:
112 if tag['commit']:
116 # annotated tag
113 # annotated tag
117 _stdout, _stderr = Command(cwd).execute(
114 _stdout, _stderr = Command(cwd).execute(
118 """%s && git tag -a %s -m "%s" """ % (
115 f"""{git_ident} && git tag -a {tag['name']} -m "{tag['commit']}" """
119 git_ident, tag['name'], tag['commit']))
116 )
120 else:
117 else:
121 # lightweight tag
118 # lightweight tag
122 _stdout, _stderr = Command(cwd).execute(
119 _stdout, _stderr = Command(cwd).execute(
123 """%s && git tag %s""" % (
120 f"""{git_ident} && git tag {tag['name']}"""
124 git_ident, tag['name']))
121 )
125
122
126
123
127 def _add_files_and_push(vcs, dest, clone_url=None, tags=None, target_branch=None,
124 def _add_files_and_push(vcs, dest, clone_url=None, tags=None, target_branch=None,
@@ -130,9 +127,8 b' def _add_files_and_push(vcs, dest, clone'
130 Generate some files, add it to DEST repo and push back
127 Generate some files, add it to DEST repo and push back
131 vcs is git or hg and defines what VCS we want to make those files for
128 vcs is git or hg and defines what VCS we want to make those files for
132 """
129 """
133 git_ident = "git config user.name {} && git config user.email {}".format(
130 git_ident = "git config user.name Marcin KuΕΊminski && git config user.email me@email.com"
134 'Marcin KuΕΊminski', 'me@email.com')
131 cwd = jn(dest)
135 cwd = path = jn(dest)
136
132
137 # commit some stuff into this repo
133 # commit some stuff into this repo
138 _add_files(vcs, dest, clone_url, tags, target_branch, new_branch, **kwargs)
134 _add_files(vcs, dest, clone_url, tags, target_branch, new_branch, **kwargs)
@@ -151,7 +147,7 b' def _add_files_and_push(vcs, dest, clone'
151 if new_branch:
147 if new_branch:
152 maybe_new_branch = '--new-branch'
148 maybe_new_branch = '--new-branch'
153 stdout, stderr = Command(cwd).execute(
149 stdout, stderr = Command(cwd).execute(
154 'hg push --verbose {} -r {} {}'.format(maybe_new_branch, target_branch, clone_url)
150 'hg push --traceback --verbose {} -r {} {}'.format(maybe_new_branch, target_branch, clone_url)
155 )
151 )
156 elif vcs == 'git':
152 elif vcs == 'git':
157 stdout, stderr = Command(cwd).execute(
153 stdout, stderr = Command(cwd).execute(
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -32,6 +31,7 b' import tempfile'
32 import textwrap
31 import textwrap
33 import pytest
32 import pytest
34 import logging
33 import logging
34 import requests
35
35
36 from rhodecode import events
36 from rhodecode import events
37 from rhodecode.lib.str_utils import safe_bytes
37 from rhodecode.lib.str_utils import safe_bytes
@@ -40,19 +40,33 b' from rhodecode.model.db import Integrati'
40 from rhodecode.model.integration import IntegrationModel
40 from rhodecode.model.integration import IntegrationModel
41 from rhodecode.model.db import Repository
41 from rhodecode.model.db import Repository
42 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
43 from rhodecode.model.settings import SettingsModel
44 from rhodecode.integrations.types.webhook import WebhookIntegrationType
43 from rhodecode.integrations.types.webhook import WebhookIntegrationType
45
44
46 from rhodecode.tests import GIT_REPO, HG_REPO
45 from rhodecode.tests import GIT_REPO, HG_REPO
47 from rhodecode.tests.fixture import Fixture
46 from rhodecode.tests.fixture import Fixture
48 from rhodecode.tests.server_utils import RcWebServer
47 from rhodecode.tests.server_utils import RcWebServer
49
48
49
50 REPO_GROUP = 'a_repo_group'
50 REPO_GROUP = 'a_repo_group'
51 HG_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, HG_REPO)
51 HG_REPO_WITH_GROUP = f'{REPO_GROUP}/{HG_REPO}'
52 GIT_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, GIT_REPO)
52 GIT_REPO_WITH_GROUP = f'{REPO_GROUP}/{GIT_REPO}'
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56 # Docker image running httpbin...
57 HTTPBIN_DOMAIN = 'http://httpbin'
58 HTTPBIN_POST = HTTPBIN_DOMAIN + '/post'
59
60
61 def check_httpbin_connection():
62 try:
63 response = requests.get(HTTPBIN_DOMAIN)
64 return response.status_code == 200
65 except Exception as e:
66 print(e)
67
68 return False
69
56
70
57 @pytest.fixture(scope="module")
71 @pytest.fixture(scope="module")
58 def rcextensions(request, db_connection, tmpdir_factory):
72 def rcextensions(request, db_connection, tmpdir_factory):
@@ -74,8 +88,7 b' def rcextensions(request, db_connection,'
74 pytest.fail(
88 pytest.fail(
75 "Path for rcextensions already exists, please clean up before "
89 "Path for rcextensions already exists, please clean up before "
76 "test run this path: %s" % (rcextensions_path, ))
90 "test run this path: %s" % (rcextensions_path, ))
77 return
91 else:
78
79 request.addfinalizer(rcextensions_path.remove)
92 request.addfinalizer(rcextensions_path.remove)
80 init_path.write_binary(safe_bytes(init_content), ensure=True)
93 init_path.write_binary(safe_bytes(init_content), ensure=True)
81
94
@@ -127,7 +140,7 b' def rc_web_server('
127 request, vcsserver_factory, available_port_factory,
140 request, vcsserver_factory, available_port_factory,
128 rc_web_server_config_factory, repos, rcextensions):
141 rc_web_server_config_factory, repos, rcextensions):
129 """
142 """
130 Run the web server as a subprocess. with it's own instance of vcsserver
143 Run the web server as a subprocess. with its own instance of vcsserver
131 """
144 """
132 rcweb_port = available_port_factory()
145 rcweb_port = available_port_factory()
133 log.info('Using rcweb ops test port {}'.format(rcweb_port))
146 log.info('Using rcweb ops test port {}'.format(rcweb_port))
@@ -175,55 +188,6 b' def disable_locking(baseapp):'
175
188
176
189
177 @pytest.fixture()
190 @pytest.fixture()
178 def enable_auth_plugins(request, baseapp, csrf_token):
179 """
180 Return a factory object that when called, allows to control which
181 authentication plugins are enabled.
182 """
183 def _enable_plugins(plugins_list, override=None):
184 override = override or {}
185 params = {
186 'auth_plugins': ','.join(plugins_list),
187 }
188
189 # helper translate some names to others
190 name_map = {
191 'token': 'authtoken'
192 }
193
194 for module in plugins_list:
195 plugin_name = module.partition('#')[-1]
196 if plugin_name in name_map:
197 plugin_name = name_map[plugin_name]
198 enabled_plugin = 'auth_%s_enabled' % plugin_name
199 cache_ttl = 'auth_%s_cache_ttl' % plugin_name
200
201 # default params that are needed for each plugin,
202 # `enabled` and `cache_ttl`
203 params.update({
204 enabled_plugin: True,
205 cache_ttl: 0
206 })
207 if override.get:
208 params.update(override.get(module, {}))
209
210 validated_params = params
211 for k, v in validated_params.items():
212 setting = SettingsModel().create_or_update_setting(k, v)
213 Session().add(setting)
214 Session().commit()
215
216 SettingsModel().invalidate_settings_cache()
217
218 def cleanup():
219 _enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
220
221 request.addfinalizer(cleanup)
222
223 return _enable_plugins
224
225
226 @pytest.fixture()
227 def fs_repo_only(request, rhodecode_fixtures):
191 def fs_repo_only(request, rhodecode_fixtures):
228 def fs_repo_fabric(repo_name, repo_type):
192 def fs_repo_fabric(repo_name, repo_type):
229 rhodecode_fixtures.create_repo(repo_name, repo_type=repo_type)
193 rhodecode_fixtures.create_repo(repo_name, repo_type=repo_type)
@@ -245,7 +209,7 b' def enable_webhook_push_integration(requ'
245 Session().add(integration)
209 Session().add(integration)
246
210
247 settings = dict(
211 settings = dict(
248 url='http://httpbin.org/post',
212 url=HTTPBIN_POST,
249 secret_token='secret',
213 secret_token='secret',
250 username=None,
214 username=None,
251 password=None,
215 password=None,
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -37,7 +36,7 b' from rhodecode.tests.vcs_operations impo'
37 def rc_web_server_config_modification():
36 def rc_web_server_config_modification():
38 return [
37 return [
39 {'app:main': {'auth_ret_code': '403'}},
38 {'app:main': {'auth_ret_code': '403'}},
40 {'app:main': {'auth_ret_code_detection': 'true'}},
39 #{'app:main': {'auth_ret_code_detection': 'true'}},
41 ]
40 ]
42
41
43
42
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -37,7 +36,7 b' from rhodecode.tests.vcs_operations impo'
37 def rc_web_server_config_modification():
36 def rc_web_server_config_modification():
38 return [
37 return [
39 {'app:main': {'auth_ret_code': '404'}},
38 {'app:main': {'auth_ret_code': '404'}},
40 {'app:main': {'auth_ret_code_detection': 'false'}},
39 #{'app:main': {'auth_ret_code_detection': 'false'}},
41 ]
40 ]
42
41
43
42
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -37,7 +36,7 b' from rhodecode.tests.vcs_operations impo'
37 def rc_web_server_config_modification():
36 def rc_web_server_config_modification():
38 return [
37 return [
39 {'app:main': {'auth_ret_code': '600'}},
38 {'app:main': {'auth_ret_code': '600'}},
40 {'app:main': {'auth_ret_code_detection': 'false'}},
39 #{'app:main': {'auth_ret_code_detection': 'false'}},
41 ]
40 ]
42
41
43
42
@@ -48,10 +47,10 b' class TestVCSOperationsOnCustomIniConfig'
48 clone_url = rc_web_server.repo_clone_url(HG_REPO, passwd='bad!')
47 clone_url = rc_web_server.repo_clone_url(HG_REPO, passwd='bad!')
49 stdout, stderr = Command('/tmp').execute(
48 stdout, stderr = Command('/tmp').execute(
50 'hg clone', clone_url, tmpdir.strpath)
49 'hg clone', clone_url, tmpdir.strpath)
51 assert 'abort: HTTP Error 403: Forbidden' in stderr
50 assert 'abort: authorization failed' in stderr
52
51
53 def test_clone_wrong_credentials_git_ret_code(self, rc_web_server, tmpdir):
52 def test_clone_wrong_credentials_git_ret_code(self, rc_web_server, tmpdir):
54 clone_url = rc_web_server.repo_clone_url(GIT_REPO, passwd='bad!')
53 clone_url = rc_web_server.repo_clone_url(GIT_REPO, passwd='bad!')
55 stdout, stderr = Command('/tmp').execute(
54 stdout, stderr = Command('/tmp').execute(
56 'git clone', clone_url, tmpdir.strpath)
55 'git clone', clone_url, tmpdir.strpath)
57 assert 'The requested URL returned error: 403' in stderr
56 assert 'fatal: Authentication failed' in stderr
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -28,7 +27,6 b' Test suite for making push/pull operatio'
28 """
27 """
29
28
30 import os
29 import os
31 import pytest
32
30
33 from rhodecode.lib.vcs.backends.git.repository import GitRepository
31 from rhodecode.lib.vcs.backends.git.repository import GitRepository
34 from rhodecode.lib.vcs.nodes import FileNode
32 from rhodecode.lib.vcs.nodes import FileNode
@@ -55,7 +53,7 b' def test_git_push_with_small_push_buffer'
55 cmd.execute('git clone', clone_url)
53 cmd.execute('git clone', clone_url)
56
54
57 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
55 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
58 repo.in_memory_commit.add(FileNode('readme.md', content='## Hello'))
56 repo.in_memory_commit.add(FileNode(b'readme.md', content=b'## Hello'))
59 repo.in_memory_commit.commit(
57 repo.in_memory_commit.commit(
60 message='Commit on branch Master',
58 message='Commit on branch Master',
61 author='Automatic test <automatic@rhodecode.com>',
59 author='Automatic test <automatic@rhodecode.com>',
@@ -63,5 +61,5 b' def test_git_push_with_small_push_buffer'
63
61
64 repo_cmd = Command(repo.path)
62 repo_cmd = Command(repo.path)
65 stdout, stderr = repo_cmd.execute(
63 stdout, stderr = repo_cmd.execute(
66 'git -c http.postBuffer=1024 push --verbose origin master')
64 f'git -c http.postBuffer=1024 push --verbose {clone_url} master')
67 _check_proper_git_push(stdout, stderr, branch='master')
65 _check_proper_git_push(stdout, stderr, branch='master')
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -29,6 +28,7 b' Test suite for making push/pull operatio'
29
28
30
29
31 import time
30 import time
31 import logging
32
32
33 import pytest
33 import pytest
34
34
@@ -39,6 +39,7 b' from rhodecode.model.meta import Session'
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.user import UserModel
40 from rhodecode.model.user import UserModel
41 from rhodecode.tests import (GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN)
41 from rhodecode.tests import (GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN)
42 from rhodecode.tests.utils import assert_message_in_log
42
43
43 from rhodecode.tests.vcs_operations import (
44 from rhodecode.tests.vcs_operations import (
44 Command, _check_proper_clone, _check_proper_git_push,
45 Command, _check_proper_clone, _check_proper_git_push,
@@ -372,112 +373,3 b' class TestVCSOperations(object):'
372 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
373 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
373 cmd.assert_returncode_success()
374 cmd.assert_returncode_success()
374 _check_proper_clone(stdout, stderr, 'git')
375 _check_proper_clone(stdout, stderr, 'git')
375
376 def test_clone_by_auth_token(
377 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
378 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
379 'egg:rhodecode-enterprise-ce#rhodecode'])
380
381 user = user_util.create_user()
382 token = user.auth_tokens[1]
383
384 clone_url = rc_web_server.repo_clone_url(
385 HG_REPO, user=user.username, passwd=token)
386
387 stdout, stderr = Command('/tmp').execute(
388 'hg clone', clone_url, tmpdir.strpath)
389 _check_proper_clone(stdout, stderr, 'hg')
390
391 def test_clone_by_auth_token_expired(
392 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
393 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
394 'egg:rhodecode-enterprise-ce#rhodecode'])
395
396 user = user_util.create_user()
397 auth_token = AuthTokenModel().create(
398 user.user_id, u'test-token', -10, AuthTokenModel.cls.ROLE_VCS)
399 token = auth_token.api_key
400
401 clone_url = rc_web_server.repo_clone_url(
402 HG_REPO, user=user.username, passwd=token)
403
404 stdout, stderr = Command('/tmp').execute(
405 'hg clone', clone_url, tmpdir.strpath)
406 assert 'abort: authorization failed' in stderr
407
408 def test_clone_by_auth_token_bad_role(
409 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
410 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
411 'egg:rhodecode-enterprise-ce#rhodecode'])
412
413 user = user_util.create_user()
414 auth_token = AuthTokenModel().create(
415 user.user_id, u'test-token', -1, AuthTokenModel.cls.ROLE_API)
416 token = auth_token.api_key
417
418 clone_url = rc_web_server.repo_clone_url(
419 HG_REPO, user=user.username, passwd=token)
420
421 stdout, stderr = Command('/tmp').execute(
422 'hg clone', clone_url, tmpdir.strpath)
423 assert 'abort: authorization failed' in stderr
424
425 def test_clone_by_auth_token_user_disabled(
426 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
427 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
428 'egg:rhodecode-enterprise-ce#rhodecode'])
429 user = user_util.create_user()
430 user.active = False
431 Session().add(user)
432 Session().commit()
433 token = user.auth_tokens[1]
434
435 clone_url = rc_web_server.repo_clone_url(
436 HG_REPO, user=user.username, passwd=token)
437
438 stdout, stderr = Command('/tmp').execute(
439 'hg clone', clone_url, tmpdir.strpath)
440 assert 'abort: authorization failed' in stderr
441
442 def test_clone_by_auth_token_with_scope(
443 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
444 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
445 'egg:rhodecode-enterprise-ce#rhodecode'])
446 user = user_util.create_user()
447 auth_token = AuthTokenModel().create(
448 user.user_id, u'test-token', -1, AuthTokenModel.cls.ROLE_VCS)
449 token = auth_token.api_key
450
451 # manually set scope
452 auth_token.repo = Repository.get_by_repo_name(HG_REPO)
453 Session().add(auth_token)
454 Session().commit()
455
456 clone_url = rc_web_server.repo_clone_url(
457 HG_REPO, user=user.username, passwd=token)
458
459 stdout, stderr = Command('/tmp').execute(
460 'hg clone', clone_url, tmpdir.strpath)
461 _check_proper_clone(stdout, stderr, 'hg')
462
463 def test_clone_by_auth_token_with_wrong_scope(
464 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
465 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
466 'egg:rhodecode-enterprise-ce#rhodecode'])
467 user = user_util.create_user()
468 auth_token = AuthTokenModel().create(
469 user.user_id, u'test-token', -1, AuthTokenModel.cls.ROLE_VCS)
470 token = auth_token.api_key
471
472 # manually set scope
473 auth_token.repo = Repository.get_by_repo_name(GIT_REPO)
474 Session().add(auth_token)
475 Session().commit()
476
477 clone_url = rc_web_server.repo_clone_url(
478 HG_REPO, user=user.username, passwd=token)
479
480 stdout, stderr = Command('/tmp').execute(
481 'hg clone', clone_url, tmpdir.strpath)
482 assert 'abort: authorization failed' in stderr
483
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -58,7 +57,7 b' class TestVCSOperations(object):'
58 if branch_perm in ['branch.push', 'branch.push_force']:
57 if branch_perm in ['branch.push', 'branch.push_force']:
59 _check_proper_hg_push(stdout, stderr)
58 _check_proper_hg_push(stdout, stderr)
60 else:
59 else:
61 msg = "Branch `default` changes rejected by rule `*`=>{}".format(branch_perm)
60 msg = f"Branch `default` changes rejected by rule `*`=>{branch_perm}"
62 assert msg in stdout
61 assert msg in stdout
63 assert "transaction abort" in stdout
62 assert "transaction abort" in stdout
64
63
@@ -89,6 +88,6 b' class TestVCSOperations(object):'
89 if branch_perm in ['branch.push', 'branch.push_force']:
88 if branch_perm in ['branch.push', 'branch.push_force']:
90 _check_proper_git_push(stdout, stderr)
89 _check_proper_git_push(stdout, stderr)
91 else:
90 else:
92 msg = "Branch `master` changes rejected by rule `*`=>{}".format(branch_perm)
91 msg = f"Branch `master` changes rejected by rule `*`=>{branch_perm}"
93 assert msg in stderr
92 assert msg in stderr
94 assert "(pre-receive hook declined)" in stderr
93 assert "(pre-receive hook declined)" in stderr
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -47,7 +46,7 b' class TestVCSOperations(object):'
47 _add_files('hg', tmpdir.strpath, clone_url=clone_url)
46 _add_files('hg', tmpdir.strpath, clone_url=clone_url)
48
47
49 stdout, stderr = Command(tmpdir.strpath).execute(
48 stdout, stderr = Command(tmpdir.strpath).execute(
50 'hg push --verbose -f {}'.format(clone_url))
49 f'hg push --verbose -f {clone_url}')
51
50
52 _check_proper_hg_push(stdout, stderr)
51 _check_proper_hg_push(stdout, stderr)
53
52
@@ -65,7 +64,7 b' class TestVCSOperations(object):'
65 Command(tmpdir.strpath).execute(
64 Command(tmpdir.strpath).execute(
66 'git reset --hard HEAD~2')
65 'git reset --hard HEAD~2')
67 stdout, stderr = Command(tmpdir.strpath).execute(
66 stdout, stderr = Command(tmpdir.strpath).execute(
68 'git push -f {} master'.format(clone_url))
67 f'git push -f {clone_url} master')
69
68
70 assert '(forced update)' in stderr
69 assert '(forced update)' in stderr
71
70
@@ -90,7 +89,7 b' class TestVCSOperations(object):'
90 _add_files('hg', tmpdir.strpath, clone_url=clone_url)
89 _add_files('hg', tmpdir.strpath, clone_url=clone_url)
91
90
92 stdout, stderr = Command(tmpdir.strpath).execute(
91 stdout, stderr = Command(tmpdir.strpath).execute(
93 'hg push --verbose -f {}'.format(clone_url))
92 f'hg push --verbose -f {clone_url}')
94
93
95 assert "Branch `default` changes rejected by rule `*`=>branch.push" in stdout
94 assert "Branch `default` changes rejected by rule `*`=>branch.push" in stdout
96 assert "FORCE PUSH FORBIDDEN" in stdout
95 assert "FORCE PUSH FORBIDDEN" in stdout
@@ -120,3 +119,4 b' class TestVCSOperations(object):'
120 assert "Branch `master` changes rejected by rule `*`=>branch.push" in stderr
119 assert "Branch `master` changes rejected by rule `*`=>branch.push" in stderr
121 assert "FORCE PUSH FORBIDDEN" in stderr
120 assert "FORCE PUSH FORBIDDEN" in stderr
122 assert "(pre-receive hook declined)" in stderr
121 assert "(pre-receive hook declined)" in stderr
122
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -28,24 +27,15 b' Test suite for making push/pull operatio'
28 """
27 """
29
28
30 import pytest
29 import pytest
31 import requests
30
32
31
33 from rhodecode.tests import GIT_REPO, HG_REPO
32 from rhodecode.tests import GIT_REPO, HG_REPO
34 from rhodecode.tests.vcs_operations import Command, _add_files_and_push
33 from rhodecode.tests.vcs_operations import Command, _add_files_and_push
35
34 from rhodecode.tests.vcs_operations.conftest import check_httpbin_connection
36
37 def check_connection():
38 try:
39 response = requests.get('http://httpbin.org')
40 return response.status_code == 200
41 except Exception as e:
42 print(e)
43
44 return False
45
35
46
36
47 connection_available = pytest.mark.skipif(
37 connection_available = pytest.mark.skipif(
48 not check_connection(), reason="No outside internet connection available")
38 not check_httpbin_connection(), reason="No outside internet connection available")
49
39
50
40
51 @pytest.mark.usefixtures("baseapp", "enable_webhook_push_integration")
41 @pytest.mark.usefixtures("baseapp", "enable_webhook_push_integration")
@@ -54,8 +44,7 b' class TestVCSOperationsOnCustomIniConfig'
54 def test_push_with_webhook_hg(self, rc_web_server, tmpdir):
44 def test_push_with_webhook_hg(self, rc_web_server, tmpdir):
55 clone_url = rc_web_server.repo_clone_url(HG_REPO)
45 clone_url = rc_web_server.repo_clone_url(HG_REPO)
56
46
57 stdout, stderr = Command('/tmp').execute(
47 Command('/tmp').execute('hg clone', clone_url, tmpdir.strpath)
58 'hg clone', clone_url, tmpdir.strpath)
59
48
60 push_url = rc_web_server.repo_clone_url(HG_REPO)
49 push_url = rc_web_server.repo_clone_url(HG_REPO)
61 _add_files_and_push('hg', tmpdir.strpath, clone_url=push_url)
50 _add_files_and_push('hg', tmpdir.strpath, clone_url=push_url)
@@ -65,11 +54,10 b' class TestVCSOperationsOnCustomIniConfig'
65 assert "executing task TASK:<@task: rhodecode.integrations.types.webhook.post_to_webhook" in rc_log
54 assert "executing task TASK:<@task: rhodecode.integrations.types.webhook.post_to_webhook" in rc_log
66 assert "handling event repo-push with integration <rhodecode.integrations.types.webhook.WebhookIntegrationType" in rc_log
55 assert "handling event repo-push with integration <rhodecode.integrations.types.webhook.WebhookIntegrationType" in rc_log
67
56
68 def test_push_with_webhook_gut(self, rc_web_server, tmpdir):
57 def test_push_with_webhook_git(self, rc_web_server, tmpdir):
69 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
58 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
70
59
71 stdout, stderr = Command('/tmp').execute(
60 Command('/tmp').execute('git clone', clone_url, tmpdir.strpath)
72 'git clone', clone_url, tmpdir.strpath)
73
61
74 push_url = rc_web_server.repo_clone_url(GIT_REPO)
62 push_url = rc_web_server.repo_clone_url(GIT_REPO)
75 _add_files_and_push('git', tmpdir.strpath, clone_url=push_url)
63 _add_files_and_push('git', tmpdir.strpath, clone_url=push_url)
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -27,7 +26,6 b' from rhodecode.lib.vcs.backends.hg.repos'
27 from rhodecode.lib.vcs.nodes import FileNode
26 from rhodecode.lib.vcs.nodes import FileNode
28 from rhodecode.model.db import Repository
27 from rhodecode.model.db import Repository
29 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
30 from rhodecode.tests import GIT_REPO, HG_REPO
31
29
32 from rhodecode.tests.vcs_operations import (
30 from rhodecode.tests.vcs_operations import (
33 Command, _check_proper_clone, _check_proper_git_push, _check_proper_hg_push,
31 Command, _check_proper_clone, _check_proper_git_push, _check_proper_hg_push,
@@ -47,7 +45,7 b' class TestVCSOperationsSpecial(object):'
47
45
48 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
46 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
49 repo._checkout('test', create=True)
47 repo._checkout('test', create=True)
50 repo.in_memory_commit.add(FileNode('file', content=''))
48 repo.in_memory_commit.add(FileNode(b'file', content=b'some-content'))
51 repo.in_memory_commit.commit(
49 repo.in_memory_commit.commit(
52 message='Commit on branch test',
50 message='Commit on branch test',
53 author='Automatic test <automatic@rhodecode.com>',
51 author='Automatic test <automatic@rhodecode.com>',
@@ -89,7 +87,7 b' class TestVCSOperationsSpecial(object):'
89 cmd.execute('git clone', clone_url)
87 cmd.execute('git clone', clone_url)
90
88
91 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
89 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
92 repo.in_memory_commit.add(FileNode('readme.md', content='## Hello'))
90 repo.in_memory_commit.add(FileNode(b'readme.md', content=b'## Hello'))
93 repo.in_memory_commit.commit(
91 repo.in_memory_commit.commit(
94 message='Commit on branch Master',
92 message='Commit on branch Master',
95 author='Automatic test <automatic@rhodecode.com>',
93 author='Automatic test <automatic@rhodecode.com>',
@@ -99,14 +97,13 b' class TestVCSOperationsSpecial(object):'
99 stdout, stderr = repo_cmd.execute('git push --verbose origin master')
97 stdout, stderr = repo_cmd.execute('git push --verbose origin master')
100 _check_proper_git_push(stdout, stderr, branch='master')
98 _check_proper_git_push(stdout, stderr, branch='master')
101
99
102 ref = '{}/{}/pull-request/new?branch=master'.format(
100 ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=master'
103 rc_web_server.host_url(), empty_repo.repo_name)
101 assert f'remote: RhodeCode: open pull request link: {ref}' in stderr
104 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stderr
105 assert 'remote: RhodeCode: push completed' in stderr
102 assert 'remote: RhodeCode: push completed' in stderr
106
103
107 # push on the same branch
104 # push on the same branch
108 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
105 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
109 repo.in_memory_commit.add(FileNode('setup.py', content='print\n'))
106 repo.in_memory_commit.add(FileNode(b'setup.py', content=b'print\n'))
110 repo.in_memory_commit.commit(
107 repo.in_memory_commit.commit(
111 message='Commit2 on branch Master',
108 message='Commit2 on branch Master',
112 author='Automatic test2 <automatic@rhodecode.com>',
109 author='Automatic test2 <automatic@rhodecode.com>',
@@ -116,12 +113,13 b' class TestVCSOperationsSpecial(object):'
116 stdout, stderr = repo_cmd.execute('git push --verbose origin master')
113 stdout, stderr = repo_cmd.execute('git push --verbose origin master')
117 _check_proper_git_push(stdout, stderr, branch='master')
114 _check_proper_git_push(stdout, stderr, branch='master')
118
115
119 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stderr
116 assert f'remote: RhodeCode: open pull request link: {ref}' in stderr
120 assert 'remote: RhodeCode: push completed' in stderr
117 assert 'remote: RhodeCode: push completed' in stderr
121
118
122 # new Branch
119 # new Branch
123 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
120 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
124 repo.in_memory_commit.add(FileNode('feature1.py', content='## Hello world'))
121 repo._create_branch('feature', repo.commit_ids[1])
122 repo.in_memory_commit.add(FileNode(b'feature1.py', content=b'## Hello world'))
125 repo.in_memory_commit.commit(
123 repo.in_memory_commit.commit(
126 message='Commit on branch feature',
124 message='Commit on branch feature',
127 author='Automatic test <automatic@rhodecode.com>',
125 author='Automatic test <automatic@rhodecode.com>',
@@ -131,9 +129,8 b' class TestVCSOperationsSpecial(object):'
131 stdout, stderr = repo_cmd.execute('git push --verbose origin feature')
129 stdout, stderr = repo_cmd.execute('git push --verbose origin feature')
132 _check_proper_git_push(stdout, stderr, branch='feature')
130 _check_proper_git_push(stdout, stderr, branch='feature')
133
131
134 ref = '{}/{}/pull-request/new?branch=feature'.format(
132 ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=feature'
135 rc_web_server.host_url(), empty_repo.repo_name)
133 assert f'remote: RhodeCode: open pull request link: {ref}' in stderr
136 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stderr
137 assert 'remote: RhodeCode: push completed' in stderr
134 assert 'remote: RhodeCode: push completed' in stderr
138
135
139 def test_hg_push_shows_pull_request_refs(self, backend_hg, rc_web_server, tmpdir):
136 def test_hg_push_shows_pull_request_refs(self, backend_hg, rc_web_server, tmpdir):
@@ -145,7 +142,7 b' class TestVCSOperationsSpecial(object):'
145 cmd.execute('hg clone', clone_url)
142 cmd.execute('hg clone', clone_url)
146
143
147 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
144 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
148 repo.in_memory_commit.add(FileNode(u'readme.md', content=u'## Hello'))
145 repo.in_memory_commit.add(FileNode(b'readme.md', content=b'## Hello'))
149 repo.in_memory_commit.commit(
146 repo.in_memory_commit.commit(
150 message=u'Commit on branch default',
147 message=u'Commit on branch default',
151 author=u'Automatic test',
148 author=u'Automatic test',
@@ -157,14 +154,13 b' class TestVCSOperationsSpecial(object):'
157 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
154 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
158 _check_proper_hg_push(stdout, stderr, branch='default')
155 _check_proper_hg_push(stdout, stderr, branch='default')
159
156
160 ref = '{}/{}/pull-request/new?branch=default'.format(
157 ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default'
161 rc_web_server.host_url(), empty_repo.repo_name)
158 assert f'remote: RhodeCode: open pull request link: {ref}' in stdout
162 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
163 assert 'remote: RhodeCode: push completed' in stdout
159 assert 'remote: RhodeCode: push completed' in stdout
164
160
165 # push on the same branch
161 # push on the same branch
166 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
162 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
167 repo.in_memory_commit.add(FileNode(u'setup.py', content=u'print\n'))
163 repo.in_memory_commit.add(FileNode(b'setup.py', content=b'print\n'))
168 repo.in_memory_commit.commit(
164 repo.in_memory_commit.commit(
169 message=u'Commit2 on branch default',
165 message=u'Commit2 on branch default',
170 author=u'Automatic test2',
166 author=u'Automatic test2',
@@ -176,12 +172,12 b' class TestVCSOperationsSpecial(object):'
176 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
172 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
177 _check_proper_hg_push(stdout, stderr, branch='default')
173 _check_proper_hg_push(stdout, stderr, branch='default')
178
174
179 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
175 assert f'remote: RhodeCode: open pull request link: {ref}' in stdout
180 assert 'remote: RhodeCode: push completed' in stdout
176 assert 'remote: RhodeCode: push completed' in stdout
181
177
182 # new Branch
178 # new Branch
183 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
179 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
184 repo.in_memory_commit.add(FileNode(u'feature1.py', content=u'## Hello world'))
180 repo.in_memory_commit.add(FileNode(b'feature1.py', content=b'## Hello world'))
185 repo.in_memory_commit.commit(
181 repo.in_memory_commit.commit(
186 message=u'Commit on branch feature',
182 message=u'Commit on branch feature',
187 author=u'Automatic test',
183 author=u'Automatic test',
@@ -193,9 +189,8 b' class TestVCSOperationsSpecial(object):'
193 stdout, stderr = repo_cmd.execute('hg push --new-branch --verbose', clone_url)
189 stdout, stderr = repo_cmd.execute('hg push --new-branch --verbose', clone_url)
194 _check_proper_hg_push(stdout, stderr, branch='feature')
190 _check_proper_hg_push(stdout, stderr, branch='feature')
195
191
196 ref = '{}/{}/pull-request/new?branch=feature'.format(
192 ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=feature'
197 rc_web_server.host_url(), empty_repo.repo_name)
193 assert f'remote: RhodeCode: open pull request link: {ref}' in stdout
198 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
199 assert 'remote: RhodeCode: push completed' in stdout
194 assert 'remote: RhodeCode: push completed' in stdout
200
195
201 def test_hg_push_shows_pull_request_refs_book(self, backend_hg, rc_web_server, tmpdir):
196 def test_hg_push_shows_pull_request_refs_book(self, backend_hg, rc_web_server, tmpdir):
@@ -207,7 +202,7 b' class TestVCSOperationsSpecial(object):'
207 cmd.execute('hg clone', clone_url)
202 cmd.execute('hg clone', clone_url)
208
203
209 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
204 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
210 repo.in_memory_commit.add(FileNode(u'readme.md', content=u'## Hello'))
205 repo.in_memory_commit.add(FileNode(b'readme.md', content=b'## Hello'))
211 repo.in_memory_commit.commit(
206 repo.in_memory_commit.commit(
212 message=u'Commit on branch default',
207 message=u'Commit on branch default',
213 author=u'Automatic test',
208 author=u'Automatic test',
@@ -219,14 +214,13 b' class TestVCSOperationsSpecial(object):'
219 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
214 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
220 _check_proper_hg_push(stdout, stderr, branch='default')
215 _check_proper_hg_push(stdout, stderr, branch='default')
221
216
222 ref = '{}/{}/pull-request/new?branch=default'.format(
217 ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default'
223 rc_web_server.host_url(), empty_repo.repo_name)
218 assert f'remote: RhodeCode: open pull request link: {ref}' in stdout
224 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
225 assert 'remote: RhodeCode: push completed' in stdout
219 assert 'remote: RhodeCode: push completed' in stdout
226
220
227 # add bookmark
221 # add bookmark
228 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
222 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
229 repo.in_memory_commit.add(FileNode(u'setup.py', content=u'print\n'))
223 repo.in_memory_commit.add(FileNode(b'setup.py', content=b'print\n'))
230 repo.in_memory_commit.commit(
224 repo.in_memory_commit.commit(
231 message=u'Commit2 on branch default',
225 message=u'Commit2 on branch default',
232 author=u'Automatic test2',
226 author=u'Automatic test2',
@@ -238,12 +232,10 b' class TestVCSOperationsSpecial(object):'
238 stdout, stderr = repo_cmd.execute('hg push -B feature2 --verbose', clone_url)
232 stdout, stderr = repo_cmd.execute('hg push -B feature2 --verbose', clone_url)
239 _check_proper_hg_push(stdout, stderr, branch='default')
233 _check_proper_hg_push(stdout, stderr, branch='default')
240
234
241 ref = '{}/{}/pull-request/new?branch=default'.format(
235 ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?branch=default'
242 rc_web_server.host_url(), empty_repo.repo_name)
236 assert f'remote: RhodeCode: open pull request link: {ref}' in stdout
243 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
237 ref = f'{rc_web_server.host_url()}/{empty_repo.repo_name}/pull-request/new?bookmark=feature2'
244 ref = '{}/{}/pull-request/new?bookmark=feature2'.format(
238 assert f'remote: RhodeCode: open pull request link: {ref}' in stdout
245 rc_web_server.host_url(), empty_repo.repo_name)
246 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
247 assert 'remote: RhodeCode: push completed' in stdout
239 assert 'remote: RhodeCode: push completed' in stdout
248 assert 'exporting bookmark feature2' in stdout
240 assert 'exporting bookmark feature2' in stdout
249
241
@@ -1,4 +1,3 b''
1 # -*- coding: utf-8 -*-
2
1
3 # Copyright (C) 2010-2020 RhodeCode GmbH
2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
3 #
@@ -28,24 +27,14 b' Test suite for making push/pull operatio'
28 """
27 """
29
28
30 import pytest
29 import pytest
31 import requests
32
30
33 from rhodecode.tests import GIT_REPO, HG_REPO
31 from rhodecode.tests import GIT_REPO, HG_REPO
34 from rhodecode.tests.vcs_operations import Command, _add_files_and_push
32 from rhodecode.tests.vcs_operations import Command, _add_files_and_push
35
33 from rhodecode.tests.vcs_operations.conftest import check_httpbin_connection
36
37 def check_connection():
38 try:
39 response = requests.get('http://httpbin.org')
40 return response.status_code == 200
41 except Exception as e:
42 print(e)
43
44 return False
45
34
46
35
47 connection_available = pytest.mark.skipif(
36 connection_available = pytest.mark.skipif(
48 not check_connection(), reason="No outside internet connection available")
37 not check_httpbin_connection(), reason="No outside internet connection available")
49
38
50
39
51 @pytest.mark.usefixtures(
40 @pytest.mark.usefixtures(
@@ -65,7 +54,7 b' class TestVCSOperationsOnCustomIniConfig'
65
54
66 rc_log = rc_web_server.get_rc_log()
55 rc_log = rc_web_server.get_rc_log()
67 assert 'ERROR' not in rc_log
56 assert 'ERROR' not in rc_log
68 assert "'name': u'v1.0.0'" in rc_log
57 assert "{'name': 'v1.0.0'," in rc_log
69
58
70 def test_push_tag_with_commit_git(
59 def test_push_tag_with_commit_git(
71 self, rc_web_server, tmpdir):
60 self, rc_web_server, tmpdir):
@@ -80,7 +69,7 b' class TestVCSOperationsOnCustomIniConfig'
80
69
81 rc_log = rc_web_server.get_rc_log()
70 rc_log = rc_web_server.get_rc_log()
82 assert 'ERROR' not in rc_log
71 assert 'ERROR' not in rc_log
83 assert "'name': u'v1.0.0'" in rc_log
72 assert "{'name': 'v1.0.0'," in rc_log
84
73
85 def test_push_tag_with_no_commit_git(
74 def test_push_tag_with_no_commit_git(
86 self, rc_web_server, tmpdir):
75 self, rc_web_server, tmpdir):
@@ -95,4 +84,4 b' class TestVCSOperationsOnCustomIniConfig'
95
84
96 rc_log = rc_web_server.get_rc_log()
85 rc_log = rc_web_server.get_rc_log()
97 assert 'ERROR' not in rc_log
86 assert 'ERROR' not in rc_log
98 assert "'name': u'v1.0.0'" in rc_log
87 assert "{'name': 'v1.0.0'," in rc_log
@@ -1,4 +1,4 b''
1 ## -*- coding: utf-8 -*-
1 #
2
2
3 ; #################################
3 ; #################################
4 ; RHODECODE VCSSERVER CONFIGURATION
4 ; RHODECODE VCSSERVER CONFIGURATION
General Comments 0
You need to be logged in to leave comments. Login now