##// END OF EJS Templates
tests(core): made the test flag setting more flexible....
super-admin -
r5338:151d5696 default
parent child Browse files
Show More
@@ -1,23 +1,27 b''
1 1 [pytest]
2 2 testpaths = rhodecode
3 3 norecursedirs = rhodecode/public rhodecode/templates tests/scripts
4 4 cache_dir = /tmp/.pytest_cache
5 5
6 6 pyramid_config = rhodecode/tests/rhodecode.ini
7 7 vcsserver_protocol = http
8 8 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
9 9
10 10 addopts =
11 11 --pdbcls=IPython.terminal.debugger:TerminalPdb
12 12 --strict-markers
13 13 --capture=no
14 14 --show-capture=all
15 15
16 16 # --test-loglevel=INFO, show log-level during execution
17 17
18 18 markers =
19 19 vcs_operations: Mark tests depending on a running RhodeCode instance.
20 20 xfail_backends: Mark tests as xfail for given backends.
21 21 skip_backends: Mark tests as skipped for given backends.
22 22 backends: Mark backends
23 23 dbs: database markers for running tests for given DB
24
25 env =
26 RC_TEST=1
27 RUN_ENV=test
@@ -1,46 +1,52 b''
1 1 # test related requirements
2 2
3 3 cov-core==1.15.0
4 4 coverage==7.2.3
5 5 mock==5.0.2
6 6 py==1.11.0
7 7 pytest-cov==4.0.0
8 8 coverage==7.2.3
9 9 pytest==7.3.1
10 10 attrs==22.2.0
11 11 iniconfig==2.0.0
12 12 packaging==23.1
13 13 pluggy==1.0.0
14 pytest-env==1.1.3
15 pytest==7.3.1
16 attrs==22.2.0
17 iniconfig==2.0.0
18 packaging==23.1
19 pluggy==1.0.0
14 20 pytest-rerunfailures==12.0
15 21 pytest-profiling==1.7.0
16 22 gprof2dot==2022.7.29
17 23 pytest==7.3.1
18 24 attrs==22.2.0
19 25 iniconfig==2.0.0
20 26 packaging==23.1
21 27 pluggy==1.0.0
22 28 six==1.16.0
23 29 pytest-runner==6.0.0
24 30 pytest-sugar==0.9.7
25 31 packaging==23.1
26 32 pytest==7.3.1
27 33 attrs==22.2.0
28 34 iniconfig==2.0.0
29 35 packaging==23.1
30 36 pluggy==1.0.0
31 37 termcolor==2.3.0
32 38 pytest-timeout==2.1.0
33 39 pytest==7.3.1
34 40 attrs==22.2.0
35 41 iniconfig==2.0.0
36 42 packaging==23.1
37 43 pluggy==1.0.0
38 44 webtest==3.0.0
39 45 beautifulsoup4==4.11.2
40 46 soupsieve==2.4
41 47 waitress==3.0.0
42 48 webob==1.8.7
43 49
44 50 # RhodeCode test-data
45 51 rc_testdata @ https://code.rhodecode.com/upstream/rc-testdata-dist/raw/77378e9097f700b4c1b9391b56199fe63566b5c9/rc_testdata-0.11.0.tar.gz#egg=rc_testdata
46 52 rc_testdata==0.11.0
@@ -1,91 +1,91 b''
1 1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import os
20 20 import datetime
21 21 import collections
22 22 import logging
23 23
24 24
25 25 now = datetime.datetime.now()
26 26 now = now.strftime("%Y-%m-%d %H:%M:%S") + '.' + f"{int(now.microsecond/1000):03d}"
27 27
28 28 log = logging.getLogger(__name__)
29 29 log.debug(f'{now} Starting RhodeCode imports...')
30 30
31 31
32 32 VERSION = tuple(open(os.path.join(
33 33 os.path.dirname(__file__), 'VERSION')).read().split('.'))
34 34
35 35 BACKENDS = collections.OrderedDict()
36 36
37 37 BACKENDS['hg'] = 'Mercurial repository'
38 38 BACKENDS['git'] = 'Git repository'
39 39 BACKENDS['svn'] = 'Subversion repository'
40 40
41 41
42 42 CELERY_ENABLED = False
43 43 CELERY_EAGER = False
44 44
45 45 # link to config for pyramid
46 46 CONFIG = {}
47 47
48 48
49 49 class ConfigGet:
50 50 NotGiven = object()
51 51
52 52 def _get_val_or_missing(self, key, missing):
53 53 if key not in CONFIG:
54 54 if missing == self.NotGiven:
55 55 return missing
56 56 # we don't get key, we don't get missing value, return nothing similar as config.get(key)
57 57 return None
58 58 else:
59 59 val = CONFIG[key]
60 60 return val
61 61
62 62 def get_str(self, key, missing=NotGiven):
63 63 from rhodecode.lib.str_utils import safe_str
64 64 val = self._get_val_or_missing(key, missing)
65 65 return safe_str(val)
66 66
67 67 def get_int(self, key, missing=NotGiven):
68 68 from rhodecode.lib.str_utils import safe_int
69 69 val = self._get_val_or_missing(key, missing)
70 70 return safe_int(val)
71 71
72 72 def get_bool(self, key, missing=NotGiven):
73 73 from rhodecode.lib.type_utils import str2bool
74 74 val = self._get_val_or_missing(key, missing)
75 75 return str2bool(val)
76 76
77 77 # Populated with the settings dictionary from application init in
78 78 # rhodecode.conf.environment.load_pyramid_environment
79 79 PYRAMID_SETTINGS = {}
80 80
81 81 # Linked module for extensions
82 82 EXTENSIONS = {}
83 83
84 84 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
85 85 __dbversion__ = 114 # defines current db version for migrations
86 86 __license__ = 'AGPLv3, and Commercial License'
87 87 __author__ = 'RhodeCode GmbH'
88 88 __url__ = 'https://code.rhodecode.com'
89 89
90 is_test = False
90 is_test = os.getenv('RC_TEST')
91 91 disable_error_handler = False
@@ -1,200 +1,199 b''
1 1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import os
20 20 import tempfile
21 21 import logging
22 22
23 23 from pyramid.settings import asbool
24 24
25 25 from rhodecode.config.settings_maker import SettingsMaker
26 26 from rhodecode.config import utils as config_utils
27 27
28 28 log = logging.getLogger(__name__)
29 29
30 30
31 31 def sanitize_settings_and_apply_defaults(global_config, settings):
32 32 """
33 33 Applies settings defaults and does all type conversion.
34 34
35 35 We would move all settings parsing and preparation into this place, so that
36 36 we have only one place left which deals with this part. The remaining parts
37 37 of the application would start to rely fully on well-prepared settings.
38 38
39 39 This piece would later be split up per topic to avoid a big fat monster
40 40 function.
41 41 """
42 42 jn = os.path.join
43 43
44 44 global_settings_maker = SettingsMaker(global_config)
45 45 global_settings_maker.make_setting('debug', default=False, parser='bool')
46 46 debug_enabled = asbool(global_config.get('debug'))
47 47
48 48 settings_maker = SettingsMaker(settings)
49 49
50 50 settings_maker.make_setting(
51 51 'logging.autoconfigure',
52 52 default=False,
53 53 parser='bool')
54 54
55 55 logging_conf = jn(os.path.dirname(global_config.get('__file__')), 'logging.ini')
56 56 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
57 57
58 58 # Default includes, possible to change as a user
59 59 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
60 60 log.debug(
61 61 "Using the following pyramid.includes: %s",
62 62 pyramid_includes)
63 63
64 64 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
65 65 settings_maker.make_setting('rhodecode.edition_id', 'CE')
66 66
67 67 if 'mako.default_filters' not in settings:
68 68 # set custom default filters if we don't have it defined
69 69 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
70 70 settings['mako.default_filters'] = 'h_filter'
71 71
72 72 if 'mako.directories' not in settings:
73 73 mako_directories = settings.setdefault('mako.directories', [
74 74 # Base templates of the original application
75 75 'rhodecode:templates',
76 76 ])
77 77 log.debug(
78 78 "Using the following Mako template directories: %s",
79 79 mako_directories)
80 80
81 81 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
82 82 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
83 83 raw_url = settings['beaker.session.url']
84 84 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
85 85 settings['beaker.session.url'] = 'redis://' + raw_url
86 86
87 87 settings_maker.make_setting('__file__', global_config.get('__file__'))
88 88
89 89 # TODO: johbo: Re-think this, usually the call to config.include
90 90 # should allow to pass in a prefix.
91 91 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
92 92
93 93 # Sanitize generic settings.
94 94 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
95 settings_maker.make_setting('is_test', False, parser='bool')
96 95 settings_maker.make_setting('gzip_responses', False, parser='bool')
97 96 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
98 97
99 98 # statsd
100 99 settings_maker.make_setting('statsd.enabled', False, parser='bool')
101 100 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
102 101 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
103 102 settings_maker.make_setting('statsd.statsd_prefix', '')
104 103 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
105 104
106 105 settings_maker.make_setting('vcs.svn.compatible_version', '')
107 106 settings_maker.make_setting('vcs.svn.proxy.enabled', True, parser='bool')
108 107 settings_maker.make_setting('vcs.svn.proxy.host', 'http://svn:8090', parser='string')
109 108 settings_maker.make_setting('vcs.hooks.protocol', 'http')
110 109 settings_maker.make_setting('vcs.hooks.host', '*')
111 110 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
112 111 settings_maker.make_setting('vcs.server', '')
113 112 settings_maker.make_setting('vcs.server.protocol', 'http')
114 113 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
115 114 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
116 115 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
117 116 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
118 117 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
119 118
120 119 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
121 120
122 121 # Support legacy values of vcs.scm_app_implementation. Legacy
123 122 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
124 123 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
125 124 scm_app_impl = settings['vcs.scm_app_implementation']
126 125 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
127 126 settings['vcs.scm_app_implementation'] = 'http'
128 127
129 128 settings_maker.make_setting('appenlight', False, parser='bool')
130 129
131 130 temp_store = tempfile.gettempdir()
132 131 tmp_cache_dir = jn(temp_store, 'rc_cache')
133 132
134 133 # save default, cache dir, and use it for all backends later.
135 134 default_cache_dir = settings_maker.make_setting(
136 135 'cache_dir',
137 136 default=tmp_cache_dir, default_when_empty=True,
138 137 parser='dir:ensured')
139 138
140 139 # exception store cache
141 140 settings_maker.make_setting(
142 141 'exception_tracker.store_path',
143 142 default=jn(default_cache_dir, 'exc_store'), default_when_empty=True,
144 143 parser='dir:ensured'
145 144 )
146 145
147 146 settings_maker.make_setting(
148 147 'celerybeat-schedule.path',
149 148 default=jn(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
150 149 parser='file:ensured'
151 150 )
152 151
153 152 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
154 153 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
155 154
156 155 # sessions, ensure file since no-value is memory
157 156 settings_maker.make_setting('beaker.session.type', 'file')
158 157 settings_maker.make_setting('beaker.session.data_dir', jn(default_cache_dir, 'session_data'))
159 158
160 159 # cache_general
161 160 settings_maker.make_setting('rc_cache.cache_general.backend', 'dogpile.cache.rc.file_namespace')
162 161 settings_maker.make_setting('rc_cache.cache_general.expiration_time', 60 * 60 * 12, parser='int')
163 162 settings_maker.make_setting('rc_cache.cache_general.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_general.db'))
164 163
165 164 # cache_perms
166 165 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
167 166 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60 * 60, parser='int')
168 167 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_perms_db'))
169 168
170 169 # cache_repo
171 170 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
172 171 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60 * 60 * 24 * 30, parser='int')
173 172 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_repo_db'))
174 173
175 174 # cache_license
176 175 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
177 176 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 60 * 5, parser='int')
178 177 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_license_db'))
179 178
180 179 # cache_repo_longterm memory, 96H
181 180 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
182 181 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
183 182 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
184 183
185 184 # sql_cache_short
186 185 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
187 186 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
188 187 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
189 188
190 189 # archive_cache
191 190 settings_maker.make_setting('archive_cache.store_dir', jn(default_cache_dir, 'archive_cache'), default_when_empty=True,)
192 191 settings_maker.make_setting('archive_cache.cache_size_gb', 10, parser='float')
193 192 settings_maker.make_setting('archive_cache.cache_shards', 10, parser='int')
194 193
195 194 settings_maker.env_expand()
196 195
197 196 # configure instance id
198 197 config_utils.set_instance_id(settings)
199 198
200 199 return settings
@@ -1,89 +1,88 b''
1 1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import os
20 20 import logging
21 21 import rhodecode
22 22 import collections
23 23
24 24 from rhodecode.config import utils
25 25
26 26 from rhodecode.lib.utils import load_rcextensions
27 27 from rhodecode.lib.utils2 import str2bool
28 28 from rhodecode.lib.vcs import connect_vcs
29 29
30 30 log = logging.getLogger(__name__)
31 31
32 32
33 33 def load_pyramid_environment(global_config, settings):
34 34 # Some parts of the code expect a merge of global and app settings.
35 35 settings_merged = global_config.copy()
36 36 settings_merged.update(settings)
37 37
38 38 # TODO(marcink): probably not required anymore
39 39 # configure channelstream,
40 40 settings_merged['channelstream_config'] = {
41 41 'enabled': str2bool(settings_merged.get('channelstream.enabled', False)),
42 42 'server': settings_merged.get('channelstream.server'),
43 43 'secret': settings_merged.get('channelstream.secret')
44 44 }
45 45
46 46 # If this is a test run we prepare the test environment like
47 47 # creating a test database, test search index and test repositories.
48 48 # This has to be done before the database connection is initialized.
49 if settings['is_test']:
50 rhodecode.is_test = True
49 if rhodecode.is_test:
51 50 rhodecode.disable_error_handler = True
52 51 from rhodecode import authentication
53 52 authentication.plugin_default_auth_ttl = 0
54 53
55 54 utils.initialize_test_environment(settings_merged)
56 55
57 56 # Initialize the database connection.
58 57 utils.initialize_database(settings_merged)
59 58
60 59 load_rcextensions(root_path=settings_merged['here'])
61 60
62 61 # Limit backends to `vcs.backends` from configuration, and preserve the order
63 62 for alias in rhodecode.BACKENDS.keys():
64 63 if alias not in settings['vcs.backends']:
65 64 del rhodecode.BACKENDS[alias]
66 65
67 66 _sorted_backend = sorted(rhodecode.BACKENDS.items(),
68 67 key=lambda item: settings['vcs.backends'].index(item[0]))
69 68 rhodecode.BACKENDS = collections.OrderedDict(_sorted_backend)
70 69
71 70 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
72 71
73 72 # initialize vcs client and optionally run the server if enabled
74 73 vcs_server_uri = settings['vcs.server']
75 74 vcs_server_enabled = settings['vcs.server.enable']
76 75
77 76 utils.configure_vcs(settings)
78 77
79 78 # Store the settings to make them available to other modules.
80 79
81 80 rhodecode.PYRAMID_SETTINGS = settings_merged
82 81 rhodecode.CONFIG = settings_merged
83 82 rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id()
84 83 rhodecode.CONFIG['default_base_path'] = utils.get_default_base_path()
85 84
86 85 if vcs_server_enabled:
87 86 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
88 87 else:
89 88 log.warning('vcs-server not enabled, vcs connection unavailable')
@@ -1,288 +1,287 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 from subprocess import Popen, PIPE
21 21 import os
22 22 import sys
23 23 import tempfile
24 24
25 25 import pytest
26 26 from sqlalchemy.engine import url
27 27
28 28 from rhodecode.lib.str_utils import safe_str, safe_bytes
29 29 from rhodecode.tests.fixture import TestINI
30 30
31 31
32 32 def _get_dbs_from_metafunc(metafunc):
33 33 dbs_mark = metafunc.definition.get_closest_marker('dbs')
34 34
35 35 if dbs_mark:
36 36 # Supported backends by this test function, created from pytest.mark.dbs
37 37 backends = dbs_mark.args
38 38 else:
39 39 backends = metafunc.config.getoption('--dbs')
40 40 return backends
41 41
42 42
43 43 def pytest_generate_tests(metafunc):
44 44 # Support test generation based on --dbs parameter
45 45 if 'db_backend' in metafunc.fixturenames:
46 46 requested_backends = set(metafunc.config.getoption('--dbs'))
47 47 backends = _get_dbs_from_metafunc(metafunc)
48 48 backends = requested_backends.intersection(backends)
49 49 # TODO: johbo: Disabling a backend did not work out with
50 50 # parametrization, find better way to achieve this.
51 51 if not backends:
52 52 metafunc.function._skip = True
53 53 metafunc.parametrize('db_backend_name', backends)
54 54
55 55
56 56 def pytest_collection_modifyitems(session, config, items):
57 57 remaining = [
58 58 i for i in items if not getattr(i.obj, '_skip', False)]
59 59 items[:] = remaining
60 60
61 61
62 62 @pytest.fixture()
63 63 def db_backend(
64 64 request, db_backend_name, ini_config, tmpdir_factory):
65 65 basetemp = tmpdir_factory.getbasetemp().strpath
66 66 klass = _get_backend(db_backend_name)
67 67
68 68 option_name = '--{}-connection-string'.format(db_backend_name)
69 69 connection_string = request.config.getoption(option_name) or None
70 70
71 71 return klass(
72 72 config_file=ini_config, basetemp=basetemp,
73 73 connection_string=connection_string)
74 74
75 75
76 76 def _get_backend(backend_type):
77 77 return {
78 78 'sqlite': SQLiteDBBackend,
79 79 'postgres': PostgresDBBackend,
80 80 'mysql': MySQLDBBackend,
81 81 '': EmptyDBBackend
82 82 }[backend_type]
83 83
84 84
85 85 class DBBackend(object):
86 86 _store = os.path.dirname(os.path.abspath(__file__))
87 87 _type = None
88 88 _base_ini_config = [{'app:main': {'vcs.start_server': 'false',
89 'startup.import_repos': 'false',
90 'is_test': 'False'}}]
89 'startup.import_repos': 'false'}}]
91 90 _db_url = [{'app:main': {'sqlalchemy.db1.url': ''}}]
92 91 _base_db_name = 'rhodecode_test_db_backend'
93 92
94 93 def __init__(
95 94 self, config_file, db_name=None, basetemp=None,
96 95 connection_string=None):
97 96
98 97 from rhodecode.lib.vcs.backends.hg import largefiles_store
99 98 from rhodecode.lib.vcs.backends.git import lfs_store
100 99
101 100 self.fixture_store = os.path.join(self._store, self._type)
102 101 self.db_name = db_name or self._base_db_name
103 102 self._base_ini_file = config_file
104 103 self.stderr = ''
105 104 self.stdout = ''
106 105 self._basetemp = basetemp or tempfile.gettempdir()
107 106 self._repos_location = os.path.join(self._basetemp, 'rc_test_repos')
108 107 self._repos_hg_largefiles_store = largefiles_store(self._basetemp)
109 108 self._repos_git_lfs_store = lfs_store(self._basetemp)
110 109 self.connection_string = connection_string
111 110
112 111 @property
113 112 def connection_string(self):
114 113 return self._connection_string
115 114
116 115 @connection_string.setter
117 116 def connection_string(self, new_connection_string):
118 117 if not new_connection_string:
119 118 new_connection_string = self.get_default_connection_string()
120 119 else:
121 120 new_connection_string = new_connection_string.format(
122 121 db_name=self.db_name)
123 122 url_parts = url.make_url(new_connection_string)
124 123 self._connection_string = new_connection_string
125 124 self.user = url_parts.username
126 125 self.password = url_parts.password
127 126 self.host = url_parts.host
128 127
129 128 def get_default_connection_string(self):
130 129 raise NotImplementedError('default connection_string is required.')
131 130
132 131 def execute(self, cmd, env=None, *args):
133 132 """
134 133 Runs command on the system with given ``args``.
135 134 """
136 135
137 136 command = cmd + ' ' + ' '.join(args)
138 137 sys.stdout.write(command)
139 138
140 139 # Tell Python to use UTF-8 encoding out stdout
141 140 _env = os.environ.copy()
142 141 _env['PYTHONIOENCODING'] = 'UTF-8'
143 142 if env:
144 143 _env.update(env)
145 144 self.p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=_env)
146 145 self.stdout, self.stderr = self.p.communicate()
147 146 stdout_str = safe_str(self.stdout)
148 147 sys.stdout.write(f'COMMAND:{command}\n')
149 148 sys.stdout.write(stdout_str)
150 149 return self.stdout, self.stderr
151 150
152 151 def assert_returncode_success(self):
153 152 from rich import print as pprint
154 153 if not self.p.returncode == 0:
155 154 pprint(safe_str(self.stderr))
156 155 raise AssertionError(f'non 0 retcode:{self.p.returncode}')
157 156
158 157 def assert_correct_output(self, stdout, version):
159 158 assert b'UPGRADE FOR STEP %b COMPLETED' % safe_bytes(version) in stdout
160 159
161 160 def setup_rhodecode_db(self, ini_params=None, env=None):
162 161 if not ini_params:
163 162 ini_params = self._base_ini_config
164 163
165 164 ini_params.extend(self._db_url)
166 165 with TestINI(self._base_ini_file, ini_params,
167 166 self._type, destroy=True) as _ini_file:
168 167
169 168 if not os.path.isdir(self._repos_location):
170 169 os.makedirs(self._repos_location)
171 170 if not os.path.isdir(self._repos_hg_largefiles_store):
172 171 os.makedirs(self._repos_hg_largefiles_store)
173 172 if not os.path.isdir(self._repos_git_lfs_store):
174 173 os.makedirs(self._repos_git_lfs_store)
175 174
176 175 return self.execute(
177 176 "rc-setup-app {0} --user=marcink "
178 177 "--email=marcin@rhodeocode.com --password={1} "
179 178 "--repos={2} --force-yes".format(
180 179 _ini_file, 'qweqwe', self._repos_location), env=env)
181 180
182 181 def upgrade_database(self, ini_params=None):
183 182 if not ini_params:
184 183 ini_params = self._base_ini_config
185 184 ini_params.extend(self._db_url)
186 185
187 186 test_ini = TestINI(
188 187 self._base_ini_file, ini_params, self._type, destroy=True)
189 188 with test_ini as ini_file:
190 189 if not os.path.isdir(self._repos_location):
191 190 os.makedirs(self._repos_location)
192 191
193 192 return self.execute(
194 193 "rc-upgrade-db {0} --force-yes".format(ini_file))
195 194
196 195 def setup_db(self):
197 196 raise NotImplementedError
198 197
199 198 def teardown_db(self):
200 199 raise NotImplementedError
201 200
202 201 def import_dump(self, dumpname):
203 202 raise NotImplementedError
204 203
205 204
206 205 class EmptyDBBackend(DBBackend):
207 206 _type = ''
208 207
209 208 def setup_db(self):
210 209 pass
211 210
212 211 def teardown_db(self):
213 212 pass
214 213
215 214 def import_dump(self, dumpname):
216 215 pass
217 216
218 217 def assert_returncode_success(self):
219 218 assert True
220 219
221 220
222 221 class SQLiteDBBackend(DBBackend):
223 222 _type = 'sqlite'
224 223
225 224 def get_default_connection_string(self):
226 225 return 'sqlite:///{}/{}.sqlite'.format(self._basetemp, self.db_name)
227 226
228 227 def setup_db(self):
229 228 # dump schema for tests
230 229 # cp -v $TEST_DB_NAME
231 230 self._db_url = [{'app:main': {
232 231 'sqlalchemy.db1.url': self.connection_string}}]
233 232
234 233 def import_dump(self, dumpname):
235 234 dump = os.path.join(self.fixture_store, dumpname)
236 235 target = os.path.join(self._basetemp, '{0.db_name}.sqlite'.format(self))
237 236 return self.execute(f'cp -v {dump} {target}')
238 237
239 238 def teardown_db(self):
240 239 target_db = os.path.join(self._basetemp, self.db_name)
241 240 return self.execute(f"rm -rf {target_db}.sqlite")
242 241
243 242
244 243 class MySQLDBBackend(DBBackend):
245 244 _type = 'mysql'
246 245
247 246 def get_default_connection_string(self):
248 247 return 'mysql://root:qweqwe@127.0.0.1/{}'.format(self.db_name)
249 248
250 249 def setup_db(self):
251 250 # dump schema for tests
252 251 # mysqldump -uroot -pqweqwe $TEST_DB_NAME
253 252 self._db_url = [{'app:main': {
254 253 'sqlalchemy.db1.url': self.connection_string}}]
255 254 return self.execute("mysql -v -u{} -p{} -e 'create database '{}';'".format(
256 255 self.user, self.password, self.db_name))
257 256
258 257 def import_dump(self, dumpname):
259 258 dump = os.path.join(self.fixture_store, dumpname)
260 259 return self.execute("mysql -u{} -p{} {} < {}".format(
261 260 self.user, self.password, self.db_name, dump))
262 261
263 262 def teardown_db(self):
264 263 return self.execute("mysql -v -u{} -p{} -e 'drop database '{}';'".format(
265 264 self.user, self.password, self.db_name))
266 265
267 266
268 267 class PostgresDBBackend(DBBackend):
269 268 _type = 'postgres'
270 269
271 270 def get_default_connection_string(self):
272 271 return 'postgresql://postgres:qweqwe@localhost/{}'.format(self.db_name)
273 272
274 273 def setup_db(self):
275 274 # dump schema for tests
276 275 # pg_dump -U postgres -h localhost $TEST_DB_NAME
277 276 self._db_url = [{'app:main': {'sqlalchemy.db1.url': self.connection_string}}]
278 277 cmd = f"PGPASSWORD={self.password} psql -U {self.user} -h localhost -c 'create database '{self.db_name}';'"
279 278 return self.execute(cmd)
280 279
281 280 def teardown_db(self):
282 281 cmd = f"PGPASSWORD={self.password} psql -U {self.user} -h localhost -c 'drop database if exists '{self.db_name}';'"
283 282 return self.execute(cmd)
284 283
285 284 def import_dump(self, dumpname):
286 285 dump = os.path.join(self.fixture_store, dumpname)
287 286 cmd = f"PGPASSWORD={self.password} psql -U {self.user} -h localhost -d {self.db_name} -1 -f {dump}"
288 287 return self.execute(cmd)
General Comments 0
You need to be logged in to leave comments. Login now