update_service_spec.rb 14 KB
Newer Older
1
2
require 'spec_helper'

3
describe Projects::UpdateService do
4
5
  include ProjectForksHelper

6
  let(:user) { create(:user) }
7
  let(:project) do
8
    create(:project, creator: user, namespace: user.namespace)
9
10
  end

11
12
13
  describe '#execute' do
    let(:gitlab_shell) { Gitlab::Shell.new }
    let(:admin) { create(:admin) }
14

15
    context 'when changing visibility level' do
16
17
      context 'when visibility_level is INTERNAL' do
        it 'updates the project to internal' do
18
19
          expect(TodosDestroyer::ProjectPrivateWorker).not_to receive(:perform_in)

20
          result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
21

22
23
          expect(result).to eq({ status: :success })
          expect(project).to be_internal
24
25
26
        end
      end

27
      context 'when visibility_level is PUBLIC' do
28
        it 'updates the project to public' do
29
30
          expect(TodosDestroyer::ProjectPrivateWorker).not_to receive(:perform_in)

31
          result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
32

33
34
35
36
          expect(result).to eq({ status: :success })
          expect(project).to be_public
        end
      end
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
      context 'when visibility_level is PRIVATE' do
        before do
          project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
        end

        it 'updates the project to private' do
          expect(TodosDestroyer::ProjectPrivateWorker).to receive(:perform_in).with(1.hour, project.id)

          result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE)

          expect(result).to eq({ status: :success })
          expect(project).to be_private
        end
      end

53
54
55
      context 'when visibility levels are restricted to PUBLIC only' do
        before do
          stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
56
57
        end

58
59
60
        context 'when visibility_level is INTERNAL' do
          it 'updates the project to internal' do
            result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
61

62
            expect(result).to eq({ status: :success })
63
            expect(project).to be_internal
64
          end
65
        end
66

67
68
69
        context 'when visibility_level is PUBLIC' do
          it 'does not update the project to public' do
            result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
70

71
72
73
74
75
76
77
            expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
            expect(project).to be_private
          end

          context 'when updated by an admin' do
            it 'updates the project to public' do
              result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
78

79
80
81
82
83
              expect(result).to eq({ status: :success })
              expect(project).to be_public
            end
          end
        end
84
85
      end

86
      context 'when project visibility is higher than parent group' do
87
88
89
90
91
92
93
94
        let(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }

        before do
          project.update(namespace: group, visibility_level: group.visibility_level)
        end

        it 'does not update project visibility level' do
          result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
95

96
97
98
          expect(result).to eq({ status: :error, message: 'Visibility level public is not allowed in a internal group.' })
          expect(project.reload).to be_internal
        end
99
100
      end
    end
101

102
103
104
    describe 'when updating project that has forks' do
      let(:project) { create(:project, :internal) }
      let(:forked_project) { fork_project(project) }
105

106
107
      it 'updates forks visibility level when parent set to more restrictive' do
        opts = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
108

109
110
        expect(project).to be_internal
        expect(forked_project).to be_internal
111

112
        expect(update_project(project, admin, opts)).to eq({ status: :success })
113

114
115
116
        expect(project).to be_private
        expect(forked_project.reload).to be_private
      end
117

118
119
      it 'does not update forks visibility level when parent set to less restrictive' do
        opts = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
120

121
122
        expect(project).to be_internal
        expect(forked_project).to be_internal
123

124
        expect(update_project(project, admin, opts)).to eq({ status: :success })
125

126
127
128
        expect(project).to be_public
        expect(forked_project.reload).to be_internal
      end
129
130
    end

131
132
    context 'when updating a default branch' do
      let(:project) { create(:project, :repository) }
133

134
135
      it 'changes a default branch' do
        update_project(project, admin, default_branch: 'feature')
136

137
138
        expect(Project.find(project.id).default_branch).to eq 'feature'
      end
139

140
141
142
      it 'does not change a default branch' do
        # The branch 'unexisted-branch' does not exist.
        update_project(project, admin, default_branch: 'unexisted-branch')
143

144
145
        expect(Project.find(project.id).default_branch).to eq 'master'
      end
146
    end
147

148
149
    context 'when we update project but not enabling a wiki' do
      it 'does not try to create an empty wiki' do
150
        Gitlab::Shell.new.rm_directory(project.repository_storage, project.wiki.path)
151
152
153
154
155
156

        result = update_project(project, user, { name: 'test1' })

        expect(result).to eq({ status: :success })
        expect(project.wiki_repository_exists?).to be false
      end
157
158
159
160
161
162
163
164
165

      it 'handles empty project feature attributes' do
        project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED)

        result = update_project(project, user, { name: 'test1' })

        expect(result).to eq({ status: :success })
        expect(project.wiki_repository_exists?).to be false
      end
166
167
    end

168
169
170
    context 'when enabling a wiki' do
      it 'creates a wiki' do
        project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED)
171
        Gitlab::Shell.new.rm_directory(project.repository_storage, project.wiki.path)
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

        result = update_project(project, user, project_feature_attributes: { wiki_access_level: ProjectFeature::ENABLED })

        expect(result).to eq({ status: :success })
        expect(project.wiki_repository_exists?).to be true
        expect(project.wiki_enabled?).to be true
      end

      it 'logs an error and creates a metric when wiki can not be created' do
        project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED)

        expect_any_instance_of(ProjectWiki).to receive(:wiki).and_raise(ProjectWiki::CouldNotCreateWikiError)
        expect_any_instance_of(described_class).to receive(:log_error).with("Could not create wiki for #{project.full_name}")
        expect(Gitlab::Metrics).to receive(:counter)

        update_project(project, user, project_feature_attributes: { wiki_access_level: ProjectFeature::ENABLED })
      end
    end

191
192
193
194
195
196
197
198
199
200
201
202
203
204
    context 'when changing feature visibility to private' do
      it 'updates the visibility correctly' do
        expect(TodosDestroyer::PrivateFeaturesWorker)
          .to receive(:perform_in).with(1.hour, project.id)

        result = update_project(project, user, project_feature_attributes:
                                 { issues_access_level: ProjectFeature::PRIVATE }
                               )

        expect(result).to eq({ status: :success })
        expect(project.project_feature.issues_access_level).to be(ProjectFeature::PRIVATE)
      end
    end

205
206
207
208
209
210
    context 'when updating a project that contains container images' do
      before do
        stub_container_registry_config(enabled: true)
        stub_container_registry_tags(repository: /image/, tags: %w[rc1])
        create(:container_repository, project: project, name: :image)
      end
211

212
213
      it 'does not allow to rename the project' do
        result = update_project(project, admin, path: 'renamed')
214

215
216
217
        expect(result).to include(status: :error)
        expect(result[:message]).to match(/contains container registry tags/)
      end
218

219
220
      it 'allows to update other settings' do
        result = update_project(project, admin, public_builds: true)
221

222
223
224
        expect(result[:status]).to eq :success
        expect(project.reload.public_builds).to be true
      end
225
    end
226

227
228
    context 'when renaming a project' do
      let(:repository_storage) { 'default' }
229
      let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage].legacy_disk_path }
230

231
      context 'with legacy storage' do
Nick Thomas's avatar
Nick Thomas committed
232
233
        let(:project) { create(:project, :legacy_storage, :repository, creator: user, namespace: user.namespace) }

234
        before do
235
          gitlab_shell.create_repository(repository_storage, "#{user.namespace.full_path}/existing")
236
237
238
        end

        after do
239
          gitlab_shell.remove_repository(repository_storage, "#{user.namespace.full_path}/existing")
240
241
242
243
        end

        it 'does not allow renaming when new path matches existing repository on disk' do
          result = update_project(project, admin, path: 'existing')
244

245
246
247
248
249
250
          expect(result).to include(status: :error)
          expect(result[:message]).to match('There is already a repository with that name on disk')
          expect(project).not_to be_valid
          expect(project.errors.messages).to have_key(:base)
          expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk')
        end
251

252
253
254
255
256
257
258
259
260
261
262
        it 'renames the project without upgrading it' do
          result = update_project(project, admin, path: 'new-path')

          expect(result).not_to include(status: :error)
          expect(project).to be_valid
          expect(project.errors).to be_empty
          expect(project.disk_path).to include('new-path')
          expect(project.reload.hashed_storage?(:repository)).to be_falsey
        end

        context 'when hashed storage is enabled' do
263
264
          before do
            stub_application_setting(hashed_storage_enabled: true)
265
            stub_feature_flags(skip_hashed_storage_upgrade: false)
266
267
268
269
270
271
272
273
274
275
          end

          it 'migrates project to a hashed storage instead of renaming the repo to another legacy name' do
            result = update_project(project, admin, path: 'new-path')

            expect(result).not_to include(status: :error)
            expect(project).to be_valid
            expect(project.errors).to be_empty
            expect(project.reload.hashed_storage?(:repository)).to be_truthy
          end
276

277
          context 'when skip_hashed_storage_upgrade feature flag is enabled' do
278
            before do
279
              stub_feature_flags(skip_hashed_storage_upgrade: true)
280
281
282
283
284
285
286
287
288
289
290
291
            end

            it 'renames the project without upgrading it' do
              result = update_project(project, admin, path: 'new-path')

              expect(result).not_to include(status: :error)
              expect(project).to be_valid
              expect(project.errors).to be_empty
              expect(project.disk_path).to include('new-path')
              expect(project.reload.hashed_storage?(:repository)).to be_falsey
            end
          end
292
        end
293
294
      end

295
296
      context 'with hashed storage' do
        let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
297

298
299
300
301
302
303
304
305
306
307
308
        before do
          stub_application_setting(hashed_storage_enabled: true)
        end

        it 'does not check if new path matches existing repository on disk' do
          expect(project).not_to receive(:repository_with_same_path_already_exists?)

          result = update_project(project, admin, path: 'existing')

          expect(result).to include(status: :success)
        end
309
      end
310
311
    end

312
313
314
    context 'when passing invalid parameters' do
      it 'returns an error result when record cannot be updated' do
        result = update_project(project, admin, { name: 'foo&bar' })
315

316
317
318
319
        expect(result).to eq({
          status: :error,
          message: "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
        })
320
      end
321
    end
Rob Watson's avatar
Rob Watson committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

    context 'when updating #pages_https_only', :https_pages_enabled do
      subject(:call_service) do
        update_project(project, admin, pages_https_only: false)
      end

      it 'updates the attribute' do
        expect { call_service }
          .to change { project.pages_https_only? }
          .to(false)
      end

      it 'calls Projects::UpdatePagesConfigurationService' do
        expect(Projects::UpdatePagesConfigurationService)
          .to receive(:new)
          .with(project)
          .and_call_original

        call_service
      end
    end
343
  end
344

345
  describe '#run_auto_devops_pipeline?' do
346
    subject { described_class.new(project, user).run_auto_devops_pipeline? }
347

348
349
350
351
    context 'when master contains a .gitlab-ci.yml file' do
      before do
        allow(project.repository).to receive(:gitlab_ci_yml).and_return("script: ['test']")
      end
352

353
354
355
      it { is_expected.to eq(false) }
    end

356
357
358
359
    context 'when auto devops is nil' do
      it { is_expected.to eq(false) }
    end

360
361
362
363
    context 'when auto devops is explicitly enabled' do
      before do
        project.create_auto_devops!(enabled: true)
      end
364
365

      it { is_expected.to eq(true) }
366
367
    end

368
369
370
371
    context 'when auto devops is explicitly disabled' do
      before do
        project.create_auto_devops!(enabled: false)
      end
372

373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
      it { is_expected.to eq(false) }
    end

    context 'when auto devops is set to instance setting' do
      before do
        project.create_auto_devops!(enabled: nil)
        allow(project.auto_devops).to receive(:previous_changes).and_return('enabled' => true)
      end

      context 'when auto devops is enabled system-wide' do
        before do
          stub_application_setting(auto_devops_enabled: true)
        end

        it { is_expected.to eq(true) }
      end

      context 'when auto devops is disabled system-wide' do
        before do
          stub_application_setting(auto_devops_enabled: false)
        end

        it { is_expected.to eq(false) }
      end
397
    end
398
399
  end

400
  def update_project(project, user, opts)
401
    described_class.new(project, user, opts).execute
402
  end
403
end