paperclip-youtube 2.3.8.1

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 (62) hide show
  1. data/LICENSE +26 -0
  2. data/README.md +91 -0
  3. data/Rakefile +80 -0
  4. data/generators/paperclip/USAGE +5 -0
  5. data/generators/paperclip/paperclip_generator.rb +27 -0
  6. data/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  7. data/init.rb +1 -0
  8. data/lib/generators/paperclip/USAGE +8 -0
  9. data/lib/generators/paperclip/paperclip_generator.rb +31 -0
  10. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  11. data/lib/paperclip.rb +378 -0
  12. data/lib/paperclip/attachment.rb +376 -0
  13. data/lib/paperclip/callback_compatability.rb +61 -0
  14. data/lib/paperclip/command_line.rb +86 -0
  15. data/lib/paperclip/geometry.rb +115 -0
  16. data/lib/paperclip/interpolations.rb +130 -0
  17. data/lib/paperclip/iostream.rb +45 -0
  18. data/lib/paperclip/matchers.rb +33 -0
  19. data/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
  20. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +75 -0
  21. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +54 -0
  22. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +95 -0
  23. data/lib/paperclip/processor.rb +58 -0
  24. data/lib/paperclip/railtie.rb +24 -0
  25. data/lib/paperclip/storage.rb +3 -0
  26. data/lib/paperclip/storage/filesystem.rb +73 -0
  27. data/lib/paperclip/storage/s3.rb +192 -0
  28. data/lib/paperclip/storage/youtube.rb +331 -0
  29. data/lib/paperclip/style.rb +90 -0
  30. data/lib/paperclip/thumbnail.rb +79 -0
  31. data/lib/paperclip/upfile.rb +55 -0
  32. data/lib/paperclip/version.rb +3 -0
  33. data/lib/tasks/paperclip.rake +72 -0
  34. data/rails/init.rb +2 -0
  35. data/shoulda_macros/paperclip.rb +118 -0
  36. data/test/attachment_test.rb +921 -0
  37. data/test/command_line_test.rb +138 -0
  38. data/test/database.yml +4 -0
  39. data/test/fixtures/12k.png +0 -0
  40. data/test/fixtures/50x50.png +0 -0
  41. data/test/fixtures/5k.png +0 -0
  42. data/test/fixtures/bad.png +1 -0
  43. data/test/fixtures/s3.yml +8 -0
  44. data/test/fixtures/text.txt +0 -0
  45. data/test/fixtures/twopage.pdf +0 -0
  46. data/test/fixtures/uppercase.PNG +0 -0
  47. data/test/geometry_test.rb +177 -0
  48. data/test/helper.rb +146 -0
  49. data/test/integration_test.rb +570 -0
  50. data/test/interpolations_test.rb +143 -0
  51. data/test/iostream_test.rb +71 -0
  52. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  53. data/test/matchers/validate_attachment_content_type_matcher_test.rb +47 -0
  54. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  55. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  56. data/test/paperclip_test.rb +301 -0
  57. data/test/processor_test.rb +10 -0
  58. data/test/storage_test.rb +386 -0
  59. data/test/style_test.rb +141 -0
  60. data/test/thumbnail_test.rb +227 -0
  61. data/test/upfile_test.rb +36 -0
  62. metadata +195 -0
@@ -0,0 +1,55 @@
1
+ module Paperclip
2
+ # The Upfile module is a convenience module for adding uploaded-file-type methods
3
+ # to the +File+ class. Useful for testing.
4
+ # user.avatar = File.new("test/test_avatar.jpg")
5
+ module Upfile
6
+
7
+ # Infer the MIME-type of the file from the extension.
8
+ def content_type
9
+ type = (self.path.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
10
+ case type
11
+ when %r"jp(e|g|eg)" then "image/jpeg"
12
+ when %r"tiff?" then "image/tiff"
13
+ when %r"png", "gif", "bmp" then "image/#{type}"
14
+ when "txt" then "text/plain"
15
+ when %r"html?" then "text/html"
16
+ when "js" then "application/js"
17
+ when "csv", "xml", "css" then "text/#{type}"
18
+ else
19
+ # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
20
+ content_type = (Paperclip.run("file", "-b --mime-type :file", :file => self.path).split(':').last.strip rescue "application/x-#{type}")
21
+ content_type = "application/x-#{type}" if content_type.match(/\(.*?\)/)
22
+ content_type
23
+ end
24
+ end
25
+
26
+ # Returns the file's normal name.
27
+ def original_filename
28
+ File.basename(self.path)
29
+ end
30
+
31
+ # Returns the size of the file.
32
+ def size
33
+ File.size(self)
34
+ end
35
+ end
36
+ end
37
+
38
+ if defined? StringIO
39
+ class StringIO
40
+ attr_accessor :original_filename, :content_type, :fingerprint
41
+ def original_filename
42
+ @original_filename ||= "stringio.txt"
43
+ end
44
+ def content_type
45
+ @content_type ||= "text/plain"
46
+ end
47
+ def fingerprint
48
+ @fingerprint ||= Digest::MD5.hexdigest(self.string)
49
+ end
50
+ end
51
+ end
52
+
53
+ class File #:nodoc:
54
+ include Paperclip::Upfile
55
+ end
@@ -0,0 +1,3 @@
1
+ module Paperclip
2
+ VERSION = "2.3.8.1" unless defined? Paperclip::VERSION
3
+ end
@@ -0,0 +1,72 @@
1
+ def obtain_class
2
+ class_name = ENV['CLASS'] || ENV['class']
3
+ raise "Must specify CLASS" unless class_name
4
+ class_name
5
+ end
6
+
7
+ def obtain_attachments(klass)
8
+ klass = Object.const_get(klass.to_s)
9
+ name = ENV['ATTACHMENT'] || ENV['attachment']
10
+ raise "Class #{klass.name} has no attachments specified" unless klass.respond_to?(:attachment_definitions)
11
+ if !name.blank? && klass.attachment_definitions.keys.include?(name)
12
+ [ name ]
13
+ else
14
+ klass.attachment_definitions.keys
15
+ end
16
+ end
17
+
18
+ namespace :paperclip do
19
+ desc "Refreshes both metadata and thumbnails."
20
+ task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
21
+
22
+ namespace :refresh do
23
+ desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT)."
24
+ task :thumbnails => :environment do
25
+ errors = []
26
+ klass = obtain_class
27
+ names = obtain_attachments(klass)
28
+ names.each do |name|
29
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
30
+ result = instance.send(name).reprocess!
31
+ errors << [instance.id, instance.errors] unless instance.errors.blank?
32
+ end
33
+ end
34
+ errors.each{|e| puts "#{e.first}: #{e.last.full_messages.inspect}" }
35
+ end
36
+
37
+ desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
38
+ task :metadata => :environment do
39
+ klass = obtain_class
40
+ names = obtain_attachments(klass)
41
+ names.each do |name|
42
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
43
+ if file = instance.send(name).to_file
44
+ instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
45
+ instance.send("#{name}_content_type=", file.content_type.strip)
46
+ instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
47
+ instance.save(false)
48
+ else
49
+ true
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ desc "Cleans out invalid attachments. Useful after you've added new validations."
57
+ task :clean => :environment do
58
+ klass = obtain_class
59
+ names = obtain_attachments(klass)
60
+ names.each do |name|
61
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
62
+ instance.send(name).send(:validate)
63
+ if instance.send(name).valid?
64
+ true
65
+ else
66
+ instance.send("#{name}=", nil)
67
+ instance.save
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'paperclip/railtie'
2
+ Paperclip::Railtie.insert
@@ -0,0 +1,118 @@
1
+ require 'paperclip/matchers'
2
+
3
+ module Paperclip
4
+ # =Paperclip Shoulda Macros
5
+ #
6
+ # These macros are intended for use with shoulda, and will be included into
7
+ # your tests automatically. All of the macros use the standard shoulda
8
+ # assumption that the name of the test is based on the name of the model
9
+ # you're testing (that is, UserTest is the test for the User model), and
10
+ # will load that class for testing purposes.
11
+ module Shoulda
12
+ include Matchers
13
+ # This will test whether you have defined your attachment correctly by
14
+ # checking for all the required fields exist after the definition of the
15
+ # attachment.
16
+ def should_have_attached_file name
17
+ klass = self.name.gsub(/Test$/, '').constantize
18
+ matcher = have_attached_file name
19
+ should matcher.description do
20
+ assert_accepts(matcher, klass)
21
+ end
22
+ end
23
+
24
+ # Tests for validations on the presence of the attachment.
25
+ def should_validate_attachment_presence name
26
+ klass = self.name.gsub(/Test$/, '').constantize
27
+ matcher = validate_attachment_presence name
28
+ should matcher.description do
29
+ assert_accepts(matcher, klass)
30
+ end
31
+ end
32
+
33
+ # Tests that you have content_type validations specified. There are two
34
+ # options, :valid and :invalid. Both accept an array of strings. The
35
+ # strings should be a list of content types which will pass and fail
36
+ # validation, respectively.
37
+ def should_validate_attachment_content_type name, options = {}
38
+ klass = self.name.gsub(/Test$/, '').constantize
39
+ valid = [options[:valid]].flatten
40
+ invalid = [options[:invalid]].flatten
41
+ matcher = validate_attachment_content_type(name).allowing(valid).rejecting(invalid)
42
+ should matcher.description do
43
+ assert_accepts(matcher, klass)
44
+ end
45
+ end
46
+
47
+ # Tests to ensure that you have file size validations turned on. You
48
+ # can pass the same options to this that you can to
49
+ # validate_attachment_file_size - :less_than, :greater_than, and :in.
50
+ # :less_than checks that a file is less than a certain size, :greater_than
51
+ # checks that a file is more than a certain size, and :in takes a Range or
52
+ # Array which specifies the lower and upper limits of the file size.
53
+ def should_validate_attachment_size name, options = {}
54
+ klass = self.name.gsub(/Test$/, '').constantize
55
+ min = options[:greater_than] || (options[:in] && options[:in].first) || 0
56
+ max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
57
+ range = (min..max)
58
+ matcher = validate_attachment_size(name).in(range)
59
+ should matcher.description do
60
+ assert_accepts(matcher, klass)
61
+ end
62
+ end
63
+
64
+ # Stubs the HTTP PUT for an attachment using S3 storage.
65
+ #
66
+ # @example
67
+ # stub_paperclip_s3('user', 'avatar', 'png')
68
+ def stub_paperclip_s3(model, attachment, extension)
69
+ definition = model.gsub(" ", "_").classify.constantize.
70
+ attachment_definitions[attachment.to_sym]
71
+
72
+ path = "http://s3.amazonaws.com/:id/#{definition[:path]}"
73
+ path.gsub!(/:([^\/\.]+)/) do |match|
74
+ "([^\/\.]+)"
75
+ end
76
+
77
+ begin
78
+ FakeWeb.register_uri(:put, Regexp.new(path), :body => "OK")
79
+ rescue NameError
80
+ raise NameError, "the stub_paperclip_s3 shoulda macro requires the fakeweb gem."
81
+ end
82
+ end
83
+
84
+ # Stub S3 and return a file for attachment. Best with Factory Girl.
85
+ # Uses a strict directory convention:
86
+ #
87
+ # features/support/paperclip
88
+ #
89
+ # This method is used by the Paperclip-provided Cucumber step:
90
+ #
91
+ # When I attach a "demo_tape" "mp3" file to a "band" on S3
92
+ #
93
+ # @example
94
+ # Factory.define :band_with_demo_tape, :parent => :band do |band|
95
+ # band.demo_tape { band.paperclip_fixture("band", "demo_tape", "png") }
96
+ # end
97
+ def paperclip_fixture(model, attachment, extension)
98
+ stub_paperclip_s3(model, attachment, extension)
99
+ base_path = File.join(File.dirname(__FILE__), "..", "..",
100
+ "features", "support", "paperclip")
101
+ File.new(File.join(base_path, model, "#{attachment}.#{extension}"))
102
+ end
103
+ end
104
+ end
105
+
106
+ if defined?(ActionController::Integration::Session)
107
+ class ActionController::Integration::Session #:nodoc:
108
+ include Paperclip::Shoulda
109
+ end
110
+ end
111
+
112
+ class Factory
113
+ include Paperclip::Shoulda #:nodoc:
114
+ end
115
+
116
+ class Test::Unit::TestCase #:nodoc:
117
+ extend Paperclip::Shoulda
118
+ end
@@ -0,0 +1,921 @@
1
+ # encoding: utf-8
2
+ require './test/helper'
3
+
4
+ class Dummy
5
+ # This is a dummy class
6
+ end
7
+
8
+ class AttachmentTest < Test::Unit::TestCase
9
+ should "return the path based on the url by default" do
10
+ @attachment = attachment :url => "/:class/:id/:basename"
11
+ @model = @attachment.instance
12
+ @model.id = 1234
13
+ @model.avatar_file_name = "fake.jpg"
14
+ assert_equal "#{Rails.root}/public/fake_models/1234/fake", @attachment.path
15
+ end
16
+
17
+ context "Attachment default_options" do
18
+ setup do
19
+ rebuild_model
20
+ @old_default_options = Paperclip::Attachment.default_options.dup
21
+ @new_default_options = @old_default_options.merge({
22
+ :path => "argle/bargle",
23
+ :url => "fooferon",
24
+ :default_url => "not here.png"
25
+ })
26
+ end
27
+
28
+ teardown do
29
+ Paperclip::Attachment.default_options.merge! @old_default_options
30
+ end
31
+
32
+ should "be overrideable" do
33
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
34
+ @new_default_options.keys.each do |key|
35
+ assert_equal @new_default_options[key],
36
+ Paperclip::Attachment.default_options[key]
37
+ end
38
+ end
39
+
40
+ context "without an Attachment" do
41
+ setup do
42
+ @dummy = Dummy.new
43
+ end
44
+
45
+ should "return false when asked exists?" do
46
+ assert !@dummy.avatar.exists?
47
+ end
48
+ end
49
+
50
+ context "on an Attachment" do
51
+ setup do
52
+ @dummy = Dummy.new
53
+ @attachment = @dummy.avatar
54
+ end
55
+
56
+ Paperclip::Attachment.default_options.keys.each do |key|
57
+ should "be the default_options for #{key}" do
58
+ assert_equal @old_default_options[key],
59
+ @attachment.instance_variable_get("@#{key}"),
60
+ key
61
+ end
62
+ end
63
+
64
+ context "when redefined" do
65
+ setup do
66
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
67
+ @dummy = Dummy.new
68
+ @attachment = @dummy.avatar
69
+ end
70
+
71
+ Paperclip::Attachment.default_options.keys.each do |key|
72
+ should "be the new default_options for #{key}" do
73
+ assert_equal @new_default_options[key],
74
+ @attachment.instance_variable_get("@#{key}"),
75
+ key
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ context "An attachment with similarly named interpolations" do
83
+ setup do
84
+ rebuild_model :path => ":id.omg/:id-bbq/:idwhat/:id_partition.wtf"
85
+ @dummy = Dummy.new
86
+ @dummy.stubs(:id).returns(1024)
87
+ @file = File.new(File.join(File.dirname(__FILE__),
88
+ "fixtures",
89
+ "5k.png"), 'rb')
90
+ @dummy.avatar = @file
91
+ end
92
+
93
+ teardown { @file.close }
94
+
95
+ should "make sure that they are interpolated correctly" do
96
+ assert_equal "1024.omg/1024-bbq/1024what/000/001/024.wtf", @dummy.avatar.path
97
+ end
98
+ end
99
+
100
+ context "An attachment with :timestamp interpolations" do
101
+ setup do
102
+ @file = StringIO.new("...")
103
+ @zone = 'UTC'
104
+ Time.stubs(:zone).returns(@zone)
105
+ @zone_default = 'Eastern Time (US & Canada)'
106
+ Time.stubs(:zone_default).returns(@zone_default)
107
+ end
108
+
109
+ context "using default time zone" do
110
+ setup do
111
+ rebuild_model :path => ":timestamp", :use_default_time_zone => true
112
+ @dummy = Dummy.new
113
+ @dummy.avatar = @file
114
+ end
115
+
116
+ should "return a time in the default zone" do
117
+ assert_equal @dummy.avatar_updated_at.in_time_zone(@zone_default).to_s, @dummy.avatar.path
118
+ end
119
+ end
120
+
121
+ context "using per-thread time zone" do
122
+ setup do
123
+ rebuild_model :path => ":timestamp", :use_default_time_zone => false
124
+ @dummy = Dummy.new
125
+ @dummy.avatar = @file
126
+ end
127
+
128
+ should "return a time in the per-thread zone" do
129
+ assert_equal @dummy.avatar_updated_at.in_time_zone(@zone).to_s, @dummy.avatar.path
130
+ end
131
+ end
132
+ end
133
+
134
+ context "An attachment with :hash interpolations" do
135
+ setup do
136
+ @file = StringIO.new("...")
137
+ end
138
+
139
+ should "raise if no secret is provided" do
140
+ @attachment = attachment :path => ":hash"
141
+ @attachment.assign @file
142
+
143
+ assert_raise ArgumentError do
144
+ @attachment.path
145
+ end
146
+ end
147
+
148
+ context "when secret is set" do
149
+ setup do
150
+ @attachment = attachment :path => ":hash", :hash_secret => "w00t"
151
+ @attachment.stubs(:instance_read).with(:updated_at).returns(Time.at(1234567890))
152
+ @attachment.stubs(:instance_read).with(:file_name).returns("bla.txt")
153
+ @attachment.instance.id = 1234
154
+ @attachment.assign @file
155
+ end
156
+
157
+ should "interpolate the hash data" do
158
+ @attachment.expects(:interpolate).with(@attachment.options[:hash_data],anything).returns("interpolated_stuff")
159
+ @attachment.hash
160
+ end
161
+
162
+ should "result in the correct interpolation" do
163
+ assert_equal "fake_models/avatars/1234/original/1234567890", @attachment.send(:interpolate,@attachment.options[:hash_data])
164
+ end
165
+
166
+ should "result in a correct hash" do
167
+ assert_equal "d22b617d1bf10016aa7d046d16427ae203f39fce", @attachment.path
168
+ end
169
+
170
+ should "generate a hash digest with the correct style" do
171
+ OpenSSL::HMAC.expects(:hexdigest).with(anything, anything, "fake_models/avatars/1234/medium/1234567890")
172
+ @attachment.path("medium")
173
+ end
174
+ end
175
+ end
176
+
177
+ context "An attachment with a :rails_env interpolation" do
178
+ setup do
179
+ @rails_env = "blah"
180
+ @id = 1024
181
+ rebuild_model :path => ":rails_env/:id.png"
182
+ @dummy = Dummy.new
183
+ @dummy.stubs(:id).returns(@id)
184
+ @file = StringIO.new(".")
185
+ @dummy.avatar = @file
186
+ Rails.stubs(:env).returns(@rails_env)
187
+ end
188
+
189
+ should "return the proper path" do
190
+ assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path
191
+ end
192
+ end
193
+
194
+ context "An attachment with a default style and an extension interpolation" do
195
+ setup do
196
+ @attachment = attachment :path => ":basename.:extension",
197
+ :styles => { :default => ["100x100", :png] },
198
+ :default_style => :default
199
+ @file = StringIO.new("...")
200
+ @file.stubs(:original_filename).returns("file.jpg")
201
+ end
202
+ should "return the right extension for the path" do
203
+ @attachment.assign(@file)
204
+ assert_equal "file.png", @attachment.path
205
+ end
206
+ end
207
+
208
+ context "An attachment with :convert_options" do
209
+ setup do
210
+ rebuild_model :styles => {
211
+ :thumb => "100x100",
212
+ :large => "400x400"
213
+ },
214
+ :convert_options => {
215
+ :all => "-do_stuff",
216
+ :thumb => "-thumbnailize"
217
+ }
218
+ @dummy = Dummy.new
219
+ @dummy.avatar
220
+ end
221
+
222
+ should "report the correct options when sent #extra_options_for(:thumb)" do
223
+ assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
224
+ end
225
+
226
+ should "report the correct options when sent #extra_options_for(:large)" do
227
+ assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
228
+ end
229
+ end
230
+
231
+ context "An attachment with :convert_options that is a proc" do
232
+ setup do
233
+ rebuild_model :styles => {
234
+ :thumb => "100x100",
235
+ :large => "400x400"
236
+ },
237
+ :convert_options => {
238
+ :all => lambda{|i| i.all },
239
+ :thumb => lambda{|i| i.thumb }
240
+ }
241
+ Dummy.class_eval do
242
+ def all; "-all"; end
243
+ def thumb; "-thumb"; end
244
+ end
245
+ @dummy = Dummy.new
246
+ @dummy.avatar
247
+ end
248
+
249
+ should "report the correct options when sent #extra_options_for(:thumb)" do
250
+ assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
251
+ end
252
+
253
+ should "report the correct options when sent #extra_options_for(:large)" do
254
+ assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
255
+ end
256
+ end
257
+
258
+ context "An attachment with :path that is a proc" do
259
+ setup do
260
+ rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
261
+
262
+ @file = File.new(File.join(File.dirname(__FILE__),
263
+ "fixtures",
264
+ "5k.png"), 'rb')
265
+ @dummyA = Dummy.new(:other => 'a')
266
+ @dummyA.avatar = @file
267
+ @dummyB = Dummy.new(:other => 'b')
268
+ @dummyB.avatar = @file
269
+ end
270
+
271
+ teardown { @file.close }
272
+
273
+ should "return correct path" do
274
+ assert_equal "path/a.png", @dummyA.avatar.path
275
+ assert_equal "path/b.png", @dummyB.avatar.path
276
+ end
277
+ end
278
+
279
+ context "An attachment with :styles that is a proc" do
280
+ setup do
281
+ rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} }
282
+
283
+ @attachment = Dummy.new.avatar
284
+ end
285
+
286
+ should "have the correct geometry" do
287
+ assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
288
+ end
289
+ end
290
+
291
+ context "An attachment with :url that is a proc" do
292
+ setup do
293
+ rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
294
+
295
+ @file = File.new(File.join(File.dirname(__FILE__),
296
+ "fixtures",
297
+ "5k.png"), 'rb')
298
+ @dummyA = Dummy.new(:other => 'a')
299
+ @dummyA.avatar = @file
300
+ @dummyB = Dummy.new(:other => 'b')
301
+ @dummyB.avatar = @file
302
+ end
303
+
304
+ teardown { @file.close }
305
+
306
+ should "return correct url" do
307
+ assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
308
+ assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
309
+ end
310
+ end
311
+
312
+ geometry_specs = [
313
+ [ lambda{|z| "50x50#" }, :png ],
314
+ lambda{|z| "50x50#" },
315
+ { :geometry => lambda{|z| "50x50#" } }
316
+ ]
317
+ geometry_specs.each do |geometry_spec|
318
+ context "An attachment geometry like #{geometry_spec}" do
319
+ setup do
320
+ rebuild_model :styles => { :normal => geometry_spec }
321
+ @attachment = Dummy.new.avatar
322
+ end
323
+
324
+ context "when assigned" do
325
+ setup do
326
+ @file = StringIO.new(".")
327
+ @attachment.assign(@file)
328
+ end
329
+
330
+ should "have the correct geometry" do
331
+ assert_equal "50x50#", @attachment.styles[:normal][:geometry]
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ context "An attachment with both 'normal' and hash-style styles" do
338
+ setup do
339
+ rebuild_model :styles => {
340
+ :normal => ["50x50#", :png],
341
+ :hash => { :geometry => "50x50#", :format => :png }
342
+ }
343
+ @dummy = Dummy.new
344
+ @attachment = @dummy.avatar
345
+ end
346
+
347
+ [:processors, :whiny, :convert_options, :geometry, :format].each do |field|
348
+ should "have the same #{field} field" do
349
+ assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field]
350
+ end
351
+ end
352
+ end
353
+
354
+ context "An attachment with :processors that is a proc" do
355
+ setup do
356
+ rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
357
+ @attachment = Dummy.new.avatar
358
+ end
359
+
360
+ context "when assigned" do
361
+ setup do
362
+ @attachment.assign(StringIO.new("."))
363
+ end
364
+
365
+ should "have the correct processors" do
366
+ assert_equal [ :test ], @attachment.styles[:normal][:processors]
367
+ end
368
+ end
369
+ end
370
+
371
+ context "An attachment with erroring processor" do
372
+ setup do
373
+ rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
374
+ @dummy = Dummy.new
375
+ Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
376
+ @file = StringIO.new("...")
377
+ @file.stubs(:to_tempfile).returns(@file)
378
+ @dummy.avatar = @file
379
+ end
380
+
381
+ should "correctly forward processing error message to the instance" do
382
+ @dummy.valid?
383
+ assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
384
+ end
385
+ end
386
+
387
+ context "An attachment with multiple processors" do
388
+ setup do
389
+ class Paperclip::Test < Paperclip::Processor; end
390
+ @style_params = { :once => {:one => 1, :two => 2} }
391
+ rebuild_model :processors => [:thumbnail, :test], :styles => @style_params
392
+ @dummy = Dummy.new
393
+ @file = StringIO.new("...")
394
+ @file.stubs(:to_tempfile).returns(@file)
395
+ Paperclip::Test.stubs(:make).returns(@file)
396
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
397
+ end
398
+
399
+ context "when assigned" do
400
+ setup { @dummy.avatar = @file }
401
+
402
+ before_should "call #make on all specified processors" do
403
+ Paperclip::Thumbnail.expects(:make).with(any_parameters).returns(@file)
404
+ Paperclip::Test.expects(:make).with(any_parameters).returns(@file)
405
+ end
406
+
407
+ before_should "call #make with the right parameters passed as second argument" do
408
+ expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
409
+ Paperclip::Thumbnail.expects(:make).with(anything, expected_params, anything).returns(@file)
410
+ end
411
+
412
+ before_should "call #make with attachment passed as third argument" do
413
+ Paperclip::Test.expects(:make).with(anything, anything, @dummy.avatar).returns(@file)
414
+ end
415
+ end
416
+ end
417
+
418
+ should "include the filesystem module when loading the filesystem storage" do
419
+ rebuild_model :storage => :filesystem
420
+ @dummy = Dummy.new
421
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
422
+ end
423
+
424
+ should "include the filesystem module even if capitalization is wrong" do
425
+ rebuild_model :storage => :FileSystem
426
+ @dummy = Dummy.new
427
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
428
+ end
429
+
430
+ should "raise an error if you try to include a storage module that doesn't exist" do
431
+ rebuild_model :storage => :not_here
432
+ @dummy = Dummy.new
433
+ assert_raises(Paperclip::StorageMethodNotFound) do
434
+ @dummy.avatar
435
+ end
436
+ end
437
+
438
+ context "An attachment with styles but no processors defined" do
439
+ setup do
440
+ rebuild_model :processors => [], :styles => {:something => '1'}
441
+ @dummy = Dummy.new
442
+ @file = StringIO.new("...")
443
+ end
444
+ should "raise when assigned to" do
445
+ assert_raises(RuntimeError){ @dummy.avatar = @file }
446
+ end
447
+ end
448
+
449
+ context "An attachment without styles and with no processors defined" do
450
+ setup do
451
+ rebuild_model :processors => [], :styles => {}
452
+ @dummy = Dummy.new
453
+ @file = StringIO.new("...")
454
+ end
455
+ should "not raise when assigned to" do
456
+ @dummy.avatar = @file
457
+ end
458
+ end
459
+
460
+ context "Assigning an attachment with post_process hooks" do
461
+ setup do
462
+ rebuild_class :styles => { :something => "100x100#" }
463
+ Dummy.class_eval do
464
+ before_avatar_post_process :do_before_avatar
465
+ after_avatar_post_process :do_after_avatar
466
+ before_post_process :do_before_all
467
+ after_post_process :do_after_all
468
+ def do_before_avatar; end
469
+ def do_after_avatar; end
470
+ def do_before_all; end
471
+ def do_after_all; end
472
+ end
473
+ @file = StringIO.new(".")
474
+ @file.stubs(:to_tempfile).returns(@file)
475
+ @dummy = Dummy.new
476
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
477
+ @attachment = @dummy.avatar
478
+ end
479
+
480
+ should "call the defined callbacks when assigned" do
481
+ @dummy.expects(:do_before_avatar).with()
482
+ @dummy.expects(:do_after_avatar).with()
483
+ @dummy.expects(:do_before_all).with()
484
+ @dummy.expects(:do_after_all).with()
485
+ Paperclip::Thumbnail.expects(:make).returns(@file)
486
+ @dummy.avatar = @file
487
+ end
488
+
489
+ should "not cancel the processing if a before_post_process returns nil" do
490
+ @dummy.expects(:do_before_avatar).with().returns(nil)
491
+ @dummy.expects(:do_after_avatar).with()
492
+ @dummy.expects(:do_before_all).with().returns(nil)
493
+ @dummy.expects(:do_after_all).with()
494
+ Paperclip::Thumbnail.expects(:make).returns(@file)
495
+ @dummy.avatar = @file
496
+ end
497
+
498
+ should "cancel the processing if a before_post_process returns false" do
499
+ @dummy.expects(:do_before_avatar).never
500
+ @dummy.expects(:do_after_avatar).never
501
+ @dummy.expects(:do_before_all).with().returns(false)
502
+ @dummy.expects(:do_after_all)
503
+ Paperclip::Thumbnail.expects(:make).never
504
+ @dummy.avatar = @file
505
+ end
506
+
507
+ should "cancel the processing if a before_avatar_post_process returns false" do
508
+ @dummy.expects(:do_before_avatar).with().returns(false)
509
+ @dummy.expects(:do_after_avatar)
510
+ @dummy.expects(:do_before_all).with().returns(true)
511
+ @dummy.expects(:do_after_all)
512
+ Paperclip::Thumbnail.expects(:make).never
513
+ @dummy.avatar = @file
514
+ end
515
+ end
516
+
517
+ context "Assigning an attachment" do
518
+ setup do
519
+ rebuild_model :styles => { :something => "100x100#" }
520
+ @file = StringIO.new(".")
521
+ @file.stubs(:original_filename).returns("5k.png\n\n")
522
+ @file.stubs(:content_type).returns("image/png\n\n")
523
+ @file.stubs(:to_tempfile).returns(@file)
524
+ @dummy = Dummy.new
525
+ Paperclip::Thumbnail.expects(:make).returns(@file)
526
+ @attachment = @dummy.avatar
527
+ @dummy.avatar = @file
528
+ end
529
+
530
+ should "strip whitespace from original_filename field" do
531
+ assert_equal "5k.png", @dummy.avatar.original_filename
532
+ end
533
+
534
+ should "strip whitespace from content_type field" do
535
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
536
+ end
537
+ end
538
+
539
+ context "Attachment with strange letters" do
540
+ setup do
541
+ rebuild_model
542
+
543
+ @not_file = mock("not_file")
544
+ @tempfile = mock("tempfile")
545
+ @not_file.stubs(:nil?).returns(false)
546
+ @not_file.expects(:size).returns(10)
547
+ @tempfile.expects(:size).returns(10)
548
+ @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
549
+ @not_file.expects(:content_type).returns("image/png\r\n")
550
+
551
+ @dummy = Dummy.new
552
+ @attachment = @dummy.avatar
553
+ @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
554
+ @attachment.expects(:queue_existing_for_delete)
555
+ @attachment.expects(:post_process)
556
+ @attachment.expects(:to_tempfile).returns(@tempfile)
557
+ @attachment.expects(:generate_fingerprint).with(@tempfile).returns("12345")
558
+ @attachment.expects(:generate_fingerprint).with(@not_file).returns("12345")
559
+ @dummy.avatar = @not_file
560
+ end
561
+
562
+ should "not remove strange letters" do
563
+ assert_equal "sheep_say_bæ.png", @dummy.avatar.original_filename
564
+ end
565
+ end
566
+
567
+ context "Attachment with uppercase extension and a default style" do
568
+ setup do
569
+ @old_defaults = Paperclip::Attachment.default_options.dup
570
+ Paperclip::Attachment.default_options.merge!({
571
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
572
+ })
573
+ FileUtils.rm_rf("tmp")
574
+ rebuild_model
575
+ @instance = Dummy.new
576
+ @instance.stubs(:id).returns 123
577
+
578
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "uppercase.PNG"), 'rb')
579
+
580
+ styles = {:styles => { :large => ["400x400", :jpg],
581
+ :medium => ["100x100", :jpg],
582
+ :small => ["32x32#", :jpg]},
583
+ :default_style => :small}
584
+ @attachment = Paperclip::Attachment.new(:avatar,
585
+ @instance,
586
+ styles)
587
+ now = Time.now
588
+ Time.stubs(:now).returns(now)
589
+ @attachment.assign(@file)
590
+ @attachment.save
591
+ end
592
+
593
+ teardown do
594
+ @file.close
595
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
596
+ end
597
+
598
+ should "should have matching to_s and url methods" do
599
+ file = @attachment.to_file
600
+ assert file
601
+ assert_match @attachment.to_s, @attachment.url
602
+ assert_match @attachment.to_s(:small), @attachment.url(:small)
603
+ file.close
604
+ end
605
+ end
606
+
607
+ context "An attachment" do
608
+ setup do
609
+ @old_defaults = Paperclip::Attachment.default_options.dup
610
+ Paperclip::Attachment.default_options.merge!({
611
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
612
+ })
613
+ FileUtils.rm_rf("tmp")
614
+ rebuild_model
615
+ @instance = Dummy.new
616
+ @instance.stubs(:id).returns 123
617
+ @attachment = Paperclip::Attachment.new(:avatar, @instance)
618
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
619
+ end
620
+
621
+ teardown do
622
+ @file.close
623
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
624
+ end
625
+
626
+ should "raise if there are not the correct columns when you try to assign" do
627
+ @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
628
+ assert_raises(Paperclip::PaperclipError) do
629
+ @other_attachment.assign(@file)
630
+ end
631
+ end
632
+
633
+ should "return its default_url when no file assigned" do
634
+ assert @attachment.to_file.nil?
635
+ assert_equal "/avatars/original/missing.png", @attachment.url
636
+ assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
637
+ end
638
+
639
+ should "return nil as path when no file assigned" do
640
+ assert @attachment.to_file.nil?
641
+ assert_equal nil, @attachment.path
642
+ assert_equal nil, @attachment.path(:blah)
643
+ end
644
+
645
+ context "with a file assigned in the database" do
646
+ setup do
647
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
648
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
649
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
650
+ dtnow = DateTime.now
651
+ @now = Time.now
652
+ Time.stubs(:now).returns(@now)
653
+ @attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
654
+ end
655
+
656
+ should "return a correct url even if the file does not exist" do
657
+ assert_nil @attachment.to_file
658
+ assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
659
+ end
660
+
661
+ should "make sure the updated_at mtime is in the url if it is defined" do
662
+ assert_match %r{#{@now.to_i}$}, @attachment.url(:blah)
663
+ end
664
+
665
+ should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
666
+ assert_no_match %r{#{@now.to_i}$}, @attachment.url(:blah, false)
667
+ end
668
+
669
+ context "with the updated_at field removed" do
670
+ setup do
671
+ @attachment.stubs(:instance_read).with(:updated_at).returns(nil)
672
+ end
673
+
674
+ should "only return the url without the updated_at when sent #url" do
675
+ assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
676
+ end
677
+ end
678
+
679
+ should "return the proper path when filename has a single .'s" do
680
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
681
+ end
682
+
683
+ should "return the proper path when filename has multiple .'s" do
684
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
685
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
686
+ end
687
+
688
+ context "when expecting three styles" do
689
+ setup do
690
+ styles = {:styles => { :large => ["400x400", :png],
691
+ :medium => ["100x100", :gif],
692
+ :small => ["32x32#", :jpg]}}
693
+ @attachment = Paperclip::Attachment.new(:avatar,
694
+ @instance,
695
+ styles)
696
+ end
697
+
698
+ context "and assigned a file" do
699
+ setup do
700
+ now = Time.now
701
+ Time.stubs(:now).returns(now)
702
+ @attachment.assign(@file)
703
+ end
704
+
705
+ should "be dirty" do
706
+ assert @attachment.dirty?
707
+ end
708
+
709
+ context "and saved" do
710
+ setup do
711
+ @attachment.save
712
+ end
713
+
714
+ should "return the real url" do
715
+ file = @attachment.to_file
716
+ assert file
717
+ assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
718
+ assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
719
+ file.close
720
+ end
721
+
722
+ should "commit the files to disk" do
723
+ [:large, :medium, :small].each do |style|
724
+ io = @attachment.to_file(style)
725
+ # p "in commit to disk test, io is #{io.inspect} and @instance.id is #{@instance.id}"
726
+ assert File.exists?(io.path)
727
+ assert ! io.is_a?(::Tempfile)
728
+ io.close
729
+ end
730
+ end
731
+
732
+ should "save the files as the right formats and sizes" do
733
+ [[:large, 400, 61, "PNG"],
734
+ [:medium, 100, 15, "GIF"],
735
+ [:small, 32, 32, "JPEG"]].each do |style|
736
+ cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
737
+ out = `#{cmd}`
738
+ width, height, size, format = out.split(" ")
739
+ assert_equal style[1].to_s, width.to_s
740
+ assert_equal style[2].to_s, height.to_s
741
+ assert_equal style[3].to_s, format.to_s
742
+ end
743
+ end
744
+
745
+ should "still have its #file attribute not be nil" do
746
+ assert ! (file = @attachment.to_file).nil?
747
+ file.close
748
+ end
749
+
750
+ context "and trying to delete" do
751
+ setup do
752
+ @existing_names = @attachment.styles.keys.collect do |style|
753
+ @attachment.path(style)
754
+ end
755
+ end
756
+
757
+ should "delete the files after assigning nil" do
758
+ @attachment.expects(:instance_write).with(:file_name, nil)
759
+ @attachment.expects(:instance_write).with(:content_type, nil)
760
+ @attachment.expects(:instance_write).with(:file_size, nil)
761
+ @attachment.expects(:instance_write).with(:updated_at, nil)
762
+ @attachment.assign nil
763
+ @attachment.save
764
+ @existing_names.each{|f| assert ! File.exists?(f) }
765
+ end
766
+
767
+ should "delete the files when you call #clear and #save" do
768
+ @attachment.expects(:instance_write).with(:file_name, nil)
769
+ @attachment.expects(:instance_write).with(:content_type, nil)
770
+ @attachment.expects(:instance_write).with(:file_size, nil)
771
+ @attachment.expects(:instance_write).with(:updated_at, nil)
772
+ @attachment.clear
773
+ @attachment.save
774
+ @existing_names.each{|f| assert ! File.exists?(f) }
775
+ end
776
+
777
+ should "delete the files when you call #delete" do
778
+ @attachment.expects(:instance_write).with(:file_name, nil)
779
+ @attachment.expects(:instance_write).with(:content_type, nil)
780
+ @attachment.expects(:instance_write).with(:file_size, nil)
781
+ @attachment.expects(:instance_write).with(:updated_at, nil)
782
+ @attachment.destroy
783
+ @existing_names.each{|f| assert ! File.exists?(f) }
784
+ end
785
+ end
786
+ end
787
+ end
788
+ end
789
+
790
+ end
791
+
792
+ context "when trying a nonexistant storage type" do
793
+ setup do
794
+ rebuild_model :storage => :not_here
795
+ end
796
+
797
+ should "not be able to find the module" do
798
+ assert_raise(Paperclip::StorageMethodNotFound){ Dummy.new.avatar }
799
+ end
800
+ end
801
+ end
802
+
803
+ context "An attachment with only a avatar_file_name column" do
804
+ setup do
805
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
806
+ table.column :avatar_file_name, :string
807
+ end
808
+ rebuild_class
809
+ @dummy = Dummy.new
810
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
811
+ end
812
+
813
+ teardown { @file.close }
814
+
815
+ should "not error when assigned an attachment" do
816
+ assert_nothing_raised { @dummy.avatar = @file }
817
+ end
818
+
819
+ should "return the time when sent #avatar_updated_at" do
820
+ now = Time.now
821
+ Time.stubs(:now).returns(now)
822
+ @dummy.avatar = @file
823
+ assert_equal now.to_i, @dummy.avatar.updated_at.to_i
824
+ end
825
+
826
+ should "return nil when reloaded and sent #avatar_updated_at" do
827
+ @dummy.save
828
+ @dummy.reload
829
+ assert_nil @dummy.avatar.updated_at
830
+ end
831
+
832
+ should "return the right value when sent #avatar_file_size" do
833
+ @dummy.avatar = @file
834
+ assert_equal @file.size, @dummy.avatar.size
835
+ end
836
+
837
+ context "and avatar_updated_at column" do
838
+ setup do
839
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
840
+ rebuild_class
841
+ @dummy = Dummy.new
842
+ end
843
+
844
+ should "not error when assigned an attachment" do
845
+ assert_nothing_raised { @dummy.avatar = @file }
846
+ end
847
+
848
+ should "return the right value when sent #avatar_updated_at" do
849
+ now = Time.now
850
+ Time.stubs(:now).returns(now)
851
+ @dummy.avatar = @file
852
+ assert_equal now.to_i, @dummy.avatar.updated_at
853
+ end
854
+ end
855
+
856
+ context "and avatar_content_type column" do
857
+ setup do
858
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
859
+ rebuild_class
860
+ @dummy = Dummy.new
861
+ end
862
+
863
+ should "not error when assigned an attachment" do
864
+ assert_nothing_raised { @dummy.avatar = @file }
865
+ end
866
+
867
+ should "return the right value when sent #avatar_content_type" do
868
+ @dummy.avatar = @file
869
+ assert_equal "image/png", @dummy.avatar.content_type
870
+ end
871
+ end
872
+
873
+ context "and avatar_file_size column" do
874
+ setup do
875
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
876
+ rebuild_class
877
+ @dummy = Dummy.new
878
+ end
879
+
880
+ should "not error when assigned an attachment" do
881
+ assert_nothing_raised { @dummy.avatar = @file }
882
+ end
883
+
884
+ should "return the right value when sent #avatar_file_size" do
885
+ @dummy.avatar = @file
886
+ assert_equal @file.size, @dummy.avatar.size
887
+ end
888
+
889
+ should "return the right value when saved, reloaded, and sent #avatar_file_size" do
890
+ @dummy.avatar = @file
891
+ @dummy.save
892
+ @dummy = Dummy.find(@dummy.id)
893
+ assert_equal @file.size, @dummy.avatar.size
894
+ end
895
+ end
896
+
897
+ context "and avatar_fingerprint column" do
898
+ setup do
899
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_fingerprint, :string
900
+ rebuild_class
901
+ @dummy = Dummy.new
902
+ end
903
+
904
+ should "not error when assigned an attachment" do
905
+ assert_nothing_raised { @dummy.avatar = @file }
906
+ end
907
+
908
+ should "return the right value when sent #avatar_fingerprint" do
909
+ @dummy.avatar = @file
910
+ assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
911
+ end
912
+
913
+ should "return the right value when saved, reloaded, and sent #avatar_fingerprint" do
914
+ @dummy.avatar = @file
915
+ @dummy.save
916
+ @dummy = Dummy.find(@dummy.id)
917
+ assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
918
+ end
919
+ end
920
+ end
921
+ end