carrierwave_direct 0.0.16 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +14 -9
  3. data/Changelog.md +45 -1
  4. data/README.md +37 -23
  5. data/carrierwave_direct.gemspec +4 -3
  6. data/gemfiles/{3.2.gemfile → 5.1.gemfile} +3 -3
  7. data/gemfiles/{4.0.gemfile → 5.2.gemfile} +3 -3
  8. data/gemfiles/{4.1.gemfile → 6.0.gemfile} +3 -3
  9. data/gemfiles/6.1.gemfile +13 -0
  10. data/lib/carrierwave_direct/action_view_extensions/form_helper.rb +1 -1
  11. data/lib/carrierwave_direct/form_builder.rb +30 -12
  12. data/lib/carrierwave_direct/mount.rb +1 -11
  13. data/lib/carrierwave_direct/policies/aws4_hmac_sha256.rb +93 -0
  14. data/lib/carrierwave_direct/policies/aws_base64_sha1.rb +57 -0
  15. data/lib/carrierwave_direct/policies/base.rb +21 -0
  16. data/lib/carrierwave_direct/test/capybara_helpers.rb +3 -3
  17. data/lib/carrierwave_direct/test/helpers.rb +1 -1
  18. data/lib/carrierwave_direct/uploader.rb +55 -56
  19. data/lib/carrierwave_direct/validations/active_model.rb +2 -2
  20. data/lib/carrierwave_direct/version.rb +1 -1
  21. data/spec/form_builder_spec.rb +24 -15
  22. data/spec/mount_spec.rb +2 -2
  23. data/spec/orm/activerecord_spec.rb +11 -7
  24. data/spec/orm/indirect_activerecord_spec.rb +7 -1
  25. data/spec/policies/aws4_hmac_sha256_spec.rb +243 -0
  26. data/spec/policies/aws_base64_sha1_spec.rb +229 -0
  27. data/spec/spec_helper.rb +5 -0
  28. data/spec/support/carrier_wave_config.rb +1 -0
  29. data/spec/test/capybara_helpers_spec.rb +4 -4
  30. data/spec/test/helpers_spec.rb +3 -3
  31. data/spec/uploader_spec.rb +20 -26
  32. metadata +36 -18
  33. data/lib/carrierwave_direct/uploader/direct_url.rb +0 -15
  34. data/spec/uploader/direct_url_spec.rb +0 -26
data/spec/mount_spec.rb CHANGED
@@ -18,12 +18,12 @@ describe CarrierWaveDirect::Mount do
18
18
 
19
19
  describe "#has_video_upload?" do
20
20
  it "returns false when video does not have a key" do
21
- allow(subject.video).to receive(:has_key?).and_return(false)
21
+ subject.video.key = nil
22
22
  expect(subject).to_not have_video_upload
23
23
  end
24
24
 
25
25
  it "returns true when video has a key" do
26
- allow(subject.video).to receive(:has_key?).and_return(true)
26
+ subject.video.key = sample(:s3_key)
27
27
  expect(subject).to have_video_upload
28
28
  end
29
29
  end
@@ -9,8 +9,12 @@ describe CarrierWaveDirect::ActiveRecord do
9
9
  :adapter => 'sqlite3',
10
10
  :database => ':memory:'
11
11
  }
12
-
13
- class TestMigration < ActiveRecord::Migration
12
+ if ActiveRecord::VERSION::MAJOR >= 5
13
+ migration_class = ::ActiveRecord::Migration[5.0]
14
+ else
15
+ migration_class = ::ActiveRecord::Migration
16
+ end
17
+ class TestMigration < migration_class
14
18
  def self.up
15
19
  create_table :parties, :force => true do |t|
16
20
  t.column :video, :string
@@ -100,8 +104,8 @@ describe CarrierWaveDirect::ActiveRecord do
100
104
  messages = I18n.t("errors.messages.carrierwave_direct_filename_invalid")
101
105
 
102
106
  if i18n_options
103
- if i18n_options[:extension_white_list]
104
- extensions = i18n_options[:extension_white_list].to_sentence
107
+ if i18n_options[:extension_allowlist]
108
+ extensions = i18n_options[:extension_allowlist].to_sentence
105
109
  messages += I18n.t("errors.messages.carrierwave_direct_allowed_extensions", :extensions => extensions)
106
110
  end
107
111
 
@@ -240,7 +244,7 @@ describe CarrierWaveDirect::ActiveRecord do
240
244
 
241
245
  context "where the uploader has an extension white list" do
242
246
  before do
243
- subject.video.stub(:extension_white_list).and_return(%w{avi mp4})
247
+ subject.video.stub(:extension_allowlist).and_return(%w{avi mp4})
244
248
  end
245
249
 
246
250
  context "and the uploaded file's extension is included in the list" do
@@ -298,7 +302,7 @@ describe CarrierWaveDirect::ActiveRecord do
298
302
  context "on create" do
299
303
  context "where the uploader has an extension white list" do
300
304
  before do
301
- subject.video.stub(:extension_white_list).and_return(%w{avi mp4})
305
+ subject.video.stub(:extension_allowlist).and_return(%w{avi mp4})
302
306
  end
303
307
 
304
308
  context "and the url's extension is included in the list" do
@@ -321,7 +325,7 @@ describe CarrierWaveDirect::ActiveRecord do
321
325
  end
322
326
 
323
327
  it_should_behave_like "a remote net url i18n error message" do
324
- let(:i18n_options) { {:extension_white_list => %w{avi mp4} } }
328
+ let(:i18n_options) { {:extension_allowlist => %w{avi mp4} } }
325
329
  end
326
330
 
327
331
  it "should include the white listed extensions in the error message" do
@@ -10,7 +10,13 @@ describe CarrierWave::ActiveRecord do
10
10
  :database => ':memory:'
11
11
  }
12
12
 
13
- class OtherTestMigration < ActiveRecord::Migration
13
+ if ActiveRecord::VERSION::MAJOR >= 5
14
+ migration_class = ::ActiveRecord::Migration[5.0]
15
+ else
16
+ migration_class = ::ActiveRecord::Migration
17
+ end
18
+
19
+ class OtherTestMigration < migration_class
14
20
  def self.up
15
21
  create_table :other_parties, :force => true do |t|
16
22
  t.column :video, :string
@@ -0,0 +1,243 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'data/sample_data'
4
+
5
+ describe CarrierWaveDirect::Policies::Aws4HmacSha256 do
6
+ let(:subject) { described_class.new(uploader) }
7
+ let(:uploader) { DirectUploader.new }
8
+ let(:mounted_model) { MountedClass.new }
9
+ let(:mounted_subject) { DirectUploader.new(mounted_model, sample(:mounted_as)) }
10
+
11
+ describe "#direct_fog_hash" do
12
+ it "should return the policy hash" do
13
+ expect(subject.direct_fog_hash.keys).to eq([:key, :acl, :policy, :"X-Amz-Signature", :"X-Amz-Credential", :"X-Amz-Algorithm", :"X-Amz-Date", :uri])
14
+ expect(subject.direct_fog_hash[:acl]).to eq 'public-read'
15
+ expect(subject.direct_fog_hash[:key]).to match /\$\{filename\}/
16
+ expect(subject.direct_fog_hash[:"X-Amz-Algorithm"]).to eq "AWS4-HMAC-SHA256"
17
+ expect(subject.direct_fog_hash[:uri]).to eq "https://s3.amazonaws.com/AWS_FOG_DIRECTORY/"
18
+ end
19
+ end
20
+
21
+ # http://aws.amazon.com/articles/1434?_encoding=UTF8
22
+ #http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
23
+ describe "#policy" do
24
+ def decoded_policy(options = {}, &block)
25
+ instance = options.delete(:subject) || subject
26
+ JSON.parse(Base64.decode64(instance.policy(options, &block)))
27
+ end
28
+
29
+ context "policy is given a block" do
30
+ it "should yield the options to the block" do
31
+ number = 0
32
+ subject.policy do |conditions|
33
+ number+=1
34
+ end
35
+ expect(number).to eq 1
36
+ end
37
+ it "should include new options in the conditions" do
38
+ policy = subject.policy do |conditions|
39
+ conditions << {"x-aws-storage-class" => "STANDARD"}
40
+ end
41
+ decoded = JSON.parse(Base64.decode64(policy))
42
+ expect(decoded['conditions'].last['x-aws-storage-class']).to eq "STANDARD"
43
+ end
44
+ end
45
+
46
+ it "should return Base64-encoded JSON" do
47
+ expect(decoded_policy).to be_a(Hash)
48
+ end
49
+
50
+ it "should not contain any new lines" do
51
+ expect(subject.policy).to_not include("\n")
52
+ end
53
+
54
+ it "should be cached" do
55
+ Timecop.freeze(Time.now) do
56
+ @policy_now = subject.policy
57
+ end
58
+ Timecop.freeze(1.second.from_now) do
59
+ @policy_later = subject.policy
60
+ end
61
+ expect(@policy_later).to eql @policy_now
62
+ end
63
+
64
+ context "expiration" do
65
+ def expiration(options = {})
66
+ decoded_policy(options)["expiration"]
67
+ end
68
+
69
+ # JSON times have no seconds, so accept upto one second inaccuracy
70
+ def have_expiration(expires_in = DirectUploader.upload_expiration)
71
+ be_within(1.second).of (Time.now + expires_in)
72
+ end
73
+
74
+ it "should be valid ISO8601 and not use default Time#to_json" do
75
+ Time.any_instance.stub(:to_json) { '"Invalid time"' } # JSON gem
76
+ Time.any_instance.stub(:as_json) { '"Invalid time"' } # Active Support
77
+ expect { Time.iso8601(expiration) }.to_not raise_error
78
+ end
79
+
80
+ it "should be #{DirectUploader.upload_expiration / 3600} hours from now" do
81
+ Timecop.freeze(Time.now) do
82
+ expect(Time.parse(expiration)).to have_expiration
83
+ end
84
+ end
85
+
86
+ it "should be encoded as a utc time" do
87
+ expect(Time.parse(expiration)).to be_utc
88
+ end
89
+
90
+ it "should be #{sample(:expiration) / 60 } minutes from now when passing {:expiration => #{sample(:expiration)}}" do
91
+ Timecop.freeze(Time.now) do
92
+ expect(Time.parse(expiration(:expiration => sample(:expiration)))).to have_expiration(sample(:expiration))
93
+ end
94
+ end
95
+ end
96
+
97
+ context "conditions" do
98
+ def conditions(options = {})
99
+ decoded_policy(options)["conditions"]
100
+ end
101
+
102
+ def have_condition(field, value = nil)
103
+ field.is_a?(Hash) ? include(field) : include(["starts-with", "$#{field}", value.to_s])
104
+ end
105
+
106
+ context "should include" do
107
+ it "'utf8' if enforce_ut8 is set" do
108
+ expect(conditions(enforce_utf8: true)).to have_condition(:utf8)
109
+ end
110
+
111
+ it "'utf8' if enforce_ut8 is set" do
112
+ expect(conditions).to_not have_condition(:utf8)
113
+ end
114
+
115
+ # S3 conditions
116
+ it "'key'" do
117
+ allow(mounted_subject).to receive(:key).and_return(sample(:s3_key))
118
+ expect(conditions(
119
+ :subject => mounted_subject
120
+ )).to have_condition(:key, sample(:s3_key))
121
+ end
122
+
123
+ it "'key' without FILENAME_WILDCARD" do
124
+ expect(conditions(
125
+ :subject => mounted_subject
126
+ )).to have_condition(:key, mounted_subject.key.sub("${filename}", ""))
127
+ end
128
+
129
+ it "'bucket'" do
130
+ expect(conditions).to have_condition("bucket" => uploader.fog_directory)
131
+ end
132
+
133
+ it "'acl'" do
134
+ expect(conditions).to have_condition("acl" => uploader.acl)
135
+ end
136
+
137
+ it "'success_action_redirect'" do
138
+ uploader.success_action_redirect = "http://example.com/some_url"
139
+ expect(conditions).to have_condition("success_action_redirect" => "http://example.com/some_url")
140
+ end
141
+
142
+ it "does not have 'content-type' when will_include_content_type is false" do
143
+ allow(uploader.class).to receive(:will_include_content_type).and_return(false)
144
+ expect(conditions).to_not have_condition('Content-Type')
145
+ end
146
+
147
+ it "has 'content-type' when will_include_content_type is true" do
148
+ allow(uploader.class).to receive(:will_include_content_type).and_return(true)
149
+ expect(conditions).to have_condition('Content-Type')
150
+ end
151
+
152
+ context 'when use_action_status is true' do
153
+ before(:all) do
154
+ DirectUploader.use_action_status = true
155
+ end
156
+
157
+ after(:all) do
158
+ DirectUploader.use_action_status = false
159
+ end
160
+
161
+ it "'success_action_status'" do
162
+ uploader.success_action_status = '200'
163
+ expect(conditions).to have_condition("success_action_status" => "200")
164
+ end
165
+
166
+ it "does not have 'success_action_redirect'" do
167
+ uploader.success_action_redirect = "http://example.com/some_url"
168
+ expect(conditions).to_not have_condition("success_action_redirect" => "http://example.com/some_url")
169
+ end
170
+ end
171
+
172
+ context "'content-length-range of'" do
173
+ def have_content_length_range(options = {})
174
+ include([
175
+ "content-length-range",
176
+ options[:min_file_size] || DirectUploader.min_file_size,
177
+ options[:max_file_size] || DirectUploader.max_file_size,
178
+ ])
179
+ end
180
+
181
+ it "#{DirectUploader.min_file_size} bytes" do
182
+ expect(conditions).to have_content_length_range
183
+ end
184
+
185
+ it "#{DirectUploader.max_file_size} bytes" do
186
+ expect(conditions).to have_content_length_range
187
+ end
188
+
189
+ it "#{sample(:min_file_size)} bytes when passing {:min_file_size => #{sample(:min_file_size)}}" do
190
+ expect(conditions(
191
+ :min_file_size => sample(:min_file_size)
192
+ )).to have_content_length_range(:min_file_size => sample(:min_file_size))
193
+ end
194
+
195
+ it "#{sample(:max_file_size)} bytes when passing {:max_file_size => #{sample(:max_file_size)}}" do
196
+ expect(conditions(
197
+ :max_file_size => sample(:max_file_size)
198
+ )).to have_content_length_range(:max_file_size => sample(:max_file_size))
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ describe "clear!" do
206
+ it "should reset the cached policy string" do
207
+ Timecop.freeze(Time.now) do
208
+ @policy_now = subject.policy
209
+ end
210
+ subject.clear!
211
+
212
+ Timecop.freeze(1.second.from_now) do
213
+ @policy_after_reset = subject.policy
214
+ end
215
+ expect(@policy_after_reset).not_to eql @policy_now
216
+ end
217
+ end
218
+
219
+ describe "#signature" do
220
+ it "should not contain any new lines" do
221
+ expect(subject.signature).to_not include("\n")
222
+ end
223
+
224
+ it "should return a HMAC hexdigest encoded 'sha256' hash of the secret key and policy document" do
225
+ expect(subject.signature).to eq OpenSSL::HMAC.hexdigest(
226
+ OpenSSL::Digest.new('sha256'),
227
+ subject.signing_key, subject.policy
228
+ )
229
+ end
230
+ end
231
+ #http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
232
+ describe "#signature_key" do
233
+ it "should include correct signature_key elements" do
234
+ kDate = OpenSSL::HMAC.digest('sha256', "AWS4" + uploader.aws_secret_access_key, Time.now.utc.strftime("%Y%m%d"))
235
+ kRegion = OpenSSL::HMAC.digest('sha256', kDate, uploader.region)
236
+ kService = OpenSSL::HMAC.digest('sha256', kRegion, 's3')
237
+ kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
238
+
239
+ expect(subject.signing_key).to eq (kSigning)
240
+ end
241
+ end
242
+ end
243
+
@@ -0,0 +1,229 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'data/sample_data'
4
+
5
+ describe CarrierWaveDirect::Policies::AwsBase64Sha1 do
6
+ let(:subject) { described_class.new(uploader) }
7
+ let(:uploader) { DirectUploader.new }
8
+ let(:mounted_model) { MountedClass.new }
9
+ let(:mounted_subject) { DirectUploader.new(mounted_model, sample(:mounted_as)) }
10
+
11
+ describe "#direct_fog_hash" do
12
+ it "should return the policy hash" do
13
+ expect(subject.direct_fog_hash.keys).to eq([:key, :AWSAccessKeyId, :acl, :policy, :signature, :uri])
14
+ expect(subject.direct_fog_hash[:acl]).to eq 'public-read'
15
+ expect(subject.direct_fog_hash[:key]).to match /\$\{filename\}/
16
+ expect(subject.direct_fog_hash[:uri]).to eq "https://s3.amazonaws.com/AWS_FOG_DIRECTORY/"
17
+ end
18
+ end
19
+
20
+ describe "#policy" do
21
+ def decoded_policy(options = {}, &block)
22
+ instance = options.delete(:subject) || subject
23
+ JSON.parse(Base64.decode64(instance.policy(options, &block)))
24
+ end
25
+
26
+ context "policy is given a block" do
27
+ it "should yield the options to the block" do
28
+ number = 0
29
+ subject.policy do |conditions|
30
+ number+=1
31
+ end
32
+ expect(number).to eq 1
33
+ end
34
+ it "should include new options in the conditions" do
35
+ policy = subject.policy do |conditions|
36
+ conditions << {"x-aws-storage-class" => "STANDARD"}
37
+ end
38
+ decoded = JSON.parse(Base64.decode64(policy))
39
+ expect(decoded['conditions'].last['x-aws-storage-class']).to eq "STANDARD"
40
+ end
41
+ end
42
+
43
+ it "should return Base64-encoded JSON" do
44
+ expect(decoded_policy).to be_a(Hash)
45
+ end
46
+
47
+ it "should not contain any new lines" do
48
+ expect(subject.policy).to_not include("\n")
49
+ end
50
+
51
+ it "should be cached" do
52
+ Timecop.freeze(Time.now) do
53
+ @policy_now = subject.policy
54
+ end
55
+ Timecop.freeze(1.second.from_now) do
56
+ @policy_later = subject.policy
57
+ end
58
+ expect(@policy_later).to eql @policy_now
59
+ end
60
+
61
+ context "expiration" do
62
+ def expiration(options = {})
63
+ decoded_policy(options)["expiration"]
64
+ end
65
+
66
+ # JSON times have no seconds, so accept upto one second inaccuracy
67
+ def have_expiration(expires_in = DirectUploader.upload_expiration)
68
+ be_within(1.second).of (Time.now + expires_in)
69
+ end
70
+
71
+ it "should be valid ISO8601 and not use default Time#to_json" do
72
+ Time.any_instance.stub(:to_json) { '"Invalid time"' } # JSON gem
73
+ Time.any_instance.stub(:as_json) { '"Invalid time"' } # Active Support
74
+ expect { Time.iso8601(expiration) }.to_not raise_error
75
+ end
76
+
77
+ it "should be #{DirectUploader.upload_expiration / 3600} hours from now" do
78
+ Timecop.freeze(Time.now) do
79
+ expect(Time.parse(expiration)).to have_expiration
80
+ end
81
+ end
82
+
83
+ it "should be encoded as a utc time" do
84
+ expect(Time.parse(expiration)).to be_utc
85
+ end
86
+
87
+ it "should be #{sample(:expiration) / 60 } minutes from now when passing {:expiration => #{sample(:expiration)}}" do
88
+ Timecop.freeze(Time.now) do
89
+ expect(Time.parse(expiration(:expiration => sample(:expiration)))).to have_expiration(sample(:expiration))
90
+ end
91
+ end
92
+ end
93
+
94
+ context "conditions" do
95
+ def conditions(options = {})
96
+ decoded_policy(options)["conditions"]
97
+ end
98
+
99
+ def have_condition(field, value = nil)
100
+ field.is_a?(Hash) ? include(field) : include(["starts-with", "$#{field}", value.to_s])
101
+ end
102
+
103
+ context "should include" do
104
+ it "'utf8' if enforce_ut8 is set" do
105
+ expect(conditions(enforce_utf8: true)).to have_condition(:utf8)
106
+ end
107
+
108
+ it "'utf8' if enforce_ut8 is set" do
109
+ expect(conditions).to_not have_condition(:utf8)
110
+ end
111
+
112
+ # S3 conditions
113
+ it "'key'" do
114
+ allow(mounted_subject).to receive(:key).and_return(sample(:s3_key))
115
+ expect(conditions(
116
+ :subject => mounted_subject
117
+ )).to have_condition(:key, sample(:s3_key))
118
+ end
119
+
120
+ it "'key' without FILENAME_WILDCARD" do
121
+ expect(conditions(
122
+ :subject => mounted_subject
123
+ )).to have_condition(:key, mounted_subject.key.sub("${filename}", ""))
124
+ end
125
+
126
+ it "'bucket'" do
127
+ expect(conditions).to have_condition("bucket" => uploader.fog_directory)
128
+ end
129
+
130
+ it "'acl'" do
131
+ expect(conditions).to have_condition("acl" => uploader.acl)
132
+ end
133
+
134
+ it "'success_action_redirect'" do
135
+ uploader.success_action_redirect = "http://example.com/some_url"
136
+ expect(conditions).to have_condition("success_action_redirect" => "http://example.com/some_url")
137
+ end
138
+
139
+ it "does not have 'content-type' when will_include_content_type is false" do
140
+ allow(uploader.class).to receive(:will_include_content_type).and_return(false)
141
+ expect(conditions).to_not have_condition('Content-Type')
142
+ end
143
+
144
+ it "has 'content-type' when will_include_content_type is true" do
145
+ allow(uploader.class).to receive(:will_include_content_type).and_return(true)
146
+ expect(conditions).to have_condition('Content-Type')
147
+ end
148
+
149
+ context 'when use_action_status is true' do
150
+ before(:all) do
151
+ DirectUploader.use_action_status = true
152
+ end
153
+
154
+ after(:all) do
155
+ DirectUploader.use_action_status = false
156
+ end
157
+
158
+ it "'success_action_status'" do
159
+ uploader.success_action_status = '200'
160
+ expect(conditions).to have_condition("success_action_status" => "200")
161
+ end
162
+
163
+ it "does not have 'success_action_redirect'" do
164
+ uploader.success_action_redirect = "http://example.com/some_url"
165
+ expect(conditions).to_not have_condition("success_action_redirect" => "http://example.com/some_url")
166
+ end
167
+ end
168
+
169
+ context "'content-length-range of'" do
170
+ def have_content_length_range(options = {})
171
+ include([
172
+ "content-length-range",
173
+ options[:min_file_size] || DirectUploader.min_file_size,
174
+ options[:max_file_size] || DirectUploader.max_file_size,
175
+ ])
176
+ end
177
+
178
+ it "#{DirectUploader.min_file_size} bytes" do
179
+ expect(conditions).to have_content_length_range
180
+ end
181
+
182
+ it "#{DirectUploader.max_file_size} bytes" do
183
+ expect(conditions).to have_content_length_range
184
+ end
185
+
186
+ it "#{sample(:min_file_size)} bytes when passing {:min_file_size => #{sample(:min_file_size)}}" do
187
+ expect(conditions(
188
+ :min_file_size => sample(:min_file_size)
189
+ )).to have_content_length_range(:min_file_size => sample(:min_file_size))
190
+ end
191
+
192
+ it "#{sample(:max_file_size)} bytes when passing {:max_file_size => #{sample(:max_file_size)}}" do
193
+ expect(conditions(
194
+ :max_file_size => sample(:max_file_size)
195
+ )).to have_content_length_range(:max_file_size => sample(:max_file_size))
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ describe "clear!" do
203
+ it "should reset the cached policy string" do
204
+ Timecop.freeze(Time.now) do
205
+ @policy_now = subject.policy
206
+ end
207
+ subject.clear!
208
+
209
+ Timecop.freeze(1.second.from_now) do
210
+ @policy_after_reset = subject.policy
211
+ end
212
+ expect(@policy_after_reset).not_to eql @policy_now
213
+ end
214
+ end
215
+
216
+ describe "#signature" do
217
+ it "should not contain any new lines" do
218
+ expect(subject.signature).to_not include("\n")
219
+ end
220
+
221
+ it "should return a base64 encoded 'sha1' hash of the secret key and policy document" do
222
+ expect(Base64.decode64(subject.signature)).to eq OpenSSL::HMAC.digest(
223
+ OpenSSL::Digest.new('sha1'),
224
+ uploader.aws_secret_access_key, subject.policy
225
+ )
226
+ end
227
+ end
228
+ end
229
+