require 'spec_helper' require 'rest-client' describe QuickMix do include UsesTempFiles before do @user = FactoryGirl.create(:user) @connection = FactoryGirl.create(:connection, :user => @user) @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') @music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true) @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) @recording = FactoryGirl.create(:recording, :music_session => @music_session, :owner => @user) end it "should copy from a regular track properly" do @quick_mix = QuickMix.create(@recording, @user) @quick_mix.user.id.should == @track.connection.user.id @quick_mix.next_part_to_upload.should == 0 @quick_mix.fully_uploaded.should == false end it "should update the next part to upload properly" do @quick_mix = QuickMix.create(@recording, @user) @quick_mix.upload_part_complete(1, 1000) @quick_mix.errors.any?.should be_true @quick_mix.errors[:ogg_length][0].should == "is too short (minimum is 1 character)" @quick_mix.errors[:ogg_md5][0].should == "can't be blank" end it "gets a url for the track" do @quick_mix = QuickMix.create(@recording, @user) @quick_mix.errors.any?.should be_false @quick_mix[:ogg_url].should == "recordings/#{@recording.created_at.strftime('%m-%d-%Y')}/#{@recording.id}/stream-mix-#{@quick_mix.id}.ogg" @quick_mix[:mp3_url].should == "recordings/#{@recording.created_at.strftime('%m-%d-%Y')}/#{@recording.id}/stream-mix-#{@quick_mix.id}.mp3" end it "signs url" do stub_const("APP_CONFIG", app_config) @quick_mix = QuickMix.create(@recording, @user) @quick_mix.sign_url.should_not be_nil end describe "aws-based operations", :aws => true do def put_file_to_aws(signed_data, contents) begin RestClient.put( signed_data[:url], contents, { :'Content-Type' => 'audio/ogg', :Date => signed_data[:datetime], :'Content-MD5' => signed_data[:md5], :Authorization => signed_data[:authorization] }) rescue => e puts e.response raise e end end # create a test file upload_file='some_file.ogg' in_directory_with_file(upload_file) upload_file_contents="ogg binary stuff in here" md5 = Base64.encode64(Digest::MD5.digest(upload_file_contents)).chomp test_config = app_config s3_manager = S3Manager.new(test_config.aws_bucket, test_config.aws_access_key_id, test_config.aws_secret_access_key) before do stub_const("APP_CONFIG", app_config) # this block of code will fully upload a sample file to s3 content_for_file(upload_file_contents) s3_manager.delete_folder('recordings') # keep the bucket clean to save cost, and make it easier if post-mortuem debugging end it "cant mark a part complete without having started it" do @quick_mix = QuickMix.create(@recording, @user) @quick_mix.upload_start(1000, "abc") @quick_mix.upload_part_complete(1, 1000) @quick_mix.errors.any?.should be_true @quick_mix.errors[:next_part_to_upload][0].should == ValidationMessages::PART_NOT_STARTED end it "no parts" do @quick_mix = QuickMix.create(@recording, @user) @quick_mix.upload_start(1000, "abc") @quick_mix.upload_next_part(1000, "abc") @quick_mix.errors.any?.should be_false @quick_mix.upload_part_complete(1, 1000) @quick_mix.errors.any?.should be_true @quick_mix.errors[:next_part_to_upload][0].should == ValidationMessages::PART_NOT_FOUND_IN_AWS end it "enough part failures reset the upload" do @quick_mix = QuickMix.create(@recording, @user) @quick_mix.upload_start(File.size(upload_file), md5) @quick_mix.upload_next_part(File.size(upload_file), md5) @quick_mix.errors.any?.should be_false APP_CONFIG.max_track_part_upload_failures.times do |i| @quick_mix.upload_part_complete(@quick_mix.next_part_to_upload, File.size(upload_file)) @quick_mix.errors[:next_part_to_upload] == [ValidationMessages::PART_NOT_FOUND_IN_AWS] part_failure_rollover = i == APP_CONFIG.max_track_part_upload_failures - 1 expected_is_part_uploading = !part_failure_rollover expected_part_failures = part_failure_rollover ? 0 : i + 1 @quick_mix.reload @quick_mix.is_part_uploading.should == expected_is_part_uploading @quick_mix.part_failures.should == expected_part_failures end @quick_mix.reload @quick_mix.upload_failures.should == 1 @quick_mix.file_offset.should == 0 @quick_mix.next_part_to_upload.should == 0 @quick_mix.upload_id.should be_nil @quick_mix.ogg_md5.should be_nil @quick_mix.ogg_length.should == 0 end it "enough upload failures fails the upload forever" do APP_CONFIG.stub(:max_track_upload_failures).and_return(1) APP_CONFIG.stub(:max_track_part_upload_failures).and_return(2) @quick_mix = QuickMix.create(@recording, @user) APP_CONFIG.max_track_upload_failures.times do |j| @quick_mix.upload_start(File.size(upload_file), md5) @quick_mix.upload_next_part(File.size(upload_file), md5) @quick_mix.errors.any?.should be_false APP_CONFIG.max_track_part_upload_failures.times do |i| @quick_mix.upload_part_complete(@quick_mix.next_part_to_upload, File.size(upload_file)) @quick_mix.errors[:next_part_to_upload] == [ValidationMessages::PART_NOT_FOUND_IN_AWS] part_failure_rollover = i == APP_CONFIG.max_track_part_upload_failures - 1 expected_is_part_uploading = part_failure_rollover ? false : true expected_part_failures = part_failure_rollover ? 0 : i + 1 @quick_mix.reload @quick_mix.is_part_uploading.should == expected_is_part_uploading @quick_mix.part_failures.should == expected_part_failures end @quick_mix.upload_failures.should == j + 1 end @quick_mix.reload @quick_mix.upload_failures.should == APP_CONFIG.max_track_upload_failures @quick_mix.file_offset.should == 0 @quick_mix.next_part_to_upload.should == 0 @quick_mix.upload_id.should be_nil @quick_mix.ogg_md5.should be_nil @quick_mix.ogg_length.should == 0 # try to poke it and get the right kind of error back @quick_mix.upload_next_part(File.size(upload_file), md5) @quick_mix.errors[:upload_failures] = [ValidationMessages::UPLOAD_FAILURES_EXCEEDED] end describe "correctly uploaded a file" do before do @quick_mix = QuickMix.create(@recording, @user) @quick_mix.upload_start(File.size(upload_file), md5) @quick_mix.upload_next_part(File.size(upload_file), md5) signed_data = @quick_mix.upload_sign(md5) @response = put_file_to_aws(signed_data, upload_file_contents) @quick_mix.upload_part_complete(@quick_mix.next_part_to_upload, File.size(upload_file)) @quick_mix.errors.any?.should be_false @quick_mix.upload_complete @quick_mix.marking_complete = false # this is a side effect of using the same model throughout this process; controllers wouldn't see this @quick_mix.finish(File.size(upload_file), md5) @quick_mix.errors.any?.should be_false # let's verify that .finish sets everything it should @quick_mix.mp3_length.should == File.size(upload_file) @quick_mix.mp3_md5.should == md5 @quick_mix.completed.should be_true @quick_mix.recording.reload @quick_mix.recording.first_quick_mix_id.should == @quick_mix.id end it "can download an updated file" do @response = RestClient.get @quick_mix.sign_url @response.body.should == upload_file_contents end it "can't mark completely uploaded twice" do @quick_mix.upload_complete @quick_mix.errors.any?.should be_true @quick_mix.errors[:fully_uploaded][0].should == "already set" @quick_mix.part_failures.should == 0 end it "can't ask for a next part if fully uploaded" do @quick_mix.upload_next_part(File.size(upload_file), md5) @quick_mix.errors.any?.should be_true @quick_mix.errors[:fully_uploaded][0].should == "already set" @quick_mix.part_failures.should == 0 end it "can't ask for mark part complete if fully uploaded" do @quick_mix.upload_part_complete(1, 1000) @quick_mix.errors.any?.should be_true @quick_mix.errors[:fully_uploaded][0].should == "already set" @quick_mix.part_failures.should == 0 end end end end