require 'minitest/autorun'
require 'mysql2'
require 'lhm'
require 'toxiproxy'

require 'integration/sql_retry/lock_wait_timeout_test_helper'
require 'integration/sql_retry/db_connection_helper'
require 'integration/sql_retry/proxysql_helper'
require 'integration/toxiproxy_helper'

describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do
  include ToxiproxyHelper

  before(:each) do
    @old_logger = Lhm.logger
    @logger = StringIO.new
    Lhm.logger = Logger.new(@logger)

    @connection = DBConnectionHelper::new_mysql_connection(:proxysql, true, true)

    @lhm_retry = Lhm::SqlRetry.new(@connection, retry_options: {},
                                   reconnect_with_consistent_host: true)
  end

  after(:each) do
    # Restore default logger
    Lhm.logger = @old_logger
  end

  it "Will abort if service is down" do

    e = assert_raises Lhm::Error do
      #Service down
      Toxiproxy[:mysql_proxysql].down do
        @lhm_retry.with_retries do |retriable_connection|
          retriable_connection.execute("INSERT INTO #{DBConnectionHelper.test_table_name} (id) VALUES (2000)")
        end
      end
    end
    assert_equal Lhm::Error, e.class
    assert_match(/LHM tried the reconnection procedure but failed. Latest error:/, e.message)
  end

  it "Will retry until connection is achieved" do

    #Creating a network blip
    ToxiproxyHelper.with_kill_and_restart(:mysql_proxysql, 2.seconds) do
      @lhm_retry.with_retries do |retriable_connection|
        retriable_connection.execute("INSERT INTO #{DBConnectionHelper.test_table_name} (id) VALUES (2000)")
      end
    end

    assert_equal @connection.execute("Select * from #{DBConnectionHelper.test_table_name} WHERE id=2000").to_a.first.first, 2000

    logs = @logger.string.split("\n")

    assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
    assert logs.last.include?("LHM successfully reconnected to initial host")
  end

  it "Will abort if new writer is not same host" do
    # The hostname will be constant before the blip
    Lhm::SqlRetry.any_instance.stubs(:hostname).returns("mysql-1").then.returns("mysql-2")

    # Need new instance for stub to take into effect
    lhm_retry = Lhm::SqlRetry.new(@connection, retry_options: {},
                                  reconnect_with_consistent_host: true)

    e = assert_raises Lhm::Error do
      #Creating a network blip
      ToxiproxyHelper.with_kill_and_restart(:mysql_proxysql, 2.seconds) do
        lhm_retry.with_retries do |retriable_connection|
          retriable_connection.execute("INSERT INTO #{DBConnectionHelper.test_table_name} (id) VALUES (2000)")
        end
      end
    end

    assert_equal e.class, Lhm::Error
    assert_match(/LHM tried the reconnection procedure but failed. Latest error: Reconnected to wrong host/, e.message)

    logs = @logger.string.split("\n")

    assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
    assert logs.last.include?("Lost connection to MySQL server at 'reading initial communication packet")
  end

  it "Will abort if failover happens (mimicked with proxySQL)" do
    e = assert_raises Lhm::Error do
      #Creates a failover by switching the target hostgroup for the #hostname
      ProxySQLHelper.with_lhm_hostgroup_flip do
        #Creating a network blip
        ToxiproxyHelper.with_kill_and_restart(:mysql_proxysql, 2.seconds) do
          @lhm_retry.with_retries do |retriable_connection|
            retriable_connection.execute("INSERT INTO #{DBConnectionHelper.test_table_name} (id) VALUES (2000)")
          end
        end
      end
    end

    assert_equal e.class, Lhm::Error
    assert_match(/LHM tried the reconnection procedure but failed. Latest error: Reconnected to wrong host/, e.message)

    logs = @logger.string.split("\n")

    assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
    assert logs.last.include?("Lost connection to MySQL server at 'reading initial communication packet")
  end
end