require 'spec_helper'
require 'rest-client'

describe RecordedBackingTrack 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)
    @backing_track = FactoryGirl.create(:backing_track, :connection => @connection)
    @recording = FactoryGirl.create(:recording, :music_session => @music_session, :owner => @user)
  end

  it "should copy from a regular track properly" do
    @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)

    @recorded_backing_track.user.id.should == @backing_track.connection.user.id
    @recorded_backing_track.filename.should == @backing_track.filename
    @recorded_backing_track.next_part_to_upload.should == 0
    @recorded_backing_track.fully_uploaded.should == false
    @recorded_backing_track.client_id = @connection.client_id
    @recorded_backing_track.backing_track_id = @backing_track.id
  end

  it "should update the next part to upload properly" do
    @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
    @recorded_backing_track.upload_part_complete(1, 1000)
    @recorded_backing_track.errors.any?.should be_true
    @recorded_backing_track.errors[:length][0].should == "is too short (minimum is 1 character)"
    @recorded_backing_track.errors[:md5][0].should == "can't be blank"
  end

  it "properly finds a recorded track given its upload filename" do
    @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
    @recorded_backing_track.save.should be_true
    RecordedBackingTrack.find_by_recording_id_and_backing_track_id(@recorded_backing_track.recording_id, @recorded_backing_track.backing_track_id).should == @recorded_backing_track
  end

  it "gets a url for the track" do
    @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
    @recorded_backing_track.errors.any?.should be_false
    @recorded_backing_track[:url].should == "recordings/#{@recorded_backing_track.created_at.strftime('%m-%d-%Y')}/#{@recording.id}/backing-track-#{@backing_track.client_track_id}.ogg"
  end

  it "signs url" do
    stub_const("APP_CONFIG", app_config)
    @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
    @recorded_backing_track.sign_url.should_not be_nil
  end

  it "can not be downloaded if no claimed recording" do
    user2 = FactoryGirl.create(:user)
    @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
    @recorded_backing_track.can_download?(user2).should be_false
    @recorded_backing_track.can_download?(@user).should be_false
  end

  it "can be downloaded if there is a claimed recording" do
    @recorded_track = RecordedTrack.create_from_track(@track, @recording)
    @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
    @recording.claim(@user, "my recording", "my description", Genre.first, true).errors.any?.should be_false
    @recorded_backing_track.can_download?(@user).should be_true
  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
      @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
      @recorded_backing_track.upload_start(1000, "abc")
      @recorded_backing_track.upload_part_complete(1, 1000)
      @recorded_backing_track.errors.any?.should be_true
      @recorded_backing_track.errors[:next_part_to_upload][0].should == ValidationMessages::PART_NOT_STARTED
    end

    it "no parts" do
      @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
      @recorded_backing_track.upload_start(1000, "abc")
      @recorded_backing_track.upload_next_part(1000, "abc")
      @recorded_backing_track.errors.any?.should be_false
      @recorded_backing_track.upload_part_complete(1, 1000)
      @recorded_backing_track.errors.any?.should be_true
      @recorded_backing_track.errors[:next_part_to_upload][0].should == ValidationMessages::PART_NOT_FOUND_IN_AWS
    end

    it "enough part failures reset the upload" do
      @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
      @recorded_backing_track.upload_start(File.size(upload_file), md5)
      @recorded_backing_track.upload_next_part(File.size(upload_file), md5)
      @recorded_backing_track.errors.any?.should be_false
      APP_CONFIG.max_track_part_upload_failures.times do |i|
        @recorded_backing_track.upload_part_complete(@recorded_backing_track.next_part_to_upload, File.size(upload_file))
        @recorded_backing_track.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
        @recorded_backing_track.reload
        @recorded_backing_track.is_part_uploading.should == expected_is_part_uploading
        @recorded_backing_track.part_failures.should == expected_part_failures
      end

      @recorded_backing_track.reload
      @recorded_backing_track.upload_failures.should == 1
      @recorded_backing_track.file_offset.should == 0
      @recorded_backing_track.next_part_to_upload.should == 0
      @recorded_backing_track.upload_id.should be_nil
      @recorded_backing_track.md5.should be_nil
      @recorded_backing_track.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)
      @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
      APP_CONFIG.max_track_upload_failures.times do |j|
        @recorded_backing_track.upload_start(File.size(upload_file), md5)
        @recorded_backing_track.upload_next_part(File.size(upload_file), md5)
        @recorded_backing_track.errors.any?.should be_false
        APP_CONFIG.max_track_part_upload_failures.times do |i|
          @recorded_backing_track.upload_part_complete(@recorded_backing_track.next_part_to_upload, File.size(upload_file))
          @recorded_backing_track.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
          @recorded_backing_track.reload
          @recorded_backing_track.is_part_uploading.should == expected_is_part_uploading
          @recorded_backing_track.part_failures.should == expected_part_failures
        end
        @recorded_backing_track.upload_failures.should == j + 1
      end

      @recorded_backing_track.reload
      @recorded_backing_track.upload_failures.should == APP_CONFIG.max_track_upload_failures
      @recorded_backing_track.file_offset.should == 0
      @recorded_backing_track.next_part_to_upload.should == 0
      @recorded_backing_track.upload_id.should be_nil
      @recorded_backing_track.md5.should be_nil
      @recorded_backing_track.length.should == 0

      # try to poke it and get the right kind of error back
      @recorded_backing_track.upload_next_part(File.size(upload_file), md5)
      @recorded_backing_track.errors[:upload_failures] = [ValidationMessages::UPLOAD_FAILURES_EXCEEDED]
    end

    describe "correctly uploaded a file" do

      before do
        @recorded_backing_track  = RecordedBackingTrack.create_from_backing_track(@backing_track, @recording)
        @recorded_backing_track.upload_start(File.size(upload_file), md5)
        @recorded_backing_track.upload_next_part(File.size(upload_file), md5)
        signed_data = @recorded_backing_track.upload_sign(md5)
        @response = put_file_to_aws(signed_data, upload_file_contents)
        @recorded_backing_track.upload_part_complete(@recorded_backing_track.next_part_to_upload, File.size(upload_file))
        @recorded_backing_track.errors.any?.should be_false
        @recorded_backing_track.upload_complete
        @recorded_backing_track.errors.any?.should be_false
        @recorded_backing_track.marking_complete = false
      end

      it "can download an updated file" do
        @response = RestClient.get @recorded_backing_track.sign_url
        @response.body.should == upload_file_contents
      end

      it "can't mark completely uploaded twice" do
        @recorded_backing_track.upload_complete
        @recorded_backing_track.errors.any?.should be_true
        @recorded_backing_track.errors[:fully_uploaded][0].should == "already set"
        @recorded_backing_track.part_failures.should == 0
      end

      it "can't ask for a next part if fully uploaded" do
        @recorded_backing_track.upload_next_part(File.size(upload_file), md5)
        @recorded_backing_track.errors.any?.should be_true
        @recorded_backing_track.errors[:fully_uploaded][0].should == "already set"
        @recorded_backing_track.part_failures.should == 0
      end

      it "can't ask for mark part complete if fully uploaded" do
        @recorded_backing_track.upload_part_complete(1, 1000)
        @recorded_backing_track.errors.any?.should be_true
        @recorded_backing_track.errors[:fully_uploaded][0].should == "already set"
        @recorded_backing_track.part_failures.should == 0
      end
    end
  end
end

