<commit_before>require 'spec_helper'

RSpec.describe Mysql2::Client do # rubocop:disable Metrics/BlockLength
  context "using defaults file" do
    let(:cnf_file) { File.expand_path('../../my.cnf', __FILE__) }

    it "should not raise an exception for valid defaults group" do
      expect do
        new_client(default_file: cnf_file, default_group: "test")
      end.not_to raise_error
    end

    it "should not raise an exception without default group" do
      expect do
        new_client(default_file: cnf_file)
      end.not_to raise_error
    end
  end

  it "should raise a Mysql::Error::ConnectionError upon connection failure" do
    expect do
      # The odd local host IP address forces the mysql client library to
      # use a TCP socket rather than a domain socket.
      new_client('host' => '127.0.0.2', 'port' => 999999)
    end.to raise_error(Mysql2::Error::ConnectionError)
  end

  it "should raise an exception on create for invalid encodings" do
    expect do
      new_client(encoding: "fake")
    end.to raise_error(Mysql2::Error)
  end

  it "should raise an exception on non-string encodings" do
    expect do
      new_client(encoding: :fake)
    end.to raise_error(TypeError)
  end

  it "should not raise an exception on create for a valid encoding" do
    expect do
      new_client(encoding: "utf8")
    end.not_to raise_error

    expect do
      new_client(DatabaseCredentials['root'].merge(encoding: "big5"))
    end.not_to raise_error
  end

  Klient = Class.new(Mysql2::Client) do
    attr_reader :connect_args
    def connect(*args)
      @connect_args ||= []
      @connect_args << args
    end
  end

  it "should accept connect flags and pass them to #connect" do
    client = Klient.new flags: Mysql2::Client::FOUND_ROWS
    expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to be > 0
  end

  it "should parse flags array" do
    client = Klient.new flags: %w[FOUND_ROWS -PROTOCOL_41]
    expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
    expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
  end

  it "should parse flags string" do
    client = Klient.new flags: "FOUND_ROWS -PROTOCOL_41"
    expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
    expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
  end

  it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do
    client = Klient.new
    client_flags = Mysql2::Client::REMEMBER_OPTIONS |
                   Mysql2::Client::LONG_PASSWORD |
                   Mysql2::Client::LONG_FLAG |
                   Mysql2::Client::TRANSACTIONS |
                   Mysql2::Client::PROTOCOL_41 |
                   Mysql2::Client::SECURE_CONNECTION |
                   Mysql2::Client::CONNECT_ATTRS
    expect(client.connect_args.last[6]).to eql(client_flags)
  end

  it "should execute init command" do
    options = DatabaseCredentials['root'].dup
    options[:init_command] = "SET @something = 'setting_value';"
    client = new_client(options)
    result = client.query("SELECT @something;")
    expect(result.first['@something']).to eq('setting_value')
  end

  it "should send init_command after reconnect" do
    options = DatabaseCredentials['root'].dup
    options[:init_command] = "SET @something = 'setting_value';"
    options[:reconnect] = true
    client = new_client(options)

    result = client.query("SELECT @something;")
    expect(result.first['@something']).to eq('setting_value')

    # get the current connection id
    result = client.query("SELECT CONNECTION_ID()")
    first_conn_id = result.first['CONNECTION_ID()']

    # break the current connection
    expect { client.query("KILL #{first_conn_id}") }.to raise_error(Mysql2::Error)

    client.ping # reconnect now

    # get the new connection id
    result = client.query("SELECT CONNECTION_ID()")
    second_conn_id = result.first['CONNECTION_ID()']

    # confirm reconnect by checking the new connection id
    expect(first_conn_id).not_to eq(second_conn_id)

    # At last, check that the init command executed
    result = client.query("SELECT @something;")
    expect(result.first['@something']).to eq('setting_value')
  end

  it "should have a global default_query_options hash" do
    expect(Mysql2::Client).to respond_to(:default_query_options)
  end

  it "should be able to connect via SSL options" do
    ssl = @client.query "SHOW VARIABLES LIKE 'have_ssl'"
    ssl_uncompiled = ssl.any? { |x| x['Value'] == 'OFF' }
    pending("DON'T WORRY, THIS TEST PASSES - but SSL is not compiled into your MySQL daemon.") if ssl_uncompiled
    ssl_disabled = ssl.any? { |x| x['Value'] == 'DISABLED' }
    pending("DON'T WORRY, THIS TEST PASSES - but SSL is not enabled in your MySQL daemon.") if ssl_disabled

    # You may need to adjust the lines below to match your SSL certificate paths
    ssl_client = nil
    option_overrides = {
      'host'     => 'mysql2gem.example.com', # must match the certificates
      :sslkey    => '/etc/mysql/client-key.pem',
      :sslcert   => '/etc/mysql/client-cert.pem',
      :sslca     => '/etc/mysql/ca-cert.pem',
      :sslcipher => 'DHE-RSA-AES256-SHA',
      :sslverify => true,
    }
    %i[sslkey sslcert sslca].each do |item|
      unless File.exist?(option_overrides[item])
        pending("DON'T WORRY, THIS TEST PASSES - but #{option_overrides[item]} does not exist.")
        break
      end
    end
    expect do
      ssl_client = new_client(option_overrides)
    end.not_to raise_error

    results = Hash[ssl_client.query('SHOW STATUS WHERE Variable_name LIKE "Ssl_%"').map { |x| x.values_at('Variable_name', 'Value') }]
    expect(results['Ssl_cipher']).not_to be_empty
    expect(results['Ssl_version']).not_to be_empty

    expect(ssl_client.ssl_cipher).not_to be_empty
    expect(results['Ssl_cipher']).to eql(ssl_client.ssl_cipher)
  end

  def run_gc
    if defined?(Rubinius)
      GC.run(true)
    else
      GC.start
    end
    sleep(0.5)
  end

  it "should terminate connections when calling close" do<commit_msg>Add tests to verify ssl_mode option.<commit_after>require 'spec_helper'

RSpec.describe Mysql2::Client do # rubocop:disable Metrics/BlockLength
  context "using defaults file" do
    let(:cnf_file) { File.expand_path('../../my.cnf', __FILE__) }

    it "should not raise an exception for valid defaults group" do
      expect do
        new_client(default_file: cnf_file, default_group: "test")
      end.not_to raise_error
    end

    it "should not raise an exception without default group" do
      expect do
        new_client(default_file: cnf_file)
      end.not_to raise_error
    end
  end

  it "should raise a Mysql::Error::ConnectionError upon connection failure" do
    expect do
      # The odd local host IP address forces the mysql client library to
      # use a TCP socket rather than a domain socket.
      new_client('host' => '127.0.0.2', 'port' => 999999)
    end.to raise_error(Mysql2::Error::ConnectionError)
  end

  it "should raise an exception on create for invalid encodings" do
    expect do
      new_client(encoding: "fake")
    end.to raise_error(Mysql2::Error)
  end

  it "should raise an exception on non-string encodings" do
    expect do
      new_client(encoding: :fake)
    end.to raise_error(TypeError)
  end

  it "should not raise an exception on create for a valid encoding" do
    expect do
      new_client(encoding: "utf8")
    end.not_to raise_error

    expect do
      new_client(DatabaseCredentials['root'].merge(encoding: "big5"))
    end.not_to raise_error
  end

  Klient = Class.new(Mysql2::Client) do
    attr_reader :connect_args
    def connect(*args)
      @connect_args ||= []
      @connect_args << args
    end
  end

  it "should accept connect flags and pass them to #connect" do
    client = Klient.new flags: Mysql2::Client::FOUND_ROWS
    expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to be > 0
  end

  it "should parse flags array" do
    client = Klient.new flags: %w[FOUND_ROWS -PROTOCOL_41]
    expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
    expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
  end

  it "should parse flags string" do
    client = Klient.new flags: "FOUND_ROWS -PROTOCOL_41"
    expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
    expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
  end

  it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do
    client = Klient.new
    client_flags = Mysql2::Client::REMEMBER_OPTIONS |
                   Mysql2::Client::LONG_PASSWORD |
                   Mysql2::Client::LONG_FLAG |
                   Mysql2::Client::TRANSACTIONS |
                   Mysql2::Client::PROTOCOL_41 |
                   Mysql2::Client::SECURE_CONNECTION |
                   Mysql2::Client::CONNECT_ATTRS
    expect(client.connect_args.last[6]).to eql(client_flags)
  end

  it "should execute init command" do
    options = DatabaseCredentials['root'].dup
    options[:init_command] = "SET @something = 'setting_value';"
    client = new_client(options)
    result = client.query("SELECT @something;")
    expect(result.first['@something']).to eq('setting_value')
  end

  it "should send init_command after reconnect" do
    options = DatabaseCredentials['root'].dup
    options[:init_command] = "SET @something = 'setting_value';"
    options[:reconnect] = true
    client = new_client(options)

    result = client.query("SELECT @something;")
    expect(result.first['@something']).to eq('setting_value')

    # get the current connection id
    result = client.query("SELECT CONNECTION_ID()")
    first_conn_id = result.first['CONNECTION_ID()']

    # break the current connection
    expect { client.query("KILL #{first_conn_id}") }.to raise_error(Mysql2::Error)

    client.ping # reconnect now

    # get the new connection id
    result = client.query("SELECT CONNECTION_ID()")
    second_conn_id = result.first['CONNECTION_ID()']

    # confirm reconnect by checking the new connection id
    expect(first_conn_id).not_to eq(second_conn_id)

    # At last, check that the init command executed
    result = client.query("SELECT @something;")
    expect(result.first['@something']).to eq('setting_value')
  end

  it "should have a global default_query_options hash" do
    expect(Mysql2::Client).to respond_to(:default_query_options)
  end

  context "SSL" do
    before(:example) do
      ssl = @client.query "SHOW VARIABLES LIKE 'have_ssl'"
      ssl_uncompiled = ssl.any? { |x| x['Value'] == 'OFF' }
      pending("DON'T WORRY, THIS TEST PASSES - but SSL is not compiled into your MySQL daemon.") if ssl_uncompiled
      ssl_disabled = ssl.any? { |x| x['Value'] == 'DISABLED' }
      pending("DON'T WORRY, THIS TEST PASSES - but SSL is not enabled in your MySQL daemon.") if ssl_disabled

      %i[sslkey sslcert sslca].each do |item|
        unless File.exist?(option_overrides[item])
          pending("DON'T WORRY, THIS TEST PASSES - but #{option_overrides[item]} does not exist.")
          break
        end
      end
    end

    let(:option_overrides) do
      {
        'host'     => 'mysql2gem.example.com', # must match the certificates
        :sslkey    => '/etc/mysql/client-key.pem',
        :sslcert   => '/etc/mysql/client-cert.pem',
        :sslca     => '/etc/mysql/ca-cert.pem',
        :sslcipher => 'DHE-RSA-AES256-SHA',
        :sslverify => true,
      }
    end

    let(:ssl_client) do
      new_client(option_overrides)
    end

    %i[disabled preferred required verify_ca verify_identity].each do |ssl_mode|
      it "should set ssl_mode option #{ssl_mode}" do
        options = {
          ssl_mode: ssl_mode,
        }
        options.merge!(option_overrides)
        expect do
          new_client(options)
        end.to_not output.to_stderr
      end
    end

    it "should be able to connect via SSL options" do
      # You may need to adjust the lines below to match your SSL certificate paths
      results = Hash[ssl_client.query('SHOW STATUS WHERE Variable_name LIKE "Ssl_%"').map { |x| x.values_at('Variable_name', 'Value') }]
      expect(results['Ssl_cipher']).not_to be_empty
      expect(results['Ssl_version']).not_to be_empty

      expect(ssl_client.ssl_cipher).not_to be_empty
      expect(results['Ssl_cipher']).to eql(ssl_client.ssl_cipher)
    end
  end

  def run_gc
    if defined?(Rubinius)
      GC.run(true)
    else
      GC.start
    end
    sleep(0.5)
  end

  it "should terminate connections when calling close" do
{'fraction_non_alphanumeric': 0.10998597912447422, 'fraction_numerical': 0.00965882536220595, 'mean_word_length': 3.289675910457735, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1432032', 'n_tokens_mistral': 4289, 'n_tokens_neox': 4123, 'n_words': 1050}