paperclip 3.5.4 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +17 -0
  3. data/.github/issue_template.md +3 -0
  4. data/.gitignore +0 -6
  5. data/.hound.yml +1055 -0
  6. data/.rubocop.yml +1 -0
  7. data/.travis.yml +17 -20
  8. data/Appraisals +4 -16
  9. data/CONTRIBUTING.md +29 -13
  10. data/Gemfile +11 -3
  11. data/LICENSE +1 -3
  12. data/MIGRATING-ES.md +317 -0
  13. data/MIGRATING.md +375 -0
  14. data/NEWS +262 -49
  15. data/README.md +496 -169
  16. data/RELEASING.md +17 -0
  17. data/Rakefile +6 -8
  18. data/UPGRADING +12 -9
  19. data/features/basic_integration.feature +27 -8
  20. data/features/migration.feature +0 -24
  21. data/features/step_definitions/attachment_steps.rb +44 -36
  22. data/features/step_definitions/html_steps.rb +2 -2
  23. data/features/step_definitions/rails_steps.rb +68 -37
  24. data/features/step_definitions/s3_steps.rb +2 -2
  25. data/features/step_definitions/web_steps.rb +1 -103
  26. data/features/support/env.rb +3 -2
  27. data/features/support/file_helpers.rb +2 -2
  28. data/features/support/fixtures/gemfile.txt +1 -1
  29. data/features/support/paths.rb +1 -1
  30. data/features/support/rails.rb +2 -25
  31. data/gemfiles/4.2.gemfile +17 -0
  32. data/gemfiles/5.0.gemfile +17 -0
  33. data/lib/generators/paperclip/paperclip_generator.rb +9 -3
  34. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +2 -2
  35. data/lib/paperclip/attachment.rb +170 -52
  36. data/lib/paperclip/attachment_registry.rb +3 -2
  37. data/lib/paperclip/callbacks.rb +13 -1
  38. data/lib/paperclip/content_type_detector.rb +26 -22
  39. data/lib/paperclip/errors.rb +8 -1
  40. data/lib/paperclip/file_command_content_type_detector.rb +6 -8
  41. data/lib/paperclip/filename_cleaner.rb +0 -1
  42. data/lib/paperclip/geometry_detector_factory.rb +6 -4
  43. data/lib/paperclip/geometry_parser_factory.rb +1 -1
  44. data/lib/paperclip/glue.rb +1 -1
  45. data/lib/paperclip/has_attached_file.rb +17 -1
  46. data/lib/paperclip/helpers.rb +15 -11
  47. data/lib/paperclip/interpolations/plural_cache.rb +6 -5
  48. data/lib/paperclip/interpolations.rb +31 -13
  49. data/lib/paperclip/io_adapters/abstract_adapter.rb +34 -5
  50. data/lib/paperclip/io_adapters/attachment_adapter.rb +19 -8
  51. data/lib/paperclip/io_adapters/data_uri_adapter.rb +11 -16
  52. data/lib/paperclip/io_adapters/empty_string_adapter.rb +5 -4
  53. data/lib/paperclip/io_adapters/file_adapter.rb +12 -6
  54. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +8 -7
  55. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -6
  56. data/lib/paperclip/io_adapters/nil_adapter.rb +8 -5
  57. data/lib/paperclip/io_adapters/registry.rb +6 -2
  58. data/lib/paperclip/io_adapters/stringio_adapter.rb +15 -16
  59. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +10 -6
  60. data/lib/paperclip/io_adapters/uri_adapter.rb +43 -19
  61. data/lib/paperclip/locales/en.yml +1 -0
  62. data/lib/paperclip/logger.rb +1 -1
  63. data/lib/paperclip/matchers/have_attached_file_matcher.rb +2 -1
  64. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
  65. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +2 -1
  66. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +2 -1
  67. data/lib/paperclip/media_type_spoof_detector.rb +93 -0
  68. data/lib/paperclip/processor.rb +15 -43
  69. data/lib/paperclip/processor_helpers.rb +50 -0
  70. data/lib/paperclip/rails_environment.rb +25 -0
  71. data/lib/paperclip/schema.rb +10 -8
  72. data/lib/paperclip/storage/filesystem.rb +14 -3
  73. data/lib/paperclip/storage/fog.rb +38 -20
  74. data/lib/paperclip/storage/s3.rb +124 -73
  75. data/lib/paperclip/style.rb +8 -3
  76. data/lib/paperclip/tempfile_factory.rb +5 -1
  77. data/lib/paperclip/thumbnail.rb +34 -19
  78. data/lib/paperclip/url_generator.rb +26 -14
  79. data/lib/paperclip/validators/attachment_content_type_validator.rb +4 -0
  80. data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
  81. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
  82. data/lib/paperclip/validators/attachment_presence_validator.rb +4 -0
  83. data/lib/paperclip/validators/attachment_size_validator.rb +5 -3
  84. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +31 -0
  85. data/lib/paperclip/validators.rb +11 -4
  86. data/lib/paperclip/version.rb +3 -1
  87. data/lib/paperclip.rb +31 -11
  88. data/lib/tasks/paperclip.rake +34 -5
  89. data/paperclip.gemspec +21 -16
  90. data/shoulda_macros/paperclip.rb +0 -1
  91. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  92. data/{test/attachment_processing_test.rb → spec/paperclip/attachment_processing_spec.rb} +17 -21
  93. data/spec/paperclip/attachment_registry_spec.rb +158 -0
  94. data/{test/attachment_test.rb → spec/paperclip/attachment_spec.rb} +519 -409
  95. data/{test/content_type_detector_test.rb → spec/paperclip/content_type_detector_spec.rb} +17 -20
  96. data/spec/paperclip/file_command_content_type_detector_spec.rb +40 -0
  97. data/spec/paperclip/filename_cleaner_spec.rb +13 -0
  98. data/spec/paperclip/geometry_detector_spec.rb +39 -0
  99. data/{test/geometry_parser_test.rb → spec/paperclip/geometry_parser_spec.rb} +27 -27
  100. data/{test/geometry_test.rb → spec/paperclip/geometry_spec.rb} +50 -52
  101. data/spec/paperclip/glue_spec.rb +44 -0
  102. data/spec/paperclip/has_attached_file_spec.rb +158 -0
  103. data/{test/integration_test.rb → spec/paperclip/integration_spec.rb} +174 -129
  104. data/{test/interpolations_test.rb → spec/paperclip/interpolations_spec.rb} +79 -46
  105. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +160 -0
  106. data/{test/io_adapters/attachment_adapter_test.rb → spec/paperclip/io_adapters/attachment_adapter_spec.rb} +33 -32
  107. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +89 -0
  108. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  109. data/{test/io_adapters/file_adapter_test.rb → spec/paperclip/io_adapters/file_adapter_spec.rb} +38 -42
  110. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +138 -0
  111. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  112. data/{test/io_adapters/nil_adapter_test.rb → spec/paperclip/io_adapters/nil_adapter_spec.rb} +7 -7
  113. data/{test/io_adapters/registry_test.rb → spec/paperclip/io_adapters/registry_spec.rb} +12 -9
  114. data/{test/io_adapters/stringio_adapter_test.rb → spec/paperclip/io_adapters/stringio_adapter_spec.rb} +21 -18
  115. data/{test/io_adapters/uploaded_file_adapter_test.rb → spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb} +46 -46
  116. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +220 -0
  117. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  118. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +109 -0
  119. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  120. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  121. data/spec/paperclip/media_type_spoof_detector_spec.rb +120 -0
  122. data/spec/paperclip/meta_class_spec.rb +30 -0
  123. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
  124. data/spec/paperclip/paperclip_spec.rb +192 -0
  125. data/spec/paperclip/plural_cache_spec.rb +37 -0
  126. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  127. data/{test/processor_test.rb → spec/paperclip/processor_spec.rb} +7 -7
  128. data/spec/paperclip/rails_environment_spec.rb +33 -0
  129. data/{test/rake_test.rb → spec/paperclip/rake_spec.rb} +15 -15
  130. data/spec/paperclip/schema_spec.rb +248 -0
  131. data/{test/storage/filesystem_test.rb → spec/paperclip/storage/filesystem_spec.rb} +18 -18
  132. data/spec/paperclip/storage/fog_spec.rb +566 -0
  133. data/spec/paperclip/storage/s3_live_spec.rb +188 -0
  134. data/spec/paperclip/storage/s3_spec.rb +1693 -0
  135. data/spec/paperclip/style_spec.rb +254 -0
  136. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  137. data/spec/paperclip/tempfile_spec.rb +35 -0
  138. data/{test/thumbnail_test.rb → spec/paperclip/thumbnail_spec.rb} +158 -137
  139. data/spec/paperclip/url_generator_spec.rb +221 -0
  140. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  141. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
  142. data/{test/validators/attachment_presence_validator_test.rb → spec/paperclip/validators/attachment_presence_validator_spec.rb} +20 -20
  143. data/{test/validators/attachment_size_validator_test.rb → spec/paperclip/validators/attachment_size_validator_spec.rb} +77 -64
  144. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
  145. data/spec/paperclip/validators_spec.rb +164 -0
  146. data/spec/spec_helper.rb +46 -0
  147. data/spec/support/assertions.rb +82 -0
  148. data/spec/support/fake_model.rb +25 -0
  149. data/spec/support/fake_rails.rb +12 -0
  150. data/spec/support/fixtures/empty.html +1 -0
  151. data/spec/support/fixtures/empty.xlsx +0 -0
  152. data/spec/support/fixtures/spaced file.jpg +0 -0
  153. data/spec/support/matchers/accept.rb +5 -0
  154. data/spec/support/matchers/exist.rb +5 -0
  155. data/spec/support/matchers/have_column.rb +23 -0
  156. data/{test → spec}/support/mock_attachment.rb +2 -0
  157. data/{test → spec}/support/mock_url_generator_builder.rb +2 -2
  158. data/spec/support/model_reconstruction.rb +68 -0
  159. data/spec/support/reporting.rb +11 -0
  160. data/spec/support/test_data.rb +13 -0
  161. data/spec/support/version_helper.rb +9 -0
  162. metadata +344 -226
  163. data/RUNNING_TESTS.md +0 -4
  164. data/cucumber/paperclip_steps.rb +0 -6
  165. data/gemfiles/3.0.gemfile +0 -11
  166. data/gemfiles/3.1.gemfile +0 -11
  167. data/gemfiles/3.2.gemfile +0 -11
  168. data/gemfiles/4.0.gemfile +0 -11
  169. data/test/attachment_definitions_test.rb +0 -12
  170. data/test/attachment_registry_test.rb +0 -88
  171. data/test/file_command_content_type_detector_test.rb +0 -27
  172. data/test/filename_cleaner_test.rb +0 -14
  173. data/test/generator_test.rb +0 -84
  174. data/test/geometry_detector_test.rb +0 -24
  175. data/test/has_attached_file_test.rb +0 -125
  176. data/test/helper.rb +0 -232
  177. data/test/io_adapters/abstract_adapter_test.rb +0 -58
  178. data/test/io_adapters/data_uri_adapter_test.rb +0 -74
  179. data/test/io_adapters/empty_string_adapter_test.rb +0 -18
  180. data/test/io_adapters/http_url_proxy_adapter_test.rb +0 -102
  181. data/test/io_adapters/identity_adapter_test.rb +0 -8
  182. data/test/io_adapters/uri_adapter_test.rb +0 -102
  183. data/test/matchers/have_attached_file_matcher_test.rb +0 -24
  184. data/test/matchers/validate_attachment_content_type_matcher_test.rb +0 -110
  185. data/test/matchers/validate_attachment_presence_matcher_test.rb +0 -69
  186. data/test/matchers/validate_attachment_size_matcher_test.rb +0 -86
  187. data/test/meta_class_test.rb +0 -32
  188. data/test/paperclip_missing_attachment_styles_test.rb +0 -90
  189. data/test/paperclip_test.rb +0 -217
  190. data/test/plural_cache_test.rb +0 -36
  191. data/test/schema_test.rb +0 -200
  192. data/test/storage/fog_test.rb +0 -473
  193. data/test/storage/s3_live_test.rb +0 -179
  194. data/test/storage/s3_test.rb +0 -1356
  195. data/test/style_test.rb +0 -213
  196. data/test/support/mock_model.rb +0 -2
  197. data/test/tempfile_factory_test.rb +0 -17
  198. data/test/url_generator_test.rb +0 -187
  199. data/test/validators/attachment_content_type_validator_test.rb +0 -324
  200. data/test/validators_test.rb +0 -61
  201. /data/{test → spec}/database.yml +0 -0
  202. /data/{test → spec/support}/fixtures/12k.png +0 -0
  203. /data/{test → spec/support}/fixtures/50x50.png +0 -0
  204. /data/{test → spec/support}/fixtures/5k.png +0 -0
  205. /data/{test → spec/support}/fixtures/animated +0 -0
  206. /data/{test → spec/support}/fixtures/animated.gif +0 -0
  207. /data/{test → spec/support}/fixtures/animated.unknown +0 -0
  208. /data/{test → spec/support}/fixtures/bad.png +0 -0
  209. /data/{test → spec/support}/fixtures/fog.yml +0 -0
  210. /data/{test → spec/support}/fixtures/rotated.jpg +0 -0
  211. /data/{test → spec/support}/fixtures/s3.yml +0 -0
  212. /data/{test → spec/support}/fixtures/spaced file.png +0 -0
  213. /data/{test → spec/support}/fixtures/text.txt +0 -0
  214. /data/{test → spec/support}/fixtures/twopage.pdf +0 -0
  215. /data/{test → spec/support}/fixtures/uppercase.PNG +0 -0
  216. /data/{test → spec}/support/mock_interpolator.rb +0 -0
@@ -0,0 +1,1693 @@
1
+ require "spec_helper"
2
+ require "aws-sdk-s3"
3
+
4
+ describe Paperclip::Storage::S3 do
5
+ before do
6
+ Aws.config[:stub_responses] = true
7
+ end
8
+
9
+ def aws2_add_region
10
+ { s3_region: 'us-east-1' }
11
+ end
12
+
13
+ context "Parsing S3 credentials" do
14
+ before do
15
+ @proxy_settings = {host: "127.0.0.1", port: 8888, user: "foo", password: "bar"}
16
+ rebuild_model (aws2_add_region).merge storage: :s3,
17
+ bucket: "testing",
18
+ http_proxy: @proxy_settings,
19
+ s3_credentials: {not: :important}
20
+ @dummy = Dummy.new
21
+ @avatar = @dummy.avatar
22
+ end
23
+
24
+ it "gets the correct credentials when RAILS_ENV is production" do
25
+ rails_env("production") do
26
+ assert_equal({key: "12345"},
27
+ @avatar.parse_credentials('production' => {key: '12345'},
28
+ development: {key: "54321"}))
29
+ end
30
+ end
31
+
32
+ it "gets the correct credentials when RAILS_ENV is development" do
33
+ rails_env("development") do
34
+ assert_equal({key: "54321"},
35
+ @avatar.parse_credentials('production' => {key: '12345'},
36
+ development: {key: "54321"}))
37
+ end
38
+ end
39
+
40
+ it "returns the argument if the key does not exist" do
41
+ rails_env("not really an env") do
42
+ assert_equal({test: "12345"}, @avatar.parse_credentials(test: "12345"))
43
+ end
44
+ end
45
+
46
+ it "supports HTTP proxy settings" do
47
+ rails_env("development") do
48
+ assert_equal(true, @avatar.using_http_proxy?)
49
+ assert_equal(@proxy_settings[:host], @avatar.http_proxy_host)
50
+ assert_equal(@proxy_settings[:port], @avatar.http_proxy_port)
51
+ assert_equal(@proxy_settings[:user], @avatar.http_proxy_user)
52
+ assert_equal(@proxy_settings[:password], @avatar.http_proxy_password)
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ context ":bucket option via :s3_credentials" do
59
+
60
+ before do
61
+ rebuild_model (aws2_add_region).merge storage: :s3,
62
+ s3_credentials: {bucket: 'testing'}
63
+ @dummy = Dummy.new
64
+ end
65
+
66
+ it "populates #bucket_name" do
67
+ assert_equal @dummy.avatar.bucket_name, 'testing'
68
+ end
69
+
70
+ end
71
+
72
+ context ":bucket option" do
73
+
74
+ before do
75
+ rebuild_model (aws2_add_region).merge storage: :s3,
76
+ bucket: "testing", s3_credentials: {}
77
+ @dummy = Dummy.new
78
+ end
79
+
80
+ it "populates #bucket_name" do
81
+ assert_equal @dummy.avatar.bucket_name, 'testing'
82
+ end
83
+
84
+ end
85
+
86
+ context "missing :bucket option" do
87
+
88
+ before do
89
+ rebuild_model (aws2_add_region).merge storage: :s3,
90
+ http_proxy: @proxy_settings,
91
+ s3_credentials: {not: :important}
92
+
93
+ @dummy = Dummy.new
94
+ @dummy.avatar = stringy_file
95
+
96
+ end
97
+
98
+ it "raises an argument error" do
99
+ expect { @dummy.save }.to raise_error(ArgumentError, /missing required :bucket option/)
100
+ end
101
+
102
+ end
103
+
104
+ context "" do
105
+ before do
106
+ rebuild_model (aws2_add_region).merge storage: :s3,
107
+ s3_credentials: {},
108
+ bucket: "bucket",
109
+ path: ":attachment/:basename:dotextension",
110
+ url: ":s3_path_url"
111
+ @dummy = Dummy.new
112
+ @dummy.avatar = stringy_file
113
+ @dummy.stubs(:new_record?).returns(false)
114
+ end
115
+
116
+ it "returns a url based on an S3 path" do
117
+ assert_match %r{^//s3.amazonaws.com/bucket/avatars/data[^\.]}, @dummy.avatar.url
118
+ end
119
+
120
+ it "uses the correct bucket" do
121
+ assert_equal "bucket", @dummy.avatar.s3_bucket.name
122
+ end
123
+
124
+ it "uses the correct key" do
125
+ assert_equal "avatars/data", @dummy.avatar.s3_object.key
126
+ end
127
+ end
128
+
129
+ context "s3_protocol" do
130
+ ["http", :http, ""].each do |protocol|
131
+ context "as #{protocol.inspect}" do
132
+ before do
133
+ rebuild_model (aws2_add_region).merge storage: :s3,
134
+ s3_protocol: protocol
135
+ @dummy = Dummy.new
136
+ end
137
+
138
+ it "returns the s3_protocol in string" do
139
+ assert_equal protocol.to_s, @dummy.avatar.s3_protocol
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ context "s3_protocol: 'https'" do
146
+ before do
147
+ rebuild_model (aws2_add_region).merge storage: :s3,
148
+ s3_credentials: {},
149
+ s3_protocol: 'https',
150
+ bucket: "bucket",
151
+ path: ":attachment/:basename:dotextension"
152
+ @dummy = Dummy.new
153
+ @dummy.avatar = stringy_file
154
+ @dummy.stubs(:new_record?).returns(false)
155
+ end
156
+
157
+ it "returns a url based on an S3 path" do
158
+ assert_match %r{^https://s3.amazonaws.com/bucket/avatars/data[^\.]}, @dummy.avatar.url
159
+ end
160
+ end
161
+
162
+ context "s3_protocol: ''" do
163
+ before do
164
+ rebuild_model (aws2_add_region).merge storage: :s3,
165
+ s3_credentials: {},
166
+ s3_protocol: '',
167
+ bucket: "bucket",
168
+ path: ":attachment/:basename:dotextension"
169
+ @dummy = Dummy.new
170
+ @dummy.avatar = stringy_file
171
+ @dummy.stubs(:new_record?).returns(false)
172
+ end
173
+
174
+ it "returns a protocol-relative URL" do
175
+ assert_match %r{^//s3.amazonaws.com/bucket/avatars/data[^\.]}, @dummy.avatar.url
176
+ end
177
+ end
178
+
179
+ context "s3_protocol: :https" do
180
+ before do
181
+ rebuild_model (aws2_add_region).merge storage: :s3,
182
+ s3_credentials: {},
183
+ s3_protocol: :https,
184
+ bucket: "bucket",
185
+ path: ":attachment/:basename:dotextension"
186
+ @dummy = Dummy.new
187
+ @dummy.avatar = stringy_file
188
+ @dummy.stubs(:new_record?).returns(false)
189
+ end
190
+
191
+ it "returns a url based on an S3 path" do
192
+ assert_match %r{^https://s3.amazonaws.com/bucket/avatars/data[^\.]}, @dummy.avatar.url
193
+ end
194
+ end
195
+
196
+ context "s3_protocol: ''" do
197
+ before do
198
+ rebuild_model (aws2_add_region).merge storage: :s3,
199
+ s3_credentials: {},
200
+ s3_protocol: '',
201
+ bucket: "bucket",
202
+ path: ":attachment/:basename:dotextension"
203
+ @dummy = Dummy.new
204
+ @dummy.avatar = stringy_file
205
+ @dummy.stubs(:new_record?).returns(false)
206
+ end
207
+
208
+ it "returns a url based on an S3 path" do
209
+ assert_match %r{^//s3.amazonaws.com/bucket/avatars/data[^\.]}, @dummy.avatar.url
210
+ end
211
+ end
212
+
213
+ context "An attachment that uses S3 for storage and has the style in the path" do
214
+ before do
215
+ rebuild_model (aws2_add_region).merge storage: :s3,
216
+ bucket: "testing",
217
+ path: ":attachment/:style/:basename:dotextension",
218
+ styles: {
219
+ thumb: "80x80>"
220
+ },
221
+ s3_credentials: {
222
+ 'access_key_id' => "12345",
223
+ 'secret_access_key' => "54321"
224
+ }
225
+
226
+ @dummy = Dummy.new
227
+ @dummy.avatar = stringy_file
228
+ @avatar = @dummy.avatar
229
+ end
230
+
231
+ it "uses an S3 object based on the correct path for the default style" do
232
+ assert_equal("avatars/original/data", @dummy.avatar.s3_object.key)
233
+ end
234
+
235
+ it "uses an S3 object based on the correct path for the custom style" do
236
+ assert_equal("avatars/thumb/data", @dummy.avatar.s3_object(:thumb).key)
237
+ end
238
+ end
239
+
240
+ # the s3_host_name will be defined by the s3_region
241
+ context "s3_host_name" do
242
+ before do
243
+ rebuild_model storage: :s3,
244
+ s3_credentials: {},
245
+ bucket: "bucket",
246
+ path: ":attachment/:basename:dotextension",
247
+ s3_host_name: "s3-ap-northeast-1.amazonaws.com",
248
+ s3_region: "ap-northeast-1"
249
+ @dummy = Dummy.new
250
+ @dummy.avatar = stringy_file
251
+ @dummy.stubs(:new_record?).returns(false)
252
+ end
253
+
254
+ it "returns a url based on an :s3_host_name path" do
255
+ assert_match %r{^//s3-ap-northeast-1.amazonaws.com/bucket/avatars/data[^\.]}, @dummy.avatar.url
256
+ end
257
+
258
+ it "uses the S3 bucket with the correct host name" do
259
+ assert_equal "s3.ap-northeast-1.amazonaws.com",
260
+ @dummy.avatar.s3_bucket.client.config.endpoint.host
261
+ end
262
+ end
263
+
264
+ context "dynamic s3_host_name" do
265
+ before do
266
+ rebuild_model (aws2_add_region).merge storage: :s3,
267
+ s3_credentials: {},
268
+ bucket: "bucket",
269
+ path: ":attachment/:basename:dotextension",
270
+ s3_host_name: lambda {|a| a.instance.value }
271
+ @dummy = Dummy.new
272
+ class << @dummy
273
+ attr_accessor :value
274
+ end
275
+ @dummy.avatar = stringy_file
276
+ @dummy.stubs(:new_record?).returns(false)
277
+ end
278
+
279
+ it "uses s3_host_name as a proc if available" do
280
+ @dummy.value = "s3.something.com"
281
+ assert_equal "//s3.something.com/bucket/avatars/data", @dummy.avatar.url(:original, timestamp: false)
282
+ end
283
+ end
284
+
285
+ context "use_accelerate_endpoint" do
286
+ context "defaults to false" do
287
+ before do
288
+ rebuild_model(
289
+ storage: :s3,
290
+ s3_credentials: {},
291
+ bucket: "bucket",
292
+ path: ":attachment/:basename:dotextension",
293
+ s3_host_name: "s3-ap-northeast-1.amazonaws.com",
294
+ s3_region: "ap-northeast-1",
295
+ )
296
+ @dummy = Dummy.new
297
+ @dummy.avatar = stringy_file
298
+ @dummy.stubs(:new_record?).returns(false)
299
+ end
300
+
301
+ it "returns a url based on an :s3_host_name path" do
302
+ assert_match %r{^//s3-ap-northeast-1.amazonaws.com/bucket/avatars/data[^\.]},
303
+ @dummy.avatar.url
304
+ end
305
+
306
+ it "uses the S3 client with the use_accelerate_endpoint config is false" do
307
+ expect(@dummy.avatar.s3_bucket.client.config.use_accelerate_endpoint).to be(false)
308
+ end
309
+ end
310
+
311
+ context "set to true" do
312
+ before do
313
+ rebuild_model(
314
+ storage: :s3,
315
+ s3_credentials: {},
316
+ bucket: "bucket",
317
+ path: ":attachment/:basename:dotextension",
318
+ s3_host_name: "s3-accelerate.amazonaws.com",
319
+ s3_region: "ap-northeast-1",
320
+ use_accelerate_endpoint: true,
321
+ )
322
+ @dummy = Dummy.new
323
+ @dummy.avatar = stringy_file
324
+ @dummy.stubs(:new_record?).returns(false)
325
+ end
326
+
327
+ it "returns a url based on an :s3_host_name path" do
328
+ assert_match %r{^//s3-accelerate.amazonaws.com/bucket/avatars/data[^\.]},
329
+ @dummy.avatar.url
330
+ end
331
+
332
+ it "uses the S3 client with the use_accelerate_endpoint config is true" do
333
+ expect(@dummy.avatar.s3_bucket.client.config.use_accelerate_endpoint).to be(true)
334
+ end
335
+ end
336
+ end
337
+
338
+ context "An attachment that uses S3 for storage and has styles that return different file types" do
339
+ before do
340
+ rebuild_model (aws2_add_region).merge storage: :s3,
341
+ styles: { large: ['500x500#', :jpg] },
342
+ bucket: "bucket",
343
+ path: ":attachment/:basename:dotextension",
344
+ s3_credentials: {
345
+ 'access_key_id' => "12345",
346
+ 'secret_access_key' => "54321"
347
+ }
348
+
349
+ File.open(fixture_file('5k.png'), 'rb') do |file|
350
+ @dummy = Dummy.new
351
+ @dummy.avatar = file
352
+ @dummy.stubs(:new_record?).returns(false)
353
+ end
354
+ end
355
+
356
+ it "returns a url containing the correct original file mime type" do
357
+ assert_match /.+\/5k.png/, @dummy.avatar.url
358
+ end
359
+
360
+ it 'uses the correct key for the original file mime type' do
361
+ assert_match /.+\/5k.png/, @dummy.avatar.s3_object.key
362
+ end
363
+
364
+ it "returns a url containing the correct processed file mime type" do
365
+ assert_match /.+\/5k.jpg/, @dummy.avatar.url(:large)
366
+ end
367
+
368
+ it "uses the correct key for the processed file mime type" do
369
+ assert_match /.+\/5k.jpg/, @dummy.avatar.s3_object(:large).key
370
+ end
371
+ end
372
+
373
+ context "An attachment that uses S3 for storage and has a proc for styles" do
374
+ before do
375
+ rebuild_model (aws2_add_region).merge storage: :s3,
376
+ styles: lambda { |attachment| attachment.instance.counter
377
+ {thumbnail: { geometry: "50x50#",
378
+ s3_headers: {'Cache-Control' => 'max-age=31557600'}} }},
379
+ bucket: "bucket",
380
+ path: ":attachment/:style/:basename:dotextension",
381
+ s3_credentials: {
382
+ 'access_key_id' => "12345",
383
+ 'secret_access_key' => "54321"
384
+ }
385
+
386
+ @file = File.new(fixture_file('5k.png'), 'rb')
387
+
388
+ Dummy.class_eval do
389
+ def counter
390
+ @counter ||= 0
391
+ @counter += 1
392
+ @counter
393
+ end
394
+ end
395
+
396
+ @dummy = Dummy.new
397
+ @dummy.avatar = @file
398
+
399
+ object = stub
400
+ @dummy.avatar.stubs(:s3_object).with(:original).returns(object)
401
+ @dummy.avatar.stubs(:s3_object).with(:thumbnail).returns(object)
402
+
403
+ object.expects(:upload_file)
404
+ .with(anything, content_type: 'image/png',
405
+ acl: :"public-read")
406
+ object.expects(:upload_file)
407
+ .with(anything, content_type: 'image/png',
408
+ acl: :"public-read",
409
+ cache_control: 'max-age=31557600')
410
+ @dummy.save
411
+ end
412
+
413
+ after { @file.close }
414
+
415
+ it "succeeds" do
416
+ assert_equal @dummy.counter, 7
417
+ end
418
+ end
419
+
420
+ context "An attachment that uses S3 for storage and has styles" do
421
+ before do
422
+ rebuild_model(
423
+ (aws2_add_region).merge(
424
+ storage: :s3,
425
+ styles: { thumb: ["90x90#", :jpg] },
426
+ bucket: "bucket",
427
+ s3_credentials: {
428
+ "access_key_id" => "12345",
429
+ "secret_access_key" => "54321" }
430
+ )
431
+ )
432
+
433
+ @file = File.new(fixture_file("5k.png"), "rb")
434
+ @dummy = Dummy.new
435
+ @dummy.avatar = @file
436
+ @dummy.save
437
+ end
438
+
439
+ context "reprocess" do
440
+ before do
441
+ @object = stub
442
+ @dummy.avatar.stubs(:s3_object).with(:original).returns(@object)
443
+ @dummy.avatar.stubs(:s3_object).with(:thumb).returns(@object)
444
+ @object.stubs(:get).yields(@file.read)
445
+ @object.stubs(:exists?).returns(true)
446
+ end
447
+
448
+ it "uploads original" do
449
+ @object.expects(:upload_file).with(
450
+ anything,
451
+ content_type: "image/png",
452
+ acl: :"public-read").returns(true)
453
+ @object.expects(:upload_file).with(
454
+ anything,
455
+ content_type: "image/jpeg",
456
+ acl: :"public-read").returns(true)
457
+ @dummy.avatar.reprocess!
458
+ end
459
+
460
+ it "doesn't upload original" do
461
+ @object.expects(:upload_file).with(
462
+ anything,
463
+ content_type: "image/jpeg",
464
+ acl: :"public-read").returns(true)
465
+ @dummy.avatar.reprocess!(:thumb)
466
+ end
467
+ end
468
+
469
+ after { @file.close }
470
+ end
471
+
472
+ context "An attachment that uses S3 for storage and has spaces in file name" do
473
+ before do
474
+ rebuild_model(
475
+ (aws2_add_region).merge storage: :s3,
476
+ styles: { large: ["500x500#", :jpg] },
477
+ bucket: "bucket",
478
+ s3_credentials: { "access_key_id" => "12345",
479
+ "secret_access_key" => "54321" }
480
+ )
481
+
482
+ File.open(fixture_file("spaced file.png"), "rb") do |file|
483
+ @dummy = Dummy.new
484
+ @dummy.avatar = file
485
+ @dummy.stubs(:new_record?).returns(false)
486
+ end
487
+ end
488
+
489
+ it "returns a replaced version for path" do
490
+ assert_match /.+\/spaced_file\.png/, @dummy.avatar.path
491
+ end
492
+
493
+ it "returns a replaced version for url" do
494
+ assert_match /.+\/spaced_file\.png/, @dummy.avatar.url
495
+ end
496
+ end
497
+
498
+ context "An attachment that uses S3 for storage and has a question mark in file name" do
499
+ before do
500
+ rebuild_model (aws2_add_region).merge storage: :s3,
501
+ styles: { large: ['500x500#', :jpg] },
502
+ bucket: "bucket",
503
+ s3_credentials: {
504
+ 'access_key_id' => "12345",
505
+ 'secret_access_key' => "54321"
506
+ }
507
+
508
+ stringio = stringy_file
509
+ class << stringio
510
+ def original_filename
511
+ "question?mark.png"
512
+ end
513
+ end
514
+ file = Paperclip.io_adapters.for(stringio, hash_digest: Digest::MD5)
515
+ @dummy = Dummy.new
516
+ @dummy.avatar = file
517
+ @dummy.save
518
+ @dummy.stubs(:new_record?).returns(false)
519
+ end
520
+
521
+ it "returns a replaced version for path" do
522
+ assert_match /.+\/question_mark\.png/, @dummy.avatar.path
523
+ end
524
+
525
+ it "returns a replaced version for url" do
526
+ assert_match /.+\/question_mark\.png/, @dummy.avatar.url
527
+ end
528
+ end
529
+
530
+ context "" do
531
+ before do
532
+ rebuild_model (aws2_add_region).merge storage: :s3,
533
+ s3_credentials: {},
534
+ bucket: "bucket",
535
+ path: ":attachment/:basename:dotextension",
536
+ url: ":s3_domain_url"
537
+ @dummy = Dummy.new
538
+ @dummy.avatar = stringy_file
539
+ @dummy.stubs(:new_record?).returns(false)
540
+ end
541
+
542
+ it "returns a url based on an S3 subdomain" do
543
+ assert_match %r{^//bucket.s3.amazonaws.com/avatars/data[^\.]}, @dummy.avatar.url
544
+ end
545
+ end
546
+
547
+ context "" do
548
+ before do
549
+ rebuild_model(
550
+ (aws2_add_region).merge storage: :s3,
551
+ s3_credentials: {
552
+ production: { bucket: "prod_bucket" },
553
+ development: { bucket: "dev_bucket" }
554
+ },
555
+ bucket: "bucket",
556
+ s3_host_alias: "something.something.com",
557
+ path: ":attachment/:basename:dotextension",
558
+ url: ":s3_alias_url"
559
+ )
560
+ @dummy = Dummy.new
561
+ @dummy.avatar = stringy_file
562
+ @dummy.stubs(:new_record?).returns(false)
563
+ end
564
+
565
+ it "returns a url based on the host_alias" do
566
+ assert_match %r{^//something.something.com/avatars/data[^\.]}, @dummy.avatar.url
567
+ end
568
+ end
569
+
570
+ context "generating a url with a prefixed host alias" do
571
+ before do
572
+ rebuild_model(
573
+ aws2_add_region.merge(
574
+ storage: :s3,
575
+ s3_credentials: {
576
+ production: { bucket: "prod_bucket" },
577
+ development: { bucket: "dev_bucket" },
578
+ },
579
+ bucket: "bucket",
580
+ s3_host_alias: "something.something.com",
581
+ s3_prefixes_in_alias: 2,
582
+ path: "prefix1/prefix2/:attachment/:basename:dotextension",
583
+ url: ":s3_alias_url",
584
+ )
585
+ )
586
+ @dummy = Dummy.new
587
+ @dummy.avatar = stringy_file
588
+ @dummy.stubs(:new_record?).returns(false)
589
+ end
590
+
591
+ it "returns a url with the prefixes removed" do
592
+ assert_match %r{^//something.something.com/avatars/data[^\.]},
593
+ @dummy.avatar.url
594
+ end
595
+ end
596
+
597
+ context "generating a url with a proc as the host alias" do
598
+ before do
599
+ rebuild_model (aws2_add_region).merge storage: :s3,
600
+ s3_credentials: { bucket: "prod_bucket" },
601
+ s3_host_alias: Proc.new{|atch| "cdn#{atch.instance.counter % 4}.example.com"},
602
+ path: ":attachment/:basename:dotextension",
603
+ url: ":s3_alias_url"
604
+ Dummy.class_eval do
605
+ def counter
606
+ @counter ||= 0
607
+ @counter += 1
608
+ @counter
609
+ end
610
+ end
611
+ @dummy = Dummy.new
612
+ @dummy.avatar = stringy_file
613
+ @dummy.stubs(:new_record?).returns(false)
614
+ end
615
+
616
+ it "returns a url based on the host_alias" do
617
+ assert_match %r{^//cdn1.example.com/avatars/data[^\.]}, @dummy.avatar.url
618
+ assert_match %r{^//cdn2.example.com/avatars/data[^\.]}, @dummy.avatar.url
619
+ end
620
+
621
+ it "still returns the bucket name" do
622
+ assert_equal "prod_bucket", @dummy.avatar.bucket_name
623
+ end
624
+
625
+ end
626
+
627
+ context "" do
628
+ before do
629
+ rebuild_model (aws2_add_region).merge storage: :s3,
630
+ s3_credentials: {},
631
+ bucket: "bucket",
632
+ path: ":attachment/:basename:dotextension",
633
+ url: ":asset_host"
634
+ @dummy = Dummy.new
635
+ @dummy.avatar = stringy_file
636
+ @dummy.stubs(:new_record?).returns(false)
637
+ end
638
+
639
+ it "returns a relative URL for Rails to calculate assets host" do
640
+ assert_match %r{^avatars/data[^\.]}, @dummy.avatar.url
641
+ end
642
+
643
+ end
644
+
645
+ context "Generating a secure url with an expiration" do
646
+ before do
647
+ @build_model_with_options = lambda {|options|
648
+ base_options = {
649
+ storage: :s3,
650
+ s3_credentials: {
651
+ production: { bucket: "prod_bucket" },
652
+ development: { bucket: "dev_bucket" }
653
+ },
654
+ s3_host_alias: "something.something.com",
655
+ s3_permissions: "private",
656
+ path: ":attachment/:basename:dotextension",
657
+ url: ":s3_alias_url"
658
+ }
659
+
660
+ rebuild_model (aws2_add_region).merge base_options.merge(options)
661
+ }
662
+ end
663
+
664
+ it "uses default options" do
665
+ @build_model_with_options[{}]
666
+
667
+ rails_env("production") do
668
+ @dummy = Dummy.new
669
+ @dummy.avatar = stringy_file
670
+
671
+ object = stub
672
+ @dummy.avatar.stubs(:s3_object).returns(object)
673
+
674
+ object.expects(:presigned_url).with(:get, expires_in: 3600)
675
+ @dummy.avatar.expiring_url
676
+ end
677
+ end
678
+
679
+ it "allows overriding s3_url_options" do
680
+ @build_model_with_options[s3_url_options: { response_content_disposition: "inline" }]
681
+
682
+ rails_env("production") do
683
+ @dummy = Dummy.new
684
+ @dummy.avatar = stringy_file
685
+
686
+ object = stub
687
+ @dummy.avatar.stubs(:s3_object).returns(object)
688
+ object.expects(:presigned_url)
689
+ .with(:get, expires_in: 3600,
690
+ response_content_disposition: "inline")
691
+ @dummy.avatar.expiring_url
692
+ end
693
+ end
694
+
695
+ it "allows overriding s3_object options with a proc" do
696
+ @build_model_with_options[s3_url_options: lambda {|attachment| { response_content_type: attachment.avatar_content_type } }]
697
+
698
+ rails_env("production") do
699
+ @dummy = Dummy.new
700
+
701
+ @file = stringy_file
702
+ @file.stubs(:original_filename).returns("5k.png\n\n")
703
+ Paperclip.stubs(:run).returns('image/png')
704
+ @file.stubs(:content_type).returns("image/png\n\n")
705
+ @file.stubs(:to_tempfile).returns(@file)
706
+
707
+ @dummy.avatar = @file
708
+
709
+ object = stub
710
+ @dummy.avatar.stubs(:s3_object).returns(object)
711
+ object.expects(:presigned_url)
712
+ .with(:get, expires_in: 3600, response_content_type: "image/png")
713
+ @dummy.avatar.expiring_url
714
+ end
715
+ end
716
+ end
717
+
718
+ context "#expiring_url" do
719
+ before { @dummy = Dummy.new }
720
+
721
+ context "with no attachment" do
722
+ before { assert(!@dummy.avatar.exists?) }
723
+
724
+ it "returns the default URL" do
725
+ assert_equal(@dummy.avatar.url, @dummy.avatar.expiring_url)
726
+ end
727
+
728
+ it 'generates a url for a style when a file does not exist' do
729
+ assert_equal(@dummy.avatar.url(:thumb), @dummy.avatar.expiring_url(3600, :thumb))
730
+ end
731
+ end
732
+
733
+ it "generates the same url when using Times and Integer offsets" do
734
+ assert_equal @dummy.avatar.expiring_url(1234), @dummy.avatar.expiring_url(Time.now + 1234)
735
+ end
736
+ end
737
+
738
+ context "Generating a url with an expiration for each style" do
739
+ before do
740
+ rebuild_model (aws2_add_region).merge storage: :s3,
741
+ s3_credentials: {
742
+ production: { bucket: "prod_bucket" },
743
+ development: { bucket: "dev_bucket" }
744
+ },
745
+ s3_permissions: :private,
746
+ s3_host_alias: "something.something.com",
747
+ path: ":attachment/:style/:basename:dotextension",
748
+ url: ":s3_alias_url"
749
+
750
+ rails_env("production") do
751
+ @dummy = Dummy.new
752
+ @dummy.avatar = stringy_file
753
+ end
754
+ end
755
+
756
+ it "generates a url for the thumb" do
757
+ object = stub
758
+ @dummy.avatar.stubs(:s3_object).with(:thumb).returns(object)
759
+ object.expects(:presigned_url).with(:get, expires_in: 1800)
760
+ @dummy.avatar.expiring_url(1800, :thumb)
761
+ end
762
+
763
+ it "generates a url for the default style" do
764
+ object = stub
765
+ @dummy.avatar.stubs(:s3_object).with(:original).returns(object)
766
+ object.expects(:presigned_url).with(:get, expires_in: 1800)
767
+ @dummy.avatar.expiring_url(1800)
768
+ end
769
+ end
770
+
771
+ context "Parsing S3 credentials with a bucket in them" do
772
+ before do
773
+ rebuild_model (aws2_add_region).merge storage: :s3,
774
+ s3_credentials: {
775
+ production: { bucket: "prod_bucket" },
776
+ development: { bucket: "dev_bucket" }
777
+ }
778
+ @dummy = Dummy.new
779
+ end
780
+
781
+ it "gets the right bucket in production" do
782
+ rails_env("production") do
783
+ assert_equal "prod_bucket", @dummy.avatar.bucket_name
784
+ assert_equal "prod_bucket", @dummy.avatar.s3_bucket.name
785
+ end
786
+ end
787
+
788
+ it "gets the right bucket in development" do
789
+ rails_env("development") do
790
+ assert_equal "dev_bucket", @dummy.avatar.bucket_name
791
+ assert_equal "dev_bucket", @dummy.avatar.s3_bucket.name
792
+ end
793
+ end
794
+ end
795
+
796
+ # the bucket.name is determined by the :s3_region
797
+ context "Parsing S3 credentials with a s3_host_name in them" do
798
+ before do
799
+ rebuild_model storage: :s3,
800
+ bucket: 'testing',
801
+ s3_credentials: {
802
+ production: {
803
+ s3_region: "world-end",
804
+ s3_host_name: "s3-world-end.amazonaws.com" },
805
+ development: {
806
+ s3_region: "ap-northeast-1",
807
+ s3_host_name: "s3-ap-northeast-1.amazonaws.com" },
808
+ test: {
809
+ s3_region: "" }
810
+ }
811
+ @dummy = Dummy.new
812
+ end
813
+
814
+ it "gets the right s3_host_name in production" do
815
+ rails_env("production") do
816
+ assert_match %r{^s3-world-end.amazonaws.com}, @dummy.avatar.s3_host_name
817
+ assert_match %r{^s3.world-end.amazonaws.com},
818
+ @dummy.avatar.s3_bucket.client.config.endpoint.host
819
+ end
820
+ end
821
+
822
+ it "gets the right s3_host_name in development" do
823
+ rails_env("development") do
824
+ assert_match %r{^s3.ap-northeast-1.amazonaws.com},
825
+ @dummy.avatar.s3_host_name
826
+ assert_match %r{^s3.ap-northeast-1.amazonaws.com},
827
+ @dummy.avatar.s3_bucket.client.config.endpoint.host
828
+ end
829
+ end
830
+
831
+ it "gets the right s3_host_name if the key does not exist" do
832
+ rails_env("test") do
833
+ assert_match %r{^s3.amazonaws.com}, @dummy.avatar.s3_host_name
834
+ assert_raises(Aws::Errors::MissingRegionError) do
835
+ @dummy.avatar.s3_bucket.client.config.endpoint.host
836
+ end
837
+ end
838
+ end
839
+ end
840
+
841
+ context "An attachment with S3 storage" do
842
+ before do
843
+ rebuild_model (aws2_add_region).merge storage: :s3,
844
+ bucket: "testing",
845
+ path: ":attachment/:style/:basename:dotextension",
846
+ s3_credentials: {
847
+ access_key_id: "12345",
848
+ secret_access_key: "54321"
849
+ }
850
+ end
851
+
852
+ it "is extended by the S3 module" do
853
+ assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3)
854
+ end
855
+
856
+ it "won't be extended by the Filesystem module" do
857
+ assert ! Dummy.new.avatar.is_a?(Paperclip::Storage::Filesystem)
858
+ end
859
+
860
+ context "when assigned" do
861
+ before do
862
+ @file = File.new(fixture_file('5k.png'), 'rb')
863
+ @dummy = Dummy.new
864
+ @dummy.avatar = @file
865
+ @dummy.stubs(:new_record?).returns(false)
866
+ end
867
+
868
+ after { @file.close }
869
+
870
+ it "does not get a bucket to get a URL" do
871
+ @dummy.avatar.expects(:s3).never
872
+ @dummy.avatar.expects(:s3_bucket).never
873
+ assert_match %r{^//s3\.amazonaws\.com/testing/avatars/original/5k\.png}, @dummy.avatar.url
874
+ end
875
+
876
+ it "is rewound after flush_writes" do
877
+ @dummy.avatar.instance_eval "def after_flush_writes; end"
878
+ @dummy.avatar.stubs(:s3_object).returns(stub(upload_file: true))
879
+ files = @dummy.avatar.queued_for_write.values.each(&:read)
880
+ @dummy.save
881
+ assert files.none?(&:eof?), "Expect all the files to be rewound."
882
+ end
883
+
884
+ it "is removed after after_flush_writes" do
885
+ @dummy.avatar.stubs(:s3_object).returns(stub(upload_file: true))
886
+ paths = @dummy.avatar.queued_for_write.values.map(&:path)
887
+ @dummy.save
888
+ assert paths.none?{ |path| File.exist?(path) },
889
+ "Expect all the files to be deleted."
890
+ end
891
+
892
+ it "will retry to save again but back off on SlowDown" do
893
+ @dummy.avatar.stubs(:sleep)
894
+ Aws::S3::Object.any_instance.stubs(:upload_file).
895
+ raises(Aws::S3::Errors::SlowDown.new(stub,
896
+ stub(status: 503, body: "")))
897
+ expect {@dummy.save}.to raise_error(Aws::S3::Errors::SlowDown)
898
+ expect(@dummy.avatar).to have_received(:sleep).with(1)
899
+ expect(@dummy.avatar).to have_received(:sleep).with(2)
900
+ expect(@dummy.avatar).to have_received(:sleep).with(4)
901
+ expect(@dummy.avatar).to have_received(:sleep).with(8)
902
+ expect(@dummy.avatar).to have_received(:sleep).with(16)
903
+ end
904
+
905
+ context "and saved" do
906
+ before do
907
+ object = stub
908
+ @dummy.avatar.stubs(:s3_object).returns(object)
909
+ object.expects(:upload_file)
910
+ .with(anything, content_type: 'image/png', acl: :"public-read")
911
+ @dummy.save
912
+ end
913
+
914
+ it "succeeds" do
915
+ assert true
916
+ end
917
+ end
918
+
919
+ context "and saved without a bucket" do
920
+ before do
921
+ Aws::S3::Bucket.any_instance.expects(:create)
922
+ Aws::S3::Object.any_instance.stubs(:upload_file).
923
+ raises(Aws::S3::Errors::NoSuchBucket
924
+ .new(stub,
925
+ stub(status: 404, body: "<foo/>"))).then.returns(nil)
926
+ @dummy.save
927
+ end
928
+
929
+ it "succeeds" do
930
+ assert true
931
+ end
932
+ end
933
+
934
+ context "and remove" do
935
+ before do
936
+ Aws::S3::Object.any_instance.stubs(:exists?).returns(true)
937
+ Aws::S3::Object.any_instance.stubs(:delete)
938
+ @dummy.destroy
939
+ end
940
+
941
+ it "succeeds" do
942
+ assert true
943
+ end
944
+ end
945
+
946
+ context 'that the file were missing' do
947
+ before do
948
+ Aws::S3::Object.any_instance.stubs(:exists?)
949
+ .raises(Aws::S3::Errors::ServiceError.new("rspec stub raises",
950
+ "object exists?"))
951
+ end
952
+
953
+ it 'returns false on exists?' do
954
+ assert !@dummy.avatar.exists?
955
+ end
956
+ end
957
+ end
958
+ end
959
+
960
+ context "An attachment with S3 storage and bucket defined as a Proc" do
961
+ before do
962
+ rebuild_model (aws2_add_region).merge storage: :s3,
963
+ bucket: lambda { |attachment| "bucket_#{attachment.instance.other}" },
964
+ s3_credentials: {not: :important}
965
+ end
966
+
967
+ it "gets the right bucket name" do
968
+ assert "bucket_a", Dummy.new(other: 'a').avatar.bucket_name
969
+ assert "bucket_a", Dummy.new(other: 'a').avatar.s3_bucket.name
970
+ assert "bucket_b", Dummy.new(other: 'b').avatar.bucket_name
971
+ assert "bucket_b", Dummy.new(other: 'b').avatar.s3_bucket.name
972
+ end
973
+ end
974
+
975
+ context "An attachment with S3 storage and S3 credentials defined as a Proc" do
976
+ before do
977
+ rebuild_model (aws2_add_region).merge storage: :s3,
978
+ bucket: {not: :important},
979
+ s3_credentials: lambda { |attachment|
980
+ Hash['access_key_id' => "access#{attachment.instance.other}", 'secret_access_key' => "secret#{attachment.instance.other}"]
981
+ }
982
+ end
983
+
984
+ it "gets the right credentials" do
985
+ assert "access1234", Dummy.new(other: '1234').avatar.s3_credentials[:access_key_id]
986
+ assert "secret1234", Dummy.new(other: '1234').avatar.s3_credentials[:secret_access_key]
987
+ end
988
+ end
989
+
990
+ context "An attachment with S3 storage and S3 credentials with a :credential_provider" do
991
+ before do
992
+ class DummyCredentialProvider; end
993
+
994
+ rebuild_model (aws2_add_region).merge storage: :s3,
995
+ bucket: "testing",
996
+ s3_credentials: {
997
+ credentials: DummyCredentialProvider.new
998
+ }
999
+ @dummy = Dummy.new
1000
+ end
1001
+
1002
+ it "sets the credential-provider" do
1003
+ expect(@dummy.avatar.s3_bucket.client.config.credentials).to be_a DummyCredentialProvider
1004
+ end
1005
+ end
1006
+
1007
+ context "An attachment with S3 storage and S3 credentials in an unsupported manor" do
1008
+ before do
1009
+ rebuild_model (aws2_add_region).merge storage: :s3,
1010
+ bucket: "testing", s3_credentials: ["unsupported"]
1011
+ @dummy = Dummy.new
1012
+ end
1013
+
1014
+ it "does not accept the credentials" do
1015
+ assert_raises(ArgumentError) do
1016
+ @dummy.avatar.s3_credentials
1017
+ end
1018
+ end
1019
+ end
1020
+
1021
+ context "An attachment with S3 storage and S3 credentials not supplied" do
1022
+ before do
1023
+ rebuild_model (aws2_add_region).merge storage: :s3, bucket: "testing"
1024
+ @dummy = Dummy.new
1025
+ end
1026
+
1027
+ it "does not parse any credentials" do
1028
+ assert_equal({}, @dummy.avatar.s3_credentials)
1029
+ end
1030
+ end
1031
+
1032
+ context "An attachment with S3 storage and specific s3 headers set" do
1033
+ before do
1034
+ rebuild_model (aws2_add_region).merge storage: :s3,
1035
+ bucket: "testing",
1036
+ path: ":attachment/:style/:basename:dotextension",
1037
+ s3_credentials: {
1038
+ 'access_key_id' => "12345",
1039
+ 'secret_access_key' => "54321"
1040
+ },
1041
+ s3_headers: {'Cache-Control' => 'max-age=31557600'}
1042
+ end
1043
+
1044
+ context "when assigned" do
1045
+ before do
1046
+ @file = File.new(fixture_file('5k.png'), 'rb')
1047
+ @dummy = Dummy.new
1048
+ @dummy.avatar = @file
1049
+ end
1050
+
1051
+ after { @file.close }
1052
+
1053
+ context "and saved" do
1054
+ before do
1055
+ object = stub
1056
+ @dummy.avatar.stubs(:s3_object).returns(object)
1057
+
1058
+ object.expects(:upload_file)
1059
+ .with(anything,
1060
+ content_type: 'image/png',
1061
+ acl: :"public-read",
1062
+ cache_control: 'max-age=31557600')
1063
+ @dummy.save
1064
+ end
1065
+
1066
+ it "succeeds" do
1067
+ assert true
1068
+ end
1069
+ end
1070
+ end
1071
+ end
1072
+
1073
+ context "An attachment with S3 storage and metadata set using header names" do
1074
+ before do
1075
+ rebuild_model (aws2_add_region).merge storage: :s3,
1076
+ bucket: "testing",
1077
+ path: ":attachment/:style/:basename:dotextension",
1078
+ s3_credentials: {
1079
+ 'access_key_id' => "12345",
1080
+ 'secret_access_key' => "54321"
1081
+ },
1082
+ s3_headers: {'x-amz-meta-color' => 'red'}
1083
+ end
1084
+
1085
+ context "when assigned" do
1086
+ before do
1087
+ @file = File.new(fixture_file('5k.png'), 'rb')
1088
+ @dummy = Dummy.new
1089
+ @dummy.avatar = @file
1090
+ end
1091
+
1092
+ after { @file.close }
1093
+
1094
+ context "and saved" do
1095
+ before do
1096
+ object = stub
1097
+ @dummy.avatar.stubs(:s3_object).returns(object)
1098
+
1099
+ object.expects(:upload_file)
1100
+ .with(anything,
1101
+ content_type: 'image/png',
1102
+ acl: :"public-read",
1103
+ metadata: { "color" => "red" })
1104
+ @dummy.save
1105
+ end
1106
+
1107
+ it "succeeds" do
1108
+ assert true
1109
+ end
1110
+ end
1111
+ end
1112
+ end
1113
+
1114
+ context "An attachment with S3 storage and metadata set using the :s3_metadata option" do
1115
+ before do
1116
+ rebuild_model (aws2_add_region).merge storage: :s3,
1117
+ bucket: "testing",
1118
+ path: ":attachment/:style/:basename:dotextension",
1119
+ s3_credentials: {
1120
+ 'access_key_id' => "12345",
1121
+ 'secret_access_key' => "54321"
1122
+ },
1123
+ s3_metadata: { "color" => "red" }
1124
+ end
1125
+
1126
+ context "when assigned" do
1127
+ before do
1128
+ @file = File.new(fixture_file('5k.png'), 'rb')
1129
+ @dummy = Dummy.new
1130
+ @dummy.avatar = @file
1131
+ end
1132
+
1133
+ after { @file.close }
1134
+
1135
+ context "and saved" do
1136
+ before do
1137
+ object = stub
1138
+ @dummy.avatar.stubs(:s3_object).returns(object)
1139
+
1140
+ object.expects(:upload_file)
1141
+ .with(anything,
1142
+ content_type: 'image/png',
1143
+ acl: :"public-read",
1144
+ metadata: { "color" => "red" })
1145
+ @dummy.save
1146
+ end
1147
+
1148
+ it "succeeds" do
1149
+ assert true
1150
+ end
1151
+ end
1152
+ end
1153
+ end
1154
+
1155
+ context "An attachment with S3 storage and storage class set" do
1156
+ context "using the header name" do
1157
+ before do
1158
+ rebuild_model (aws2_add_region).merge storage: :s3,
1159
+ bucket: "testing",
1160
+ path: ":attachment/:style/:basename:dotextension",
1161
+ s3_credentials: {
1162
+ 'access_key_id' => "12345",
1163
+ 'secret_access_key' => "54321"
1164
+ },
1165
+ s3_headers: { "x-amz-storage-class" => "reduced_redundancy" }
1166
+ end
1167
+
1168
+ context "when assigned" do
1169
+ before do
1170
+ @file = File.new(fixture_file('5k.png'), 'rb')
1171
+ @dummy = Dummy.new
1172
+ @dummy.avatar = @file
1173
+ end
1174
+
1175
+ after { @file.close }
1176
+
1177
+ context "and saved" do
1178
+ before do
1179
+ object = stub
1180
+ @dummy.avatar.stubs(:s3_object).returns(object)
1181
+
1182
+ object.expects(:upload_file)
1183
+ .with(anything,
1184
+ content_type: 'image/png',
1185
+ acl: :"public-read",
1186
+ storage_class: "reduced_redundancy")
1187
+ @dummy.save
1188
+ end
1189
+
1190
+ it "succeeds" do
1191
+ assert true
1192
+ end
1193
+ end
1194
+ end
1195
+ end
1196
+
1197
+ context "using per style hash" do
1198
+ before do
1199
+ rebuild_model (aws2_add_region).merge :storage => :s3,
1200
+ :bucket => "testing",
1201
+ :path => ":attachment/:style/:basename.:extension",
1202
+ :styles => {
1203
+ :thumb => "80x80>"
1204
+ },
1205
+ :s3_credentials => {
1206
+ 'access_key_id' => "12345",
1207
+ 'secret_access_key' => "54321"
1208
+ },
1209
+ :s3_storage_class => {
1210
+ :thumb => :reduced_redundancy
1211
+ }
1212
+ end
1213
+
1214
+ context "when assigned" do
1215
+ before do
1216
+ @file = File.new(fixture_file('5k.png'), 'rb')
1217
+ @dummy = Dummy.new
1218
+ @dummy.avatar = @file
1219
+ end
1220
+
1221
+ after { @file.close }
1222
+
1223
+ context "and saved" do
1224
+ before do
1225
+ object = stub
1226
+ [:thumb, :original].each do |style|
1227
+ @dummy.avatar.stubs(:s3_object).with(style).returns(object)
1228
+
1229
+ expected_options = {
1230
+ :content_type => "image/png",
1231
+ acl: :"public-read"
1232
+ }
1233
+ expected_options.merge!(:storage_class => :reduced_redundancy) if style == :thumb
1234
+
1235
+ object.expects(:upload_file)
1236
+ .with(anything, expected_options)
1237
+ end
1238
+ @dummy.save
1239
+ end
1240
+
1241
+ it "succeeds" do
1242
+ assert true
1243
+ end
1244
+ end
1245
+ end
1246
+ end
1247
+
1248
+ context "using global hash option" do
1249
+ before do
1250
+ rebuild_model (aws2_add_region).merge :storage => :s3,
1251
+ :bucket => "testing",
1252
+ :path => ":attachment/:style/:basename.:extension",
1253
+ :styles => {
1254
+ :thumb => "80x80>"
1255
+ },
1256
+ :s3_credentials => {
1257
+ 'access_key_id' => "12345",
1258
+ 'secret_access_key' => "54321"
1259
+ },
1260
+ :s3_storage_class => :reduced_redundancy
1261
+ end
1262
+
1263
+ context "when assigned" do
1264
+ before do
1265
+ @file = File.new(fixture_file('5k.png'), 'rb')
1266
+ @dummy = Dummy.new
1267
+ @dummy.avatar = @file
1268
+ end
1269
+
1270
+ after { @file.close }
1271
+
1272
+ context "and saved" do
1273
+ before do
1274
+ object = stub
1275
+ [:thumb, :original].each do |style|
1276
+ @dummy.avatar.stubs(:s3_object).with(style).returns(object)
1277
+
1278
+ object.expects(:upload_file)
1279
+ .with(anything, :content_type => "image/png",
1280
+ acl: :"public-read",
1281
+ :storage_class => :reduced_redundancy)
1282
+ end
1283
+ @dummy.save
1284
+ end
1285
+
1286
+ it "succeeds" do
1287
+ assert true
1288
+ end
1289
+ end
1290
+ end
1291
+ end
1292
+ end
1293
+
1294
+ context "Can disable AES256 encryption multiple ways" do
1295
+ [nil, false, ''].each do |tech|
1296
+ before do
1297
+ rebuild_model(
1298
+ (aws2_add_region).merge storage: :s3,
1299
+ bucket: "testing",
1300
+ path: ":attachment/:style/:basename:dotextension",
1301
+ s3_credentials: {
1302
+ 'access_key_id' => "12345",
1303
+ 'secret_access_key' => "54321"},
1304
+ s3_server_side_encryption: tech)
1305
+ end
1306
+
1307
+ context "when assigned" do
1308
+ before do
1309
+ @file = File.new(fixture_file('5k.png'), 'rb')
1310
+ @dummy = Dummy.new
1311
+ @dummy.avatar = @file
1312
+ end
1313
+
1314
+ after { @file.close }
1315
+
1316
+ context "and saved" do
1317
+ before do
1318
+ object = stub
1319
+ @dummy.avatar.stubs(:s3_object).returns(object)
1320
+
1321
+ object.expects(:upload_file)
1322
+ .with(anything, :content_type => "image/png", acl: :"public-read")
1323
+ @dummy.save
1324
+ end
1325
+
1326
+ it "succeeds" do
1327
+ assert true
1328
+ end
1329
+ end
1330
+ end
1331
+ end
1332
+ end
1333
+
1334
+ context "An attachment with S3 storage and using AES256 encryption" do
1335
+ before do
1336
+ rebuild_model (aws2_add_region).merge storage: :s3,
1337
+ bucket: "testing",
1338
+ path: ":attachment/:style/:basename:dotextension",
1339
+ s3_credentials: {
1340
+ 'access_key_id' => "12345",
1341
+ 'secret_access_key' => "54321"
1342
+ },
1343
+ s3_server_side_encryption: "AES256"
1344
+ end
1345
+
1346
+ context "when assigned" do
1347
+ before do
1348
+ @file = File.new(fixture_file('5k.png'), 'rb')
1349
+ @dummy = Dummy.new
1350
+ @dummy.avatar = @file
1351
+ end
1352
+
1353
+ after { @file.close }
1354
+
1355
+ context "and saved" do
1356
+ before do
1357
+ object = stub
1358
+ @dummy.avatar.stubs(:s3_object).returns(object)
1359
+
1360
+ object.expects(:upload_file)
1361
+ .with(anything, content_type: "image/png",
1362
+ acl: :"public-read",
1363
+ server_side_encryption: "AES256")
1364
+ @dummy.save
1365
+ end
1366
+
1367
+ it "succeeds" do
1368
+ assert true
1369
+ end
1370
+ end
1371
+ end
1372
+ end
1373
+
1374
+ context "An attachment with S3 storage and storage class set using the :storage_class option" do
1375
+ before do
1376
+ rebuild_model (aws2_add_region).merge storage: :s3,
1377
+ bucket: "testing",
1378
+ path: ":attachment/:style/:basename:dotextension",
1379
+ s3_credentials: {
1380
+ 'access_key_id' => "12345",
1381
+ 'secret_access_key' => "54321"
1382
+ },
1383
+ s3_storage_class: :reduced_redundancy
1384
+ end
1385
+
1386
+ context "when assigned" do
1387
+ before do
1388
+ @file = File.new(fixture_file('5k.png'), 'rb')
1389
+ @dummy = Dummy.new
1390
+ @dummy.avatar = @file
1391
+ end
1392
+
1393
+ after { @file.close }
1394
+
1395
+ context "and saved" do
1396
+ before do
1397
+ object = stub
1398
+ @dummy.avatar.stubs(:s3_object).returns(object)
1399
+
1400
+ object.expects(:upload_file)
1401
+ .with(anything,
1402
+ content_type: "image/png",
1403
+ acl: :"public-read",
1404
+ storage_class: :reduced_redundancy)
1405
+ @dummy.save
1406
+ end
1407
+
1408
+ it "succeeds" do
1409
+ assert true
1410
+ end
1411
+ end
1412
+ end
1413
+ end
1414
+
1415
+ context "with S3 credentials supplied as Pathname" do
1416
+ before do
1417
+ ENV['S3_KEY'] = 'pathname_key'
1418
+ ENV['S3_BUCKET'] = 'pathname_bucket'
1419
+ ENV['S3_SECRET'] = 'pathname_secret'
1420
+
1421
+ rails_env('test') do
1422
+ rebuild_model (aws2_add_region).merge storage: :s3,
1423
+ s3_credentials: Pathname.new(fixture_file('s3.yml'))
1424
+
1425
+ Dummy.delete_all
1426
+ @dummy = Dummy.new
1427
+ end
1428
+ end
1429
+
1430
+ it "parses the credentials" do
1431
+ assert_equal 'pathname_bucket', @dummy.avatar.bucket_name
1432
+
1433
+ assert_equal 'pathname_key',
1434
+ @dummy.avatar.s3_bucket.client.config.access_key_id
1435
+
1436
+ assert_equal 'pathname_secret',
1437
+ @dummy.avatar.s3_bucket.client.config.secret_access_key
1438
+ end
1439
+ end
1440
+
1441
+ context "with S3 credentials in a YAML file" do
1442
+ before do
1443
+ ENV['S3_KEY'] = 'env_key'
1444
+ ENV['S3_BUCKET'] = 'env_bucket'
1445
+ ENV['S3_SECRET'] = 'env_secret'
1446
+
1447
+ rails_env('test') do
1448
+ rebuild_model (aws2_add_region).merge storage: :s3,
1449
+ s3_credentials: File.new(fixture_file('s3.yml'))
1450
+
1451
+ Dummy.delete_all
1452
+
1453
+ @dummy = Dummy.new
1454
+ end
1455
+ end
1456
+
1457
+ it "runs the file through ERB" do
1458
+ assert_equal 'env_bucket', @dummy.avatar.bucket_name
1459
+
1460
+ assert_equal 'env_key',
1461
+ @dummy.avatar.s3_bucket.client.config.access_key_id
1462
+
1463
+ assert_equal 'env_secret',
1464
+ @dummy.avatar.s3_bucket.client.config.secret_access_key
1465
+ end
1466
+ end
1467
+
1468
+ context "S3 Permissions" do
1469
+ context "defaults to :public_read" do
1470
+ before do
1471
+ rebuild_model (aws2_add_region).merge storage: :s3,
1472
+ bucket: "testing",
1473
+ path: ":attachment/:style/:basename:dotextension",
1474
+ s3_credentials: {
1475
+ 'access_key_id' => "12345",
1476
+ 'secret_access_key' => "54321"
1477
+ }
1478
+ end
1479
+
1480
+ context "when assigned" do
1481
+ before do
1482
+ @file = File.new(fixture_file('5k.png'), 'rb')
1483
+ @dummy = Dummy.new
1484
+ @dummy.avatar = @file
1485
+ end
1486
+
1487
+ after { @file.close }
1488
+
1489
+ context "and saved" do
1490
+ before do
1491
+ object = stub
1492
+ @dummy.avatar.stubs(:s3_object).returns(object)
1493
+
1494
+ object.expects(:upload_file)
1495
+ .with(anything, content_type: "image/png", acl: :"public-read")
1496
+ @dummy.save
1497
+ end
1498
+
1499
+ it "succeeds" do
1500
+ assert true
1501
+ end
1502
+ end
1503
+ end
1504
+ end
1505
+
1506
+ context "string permissions set" do
1507
+ before do
1508
+ rebuild_model (aws2_add_region).merge storage: :s3,
1509
+ bucket: "testing",
1510
+ path: ":attachment/:style/:basename:dotextension",
1511
+ s3_credentials: {
1512
+ 'access_key_id' => "12345",
1513
+ 'secret_access_key' => "54321"
1514
+ },
1515
+ s3_permissions: :private
1516
+ end
1517
+
1518
+ context "when assigned" do
1519
+ before do
1520
+ @file = File.new(fixture_file('5k.png'), 'rb')
1521
+ @dummy = Dummy.new
1522
+ @dummy.avatar = @file
1523
+ end
1524
+
1525
+ after { @file.close }
1526
+
1527
+ context "and saved" do
1528
+ before do
1529
+ object = stub
1530
+ @dummy.avatar.stubs(:s3_object).returns(object)
1531
+
1532
+ object.expects(:upload_file)
1533
+ .with(anything, content_type: "image/png", acl: :private)
1534
+ @dummy.save
1535
+ end
1536
+
1537
+ it "succeeds" do
1538
+ assert true
1539
+ end
1540
+ end
1541
+ end
1542
+ end
1543
+
1544
+ context "hash permissions set" do
1545
+ before do
1546
+ rebuild_model (aws2_add_region).merge storage: :s3,
1547
+ bucket: "testing",
1548
+ path: ":attachment/:style/:basename:dotextension",
1549
+ styles: {
1550
+ thumb: "80x80>"
1551
+ },
1552
+ s3_credentials: {
1553
+ 'access_key_id' => "12345",
1554
+ 'secret_access_key' => "54321"
1555
+ },
1556
+ s3_permissions: {
1557
+ original: :private,
1558
+ thumb: :public_read
1559
+ }
1560
+ end
1561
+
1562
+ context "when assigned" do
1563
+ before do
1564
+ @file = File.new(fixture_file('5k.png'), 'rb')
1565
+ @dummy = Dummy.new
1566
+ @dummy.avatar = @file
1567
+ end
1568
+
1569
+ after { @file.close }
1570
+
1571
+ context "and saved" do
1572
+ before do
1573
+ [:thumb, :original].each do |style|
1574
+ object = stub
1575
+ @dummy.avatar.stubs(:s3_object).with(style).returns(object)
1576
+
1577
+ object.expects(:upload_file)
1578
+ .with(anything,
1579
+ content_type: "image/png",
1580
+ acl: style == :thumb ? :public_read : :private)
1581
+ end
1582
+ @dummy.save
1583
+ end
1584
+
1585
+ it "succeeds" do
1586
+ assert true
1587
+ end
1588
+ end
1589
+ end
1590
+ end
1591
+
1592
+ context "proc permission set" do
1593
+ before do
1594
+ rebuild_model(
1595
+ (aws2_add_region).merge storage: :s3,
1596
+ bucket: "testing",
1597
+ path: ":attachment/:style/:basename:dotextension",
1598
+ styles: {
1599
+ thumb: "80x80>"
1600
+ },
1601
+ s3_credentials: {
1602
+ 'access_key_id' => "12345",
1603
+ 'secret_access_key' => "54321"
1604
+ },
1605
+ s3_permissions: lambda {|attachment, style|
1606
+ attachment.instance.private_attachment? && style.to_sym != :thumb ? :private : :"public-read"
1607
+ }
1608
+ )
1609
+ end
1610
+ end
1611
+ end
1612
+
1613
+ context "An attachment with S3 storage and metadata set using a proc as headers" do
1614
+ before do
1615
+ rebuild_model(
1616
+ (aws2_add_region).merge storage: :s3,
1617
+ bucket: "testing",
1618
+ path: ":attachment/:style/:basename:dotextension",
1619
+ styles: {
1620
+ thumb: "80x80>"
1621
+ },
1622
+ s3_credentials: {
1623
+ 'access_key_id' => "12345",
1624
+ 'secret_access_key' => "54321"
1625
+ },
1626
+ s3_headers: lambda {|attachment|
1627
+ {'Content-Disposition' => "attachment; filename=\"#{attachment.name}\""}
1628
+ }
1629
+ )
1630
+ end
1631
+
1632
+ context "when assigned" do
1633
+ before do
1634
+ @file = File.new(fixture_file('5k.png'), 'rb')
1635
+ @dummy = Dummy.new
1636
+ @dummy.stubs(name: 'Custom Avatar Name.png')
1637
+ @dummy.avatar = @file
1638
+ end
1639
+
1640
+ after { @file.close }
1641
+
1642
+ context "and saved" do
1643
+ before do
1644
+ [:thumb, :original].each do |style|
1645
+ object = stub
1646
+ @dummy.avatar.stubs(:s3_object).with(style).returns(object)
1647
+
1648
+ object.expects(:upload_file)
1649
+ .with(anything,
1650
+ content_type: "image/png",
1651
+ acl: :"public-read",
1652
+ content_disposition: 'attachment; filename="Custom Avatar Name.png"')
1653
+ end
1654
+ @dummy.save
1655
+ end
1656
+
1657
+ it "succeeds" do
1658
+ assert true
1659
+ end
1660
+ end
1661
+ end
1662
+ end
1663
+
1664
+ context "path is a proc" do
1665
+ before do
1666
+ rebuild_model (aws2_add_region).merge storage: :s3,
1667
+ path: ->(attachment) { attachment.instance.attachment_path }
1668
+
1669
+ @dummy = Dummy.new
1670
+ @dummy.class_eval do
1671
+ def attachment_path
1672
+ '/some/dynamic/path'
1673
+ end
1674
+ end
1675
+ @dummy.avatar = stringy_file
1676
+ end
1677
+
1678
+ it "returns a correct path" do
1679
+ assert_match '/some/dynamic/path', @dummy.avatar.path
1680
+ end
1681
+ end
1682
+
1683
+ private
1684
+
1685
+ def rails_env(env)
1686
+ stored_env, Rails.env = Rails.env, env
1687
+ begin
1688
+ yield
1689
+ ensure
1690
+ Rails.env = stored_env
1691
+ end
1692
+ end
1693
+ end