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

