require 'spec_helper'
require 'fileutils'

# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests
describe AudioMixer do

  include UsesTempFiles

  let(:audiomixer) { AudioMixer.new }
  let(:manifest) { {} }
  let(:valid_manifest) { make_manifest(manifest) }

  def valid_files(manifest)
    manifest["files"] = [ {"codec" => "vorbis", "offset" => 0, "filename" => "/some/path"} ]
  end
  def valid_output(manifest)
    manifest["output"] = { "codec" => "vorbis" }
  end
  def valid_timeline(manifest)
    manifest["timeline"] = []
  end
  def valid_recording(manifest)
    manifest["recording_id"] = "record1"
  end
  def valid_mix(manifest)
    manifest["mix_id"] = "mix1"
  end

  def make_manifest(manifest)
    valid_files(manifest)
    valid_output(manifest)
    valid_timeline(manifest)
    valid_recording(manifest)
    valid_mix(manifest)
    return manifest
  end


  before(:each) do
    stub_const("APP_CONFIG", app_config)
    module EM
      @next_tick_queue = []
    end

    MQRouter.client_exchange = double("client_exchange")
    MQRouter.user_exchange = double("user_exchange")
    MQRouter.subscription_exchange = double("subscription_exchange")
    MQRouter.client_exchange.should_receive(:publish).any_number_of_times
    MQRouter.user_exchange.should_receive(:publish).any_number_of_times
    MQRouter.subscription_exchange.should_receive(:publish).any_number_of_times
  end


  describe "validate" do

    it "no manifest" do
      expect { audiomixer.validate }.to raise_error("no manifest specified")
    end

    it "no files specified" do
      audiomixer.manifest = manifest
      expect { audiomixer.validate }.to raise_error("no files specified")
    end

    it "no codec specified" do
      audiomixer.manifest = { "files" => [ {"offset" => 0, "filename" => "/some/path"} ] }
      expect { audiomixer.validate }.to raise_error("no codec specified")
    end

    it "no offset specified" do
      audiomixer.manifest = { "files" => [ {"codec" => "vorbis", "filename" => "/some/path"} ] }
      expect { audiomixer.validate }.to raise_error("no offset specified")
    end

    it "no output specified" do
      valid_files(manifest)
      audiomixer.manifest = manifest
      audiomixer.manifest = { "files" => [ {"codec" => "vorbis", "offset" => 0, "filename" => "/some/path"} ] }
      expect { audiomixer.validate }.to raise_error("no output specified")
    end

    it "no recording_id specified" do
      valid_files(manifest)
      valid_output(manifest)
      valid_timeline(manifest)
      valid_mix(manifest)
      audiomixer.manifest = manifest
      expect { audiomixer.validate }.to raise_error("no recording_id specified")
    end

    it "no mix_id specified" do
      valid_files(manifest)
      valid_output(manifest)
      valid_timeline(manifest)
      valid_recording(manifest)
      audiomixer.manifest = manifest
      expect { audiomixer.validate }.to raise_error("no mix_id specified")
    end
  end


  describe "fetch_audio_files" do
    it "get upset if file doesn't exist" do
      audiomixer.manifest = { "files" => [ {"codec" => "vorbis", "offset" => 0, "filename" => "/some/bogus/path"} ] }
      expect { audiomixer.fetch_audio_files }.to raise_error("no file located at: /some/bogus/path")
    end
  end

  describe "prepare_manifest" do
    it "writes manifest as json to file" do
      audiomixer.manifest = valid_manifest
      audiomixer.prepare_manifest

      File.read(audiomixer.manifest_file).should == valid_manifest.to_json
    end
  end


  describe "prepare_output" do
    it "can be written to" do
      audiomixer.manifest = valid_manifest
      audiomixer.prepare_output

      File.open(audiomixer.manifest[:output][:filename] ,"w") do |f|
        f.write("tickle")
      end

      File.read(audiomixer.manifest[:output][:filename]).should == "tickle"
    end
  end


  describe "prepare_error_out" do
    it "can be written to" do
      audiomixer.manifest = valid_manifest
      audiomixer.prepare_error_out

      File.open(audiomixer.manifest[:error_out] ,"w") do |f|
        f.write("some_error")
      end

      File.read(audiomixer.manifest[:error_out]).should == "some_error"
    end
  end
=begin

  describe "integration" do

    sample_ogg='sample.ogg'
    in_directory_with_file(sample_ogg)


    before(:each) do
      content_for_file("ogg goodness")
      @user = FactoryGirl.create(:user)
      @connection = FactoryGirl.create(:connection, :user => @user)
      @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument')
      @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument)
      @music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true)
      # @music_session.connections << @connection
      @music_session.save
      @connection.join_the_session(@music_session, true, [@track], @user, 10)
      @recording = Recording.start(@music_session, @user)
      @recording.stop
      @recording.claim(@user, "name", "description", Genre.first, true)
      @recording.errors.any?.should be_false
    end

    describe "simulated" do

      let (:local_files_manifest) {
        {
            :files =>  [ { :codec => :vorbis, :offset => 0, :filename => sample_ogg} ],
            :output => { :codec => :vorbis },
            :timeline => [ {:timestamp => 0, :mix => [{:balance => 0, :level => 100}]} ],
            :recording_id => "recording1"
        }
      }
      
      # stub out methods that are environmentally sensitive (so as to skip s3, and not run an actual audiomixer)
      before(:each) do
        AudioMixer.any_instance.stub(:execute) do |manifest_file|
          output_ogg_filename = JSON.parse(File.read(manifest_file))['output']['filename']
          FileUtils.touch output_ogg_filename
          output_mp3_filename = JSON.parse(File.read(manifest_file))['output']['filename_mp3']
          FileUtils.touch output_mp3_filename
        end

        AudioMixer.any_instance.stub(:postback) # don't actually post resulting off file up
      end

      describe "perform" do

        # this case does not talk to redis, does not run the real audiomixer, and does not actually talk with s3
        # but it does talk to the database and verifies all the other logic
        it "success" do
          Mix.any_instance.stub(:manifest).and_return(local_files_manifest) # don't actually post resulting off file up
          @mix = Mix.schedule(@recording)
          @mix.reload
          @mix.started_at.should_not be_nil
          AudioMixer.perform(@mix.id, @mix.sign_url(60 * 60 * 24, 'ogg'), @mix.sign_url(60 * 60 * 24, 'mp3'))
          @mix.reload
          @mix.completed.should be_true
          @mix.ogg_length.should == 0
          @mix.ogg_md5.should == 'd41d8cd98f00b204e9800998ecf8427e'  # that's the md5 of a touched file
          @mix.mp3_length.should == 0
          @mix.mp3_md5.should == 'd41d8cd98f00b204e9800998ecf8427e'  # that's the md5 of a touched file
        end

        it "errored" do
          local_files_manifest[:files][0][:filename] = '/some/path/to/nowhere'
          Mix.any_instance.stub(:manifest).and_return(local_files_manifest)
          @mix = Mix.schedule(@recording)
          expect{ AudioMixer.perform(@mix.id, @mix.sign_url(60 * 60 * 24, 'ogg'),  @mix.sign_url(60 * 60 * 24, 'mp3')) }.to raise_error
          @mix.reload
          @mix.completed.should be_false
          @mix.error_count.should == 1
          @mix.error_reason.should == "unhandled-job-exception"
          @mix.error_detail.should == "no file located at: /some/path/to/nowhere"
        end
      end

      describe "with resque-spec" do

        before(:each) do
          ResqueSpec.reset!
        end

        it "should have been enqueued because mix got scheduled" do
          Mix.any_instance.stub(:manifest).and_return(local_files_manifest)
          @mix = Mix.schedule(@recording)
          AudioMixer.should have_queue_size_of(1)
        end

        it "should actually run the job" do
          with_resque do
            Mix.any_instance.stub(:manifest).and_return(local_files_manifest)
            @mix = Mix.schedule(@recording)
          end

          @mix.reload
          @mix.completed.should be_true
          @mix.ogg_length.should == 0
          @mix.ogg_md5.should == 'd41d8cd98f00b204e9800998ecf8427e'  # that's the md5 of a touched file, which is what the stubbed :execute does
        end

        it "bails out with no error if already completed" do
          with_resque do
            Mix.any_instance.stub(:manifest).and_return(local_files_manifest)
            @mix = Mix.schedule(@recording)
          end

          @mix.reload
          @mix.completed.should be_true

          with_resque do
            @mix.enqueue
          end

          @mix.reload
          @mix.completed.should be_true
        end
      end
    end

    # these tests try to run the job with minimal faking.   Here's what we still fake:
    # we don't run audiomixer. audiomixer is tested already
    # we don't run redis and actual resque, because that's tested by resque/resque-spec
    describe "full", :aws => true do

      let (:s3_manifest) {
        {
            :files =>  [ { :codec => :vorbis, :offset => 0, :filename => @s3_sample } ],
            :output => { :codec => :vorbis },
            :timeline => [ {:timestamp => 0, :mix => [{:balance => 0, :level => 100}]} ],
            :recording_id => @recording.id
        }
      }

      before(:each) do
        test_config = app_config
        key = "audiomixer/#{sample_ogg}"
        @s3_manager = S3Manager.new(test_config.aws_bucket, test_config.aws_access_key_id, test_config.aws_secret_access_key)

        # put a file in s3
        @s3_manager.upload(key, sample_ogg)

        # create a signed url that the job will use to fetch it back down
        @s3_sample = @s3_manager.sign_url(key, :secure => false)

        AudioMixer.any_instance.stub(:execute) do |manifest_file|
          output_filename_ogg = JSON.parse(File.read(manifest_file))['output']['filename']
          FileUtils.touch output_filename_ogg
          output_filename_mp3 = JSON.parse(File.read(manifest_file))['output']['filename_mp3']
          FileUtils.touch output_filename_mp3
        end
      end

      it "completes" do
        with_resque do
          Mix.any_instance.stub(:manifest).and_return(s3_manifest)
          @mix = Mix.schedule(@recording)
        end

        @mix.reload
        @mix.completed.should be_true
        @mix.ogg_length.should == 0
        @mix.ogg_md5.should == 'd41d8cd98f00b204e9800998ecf8427e'  # that's the md5 of a touched file, which is what the stubbed :execute does
        @mix.mp3_length.should == 0
        @mix.mp3_md5.should == 'd41d8cd98f00b204e9800998ecf8427e'  # that's the md5 of a touched file, which is what the stubbed :execute does
      end
    end
  end
=end

end
