issues_controller_spec.rb 10.8 KB
Newer Older
1 2 3
require('spec_helper')

describe Projects::IssuesController do
4
  let(:project) { create(:project_empty_repo) }
5 6
  let(:user)    { create(:user) }
  let(:issue)   { create(:issue, project: project) }
7

8
  describe "GET #index" do
9 10 11 12 13
    context 'external issue tracker' do
      it 'redirects to the external issue tracker' do
        external = double(issues_url: 'https://example.com/issues')
        allow(project).to receive(:external_issue_tracker).and_return(external)
        controller.instance_variable_set(:@project, project)
14

15
        get :index, namespace_id: project.namespace.path, project_id: project
16

17 18
        expect(response).to redirect_to('https://example.com/issues')
      end
19 20
    end

21 22 23 24 25
    context 'internal issue tracker' do
      before do
        sign_in(user)
        project.team << [user, :developer]
      end
26

27 28
      it "returns index" do
        get :index, namespace_id: project.namespace.path, project_id: project.path
29

30 31
        expect(response).to have_http_status(200)
      end
32

33
      it "returns 301 if request path doesn't match project path" do
34 35 36 37
        get :index, namespace_id: project.namespace.path, project_id: project.path.upcase

        expect(response).to redirect_to(namespace_project_issues_path(project.namespace, project))
      end
38

39 40 41
      it "returns 404 when issues are disabled" do
        project.issues_enabled = false
        project.save
42

43 44 45 46 47 48 49 50 51 52
        get :index, namespace_id: project.namespace.path, project_id: project.path
        expect(response).to have_http_status(404)
      end

      it "returns 404 when external issue tracker is enabled" do
        controller.instance_variable_set(:@project, project)
        allow(project).to receive(:default_issues_tracker?).and_return(false)

        get :index, namespace_id: project.namespace.path, project_id: project.path
        expect(response).to have_http_status(404)
53 54 55 56 57 58 59 60 61 62 63 64 65 66
      end
    end
  end

  describe 'GET #new' do
    context 'external issue tracker' do
      it 'redirects to the external issue tracker' do
        external = double(new_issue_path: 'https://example.com/issues/new')
        allow(project).to receive(:external_issue_tracker).and_return(external)
        controller.instance_variable_set(:@project, project)

        get :new, namespace_id: project.namespace.path, project_id: project

        expect(response).to redirect_to('https://example.com/issues/new')
67
      end
68
    end
69 70
  end

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
  describe 'PUT #update' do
    context 'when moving issue to another private project' do
      let(:another_project) { create(:project, :private) }

      before do
        sign_in(user)
        project.team << [user, :developer]
      end

      context 'when user has access to move issue' do
        before { another_project.team << [user, :reporter] }

        it 'moves issue to another project' do
          move_issue

          expect(response).to have_http_status :found
87
          expect(another_project.issues).not_to be_empty
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
        end
      end

      context 'when user does not have access to move issue' do
        it 'responds with 404' do
          move_issue

          expect(response).to have_http_status :not_found
        end
      end

      def move_issue
        put :update,
          namespace_id: project.namespace.to_param,
          project_id: project.to_param,
          id: issue.iid,
          issue: { title: 'New title' },
          move_to_project_id: another_project.id
      end
    end
  end

110
  describe 'Confidential Issues' do
111
    let(:project) { create(:project_empty_repo, :public) }
112 113 114 115 116 117 118 119 120 121
    let(:assignee) { create(:assignee) }
    let(:author) { create(:user) }
    let(:non_member) { create(:user) }
    let(:member) { create(:user) }
    let(:admin) { create(:admin) }
    let!(:issue) { create(:issue, project: project) }
    let!(:unescaped_parameter_value) { create(:issue, :confidential, project: project, author: author) }
    let!(:request_forgery_timing_attack) { create(:issue, :confidential, project: project, assignee: assignee) }

    describe 'GET #index' do
122
      it 'does not list confidential issues for guests' do
123 124 125 126 127 128
        sign_out(:user)
        get_issues

        expect(assigns(:issues)).to eq [issue]
      end

129
      it 'does not list confidential issues for non project members' do
130 131 132 133 134 135
        sign_in(non_member)
        get_issues

        expect(assigns(:issues)).to eq [issue]
      end

136
      it 'does not list confidential issues for project members with guest role' do
137 138 139 140 141 142 143 144
        sign_in(member)
        project.team << [member, :guest]

        get_issues

        expect(assigns(:issues)).to eq [issue]
      end

145
      it 'lists confidential issues for author' do
146 147 148 149 150 151 152
        sign_in(author)
        get_issues

        expect(assigns(:issues)).to include unescaped_parameter_value
        expect(assigns(:issues)).not_to include request_forgery_timing_attack
      end

153
      it 'lists confidential issues for assignee' do
154 155 156 157 158 159 160
        sign_in(assignee)
        get_issues

        expect(assigns(:issues)).not_to include unescaped_parameter_value
        expect(assigns(:issues)).to include request_forgery_timing_attack
      end

161
      it 'lists confidential issues for project members' do
162 163 164 165 166 167 168 169 170
        sign_in(member)
        project.team << [member, :developer]

        get_issues

        expect(assigns(:issues)).to include unescaped_parameter_value
        expect(assigns(:issues)).to include request_forgery_timing_attack
      end

171
      it 'lists confidential issues for admin' do
172 173 174 175 176 177 178 179 180 181 182 183 184
        sign_in(admin)
        get_issues

        expect(assigns(:issues)).to include unescaped_parameter_value
        expect(assigns(:issues)).to include request_forgery_timing_attack
      end

      def get_issues
        get :index,
          namespace_id: project.namespace.to_param,
          project_id: project.to_param
      end
    end
185

186 187
    shared_examples_for 'restricted action' do |http_status|
      it 'returns 404 for guests' do
188
        sign_out(:user)
189 190 191 192 193 194 195 196 197
        go(id: unescaped_parameter_value.to_param)

        expect(response).to have_http_status :not_found
      end

      it 'returns 404 for non project members' do
        sign_in(non_member)
        go(id: unescaped_parameter_value.to_param)

198 199 200 201 202 203 204 205
        expect(response).to have_http_status :not_found
      end

      it 'returns 404 for project members with guest role' do
        sign_in(member)
        project.team << [member, :guest]
        go(id: unescaped_parameter_value.to_param)

206 207 208 209 210 211 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 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
        expect(response).to have_http_status :not_found
      end

      it "returns #{http_status[:success]} for author" do
        sign_in(author)
        go(id: unescaped_parameter_value.to_param)

        expect(response).to have_http_status http_status[:success]
      end

      it "returns #{http_status[:success]} for assignee" do
        sign_in(assignee)
        go(id: request_forgery_timing_attack.to_param)

        expect(response).to have_http_status http_status[:success]
      end

      it "returns #{http_status[:success]} for project members" do
        sign_in(member)
        project.team << [member, :developer]
        go(id: unescaped_parameter_value.to_param)

        expect(response).to have_http_status http_status[:success]
      end

      it "returns #{http_status[:success]} for admin" do
        sign_in(admin)
        go(id: unescaped_parameter_value.to_param)

        expect(response).to have_http_status http_status[:success]
      end
    end

    describe 'GET #show' do
      it_behaves_like 'restricted action', success: 200

      def go(id:)
        get :show,
          namespace_id: project.namespace.to_param,
          project_id: project.to_param,
          id: id
      end
    end

    describe 'GET #edit' do
      it_behaves_like 'restricted action', success: 200

      def go(id:)
        get :edit,
          namespace_id: project.namespace.to_param,
          project_id: project.to_param,
          id: id
      end
    end

    describe 'PUT #update' do
      it_behaves_like 'restricted action', success: 302

      def go(id:)
        put :update,
          namespace_id: project.namespace.to_param,
          project_id: project.to_param,
          id: id,
          issue: { title: 'New title' }
      end
    end
272
  end
273

274 275 276
  describe 'POST #create' do
    context 'Akismet is enabled' do
      before do
277
        allow_any_instance_of(Spammable).to receive(:check_for_spam?).and_return(true)
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
        allow_any_instance_of(Gitlab::AkismetHelper).to receive(:is_spam?).and_return(true)
      end

      def post_spam_issue
        sign_in(user)
        spam_project = create(:empty_project, :public)
        post :create, {
          namespace_id: spam_project.namespace.to_param,
          project_id: spam_project.to_param,
          issue: { title: 'Spam Title', description: 'Spam lives here' }
        }
      end

      it 'rejects an issue recognized as spam' do
        expect{ post_spam_issue }.not_to change(Issue, :count)
        expect(response).to render_template(:new)
      end

      it 'creates a spam log' do
        post_spam_issue
        spam_logs = SpamLog.all
        expect(spam_logs.count).to eq(1)
        expect(spam_logs[0].title).to eq('Spam Title')
      end
    end
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322

    context 'user agent details are saved' do
      before do
        request.env['action_dispatch.remote_ip'] = '127.0.0.1'
      end

      def post_new_issue
        sign_in(user)
        project = create(:empty_project, :public)
        post :create, {
          namespace_id: project.namespace.to_param,
          project_id: project.to_param,
          issue: { title: 'Title', description: 'Description' }
        }
      end

      it 'creates a user agent detail' do
        expect{ post_new_issue }.to change(UserAgentDetail, :count)
      end
    end
323 324
  end

325
  describe "DELETE #destroy" do
326 327 328 329
    context "when the user is a developer" do
      before { sign_in(user) }
      it "rejects a developer to destroy an issue" do
        delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
330
        expect(response).to have_http_status(404)
331
      end
332 333
    end

334 335 336 337 338
    context "when the user is owner" do
      let(:owner)     { create(:user) }
      let(:namespace) { create(:namespace, owner: owner) }
      let(:project)   { create(:project, namespace: namespace) }

339
      before { sign_in(owner) }
340

341
      it "deletes the issue" do
342 343
        delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid

344
        expect(response).to have_http_status(302)
345
        expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now
346 347 348
      end
    end
  end
349 350 351 352 353 354 355

  describe 'POST #toggle_award_emoji' do
    before do
      sign_in(user)
      project.team << [user, :developer]
    end

356
    it "toggles the award emoji" do
Z.J. van de Weg's avatar
Z.J. van de Weg committed
357
      expect do
358
        post(:toggle_award_emoji, namespace_id: project.namespace.path,
Z.J. van de Weg's avatar
Z.J. van de Weg committed
359 360
                                  project_id: project.path, id: issue.iid, name: "thumbsup")
      end.to change { issue.award_emoji.count }.by(1)
361

362
      expect(response).to have_http_status(200)
363 364
    end
  end
365
end