require 'spec_helper'

require 'flapjack/data/entity_check'

describe Flapjack::Data::EntityCheck, :redis => true do

  let(:name)  { 'abc-123' }
  let(:check) { 'ping' }

  let(:half_an_hour) { 30 * 60 }
  let(:t) { Time.now.to_i }
  let(:five_minutes) { 60 * 5 }
  let(:five_hours_ago) { t - (60 * 60 * 5) }
  let(:four_hours_ago) { t - (60 * 60 * 4) }
  let(:three_hours_ago) { t - (60 * 60 * 3) }
  let(:two_hours_ago) { t - (60 * 60 * 2) }
  let(:one_hour) { 60 * 60 }
  let(:two_hours) { 60 * 60 * 2 }
  let(:three_hours) { 60 * 60 * 3 }
  let(:five_hours) { 60 * 60 * 5 }
  let(:seven_hours) { 60 * 60 * 7 }

  before(:each) do
    Flapjack::Data::Contact.add({ 'id'         => '362',
                                  'first_name' => 'John',
                                  'last_name'  => 'Johnson',
                                  'email'      => 'johnj@example.com' },
                                :redis       => @redis)

    Flapjack::Data::Entity.add({ 'id'   => '5000',
                                 'name' => name,
                                 'contacts' => ['362'] },
                               :redis => @redis)
  end

  it "is created for an event id" do
    ec = Flapjack::Data::EntityCheck.for_event_id("#{name}:ping", :redis => @redis)
    expect(ec).not_to be_nil
    expect(ec.entity).not_to be_nil
    expect(ec.entity.name).not_to be_nil
    expect(ec.entity.name).to eq(name)
    expect(ec.check).not_to be_nil
    expect(ec.check).to eq('ping')
  end

  it "is created for an entity name" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, 'ping', :redis => @redis)
    expect(ec).not_to be_nil
    expect(ec.entity).not_to be_nil
    expect(ec.entity.name).not_to be_nil
    expect(ec.entity.name).to eq(name)
    expect(ec.check).not_to be_nil
    expect(ec.check).to eq('ping')
  end

  it "is created for an entity id" do
    ec = Flapjack::Data::EntityCheck.for_entity_id(5000, 'ping', :redis => @redis)
    expect(ec).not_to be_nil
    expect(ec.entity).not_to be_nil
    expect(ec.entity.name).not_to be_nil
    expect(ec.entity.name).to eq(name)
    expect(ec.check).not_to be_nil
    expect(ec.check).to eq('ping')
  end

  it "is created for an entity object" do
    e = Flapjack::Data::Entity.find_by_name(name, :redis => @redis)
    ec = Flapjack::Data::EntityCheck.for_entity(e, 'ping', :redis => @redis)
    expect(ec).not_to be_nil
    expect(ec.entity).not_to be_nil
    expect(ec.entity.name).not_to be_nil
    expect(ec.entity.name).to eq(name)
    expect(ec.check).not_to be_nil
    expect(ec.check).to eq('ping')
  end

  it "is not created for a missing entity" do
    expect {
      Flapjack::Data::EntityCheck.for_entity(nil, 'ping', :redis => @redis)
    }.to raise_error
  end

  it "raises an error if not created with a redis connection handle" do
    expect {
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, 'ping')
    }.to raise_error
  end

  it 'registers with current_checks and current_entities if explicitly added' do
    expect(@redis.zrange("current_checks:#{name}", 0, -1)).to eq([])
    expect(@redis.zrange("current_entities", 0, -1)).to eq([])

    check_data = {'entity_id' => '5000',
                  'name'      => 'ssh'}

    Flapjack::Data::EntityCheck.add(check_data, :redis => @redis)

    expect(@redis.zrange("current_checks:#{name}", 0, -1)).to eq(["ssh"])
    expect(@redis.zrange("current_entities", 0, -1)).to eq([name])
  end

  it 'creates tags if passed when explicitly added' do
    check_data = {'entity_id' => '5000',
                  'name'      => 'ssh',
                  'tags'      => ['virtual', 'staging']}

    Flapjack::Data::EntityCheck.add(check_data, :redis => @redis)

    entity_check = Flapjack::Data::EntityCheck.for_event_id("#{name}:ssh", :redis => @redis)
    expect(entity_check).not_to be_nil
    expect(entity_check.tags).to eq(Set.new(['abc-123', 'ssh', 'virtual', 'staging']))
  end

  it "adds tags to checks" do
    entity_check = Flapjack::Data::EntityCheck.for_entity_name(name, 'ping', :redis => @redis)

    entity_check.add_tags('source:foobar', 'foo')

    expect(entity_check.tags).to include("source:foobar")
    expect(entity_check.tags).to include("foo")

    # and test the tags as read back from redis
    entity_check = Flapjack::Data::EntityCheck.for_event_id("#{name}:ping", :redis => @redis)
    expect(entity_check.tags).to include("source:foobar")
    expect(entity_check.tags).to include("foo")
  end

  it "deletes tags from checks" do
    entity_check = Flapjack::Data::EntityCheck.for_entity_name(name, 'ping', :redis => @redis)

    entity_check.add_tags('source:foobar', 'foo')

    expect(entity_check.tags).to include("source:foobar")
    expect(entity_check.tags).to include("foo")

    entity_check.delete_tags('source:foobar')

    # and test the tags as read back from redis
    entity_check = Flapjack::Data::EntityCheck.for_event_id("#{name}:ping", :redis => @redis)
    expect(entity_check.tags).not_to include("source:foobar")
    expect(entity_check.tags).to include("foo")
  end

  context "maintenance" do

    it "returns that it is in unscheduled maintenance" do
      @redis.set("#{name}:#{check}:unscheduled_maintenance", Time.now.to_i.to_s)

      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      expect(ec).to be_in_unscheduled_maintenance
    end

    it "returns that it is not in unscheduled maintenance" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      expect(ec).not_to be_in_unscheduled_maintenance
    end

    it "returns that it is in scheduled maintenance" do
      @redis.set("#{name}:#{check}:scheduled_maintenance", Time.now.to_i.to_s)

      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      expect(ec).to be_in_scheduled_maintenance
    end

    it "returns that it is not in scheduled maintenance" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      expect(ec).not_to be_in_scheduled_maintenance
    end

    it "returns its current maintenance period" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      expect(ec.current_maintenance(:scheduled => true)).to be_nil

      t = Time.now.to_i

      ec.create_unscheduled_maintenance(t, half_an_hour, :summary => 'oops')
      expect(ec.current_maintenance).to eq(:start_time => t,
                                           :duration => half_an_hour,
                                           :summary => 'oops')
    end

    it "creates an unscheduled maintenance period" do
      t = Time.now.to_i
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_unscheduled_maintenance(t, half_an_hour, :summary => 'oops')

      expect(ec).to be_in_unscheduled_maintenance

      umps = ec.maintenances(nil, nil, :scheduled => false)
      expect(umps).not_to be_nil
      expect(umps).to be_an(Array)
      expect(umps.size).to eq(1)
      expect(umps[0]).to be_a(Hash)

      start_time = umps[0][:start_time]
      expect(start_time).not_to be_nil
      expect(start_time).to be_an(Integer)
      expect(start_time).to eq(t)

      duration = umps[0][:duration]
      expect(duration).not_to be_nil
      expect(duration).to be_a(Float)
      expect(duration).to eq(half_an_hour)

      summary = @redis.get("#{name}:#{check}:#{t}:unscheduled_maintenance:summary")
      expect(summary).not_to be_nil
      expect(summary).to eq('oops')
    end

    it "creates an unscheduled maintenance period and ends the current one early", :time => true do
      t = Time.now.to_i
      later_t = t + (15 * 60)
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_unscheduled_maintenance(t, half_an_hour, :summary => 'oops')
      Delorean.time_travel_to(Time.at(later_t))
      ec.create_unscheduled_maintenance(later_t, half_an_hour, :summary => 'spoo')

      expect(ec).to be_in_unscheduled_maintenance

      umps = ec.maintenances(nil, nil, :scheduled => false)
      expect(umps).not_to be_nil
      expect(umps).to be_an(Array)
      expect(umps.size).to eq(2)
      expect(umps[0]).to be_a(Hash)

      start_time = umps[0][:start_time]
      expect(start_time).not_to be_nil
      expect(start_time).to be_an(Integer)
      expect(start_time).to eq(t)

      duration = umps[0][:duration]
      expect(duration).not_to be_nil
      expect(duration).to be_a(Float)
      expect(duration).to eq(15 * 60)

      start_time_curr = umps[1][:start_time]
      expect(start_time_curr).not_to be_nil
      expect(start_time_curr).to be_an(Integer)
      expect(start_time_curr).to eq(later_t)

      duration_curr = umps[1][:duration]
      expect(duration_curr).not_to be_nil
      expect(duration_curr).to be_a(Float)
      expect(duration_curr).to eq(half_an_hour)
    end

    it "creates an unscheduled maintenance period from a human readable time" do
      Flapjack::Data::EntityCheck.create_maintenance(:redis => @redis, :entity => name, :check => check, :type => 'unscheduled', :started => '14/3/2027 3pm', :duration => '30 minutes', :reason => 'oops')
      t = Time.local(2027, 3, 14, 15, 0).to_i

      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      expect(ec).to be_in_unscheduled_maintenance

      umps = ec.maintenances(nil, nil, :scheduled => false)
      expect(umps).not_to be_nil
      expect(umps).to be_an(Array)
      expect(umps.size).to eq(1)
      expect(umps[0]).to be_a(Hash)

      start_time = umps[0][:start_time]
      expect(start_time).not_to be_nil
      expect(start_time).to be_an(Integer)
      expect(start_time).to eq(t)

      duration = umps[0][:duration]
      expect(duration).not_to be_nil
      expect(duration).to be_a(Float)
      expect(duration).to eq(1800.0)

      summary = @redis.get("#{name}:#{check}:#{t}:unscheduled_maintenance:summary")
      expect(summary).not_to be_nil
      expect(summary).to eq('oops')
    end

    it "ends an unscheduled maintenance period", :time => true do
      t = Time.now.to_i
      later_t = t + (15 * 60)
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

      ec.create_unscheduled_maintenance(t, half_an_hour, :summary => 'oops')
      expect(ec).to be_in_unscheduled_maintenance

      Delorean.time_travel_to(Time.at(later_t))
      expect(ec).to be_in_unscheduled_maintenance
      ec.end_unscheduled_maintenance(later_t)
      expect(ec).not_to be_in_unscheduled_maintenance

      umps = ec.maintenances(nil, nil, :scheduled => false)
      expect(umps).not_to be_nil
      expect(umps).to be_an(Array)
      expect(umps.size).to eq(1)
      expect(umps[0]).to be_a(Hash)

      start_time = umps[0][:start_time]
      expect(start_time).not_to be_nil
      expect(start_time).to be_an(Integer)
      expect(start_time).to eq(t)

      duration = umps[0][:duration]
      expect(duration).not_to be_nil
      expect(duration).to be_a(Float)
      expect(duration).to eq(15 * 60)
    end

    it "creates a scheduled maintenance period for a future time" do
      t = Time.now.to_i
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_scheduled_maintenance(t + (60 * 60),
                                      half_an_hour, :summary => "30 minutes")

      smps = ec.maintenances(nil, nil, :scheduled => true)
      expect(smps).not_to be_nil
      expect(smps).to be_an(Array)
      expect(smps.size).to eq(1)
      expect(smps[0]).to be_a(Hash)

      start_time = smps[0][:start_time]
      expect(start_time).not_to be_nil
      expect(start_time).to be_an(Integer)
      expect(start_time).to eq(t + (60 * 60))

      duration = smps[0][:duration]
      expect(duration).not_to be_nil
      expect(duration).to be_a(Float)
      expect(duration).to eq(half_an_hour)
    end

    it "creates a scheduled maintenance period covering the current time" do
      t = Time.now.to_i
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_scheduled_maintenance(t - (60 * 60),
                                      2 * (60 * 60), :summary => "2 hours")

      smps = ec.maintenances(nil, nil, :scheduled => true)
      expect(smps).not_to be_nil
      expect(smps).to be_an(Array)
      expect(smps.size).to eq(1)
      expect(smps[0]).to be_a(Hash)

      start_time = smps[0][:start_time]
      expect(start_time).not_to be_nil
      expect(start_time).to be_an(Integer)
      expect(start_time).to eq(t - (60 * 60))

      duration = smps[0][:duration]
      expect(duration).not_to be_nil
      expect(duration).to be_a(Float)
      expect(duration).to eq(2 * (60 * 60))

      ttl = @redis.ttl("#{name}:#{check}:scheduled_maintenance")
      expect(ttl).to be_within(20).of(60 * 60) # 20-second margin of error,
      # should be large enough for a slow machine running the test -- duration
      # is considered to start at the begininng of the period
    end

    it "creates an scheduled maintenance period from a human readable time" do
      Flapjack::Data::EntityCheck.create_maintenance(:redis => @redis, :entity => name, :check => check, :type => 'scheduled', :started => '14/3/2027 3pm', :duration => '30 minutes', :reason => 'oops')
      t = Time.local(2027, 3, 14, 15, 0).to_i
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

      smps = ec.maintenances(nil, nil, :scheduled => true)
      expect(smps).not_to be_nil
      expect(smps).to be_an(Array)
      expect(smps.size).to eq(1)
      expect(smps[0]).to be_a(Hash)

      start_time = smps[0][:start_time]
      expect(start_time).not_to be_nil
      expect(start_time).to be_an(Integer)
      expect(start_time).to eq(t)

      duration = smps[0][:duration]
      expect(duration).not_to be_nil
      expect(duration).to be_a(Float)
      expect(duration).to eq(1800.0)

      summary = @redis.get("#{name}:#{check}:#{t}:scheduled_maintenance:summary")
      expect(summary).not_to be_nil
      expect(summary).to eq('oops')
    end

    it "removes a scheduled maintenance period for a future time" do
      t = Time.now.to_i
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_scheduled_maintenance(t + (60 * 60),
                                      2 * (60 * 60), :summary => "2 hours")

      ec.end_scheduled_maintenance(t + (60 * 60))

      smps = ec.maintenances(nil, nil, :scheduled => true)
      expect(smps).not_to be_nil
      expect(smps).to be_an(Array)
      expect(smps).to be_empty
    end

    # maint period starts an hour from now, goes for two hours -- at 30 minutes into
    # it we stop it, and its duration should be 30 minutes
    it "shortens a scheduled maintenance period covering a current time", :time => true do
      t = Time.now.to_i
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_scheduled_maintenance(t + (60 * 60),
                                      2 * (60 * 60), :summary => "2 hours")

      Delorean.time_travel_to(Time.at(t + (90 * 60)))

      ec.end_scheduled_maintenance(t + (60 * 60))

      smps = ec.maintenances(nil, nil, :scheduled => true)
      expect(smps).not_to be_nil
      expect(smps).to be_an(Array)
      expect(smps).not_to be_empty
      expect(smps.size).to eq(1)
      expect(smps.first[:duration]).to eq(30 * 60)
    end

    it "does not alter or remove a scheduled maintenance period covering a past time", :time => true do
      t = Time.now.to_i
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_scheduled_maintenance(t + (60 * 60),
                                      2 * (60 * 60), :summary => "2 hours")

      Delorean.time_travel_to(Time.at(t + (6 * (60 * 60))))

      ec.end_scheduled_maintenance(t + (60 * 60))

      smps = ec.maintenances(nil, nil, :scheduled => true)
      expect(smps).not_to be_nil
      expect(smps).to be_an(Array)
      expect(smps).not_to be_empty
      expect(smps.size).to eq(1)
      expect(smps.first[:duration]).to eq(2 * (60 * 60))
    end

    it "returns a list of scheduled maintenance periods" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_scheduled_maintenance(five_hours_ago, half_an_hour,
                                      :summary => "first")
      ec.create_scheduled_maintenance(three_hours_ago, half_an_hour,
                                      :summary => "second")

      smp = ec.maintenances(nil, nil, :scheduled => true)
      expect(smp).not_to be_nil
      expect(smp).to be_an(Array)
      expect(smp.size).to eq(2)
      expect(smp[0]).to eq(:start_time => five_hours_ago,
                           :end_time   => five_hours_ago + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "first")
      expect(smp[1]).to eq(:start_time => three_hours_ago,
                           :end_time   => three_hours_ago + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "second")
    end

    it "returns a list of unscheduled maintenance periods" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_unscheduled_maintenance(five_hours_ago,
                                        half_an_hour, :summary => "first")
      ec.create_unscheduled_maintenance(three_hours_ago,
                                        half_an_hour, :summary => "second")

      ump =  ec.maintenances(nil, nil, :scheduled => false)
      expect(ump).not_to be_nil
      expect(ump).to be_an(Array)
      expect(ump.size).to eq(2)
      expect(ump[0]).to eq(:start_time => five_hours_ago,
                           :end_time   => five_hours_ago + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "first")
      expect(ump[1]).to eq(:start_time => three_hours_ago,
                           :end_time   => three_hours_ago + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "second")
    end

    it "finds current scheduled maintenance periods for multiple entities" do
      ec = nil

      %w(alpha lima bravo).each do |entity|
        Flapjack::Data::Entity.add({ 'name' => entity }, :redis => @redis)

        ec = Flapjack::Data::EntityCheck.for_entity_name(entity, check, :redis => @redis)
        ec.create_scheduled_maintenance(five_hours_ago, seven_hours,
          :summary => "Test scheduled maintenance for #{entity}")
      end

      smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis,
        :type => 'scheduled', :finishing => 'more than 0 minutes from now').sort_by { |k| k[:entity] }

      expect(smp).to be_an(Array)
      expect(smp.size).to eq(3)
      %w(alpha bravo lima).each_with_index do |entity, index|
      expect(smp[index]).to eq(:entity     => entity,
                               :check      => "ping",
                               # The state here is nil due to no check having gone
                               # through for this item.  This is normally 'critical' or 'ok'
                               :state      => nil,
                               :start_time => five_hours_ago,
                               :end_time   => five_hours_ago + seven_hours,
                               :duration   => seven_hours,
                               :summary    => "Test scheduled maintenance for #{entity}")
      end
    end

  it "finds current unscheduled maintenance periods for multiple entities" do
      ec = nil

      %w(alpha bravo lima).each do |entity|
        Flapjack::Data::Entity.add({ 'name' => entity }, :redis => @redis)

        ec = Flapjack::Data::EntityCheck.for_entity_name(entity, check, :redis => @redis)
        ec.create_unscheduled_maintenance(five_hours_ago, seven_hours, :summary => "Test unscheduled maintenance for #{entity}")
      end

      ump = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'unscheduled', :finishing => 'more than 0 minutes from now').sort_by { |k| k[:entity] }

      expect(ump).not_to be_nil
      expect(ump).to be_an(Array)
      expect(ump.size).to eq(3)
      %w(alpha bravo lima).each_with_index do |entity, index|
      expect(ump[index]).to eq(:entity     => entity,
                               :check      => "ping",
                               # The state here is nil due to no check having gone
                               # through for this item.  This is normally 'critical' or 'ok'
                               :state      => nil,
                               :start_time => five_hours_ago,
                               :end_time   => five_hours_ago + seven_hours,
                               :duration   => seven_hours,
                               :summary    => "Test unscheduled maintenance for #{entity}")
      end
    end

    it "finds all scheduled maintenance starting more than 3 hours ago" do
      ['more than three hours ago', 'before 3 hours ago'].each do |input|
        ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

        # Maintenance in the past, now ended
        ec.create_scheduled_maintenance(five_hours_ago, half_an_hour, :summary => "30 minute maintenance")
        # Maintenance started in the past, still running
        ec.create_scheduled_maintenance(three_hours_ago + five_minutes, seven_hours, :summary => "Scheduled maintenance started 3 hours ago")
        ec.create_scheduled_maintenance(four_hours_ago, seven_hours, :summary => "Scheduled maintenance started 4 hours ago")

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis,
          :type => 'scheduled', :started => input).sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(2)

        expect(smp[0]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => five_hours_ago,
                             :end_time   => five_hours_ago + half_an_hour,
                             :duration   => half_an_hour,
                             :summary    => "30 minute maintenance")
        expect(smp[1]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => four_hours_ago,
                             :end_time   => four_hours_ago + seven_hours,
                             :duration   => seven_hours,
                             :summary    => "Scheduled maintenance started 4 hours ago")
      end
    end

    it "finds all scheduled maintenance starting within the next four hours" do
      ['less than four hours ago', 'after 4 hours ago'].each do |input|
        ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

        # Maintenance in the past, now ended
        ec.create_scheduled_maintenance(five_hours_ago, half_an_hour, :summary => "30 minute maintenance")
        # Maintenance started in the past, still running
        ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Scheduled maintenance started 3 hours ago")
        ec.create_scheduled_maintenance(four_hours_ago, seven_hours, :summary => "Scheduled maintenance started 4 hours ago")

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :started => input).sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(1)

        expect(smp[0]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => three_hours_ago,
                             :end_time   => three_hours_ago + seven_hours,
                             :duration   => seven_hours,
                             :summary    => "Scheduled maintenance started 3 hours ago")
      end
    end

    it "finds all scheduled maintenance ending within the next two hours" do
      ['less than two hours', 'before 2 hours'].each do |input|
        ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
        # Maintenance in the past, now ended
        ec.create_scheduled_maintenance(two_hours_ago + five_minutes, half_an_hour, :summary => "Scheduled maintenance started 3 hours ago")
        # Maintenance started in the past, still running
        ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Scheduled maintenance started 3 hours ago for 7 hours")
        ec.create_scheduled_maintenance(five_hours_ago, seven_hours + five_minutes, :summary => "Scheduled maintenance started 5 hours ago")
        # Current maintenance
        ec.create_scheduled_maintenance(t, half_an_hour, :summary => "Scheduled maintenance started now")
        # Future maintenance
        ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :finishing => input).sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(3)

        expect(smp[0]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => two_hours_ago + five_minutes,
                             :end_time   => two_hours_ago + five_minutes + half_an_hour,
                             :duration   => half_an_hour,
                             :summary    => "Scheduled maintenance started 3 hours ago")
        expect(smp[1]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => t,
                             :end_time   => t + half_an_hour,
                             :duration   => half_an_hour,
                             :summary    => "Scheduled maintenance started now")
        expect(smp[2]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => t + five_minutes,
                             :end_time   => t + five_minutes + half_an_hour,
                             :duration   => half_an_hour,
                             :summary    => "Scheduled maintenance starting in 5 minutes")
      end
    end

    it "finds all scheduled maintenance ending between two times (1 hour ago - 2 hours ago)" do
        ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
        # Maintenance in the past, now ended
        ec.create_scheduled_maintenance(two_hours_ago + five_minutes, half_an_hour, :summary => "Scheduled maintenance started 1 hour, 55 minutes ago")
        # Maintenance started in the past, still running
        ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Scheduled maintenance started 3 hours ago for 7 hours")
        ec.create_scheduled_maintenance(five_hours_ago, three_hours + five_minutes, :summary => "Scheduled maintenance started 5 hours ago")
        # Future maintenance
        ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :finishing => 'between one and two hours ago').sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(2)

        expect(smp[0]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => five_hours_ago,
                             :end_time   => five_hours_ago + three_hours + five_minutes,
                             :duration   => three_hours + five_minutes,
                             :summary    => "Scheduled maintenance started 5 hours ago")
        expect(smp[1]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => two_hours_ago + five_minutes,
                             :end_time   => two_hours_ago + five_minutes + half_an_hour,
                             :duration   => half_an_hour,
                             :summary    => "Scheduled maintenance started 1 hour, 55 minutes ago")
    end

    it "finds all scheduled maintenance ending between two times (1 hour from now - 2 hours from now)" do
        ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
        # Maintenance in the past, now ended
        ec.create_scheduled_maintenance(two_hours_ago + five_minutes, half_an_hour, :summary => "Scheduled maintenance started 1 hour, 55 minutes ago")
        # Maintenance started in the past, still running
        ec.create_scheduled_maintenance(three_hours_ago, five_hours - five_minutes, :summary => "Scheduled maintenance started 3 hours ago for 4 hours, 25 minutes")
        ec.create_scheduled_maintenance(five_hours_ago, three_hours + five_minutes, :summary => "Scheduled maintenance started 5 hours ago")
        # Future maintenance
        ec.create_scheduled_maintenance(t + five_minutes, one_hour, :summary => "Scheduled maintenance starting in 5 minutes")

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :finishing => 'between one and two hours').sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(2)

        expect(smp[0]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => three_hours_ago,
                             :end_time   => three_hours_ago + five_hours - five_minutes,
                             :duration   => five_hours - five_minutes,
                             :summary    => "Scheduled maintenance started 3 hours ago for 4 hours, 25 minutes")
        expect(smp[1]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => t + five_minutes,
                             :end_time   => t + five_minutes + one_hour,
                             :duration   => one_hour,
                             :summary    => "Scheduled maintenance starting in 5 minutes")
    end

    it "finds all scheduled maintenance ending in more than two hours" do
      ['more than two hours', 'after 2 hours'].each do |input|
        ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
        # Maintenance in the past, now ended
        ec.create_scheduled_maintenance(two_hours_ago, half_an_hour, :summary => "Scheduled maintenance started 2 hours ago")
        # Maintenance started in the past, still running
        ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Scheduled maintenance started 3 hours ago for 7 hours")
        ec.create_scheduled_maintenance(five_hours_ago, seven_hours, :summary => "Scheduled maintenance started 5 hours ago")
        # Current maintenance
        ec.create_scheduled_maintenance(t, half_an_hour, :summary => "Scheduled maintenance started now")
        # Future maintenance
        ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :finishing => input).sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(1)

        expect(smp[0]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => three_hours_ago,
                             :end_time   => three_hours_ago + seven_hours,
                             :duration   => seven_hours,
                             :summary    => "Scheduled maintenance started 3 hours ago for 7 hours")
      end
    end

    it "finds all scheduled maintenance with a duration of less than one hour" do
      ['less than', 'before'].each do |input|
        ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
        # Maintenance in the past, now ended
        ec.create_scheduled_maintenance(two_hours_ago, half_an_hour, :summary => "Scheduled maintenance started 3 hours ago")
        # Maintenance started in the past, still running
        ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Scheduled maintenance started 3 hours ago for 7 hours")
        ec.create_scheduled_maintenance(five_hours_ago, seven_hours, :summary => "Scheduled maintenance started 5 hours ago")
        # Current maintenance
        ec.create_scheduled_maintenance(t, half_an_hour, :summary => "Scheduled maintenance started now")
        # Future maintenance
        ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :duration => "#{input} one hour").sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(3)

        expect(smp[0]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => two_hours_ago,
                             :end_time   => two_hours_ago + half_an_hour,
                             :duration   => half_an_hour,
                             :summary    => "Scheduled maintenance started 3 hours ago")
        expect(smp[1]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => t,
                             :end_time   => t + half_an_hour,
                             :duration   => half_an_hour,
                             :summary    => "Scheduled maintenance started now")
        expect(smp[2]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => t + five_minutes,
                             :end_time   => t + five_minutes + half_an_hour,
                             :duration   => half_an_hour,
                             :summary    => "Scheduled maintenance starting in 5 minutes")
      end
    end

    it "finds all scheduled maintenance with a duration of 30 minutes" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      # Maintenance in the past, now ended
      ec.create_scheduled_maintenance(two_hours_ago, half_an_hour, :summary => "Scheduled maintenance started 3 hours ago")
      # Maintenance started in the past, still running
      ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Scheduled maintenance started 3 hours ago for 7 hours")
      ec.create_scheduled_maintenance(five_hours_ago, seven_hours, :summary => "Scheduled maintenance started 5 hours ago")
      # Current maintenance
      ec.create_scheduled_maintenance(t, half_an_hour, :summary => "Scheduled maintenance started now")
      # Future maintenance
      ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

      smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :duration => '30 minutes').sort_by { |k| k[:entity] }

      expect(smp).to be_an(Array)
      expect(smp.size).to eq(3)

      expect(smp[0]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => two_hours_ago,
                           :end_time   => two_hours_ago + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Scheduled maintenance started 3 hours ago")
      expect(smp[1]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t,
                           :end_time   => t + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Scheduled maintenance started now")
      expect(smp[2]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t + five_minutes,
                           :end_time   => t + five_minutes + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Scheduled maintenance starting in 5 minutes")
    end

    it "finds all scheduled maintenance with a duration of between 15 and 65 minutes" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      # Maintenance in the past, now ended
      ec.create_scheduled_maintenance(two_hours_ago, half_an_hour, :summary => "Scheduled maintenance started 3 hours ago")
      # Maintenance started in the past, still running
      ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Scheduled maintenance started 3 hours ago for 7 hours")
      ec.create_scheduled_maintenance(five_hours_ago, one_hour, :summary => "Scheduled maintenance started 5 hours ago")
      # Current maintenance
      ec.create_scheduled_maintenance(t, half_an_hour, :summary => "Scheduled maintenance started now")
      # Future maintenance
      ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

      smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :duration => 'between 15 and 65 minutes').sort_by { |k| k[:entity] }

      expect(smp).to be_an(Array)
      expect(smp.size).to eq(4)

      expect(smp[1]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => two_hours_ago,
                           :end_time   => two_hours_ago + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Scheduled maintenance started 3 hours ago")
      expect(smp[0]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => five_hours_ago,
                           :end_time   => five_hours_ago + one_hour,
                           :duration   => one_hour,
                           :summary    => "Scheduled maintenance started 5 hours ago")
      expect(smp[2]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t,
                           :end_time   => t + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Scheduled maintenance started now")
      expect(smp[3]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t + five_minutes,
                           :end_time   => t + five_minutes + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Scheduled maintenance starting in 5 minutes")
    end

    it "finds all scheduled maintenance with a duration of more than one hour" do
      ['more than, after'].each do |input|
        ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
        # Maintenance in the past, now ended
        ec.create_scheduled_maintenance(two_hours_ago, half_an_hour, :summary => "Scheduled maintenance started 3 hours ago")
        # Maintenance started in the past, still running
        ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Scheduled maintenance started 3 hours ago for 7 hours")
        ec.create_scheduled_maintenance(five_hours_ago, seven_hours, :summary => "Scheduled maintenance started 5 hours ago")
        # Current maintenance
        ec.create_scheduled_maintenance(t, half_an_hour, :summary => "Scheduled maintenance started now")
        # Future maintenance
        ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :duration => "#{input} one hour").sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(2)

        expect(smp[0]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => five_hours_ago,
                             :end_time   => five_hours_ago + seven_hours,
                             :duration   => seven_hours,
                             :summary    => "Scheduled maintenance started 5 hours ago")
        expect(smp[1]).to eq(:entity     => name,
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => three_hours_ago,
                             :end_time   => three_hours_ago + seven_hours,
                             :duration   => seven_hours,
                             :summary    => "Scheduled maintenance started 3 hours ago for 7 hours")
      end
    end

    it "finds all scheduled maintenance with a particular entity name" do
      ['bravo', 'br.*'].each do |input|
        %w(alpha bravo lima).each do |entity|
          Flapjack::Data::Entity.add({ 'name' => entity }, :redis => @redis)

          ec = Flapjack::Data::EntityCheck.for_entity_name(entity, check, :redis => @redis)
          ec.create_scheduled_maintenance(five_hours_ago, seven_hours, :summary => "Test scheduled maintenance for #{entity}")
        end

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :entity => input ).sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(1)
        expect(smp[0]).to eq(:entity     => "bravo",
                             :check      => check,
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => five_hours_ago,
                             :end_time   => five_hours_ago + seven_hours,
                             :duration   => seven_hours,
                             :summary    => "Test scheduled maintenance for bravo")
      end
    end

    it "finds all scheduled maintenance with a particular check name" do
      ['http', 'ht.*'].each do |input|
        %w(ping http ssh).each do |check|
          ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
          ec.create_scheduled_maintenance(five_hours_ago, seven_hours, :summary => "Test scheduled maintenance for #{check}")
        end

        smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :check => input ).sort_by { |k| k[:entity] }

        expect(smp).to be_an(Array)
        expect(smp.size).to eq(1)
        expect(smp[0]).to eq(:entity     => name,
                             :check      => "http",
                             # The state here is nil due to no check having gone
                             # through for this item.  This is normally 'critical' or 'ok'
                             :state      => nil,
                             :start_time => five_hours_ago,
                             :end_time   => five_hours_ago + seven_hours,
                             :duration   => seven_hours,
                             :summary    => "Test scheduled maintenance for http")
      end
    end

    it "finds all scheduled maintenance with a particular summary" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

      # Maintenance started in the past, still running
      ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Bring me a shrubbery!")
      # Current maintenance
      ec.create_scheduled_maintenance(t, half_an_hour, :summary => "Bring me a shrubbery!")
      # Future maintenance
      ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

      smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :reason => "Bring me a shrubbery!").sort_by { |k| k[:entity] }

      expect(smp).to be_an(Array)
      expect(smp.size).to eq(2)

      expect(smp[0]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => three_hours_ago,
                           :end_time   => three_hours_ago + seven_hours,
                           :duration   => seven_hours,
                           :summary    => "Bring me a shrubbery!")
      expect(smp[1]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t,
                           :end_time   => t + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Bring me a shrubbery!")
    end

    it "finds all scheduled maintenance with a summary regex" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

      # Maintenance started in the past, still running
      ec.create_scheduled_maintenance(three_hours_ago, seven_hours, :summary => "Bring me a shrubbery!")
      # Current maintenance
      ec.create_scheduled_maintenance(t, half_an_hour, :summary => "Bring me a shrubbery!")
      # Future maintenance
      ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

      smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled', :reason => '.* shrubbery!').sort_by { |k| k[:entity] }

      expect(smp).to be_an(Array)
      expect(smp.size).to eq(2)

      expect(smp[0]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => three_hours_ago,
                           :end_time   => three_hours_ago + seven_hours,
                           :duration   => seven_hours,
                           :summary    => "Bring me a shrubbery!")
      expect(smp[1]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t,
                           :end_time   => t + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Bring me a shrubbery!")
    end

    it "deletes scheduled maintenance from list" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      # Current maintenance
      ec.create_scheduled_maintenance(two_hours_ago, seven_hours, :summary => "Scheduled maintenance started 2 hours ago")
      ec.create_scheduled_maintenance(t + half_an_hour, half_an_hour, :summary => "Scheduled maintenance starting in half an hour")
      # Future maintenance
      ec.create_scheduled_maintenance(t + five_minutes, half_an_hour, :summary => "Scheduled maintenance starting in 5 minutes")

      smp = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled').sort_by { |k| k[:entity] }

      expect(smp).to be_an(Array)
      expect(smp.size).to eq(3)

      expect(smp[0]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => two_hours_ago,
                           :end_time   => two_hours_ago + seven_hours,
                           :duration   => seven_hours,
                           :summary    => "Scheduled maintenance started 2 hours ago")
      expect(smp[1]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t + five_minutes,
                           :end_time   => t + five_minutes + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Scheduled maintenance starting in 5 minutes")
      expect(smp[2]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t + half_an_hour,
                           :end_time   => t + half_an_hour + half_an_hour,
                           :duration   => half_an_hour,
                           :summary    => "Scheduled maintenance starting in half an hour")

      delete = Flapjack::Data::EntityCheck.delete_maintenance(:redis => @redis, :type => 'scheduled', :duration => '30 minutes' )
      expect(delete).to eq({})

      remain = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'scheduled').sort_by { |k| k[:entity] }

      expect(remain).to be_an(Array)
      expect(remain.size).to eq(1)

      expect(remain[0]).to eq(:entity     => name,
                              :check      => check,
                              # The state here is nil due to no check having gone
                              # through for this item.  This is normally 'critical' or 'ok'
                              :state      => nil,
                              :start_time => two_hours_ago,
                              :end_time   => two_hours_ago + seven_hours,
                              :duration   => seven_hours,
                              :summary    => "Scheduled maintenance started 2 hours ago")
    end

    it "deletes unscheduled maintenance from list" do
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
      ec.create_unscheduled_maintenance(t, half_an_hour, :summary => "Unscheduled maintenance starting now")

      ump = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'unscheduled').sort_by { |k| k[:entity] }
      expect(ump).to be_an(Array)
      expect(ump.size).to eq(1)

      expect(ump[0]).to eq(:entity     => name,
                              :check      => check,
                              # The state here is nil due to no check having gone
                              # through for this item.  This is normally 'critical' or 'ok'
                              :state      => nil,
                              :start_time => t,
                              :duration   => half_an_hour,
                              :end_time   => t + half_an_hour,
                              :summary    => "Unscheduled maintenance starting now")

      later_t = t + (15 * 60)
      Delorean.time_travel_to(Time.at(later_t))

      delete = Flapjack::Data::EntityCheck.delete_maintenance(:redis => @redis, :type => 'unscheduled', :duration => '30 minutes')
      expect(delete).to eq({})

      remain = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'unscheduled').sort_by { |k| k[:entity] }
      expect(remain).to be_an(Array)
      expect(remain.size).to eq(1)

      expect(remain[0]).to eq(:entity     => name,
                              :check      => check,
                              # The state here is nil due to no check having gone
                              # through for this item.  This is normally 'critical' or 'ok'
                              :state      => nil,
                              :start_time => t,
                              :duration   => half_an_hour,
                              :end_time   => t + half_an_hour,
                              :summary    => "Unscheduled maintenance starting now")
    end

    it "shows errors when deleting maintenance in the past" do
      Flapjack::Data::EntityCheck.create_maintenance(:redis => @redis, :entity => name, :check => check, :type => 'unscheduled', :started => '14/3/1927 3pm', :duration => '30 minutes', :reason => 'Unscheduled maintenance')
      t = Time.local(1927, 3, 14, 15, 0).to_i
      ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

      ump = Flapjack::Data::EntityCheck.find_maintenance(:redis => @redis, :type => 'unscheduled').sort_by { |k| k[:entity] }
      expect(ump).to be_an(Array)
      expect(ump.size).to eq(1)

      expect(ump[0]).to eq(:entity     => name,
                           :check      => check,
                           # The state here is nil due to no check having gone
                           # through for this item.  This is normally 'critical' or 'ok'
                           :state      => nil,
                           :start_time => t,
                           :duration   => half_an_hour,
                           :end_time   => t + half_an_hour,
                           :summary    => "Unscheduled maintenance")

      delete = Flapjack::Data::EntityCheck.delete_maintenance(:redis => @redis, :type => 'unscheduled', :duration => '30 minutes')
      expect(delete).to eq({"abc-123:ping:#{t}"=>"Maintenance can't be deleted as it finished in the past"})
    end
  end

  it "returns its state" do
    @redis.hset("check:#{name}:#{check}", 'state', 'ok')

    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
    state = ec.state
    expect(state).not_to be_nil
    expect(state).to eq('ok')
  end

  it "updates state" do
    @redis.hset("check:#{name}:#{check}", 'state', 'ok')

    old_timestamp = @redis.hget("check:#{name}:#{check}", 'last_update')

    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
    ec.update_state('critical')

    state = @redis.hget("check:#{name}:#{check}", 'state')
    expect(state).not_to be_nil
    expect(state).to eq('critical')

    new_timestamp = @redis.hget("check:#{name}:#{check}", 'last_update')
    expect(new_timestamp).not_to eq(old_timestamp)
  end

  it "updates enabled checks" do
    ts = Time.now.to_i
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    # was check.last_update=
    @redis.hset("check:#{name}:#{check}", 'last_update', ts)
    @redis.zadd("all_checks", ts, @key)
    @redis.zadd("all_checks:#{name}", ts, check)
    @redis.zadd("current_checks:#{name}", ts, check)
    @redis.zadd('current_entities', ts, name)

    saved_check_ts = @redis.zscore("current_checks:#{name}", check)
    expect(saved_check_ts).not_to be_nil
    expect(saved_check_ts).to eq(ts)
    saved_entity_ts = @redis.zscore("current_entities", name)
    expect(saved_entity_ts).not_to be_nil
    expect(saved_entity_ts).to eq(ts)
  end

  it "exposes that it is enabled" do
    @redis.zadd("current_checks:#{name}", Time.now.to_i, check)
    @redis.zadd("current_entities", Time.now.to_i, name)
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    e = ec.enabled?
    expect(e).to be true
  end

  it "exposes that it is disabled" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    e = ec.enabled?
    expect(e).to be false
  end

  it "disables checks" do
    @redis.zadd("current_checks:#{name}", Time.now.to_i, check)
    @redis.zadd("current_entities", Time.now.to_i, name)
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
    ec.disable!

    saved_check_ts = @redis.zscore("current_checks:#{name}", check)
    saved_entity_ts = @redis.zscore("current_entities", name)
    expect(saved_check_ts).to be_nil
    expect(saved_entity_ts).to be_nil
  end

  it "does not update state with invalid value" do
    @redis.hset("check:#{name}:#{check}", 'state', 'ok')

    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
    ec.update_state('silly')

    state = @redis.hget("check:#{name}:#{check}", 'state')
    expect(state).not_to be_nil
    expect(state).to eq('ok')
  end

  it "does not update state with a repeated state value" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
    ec.update_state('critical', :summary => 'small problem', :details => 'none')
    changed_at = @redis.hget("check:#{name}:#{check}", 'last_change')
    summary = ec.summary
    details = ec.details

    ec.update_state('critical', :summary => 'big problem', :details => 'some')
    new_changed_at = @redis.hget("check:#{name}:#{check}", 'last_change')
    new_summary = ec.summary
    new_details = ec.details

    expect(changed_at).not_to be_nil
    expect(new_changed_at).not_to be_nil
    expect(new_changed_at).to eq(changed_at)

    expect(summary).not_to be_nil
    expect(new_summary).not_to be_nil
    expect(new_summary).not_to eq(summary)
    expect(summary).to eq('small problem')
    expect(new_summary).to eq('big problem')

    expect(details).not_to be_nil
    expect(new_details).not_to be_nil
    expect(new_details).not_to eq(details)
    expect(details).to eq('none')
    expect(new_details).to eq('some')
  end

  def time_before(t, min, sec = 0)
    t - ((60 * min) + sec)
  end

  it "returns a list of historical states for a time range" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    t = Time.now.to_i
    ec.update_state('ok', :timestamp => time_before(t, 5), :summary => 'a')
    ec.update_state('critical', :timestamp => time_before(t, 4), :summary => 'b')
    ec.update_state('ok', :timestamp => time_before(t, 3), :summary => 'c')
    ec.update_state('critical', :timestamp => time_before(t, 2), :summary => 'd')
    ec.update_state('ok', :timestamp => time_before(t, 1), :summary => 'e')

    states = ec.historical_states(time_before(t, 4), t)
    expect(states).not_to be_nil
    expect(states).to be_an(Array)
    expect(states.size).to eq(4)
    expect(states[0][:summary]).to eq('b')
    expect(states[1][:summary]).to eq('c')
    expect(states[2][:summary]).to eq('d')
    expect(states[3][:summary]).to eq('e')
  end

  it "returns a list of historical unscheduled maintenances for a time range" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    t = Time.now.to_i
    ec.update_state('ok', :timestamp => time_before(t, 5), :summary => 'a')
    ec.update_state('critical', :timestamp => time_before(t, 4), :summary => 'b')
    ec.update_state('ok', :timestamp => time_before(t, 3), :summary => 'c')
    ec.update_state('critical', :timestamp => time_before(t, 2), :summary => 'd')
    ec.update_state('ok', :timestamp => time_before(t, 1), :summary => 'e')

    states = ec.historical_states(time_before(t, 4), t)
    expect(states).not_to be_nil
    expect(states).to be_an(Array)
    expect(states.size).to eq(4)
    expect(states[0][:summary]).to eq('b')
    expect(states[1][:summary]).to eq('c')
    expect(states[2][:summary]).to eq('d')
    expect(states[3][:summary]).to eq('e')
  end

  it "returns a list of historical scheduled maintenances for a time range" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    t = Time.now.to_i

    ec.create_scheduled_maintenance(time_before(t, 180),
                                    half_an_hour, :summary => "a")
    ec.create_scheduled_maintenance(time_before(t, 120),
                                    half_an_hour, :summary => "b")
    ec.create_scheduled_maintenance(time_before(t, 60),
                                    half_an_hour, :summary => "c")

    sched_maint_periods = ec.maintenances(time_before(t, 150), t,
                                          :scheduled => true)
    expect(sched_maint_periods).not_to be_nil
    expect(sched_maint_periods).to be_an(Array)
    expect(sched_maint_periods.size).to eq(2)
    expect(sched_maint_periods[0][:summary]).to eq('b')
    expect(sched_maint_periods[1][:summary]).to eq('c')
  end

  it "returns that it has failed" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    @redis.hset("check:#{name}:#{check}", 'state', 'warning')
    expect(ec).to be_failed

    @redis.hset("check:#{name}:#{check}", 'state', 'critical')
    expect(ec).to be_failed

    @redis.hset("check:#{name}:#{check}", 'state', 'unknown')
    expect(ec).to be_failed
  end

  it "returns that it has not failed" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    @redis.hset("check:#{name}:#{check}", 'state', 'ok')
    expect(ec).not_to be_failed

    @redis.hset("check:#{name}:#{check}", 'state', 'acknowledgement')
    expect(ec).not_to be_failed
  end

  it "returns a status summary" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)

    t = Time.now.to_i
    ec.update_state('ok', :timestamp => time_before(t, 5), :summary => 'a')
    ec.update_state('critical', :timestamp => time_before(t, 4), :summary => 'b')
    ec.update_state('ok', :timestamp => time_before(t, 3), :summary => 'c')
    ec.update_state('critical', :timestamp => time_before(t, 2), :summary => 'd')

    summary = ec.summary
    expect(summary).to eq('d')
  end

  it "returns timestamps for its last notifications" do
    t = Time.now.to_i
    @redis.set("#{name}:#{check}:last_problem_notification", t - 30)
    @redis.set("#{name}:#{check}:last_acknowledgement_notification", t - 15)
    @redis.set("#{name}:#{check}:last_recovery_notification", t)

    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
    expect(ec.last_notification_for_state(:problem)[:timestamp]).to eq(t - 30)
    expect(ec.last_notification_for_state(:acknowledgement)[:timestamp]).to eq(t - 15)
    expect(ec.last_notification_for_state(:recovery)[:timestamp]).to eq(t)
  end

  it "finds all related contacts" do
    ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
    contacts = ec.contacts
    expect(contacts).not_to be_nil
    expect(contacts).to be_an(Array)
    expect(contacts.size).to eq(1)
    expect(contacts.first.name).to eq('John Johnson')
  end

  it "generates ephemeral tags for itself" do
    ec = Flapjack::Data::EntityCheck.for_entity_name('foo-app-01.example.com', 'Disk / Utilisation', :create_entity => true, :redis => @redis)
    tags = ec.tags
    expect(tags).not_to be_nil
    expect(tags).to be_a(Set)
    expect(['foo-app-01', 'example.com', 'disk', '/', 'utilisation'].to_set.subset?(tags)).to be true
  end

  it "returns unacknowledged failing checks"

end