summaryrefslogtreecommitdiff
path: root/spec/models/upload_spec.rb
blob: 2dea2c6015f6bd22ac7bf4032f6bf6005f1c8548 (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
require 'rails_helper'

describe Upload, type: :model do
  describe 'assocations' do
    it { is_expected.to belong_to(:model) }
  end

  describe 'validations' do
    it { is_expected.to validate_presence_of(:size) }
    it { is_expected.to validate_presence_of(:path) }
    it { is_expected.to validate_presence_of(:model) }
    it { is_expected.to validate_presence_of(:uploader) }
  end

  describe 'callbacks' do
    context 'for a file above the checksum threshold' do
      it 'schedules checksum calculation' do
        stub_const('UploadChecksumWorker', spy)

        upload = described_class.create(
          path: __FILE__,
          size: described_class::CHECKSUM_THRESHOLD + 1.kilobyte,
          model: build_stubbed(:user),
          uploader: double('ExampleUploader')
        )

        expect(UploadChecksumWorker)
          .to have_received(:perform_async).with(upload.id)
      end
    end

    context 'for a file at or below the checksum threshold' do
      it 'calculates checksum immediately before save' do
        upload = described_class.new(
          path: __FILE__,
          size: described_class::CHECKSUM_THRESHOLD,
          model: build_stubbed(:user),
          uploader: double('ExampleUploader')
        )

        expect { upload.save }
          .to change { upload.checksum }.from(nil)
          .to(a_string_matching(/\A\h{64}\z/))
      end
    end
  end

  describe '.remove_path' do
    it 'removes all records at the given path' do
      described_class.create!(
        size: File.size(__FILE__),
        path: __FILE__,
        model: build_stubbed(:user),
        uploader: 'AvatarUploader'
      )

      expect { described_class.remove_path(__FILE__) }
        .to change { described_class.count }.from(1).to(0)
    end
  end

  describe '.record' do
    let(:fake_uploader) do
      double(
        file: double(size: 12_345),
        relative_path: 'foo/bar.jpg',
        model: build_stubbed(:user),
        class: 'AvatarUploader'
      )
    end

    it 'removes existing paths before creation' do
      expect(described_class).to receive(:remove_path)
        .with(fake_uploader.relative_path)

      described_class.record(fake_uploader)
    end

    it 'creates a new record and assigns size, path, model, and uploader' do
      upload = described_class.record(fake_uploader)

      aggregate_failures do
        expect(upload).to be_persisted
        expect(upload.size).to eq fake_uploader.file.size
        expect(upload.path).to eq fake_uploader.relative_path
        expect(upload.model_id).to eq fake_uploader.model.id
        expect(upload.model_type).to eq fake_uploader.model.class.to_s
        expect(upload.uploader).to eq fake_uploader.class
      end
    end
  end

  describe '#absolute_path' do
    it 'returns the path directly when already absolute' do
      path = '/path/to/namespace/project/secret/file.jpg'
      upload = described_class.new(path: path)

      expect(upload).not_to receive(:uploader_class)

      expect(upload.absolute_path).to eq path
    end

    it "delegates to the uploader's absolute_path method" do
      uploader = spy('FakeUploader')
      upload = described_class.new(path: 'secret/file.jpg')
      expect(upload).to receive(:uploader_class).and_return(uploader)

      upload.absolute_path

      expect(uploader).to have_received(:absolute_path).with(upload)
    end
  end

  describe '#calculate_checksum' do
    it 'calculates the SHA256 sum' do
      upload = described_class.new(
        path: __FILE__,
        size: described_class::CHECKSUM_THRESHOLD - 1.megabyte
      )
      expected = Digest::SHA256.file(__FILE__).hexdigest

      expect { upload.calculate_checksum }
        .to change { upload.checksum }.from(nil).to(expected)
    end

    it 'returns nil for a non-existant file' do
      upload = described_class.new(
        path: __FILE__,
        size: described_class::CHECKSUM_THRESHOLD - 1.megabyte
      )

      expect(upload).to receive(:exist?).and_return(false)

      expect(upload.calculate_checksum).to be_nil
    end
  end

  describe '#exist?' do
    it 'returns true when the file exists' do
      upload = described_class.new(path: __FILE__)

      expect(upload).to exist
    end

    it 'returns false when the file does not exist' do
      upload = described_class.new(path: "#{__FILE__}-nope")

      expect(upload).not_to exist
    end
  end
end