require 'shared/all_success_return_values'

shared_examples_for "connection" do

  before :each do
    ensure_node(zk, path, data)
  end

  after :each do
    ensure_node(zk, path, data)
  end

  # unfortunately, we can't test w/o exercising other parts of the driver, so
  # if "set" is broken, this test will fail as well (but whaddyagonnado?)
  describe :get do
    describe :sync, :sync => true do
      it_should_behave_like "all success return values"

      before do
        @rv = zk.get(:path => path)
      end

      it %[should return the data] do
        expect(@rv[:data]).to eq(data)
      end

      it %[should return a stat] do
        expect(@rv[:stat]).not_to be_nil
        expect(@rv[:stat]).to be_kind_of(Zookeeper::Stat)
      end
    end

    describe :sync_watch, :sync => true do
      it_should_behave_like "all success return values"

      before do
        @event = nil
        @watcher = Zookeeper::Callbacks::WatcherCallback.new

        @rv = zk.get(:path => path, :watcher => @watcher, :watcher_context => path)
      end

      it %[should return the data] do
        expect(@rv[:data]).to eq(data)
      end

      it %[should set a watcher on the node] do
        # test the watcher by changing node data
        expect(zk.set(:path => path, :data => 'blah')[:rc]).to be_zero

        wait_until(1.0) { @watcher.completed? }

        expect(@watcher.path).to eq(path)
        expect(@watcher.context).to eq(path)
        expect(@watcher).to be_completed
        expect(@watcher.type).to eq(Zookeeper::ZOO_CHANGED_EVENT)
      end
    end

    describe :async, :async => true do
      before do
        @cb = Zookeeper::Callbacks::DataCallback.new

        @rv = zk.get(:path => path, :callback => @cb, :callback_context => path)
        wait_until(1.0) { @cb.completed? }
        expect(@cb).to be_completed
      end

      it_should_behave_like "all success return values"

      it %[should have a return code of ZOK] do
        expect(@cb.return_code).to eq(Zookeeper::ZOK)
      end

      it %[should have the stat object in the callback] do
        expect(@cb.stat).not_to be_nil
        expect(@cb.stat).to be_kind_of(Zookeeper::Stat)
      end

      it %[should have the data] do
        expect(@cb.data).to eq(data)
      end
    end

    describe :async_watch, :async => true, :method => :get, :watch => true do
      it_should_behave_like "all success return values"

      before do
        logger.debug { "-----------------> MAKING ASYNC GET REQUEST WITH WATCH <--------------------" }
        @cb = Zookeeper::Callbacks::DataCallback.new
        @watcher = Zookeeper::Callbacks::WatcherCallback.new

        @rv = zk.get(:path => path, :callback => @cb, :callback_context => path, :watcher => @watcher, :watcher_context => path)
        wait_until(1.0) { @cb.completed? }
        expect(@cb).to be_completed
        logger.debug { "-----------------> ASYNC GET REQUEST WITH WATCH COMPLETE <--------------------" }
      end

      it %[should have the stat object in the callback] do
        expect(@cb.stat).not_to be_nil
        expect(@cb.stat).to be_kind_of(Zookeeper::Stat)
      end

      it %[should have the data] do
        expect(@cb.data).to eq(data)
      end

      it %[should have a return code of ZOK] do
        expect(@cb.return_code).to eq(Zookeeper::ZOK)
      end

      it %[should set a watcher on the node] do
        expect(zk.set(:path => path, :data => 'blah')[:rc]).to be_zero

        wait_until(2) { @watcher.completed? }

        expect(@watcher).to be_completed

        expect(@watcher.path).to eq(path)
        expect(@watcher.context).to eq(path)
      end
    end

    describe 'bad arguments' do
      it %[should barf with a BadArguments error] do
        expect { zk.get(:bad_arg => 'what!?') }.to raise_error(Zookeeper::Exceptions::BadArguments)
      end
    end
  end   # get

  describe :set do
    before do
      @new_data = "Four score and \007 years ago"
      @stat = zk.stat(:path => path)[:stat]
    end

    describe :sync, :sync => true do
      describe 'without version' do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.set(:path => path, :data => @new_data)
        end

        it %[should return the new stat] do
          expect(@rv[:stat]).not_to be_nil
          expect(@rv[:stat]).to be_kind_of(Zookeeper::Stat)
          expect(@rv[:stat].version).to be > @stat.version
        end
      end

      describe 'with current version' do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.set(:path => path, :data => @new_data, :version => @stat.version)
        end

        it %[should return the new stat] do
          expect(@rv[:stat]).not_to be_nil
          expect(@rv[:stat]).to be_kind_of(Zookeeper::Stat)
          expect(@rv[:stat].version).to be > @stat.version
        end
      end

      describe 'with outdated version' do
        before do
          # need to do a couple of sets to ramp up the version
          3.times { |n| @stat = zk.set(:path => path, :data => "#{@new_data}#{n}")[:stat] }

          @rv = zk.set(:path => path, :data => @new_data, :version => 0)
        end

        it %[should have a return code of ZBADVERSION] do
          expect(@rv[:rc]).to eq(Zookeeper::ZBADVERSION)
        end

        it %[should return a stat with !exists] do
          expect(@rv[:stat].exists).to be_falsey
        end
      end

      describe 'error' do
        it %[should barf if the data size is too large], :input_size => true do
          large_data = '0' * (1024 ** 2)

          expect { zk.set(:path => path, :data => large_data) }.to raise_error(Zookeeper::Exceptions::DataTooLargeException)
        end
      end
    end   # sync

    describe :async, :async => true do
      before do
        @cb = Zookeeper::Callbacks::StatCallback.new
      end

      describe 'without version' do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.set(:path => path, :data => @new_data, :callback => @cb, :callback_context => path)

          wait_until(2) { @cb.completed? }
          expect(@cb).to be_completed
        end

        it %[should have the stat in the callback] do
          expect(@cb.stat).not_to be_nil
          expect(@cb.stat.version).to be > @stat.version
        end

        it %[should have a return code of ZOK] do
          expect(@cb.return_code).to eq(Zookeeper::ZOK)
        end
      end

      describe 'with current version' do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.set(:path => path, :data => @new_data, :callback => @cb, :callback_context => path, :version => @stat.version)

          wait_until(2) { @cb.completed? }
          expect(@cb).to be_completed
        end

        it %[should have the stat in the callback] do
          expect(@cb.stat).not_to be_nil
          expect(@cb.stat.version).to be > @stat.version
        end

        it %[should have a return code of ZOK] do
          expect(@cb.return_code).to eq(Zookeeper::ZOK)
        end
      end

      describe 'with outdated version' do
        before do
          # need to do a couple of sets to ramp up the version
          3.times { |n| @stat = zk.set(:path => path, :data => "#{@new_data}#{n}")[:stat] }

          @rv = zk.set(:path => path, :data => @new_data, :callback => @cb, :callback_context => path, :version => 0)

          wait_until(2) { @cb.completed? }
          expect(@cb).to be_completed
        end

        it %[should have a return code of ZBADVERSION] do
          expect(@cb.return_code).to eq(Zookeeper::ZBADVERSION)
        end

        it %[should return a stat with !exists] do
          expect(@cb.stat.exists).to be_falsey
        end
      end

      describe 'error' do
        it %[should barf if the data size is too large], :input_size => true do
          large_data = '0' * (1024 ** 2)

          expect { zk.set(:path => path, :data => large_data, :callback => @cb, :callback_context => path) }.to raise_error(Zookeeper::Exceptions::DataTooLargeException)
        end
      end

    end # async
  end # set

  describe :add_auth do
    it %[should return ZOK if everything goes swimingly] do
      result = zk.add_auth(:scheme => 'digest', :cert => 'test_user:test_password')

      rv = result[:rc]

      # gahhh, this shouldn't be like this.... :P
      rv = rv.respond_to?(:intValue) ? rv.intValue : rv

      expect(rv).to eq(Zookeeper::ZOK)
    end
  end

  describe :get_children do
    before do
      @children = %w[child0 child1 child2]

      @children.each do |name|
        zk.create(:path => "#{path}/#{name}", :data => name)
      end
    end

    after do
      @children.each do |name|
        zk.delete(:path => "#{path}/#{name}")
      end
    end

    describe :sync, :sync => true do
      it_should_behave_like "all success return values"

      before do
        @rv = zk.get_children(:path => path)
      end

      it %[should have an array of names of the children] do
        expect(@rv[:children]).to be_kind_of(Array)
        expect(@rv[:children].length).to eq(3)
        expect(@rv[:children].sort).to eq(@children.sort)
      end

      # "Three shall be the number of the counting, and the number of the counting shall be 3"

      it %[should have a stat object whose num_children is 3] do
        expect(@rv[:stat]).not_to be_nil
        expect(@rv[:stat]).to be_kind_of(Zookeeper::Stat)
        expect(@rv[:stat].num_children).to eq(3)
      end
    end

    describe :sync_watch, :sync => true do
      it_should_behave_like "all success return values"

      before do
        @addtl_child = 'child3'

        @watcher = Zookeeper::Callbacks::WatcherCallback.new

        @rv = zk.get_children(:path => path, :watcher => @watcher, :watcher_context => path)
      end

      after do
        zk.delete(:path => "#{path}/#{@addtl_child}")
      end

      it %[should have an array of names of the children] do
        expect(@rv[:children]).to be_kind_of(Array)
        expect(@rv[:children].length).to eq(3)
        expect(@rv[:children].sort).to eq(@children.sort)
      end

      it %[should have a stat object whose num_children is 3] do
        expect(@rv[:stat]).not_to be_nil
        expect(@rv[:stat]).to be_kind_of(Zookeeper::Stat)
        expect(@rv[:stat].num_children).to eq(3)
      end

      it %[should set a watcher for children on the node] do
        expect(@watcher).not_to be_completed

        expect(zk.create(:path => "#{path}/#{@addtl_child}", :data => '')[:rc]).to eq(Zookeeper::ZOK)

        wait_until { @watcher.completed? }
        expect(@watcher).to be_completed

        expect(@watcher.path).to eq(path)
        expect(@watcher.context).to eq(path)
        expect(@watcher.type).to eq(Zookeeper::ZOO_CHILD_EVENT)
      end
    end

    describe :async, :async => true do
      it_should_behave_like "all success return values"

      before do
        @cb = Zookeeper::Callbacks::StringsCallback.new
        @rv = zk.get_children(:path => path, :callback => @cb, :callback_context => path)

        wait_until { @cb.completed? }
        expect(@cb).to be_completed
      end

      it %[should succeed] do
        expect(@cb.return_code).to eq(Zookeeper::ZOK)
      end

      it %[should return an array of children] do
        expect(@cb.children).to be_kind_of(Array)
        expect(@cb.children.length).to eq(3)
        expect(@cb.children.sort).to eq(@children.sort)
      end

      it %[should have a stat object whose num_children is 3] do
        expect(@cb.stat).not_to be_nil
        expect(@cb.stat).to be_kind_of(Zookeeper::Stat)
        expect(@cb.stat.num_children).to eq(3)
      end
    end

    describe :async_watch, :async => true do
      it_should_behave_like "all success return values"

      before do
        @addtl_child = 'child3'

        @watcher = Zookeeper::Callbacks::WatcherCallback.new
        @cb = Zookeeper::Callbacks::StringsCallback.new

        @rv = zk.get_children(:path => path, :watcher => @watcher, :watcher_context => path, :callback => @cb, :callback_context => path)
        wait_until { @cb.completed? }
        expect(@cb).to be_completed
      end

      after do
        zk.delete(:path => "#{path}/#{@addtl_child}")
      end

      it %[should succeed] do
        expect(@cb.return_code).to eq(Zookeeper::ZOK)
      end

      it %[should return an array of children] do
        expect(@cb.children).to be_kind_of(Array)
        expect(@cb.children.length).to eq(3)
        expect(@cb.children.sort).to eq(@children.sort)
      end

      it %[should have a stat object whose num_children is 3] do
        expect(@cb.stat).not_to be_nil
        expect(@cb.stat).to be_kind_of(Zookeeper::Stat)
        expect(@cb.stat.num_children).to eq(3)
      end

      it %[should set a watcher for children on the node] do
        expect(@watcher).not_to be_completed

        expect(zk.create(:path => "#{path}/#{@addtl_child}", :data => '')[:rc]).to eq(Zookeeper::ZOK)

        wait_until { @watcher.completed? }
        expect(@watcher).to be_completed

        expect(@watcher.path).to eq(path)
        expect(@watcher.context).to eq(path)
        expect(@watcher.type).to eq(Zookeeper::ZOO_CHILD_EVENT)
      end
    end
  end

  describe :child_watcher_behavior do
    describe :async_watch, :async => true do
      it_should_behave_like "all success return values"

      before do
        @watcher = Zookeeper::Callbacks::WatcherCallback.new
        @cb = Zookeeper::Callbacks::StringsCallback.new

        @rv = zk.get_children(:path => path, :watcher => @watcher, :watcher_context => path, :callback => @cb, :callback_context => path)
        wait_until { @cb.completed? }
        expect(@cb).to be_completed
      end

      it %[should fire the watcher when the node has been deleted] do
        expect(@watcher).not_to be_completed

        expect(zk.delete(:path => path)[:rc]).to eq(Zookeeper::ZOK)

        wait_until { @watcher.completed? }
        expect(@watcher).to be_completed

        expect(@watcher.path).to eq(path)
        expect(@watcher.context).to eq(path)
        expect(@watcher.type).to eq(Zookeeper::ZOO_DELETED_EVENT)
      end
    end
  end


  # NOTE: the jruby version of stat on non-existent node will have a
  # return_code of 0, but the C version will have a return_code of -101
  describe :stat do
    describe :sync, :sync => true do
      it_should_behave_like "all success return values"

      before do
        @rv = zk.stat(:path => path)
      end

      it %[should have a stat object] do
        expect(@rv[:stat]).to be_kind_of(Zookeeper::Stat)
      end
    end

    describe :sync_watch, :sync => true do
      it_should_behave_like "all success return values"

      before do
        @watcher = Zookeeper::Callbacks::WatcherCallback.new

        @rv = zk.stat(:path => path, :watcher => @watcher, :watcher_context => path)
      end

      it %[should have a stat object] do
        expect(@rv[:stat]).to be_kind_of(Zookeeper::Stat)
      end

      it %[should set a watcher for data changes on the node] do
        expect(@watcher).not_to be_completed

        expect(zk.set(:path => path, :data => 'skunk')[:rc]).to eq(Zookeeper::ZOK)

        wait_until { @watcher.completed? }
        expect(@watcher).to be_completed

        expect(@watcher.path).to eq(path)
        expect(@watcher.context).to eq(path)
        expect(@watcher.type).to eq(Zookeeper::ZOO_CHANGED_EVENT)
      end
    end

    describe :async, :async => true do
      it_should_behave_like "all success return values"

      before do
        @cb = Zookeeper::Callbacks::StatCallback.new
        @rv = zk.stat(:path => path, :callback => @cb, :callback_context => path)

        wait_until { @cb.completed? }
        expect(@cb).to be_completed
      end

      it %[should succeed] do
        expect(@cb.return_code).to eq(Zookeeper::ZOK)
      end

      it %[should have a stat object] do
        expect(@cb.stat).to be_kind_of(Zookeeper::Stat)
      end
    end

    describe :async_watch, :async => true do
      it_should_behave_like "all success return values"

      before do
        @addtl_child = 'child3'

        @watcher = Zookeeper::Callbacks::WatcherCallback.new

        @cb = Zookeeper::Callbacks::StatCallback.new
        @rv = zk.stat(:path => path, :callback => @cb, :callback_context => path, :watcher => @watcher, :watcher_context => path)

        wait_until { @cb.completed? }
        expect(@cb).to be_completed
      end

      after do
        zk.delete(:path => "#{path}/#{@addtl_child}")
      end

      it %[should succeed] do
        expect(@cb.return_code).to eq(Zookeeper::ZOK)
      end

      it %[should have a stat object] do
        expect(@cb.stat).to be_kind_of(Zookeeper::Stat)
      end

      it %[should set a watcher for data changes on the node] do
        expect(@watcher).not_to be_completed

        expect(zk.set(:path => path, :data => 'skunk')[:rc]).to eq(Zookeeper::ZOK)

        wait_until { @watcher.completed? }
        expect(@watcher).to be_completed

        expect(@watcher.path).to eq(path)
        expect(@watcher.context).to eq(path)
        expect(@watcher.type).to eq(Zookeeper::ZOO_CHANGED_EVENT)
      end
    end
  end   # stat

  describe :create do
    before do
      # remove the path set up by the global 'before' block
      zk.delete(:path => path)
    end

    describe :sync, :sync => true do
      describe 'error' do
        it %[should barf if the data size is too large], :input_size => true do
          large_data = '0' * (1024 ** 2)

          expect { zk.create(:path => path, :data => large_data) }.to raise_error(Zookeeper::Exceptions::DataTooLargeException)
        end
      end

      describe :default_flags do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.create(:path => path)
        end

        it %[should return the path that was set] do
          expect(@rv[:path]).to eq(path)
        end

        it %[should have created a permanent node] do
          st = zk.stat(:path => path)
          expect(st[:rc]).to eq(Zookeeper::ZOK)

          expect(st[:stat].ephemeral_owner).to eq(0)
        end
      end

      describe :ephemeral do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.create(:path => path, :ephemeral => true)
        end

        it %[should return the path that was set] do
          expect(@rv[:path]).to eq(path)
        end

        it %[should have created a ephemeral node] do
          st = zk.stat(:path => path)
          expect(st[:rc]).to eq(Zookeeper::ZOK)

          expect(st[:stat].ephemeral_owner).not_to be_zero
        end
      end

      describe :sequence do
        it_should_behave_like "all success return values"

        before do
          @orig_path  = path
          @rv         = zk.create(:path => path, :sequence => true)
          @s_path     = @rv[:path]    # make sure this gets cleaned up
        end

        after do
          zk.delete(:path => @s_path)
        end

        it %[should return the path that was set] do
          expect(@rv[:path]).not_to eq(@orig_path)
        end

        it %[should have created a permanent node] do
          st = zk.stat(:path => @s_path)
          expect(st[:rc]).to eq(Zookeeper::ZOK)

          expect(st[:stat].ephemeral_owner).to be_zero
        end
      end

      describe :ephemeral_sequence do
        it_should_behave_like "all success return values"

        before do
          @orig_path  = path
          @rv         = zk.create(:path => path, :sequence => true, :ephemeral => true)
          @s_path     = @rv[:path]    # make sure this gets cleaned up
        end

        after do
          zk.delete(:path => @s_path)
        end

        it %[should return the path that was set] do
          expect(@rv[:path]).not_to eq(@orig_path)
        end

        it %[should have created an ephemeral node] do
          st = zk.stat(:path => @s_path)
          expect(st[:rc]).to eq(Zookeeper::ZOK)

          expect(st[:stat].ephemeral_owner).not_to be_zero
        end
      end

      xdescribe :acl do
        it %[should work] do
          pending "need to write acl tests"
        end
      end
    end

    describe :async, :async => true do
      before do
        @cb = Zookeeper::Callbacks::StringCallback.new
      end

      describe :default_flags do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.create(:path => path, :callback => @cb, :callback_context => path)
          wait_until(2) { @cb.completed? }
          expect(@cb).to be_completed
        end

        it %[should have a path] do
          expect(@cb.path).not_to be_nil
        end

        it %[should return the path that was set] do
          expect(@cb.path).to eq(path)
        end

        it %[should have created a permanent node] do
          st = zk.stat(:path => path)
          expect(st[:rc]).to eq(Zookeeper::ZOK)

          expect(st[:stat].ephemeral_owner).to eq(0)
        end
      end

      describe 'error' do
        it %[should barf if the data size is too large], :input_size => true do
          large_data = '0' * (1024 ** 2)

          expect do
            zk.create(:path => path, :data => large_data, :callback => @cb, :callback_context => path)
          end.to raise_error(Zookeeper::Exceptions::DataTooLargeException)
        end
      end


      describe :ephemeral do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.create(:path => path, :ephemeral => true, :callback => @cb, :callback_context => path)
          wait_until(2) { @cb.completed? }
          expect(@cb).to be_completed
        end

        it %[should have a path] do
          expect(@cb.path).not_to be_nil
        end

        it %[should return the path that was set] do
          expect(@cb.path).to eq(path)
        end

        it %[should have created a ephemeral node] do
          st = zk.stat(:path => path)
          expect(st[:rc]).to eq(Zookeeper::ZOK)

          expect(st[:stat].ephemeral_owner).not_to be_zero
        end
      end

      describe :sequence do
        it_should_behave_like "all success return values"

        before do
          @orig_path  = path
          @rv         = zk.create(:path => path, :sequence => true, :callback => @cb, :callback_context => path)

          wait_until(2) { @cb.completed? }
          expect(@cb).to be_completed

          @s_path = @cb.path
        end

        after do
          zk.delete(:path => @s_path)
        end

        it %[should have a path] do
          expect(@cb.path).not_to be_nil
        end

        it %[should return the path that was set] do
          expect(@cb.path).not_to eq(@orig_path)
        end

        it %[should have created a permanent node] do
          st = zk.stat(:path => @s_path)
          expect(st[:rc]).to eq(Zookeeper::ZOK)

          expect(st[:stat].ephemeral_owner).to be_zero
        end
      end

      describe :ephemeral_sequence do
        it_should_behave_like "all success return values"

        before do
          @orig_path  = path
          @rv         = zk.create(:path => path, :sequence => true, :ephemeral => true, :callback => @cb, :callback_context => path)
          path       = @rv[:path]    # make sure this gets cleaned up

          wait_until(2) { @cb.completed? }
          expect(@cb).to be_completed
          @s_path = @cb.path
        end

        after do
          zk.delete(:path => @s_path)
        end

        it %[should have a path] do
          expect(@cb.path).not_to be_nil
        end

        it %[should return the path that was set] do
          expect(@s_path).not_to eq(@orig_path)
        end

        it %[should have created an ephemeral node] do
          st = zk.stat(:path => @s_path)
          expect(st[:rc]).to eq(Zookeeper::ZOK)

          expect(st[:stat].ephemeral_owner).not_to be_zero
        end
      end # ephemeral_sequence
    end # async
  end # create

  describe :delete do
    describe :sync, :sync => true do
      describe 'without version' do
        it_should_behave_like "all success return values"

        before do
          zk.create(:path => path)
          @rv = zk.delete(:path => path)
        end

        it %[should have deleted the node] do
          expect(zk.stat(:path => path)[:stat].exists).to be_falsey
        end
      end

      describe 'with current version' do
        it_should_behave_like "all success return values"

        before do
          zk.create(:path => path)

          @stat = zk.stat(:path => path)[:stat]
          expect(@stat.exists).to be_truthy

          @rv = zk.delete(:path => path, :version => @stat.version)
        end

        it %[should have deleted the node] do
          expect(zk.stat(:path => path)[:stat].exists).to be_falsey
        end
      end

      describe 'with old version' do
        before do
          3.times { |n| @stat = zk.set(:path => path, :data => n.to_s)[:stat] }

          @rv = zk.delete(:path => path, :version => 0)
        end

        it %[should have a return code of ZBADVERSION] do
          expect(@rv[:rc]).to eq(Zookeeper::ZBADVERSION)
        end
      end
    end # sync

    describe :async, :async => true do
      before do
        @cb = Zookeeper::Callbacks::VoidCallback.new
      end

      describe 'without version' do
        it_should_behave_like "all success return values"

        before do
          @rv = zk.delete(:path => path, :callback => @cb, :callback_context => path)
          wait_until { @cb.completed? }
          expect(@cb).to be_completed
        end

        it %[should have a success return_code] do
          expect(@cb.return_code).to eq(Zookeeper::ZOK)
        end

        it %[should have deleted the node] do
          expect(zk.stat(:path => path)[:stat].exists).to be_falsey
        end
      end

      describe 'with current version' do
        it_should_behave_like "all success return values"

        before do
          @stat = zk.stat(:path => path)[:stat]
          @rv   = zk.delete(:path => path, :version => @stat.version, :callback => @cb, :callback_context => path)
          wait_until { @cb.completed? }
          expect(@cb).to be_completed
        end

        it %[should have a success return_code] do
          expect(@cb.return_code).to eq(Zookeeper::ZOK)
        end

        it %[should have deleted the node] do
          expect(zk.stat(:path => path)[:stat].exists).to be_falsey
        end
      end

      describe 'with old version' do
        before do
          3.times { |n| @stat = zk.set(:path => path, :data => n.to_s)[:stat] }

          @rv = zk.delete(:path => path, :version => 0, :callback => @cb, :callback_context => path)
          wait_until { @cb.completed? }
          expect(@cb).to be_completed
        end

        it %[should have a return code of ZBADVERSION] do
          expect(@cb.return_code).to eq(Zookeeper::ZBADVERSION)
        end
      end
    end # async
  end # delete

  describe :get_acl do
    describe :sync, :sync => true do
      it_should_behave_like "all success return values"

      before do
        @rv = zk.get_acl(:path => path)
      end

      it %[should return a stat for the path] do
        expect(@rv[:stat]).to be_kind_of(Zookeeper::Stat)
      end

      it %[should return the acls] do
        acls = @rv[:acl]
        expect(acls).to be_kind_of(Array)
        h = acls.first

        expect(h).to be_kind_of(Hash)

        expect(h[:perms]).to eq(Zookeeper::ZOO_PERM_ALL)
        expect(h[:id][:scheme]).to eq('world')
        expect(h[:id][:id]).to eq('anyone')
      end
    end

    describe :async, :async => true do
      it_should_behave_like "all success return values"

      before do
        @cb = Zookeeper::Callbacks::ACLCallback.new
        @rv = zk.get_acl(:path => path, :callback => @cb, :callback_context => path)

        wait_until(2) { @cb.completed? }
        expect(@cb).to be_completed
      end

      it %[should return a stat for the path] do
        expect(@cb.stat).to be_kind_of(Zookeeper::Stat)
      end

      it %[should return the acls] do
        acls = @cb.acl
        expect(acls).to be_kind_of(Array)

        acl = acls.first
        expect(acl).to be_kind_of(Zookeeper::ACLs::ACL)

        expect(acl.perms).to eq(Zookeeper::ZOO_PERM_ALL)

        expect(acl.id.scheme).to eq('world')
        expect(acl.id.id).to eq('anyone')
      end
    end
  end

  xdescribe :set_acl do
    before do
      @perms = 5
      @new_acl = [Zookeeper::ACLs::ACL.new(:perms => @perms, :id => Zookeeper::Constants::ZOO_ANYONE_ID_UNSAFE)]
      pending("No idea how to set ACLs")
    end

    describe :sync, :sync => true do
      it_should_behave_like "all success return values"

      before do
        @rv = zk.set_acl(:path => path, :acl => @new_acl)
      end
    end
  end

  describe :session_id do
    it %[should return the session_id as a Fixnum] do
      expect(zk.session_id).to be_kind_of(Integer)
    end
  end

  describe :session_passwd do
    it %[should return the session passwd as a String] do
      expect(zk.session_passwd).to be_kind_of(String)
    end
  end

  describe :sync, :sync => true do
    describe :success do
      it_should_behave_like "all success return values"

      before do
        @cb = Zookeeper::Callbacks::StringCallback.new
        @rv = zk.sync(:path => path, :callback => @cb)

        wait_until(2) { @cb.completed }
        expect(@cb).to be_completed
      end
    end

    describe :errors do
      it %[should barf with BadArguments if :callback is not given] do
        expect { zk.sync(:path => path) }.to raise_error(Zookeeper::Exceptions::BadArguments)
      end
    end
  end

  describe :event_dispatch_thread? do
    it %[should return true when called on the event dispatching thread] do
      @result = nil

      cb = lambda do |hash|
        @result = zk.event_dispatch_thread?
      end

      @rv = zk.sync(:path => path, :callback => cb)

      expect(wait_until(2) { @result == true }).to be_truthy
    end

    it %[should return false when not on the event dispatching thread] do
      expect(zk.event_dispatch_thread?).not_to be_truthy
    end
  end

  describe :close do
    describe 'from the event dispatch thread' do
      it %[should not deadlock] do

        evil_cb = lambda do |*|
          logger.debug { "calling close event_dispatch_thread? #{zk.event_dispatch_thread?}" }
          zk.close
        end

        begin
          zk.stat(:path => path, :callback => evil_cb)
        rescue IOError
          # captures flaky IOError: stream closed in another thread
        end

        wait_until { zk.closed? }
        expect(zk).to be_closed
      end
    end
  end

  unless defined?(::JRUBY_VERSION)
    describe 'fork protection' do
      it %[should raise an InheritedConnectionError if the current Process.pid is different from the one that created the client] do
        pid = Process.pid
        begin
          allow(Process).to receive(:pid).and_return(-1)
          expect { zk.stat(:path => path) }.to raise_error(Zookeeper::Exceptions::InheritedConnectionError)
        ensure
          # ensure we reset this, only want it to fail during the test
          allow(Process).to receive(:pid).and_return(pid)
        end
      end
    end
  end
end

