summaryrefslogtreecommitdiff
path: root/spec/services/projects/container_repository/delete_tags_service_spec.rb
blob: 5116427dad20816d3f19da742cc40f2afd8ce154 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Projects::ContainerRepository::DeleteTagsService do
  include_context 'container repository delete tags service shared context'

  let(:service) { described_class.new(project, user, params) }

  let_it_be(:available_service_classes) do
    [
      ::Projects::ContainerRepository::Gitlab::DeleteTagsService,
      ::Projects::ContainerRepository::ThirdParty::DeleteTagsService
    ]
  end

  RSpec.shared_examples 'logging a success response' do
    it 'logs an info message' do
      expect(service).to receive(:log_info).with(
        service_class: 'Projects::ContainerRepository::DeleteTagsService',
        message: 'deleted tags',
        container_repository_id: repository.id,
        deleted_tags_count: tags.size
      )

      subject
    end
  end

  RSpec.shared_examples 'logging an error response' do |message: 'could not delete tags'|
    it 'logs an error message' do
      expect(service).to receive(:log_error).with(
        service_class: 'Projects::ContainerRepository::DeleteTagsService',
        message: message,
        container_repository_id: repository.id
      )

      subject
    end
  end

  RSpec.shared_examples 'calling the correct delete tags service' do |expected_service_class|
    let(:service_response) { { status: :success, deleted: tags } }
    let(:excluded_service_class) { available_service_classes.excluding(expected_service_class).first }

    before do
      service_double = double
      expect(expected_service_class).to receive(:new).with(repository, tags).and_return(service_double)
      expect(excluded_service_class).not_to receive(:new)
      expect(service_double).to receive(:execute).and_return(service_response)
    end

    it { is_expected.to include(status: :success) }

    it_behaves_like 'logging a success response'

    context 'with an error service response' do
      let(:service_response) { { status: :error, message: 'could not delete tags' } }

      it { is_expected.to include(status: :error) }

      it_behaves_like 'logging an error response'
    end
  end

  RSpec.shared_examples 'handling invalid params' do
    context 'with invalid params' do
      before do
        expect(::Projects::ContainerRepository::Gitlab::DeleteTagsService).not_to receive(:new)
        expect(::Projects::ContainerRepository::ThirdParty::DeleteTagsService).not_to receive(:new)
        expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_name)
      end

      context 'when no params are specified' do
        let_it_be(:params) { {} }

        it { is_expected.to include(status: :error) }
      end

      context 'with empty tags' do
        let_it_be(:tags) { [] }

        it { is_expected.to include(status: :error) }
      end
    end
  end

  describe '#execute' do
    let(:tags) { %w[A Ba] }

    subject { service.execute(repository) }

    before do
      stub_feature_flags(container_registry_expiration_policies_throttling: false)
    end

    context 'without permissions' do
      it { is_expected.to include(status: :error) }
    end

    context 'with permissions' do
      before do
        project.add_developer(user)
      end

      context 'when the registry supports fast delete' do
        context 'and the feature is enabled' do
          before do
            allow(repository.client).to receive(:supports_tag_delete?).and_return(true)
          end

          it_behaves_like 'calling the correct delete tags service', ::Projects::ContainerRepository::Gitlab::DeleteTagsService

          it_behaves_like 'handling invalid params'

          context 'with the real service' do
            before do
              stub_delete_reference_requests(tags)
              expect_delete_tag_by_names(tags)
            end

            it { is_expected.to include(status: :success) }

            it_behaves_like 'logging a success response'
          end

          context 'with a timeout error' do
            before do
              expect_next_instance_of(::Projects::ContainerRepository::Gitlab::DeleteTagsService) do |delete_service|
                expect(delete_service).to receive(:delete_tags).and_raise(::Projects::ContainerRepository::Gitlab::DeleteTagsService::TimeoutError)
              end
            end

            it { is_expected.to include(status: :error, message: 'timeout while deleting tags') }

            it_behaves_like 'logging an error response', message: 'timeout while deleting tags'
          end
        end

        context 'and the feature is disabled' do
          before do
            stub_feature_flags(container_registry_fast_tag_delete: false)
          end

          it_behaves_like 'calling the correct delete tags service', ::Projects::ContainerRepository::ThirdParty::DeleteTagsService

          it_behaves_like 'handling invalid params'

          context 'with the real service' do
            before do
              stub_upload('sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3')
              tags.each { |tag| stub_put_manifest_request(tag) }
              expect_delete_tag_by_digest('sha256:dummy')
            end

            it { is_expected.to include(status: :success) }

            it_behaves_like 'logging a success response'
          end
        end
      end

      context 'when the registry does not support fast delete' do
        before do
          allow(repository.client).to receive(:supports_tag_delete?).and_return(false)
        end

        it_behaves_like 'calling the correct delete tags service', ::Projects::ContainerRepository::ThirdParty::DeleteTagsService

        it_behaves_like 'handling invalid params'
      end
    end
  end
end