paperclip_jk 5.0.0.beta2

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