badges_spec.rb 13.3 KB
Newer Older
1 2
# frozen_string_literal: true

3 4 5
require 'spec_helper'

describe API::Badges do
6
  let(:maintainer) { create(:user, username: 'maintainer_user') }
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
  let(:developer) { create(:user) }
  let(:access_requester) { create(:user) }
  let(:stranger) { create(:user) }
  let(:project_group) { create(:group) }
  let(:project) { setup_project }
  let!(:group) { setup_group }

  shared_context 'source helpers' do
    def get_source(source_type)
      source_type == 'project' ? project : group
    end
  end

  shared_examples 'GET /:sources/:id/badges' do |source_type|
    include_context 'source helpers'

    let(:source) { get_source(source_type) }

    context "with :sources == #{source_type.pluralize}" do
      it_behaves_like 'a 404 response when source is private' do
        let(:route) { get api("/#{source_type.pluralize}/#{source.id}/badges", stranger) }
      end

30
      %i[maintainer developer access_requester stranger].each do |type|
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
        context "when authenticated as a #{type}" do
          it 'returns 200' do
            user = public_send(type)
            badges_count = source_type == 'project' ? 3 : 2

            get api("/#{source_type.pluralize}/#{source.id}/badges", user)

            expect(response).to have_gitlab_http_status(200)
            expect(response).to include_pagination_headers
            expect(json_response).to be_an Array
            expect(json_response.size).to eq(badges_count)
          end
        end
      end

      it 'avoids N+1 queries' do
        # Establish baseline
48
        get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
49 50

        control = ActiveRecord::QueryRecorder.new do
51
          get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
52 53 54 55 56
        end

        project.add_developer(create(:user))

        expect do
57
          get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
        end.not_to exceed_query_limit(control)
      end
    end
  end

  shared_examples 'GET /:sources/:id/badges/:badge_id' do |source_type|
    include_context 'source helpers'

    let(:source) { get_source(source_type) }

    context "with :sources == #{source_type.pluralize}" do
      it_behaves_like 'a 404 response when source is private' do
        let(:route) { get api("/#{source_type.pluralize}/#{source.id}/badges/#{developer.id}", stranger) }
      end

      context 'when authenticated as a non-member' do
74
        %i[maintainer developer access_requester stranger].each do |type|
75 76 77
          let(:badge) { source.badges.first }

          context "as a #{type}" do
78
            it 'returns 200', :quarantine do
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
              user = public_send(type)

              get api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", user)

              expect(response).to have_gitlab_http_status(200)
              expect(json_response['id']).to eq(badge.id)
              expect(json_response['link_url']).to eq(badge.link_url)
              expect(json_response['rendered_link_url']).to eq(badge.rendered_link_url)
              expect(json_response['image_url']).to eq(badge.image_url)
              expect(json_response['rendered_image_url']).to eq(badge.rendered_image_url)
              expect(json_response['kind']).to eq source_type
            end
          end
        end
      end
    end
  end

  shared_examples 'POST /:sources/:id/badges' do |source_type|
    include_context 'source helpers'

    let(:source) { get_source(source_type) }
    let(:example_url) { 'http://www.example.com' }
    let(:example_url2) { 'http://www.example1.com' }

    context "with :sources == #{source_type.pluralize}" do
      it_behaves_like 'a 404 response when source is private' do
        let(:route) do
          post api("/#{source_type.pluralize}/#{source.id}/badges", stranger),
blackst0ne's avatar
blackst0ne committed
108
               params: { link_url: example_url, image_url: example_url2 }
109 110 111 112 113 114 115 116 117 118
        end
      end

      context 'when authenticated as a non-member or member with insufficient rights' do
        %i[access_requester stranger developer].each do |type|
          context "as a #{type}" do
            it 'returns 403' do
              user = public_send(type)

              post api("/#{source_type.pluralize}/#{source.id}/badges", user),
blackst0ne's avatar
blackst0ne committed
119
                   params: { link_url: example_url, image_url: example_url2 }
120 121 122 123 124 125 126

              expect(response).to have_gitlab_http_status(403)
            end
          end
        end
      end

127
      context 'when authenticated as a maintainer/owner' do
128 129
        it 'creates a new badge' do
          expect do
130
            post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
blackst0ne's avatar
blackst0ne committed
131
                params: { link_url: example_url, image_url: example_url2 }
132 133 134 135 136 137 138 139 140 141 142

            expect(response).to have_gitlab_http_status(201)
          end.to change { source.badges.count }.by(1)

          expect(json_response['link_url']).to eq(example_url)
          expect(json_response['image_url']).to eq(example_url2)
          expect(json_response['kind']).to eq source_type
        end
      end

      it 'returns 400 when link_url is not given' do
143
        post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
blackst0ne's avatar
blackst0ne committed
144
             params: { link_url: example_url }
145 146 147 148 149

        expect(response).to have_gitlab_http_status(400)
      end

      it 'returns 400 when image_url is not given' do
150
        post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
blackst0ne's avatar
blackst0ne committed
151
             params: { image_url: example_url2 }
152 153 154 155 156

        expect(response).to have_gitlab_http_status(400)
      end

      it 'returns 400 when link_url or image_url is not valid' do
157
        post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
blackst0ne's avatar
blackst0ne committed
158
             params: { link_url: 'whatever', image_url: 'whatever' }
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

        expect(response).to have_gitlab_http_status(400)
      end
    end
  end

  shared_examples 'PUT /:sources/:id/badges/:badge_id' do |source_type|
    include_context 'source helpers'

    let(:source) { get_source(source_type) }

    context "with :sources == #{source_type.pluralize}" do
      let(:badge) { source.badges.first }
      let(:example_url) { 'http://www.example.com' }
      let(:example_url2) { 'http://www.example1.com' }

      it_behaves_like 'a 404 response when source is private' do
        let(:route) do
          put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", stranger),
blackst0ne's avatar
blackst0ne committed
178
              params: { link_url: example_url }
179 180 181 182 183 184 185 186 187 188
        end
      end

      context 'when authenticated as a non-member or member with insufficient rights' do
        %i[access_requester stranger developer].each do |type|
          context "as a #{type}" do
            it 'returns 403' do
              user = public_send(type)

              put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", user),
blackst0ne's avatar
blackst0ne committed
189
                  params: { link_url: example_url }
190 191 192 193 194 195 196

              expect(response).to have_gitlab_http_status(403)
            end
          end
        end
      end

197
      context 'when authenticated as a maintainer/owner' do
198
        it 'updates the member', :quarantine do
199
          put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer),
blackst0ne's avatar
blackst0ne committed
200
              params: { link_url: example_url, image_url: example_url2 }
201 202 203 204 205 206 207 208 209

          expect(response).to have_gitlab_http_status(200)
          expect(json_response['link_url']).to eq(example_url)
          expect(json_response['image_url']).to eq(example_url2)
          expect(json_response['kind']).to eq source_type
        end
      end

      it 'returns 400 when link_url or image_url is not valid' do
210
        put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer),
blackst0ne's avatar
blackst0ne committed
211
            params: { link_url: 'whatever', image_url: 'whatever' }
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243

        expect(response).to have_gitlab_http_status(400)
      end
    end
  end

  shared_examples 'DELETE /:sources/:id/badges/:badge_id' do |source_type|
    include_context 'source helpers'

    let(:source) { get_source(source_type) }

    context "with :sources == #{source_type.pluralize}" do
      let(:badge) { source.badges.first }

      it_behaves_like 'a 404 response when source is private' do
        let(:route) { delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", stranger) }
      end

      context 'when authenticated as a non-member or member with insufficient rights' do
        %i[access_requester developer stranger].each do |type|
          context "as a #{type}" do
            it 'returns 403' do
              user = public_send(type)

              delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", user)

              expect(response).to have_gitlab_http_status(403)
            end
          end
        end
      end

244
      context 'when authenticated as a maintainer/owner', :quarantine do
245 246
        it 'deletes the badge' do
          expect do
247
            delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer)
248 249 250 251 252 253

            expect(response).to have_gitlab_http_status(204)
          end.to change { source.badges.count }.by(-1)
        end

        it_behaves_like '412 response' do
254
          let(:request) { api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer) }
255 256 257 258
        end
      end

      it 'returns 404 if badge does not exist' do
259
        delete api("/#{source_type.pluralize}/#{source.id}/badges/123", maintainer)
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

        expect(response).to have_gitlab_http_status(404)
      end
    end
  end

  shared_examples 'GET /:sources/:id/badges/render' do |source_type|
    include_context 'source helpers'

    let(:source) { get_source(source_type) }
    let(:example_url) { 'http://www.example.com' }
    let(:example_url2) { 'http://www.example1.com' }

    context "with :sources == #{source_type.pluralize}" do
      it_behaves_like 'a 404 response when source is private' do
        let(:route) do
          get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", stranger)
        end
      end

      context 'when authenticated as a non-member or member with insufficient rights' do
        %i[access_requester stranger developer].each do |type|
          context "as a #{type}" do
            it 'returns 403' do
              user = public_send(type)

              get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", user)

              expect(response).to have_gitlab_http_status(403)
            end
          end
        end
      end

294
      context 'when authenticated as a maintainer/owner' do
295
        it 'gets the rendered badge values' do
296
          get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", maintainer)
297 298 299 300 301 302 303 304 305 306 307 308

          expect(response).to have_gitlab_http_status(200)

          expect(json_response.keys).to contain_exactly('link_url', 'rendered_link_url', 'image_url', 'rendered_image_url')
          expect(json_response['link_url']).to eq(example_url)
          expect(json_response['image_url']).to eq(example_url2)
          expect(json_response['rendered_link_url']).to eq(example_url)
          expect(json_response['rendered_image_url']).to eq(example_url2)
        end
      end

      it 'returns 400 when link_url is not given' do
309
        get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}", maintainer)
310 311 312 313 314

        expect(response).to have_gitlab_http_status(400)
      end

      it 'returns 400 when image_url is not given' do
315
        get api("/#{source_type.pluralize}/#{source.id}/badges/render?image_url=#{example_url}", maintainer)
316 317 318 319 320

        expect(response).to have_gitlab_http_status(400)
      end

      it 'returns 400 when link_url or image_url is not valid' do
321
        get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=whatever&image_url=whatever", maintainer)
322 323 324 325 326 327 328 329 330

        expect(response).to have_gitlab_http_status(400)
      end
    end
  end

  context 'when deleting a badge' do
    context 'and the source is a project' do
      it 'cannot delete badges owned by the project group' do
331
        delete api("/projects/#{project.id}/badges/#{project_group.badges.first.id}", maintainer)
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349

        expect(response).to have_gitlab_http_status(403)
      end
    end
  end

  describe 'Endpoints' do
    %w(project group).each do |source_type|
      it_behaves_like 'GET /:sources/:id/badges', source_type
      it_behaves_like 'GET /:sources/:id/badges/:badge_id', source_type
      it_behaves_like 'GET /:sources/:id/badges/render', source_type
      it_behaves_like 'POST /:sources/:id/badges', source_type
      it_behaves_like 'PUT /:sources/:id/badges/:badge_id', source_type
      it_behaves_like 'DELETE /:sources/:id/badges/:badge_id', source_type
    end
  end

  def setup_project
350
    create(:project, :public, creator_id: maintainer.id, namespace: project_group) do |project|
351
      project.add_developer(developer)
352
      project.add_maintainer(maintainer)
353 354 355 356 357 358 359 360
      project.request_access(access_requester)
      project.project_badges << build(:project_badge, project: project)
      project.project_badges << build(:project_badge, project: project)
      project_group.badges << build(:group_badge, group: group)
    end
  end

  def setup_group
361
    create(:group, :public) do |group|
362
      group.add_developer(developer)
363
      group.add_owner(maintainer)
364 365 366 367 368 369
      group.request_access(access_requester)
      group.badges << build(:group_badge, group: group)
      group.badges << build(:group_badge, group: group)
    end
  end
end