require 'spec_helper'

describe 'Client - TLS spec', :jruby_excluded do

  context 'when server does not support TLS' do

    before(:each) do
      @non_tls_server = NatsServerControl.new("nats://127.0.0.1:4222")
      @non_tls_server.start_server
    end

    after(:each) do
      @non_tls_server.kill_server
    end

    it 'should error if client requires TLS' do
      errors = []
      closes = 0
      reconnects = 0
      disconnects = 0

      options = {
        :uri => 'nats://127.0.0.1:4222',
        :reconnect => false,
        :tls => {
          :ssl_version => :TLSv1_2,
          :protocols => [:tlsv1_2],
          :private_key_file => './spec/configs/certs/key.pem',
          :cert_chain_file  => './spec/configs/certs/server.pem',
          :verify_peer      => false
        }
      }

      with_em_timeout(5) do |future|
        nc = nil
        NATS.on_error      {|e| errors << e }
        NATS.on_close      { closes += 1 }
        NATS.on_reconnect  { reconnects += 1 }
        NATS.on_disconnect { disconnects += 1 }

        nc = NATS.connect(options)
      end

      expect(errors.count).to eql(2)
      expect(errors.first).to be_a(NATS::ClientError)
      expect(errors.first.to_s).to eql("TLS/SSL not supported by server")
      expect(errors.last).to be_a(NATS::ConnectError)
      expect(closes).to eql(1)
      expect(reconnects).to eql(0)

      # Technically we were never connected to the NATS service
      # in that server so we don't call disconnect right now.
      expect(disconnects).to eql(0)
    end
  end

  context 'when server requires TLS and no auth needed' do

    before(:each) do
      @tls_no_auth = NatsServerControl.new("nats://127.0.0.1:4444", '/tmp/test-nats-4444.pid', "-c ./spec/configs/tls-no-auth.conf")
      @tls_no_auth.start_server
    end

    after(:each) do
      @tls_no_auth.kill_server
    end

    it 'should error if client does not set secure connection and dispatch callbacks' do
      errors = []
      closes = 0
      reconnects = 0
      disconnects = 0
      reconnects = 0

      with_em_timeout(3) do |future|
        nc = nil

        NATS.on_close      { closes += 1 }
        NATS.on_reconnect  { reconnects += 1 }
        NATS.on_disconnect { disconnects += 1 }

        NATS.on_error do |e|
          errors << e
        end
        nc = NATS.connect(:uri => 'nats://127.0.0.1:4444', :reconnect => false)
      end

      expect(errors.count > 0).to eq(true)
      expect(errors.first).to be_a(NATS::ClientError)
      expect(closes).to eql(1)
      expect(reconnects).to eql(0)
      expect(disconnects).to eql(1)
    end
  end

  context 'when server requires TLS and authentication' do

    before(:each) do
      @tls_auth = NatsServerControl.new("nats://127.0.0.1:4443", '/tmp/test-nats-4443.pid', "-c ./spec/configs/tls.conf")
      @tls_auth.start_server
    end

    after(:each) do
      @tls_auth.kill_server
    end

    it 'should error if client does not set secure connection and dispatch callbacks' do
      errors = []
      with_em_timeout(3) do |future|
        nc = nil
        NATS.on_error do|e|
          errors << e
        end
        nc = NATS.connect(:uri => 'nats://127.0.0.1:4443', :reconnect => false)
      end

      # Client disconnected from server
      expect(errors.count > 0).to eq(true)
      expect(errors.first).to be_a(NATS::ClientError)
    end

    it 'should error if client does not set secure connection and stop trying to reconnect eventually' do
      errors = []
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(10) do |future|
        nc = nil

        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do
          disconnects += 1
        end

        NATS.on_reconnect do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://127.0.0.1:4443'))
          reconnects += 1
        end

        NATS.on_close do
          # NOTE: We cannot close again here in tests since
          # we would be getting double fiber called errors.
          # future.resume(nc) if not nc.closing?
          closes += 1
          future.resume
        end

        nc = NATS.connect({
          :servers => ['nats://127.0.0.1:4443'],
          :max_reconnect_attempts => 2,
          :reconnect_time_wait => 1
        })
      end

      # FIXME: It will be trying to reconnect for a number of times
      # and some of the erros that will be getting could be errors
      # such as Unknown Protocol due to parser failing with secure conn.
      expect(reconnects).to eql(3)
      expect(disconnects).to eql(4)
      expect(closes).to eql(1)
      expect(errors.count > 0).to eq(true)
      expect(errors.count < 10).to eq(true)

      # Client disconnected from server
      expect(errors.first).to be_a(NATS::ClientError)
      expect(errors.last).to be_a(NATS::ConnectError)
    end

    it 'should reject secure connection when using deprecated versions' do
      errors = []
      connects = 0
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(10) do |future|
        nc = nil

        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        nc = NATS.connect({
          :servers => ['nats://secret:deadbeef@127.0.0.1:4443'],
          :tls => {
            :ssl_version => :sslv2
          }}) do
          connects += 1
        end
        nc.subscribe("hello")
        nc.flush do
          nc.close
        end
      end
      expect(errors.count).to eql(1)
      expect(errors.first).to be_a(NATS::ConnectError)
      expect(connects).to eql(0)
      expect(closes).to eql(1)
      expect(disconnects).to eql(0)
      expect(reconnects).to eql(0)
    end

    it 'should connect securely to server and authorize with default TLS and protocols options' do
      errors      = []
      messages    = []
      connects    = 0
      disconnects = 0
      reconnects  = 0
      closes      = 0

      with_em_timeout(10) do
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
        }
        nc = NATS.connect("tls://secret:deadbeef@127.0.0.1:4443", options) do |nc2|
          server = nc2.connected_server
          expect(server.scheme).to eql("tls")
          expect(server.host).to eql("127.0.0.1")
          expect(server.port).to eql(4443)
          expect(server.userinfo).to eql("secret:deadbeef")
          connects += 1

          info = nc2.server_info
          expect(info).to_not be(nil)
          expect(info).to be_a Hash
          expect(info).to have_key :server_id
          expect(info).to have_key :version
          expect(info).to have_key :proto
          expect(info).to have_key :client_id
          expect(info).to have_key :max_payload
          expect(info).to have_key :host
          expect(info).to have_key :port
          expect(info).to have_key :auth_required
          expect(info).to have_key :tls_required
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
            expect(messages.count).to eql(1)
          end
        end
      end
      expect(errors.count).to eql(0)

      # We are calling close so should not be calling
      # the disconnect callback here.
      expect(disconnects).to eql(0)
      expect(connects).to eql(1)
      expect(closes).to eql(1)
      expect(reconnects).to eql(0)
    end

    it 'should connect securely to server and authorize with defaults only via setting ssl enabled option' do
      errors      = []
      messages    = []
      connects    = 0
      disconnects = 0
      reconnects  = 0
      closes      = 0

      with_em_timeout(10) do
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4443'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :ssl => true
        }

        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4443'))
          connects += 1
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
          end
        end
      end
      expect(messages.count).to eql(1)
      expect(errors.count).to eql(0)

      # We are calling close so should not be calling
      # the disconnect callback here.
      expect(disconnects).to eql(0)
      expect(closes).to eql(1)
      expect(reconnects).to eql(0)
    end

    it 'should connect securely with default TLS and protocols options' do
      errors      = []
      messages    = []
      connects    = 0
      reconnects  = 0
      disconnects = 0
      closes      = 0

      with_em_timeout(10) do |future|
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4443'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            # :ssl_version => :TLSv1_2,
            # :protocols => [:tlsv1_2],
            # :private_key_file => './spec/configs/certs/key.pem',
            # :cert_chain_file  => './spec/configs/certs/server.pem',
            # :verify_peer => true
          }
        }

        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4443'))
          connects += 1
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
          end
        end
      end
      expect(errors.count).to eql(0)
      expect(messages.count).to eql(1)
      expect(reconnects).to eql(0)
      expect(closes).to eql(1)
      expect(disconnects).to eql(0)
    end
  end

  context 'when server requires TLS, certificates and authentication' do

    before(:each) do
      @tls_verify_auth = NatsServerControl.new("nats://127.0.0.1:4445", '/tmp/test-nats-4445.pid', "-c ./spec/configs/tlsverify.conf")
      @tls_verify_auth.start_server
    end

    after(:each) do
      @tls_verify_auth.kill_server
    end

    it 'should error if client does not set secure connection and dispatch callbacks' do
      errors = []
      with_em_timeout(10) do |future|
        nc = nil
        NATS.on_error do |e|
          errors << e
        end
        nc = NATS.connect(:uri => 'nats://127.0.0.1:4445', :reconnect => false)
      end
      expect(errors.count >= 2).to eql(true)

      # Client disconnected from server
      expect(errors.first).to be_a(NATS::ClientError)
      expect(errors.last).to  be_a(NATS::ConnectError)
    end

    it 'should error if client does not set secure connection and stop trying to reconnect eventually' do
      errors = []
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(10) do |future|
        nc = nil

        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          # NOTE: We cannot close again here in tests since
          # we would be getting double fiber called errors.
          # future.resume(nc) if not nc.closing?
          closes += 1
        end

        nc = NATS.connect(:uri => 'nats://127.0.0.1:4445', :reconnect_time_wait => 1, :max_reconnect_attempts => 2)
      end
      expect(errors.count > 2).to eql(true)
      expect(errors.count < 10).to eql(true)
      expect(disconnects).to eql(4)
      expect(reconnects).to eql(3)
      expect(closes).to eql(1)

      # Client disconnected from server
      expect(errors.first).to be_a(NATS::ClientError)
      expect(errors.last).to  be_a(NATS::ConnectError)
    end

    it 'should reject secure connection if no certificate is provided' do
      errors      = []
      connects    = 0
      reconnects  = 0
      disconnects = 0
      closes      = 0

      with_em_timeout(5) do |future|
        nc = nil

        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        nc = NATS.connect({
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
            :tls => {
              :ssl_version => :TLSv1_2
            }
          }) do
          connects += 1
        end
        nc.subscribe("hello")
        nc.flush
      end
      expect(errors.count).to eql(1)
      expect(errors.first).to be_a(NATS::ConnectError)
      expect(connects).to eql(0)
      expect(closes).to eql(1)
      expect(disconnects).to eql(0)
    end

    it 'should connect securely to server and authorize' do
      errors = []
      messages = []
      connects = 0
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(10) do
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :ssl_version => :TLSv1_2,
            :protocols => [:tlsv1_2],
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
            :verify_peer      => false
          }
        }

        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))

          connects += 1
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
          end
        end
      end
      expect(errors.count).to eql(0)
      expect(messages.count).to eql(1)
      expect(disconnects).to eql(0)
      expect(closes).to eql(1)
    end

    it 'should connect securely with default TLS and protocols options' do
      errors      = []
      messages    = []
      connects    = 0
      reconnects  = 0
      disconnects = 0
      closes      = 0

      with_em_timeout(10) do |future|

        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
          }
        }

        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))
          connects += 1
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
          end
        end
      end
      expect(messages.count).to eql(1)
      expect(errors.count).to eql(0)
      expect(closes).to eql(1)
      expect(reconnects).to eql(0)
      expect(disconnects).to eql(0)
    end
  end

  context 'when server requires TLS, certificates, authentication and client enables verify peer' do

    before(:each) do
      @tls_verify_auth = NatsServerControl.new("nats://127.0.0.1:4445", '/tmp/test-nats-4445.pid', "-c ./spec/configs/tlsverify.conf")
      @tls_verify_auth.start_server
    end

    after(:each) do
      @tls_verify_auth.kill_server
    end

    it 'should fail to connect if CA is not given' do
      errors = []
      connects = 0
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(3) do
        nc = nil
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :ssl_version => :TLSv1_2,
            :protocols => [:tlsv1_2],
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
            :verify_peer      => true
          }
        }
        expect do
          nc = NATS.connect(options) do |conn|
            connects += 1
          end
        end.to raise_error(NATS::Error)
      end
      expect(errors.count).to eql(0)
      expect(disconnects).to eql(0)
      expect(closes).to eql(0)
    end

    it 'should fail to connect if CA is not readable' do
      errors = []
      connects = 0
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(3) do
        nc = nil
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :ssl_version => :TLSv1_2,
            :protocols => [:tlsv1_2],
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
            :ca_file => './spec/configs/certs/does-not-exists.pem',
            :verify_peer      => true
          }
        }
        expect do
          nc = NATS.connect(options) do |conn|
            connects += 1
          end
        end.to raise_error(NATS::Error)
      end

      # No error here since it fails synchronously
      expect(errors.count).to eql(0)
      expect(disconnects).to eql(0)
      expect(closes).to eql(0)
    end

    it 'should connect securely to server and authorize' do
      errors = []
      messages = []
      connects = 0
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(10) do |future|
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :ssl_version => :TLSv1_2,
            :protocols => [:tlsv1_2],
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
            :ca_file => './spec/configs/certs/ca.pem',
            :verify_peer => true
          }
        }

        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))

          connects += 1
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
            future.resume(nc)
          end
        end
      end
      expect(errors.count).to eql(0)
      expect(messages.count).to eql(1)
      expect(disconnects).to eql(0)
      expect(closes).to eql(1)
    end

    it 'should give up connecting securely to server if cannot verify peer' do
      errors = []
      messages = []
      connects = 0
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(10) do |future|
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :ssl_version => :TLSv1_2,
            :protocols => [:tlsv1_2],
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
            :ca_file => './spec/configs/certs/bad-ca.pem',
            :verify_peer => true
          }
        }

        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))

          connects += 1
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
            future.resume(nc)
          end
        end
      end
      expect(errors.count).to eql(2)
      expect(errors.first).to be_a(NATS::ConnectError)
      expect(errors.first.to_s).to eql('TLS Verification failed checking issuer based on CA ./spec/configs/certs/bad-ca.pem')
      
      expect(errors.last).to be_a(NATS::ConnectError)
      expect(messages.count).to eql(0)
      expect(disconnects).to eql(0)
      expect(closes).to eql(1)
    end

    it 'should connect securely with default TLS and protocols options and assume verify if CA given' do
      errors      = []
      messages    = []
      connects    = 0
      reconnects  = 0
      disconnects = 0
      closes      = 0

      with_em_timeout(10) do |future|

        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
            :ca_file => './spec/configs/certs/ca.pem'
          }
        }

        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))
          connects += 1
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
          end
        end
      end
      expect(messages.count).to eql(1)
      expect(errors.count).to eql(0)
      expect(closes).to eql(1)
      expect(reconnects).to eql(0)
      expect(disconnects).to eql(0)
    end

    it 'should connect securely with verify peer when multiple CAs are present in the CA file' do
      errors      = []
      messages    = []
      connects    = 0
      reconnects  = 0
      disconnects = 0
      closes      = 0

      with_em_timeout(10) do |future|
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://secret:deadbeef@127.0.0.1:4445'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :ssl_version => :TLSv1_2,
            :protocols => [:tlsv1_2],
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
            :ca_file => './spec/configs/certs/multi-ca.pem', # First CA is invalid, second is valid
            :verify_peer => true
          }
        }

        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://secret:deadbeef@127.0.0.1:4445'))

          connects += 1
        end

        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world") do
            nc.unsubscribe(sid)
            nc.close
            future.resume(nc)
          end
        end
      end
      expect(messages.count).to eql(1)
      expect(errors.count).to eql(0)
      expect(closes).to eql(1)
      expect(reconnects).to eql(0)
      expect(disconnects).to eql(0)
    end
  end

  context 'when servers require TLS' do

    before(:each) do
      @tls_server = NatsServerControl.new("nats://127.0.0.1:4445", '/tmp/test-nats-4445.pid', "-c ./spec/configs/tls-no-auth.conf")
      @tls_server.start_server
      @tls_server2 = NatsServerControl.new("nats://127.0.0.1:4446", '/tmp/test-nats-4446.pid', "-c ./spec/configs/tls-no-auth.conf")
      @tls_server2.start_server
    end

    after(:each) do
      @tls_server.kill_server if NATS.server_running?(@tls_server.uri)
      @tls_server2.kill_server if NATS.server_running?(@tls_server2.uri)
    end

    it 'should connect securely to server and reconnect' do
      errors = []
      messages = []
      connects = 0
      reconnects = 0
      disconnects = 0
      closes = 0

      with_em_timeout(10) do |future|
        NATS.on_error do |e|
          errors << e
        end

        NATS.on_disconnect do |e|
          disconnects += 1
        end

        NATS.on_reconnect do
          reconnects += 1
        end

        NATS.on_close do
          closes += 1
        end

        options = {
          :servers => ['nats://127.0.0.1:4445','nats://127.0.0.1:4446'],
          :max_reconnect_attempts => 1,
          :dont_randomize_servers => true,
          :tls => {
            :ssl_version => :TLSv1_2,
            :protocols => [:tlsv1_2],
            :private_key_file => './spec/configs/certs/key.pem',
            :cert_chain_file  => './spec/configs/certs/server.pem',
          }
        }

        # Confirm connect is ok
        nc = NATS.connect(options) do |conn|
          expect(conn.connected_server).to eql(URI.parse('nats://127.0.0.1:4445'))

          connects += 1
        end
        sid = nc.subscribe("hello") do |msg|
          messages << msg
        end
        nc.flush do
          nc.publish("hello", "world")
        end

        # Confirm that the client reconnects
        EM.add_timer(1) do
          @tls_server.kill_server
        end

        # Should be reconnected at this point and subscriptions replayed
        EM.add_timer(2) do
          nc.flush do
            nc.publish("hello", "again") do
              nc.unsubscribe(sid)
              nc.close
              future.resume
            end
          end
        end
      end
      expect(errors.count).to eql(0)
      expect(messages.count).to eql(2)
      expect(disconnects).to eql(1)
      expect(closes).to eql(1)
    end
  end
end
