##// END OF EJS Templates
tests: fixed name generation for shadow repos
marcink -
r2890:30b99364 default
parent child Browse files
Show More
@@ -1,183 +1,183 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from mock import call, patch
23 from mock import call, patch
24
24
25 from rhodecode.lib.vcs.backends.base import Reference
25 from rhodecode.lib.vcs.backends.base import Reference
26
26
27
27
28 class TestMercurialRemoteRepoInvalidation(object):
28 class TestMercurialRemoteRepoInvalidation(object):
29 """
29 """
30 If the VCSServer is running with multiple processes or/and instances.
30 If the VCSServer is running with multiple processes or/and instances.
31 Operations on repositories are potentially handled by different processes
31 Operations on repositories are potentially handled by different processes
32 in a random fashion. The mercurial repository objects used in the VCSServer
32 in a random fashion. The mercurial repository objects used in the VCSServer
33 are caching the commits of the repo. Therefore we have to invalidate the
33 are caching the commits of the repo. Therefore we have to invalidate the
34 VCSServer caching of these objects after a writing operation.
34 VCSServer caching of these objects after a writing operation.
35 """
35 """
36
36
37 # Default reference used as a dummy during tests.
37 # Default reference used as a dummy during tests.
38 default_ref = Reference('branch', 'default', None)
38 default_ref = Reference('branch', 'default', None)
39
39
40 # Methods of vcsserver.hg.HgRemote that are "writing" operations.
40 # Methods of vcsserver.hg.HgRemote that are "writing" operations.
41 writing_methods = [
41 writing_methods = [
42 'bookmark',
42 'bookmark',
43 'commit',
43 'commit',
44 'merge',
44 'merge',
45 'pull',
45 'pull',
46 'pull_cmd',
46 'pull_cmd',
47 'rebase',
47 'rebase',
48 'strip',
48 'strip',
49 'tag',
49 'tag',
50 ]
50 ]
51
51
52 @pytest.mark.parametrize('method_name, method_args', [
52 @pytest.mark.parametrize('method_name, method_args', [
53 ('_local_merge', [default_ref, None, None, None, default_ref]),
53 ('_local_merge', [default_ref, None, None, None, default_ref]),
54 ('_local_pull', ['', default_ref]),
54 ('_local_pull', ['', default_ref]),
55 ('bookmark', [None]),
55 ('bookmark', [None]),
56 ('pull', ['', default_ref]),
56 ('pull', ['', default_ref]),
57 ('remove_tag', ['mytag', None]),
57 ('remove_tag', ['mytag', None]),
58 ('strip', [None]),
58 ('strip', [None]),
59 ('tag', ['newtag', None]),
59 ('tag', ['newtag', None]),
60 ])
60 ])
61 def test_method_invokes_invalidate_on_remote_repo(
61 def test_method_invokes_invalidate_on_remote_repo(
62 self, method_name, method_args, backend_hg):
62 self, method_name, method_args, backend_hg):
63 """
63 """
64 Check that the listed methods are invalidating the VCSServer cache
64 Check that the listed methods are invalidating the VCSServer cache
65 after invoking a writing method of their remote repository object.
65 after invoking a writing method of their remote repository object.
66 """
66 """
67 tags = {'mytag': 'mytag-id'}
67 tags = {'mytag': 'mytag-id'}
68
68
69 def add_tag(name, raw_id, *args, **kwds):
69 def add_tag(name, raw_id, *args, **kwds):
70 tags[name] = raw_id
70 tags[name] = raw_id
71
71
72 repo = backend_hg.repo.scm_instance()
72 repo = backend_hg.repo.scm_instance()
73 with patch.object(repo, '_remote') as remote:
73 with patch.object(repo, '_remote') as remote:
74 remote.lookup.return_value = ('commit-id', 'commit-idx')
74 remote.lookup.return_value = ('commit-id', 'commit-idx')
75 remote.tags.return_value = tags
75 remote.tags.return_value = tags
76 remote._get_tags.return_value = tags
76 remote._get_tags.return_value = tags
77 remote.tag.side_effect = add_tag
77 remote.tag.side_effect = add_tag
78
78
79 # Invoke method.
79 # Invoke method.
80 method = getattr(repo, method_name)
80 method = getattr(repo, method_name)
81 method(*method_args)
81 method(*method_args)
82
82
83 # Assert that every "writing" method is followed by an invocation
83 # Assert that every "writing" method is followed by an invocation
84 # of the cache invalidation method.
84 # of the cache invalidation method.
85 for counter, method_call in enumerate(remote.method_calls):
85 for counter, method_call in enumerate(remote.method_calls):
86 call_name = method_call[0]
86 call_name = method_call[0]
87 if call_name in self.writing_methods:
87 if call_name in self.writing_methods:
88 next_call = remote.method_calls[counter + 1]
88 next_call = remote.method_calls[counter + 1]
89 assert next_call == call.invalidate_vcs_cache()
89 assert next_call == call.invalidate_vcs_cache()
90
90
91 def _prepare_shadow_repo(self, pull_request):
91 def _prepare_shadow_repo(self, pull_request):
92 """
92 """
93 Helper that creates a shadow repo that can be used to reproduce the
93 Helper that creates a shadow repo that can be used to reproduce the
94 CommitDoesNotExistError when pulling in from target and source
94 CommitDoesNotExistError when pulling in from target and source
95 references.
95 references.
96 """
96 """
97 from rhodecode.model.pull_request import PullRequestModel
97 from rhodecode.model.pull_request import PullRequestModel
98 repo_id = pull_request.target_repo
98 repo_id = pull_request.target_repo.repo_id
99 target_vcs = pull_request.target_repo.scm_instance()
99 target_vcs = pull_request.target_repo.scm_instance()
100 target_ref = pull_request.target_ref_parts
100 target_ref = pull_request.target_ref_parts
101 source_ref = pull_request.source_ref_parts
101 source_ref = pull_request.source_ref_parts
102
102
103 # Create shadow repository.
103 # Create shadow repository.
104 pr = PullRequestModel()
104 pr = PullRequestModel()
105 workspace_id = pr._workspace_id(pull_request)
105 workspace_id = pr._workspace_id(pull_request)
106 shadow_repository_path = target_vcs._maybe_prepare_merge_workspace(
106 shadow_repository_path = target_vcs._maybe_prepare_merge_workspace(
107 repo_id, workspace_id, target_ref, source_ref)
107 repo_id, workspace_id, target_ref, source_ref)
108 shadow_repo = target_vcs._get_shadow_instance(shadow_repository_path)
108 shadow_repo = target_vcs._get_shadow_instance(shadow_repository_path)
109
109
110 # This will populate the cache of the mercurial repository object
110 # This will populate the cache of the mercurial repository object
111 # inside of the VCSServer.
111 # inside of the VCSServer.
112 shadow_repo.get_commit()
112 shadow_repo.get_commit()
113
113
114 return shadow_repo, source_ref, target_ref
114 return shadow_repo, source_ref, target_ref
115
115
116 @pytest.mark.backends('hg')
116 @pytest.mark.backends('hg')
117 def test_commit_does_not_exist_error_happens(self, pr_util, app):
117 def test_commit_does_not_exist_error_happens(self, pr_util, app):
118 """
118 """
119 This test is somewhat special. It does not really test the system
119 This test is somewhat special. It does not really test the system
120 instead it is more or less a precondition for the
120 instead it is more or less a precondition for the
121 "test_commit_does_not_exist_error_does_not_happen". It deactivates the
121 "test_commit_does_not_exist_error_does_not_happen". It deactivates the
122 cache invalidation and asserts that the error occurs.
122 cache invalidation and asserts that the error occurs.
123 """
123 """
124 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
124 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
125
125
126 pull_request = pr_util.create_pull_request()
126 pull_request = pr_util.create_pull_request()
127 target_vcs = pull_request.target_repo.scm_instance()
127 target_vcs = pull_request.target_repo.scm_instance()
128 source_vcs = pull_request.source_repo.scm_instance()
128 source_vcs = pull_request.source_repo.scm_instance()
129 shadow_repo, source_ref, target_ref = self._prepare_shadow_repo(
129 shadow_repo, source_ref, target_ref = self._prepare_shadow_repo(
130 pull_request)
130 pull_request)
131
131
132 # Pull from target and source references but without invalidation of
132 # Pull from target and source references but without invalidation of
133 # RemoteRepo objects and without VCSServer caching of mercurial
133 # RemoteRepo objects and without VCSServer caching of mercurial
134 # repository objects.
134 # repository objects.
135 with patch.object(shadow_repo._remote, 'invalidate_vcs_cache'):
135 with patch.object(shadow_repo._remote, 'invalidate_vcs_cache'):
136 # NOTE: Do not use patch.dict() to disable the cache because it
136 # NOTE: Do not use patch.dict() to disable the cache because it
137 # restores the WHOLE dict and not only the patched keys.
137 # restores the WHOLE dict and not only the patched keys.
138 shadow_repo._remote._wire['cache'] = False
138 shadow_repo._remote._wire['cache'] = False
139 shadow_repo._local_pull(target_vcs.path, target_ref)
139 shadow_repo._local_pull(target_vcs.path, target_ref)
140 shadow_repo._local_pull(source_vcs.path, source_ref)
140 shadow_repo._local_pull(source_vcs.path, source_ref)
141 shadow_repo._remote._wire.pop('cache')
141 shadow_repo._remote._wire.pop('cache')
142
142
143 # Try to lookup the target_ref in shadow repo. This should work because
143 # Try to lookup the target_ref in shadow repo. This should work because
144 # the shadow repo is a clone of the target and always contains all off
144 # the shadow repo is a clone of the target and always contains all off
145 # it's commits in the initial cache.
145 # it's commits in the initial cache.
146 shadow_repo.get_commit(target_ref.commit_id)
146 shadow_repo.get_commit(target_ref.commit_id)
147
147
148 # If we try to lookup the source_ref it should fail because the shadow
148 # If we try to lookup the source_ref it should fail because the shadow
149 # repo commit cache doesn't get invalidated. (Due to patched
149 # repo commit cache doesn't get invalidated. (Due to patched
150 # invalidation and caching above).
150 # invalidation and caching above).
151 with pytest.raises(CommitDoesNotExistError):
151 with pytest.raises(CommitDoesNotExistError):
152 shadow_repo.get_commit(source_ref.commit_id)
152 shadow_repo.get_commit(source_ref.commit_id)
153
153
154 @pytest.mark.backends('hg')
154 @pytest.mark.backends('hg')
155 def test_commit_does_not_exist_error_does_not_happen(self, pr_util, app):
155 def test_commit_does_not_exist_error_does_not_happen(self, pr_util, app):
156 """
156 """
157 This test simulates a pull request merge in which the pull operations
157 This test simulates a pull request merge in which the pull operations
158 are handled by a different VCSServer process than all other operations.
158 are handled by a different VCSServer process than all other operations.
159 Without correct cache invalidation this leads to an error when
159 Without correct cache invalidation this leads to an error when
160 retrieving the pulled commits afterwards.
160 retrieving the pulled commits afterwards.
161 """
161 """
162
162
163 pull_request = pr_util.create_pull_request()
163 pull_request = pr_util.create_pull_request()
164 target_vcs = pull_request.target_repo.scm_instance()
164 target_vcs = pull_request.target_repo.scm_instance()
165 source_vcs = pull_request.source_repo.scm_instance()
165 source_vcs = pull_request.source_repo.scm_instance()
166 shadow_repo, source_ref, target_ref = self._prepare_shadow_repo(
166 shadow_repo, source_ref, target_ref = self._prepare_shadow_repo(
167 pull_request)
167 pull_request)
168
168
169 # Pull from target and source references without without VCSServer
169 # Pull from target and source references without without VCSServer
170 # caching of mercurial repository objects but with active invalidation
170 # caching of mercurial repository objects but with active invalidation
171 # of RemoteRepo objects.
171 # of RemoteRepo objects.
172 # NOTE: Do not use patch.dict() to disable the cache because it
172 # NOTE: Do not use patch.dict() to disable the cache because it
173 # restores the WHOLE dict and not only the patched keys.
173 # restores the WHOLE dict and not only the patched keys.
174 shadow_repo._remote._wire['cache'] = False
174 shadow_repo._remote._wire['cache'] = False
175 shadow_repo._local_pull(target_vcs.path, target_ref)
175 shadow_repo._local_pull(target_vcs.path, target_ref)
176 shadow_repo._local_pull(source_vcs.path, source_ref)
176 shadow_repo._local_pull(source_vcs.path, source_ref)
177 shadow_repo._remote._wire.pop('cache')
177 shadow_repo._remote._wire.pop('cache')
178
178
179 # Try to lookup the target and source references in shadow repo. This
179 # Try to lookup the target and source references in shadow repo. This
180 # should work because the RemoteRepo object gets invalidated during the
180 # should work because the RemoteRepo object gets invalidated during the
181 # above pull operations.
181 # above pull operations.
182 shadow_repo.get_commit(target_ref.commit_id)
182 shadow_repo.get_commit(target_ref.commit_id)
183 shadow_repo.get_commit(source_ref.commit_id)
183 shadow_repo.get_commit(source_ref.commit_id)
General Comments 0
You need to be logged in to leave comments. Login now