describe MeterCat::Cache do

  before(:each) do
    @cache = MeterCat::Cache.instance
    @cache.clear

    @name = :test
    @value = 727
    @date = Date.parse('2013-09-05')

    @missing_name = :missing
    @other_date = Date.parse('1999-09-11')
  end

  it 'is a subclass of Hash' do
    @cache.should be_a_kind_of(Hash)
  end

  it 'is a singleton' do
    @cache.should be(MeterCat::Cache.instance)
  end

  #############################################################################
  # Cache#add

  describe '#add' do

    context 'cache miss' do

      it 'stores the new meter' do
        @cache.should_receive(:cache).with(@name, @value, @date)
        @cache.add(@name, @value, @date)
      end
    end

    context 'cache hit' do

      context 'for different day' do

        before(:each) do
          @cache.add(@name, @value, @other_date)
        end

        it 'flushes the cache' do
          @cache.should_receive(:flush).with(@name)
          @cache.add(@name, @value, @date)
        end

        it 'caches the new data' do
          @cache.should_receive(:cache).with(@name, @value, @date)
          @cache.add(@name, @value, @date)
        end
      end

      context 'for same day' do

        before(:each) do
          @cache.add(@name, @value, @date)
        end

        context 'not expired' do

          before(:each) do
            @cache[@name].should_receive(:expired?).once.and_return(false)
          end

          it 'increments the cached value' do
            @cache.add(@name, @value, @date)

            meter = @cache[@name]
            meter.should be_present
            meter.name.should eql(@name.to_s) # TODO: to_s added during Rails 5 upgrade.  Confirm it's OK
            meter.value.should eql(@value * 2)
            meter.created_on.should eql(@date)
          end

          it 'does not flush the cache' do
            @cache.should_not_receive(:flush)
            @cache.add(@name, @value, @date)
          end

        end

        it 'flushes the cache if expired' do
          @cache[@name].should_receive(:expired?).once.and_return(true)
          @cache.should_receive(:flush).with(@name)
          @cache.add(@name, @value, @date)
        end
      end
    end
  end

  #############################################################################
  # Cache#cache

  describe '#cache' do

    it 'stores a new meter in the cache' do
      now = Time.now
      Time.should_receive(:now).at_least(:once).and_return(now)

      @cache[@name].should be_nil
      @cache.cache(@name, @value, @date)

      meter = @cache[@name]
      meter.should be_present
      meter.name.should eql(@name.to_s) # TODO: to_s added during Rails 5 upgrade.  Confirm it's OK
      meter.value.should eql(@value)
      meter.created_on.should eql(@date)
      meter.created_at.should eql(now)
    end
  end

  #############################################################################
  # Cache#flush

  describe '#flush' do

    before(:each) do
      @cache.add(@name, @value, @date)
    end

    it 'adds the cached meter to the database' do
      @cache[@name].should_receive(:add).and_return(true)
      @cache.flush(@name)
    end

    it 'clears the cached meter' do
      @cache[@name].should be_present
      @cache.flush(@name)
      @cache[@name].should be_nil
    end

    it 'tolerates a cache miss' do
      @cache[@missing_name].should be_nil
      @cache.flush(@missing_name)
    end
  end

  #############################################################################
  # Cache#flush_all

  describe '#flush_all' do

    before(:each) do
      @cache.add(:foo, @value, @date)
      @cache.add(:bar, @value, @date)
    end

    it 'flushes all the cached meters to db' do
      @cache.values.each { |meter| meter.should_receive(:add).and_return(true) }
      @cache.flush_all
    end

    it 'leaves the hash empty' do
      @cache.flush_all
      @cache.should be_empty
    end
  end
end