require 'spec_helper'

describe RequestLogAnalyzer::FileFormat::Mysql do

  subject { RequestLogAnalyzer::FileFormat.load(:mysql) }

  it { should be_well_formed }
  it { should have_line_definition(:time).capturing(:timestamp) }
  it { should have_line_definition(:user_host).capturing(:user, :host, :ip) }
  it { should have_line_definition(:query_statistics).capturing(:query_time, :lock_time, :rows_sent, :rows_examined) }
  it { should have_line_definition(:use_database).capturing(:database) }
  it { should have_line_definition(:query_part).capturing(:query_fragment) }
  it { should have_line_definition(:query).capturing(:query) }
  it { should satisfy { |ff| ff.report_trackers.length == 7 } }

  describe '#parse_line' do

    let(:time_sample)                   { '# Time: 091112  8:13:56' }
    let(:user_host_sample)              { '# User@Host: admin[admin] @ db1 [10.0.0.1]' }
    let(:user_host_wo_host_sample)      { '# User@Host: admin[admin] @  [10.0.0.1]' }
    let(:user_host_wo_ip_sample)        { '# User@Host: root[root] @ localhost []' }
    let(:float_query_statistics_sample) { '# Query_time: 10  Lock_time: 0  Rows_sent: 1191307  Rows_examined: 1191307' }
    let(:int_query_statistics_sample)   { '# Query_time: 10.00000  Lock_time: 0.00000  Rows_sent: 1191307  Rows_examined: 1191307' }
    let(:partial_query_sample)          { 'AND clients.index > 0' }
    let(:full_query_sample)             { 'SELECT /*!40001 SQL_NO_CACHE */ * FROM `events`; ' }
    let(:use_db_sample)                 { 'use db;' }
    let(:set_timestamp_sample)          { 'SET timestamp=1250651725;' }
    let(:set_insertid_sample)           { 'SET insert_id=1250651725;' }
    let(:set_timestamp_insertid_sample) { 'SET timestamp=1250651725, insert_id=45674;' }

    it { should parse_line(time_sample).as(:time).and_capture(timestamp: 20_091_112_081_356) }
    it { should parse_line(user_host_sample).as(:user_host).and_capture(user: 'admin', host: 'db1', ip: '10.0.0.1') }
    it { should parse_line(user_host_wo_host_sample, 'without host').as(:user_host).and_capture(user: 'admin', host: '', ip: '10.0.0.1') }
    it { should parse_line(user_host_wo_ip_sample, 'without IP').as(:user_host).and_capture(user: 'root', host: 'localhost', ip: '') }
    it { should parse_line(float_query_statistics_sample, 'using floats').as(:query_statistics).and_capture(query_time: 10.0, lock_time: 0.0, rows_sent: 1_191_307, rows_examined: 1_191_307) }
    it { should parse_line(int_query_statistics_sample, 'using integers').as(:query_statistics).and_capture(query_time: 10.0, lock_time: 0.0, rows_sent: 1_191_307, rows_examined: 1_191_307) }
    it { should parse_line(partial_query_sample).as(:query_part).and_capture(query_fragment: 'AND clients.index > 0') }
    it { should parse_line(full_query_sample).as(:query).and_capture(query: 'SELECT /*!:int SQL_NO_CACHE */ * FROM events') }
    it { should parse_line(use_db_sample).as(:use_database).and_capture(database: 'db') }

    it { should_not parse_line(set_timestamp_sample) }
    it { should_not parse_line(set_insertid_sample) }
    it { should_not parse_line(set_timestamp_insertid_sample) }
  end

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

    it 'should parse a single line query entry correctly' do
      fixture = <<-EOS
        # Time: 091112 18:13:56
        # User@Host: admin[admin] @ db1 [10.0.0.1]
        # Query_time: 10  Lock_time: 0  Rows_sent: 1191307  Rows_examined: 1191307
        SELECT /*!40001 SQL_NO_CACHE */ * FROM `events`;
      EOS

      log_parser.parse_string(fixture) do |request|
        request[:query].should == 'SELECT /*!:int SQL_NO_CACHE */ * FROM events'
      end
    end

    it 'should parse a multiline query entry correctly' do
      fixture = <<-EOS
        # Time: 091112 18:13:56
        # User@Host: admin[admin] @ db1 [10.0.0.1]
        # Query_time: 10  Lock_time: 0  Rows_sent: 1191307  Rows_examined: 1191307
        SELECT * FROM `clients` WHERE (1=1
                                      AND clients.valid_from < '2009-12-05' AND (clients.valid_to IS NULL or clients.valid_to > '2009-11-20')
                                       AND clients.index > 0
                                      ) AND (clients.deleted_at IS NULL);
      EOS

      log_parser.parse_string(fixture) do |request|
        request[:query].should == 'SELECT * FROM clients WHERE (:int=:int AND clients.valid_from < :date AND (clients.valid_to IS NULL or clients.valid_to > :date) AND clients.index > :int ) AND (clients.deleted_at IS NULL)'
      end
    end

    it 'should parse a request without timestamp correctly, without warnings' do
      fixture = <<-EOS
        # User@Host: admin[admin] @ db1 [10.0.0.1]
        # Query_time: 10  Lock_time: 0  Rows_sent: 1191307  Rows_examined: 1191307
        SELECT /*!40001 SQL_NO_CACHE */ * FROM `events`;
      EOS

      log_parser.should_receive(:handle_request).once
      log_parser.should_not_receive(:warn)
      log_parser.parse_string(fixture)
    end

    it 'should parse a query with context information correctly' do
      fixture = <<-EOS
        # Time: 091112 18:13:56
        # User@Host: admin[admin] @ db1 [10.0.0.1]
        # Query_time: 10  Lock_time: 0  Rows_sent: 1191307  Rows_examined: 1191307
        use database_name;
        SET timestamp=4324342342423, insert_id = 224253443;
        SELECT /*!40001 SQL_NO_CACHE */ * FROM `events`;
      EOS

      log_parser.parse_string(fixture) do |request|
        request[:query].should == 'SELECT /*!:int SQL_NO_CACHE */ * FROM events'
      end
    end

    it 'should find 26 completed sloq query entries' do
      log_parser.should_not_receive(:warn)
      log_parser.should_receive(:handle_request).exactly(26).times
      log_parser.parse_file(log_fixture(:mysql_slow_query))
    end
  end
end