# encoding: utf-8
require 'spec_helper'
require 'scheduler/scheduler'

describe Scheduler::Manager do

  module Testing
    class RandomJob
      extend ::Scheduler::Schedule

      def self.runs=(val)
        @runs = val
      end

      def self.runs
        @runs ||= 0
      end

      every 5.minutes

      def perform
        self.class.runs+=1
        sleep 0.001
      end
    end

    class SuperLongJob
      extend ::Scheduler::Schedule

      every 10.minutes

      def perform
        sleep 1000
      end
    end
  end

  let(:manager) { Scheduler::Manager.new(DiscourseRedis.new) }

  before do
    $redis.del manager.class.lock_key
    $redis.del manager.class.queue_key
    manager.remove(Testing::RandomJob)
    manager.remove(Testing::SuperLongJob)
  end

  after do
    manager.stop!
    manager.remove(Testing::RandomJob)
    manager.remove(Testing::SuperLongJob)
  end

  describe '#sync' do

    it 'increases' do
      Scheduler::Manager.seq.should == Scheduler::Manager.seq - 1
    end
  end

  describe '#tick' do

    it 'should nuke missing jobs' do
      $redis.zadd Scheduler::Manager.queue_key, Time.now.to_i - 1000, "BLABLA"
      manager.tick
      $redis.zcard(Scheduler::Manager.queue_key).should == 0

    end

    it 'should recover from crashed manager' do

      info = manager.schedule_info(Testing::SuperLongJob)
      info.next_run = Time.now.to_i - 1
      info.write!

      manager.tick
      manager.stop!

      $redis.del manager.identity_key

      manager = Scheduler::Manager.new(DiscourseRedis.new)
      manager.reschedule_orphans!

      info = manager.schedule_info(Testing::SuperLongJob)
      info.next_run.should <= Time.now.to_i
    end

    it 'should only run pending job once' do

      Testing::RandomJob.runs = 0

      info = manager.schedule_info(Testing::RandomJob)
      info.next_run = Time.now.to_i - 1
      info.write!

      (0..5).map do
        Thread.new do
          manager = Scheduler::Manager.new(DiscourseRedis.new)
          manager.blocking_tick
          manager.stop!
        end
      end.map(&:join)

      Testing::RandomJob.runs.should == 1

      info = manager.schedule_info(Testing::RandomJob)
      info.prev_run.should be <= Time.now.to_i
      info.prev_duration.should be > 0
      info.prev_result.should == "OK"
    end

  end

  describe '#discover_schedules' do
    it 'Discovers Testing::RandomJob' do
      Scheduler::Manager.discover_schedules.should include(Testing::RandomJob)
    end
  end

  describe '#next_run' do
    it 'should be within the next 5 mins if it never ran' do

      manager.remove(Testing::RandomJob)
      manager.ensure_schedule!(Testing::RandomJob)

      manager.next_run(Testing::RandomJob)
        .should be_within(5.minutes.to_i).of(Time.now.to_i + 5.minutes)
    end
  end
end