1# frozen_string_literal: true
2
3require 'spec_helper'
4
5RSpec.describe Gitlab::GitalyClient::RepositoryService do
6  using RSpec::Parameterized::TableSyntax
7
8  let(:project) { create(:project) }
9  let(:storage_name) { project.repository_storage }
10  let(:relative_path) { project.disk_path + '.git' }
11  let(:client) { described_class.new(project.repository) }
12
13  describe '#exists?' do
14    it 'sends a repository_exists message' do
15      expect_any_instance_of(Gitaly::RepositoryService::Stub)
16        .to receive(:repository_exists)
17        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
18        .and_return(double(exists: true))
19
20      client.exists?
21    end
22  end
23
24  describe '#cleanup' do
25    it 'sends a cleanup message' do
26      expect_any_instance_of(Gitaly::RepositoryService::Stub)
27        .to receive(:cleanup)
28        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
29
30      client.cleanup
31    end
32  end
33
34  describe '#garbage_collect' do
35    it 'sends a garbage_collect message' do
36      expect_any_instance_of(Gitaly::RepositoryService::Stub)
37        .to receive(:garbage_collect)
38        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
39        .and_return(double(:garbage_collect_response))
40
41      client.garbage_collect(true, prune: true)
42    end
43  end
44
45  describe '#repack_full' do
46    it 'sends a repack_full message' do
47      expect_any_instance_of(Gitaly::RepositoryService::Stub)
48        .to receive(:repack_full)
49        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
50        .and_return(double(:repack_full_response))
51
52      client.repack_full(true)
53    end
54  end
55
56  describe '#repack_incremental' do
57    it 'sends a repack_incremental message' do
58      expect_any_instance_of(Gitaly::RepositoryService::Stub)
59        .to receive(:repack_incremental)
60        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
61        .and_return(double(:repack_incremental_response))
62
63      client.repack_incremental
64    end
65  end
66
67  describe '#repository_size' do
68    it 'sends a repository_size message' do
69      expect_any_instance_of(Gitaly::RepositoryService::Stub)
70        .to receive(:repository_size)
71        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
72        .and_return(size: 0)
73
74      client.repository_size
75    end
76  end
77
78  describe '#get_object_directory_size' do
79    it 'sends a get_object_directory_size message' do
80      expect_any_instance_of(Gitaly::RepositoryService::Stub)
81        .to receive(:get_object_directory_size)
82        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
83        .and_return(size: 0)
84
85      client.get_object_directory_size
86    end
87  end
88
89  describe '#apply_gitattributes' do
90    let(:revision) { 'master' }
91
92    it 'sends an apply_gitattributes message' do
93      expect_any_instance_of(Gitaly::RepositoryService::Stub)
94        .to receive(:apply_gitattributes)
95        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
96        .and_return(double(:apply_gitattributes_response))
97
98      client.apply_gitattributes(revision)
99    end
100  end
101
102  describe '#info_attributes' do
103    it 'reads the info attributes' do
104      expect_any_instance_of(Gitaly::RepositoryService::Stub)
105        .to receive(:get_info_attributes)
106        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
107        .and_return([])
108
109      client.info_attributes
110    end
111  end
112
113  describe '#has_local_branches?' do
114    it 'sends a has_local_branches message' do
115      expect_any_instance_of(Gitaly::RepositoryService::Stub)
116        .to receive(:has_local_branches)
117        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
118        .and_return(double(value: true))
119
120      expect(client.has_local_branches?).to be(true)
121    end
122  end
123
124  describe '#fetch_remote' do
125    let(:url) { 'https://example.com/git/repo.git' }
126
127    it 'sends a fetch_remote_request message' do
128      expected_request = gitaly_request_with_params(
129        remote_params: Gitaly::Remote.new(
130          url: url,
131          http_authorization_header: "",
132          mirror_refmaps: []
133        ),
134        ssh_key: '',
135        known_hosts: '',
136        force: false,
137        no_tags: false,
138        no_prune: false,
139        check_tags_changed: false
140      )
141
142      expect_any_instance_of(Gitaly::RepositoryService::Stub)
143        .to receive(:fetch_remote)
144        .with(expected_request, kind_of(Hash))
145        .and_return(double(value: true))
146
147      client.fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, timeout: 1, check_tags_changed: false)
148    end
149
150    context 'SSH auth' do
151      where(:ssh_mirror_url, :ssh_key_auth, :ssh_private_key, :ssh_known_hosts, :expected_params) do
152        false | false | 'key' | 'known_hosts' | {}
153        false | true  | 'key' | 'known_hosts' | {}
154        true  | false | 'key' | 'known_hosts' | { known_hosts: 'known_hosts' }
155        true  | true  | 'key' | 'known_hosts' | { ssh_key: 'key', known_hosts: 'known_hosts' }
156        true  | true  | 'key' | nil           | { ssh_key: 'key' }
157        true  | true  | nil   | 'known_hosts' | { known_hosts: 'known_hosts' }
158        true  | true  | nil   | nil           | {}
159        true  | true  | ''    | ''            | {}
160      end
161
162      with_them do
163        let(:ssh_auth) do
164          double(
165            :ssh_auth,
166            ssh_mirror_url?: ssh_mirror_url,
167            ssh_key_auth?: ssh_key_auth,
168            ssh_private_key: ssh_private_key,
169            ssh_known_hosts: ssh_known_hosts
170          )
171        end
172
173        it do
174          expected_request = gitaly_request_with_params({
175            remote_params: Gitaly::Remote.new(
176              url: url,
177              http_authorization_header: "",
178              mirror_refmaps: []
179            ),
180            ssh_key: '',
181            known_hosts: '',
182            force: false,
183            no_tags: false,
184            no_prune: false
185          }.update(expected_params))
186
187          expect_any_instance_of(Gitaly::RepositoryService::Stub)
188            .to receive(:fetch_remote)
189            .with(expected_request, kind_of(Hash))
190            .and_return(double(value: true))
191
192          client.fetch_remote(url, refmap: nil, ssh_auth: ssh_auth, forced: false, no_tags: false, timeout: 1)
193        end
194      end
195    end
196  end
197
198  describe '#calculate_checksum' do
199    it 'sends a calculate_checksum message' do
200      expect_any_instance_of(Gitaly::RepositoryService::Stub)
201        .to receive(:calculate_checksum)
202        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
203        .and_return(double(checksum: 0))
204
205      client.calculate_checksum
206    end
207  end
208
209  describe '#create_from_snapshot' do
210    it 'sends a create_repository_from_snapshot message' do
211      expect_any_instance_of(Gitaly::RepositoryService::Stub)
212        .to receive(:create_repository_from_snapshot)
213        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
214        .and_return(double)
215
216      client.create_from_snapshot('http://example.com?wiki=1', 'Custom xyz')
217    end
218  end
219
220  describe '#raw_changes_between' do
221    it 'sends a create_repository_from_snapshot message' do
222      expect_any_instance_of(Gitaly::RepositoryService::Stub)
223        .to receive(:get_raw_changes)
224        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
225        .and_return(double)
226
227      client.raw_changes_between('deadbeef', 'deadpork')
228    end
229  end
230
231  describe '#search_files_by_regexp' do
232    subject(:result) { client.search_files_by_regexp('master', '.*') }
233
234    before do
235      expect_any_instance_of(Gitaly::RepositoryService::Stub)
236        .to receive(:search_files_by_name)
237        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
238        .and_return([double(files: ['file1.txt']), double(files: ['file2.txt'])])
239    end
240
241    it 'sends a search_files_by_name message and returns a flatten array' do
242      expect(result).to contain_exactly('file1.txt', 'file2.txt')
243    end
244  end
245
246  describe '#disconnect_alternates' do
247    let(:project) { create(:project, :repository) }
248    let(:repository) { project.repository }
249    let(:repository_path) { File.join(TestEnv.repos_path, repository.relative_path) }
250    let(:pool_repository) { create(:pool_repository) }
251    let(:object_pool) { pool_repository.object_pool }
252    let(:object_pool_service) { Gitlab::GitalyClient::ObjectPoolService.new(object_pool) }
253
254    before do
255      object_pool_service.create(repository) # rubocop:disable Rails/SaveBang
256      object_pool_service.link_repository(repository)
257    end
258
259    it 'deletes the alternates file' do
260      repository.disconnect_alternates
261
262      alternates_file = File.join(repository_path, "objects", "info", "alternates")
263
264      expect(File.exist?(alternates_file)).to be_falsey
265    end
266
267    context 'when called twice' do
268      it "doesn't raise an error" do
269        repository.disconnect_alternates
270
271        expect { repository.disconnect_alternates }.not_to raise_error
272      end
273    end
274  end
275
276  describe '#rename' do
277    it 'sends a rename_repository message' do
278      expect_any_instance_of(Gitaly::RepositoryService::Stub)
279        .to receive(:rename_repository)
280        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
281        .and_return(double(value: true))
282
283      client.rename('some/new/path')
284    end
285  end
286
287  describe '#remove' do
288    it 'sends a remove_repository message' do
289      expect_any_instance_of(Gitaly::RepositoryService::Stub)
290        .to receive(:remove_repository)
291        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
292        .and_return(double(value: true))
293
294      client.remove
295    end
296  end
297
298  describe '#replicate' do
299    let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '', 'group/project') }
300
301    it 'sends a replicate_repository message' do
302      expect_any_instance_of(Gitaly::RepositoryService::Stub)
303        .to receive(:replicate_repository)
304        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
305
306      client.replicate(source_repository)
307    end
308  end
309
310  describe '#set_full_path' do
311    let(:path) { 'repo/path' }
312
313    it 'sends a set_full_path message' do
314      expect_any_instance_of(Gitaly::RepositoryService::Stub)
315        .to receive(:set_full_path)
316        .with(gitaly_request_with_params(path: path), kind_of(Hash))
317        .and_return(double)
318
319      client.set_full_path(path)
320    end
321  end
322end
323