require 'spec_helper'
begin
  require 'eventmachine'
  require 'mysql2/em'

  RSpec.describe Mysql2::EM::Client do
    it "should support async queries" do
      results = []
      EM.run do
        client1 = Mysql2::EM::Client.new DatabaseCredentials['root']
        defer1 = client1.query "SELECT sleep(0.1) as first_query"
        defer1.callback do |result|
          results << result.first
          client1.close
          EM.stop_event_loop
        end

        client2 = Mysql2::EM::Client.new DatabaseCredentials['root']
        defer2 = client2.query "SELECT sleep(0.025) second_query"
        defer2.callback do |result|
          results << result.first
          client2.close
        end
      end

      expect(results[0].keys).to include("second_query")
      expect(results[1].keys).to include("first_query")
    end

    it "should support queries in callbacks" do
      results = []
      EM.run do
        client = Mysql2::EM::Client.new DatabaseCredentials['root']
        defer1 = client.query "SELECT sleep(0.025) as first_query"
        defer1.callback do |result|
          results << result.first
          defer2 = client.query "SELECT sleep(0.025) as second_query"
          defer2.callback do |r|
            results << r.first
            client.close
            EM.stop_event_loop
          end
        end
      end

      expect(results[0].keys).to include("first_query")
      expect(results[1].keys).to include("second_query")
    end

    it "should not swallow exceptions raised in callbacks" do
      expect do
        EM.run do
          client = Mysql2::EM::Client.new DatabaseCredentials['root']
          defer = client.query "SELECT sleep(0.1) as first_query"
          defer.callback do
            client.close
            raise 'some error'
          end
          defer.errback do
            # This _shouldn't_ be run, but it needed to prevent the specs from
            # freezing if this test fails.
            EM.stop_event_loop
          end
        end
      end.to raise_error('some error')
    end

    context 'when an exception is raised by the client' do
      let(:client) { Mysql2::EM::Client.new DatabaseCredentials['root'] }
      let(:error) { StandardError.new('some error') }
      before { allow(client).to receive(:async_result).and_raise(error) }
      after { client.close }

      it "should swallow exceptions raised in by the client" do
        errors = []
        EM.run do
          defer = client.query "SELECT sleep(0.1) as first_query"
          defer.callback do
            # This _shouldn't_ be run, but it is needed to prevent the specs from
            # freezing if this test fails.
            EM.stop_event_loop
          end
          defer.errback do |err|
            errors << err
            EM.stop_event_loop
          end
        end
        expect(errors).to eq([error])
      end

      it "should fail the deferrable" do
        callbacks_run = []
        EM.run do
          defer = client.query "SELECT sleep(0.025) as first_query"
          EM.add_timer(0.1) do
            defer.callback do
              callbacks_run << :callback
              # This _shouldn't_ be run, but it is needed to prevent the specs from
              # freezing if this test fails.
              EM.stop_event_loop
            end
            defer.errback do
              callbacks_run << :errback
              EM.stop_event_loop
            end
          end
        end
        expect(callbacks_run).to eq([:errback])
      end
    end

    it "should not raise error when closing client with no query running" do
      callbacks_run = []
      EM.run do
        client = Mysql2::EM::Client.new DatabaseCredentials['root']
        defer = client.query("select sleep(0.025)")
        defer.callback do
          callbacks_run << :callback
        end
        defer.errback do
          callbacks_run << :errback
        end
        EM.add_timer(0.1) do
          expect(callbacks_run).to eq([:callback])
          expect do
            client.close
          end.not_to raise_error
          EM.stop_event_loop
        end
      end
    end
  end
rescue LoadError
  puts "EventMachine not installed, skipping the specs that use it"
end
