require 'spec_helper'
require 'rubygems'

  describe Jettywrapper do
    
    # JETTY1 = 
    before(:all) do
      @jetty_params = {
        :quiet => false,
        :jetty_home => "/path/to/jetty",
        :jetty_port => TEST_JETTY_PORTS.first,
        :solr_home => "/path/to/solr",
        :startup_wait => 0,
        :java_opts => ["-Xmx256m"],
        :jetty_opts => ["/path/to/jetty_xml", "/path/to/other_jetty_xml"]  
      }

      Jettywrapper.logger.level=3
    end

    context "downloading" do
      context "with default file" do
        it "should download the zip file" do
          Jettywrapper.should_receive(:system).with('curl -L https://github.com/projecthydra/hydra-jetty/archive/new-solr-schema.zip -o tmp/new-solr-schema.zip').and_return(system ('true'))
          Jettywrapper.download
        end
      end

      context "specifying the file" do
        it "should download the zip file" do
          Jettywrapper.should_receive(:system).with('curl -L http://example.co/file.zip -o tmp/file.zip').and_return(system ('true'))
          Jettywrapper.download('http://example.co/file.zip')
          Jettywrapper.url.should == 'http://example.co/file.zip'
        end
      end
    end

    context "unzip" do
      before do
        Jettywrapper.url = nil
      end
      context "with default file" do
        it "should download the zip file" do
          File.should_receive(:exists?).and_return(true)
          Jettywrapper.should_receive(:expanded_zip_dir).and_return('tmp/jetty_generator/hydra-jetty-new-solr-schema')
          Jettywrapper.should_receive(:system).with('unzip -d tmp/jetty_generator -qo tmp/new-solr-schema.zip').and_return(system ('true'))
          Jettywrapper.should_receive(:system).with('mv tmp/jetty_generator/hydra-jetty-new-solr-schema jetty').and_return(system ('true'))
          Jettywrapper.unzip
        end
      end

      context "specifying the file" do
        before do
          Jettywrapper.url = 'http://example.co/file.zip'
        end
        it "should download the zip file" do
          File.should_receive(:exists?).and_return(true)
          Jettywrapper.should_receive(:expanded_zip_dir).and_return('tmp/jetty_generator/interal_dir')
          Jettywrapper.should_receive(:system).with('unzip -d tmp/jetty_generator -qo tmp/file.zip').and_return(system ('true'))
          Jettywrapper.should_receive(:system).with('mv tmp/jetty_generator/interal_dir jetty').and_return(system ('true'))
          Jettywrapper.unzip
        end
      end
    end

    context ".url" do
      before do
        subject.url = nil
      end
      subject {Jettywrapper}
      context "When a constant is set" do
        before do
          ZIP_URL = 'http://example.com/foo.zip'
        end
        after do
          Object.send(:remove_const, :ZIP_URL)
        end
        its(:url) {should == 'http://example.com/foo.zip'}
      end
      context "when a url is set" do
        before do
          subject.url = 'http://example.com/bar.zip'
        end
        its(:url) {should == 'http://example.com/bar.zip'}
      end
      context "when url is not set" do
        its(:url) {should == 'https://github.com/projecthydra/hydra-jetty/archive/new-solr-schema.zip'}
      end
    end

    context ".tmp_dir" do
      subject {Jettywrapper}
      context "when a dir is set" do
        before do
          subject.tmp_dir = '/opt/tmp'
        end
        its(:tmp_dir) {should == '/opt/tmp'}
      end
      context "when dir is not set" do
        before do
          subject.tmp_dir = nil
        end
        its(:tmp_dir) {should == 'tmp'}
      end

    end

    context "app_root" do
      subject {Jettywrapper}
      context "When rails is present" do
        before do
          Jettywrapper.reset_config
          class Rails
            def self.root
              'rails_root'
            end
          end
        end
        after do
          Object.send(:remove_const, :Rails)
          Jettywrapper.reset_config
        end
        its(:app_root) {should == 'rails_root'}
      end
      context "When APP_ROOT is set" do
        before do
          APP_ROOT = 'custom_root'
        end
        after do
          Object.send(:remove_const, :APP_ROOT)
          Jettywrapper.reset_config
        end
        its(:app_root) {should == 'custom_root'}
      end
      context "otherwise" do
        its(:app_root) {should == '.'}
      end
    end

    context "config" do
      it "loads the application jetty.yml first" do
        IO.should_receive(:read).with('./config/jetty.yml').and_return("default:\n")
        config = Jettywrapper.load_config
      end

      it "loads the application jetty.yml using erb parsing" do
        IO.should_receive(:read).with('./config/jetty.yml').and_return("default:\n  a: <%= 123 %>")
        config = Jettywrapper.load_config
        config[:a] == 123
      end

      it "falls back on the distributed jetty.yml" do
        File.should_receive(:exists?).with('./config/jetty.yml').and_return(false)
        IO.should_receive(:read).with { |value| value =~ /jetty.yml/ }.and_return("default:\n")
        config = Jettywrapper.load_config
      end

      it "supports per-environment configuration" do
        ENV['environment'] = 'test'
        IO.should_receive(:read).with('./config/jetty.yml').and_return("default:\n  a: 1\ntest:\n  a: 2")
        config = Jettywrapper.load_config
        config[:a].should == 2
      end

      it "falls back on a 'default' environment configuration" do
        ENV['environment'] = 'test'
        IO.should_receive(:read).with('./config/jetty.yml').and_return("default:\n  a: 1")
        config = Jettywrapper.load_config
        config[:a].should == 1
      end
    end
    
    context "instantiation" do
      it "can be instantiated" do
        ts = Jettywrapper.instance
        ts.class.should eql(Jettywrapper)
      end

      it "can be configured with a params hash" do
        ts = Jettywrapper.configure(@jetty_params) 
        ts.quiet.should == false
        ts.jetty_home.should == "/path/to/jetty"
        ts.port.should == @jetty_params[:jetty_port]
        ts.solr_home.should == '/path/to/solr'
        ts.startup_wait.should == 0
        ts.jetty_opts.should == @jetty_params[:jetty_opts]
      end

      # passing in a hash is no longer optional
      it "raises an error when called without a :jetty_home value" do
          lambda { ts = Jettywrapper.configure }.should raise_exception
      end

      it "should override nil params with defaults" do
        jetty_params = {
          :quiet => nil,
          :jetty_home => '/path/to/jetty',
          :jetty_port => nil,
          :solr_home => nil,
          :startup_wait => nil,
          :jetty_opts => nil
        }

        ts = Jettywrapper.configure(jetty_params) 
        ts.quiet.should == true
        ts.jetty_home.should == "/path/to/jetty"
        ts.port.should == 8888
        ts.solr_home.should == File.join(ts.jetty_home, "solr")
        ts.startup_wait.should == 5
        ts.jetty_opts.should == []
      end
      
      it "passes all the expected values to jetty during startup" do
        ts = Jettywrapper.configure(@jetty_params) 
        command = ts.jetty_command
        command.should include("-Dsolr.solr.home=#{@jetty_params[:solr_home]}")
        command.should include("-Djetty.port=#{@jetty_params[:jetty_port]}")
        command.should include("-Xmx256m")
        command.should include("start.jar")
        command.slice(command.index('start.jar')+1, 2).should == @jetty_params[:jetty_opts]
      end

      it "escapes the :solr_home parameter" do
        ts = Jettywrapper.configure(@jetty_params.merge(:solr_home => '/path with spaces/to/solr'))
        command = ts.jetty_command
        command.should include("-Dsolr.solr.home=/path\\ with\\ spaces/to/solr")
      end
      
      it "has a pid if it has been started" do
        jetty_params = {
          :jetty_home => '/tmp'
        }
        ts = Jettywrapper.configure(jetty_params) 
        Jettywrapper.any_instance.stub(:process).and_return(stub('proc', :start => nil, :pid=>5454))
        ts.stop
        ts.start
        ts.pid.should eql(5454)
        ts.stop
      end
      
      it "can pass params to a start method" do
        jetty_params = {
          :jetty_home => '/tmp', :jetty_port => 8777
        }
        ts = Jettywrapper.configure(jetty_params) 
        ts.stop
        Jettywrapper.any_instance.stub(:process).and_return(stub('proc', :start => nil, :pid=>2323))
        swp = Jettywrapper.start(jetty_params)
        swp.pid.should eql(2323)
        swp.pid_file.should eql("_tmp.pid")
        swp.stop
      end
      
      it "checks to see if its pid files are stale"
      
      # return true if it's running, otherwise return false
      it "can get the status for a given jetty instance" do
        # Don't actually start jetty, just fake it
        Jettywrapper.any_instance.stub(:process).and_return(stub('proc', :start => nil, :pid=>12345))
        
        jetty_params = {
          :jetty_home => File.expand_path("#{File.dirname(__FILE__)}/../../jetty")
        }
        Jettywrapper.stop(jetty_params)
        Jettywrapper.is_jetty_running?(jetty_params).should eql(false)
        Jettywrapper.start(jetty_params)
        Jettywrapper.is_jetty_running?(jetty_params).should eql(true)
        Jettywrapper.stop(jetty_params)
      end
      
      it "can get the pid for a given jetty instance" do
        # Don't actually start jetty, just fake it
        Jettywrapper.any_instance.stub(:process).and_return(stub('proc', :start => nil, :pid=>54321))
        jetty_params = {
          :jetty_home => File.expand_path("#{File.dirname(__FILE__)}/../../jetty")
        }
        Jettywrapper.stop(jetty_params)
        Jettywrapper.pid(jetty_params).should eql(nil)
        Jettywrapper.start(jetty_params)
        Jettywrapper.pid(jetty_params).should eql(54321)
        Jettywrapper.stop(jetty_params)
      end
      
      it "can pass params to a stop method" do
        jetty_params = {
          :jetty_home => '/tmp', :jetty_port => 8777
        }
        Jettywrapper.any_instance.stub(:process).and_return(stub('proc', :start => nil, :pid=>2323))
        swp = Jettywrapper.start(jetty_params)
        (File.file? swp.pid_path).should eql(true)
        
        swp = Jettywrapper.stop(jetty_params)
        (File.file? swp.pid_path).should eql(false)
      end
      
      it "knows what its pid file should be called" do
        ts = Jettywrapper.configure(@jetty_params) 
        ts.pid_file.should eql("_path_to_jetty.pid")
      end
      
      it "knows where its pid file should be written" do
        ts = Jettywrapper.configure(@jetty_params) 
        ts.pid_dir.should eql(File.expand_path("#{ts.base_path}/tmp/pids"))
      end
      
      it "writes a pid to a file when it is started" do
        jetty_params = {
          :jetty_home => '/tmp'
        }
        ts = Jettywrapper.configure(jetty_params) 
        Jettywrapper.any_instance.stub(:process).and_return(stub('proc', :start => nil, :pid=>2222))
        ts.stop
        ts.pid_file?.should eql(false)
        ts.start
        ts.pid.should eql(2222)
        ts.pid_file?.should eql(true)
        pid_from_file = File.open( ts.pid_path ) { |f| f.gets.to_i }
        pid_from_file.should eql(2222)
      end
      
    end # end of instantiation context
    
    context "logging" do
      it "has a logger" do
        ts = Jettywrapper.configure(@jetty_params) 
        ts.logger.should be_kind_of(Logger)
      end
      
    end # end of logging context 
    
    context "wrapping a task" do
      it "wraps another method" do
        Jettywrapper.any_instance.stub(:start).and_return(true)
        Jettywrapper.any_instance.stub(:stop).and_return(true)
        error = Jettywrapper.wrap(@jetty_params) do            
        end
        error.should eql(false)
      end
      
      it "configures itself correctly when invoked via the wrap method" do
        Jettywrapper.any_instance.stub(:start).and_return(true)
        Jettywrapper.any_instance.stub(:stop).and_return(true)
        error = Jettywrapper.wrap(@jetty_params) do 
          ts = Jettywrapper.instance 
          ts.quiet.should == @jetty_params[:quiet]
          ts.jetty_home.should == "/path/to/jetty"
          ts.port.should == @jetty_params[:jetty_port]
          ts.solr_home.should == "/path/to/solr"
          ts.startup_wait.should == 0   
        end
        error.should eql(false)
      end
      
      it "captures any errors produced" do
        Jettywrapper.any_instance.stub(:start).and_return(true)
        Jettywrapper.any_instance.stub(:stop).and_return(true)
        Jettywrapper.instance.logger.should_receive(:error).with("*** Error starting jetty: this is an expected error message")
        expect { error = Jettywrapper.wrap(@jetty_params) do 
          raise "this is an expected error message"
        end }.to raise_error "this is an expected error message"
      end
      
    end # end of wrapping context

    context "quiet mode", :quiet => true do
      it "inherits the current stderr/stdout in 'loud' mode" do
        ts = Jettywrapper.configure(@jetty_params.merge(:quiet => false))
        process = ts.process
        process.io.stderr.should == $stderr
        process.io.stdout.should == $stdout
      end

      it "redirect stderr/stdout to a log file in quiet mode" do
        ts = Jettywrapper.configure(@jetty_params.merge(:quiet => true))
        process = ts.process
        process.io.stderr.should_not == $stderr
        process.io.stdout.should_not == $stdout
      end
    end
  end