require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

describe "Syslogger" do
  it "should log to the default syslog facility, with the default options" do
    logger = Syslogger.new
    Syslog.should_receive(:open).with($0, Syslog::LOG_PID | Syslog::LOG_CONS, nil).and_yield(syslog=mock("syslog", :mask= => true))
    syslog.should_receive(:log).with(Syslog::LOG_NOTICE, "Some message")
    logger.warn "Some message"
  end

  it "should log to the user facility, with specific options" do
    logger = Syslogger.new("my_app", Syslog::LOG_PID, Syslog::LOG_USER)
    Syslog.should_receive(:open).with("my_app", Syslog::LOG_PID, Syslog::LOG_USER).and_yield(syslog=mock("syslog", :mask= => true))
    syslog.should_receive(:log).with(Syslog::LOG_NOTICE, "Some message")
    logger.warn "Some message"
  end

  %w{debug info warn error fatal unknown}.each do |logger_method|
    it "should respond to the #{logger_method.inspect} method" do
      Syslogger.new.should respond_to logger_method.to_sym
    end

    it "should log #{logger_method} without raising an exception if called with a block" do
      logger = Syslogger.new
      logger.level = Logger.const_get(logger_method.upcase)
      Syslog.stub!(:open).and_yield(syslog=mock("syslog", :mask= => true))
      severity = Syslogger::MAPPING[Logger.const_get(logger_method.upcase)]
      syslog.should_receive(:log).with(severity, "Some message that doesn't need to be in a block")
      lambda {
        logger.send(logger_method.to_sym) { "Some message that doesn't need to be in a block" }
      }.should_not raise_error
    end

    it "should log #{logger_method} without raising an exception if called with a nil message" do
      logger = Syslogger.new
      lambda {
        logger.send(logger_method.to_sym, nil)
      }.should_not raise_error
    end

    it "should log #{logger_method} without raising an exception if called with a no message" do
      logger = Syslogger.new
      lambda {
        logger.send(logger_method.to_sym)
      }.should_not raise_error
    end
  end

  %w{debug info warn error}.each do |logger_method|
    it "should not log #{logger_method} when level is higher" do
      logger = Syslogger.new
      logger.level = Logger::FATAL
      Syslog.should_not_receive(:open).with($0, Syslog::LOG_PID | Syslog::LOG_CONS, nil).and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_not_receive(:log).with(Syslog::LOG_NOTICE, "Some message")
      logger.send(logger_method.to_sym, "Some message")
    end

    it "should not evaluate a block or log #{logger_method} when level is higher" do
      logger = Syslogger.new
      logger.level = Logger::FATAL
      Syslog.should_not_receive(:open).with($0, Syslog::LOG_PID | Syslog::LOG_CONS, nil).and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_not_receive(:log).with(Syslog::LOG_NOTICE, "Some message")
      logger.send(logger_method.to_sym) { violated "This block should not have been called" }
    end
  end

  it "should respond to <<" do
    logger = Syslogger.new("my_app", Syslog::LOG_PID, Syslog::LOG_USER)
    logger.should respond_to(:<<)
    Syslog.should_receive(:open).with("my_app", Syslog::LOG_PID, Syslog::LOG_USER).and_yield(syslog=mock("syslog", :mask= => true))
    syslog.should_receive(:log).with(Syslog::LOG_INFO, "yop")
    logger << "yop"
  end

  it "should respond to write" do
    logger = Syslogger.new("my_app", Syslog::LOG_PID, Syslog::LOG_USER)
    logger.should respond_to(:write)
    Syslog.should_receive(:open).with("my_app", Syslog::LOG_PID, Syslog::LOG_USER).and_yield(syslog=mock("syslog", :mask= => true))
    syslog.should_receive(:log).with(Syslog::LOG_INFO, "yop")
    logger.write "yop"
  end

  describe "add" do
    before do
      @logger = Syslogger.new("my_app", Syslog::LOG_PID, Syslog::LOG_USER)
    end
    it "should respond to add" do
      @logger.should respond_to(:add)
    end
    it "should correctly log" do
      Syslog.should_receive(:open).with("my_app", Syslog::LOG_PID, Syslog::LOG_USER).and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_receive(:log).with(Syslog::LOG_INFO, "message")
      @logger.add(Logger::INFO, "message")
    end
    it "should take the message from the block if :message is nil" do
      Syslog.should_receive(:open).with("my_app", Syslog::LOG_PID, Syslog::LOG_USER).and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_receive(:log).with(Syslog::LOG_INFO, "my message")
      @logger.add(Logger::INFO) { "my message" }
    end
    it "should use the given progname" do
      Syslog.should_receive(:open).with("progname", Syslog::LOG_PID, Syslog::LOG_USER).and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_receive(:log).with(Syslog::LOG_INFO, "message")
      @logger.add(Logger::INFO, "message", "progname") { "my message" }
    end

    it "should substitute '%' for '%%' before adding the :message" do
      Syslog.stub(:open).and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_receive(:log).with(Syslog::LOG_INFO, "%%me%%ssage%%")
      @logger.add(Logger::INFO, "%me%ssage%")
    end

    it "should strip the :message" do
      Syslog.stub(:open).and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_receive(:log).with(Syslog::LOG_INFO, "message")
      @logger.add(Logger::INFO, "\n\nmessage  ")
    end

    it "should not raise exception if asked to log with a nil message and body" do
      Syslog.should_receive(:open).
        with("my_app", Syslog::LOG_PID, Syslog::LOG_USER).
        and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_receive(:log).with(Syslog::LOG_INFO, "my_app")
      lambda {
        @logger.add(Logger::INFO, nil)
      }.should_not raise_error
    end

    it "should use given progname as the message if the message and block are nil" do
      Syslog.should_receive(:open).
        with("my_app", Syslog::LOG_PID, Syslog::LOG_USER).
        and_yield(syslog=mock("syslog", :mask= => true))
      syslog.should_receive(:log).with(Syslog::LOG_INFO, "my_app")
      @logger.add(Logger::INFO, nil)
    end
  end # describe "add"

  describe "level=" do
    before(:each) do
      @logger = Syslogger.new("my_app", Syslog::LOG_PID, Syslog::LOG_USER)
    end

    { :debug => Logger::DEBUG,
      :info  => Logger::INFO,
      :warn  => Logger::WARN,
      :error => Logger::ERROR,
      :fatal => Logger::FATAL
    }.each_pair do |level_symbol, level_value|
      it "should allow using :#{level_symbol}" do
        @logger.level = level_symbol
        @logger.level.should equal level_value
      end

      it "should allow using Fixnum #{level_value}" do
        @logger.level = level_value
        @logger.level.should equal level_value
      end
    end

    it "should not allow using random symbols" do
      lambda {
        @logger.level = :foo
      }.should raise_error
    end

    it "should not allow using symbols mapping back to non-level constants" do
      lambda {
        @logger.level = :version
      }.should raise_error
    end

    it "should not allow using strings" do
      lambda {
        @logger.level = "warn"
      }.should raise_error
    end
  end # describe "level="

  describe ":level? methods" do
    before(:each) do
      @logger = Syslogger.new("my_app", Syslog::LOG_PID, Syslog::LOG_USER)
    end

    %w{debug info warn error fatal}.each do |logger_method|
      it "should respond to the #{logger_method}? method" do
        @logger.should respond_to "#{logger_method}?".to_sym
      end
    end

    it "should not have unknown? method" do
      @logger.should_not respond_to :unknown?
    end

    it "should return true for all methods" do
      @logger.level = Logger::DEBUG
      %w{debug info warn error fatal}.each do |logger_method|
        @logger.send("#{logger_method}?").should be_true
      end
    end

    it "should return true for all except debug?" do
      @logger.level = Logger::INFO
      %w{info warn error fatal}.each do |logger_method|
        @logger.send("#{logger_method}?").should be_true
      end
      @logger.debug?.should be_false
    end

    it "should return true for warn?, error? and fatal? when WARN" do
      @logger.level = Logger::WARN
      %w{warn error fatal}.each do |logger_method|
        @logger.send("#{logger_method}?").should be_true
      end
      %w{debug info}.each do |logger_method|
        @logger.send("#{logger_method}?").should be_false
      end
    end

    it "should return true for error? and fatal? when ERROR" do
      @logger.level = Logger::ERROR
      %w{error fatal}.each do |logger_method|
        @logger.send("#{logger_method}?").should be_true
      end
      %w{warn debug info}.each do |logger_method|
        @logger.send("#{logger_method}?").should be_false
      end
    end

    it "should return true only for fatal? when FATAL" do
      @logger.level = Logger::FATAL
      @logger.fatal?.should be_true
      %w{error warn debug info}.each do |logger_method|
        @logger.send("#{logger_method}?").should be_false
      end
    end
  end # describe ":level? methods"

end # describe "Syslogger"