require 'spec_helper'

describe RequestLogAnalyzer::FileFormat::Apache do

  describe '.access_line_definition' do
    let(:format_string) { '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i" %T' }
    let(:line_definition) { RequestLogAnalyzer::FileFormat::Apache.access_line_definition(format_string) }

    it 'should create a Regexp to match the line' do
      line_definition.regexp.should be_kind_of(Regexp)
    end

    it 'should create a list of captures for the values in the lines' do
      line_definition.captures.length.should == 12
    end

    it 'should make it a header line' do
      line_definition.should be_header
    end

    it 'should make it a footer line' do
      line_definition.should be_footer
    end

    it 'should capture :duration' do
      line_definition.captures?(:duration).should == true
    end
  end

  describe '.access_line_definition' do
    it 'should parse values in microseconds when no argument is given to %D' do
      format = RequestLogAnalyzer::FileFormat::Apache.create('%D')
      format.should parse_line('12345').and_capture(duration: 0.012345)
    end

    it 'should parse values in microseconds when micro is given as argument to %D' do
      format = RequestLogAnalyzer::FileFormat::Apache.create('%{micro}D')
      format.should parse_line('12345').and_capture(duration: 0.012345)
    end

    it 'should parse values in microseconds when micro is given as argument to %D' do
      format = RequestLogAnalyzer::FileFormat::Apache.create('%{milli}D')
      format.should parse_line('12345').and_capture(duration: 12.345)
    end
  end

  describe '.create' do
    let(:format_string) { '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"' }
    subject { RequestLogAnalyzer::FileFormat::Apache.create(format_string) }

    it { should be_well_formed }
    it { should have_line_definition(:access).capturing(:timestamp, :remote_host, :bytes_sent, :http_method, :path, :http_version, :http_status) }
    it { should satisfy { |ff| ff.report_trackers.length == 8 } }
  end

  context '"vhost_combined" access log parsing' do
    subject { RequestLogAnalyzer::FileFormat.load(:apache, :vhost_combined) }
    describe '#parse_line' do
      let(:sample1) { '72.204.80.86 - psi-equipment.od1.vtiger.com [19/Oct/2013:15:41:54 +0000] "GET /vtiger6/layouts/vlayout/modules/Vtiger/resources/Vtiger.js?&v=5.10.38 HTTP/1.1" 304 - "https://psi-equipment.od1.vtiger.com/vtiger6/index.php?module=PrintTemplates&view=Detail&record=29" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36" 1/683' }

      it do
        should parse_line(sample1, 'a sample line').and_capture(
            remote_host: '72.204.80.86', remote_logname: nil, user: nil,
            vhost: 'psi-equipment.od1.vtiger.com', duration: 1.0,
            timestamp: 20_131_019_154_154, http_status: 304, http_method: 'GET',
            http_version: '1.1',          bytes_sent: 0)
      end
    end
  end

  context '"Common" access log parsing' do
    subject { RequestLogAnalyzer::FileFormat.load(:apache, :common) }

    it { should be_well_formed }
    it { should have_line_definition(:access).capturing(:remote_host, :remote_logname, :user, :timestamp, :http_status, :http_method, :http_version, :bytes_sent) }
    it { should satisfy { |ff| ff.report_trackers.length == 6 } }

    describe '#parse_line' do

      let(:sample1) { '1.129.119.13 - - [08/Sep/2009:07:54:09 -0400] "GET /profile/18543424 HTTP/1.0" 200 8223' }
      let(:sample2) { '1.82.235.29 - - [08/Sep/2009:07:54:05 -0400] "GET /gallery/fresh?page=23&per_page=16 HTTP/1.1" 200 23414' }

      it do
        should parse_line(sample1, 'a sample line').and_capture(
            remote_host: '1.129.119.13', remote_logname: nil, user: nil,
            timestamp: 20_090_908_075_409, http_status: 200, http_method: 'GET',
            http_version: '1.0',          bytes_sent: 8223)
      end

      it do
        should parse_line(sample2, 'another sample line').and_capture(
            remote_host: '1.82.235.29',  remote_logname: nil, user: nil,
            timestamp: 20_090_908_075_405, http_status: 200, http_method: 'GET',
            http_version: '1.1',          bytes_sent: 23_414)
      end

      it { should_not parse_line('nonsense', 'a nonsense line') }
    end

    describe '#parse_io' do
      let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject) }

      it 'should parse a log snippet successfully without warnings' do
        log_parser.should_receive(:handle_request).exactly(10).times
        log_parser.should_not_receive(:warn)
        log_parser.parse_file(log_fixture(:apache_common))
      end
    end
  end

  context '"Combined" access log parsing' do
    subject { RequestLogAnalyzer::FileFormat.load(:apache, :combined) }

    it { should be_well_formed }
    it { should have_line_definition(:access).capturing(:remote_host, :remote_logname, :user, :timestamp, :http_status, :http_method, :http_version, :bytes_sent, :referer, :user_agent) }
    it { should satisfy { |ff| ff.report_trackers.length == 8 } }

    describe '#parse_line' do
      let(:sample1) { '69.41.0.45 - - [02/Sep/2009:12:02:40 +0200] "GET //phpMyAdmin/ HTTP/1.1" 404 209 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)"' }
      let(:sample2) { '0:0:0:0:0:0:0:1 - - [02/Sep/2009:05:08:33 +0200] "GET / HTTP/1.1" 200 30 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9"' }

      it do
        should parse_line(sample1, 'with IPv4 address').and_capture(
            remote_host: '69.41.0.45',   remote_logname: nil, user: nil,
            timestamp: 20_090_902_120_240, http_status: 404, http_method: 'GET',
            http_version: '1.1',          bytes_sent: 209, referer: nil,
            user_agent: 'Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)')
      end

      it do
        should parse_line(sample2, 'with IPv6 address').and_capture(
            remote_host: '0:0:0:0:0:0:0:1', remote_logname: nil, user: nil,
            timestamp: 20_090_902_050_833,    http_status: 200, http_method: 'GET',
            http_version: '1.1',             bytes_sent: 30,  referer: nil,
            user_agent: 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9')
      end

      it { should_not parse_line('nonsense', 'a nonsense line') }
    end

    describe '#parse_io' do
      let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject) }

      it 'should parse a log snippet successfully without warnings' do
        log_parser.should_receive(:handle_request).exactly(5).times
        log_parser.should_not_receive(:warn)
        log_parser.parse_file(log_fixture(:apache_combined))
      end
    end
  end
end