file_mover_spec.rb 5.67 KB
Newer Older
1
2
# frozen_string_literal: true

3
4
5
require 'spec_helper'

describe FileMover do
6
7
  include FileMoverHelpers

Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
8
  let(:user) { create(:user) }
9
  let(:filename) { 'banana_sample.gif' }
10
  let(:secret) { SecureRandom.hex }
Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
11
  let(:temp_file_path) { File.join("uploads/-/system/user/#{user.id}", secret, filename) }
12

13
  let(:temp_description) do
14
15
    "test ![banana_sample](/#{temp_file_path}) "\
    "same ![banana_sample](/#{temp_file_path}) "
16
  end
Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
17
  let(:file_path) { File.join('uploads/-/system/personal_snippet', snippet.id.to_s, secret, filename) }
18
19
  let(:snippet) { create(:personal_snippet, description: temp_description) }

Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
20
21
22
23
24
  let(:tmp_uploader) do
    PersonalFileUploader.new(user, secret: secret)
  end

  let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }
25

Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
26
  subject { described_class.new(temp_file_path, from_model: user, to_model: snippet).execute }
27
28

  describe '#execute' do
29
30
    let(:tmp_upload) { tmp_uploader.upload }

31
    before do
Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
32
      tmp_uploader.store!(file)
33
    end
Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
34

35
36
37
38
39
40
    context 'local storage' do
      before do
        allow(FileUtils).to receive(:mkdir_p).with(a_string_including(File.dirname(file_path)))
        allow(FileUtils).to receive(:move).with(a_string_including(temp_file_path), a_string_including(file_path))
        allow_any_instance_of(CarrierWave::SanitizedFile).to receive(:exists?).and_return(true)
        allow_any_instance_of(CarrierWave::SanitizedFile).to receive(:size).and_return(10)
41

42
43
        stub_file_mover(temp_file_path)
      end
44

45
46
47
      context 'when move and field update successful' do
        it 'updates the description correctly' do
          subject
Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
48

49
          expect(snippet.reload.description)
50
51
            .to eq("test ![banana_sample](/uploads/-/system/personal_snippet/#{snippet.id}/#{secret}/banana_sample.gif) "\
                   "same ![banana_sample](/uploads/-/system/personal_snippet/#{snippet.id}/#{secret}/banana_sample.gif) ")
52
        end
53

54
55
56
57
58
        it 'updates existing upload record' do
          expect { subject }
            .to change { tmp_upload.reload.attributes.values_at('model_id', 'model_type') }
            .from([user.id, 'User']).to([snippet.id, 'Snippet'])
        end
59

60
61
62
63
64
        it 'schedules a background migration' do
          expect_any_instance_of(PersonalFileUploader).to receive(:schedule_background_upload).once

          subject
        end
65
      end
66

67
68
69
70
      context 'when update_markdown fails' do
        before do
          expect(FileUtils).to receive(:move).with(a_string_including(file_path), a_string_including(temp_file_path))
        end
71

72
73
74
75
76
77
        subject { described_class.new(file_path, :non_existing_field, from_model: user, to_model: snippet).execute }

        it 'does not update the description' do
          subject

          expect(snippet.reload.description)
78
79
            .to eq("test ![banana_sample](/uploads/-/system/user/#{user.id}/#{secret}/banana_sample.gif) "\
                   "same ![banana_sample](/uploads/-/system/user/#{user.id}/#{secret}/banana_sample.gif) ")
80
81
82
83
84
85
        end

        it 'does not change the upload record' do
          expect { subject }
            .not_to change { tmp_upload.reload.attributes.values_at('model_id', 'model_type') }
        end
86
      end
87
88
    end

89
    context 'when tmp uploader is not local storage' do
90
      before do
91
        stub_uploads_object_storage(uploader: PersonalFileUploader)
92
        allow_any_instance_of(PersonalFileUploader).to receive(:file_storage?) { false }
93
94
      end

95
96
97
      after do
        FileUtils.rm_f(File.join('personal_snippet', snippet.id.to_s, secret, filename))
      end
98

99
100
101
      context 'when move and field update successful' do
        it 'updates the description correctly' do
          subject
102

103
          expect(snippet.reload.description)
104
105
            .to eq("test ![banana_sample](/uploads/-/system/personal_snippet/#{snippet.id}/#{secret}/banana_sample.gif) "\
                   "same ![banana_sample](/uploads/-/system/personal_snippet/#{snippet.id}/#{secret}/banana_sample.gif) ")
106
107
108
109
110
111
112
113
114
        end

        it 'creates new target upload record an delete the old upload' do
          expect { subject }
            .to change { Upload.last.attributes.values_at('model_id', 'model_type') }
            .from([user.id, 'User']).to([snippet.id, 'Snippet'])

          expect(Upload.count).to eq(1)
        end
115
      end
116

117
118
119
120
121
122
123
      context 'when update_markdown fails' do
        subject { described_class.new(file_path, :non_existing_field, from_model: user, to_model: snippet).execute }

        it 'does not update the description' do
          subject

          expect(snippet.reload.description)
124
125
            .to eq("test ![banana_sample](/uploads/-/system/user/#{user.id}/#{secret}/banana_sample.gif) "\
                   "same ![banana_sample](/uploads/-/system/user/#{user.id}/#{secret}/banana_sample.gif) ")
126
127
128
129
130
131
        end

        it 'does not change the upload record' do
          expect { subject }
            .to change { Upload.last.attributes.values_at('model_id', 'model_type') }.from([user.id, 'User'])
        end
132
      end
133
134
    end
  end
135
136
137

  context 'security' do
    context 'when relative path is involved' do
Oswaldo Ferreir's avatar
Oswaldo Ferreir committed
138
      let(:temp_file_path) { File.join("uploads/-/system/user/#{user.id}", '..', 'another_subdir_of_temp') }
139
140
141

      it 'does not trigger move if path is outside designated directory' do
        expect(FileUtils).not_to receive(:move)
142
        expect { subject }.to raise_error(FileUploader::InvalidSecret)
143
144
145
146
147
148
149
150
151
152
153
154
155
156
      end
    end

    context 'when symlink is involved' do
      it 'does not trigger move if path is outside designated directory' do
        stub_file_mover(temp_file_path, stub_real_path: Pathname('/etc'))
        expect(FileUtils).not_to receive(:move)

        subject

        expect(snippet.reload.description).to eq(temp_description)
      end
    end
  end
157
end