require 'spec_helper'

#$DEBUG = true 
describe KJess::Client do
  before do
    @client = KJess::Client.new
  end

  after do
    KJess::Spec.reset_server( @client )
  end

  describe "connection" do
    it "knows if it is connected" do
      @client.ping
      @client.connected?.must_equal true
    end

    it "can disconnect and know it is disconnected" do
      @client.ping
      @client.connected?.must_equal true
      @client.disconnect
      @client.connected?.must_equal false
    end
  end

  describe "#version" do
    it "knows the version of the server" do
      @client.version.must_equal "2.3.4"
    end
  end

  describe "#stats" do
    it "can see the stats on an empty server" do
      @client.stats['version'].must_equal '2.3.4'
    end

    it "sees the stats on a server with queues" do
      @client.set( 'stat_q_foo', 'stat_spec_foo' )
      @client.set( 'stat_q_bar', 'stat_spec_bar' )
      @client.stats['queues'].keys.sort.must_equal %w[ stat_q_bar stat_q_foo ]
    end

    it "has an empty queues hash when there are no queues" do
      @client.stats['queues'].size.must_equal 0
    end
  end


  describe "#set" do
    it "adds a item to the server" do
      @client.stats['curr_items'].must_equal 0
      @client.set( 'set_q', "setspec" )
      @client.stats['curr_items'].must_equal 1
    end

    it "a item with an expiration expires" do
      @client.stats['curr_items'].must_equal 0
      @client.set( 'set_q_2', "setspec",  1 )
      @client.stats['curr_items'].must_equal 1
      @client.set( 'set_q_2', "setspec2" )
      @client.stats['curr_items'].must_equal 2
      while s = @client.stats do
        break if s['curr_items'] == 1
      end
      @client.get( 'set_q_2' ).must_equal 'setspec2'
    end
  end

  describe "#get" do
    it "retrieves a item from queue" do
      @client.set( 'get_q' , "a get item" )
      @client.get( 'get_q' ).must_equal 'a get item'
    end

    it "returns nil if no item is found" do
      @client.get( 'get_q' ).must_be_nil
    end

    it "waits for a period of time and then times out" do
      t1 = Time.now.to_f
      x = @client.get( 'get_q', :wait_for => 100 )
      t2 = Time.now.to_f
      (t2 - t1).must_be :>=, 0.1
      x.must_be_nil
    end

    it "raises an error if peeking and aborting" do
      lambda { @client.get( 'get_q', :peek => true, :abort => true ) }.must_raise KJess::ClientError
    end

    it "raises an error if peeking and opening" do
      lambda { @client.get( 'get_q', :peek => true, :open => true ) }.must_raise KJess::ClientError
    end

    it "raises an error if peeking and closing " do
      lambda { @client.get( 'get_q', :peek => true, :close => true ) }.must_raise KJess::ClientError
    end

    it "raises an error if aborting and opening" do
      lambda { @client.get( 'get_q', :peek => true, :open => true ) }.must_raise KJess::ClientError
    end

    it "raises an error if aborting and closing" do
      lambda { @client.get( 'get_q', :peek => true, :close => true ) }.must_raise KJess::ClientError
    end

    it "raises an error if we attempt to non-tranactionaly get after an open transaction" do
      @client.set( "get_q", "get item 1" )
      @client.set( "get_q", "get item 2" )
      @client.reserve( "get_q" )
      lambda { @client.get( "get_q" ) }.must_raise KJess::Error
    end

  end

  describe "#reserve" do
    it "reserves a item for reliable read" do
      @client.set( 'reserve_q', 'a reserve item' )
      @client.queue_stats( 'reserve_q' )['open_transactions'].must_equal 0
      @client.reserve( 'reserve_q' ).must_equal 'a reserve item'
      @client.queue_stats( 'reserve_q' )['open_transactions'].must_equal 1
    end
  end

  describe "#close_and_reserve" do
    it "reserves an item for reliable read and closes an existing read" do
      @client.set( 'reserve_q', 'a reserve item 1' )
      @client.set( 'reserve_q', 'a reserve item 2' )
      @client.queue_stats( 'reserve_q' )['open_transactions'].must_equal 0

      i1 = @client.reserve( 'reserve_q' )
      i1.must_equal 'a reserve item 1'
      @client.queue_stats( 'reserve_q' )['open_transactions'].must_equal 1

      i2 = @client.close_and_reserve( 'reserve_q' )
      i2.must_equal 'a reserve item 2'

      q_stats = @client.queue_stats('reserve_q')
      q_stats['open_transactions'].must_equal 1
      q_stats['items'].must_equal 0
    end
  end

  describe "#close" do
    it "closes an existing read" do
      @client.set( 'close_q', 'close item 1' )
      @client.queue_stats( 'close_q' )['open_transactions'].must_equal 0
      @client.reserve( 'close_q' )
      @client.queue_stats( 'close_q' )['open_transactions'].must_equal 1
      @client.close( 'close_q' )
      @client.queue_stats( 'close_q' )['items'].must_equal 0
      @client.queue_stats( 'close_q' )['open_transactions'].must_equal 0
    end

    it "does not return a new item from the queue" do
      @client.set( 'close_q', 'close item 1' )
      @client.set( 'close_q', 'close item 2' )
      @client.queue_stats( 'close_q' )['open_transactions'].must_equal 0
      @client.reserve( 'close_q' )
      @client.queue_stats( 'close_q' )['open_transactions'].must_equal 1
      @client.close( 'close_q' )
      @client.queue_stats( 'close_q' )['items'].must_equal 1
      @client.queue_stats( 'close_q' )['open_transactions'].must_equal 0
    end
  end

  describe "#abort" do
    it "aborts a reserved item" do
      @client.set( 'abort_q', 'abort item 1' )
      q_stats = @client.queue_stats('abort_q')
      q_stats['items'].must_equal 1

      @client.reserve( 'abort_q' )
      q_stats = @client.queue_stats('abort_q')
      q_stats['open_transactions'].must_equal 1

      i2 = @client.abort( 'abort_q' )
      q_stats = @client.queue_stats('abort_q')
      q_stats['open_transactions'].must_equal 0
      q_stats['items'].must_equal 1

      i2.must_be_nil
    end
  end

  describe  "#peek" do
    it "looks at a item at the front and does not remove it" do
      @client.stats['curr_items'].must_equal 0
      @client.set( 'peek_q', "peekitem" )
      @client.stats['curr_items'].must_equal 1
      @client.peek('peek_q').must_equal 'peekitem'
      @client.stats['curr_items'].must_equal 1
    end
  end

  describe "#delete" do
    it "deletes a queue" do
      @client.stats['queues'].size.must_equal 0
      @client.set( 'delete_q_1', 'delete me' )
      @client.queue_stats( 'delete_q_1' )['items'].must_equal 1
      @client.delete( 'delete_q_1' )
      @client.queue_stats('delete_q_1').must_be_nil
    end

    it "is okay to delete a queue that does not exist" do
      @client.delete( 'delete_q_does_not_exist' ).must_equal true
    end
  end

  describe "#flush" do
    it "removes all the items from a queue" do
      5.times { |x| @client.set( 'flush_q', "flush_me #{x}" ) }
      @client.queue_stats( 'flush_q' )['items'].must_equal 5
      @client.flush( 'flush_q' )
      @client.queue_stats( 'flush_q' )['items'].must_equal 0
    end

    it "is fine with flushing a non-existant queue" do
      @client.queue_stats( 'flush_q' ).must_be_nil
      @client.flush( 'flush_q' ).must_equal true
      @client.queue_stats( 'flush_q' ).must_be_nil
    end
  end

  describe "#flush_all" do
    it "removes all items from all queues" do
      @client.stats['curr_items'].must_equal 0
      3.times do |qx|
        4.times do |ix|
          @client.set( "flush_all_queue_#{qx}", "item #{qx} #{ix}" )
        end
      end
      @client.stats['queues'].size.must_equal 3
      @client.stats['curr_items'].must_equal 12
      @client.flush_all
      @client.stats['curr_items'].must_equal 0
      @client.stats['queues'].size.must_equal 3
    end
  end

  describe "#reload" do
    it "tells kestrel to reload its config" do
      @client.reload.must_equal true
    end
  end

  describe "#quit" do
    it "disconnects from the server" do
      @client.quit.must_equal true
    end
  end

  describe "#status" do
    it "returns the server status" do
      lambda { @client.status }.must_raise KJess::ClientError
    end
  end

  describe "#ping" do
    it "knows if a server is up" do
      @client.ping.must_equal true
    end
  end
end