snippets_controller_spec.rb 20 KB
Newer Older
1
2
# frozen_string_literal: true

3
4
5
require 'spec_helper'

describe SnippetsController do
6
  let(:user) { create(:user) }
7

8
9
10
11
  describe 'GET #index' do
    let(:user) { create(:user) }

    context 'when username parameter is present' do
12
13
14
15
16
17
18
19
20
      it_behaves_like 'paginated collection' do
        let(:collection) { Snippet.all }
        let(:params) { { username: user.username } }

        before do
          create(:personal_snippet, :public, author: user)
        end
      end

21
      it 'renders snippets of a user when username is present' do
blackst0ne's avatar
blackst0ne committed
22
        get :index, params: { username: user.username }
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

        expect(response).to render_template(:index)
      end
    end

    context 'when username parameter is not present' do
      it 'redirects to explore snippets page when user is not logged in' do
        get :index

        expect(response).to redirect_to(explore_snippets_path)
      end

      it 'redirects to snippets dashboard page when user is logged in' do
        sign_in(user)

        get :index

        expect(response).to redirect_to(dashboard_snippets_path)
      end
    end
  end

45
46
47
48
49
50
51
52
53
  describe 'GET #new' do
    context 'when signed in' do
      before do
        sign_in(user)
      end

      it 'responds with status 200' do
        get :new

54
        expect(response).to have_gitlab_http_status(200)
55
      end
56
57
58
59
60
61
62
63
64
65

      context 'when user is not allowed to create a personal snippet' do
        let(:user) { create(:user, :external) }

        it 'responds with status 404' do
          get :new

          expect(response).to have_gitlab_http_status(404)
        end
      end
66
67
68
69
70
71
72
73
74
75
76
    end

    context 'when not signed in' do
      it 'redirects to the sign in page' do
        get :new

        expect(response).to redirect_to(new_user_session_path)
      end
    end
  end

77
  describe 'GET #show' do
78
79
80
81
82
83
84
85
86
87
88
89
90
    context 'when the personal snippet is private' do
      let(:personal_snippet) { create(:personal_snippet, :private, author: user) }

      context 'when signed in' do
        before do
          sign_in(user)
        end

        context 'when signed in user is not the author' do
          let(:other_author) { create(:author) }
          let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) }

          it 'responds with status 404' do
blackst0ne's avatar
blackst0ne committed
91
            get :show, params: { id: other_personal_snippet.to_param }
92

93
            expect(response).to have_gitlab_http_status(404)
94
95
96
97
98
          end
        end

        context 'when signed in user is the author' do
          it 'renders the snippet' do
blackst0ne's avatar
blackst0ne committed
99
            get :show, params: { id: personal_snippet.to_param }
100
101

            expect(assigns(:snippet)).to eq(personal_snippet)
102
            expect(response).to have_gitlab_http_status(200)
103
          end
104
105

          it 'responds with status 404 when embeddable content is requested' do
106
            get :show, params: { id: personal_snippet.to_param }, format: :js
107
108
109

            expect(response).to have_gitlab_http_status(404)
          end
110
111
112
113
114
        end
      end

      context 'when not signed in' do
        it 'redirects to the sign in page' do
blackst0ne's avatar
blackst0ne committed
115
          get :show, params: { id: personal_snippet.to_param }
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

          expect(response).to redirect_to(new_user_session_path)
        end
      end
    end

    context 'when the personal snippet is internal' do
      let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }

      context 'when signed in' do
        before do
          sign_in(user)
        end

        it 'renders the snippet' do
blackst0ne's avatar
blackst0ne committed
131
          get :show, params: { id: personal_snippet.to_param }
132
133

          expect(assigns(:snippet)).to eq(personal_snippet)
134
          expect(response).to have_gitlab_http_status(200)
135
        end
136
137

        it 'responds with status 404 when embeddable content is requested' do
138
          get :show, params: { id: personal_snippet.to_param }, format: :js
139
140
141

          expect(response).to have_gitlab_http_status(404)
        end
142
143
144
145
      end

      context 'when not signed in' do
        it 'redirects to the sign in page' do
blackst0ne's avatar
blackst0ne committed
146
          get :show, params: { id: personal_snippet.to_param }
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

          expect(response).to redirect_to(new_user_session_path)
        end
      end
    end

    context 'when the personal snippet is public' do
      let(:personal_snippet) { create(:personal_snippet, :public, author: user) }

      context 'when signed in' do
        before do
          sign_in(user)
        end

        it 'renders the snippet' do
blackst0ne's avatar
blackst0ne committed
162
          get :show, params: { id: personal_snippet.to_param }
163
164

          expect(assigns(:snippet)).to eq(personal_snippet)
165
          expect(response).to have_gitlab_http_status(200)
166
        end
167
168

        it 'responds with status 200 when embeddable content is requested' do
169
          get :show, params: { id: personal_snippet.to_param }, format: :js
170
171
172
173

          expect(assigns(:snippet)).to eq(personal_snippet)
          expect(response).to have_gitlab_http_status(200)
        end
174
175
176
177
      end

      context 'when not signed in' do
        it 'renders the snippet' do
blackst0ne's avatar
blackst0ne committed
178
          get :show, params: { id: personal_snippet.to_param }
179
180

          expect(assigns(:snippet)).to eq(personal_snippet)
181
          expect(response).to have_gitlab_http_status(200)
182
183
184
185
186
187
188
189
190
191
192
        end
      end
    end

    context 'when the personal snippet does not exist' do
      context 'when signed in' do
        before do
          sign_in(user)
        end

        it 'responds with status 404' do
blackst0ne's avatar
blackst0ne committed
193
          get :show, params: { id: 'doesntexist' }
194

195
          expect(response).to have_gitlab_http_status(404)
196
197
198
199
200
        end
      end

      context 'when not signed in' do
        it 'responds with status 404' do
blackst0ne's avatar
blackst0ne committed
201
          get :show, params: { id: 'doesntexist' }
202

203
          expect(response).to redirect_to(new_user_session_path)
204
205
206
207
        end
      end
    end
  end
208

Sean McGivern's avatar
Sean McGivern committed
209
  describe 'POST #create' do
210
    def create_snippet(snippet_params = {}, additional_params = {})
Sean McGivern's avatar
Sean McGivern committed
211
212
      sign_in(user)

blackst0ne's avatar
blackst0ne committed
213
      post :create, params: {
214
        personal_snippet: { title: 'Title', content: 'Content', description: 'Description' }.merge(snippet_params)
215
216
217
      }.merge(additional_params)

      Snippet.last
Sean McGivern's avatar
Sean McGivern committed
218
219
    end

220
221
222
223
224
225
226
227
    it 'creates the snippet correctly' do
      snippet = create_snippet(visibility_level: Snippet::PRIVATE)

      expect(snippet.title).to eq('Title')
      expect(snippet.content).to eq('Content')
      expect(snippet.description).to eq('Description')
    end

228
229
230
231
232
233
234
235
236
237
238
239
240
241
    context 'when user is not allowed to create a personal snippet' do
      let(:user) { create(:user, :external) }

      it 'responds with status 404' do
        aggregate_failures do
          expect do
            create_snippet(visibility_level: Snippet::PUBLIC)
          end.not_to change { Snippet.count }

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

242
    context 'when the snippet description contains a file' do
243
244
      include FileMoverHelpers

245
246
247
248
      let(:picture_secret) { SecureRandom.hex }
      let(:text_secret) { SecureRandom.hex }
      let(:picture_file) { "/-/system/user/#{user.id}/#{picture_secret}/picture.jpg" }
      let(:text_file) { "/-/system/user/#{user.id}/#{text_secret}/text.txt" }
249
250
251
252
253
254
255
256
      let(:description) do
        "Description with picture: ![picture](/uploads#{picture_file}) and "\
        "text: [text.txt](/uploads#{text_file})"
      end

      before do
        allow(FileUtils).to receive(:mkdir_p)
        allow(FileUtils).to receive(:move)
257
258
        stub_file_mover(text_file)
        stub_file_mover(picture_file)
259
260
261
262
263
264
265
266
267
268
269
270
      end

      subject { create_snippet({ description: description }, { files: [picture_file, text_file] }) }

      it 'creates the snippet' do
        expect { subject }.to change { Snippet.count }.by(1)
      end

      it 'stores the snippet description correctly' do
        snippet = subject

        expected_description = "Description with picture: "\
271
272
          "![picture](/uploads/-/system/personal_snippet/#{snippet.id}/#{picture_secret}/picture.jpg) and "\
          "text: [text.txt](/uploads/-/system/personal_snippet/#{snippet.id}/#{text_secret}/text.txt)"
273
274
275
276
277

        expect(snippet.description).to eq(expected_description)
      end
    end

Sean McGivern's avatar
Sean McGivern committed
278
279
    context 'when the snippet is spam' do
      before do
280
281
282
        allow_next_instance_of(AkismetService) do |instance|
          allow(instance).to receive(:spam?).and_return(true)
        end
Sean McGivern's avatar
Sean McGivern committed
283
284
285
286
      end

      context 'when the snippet is private' do
        it 'creates the snippet' do
287
288
          expect { create_snippet(visibility_level: Snippet::PRIVATE) }
            .to change { Snippet.count }.by(1)
Sean McGivern's avatar
Sean McGivern committed
289
290
291
292
293
        end
      end

      context 'when the snippet is public' do
        it 'rejects the shippet' do
294
295
          expect { create_snippet(visibility_level: Snippet::PUBLIC) }
            .not_to change { Snippet.count }
Sean McGivern's avatar
Sean McGivern committed
296
297
298
        end

        it 'creates a spam log' do
299
          expect { create_snippet(visibility_level: Snippet::PUBLIC) }
300
            .to log_spam(title: 'Title', user: user, noteable_type: 'PersonalSnippet')
Sean McGivern's avatar
Sean McGivern committed
301
        end
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

        it 'renders :new with recaptcha disabled' do
          stub_application_setting(recaptcha_enabled: false)

          create_snippet(visibility_level: Snippet::PUBLIC)

          expect(response).to render_template(:new)
        end

        context 'recaptcha enabled' do
          before do
            stub_application_setting(recaptcha_enabled: true)
          end

          it 'renders :verify with recaptcha enabled' do
            create_snippet(visibility_level: Snippet::PUBLIC)

            expect(response).to render_template(:verify)
          end

          it 'renders snippet page when recaptcha verified' do
            spammy_title = 'Whatever'

            spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title)
            snippet = create_snippet({ title: spammy_title },
                                     { spam_log_id: spam_logs.last.id,
                                       recaptcha_verification: true })

            expect(response).to redirect_to(snippet_path(snippet))
          end
        end
      end
    end
  end

  describe 'PUT #update' do
338
    let(:project) { create :project }
339
340
341
342
343
    let(:snippet) { create :personal_snippet, author: user, project: project, visibility_level: visibility_level }

    def update_snippet(snippet_params = {}, additional_params = {})
      sign_in(user)

blackst0ne's avatar
blackst0ne committed
344
      put :update, params: {
345
346
347
348
349
350
351
352
353
        id: snippet.id,
        personal_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params)
      }.merge(additional_params)

      snippet.reload
    end

    context 'when the snippet is spam' do
      before do
354
355
356
        allow_next_instance_of(AkismetService) do |instance|
          allow(instance).to receive(:spam?).and_return(true)
        end
357
358
359
360
361
362
      end

      context 'when the snippet is private' do
        let(:visibility_level) { Snippet::PRIVATE }

        it 'updates the snippet' do
363
364
          expect { update_snippet(title: 'Foo') }
            .to change { snippet.reload.title }.to('Foo')
365
366
367
368
369
370
371
        end
      end

      context 'when a private snippet is made public' do
        let(:visibility_level) { Snippet::PRIVATE }

        it 'rejects the snippet' do
372
373
          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
            .not_to change { snippet.reload.title }
374
375
376
        end

        it 'creates a spam log' do
377
          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
378
            .to log_spam(title: 'Foo', user: user, noteable_type: 'PersonalSnippet')
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
        end

        it 'renders :edit with recaptcha disabled' do
          stub_application_setting(recaptcha_enabled: false)

          update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC)

          expect(response).to render_template(:edit)
        end

        context 'recaptcha enabled' do
          before do
            stub_application_setting(recaptcha_enabled: true)
          end

          it 'renders :verify with recaptcha enabled' do
            update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC)

            expect(response).to render_template(:verify)
          end

          it 'renders snippet page when recaptcha verified' do
            spammy_title = 'Whatever'

            spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title)
            snippet = update_snippet({ title: spammy_title, visibility_level: Snippet::PUBLIC },
                                     { spam_log_id: spam_logs.last.id,
                                       recaptcha_verification: true })

408
            expect(response).to redirect_to(snippet_path(snippet))
409
410
411
412
413
414
415
416
          end
        end
      end

      context 'when the snippet is public' do
        let(:visibility_level) { Snippet::PUBLIC }

        it 'rejects the shippet' do
417
418
          expect { update_snippet(title: 'Foo') }
            .not_to change { snippet.reload.title }
419
420
421
        end

        it 'creates a spam log' do
422
423
          expect {update_snippet(title: 'Foo') }
            .to log_spam(title: 'Foo', user: user, noteable_type: 'PersonalSnippet')
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
        end

        it 'renders :edit with recaptcha disabled' do
          stub_application_setting(recaptcha_enabled: false)

          update_snippet(title: 'Foo')

          expect(response).to render_template(:edit)
        end

        context 'recaptcha enabled' do
          before do
            stub_application_setting(recaptcha_enabled: true)
          end

          it 'renders :verify with recaptcha enabled' do
            update_snippet(title: 'Foo')

            expect(response).to render_template(:verify)
          end

          it 'renders snippet page when recaptcha verified' do
            spammy_title = 'Whatever'

            spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title)
            snippet = update_snippet({ title: spammy_title },
                                     { spam_log_id: spam_logs.last.id,
                                       recaptcha_verification: true })

            expect(response).to redirect_to(snippet_path(snippet))
          end
        end
Sean McGivern's avatar
Sean McGivern committed
456
457
458
459
460
461
462
463
      end
    end
  end

  describe 'POST #mark_as_spam' do
    let(:snippet) { create(:personal_snippet, :public, author: user) }

    before do
464
465
466
      allow_next_instance_of(AkismetService) do |instance|
        allow(instance).to receive_messages(submit_spam: true)
      end
Sean McGivern's avatar
Sean McGivern committed
467
468
469
470
471
472
473
474
      stub_application_setting(akismet_enabled: true)
    end

    def mark_as_spam
      admin = create(:admin)
      create(:user_agent_detail, subject: snippet)
      sign_in(admin)

blackst0ne's avatar
blackst0ne committed
475
      post :mark_as_spam, params: { id: snippet.id }
Sean McGivern's avatar
Sean McGivern committed
476
477
478
479
480
481
482
483
484
    end

    it 'updates the snippet' do
      mark_as_spam

      expect(snippet.reload).not_to be_submittable_as_spam
    end
  end

485
486
487
  describe "GET #raw" do
    context 'when the personal snippet is private' do
      let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
488

489
490
491
492
      context 'when signed in' do
        before do
          sign_in(user)
        end
493

494
495
496
        context 'when signed in user is not the author' do
          let(:other_author) { create(:author) }
          let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) }
497

498
          it 'responds with status 404' do
blackst0ne's avatar
blackst0ne committed
499
            get :raw, params: { id: other_personal_snippet.to_param }
500

501
            expect(response).to have_gitlab_http_status(404)
502
          end
503
        end
504

505
        context 'when signed in user is the author' do
506
          before do
blackst0ne's avatar
blackst0ne committed
507
            get :raw, params: { id: personal_snippet.to_param }
508
          end
509

510
511
          it 'responds with status 200' do
            expect(assigns(:snippet)).to eq(personal_snippet)
512
            expect(response).to have_gitlab_http_status(200)
513
          end
514

515
516
          it 'has expected headers' do
            expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
517

518
            expect(response.header['Content-Disposition']).to match(/inline/)
519
          end
520

521
522
523
          it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
            expect(response).to have_gitlab_http_status(200)
            expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
524
          end
525
        end
526
      end
527

528
529
      context 'when not signed in' do
        it 'redirects to the sign in page' do
blackst0ne's avatar
blackst0ne committed
530
          get :raw, params: { id: personal_snippet.to_param }
531

532
          expect(response).to redirect_to(new_user_session_path)
533
534
        end
      end
535
    end
536

537
538
    context 'when the personal snippet is internal' do
      let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }
539

540
541
542
543
      context 'when signed in' do
        before do
          sign_in(user)
        end
544

545
        it 'responds with status 200' do
blackst0ne's avatar
blackst0ne committed
546
          get :raw, params: { id: personal_snippet.to_param }
547

548
          expect(assigns(:snippet)).to eq(personal_snippet)
549
          expect(response).to have_gitlab_http_status(200)
550
        end
551
      end
552

553
554
      context 'when not signed in' do
        it 'redirects to the sign in page' do
blackst0ne's avatar
blackst0ne committed
555
          get :raw, params: { id: personal_snippet.to_param }
556

557
          expect(response).to redirect_to(new_user_session_path)
558
559
        end
      end
560
    end
561

562
563
    context 'when the personal snippet is public' do
      let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
564

565
566
567
568
      context 'when signed in' do
        before do
          sign_in(user)
        end
569

570
        it 'responds with status 200' do
blackst0ne's avatar
blackst0ne committed
571
          get :raw, params: { id: personal_snippet.to_param }
572

573
          expect(assigns(:snippet)).to eq(personal_snippet)
574
          expect(response).to have_gitlab_http_status(200)
575
        end
576

577
578
579
580
        context 'CRLF line ending' do
          let(:personal_snippet) do
            create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line")
          end
581

582
          it 'returns LF line endings by default' do
blackst0ne's avatar
blackst0ne committed
583
            get :raw, params: { id: personal_snippet.to_param }
584

585
586
            expect(response.body).to eq("first line\nsecond line\nthird line")
          end
587

588
          it 'does not convert line endings when parameter present' do
blackst0ne's avatar
blackst0ne committed
589
            get :raw, params: { id: personal_snippet.to_param, line_ending: :raw }
590

591
            expect(response.body).to eq("first line\r\nsecond line\r\nthird line")
592
          end
593
        end
594
      end
595

596
597
      context 'when not signed in' do
        it 'responds with status 200' do
blackst0ne's avatar
blackst0ne committed
598
          get :raw, params: { id: personal_snippet.to_param }
599

600
          expect(assigns(:snippet)).to eq(personal_snippet)
601
          expect(response).to have_gitlab_http_status(200)
602
603
        end
      end
604
    end
605

606
607
608
609
610
    context 'when the personal snippet does not exist' do
      context 'when signed in' do
        before do
          sign_in(user)
        end
611

612
        it 'responds with status 404' do
blackst0ne's avatar
blackst0ne committed
613
          get :raw, params: { id: 'doesntexist' }
614

615
          expect(response).to have_gitlab_http_status(404)
616
        end
617
      end
618

619
      context 'when not signed in' do
620
        it 'redirects to the sign in path' do
blackst0ne's avatar
blackst0ne committed
621
          get :raw, params: { id: 'doesntexist' }
622

623
          expect(response).to redirect_to(new_user_session_path)
624
625
626
627
        end
      end
    end
  end
628
629

  context 'award emoji on snippets' do
630
631
    let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
    let(:another_user) { create(:user) }
632
633

    before do
634
      sign_in(another_user)
635
636
637
638
639
    end

    describe 'POST #toggle_award_emoji' do
      it "toggles the award emoji" do
        expect do
blackst0ne's avatar
blackst0ne committed
640
          post(:toggle_award_emoji, params: { id: personal_snippet.to_param, name: "thumbsup" })
641
        end.to change { personal_snippet.award_emoji.count }.from(0).to(1)
642
643
644
645
646

        expect(response.status).to eq(200)
      end

      it "removes the already awarded emoji" do
blackst0ne's avatar
blackst0ne committed
647
        post(:toggle_award_emoji, params: { id: personal_snippet.to_param, name: "thumbsup" })
648

649
        expect do
blackst0ne's avatar
blackst0ne committed
650
          post(:toggle_award_emoji, params: { id: personal_snippet.to_param, name: "thumbsup" })
651
        end.to change { personal_snippet.award_emoji.count }.from(1).to(0)
652
653
654
655
656

        expect(response.status).to eq(200)
      end
    end
  end
657
658
659
660
661
662
663

  describe 'POST #preview_markdown' do
    let(:snippet) { create(:personal_snippet, :public) }

    it 'renders json in a correct format' do
      sign_in(user)

blackst0ne's avatar
blackst0ne committed
664
      post :preview_markdown, params: { id: snippet, text: '*Markdown* text' }
665

666
      expect(json_response.keys).to match_array(%w(body references))
667
668
    end
  end
669
end