require './test/helper' require 'aws' class S3Test < Test::Unit::TestCase def rails_env(env) silence_warnings do Object.const_set(:Rails, stub('Rails', :env => env)) end end def setup AWS.config(:access_key_id => "TESTKEY", :secret_access_key => "TESTSECRET", :stub_requests => true) end def teardown AWS.config(:access_key_id => nil, :secret_access_key => nil, :stub_requests => nil) end context "Parsing S3 credentials" do setup do @proxy_settings = {:host => "127.0.0.1", :port => 8888, :user => "foo", :password => "bar"} rebuild_model :storage => :s3, :bucket => "testing", :http_proxy => @proxy_settings, :s3_credentials => {:not => :important} @dummy = Dummy.new @avatar = @dummy.avatar end should "get the correct credentials when RAILS_ENV is production" do rails_env("production") assert_equal({:key => "12345"}, @avatar.parse_credentials('production' => {:key => '12345'}, :development => {:key => "54321"})) end should "get the correct credentials when RAILS_ENV is development" do rails_env("development") assert_equal({:key => "54321"}, @avatar.parse_credentials('production' => {:key => '12345'}, :development => {:key => "54321"})) end should "return the argument if the key does not exist" do rails_env("not really an env") assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345")) end should "support HTTP proxy settings" do rails_env("development") assert_equal(true, @avatar.using_http_proxy?) assert_equal(@proxy_settings[:host], @avatar.http_proxy_host) assert_equal(@proxy_settings[:port], @avatar.http_proxy_port) assert_equal(@proxy_settings[:user], @avatar.http_proxy_user) assert_equal(@proxy_settings[:password], @avatar.http_proxy_password) end end context ":bucket option via :s3_credentials" do setup do rebuild_model :storage => :s3, :s3_credentials => {:bucket => 'testing'} @dummy = Dummy.new end should "populate #bucket_name" do assert_equal @dummy.avatar.bucket_name, 'testing' end end context ":bucket option" do setup do rebuild_model :storage => :s3, :bucket => "testing", :s3_credentials => {} @dummy = Dummy.new end should "populate #bucket_name" do assert_equal @dummy.avatar.bucket_name, 'testing' end end context "missing :bucket option" do setup do rebuild_model :storage => :s3, :http_proxy => @proxy_settings, :s3_credentials => {:not => :important} @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "raise an argument error" do exception = assert_raise(ArgumentError) { @dummy.save } assert_match /missing required :bucket option/, exception.message end end context "" do setup do rebuild_model :storage => :s3, :s3_credentials => {}, :bucket => "bucket", :path => ":attachment/:basename.:extension", :url => ":s3_path_url" @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a url based on an S3 path" do assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url end should "use the correct bucket" do assert_equal "bucket", @dummy.avatar.s3_bucket.name end should "use the correct key" do assert_equal "avatars/stringio.txt", @dummy.avatar.s3_object.key end end context "s3_protocol" do ["http", :http, ""].each do |protocol| context "as #{protocol.inspect}" do setup do rebuild_model :storage => :s3, :s3_protocol => protocol @dummy = Dummy.new end should "return the s3_protocol in string" do assert_equal protocol.to_s, @dummy.avatar.s3_protocol end end end end context ":s3_protocol => 'https'" do setup do rebuild_model :storage => :s3, :s3_credentials => {}, :s3_protocol => 'https', :bucket => "bucket", :path => ":attachment/:basename.:extension" @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a url based on an S3 path" do assert_match %r{^https://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url end end context ":s3_protocol => :https" do setup do rebuild_model :storage => :s3, :s3_credentials => {}, :s3_protocol => :https, :bucket => "bucket", :path => ":attachment/:basename.:extension" @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a url based on an S3 path" do assert_match %r{^https://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url end end context ":s3_protocol => ''" do setup do rebuild_model :storage => :s3, :s3_credentials => {}, :s3_protocol => '', :bucket => "bucket", :path => ":attachment/:basename.:extension" @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a url based on an S3 path" do assert_match %r{^//s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url end end context "An attachment that uses S3 for storage and has the style in the path" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :styles => { :thumb => "80x80>" }, :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" } @dummy = Dummy.new @dummy.avatar = StringIO.new(".") @avatar = @dummy.avatar end should "use an S3 object based on the correct path for the default style" do assert_equal("avatars/original/stringio.txt", @dummy.avatar.s3_object.key) end should "use an S3 object based on the correct path for the custom style" do assert_equal("avatars/thumb/stringio.txt", @dummy.avatar.s3_object(:thumb).key) end end context "s3_host_name" do setup do rebuild_model :storage => :s3, :s3_credentials => {}, :bucket => "bucket", :path => ":attachment/:basename.:extension", :s3_host_name => "s3-ap-northeast-1.amazonaws.com" @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a url based on an :s3_host_name path" do assert_match %r{^http://s3-ap-northeast-1.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url end should "use the S3 bucket with the correct host name" do assert_equal "s3-ap-northeast-1.amazonaws.com", @dummy.avatar.s3_bucket.config.s3_endpoint end end context "dynamic s3_host_name" do setup do rebuild_model :storage => :s3, :s3_credentials => {}, :bucket => "bucket", :path => ":attachment/:basename.:extension", :s3_host_name => lambda {|a| a.instance.value } @dummy = Dummy.new class << @dummy attr_accessor :value end @dummy.avatar = StringIO.new(".") end should "use s3_host_name as a proc if available" do @dummy.value = "s3.something.com" assert_equal "http://s3.something.com/bucket/avatars/stringio.txt", @dummy.avatar.url(:original, :timestamp => false) end end context "An attachment that uses S3 for storage and has styles that return different file types" do setup do rebuild_model :styles => { :large => ['500x500#', :jpg] }, :storage => :s3, :bucket => "bucket", :path => ":attachment/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" } File.open(fixture_file('5k.png'), 'rb') do |file| @dummy = Dummy.new @dummy.avatar = file end end should "return a url containing the correct original file mime type" do assert_match /.+\/5k.png/, @dummy.avatar.url end should 'use the correct key for the original file mime type' do assert_match /.+\/5k.png/, @dummy.avatar.s3_object.key end should "return a url containing the correct processed file mime type" do assert_match /.+\/5k.jpg/, @dummy.avatar.url(:large) end should "use the correct key for the processed file mime type" do assert_match /.+\/5k.jpg/, @dummy.avatar.s3_object(:large).key end end context "An attachment that uses S3 for storage and has a proc for styles" do setup do rebuild_model :styles => lambda { |attachment| attachment.instance.counter; {:thumbnail => { :geometry => "50x50#", :s3_headers => {'Cache-Control' => 'max-age=31557600'}} }}, :storage => :s3, :bucket => "bucket", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" } @file = File.new(fixture_file('5k.png'), 'rb') Dummy.class_eval do def counter @counter ||= 0 @counter += 1 @counter end end @dummy = Dummy.new @dummy.avatar = @file object = stub @dummy.avatar.stubs(:s3_object).with(:original).returns(object) @dummy.avatar.stubs(:s3_object).with(:thumbnail).returns(object) object.expects(:write).with(anything, :content_type => 'image/png', :acl => :public_read) object.expects(:write).with(anything, :content_type => 'image/png', :acl => :public_read, :cache_control => 'max-age=31557600') @dummy.save end teardown { @file.close } should "succeed" do assert_equal @dummy.counter, 7 end end context "An attachment that uses S3 for storage and has spaces in file name" do setup do rebuild_model :styles => { :large => ['500x500#', :jpg] }, :storage => :s3, :bucket => "bucket", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" } File.open(fixture_file('spaced file.png'), 'rb') do |file| @dummy = Dummy.new @dummy.avatar = file end end should "return a replaced version for path" do assert_match /.+\/spaced_file\.png/, @dummy.avatar.path end should "return a replaced version for url" do assert_match /.+\/spaced_file\.png/, @dummy.avatar.url end end context "An attachment that uses S3 for storage and has a question mark in file name" do setup do rebuild_model :styles => { :large => ['500x500#', :jpg] }, :storage => :s3, :bucket => "bucket", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" } stringio = StringIO.new(".") class << stringio def original_filename "question?mark.png" end end file = Paperclip.io_adapters.for(stringio) @dummy = Dummy.new @dummy.avatar = file @dummy.save end should "return a replaced version for path" do assert_match /.+\/question_mark\.png/, @dummy.avatar.path end should "return a replaced version for url" do assert_match /.+\/question_mark\.png/, @dummy.avatar.url end end context "" do setup do rebuild_model :storage => :s3, :s3_credentials => {}, :bucket => "bucket", :path => ":attachment/:basename.:extension", :url => ":s3_domain_url" @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a url based on an S3 subdomain" do assert_match %r{^http://bucket.s3.amazonaws.com/avatars/stringio.txt}, @dummy.avatar.url end end context "" do setup do rebuild_model :storage => :s3, :s3_credentials => { :production => { :bucket => "prod_bucket" }, :development => { :bucket => "dev_bucket" } }, :s3_host_alias => "something.something.com", :path => ":attachment/:basename.:extension", :url => ":s3_alias_url" @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a url based on the host_alias" do assert_match %r{^http://something.something.com/avatars/stringio.txt}, @dummy.avatar.url end end context "generating a url with a proc as the host alias" do setup do rebuild_model :storage => :s3, :s3_credentials => { :bucket => "prod_bucket" }, :s3_host_alias => Proc.new{|atch| "cdn#{atch.instance.counter % 4}.example.com"}, :path => ":attachment/:basename.:extension", :url => ":s3_alias_url" Dummy.class_eval do def counter @counter ||= 0 @counter += 1 @counter end end @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a url based on the host_alias" do assert_match %r{^http://cdn1.example.com/avatars/stringio.txt}, @dummy.avatar.url assert_match %r{^http://cdn2.example.com/avatars/stringio.txt}, @dummy.avatar.url end should "still return the bucket name" do assert_equal "prod_bucket", @dummy.avatar.bucket_name end end context "" do setup do rebuild_model :storage => :s3, :s3_credentials => {}, :bucket => "bucket", :path => ":attachment/:basename.:extension", :url => ":asset_host" @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "return a relative URL for Rails to calculate assets host" do assert_match %r{^avatars/stringio\.txt}, @dummy.avatar.url end end context "Generating a secure url with an expiration" do setup do @build_model_with_options = lambda {|options| base_options = { :storage => :s3, :s3_credentials => { :production => { :bucket => "prod_bucket" }, :development => { :bucket => "dev_bucket" } }, :s3_host_alias => "something.something.com", :s3_permissions => "private", :path => ":attachment/:basename.:extension", :url => ":s3_alias_url" } rebuild_model base_options.merge(options) } end should "use default options" do @build_model_with_options[{}] rails_env("production") @dummy = Dummy.new @dummy.avatar = StringIO.new(".") object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:url_for).with(:read, :expires => 3600, :secure => true) @dummy.avatar.expiring_url end should "allow overriding s3_url_options" do @build_model_with_options[:s3_url_options => { :response_content_disposition => "inline" }] rails_env("production") @dummy = Dummy.new @dummy.avatar = StringIO.new(".") object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:url_for).with(:read, :expires => 3600, :secure => true, :response_content_disposition => "inline") @dummy.avatar.expiring_url end should "allow overriding s3_object options with a proc" do @build_model_with_options[:s3_url_options => lambda {|attachment| { :response_content_type => attachment.avatar_content_type } }] rails_env("production") @dummy = Dummy.new @file = StringIO.new(".") @file.stubs(:original_filename).returns("5k.png\n\n") @file.stubs(:content_type).returns("image/png\n\n") @file.stubs(:to_tempfile).returns(@file) @dummy.avatar = @file object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:url_for).with(:read, :expires => 3600, :secure => true, :response_content_type => "image/png") @dummy.avatar.expiring_url end end context "#expiring_url" do setup { @dummy = Dummy.new } context "with no attachment" do setup { assert(!@dummy.avatar.exists?) } should "return the default URL" do assert_equal(@dummy.avatar.url, @dummy.avatar.expiring_url) end should 'generate a url for a style when a file does not exist' do assert_equal(@dummy.avatar.url(:thumb), @dummy.avatar.expiring_url(3600, :thumb)) end end should "generate the same url when using Times and Integer offsets" do assert_equal @dummy.avatar.expiring_url(1234), @dummy.avatar.expiring_url(Time.now + 1234) end end context "Generating a url with an expiration for each style" do setup do rebuild_model :storage => :s3, :s3_credentials => { :production => { :bucket => "prod_bucket" }, :development => { :bucket => "dev_bucket" } }, :s3_permissions => :private, :s3_host_alias => "something.something.com", :path => ":attachment/:style/:basename.:extension", :url => ":s3_alias_url" rails_env("production") @dummy = Dummy.new @dummy.avatar = StringIO.new(".") end should "should generate a url for the thumb" do object = stub @dummy.avatar.stubs(:s3_object).with(:thumb).returns(object) object.expects(:url_for).with(:read, :expires => 1800, :secure => true) @dummy.avatar.expiring_url(1800, :thumb) end should "should generate a url for the default style" do object = stub @dummy.avatar.stubs(:s3_object).with(:original).returns(object) object.expects(:url_for).with(:read, :expires => 1800, :secure => true) @dummy.avatar.expiring_url(1800) end end context "Parsing S3 credentials with a bucket in them" do setup do rebuild_model :storage => :s3, :s3_credentials => { :production => { :bucket => "prod_bucket" }, :development => { :bucket => "dev_bucket" } } @dummy = Dummy.new end should "get the right bucket in production" do rails_env("production") assert_equal "prod_bucket", @dummy.avatar.bucket_name assert_equal "prod_bucket", @dummy.avatar.s3_bucket.name end should "get the right bucket in development" do rails_env("development") assert_equal "dev_bucket", @dummy.avatar.bucket_name assert_equal "dev_bucket", @dummy.avatar.s3_bucket.name end end context "Parsing S3 credentials with a s3_host_name in them" do setup do rebuild_model :storage => :s3, :bucket => 'testing', :s3_credentials => { :production => { :s3_host_name => "s3-world-end.amazonaws.com" }, :development => { :s3_host_name => "s3-ap-northeast-1.amazonaws.com" } } @dummy = Dummy.new end should "get the right s3_host_name in production" do rails_env("production") assert_match %r{^s3-world-end.amazonaws.com}, @dummy.avatar.s3_host_name assert_match %r{^s3-world-end.amazonaws.com}, @dummy.avatar.s3_bucket.config.s3_endpoint end should "get the right s3_host_name in development" do rails_env("development") assert_match %r{^s3-ap-northeast-1.amazonaws.com}, @dummy.avatar.s3_host_name assert_match %r{^s3-ap-northeast-1.amazonaws.com}, @dummy.avatar.s3_bucket.config.s3_endpoint end should "get the right s3_host_name if the key does not exist" do rails_env("test") assert_match %r{^s3.amazonaws.com}, @dummy.avatar.s3_host_name assert_match %r{^s3.amazonaws.com}, @dummy.avatar.s3_bucket.config.s3_endpoint end end context "An attachment with S3 storage" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" } end should "be extended by the S3 module" do assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3) end should "not be extended by the Filesystem module" do assert ! Dummy.new.avatar.is_a?(Paperclip::Storage::Filesystem) end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } should "not get a bucket to get a URL" do @dummy.avatar.expects(:s3).never @dummy.avatar.expects(:s3_bucket).never assert_match %r{^http://s3\.amazonaws\.com/testing/avatars/original/5k\.png}, @dummy.avatar.url end should "be rewinded after flush_writes" do @dummy.avatar.instance_eval "def after_flush_writes; end" files = @dummy.avatar.queued_for_write.values.each(&:read) @dummy.save assert files.none?(&:eof?), "Expect all the files to be rewinded." end should "be removed after after_flush_writes" do paths = @dummy.avatar.queued_for_write.values.map(&:path) @dummy.save assert paths.none?{ |path| File.exists?(path) }, "Expect all the files to be deleted." end context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read) @dummy.save end should "succeed" do assert true end end context "and saved without a bucket" do setup do AWS::S3::BucketCollection.any_instance.expects(:create).with("testing") AWS::S3::S3Object.any_instance.stubs(:write). raises(AWS::S3::Errors::NoSuchBucket.new(stub, stub(:status => 404, :body => ""))). then.returns(nil) @dummy.save end should "succeed" do assert true end end context "and remove" do setup do AWS::S3::S3Object.any_instance.stubs(:exists?).returns(true) AWS::S3::S3Object.any_instance.stubs(:delete) @dummy.destroy end should "succeed" do assert true end end context 'that the file were missing' do setup do AWS::S3::S3Object.any_instance.stubs(:exists?).raises(AWS::Errors::Base) end should 'return false on exists?' do assert !@dummy.avatar.exists? end end end end context "An attachment with S3 storage and bucket defined as a Proc" do setup do rebuild_model :storage => :s3, :bucket => lambda { |attachment| "bucket_#{attachment.instance.other}" }, :s3_credentials => {:not => :important} end should "get the right bucket name" do assert "bucket_a", Dummy.new(:other => 'a').avatar.bucket_name assert "bucket_a", Dummy.new(:other => 'a').avatar.s3_bucket.name assert "bucket_b", Dummy.new(:other => 'b').avatar.bucket_name assert "bucket_b", Dummy.new(:other => 'b').avatar.s3_bucket.name end end context "An attachment with S3 storage and S3 credentials defined as a Proc" do setup do rebuild_model :storage => :s3, :bucket => {:not => :important}, :s3_credentials => lambda { |attachment| Hash['access_key_id' => "access#{attachment.instance.other}", 'secret_access_key' => "secret#{attachment.instance.other}"] } end should "get the right credentials" do assert "access1234", Dummy.new(:other => '1234').avatar.s3_credentials[:access_key_id] assert "secret1234", Dummy.new(:other => '1234').avatar.s3_credentials[:secret_access_key] end end context "An attachment with S3 storage and S3 credentials with a :credential_provider" do setup do class DummyCredentialProvider; end rebuild_model :storage => :s3, :bucket => "testing", :s3_credentials => { :credential_provider => DummyCredentialProvider.new } @dummy = Dummy.new end should "set the credential-provider" do assert_kind_of DummyCredentialProvider, @dummy.avatar.s3_bucket.config.credential_provider end end context "An attachment with S3 storage and S3 credentials in an unsupported manor" do setup do rebuild_model :storage => :s3, :bucket => "testing", :s3_credentials => ["unsupported"] @dummy = Dummy.new end should "not accept the credentials" do assert_raise(ArgumentError) do @dummy.avatar.s3_credentials end end end context "An attachment with S3 storage and S3 credentials not supplied" do setup do rebuild_model :storage => :s3, :bucket => "testing" @dummy = Dummy.new end should "not parse any credentials" do assert_equal({}, @dummy.avatar.s3_credentials) end end context "An attachment with S3 storage and specific s3 headers set" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_headers => {'Cache-Control' => 'max-age=31557600'} end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read, :cache_control => 'max-age=31557600') @dummy.save end should "succeed" do assert true end end end end context "An attachment with S3 storage and metadata set using header names" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_headers => {'x-amz-meta-color' => 'red'} end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read, :metadata => { "color" => "red" }) @dummy.save end should "succeed" do assert true end end end end context "An attachment with S3 storage and metadata set using the :s3_metadata option" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_metadata => { "color" => "red" } end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read, :metadata => { "color" => "red" }) @dummy.save end should "succeed" do assert true end end end end context "An attachment with S3 storage and storage class set using the header name" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_headers => { "x-amz-storage-class" => "reduced_redundancy" } end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read, :storage_class => "reduced_redundancy") @dummy.save end should "succeed" do assert true end end end end context "Can disable AES256 encryption multiple ways" do [nil, false, ''].each do |tech| setup do rebuild_model( :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321"}, :s3_server_side_encryption => tech) end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read) @dummy.save end should "succeed" do assert true end end end end end context "An attachment with S3 storage and using AES256 encryption" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_server_side_encryption => :aes256 end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read, :server_side_encryption => 'AES256') @dummy.save end should "succeed" do assert true end end end end context "An attachment with S3 storage and storage class set using the :storage_class option" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_storage_class => :reduced_redundancy end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read, :storage_class => :reduced_redundancy) @dummy.save end should "succeed" do assert true end end end end context "with S3 credentials supplied as Pathname" do setup do ENV['S3_KEY'] = 'pathname_key' ENV['S3_BUCKET'] = 'pathname_bucket' ENV['S3_SECRET'] = 'pathname_secret' rails_env('test') rebuild_model :storage => :s3, :s3_credentials => Pathname.new(fixture_file('s3.yml')) Dummy.delete_all @dummy = Dummy.new end should "parse the credentials" do assert_equal 'pathname_bucket', @dummy.avatar.bucket_name assert_equal 'pathname_key', @dummy.avatar.s3_bucket.config.access_key_id assert_equal 'pathname_secret', @dummy.avatar.s3_bucket.config.secret_access_key end end context "with S3 credentials in a YAML file" do setup do ENV['S3_KEY'] = 'env_key' ENV['S3_BUCKET'] = 'env_bucket' ENV['S3_SECRET'] = 'env_secret' rails_env('test') rebuild_model :storage => :s3, :s3_credentials => File.new(fixture_file('s3.yml')) Dummy.delete_all @dummy = Dummy.new end should "run the file through ERB" do assert_equal 'env_bucket', @dummy.avatar.bucket_name assert_equal 'env_key', @dummy.avatar.s3_bucket.config.access_key_id assert_equal 'env_secret', @dummy.avatar.s3_bucket.config.secret_access_key end end context "S3 Permissions" do context "defaults to :public_read" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" } end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read) @dummy.save end should "succeed" do assert true end end end end context "string permissions set" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_permissions => :private end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do object = stub @dummy.avatar.stubs(:s3_object).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :private) @dummy.save end should "succeed" do assert true end end end end context "hash permissions set" do setup do rebuild_model :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :styles => { :thumb => "80x80>" }, :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_permissions => { :original => :private, :thumb => :public_read } end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do [:thumb, :original].each do |style| object = stub @dummy.avatar.stubs(:s3_object).with(style).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => style == :thumb ? :public_read : :private) end @dummy.save end should "succeed" do assert true end end end end context "proc permission set" do setup do rebuild_model( :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :styles => { :thumb => "80x80>" }, :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_permissions => lambda {|attachment, style| attachment.instance.private_attachment? && style.to_sym != :thumb ? :private : :public_read } ) end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.stubs(:private_attachment? => true) @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do @dummy.save end should "succeed" do assert @dummy.avatar.url().include? "https://" assert @dummy.avatar.url(:thumb).include? "http://" end end end end end context "An attachment with S3 storage and metadata set using a proc as headers" do setup do rebuild_model( :storage => :s3, :bucket => "testing", :path => ":attachment/:style/:basename.:extension", :styles => { :thumb => "80x80>" }, :s3_credentials => { 'access_key_id' => "12345", 'secret_access_key' => "54321" }, :s3_headers => lambda {|attachment| {'Content-Disposition' => "attachment; filename=\"#{attachment.name}\""} } ) end context "when assigned" do setup do @file = File.new(fixture_file('5k.png'), 'rb') @dummy = Dummy.new @dummy.stubs(:name => 'Custom Avatar Name.png') @dummy.avatar = @file end teardown { @file.close } context "and saved" do setup do [:thumb, :original].each do |style| object = stub @dummy.avatar.stubs(:s3_object).with(style).returns(object) object.expects(:write).with(anything, :content_type => "image/png", :acl => :public_read, :content_disposition => 'attachment; filename="Custom Avatar Name.png"') end @dummy.save end should "succeed" do assert true end end end end end