paperclip_jk 5.0.0.beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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