require 'spec_helper'
require 'flapjack/gateways/api/entity_check_presenter'

describe 'Flapjack::Gateways::API::EntityCheckPresenter' do

  let(:entity_check) { double(Flapjack::Data::EntityCheck) }

  let(:time) { Time.now.to_i }

  let(:states) {
    [{:state => 'critical', :timestamp => time - (4 * 60 * 60)},
     {:state => 'ok',       :timestamp => time - (4 * 60 * 60) + (5 * 60)},
     {:state => 'critical', :timestamp => time - (3 * 60 * 60)},
     {:state => 'ok',       :timestamp => time - (3 * 60 * 60) + (10 * 60)},
     {:state => 'critical', :timestamp => time - (2 * 60 * 60)},
     {:state => 'ok',       :timestamp => time - (2 * 60 * 60) + (15 * 60)},
     {:state => 'critical', :timestamp => time - (1 * 60 * 60)},
     {:state => 'ok',       :timestamp => time - (1 * 60 * 60) + (20 * 60)}
    ]
  }

  # one overlap at start, one overlap at end, one wholly overlapping,
  # one wholly contained
  let(:maintenances) {
    [{:start_time => time - ((4 * 60 * 60) + (1 * 60)), # 1 minute before outage starts
      :end_time   => time - (4 * 60 * 60) + (2 * 60),   # 2 minutes after outage starts
      :duration => (3 * 60)},
     {:start_time => time - (3 * 60 * 60) + (8 * 60),   # 2 minutes before outage ends
      :end_time   => time - (3 * 60 * 60) + (11 * 60),  # 1 minute after outage ends
      :duration => (3 * 60)},
     {:start_time => time - ((2 * 60 * 60) + (1 * 60)), # 1 minute before outage starts
      :end_time   => time - (2 * 60 * 60) + (17 * 60),  # 2 minutes after outage ends
      :duration => (3 * 60)},
     {:start_time => time - (1 * 60 * 60) + (1 * 60),   # 1 minute after outage starts
      :end_time   => time - (1 * 60 * 60) + (10 * 60),  # 10 minutes before outage ends
      :duration => (9 * 60)}
    ]
  }

  it "returns a list of outage hashes for an entity check" do
    entity_check.should_receive(:historical_states).
      with(time - (5 * 60 * 60), time - (2 * 60 * 60)).and_return(states)

    entity_check.should_receive(:historical_state_before).
      with(time - (4 * 60 * 60)).and_return(nil)

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    outages = ecp.outages(time - (5 * 60 * 60), time - (2 * 60 * 60))
    outages.should_not be_nil
    outages.should be_an(Array)
    outages.should have(4).time_ranges

    # TODO check the data in those hashes
  end

  it "returns a list of outage hashes with no start and end time set" do
    entity_check.should_receive(:historical_states).
      with(nil, nil).and_return(states)

    entity_check.should_receive(:historical_state_before).
      with(time - (4 * 60 * 60)).and_return(nil)

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    outages = ecp.outages(nil, nil)
    outages.should_not be_nil
    outages.should be_an(Array)
    outages.should have(4).time_ranges

    # TODO check the data in those hashes
  end

  it "returns a consolidated list of outage hashes with repeated state events" do
    states[1][:state] = 'critical'
    states[2][:state] = 'ok'

    entity_check.should_receive(:historical_states).
      with(nil, nil).and_return(states)

    entity_check.should_receive(:historical_state_before).
      with(time - (4 * 60 * 60)).and_return(nil)

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    outages = ecp.outages(nil, nil)
    outages.should_not be_nil
    outages.should be_an(Array)
    outages.should have(3).time_ranges
  end

  it "returns a (small) outage hash for a single state change" do
    entity_check.should_receive(:historical_states).
      with(nil, nil).and_return([{:state => 'critical', :timestamp => time - (4 * 60 * 60)}])
    entity_check.should_receive(:historical_state_before).
      with(time - (4 * 60 * 60)).and_return(nil)

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    outages = ecp.outages(nil, nil)
    outages.should_not be_nil
    outages.should be_an(Array)
    outages.should have(1).time_range
  end

  it "a list of unscheduled maintenances for an entity check" do
    entity_check.should_receive(:maintenances).
      with(time - (12 * 60 * 60), time, :scheduled => false).and_return(maintenances)

    entity_check.should_receive(:maintenances).
      with(nil, time - (12 * 60 * 60), :scheduled => false).and_return([])

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    unsched_maint = ecp.unscheduled_maintenances(time - (12 * 60 * 60), time)

    unsched_maint.should be_an(Array)
    unsched_maint.should have(4).time_ranges

    # TODO check the data in those hashes
  end

  it "a list of scheduled maintenances for an entity check" do
    entity_check.should_receive(:maintenances).
      with(time - (12 * 60 * 60), time, :scheduled => true).and_return(maintenances)

    entity_check.should_receive(:maintenances).
      with(nil, time - (12 * 60 * 60), :scheduled => true).and_return([])

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    sched_maint = ecp.scheduled_maintenances(time - (12 * 60 * 60), time)

    sched_maint.should be_an(Array)
    sched_maint.should have(4).time_ranges

    # TODO check the data in those hashes
  end

  it "returns downtime and percentage for a downtime check" do
    entity_check.should_receive(:historical_states).
      with(time - (12 * 60 * 60), time).and_return(states)

    entity_check.should_receive(:historical_state_before).
      with(time - (4 * 60 * 60)).and_return(nil)

    entity_check.should_receive(:maintenances).
      with(time - (12 * 60 * 60), time, :scheduled => true).and_return(maintenances)

    entity_check.should_receive(:maintenances).
      with(nil, time - (12 * 60 * 60), :scheduled => true).and_return([])

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    downtimes = ecp.downtime(time - (12 * 60 * 60), time)

    # 22 minutes, 3 + 8 + 11
    downtimes.should be_a(Hash)
    downtimes[:total_seconds].should == {'critical' => (22 * 60),
      'ok' => ((12 * 60 * 60) - (22 * 60))}
    downtimes[:percentages].should == {'critical' => (((22 * 60) * 100.0) / (12 * 60 * 60)),
      'ok' => ((((12 * 60 * 60) - (22 * 60)) * 100.0) / (12 * 60 *60))}
    downtimes[:downtime].should be_an(Array)
    # the last outage gets split by the intervening maintenance period,
    # but the fully covered one gets removed.
    downtimes[:downtime].should have(4).time_ranges
  end

  it "returns downtime (but no percentage) for an unbounded downtime check" do
    entity_check.should_receive(:historical_states).
      with(nil, nil).and_return(states)

    entity_check.should_receive(:historical_state_before).
      with(time - (4 * 60 * 60)).and_return(nil)

    entity_check.should_receive(:maintenances).
      with(nil, nil, :scheduled => true).and_return(maintenances)

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    downtimes = ecp.downtime(nil, nil)

    # 22 minutes, 3 + 8 + 11
    downtimes.should be_a(Hash)
    downtimes[:total_seconds].should == {'critical' => (22 * 60)}
    downtimes[:percentages].should == {'critical' => nil}
    downtimes[:downtime].should be_an(Array)
    # the last outage gets split by the intervening maintenance period,
    # but the fully covered one gets removed.
    downtimes[:downtime].should have(4).time_ranges
  end

  it "returns downtime and handles an unfinished problem state" do
    current = [{:state => 'critical', :timestamp => time - (4 * 60 * 60)},
               {:state => 'ok',       :timestamp => time - (4 * 60 * 60) + (5 * 60)},
               {:state => 'critical', :timestamp => time - (3 * 60 * 60)}]

    entity_check.should_receive(:historical_states).
      with(nil, nil).and_return(current)

    entity_check.should_receive(:historical_state_before).
      with(time - (4 * 60 * 60)).and_return(nil)

    entity_check.should_receive(:maintenances).
      with(nil, nil, :scheduled => true).and_return([])

    ecp = Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check)
    downtimes = ecp.downtime(nil, nil)

    downtimes.should be_a(Hash)
    downtimes[:total_seconds].should == {'critical' => (5 * 60)}
    downtimes[:percentages].should == {'critical' => nil}
    downtimes[:downtime].should be_an(Array)
    # the last outage gets split by the intervening maintenance period,
    # but the fully covered one gets removed.
    downtimes[:downtime].should have(2).time_ranges
  end

end