require "test_helper"


class MonthlyEnumeratorTest < ActiveSupport::TestCase
  include Hiccup


  context "with a complex schedule" do
    setup do
      @schedule = Schedule.new({
        :kind => :monthly,
        :monthly_pattern => [
          [1, "Sunday"],
          [2, "Saturday"],
          [2, "Sunday"],
          [3, "Sunday"],
          [4, "Saturday"],
          [4, "Sunday"],
          [5, "Sunday"] ],
        start_date: Date.new(2005, 1, 8)
      })
    end

    context "when enumerating backward" do
      should "return the most-recent date prior to the start_date, NOT the earliest date in the month" do
        # Start with a date in the middle of the month
        # More than one date prior to the seed date, yet
        # after the first of the month.
        date = Date.new(2013, 10, 15)
        enumerator = @schedule.enumerator.new(@schedule, date)
        assert_equal Date.new(2013, 10, 13), enumerator.prev
      end
    end
  end


  context "with a schedule that skips" do
    setup do
      @schedule = Schedule.new(
        kind: :monthly,
        skip: 2,
        monthly_pattern: [[1, "Thursday"]],
        start_date: Date.new(2015, 1, 1))
    end

    context "when enumerating from a date in a skipped month" do
      should "skip months from the schedule's start date not from the offset" do
        date = Date.new(2015, 2, 1)
        enumerator = @schedule.enumerator.new(@schedule, date)
        assert_equal Date.new(2015, 3, 5), enumerator.next
      end
    end

    context "when enumerating forward from a date toward the end of a skipped month" do
      should "find the first date from the start of an unskipped month" do
        date = Date.new(2015, 4, 30)
        enumerator = @schedule.enumerator.new(@schedule, date)
        assert_equal Date.new(2015, 5, 7), enumerator.next
      end
    end

    context "when enumerating backward from a date toward the beginning of a skipped month" do
      should "find the first date from the end of an unskipped month" do
        date = Date.new(2015, 6, 1)
        enumerator = @schedule.enumerator.new(@schedule, date)
        assert_equal Date.new(2015, 5, 7), enumerator.prev
      end
    end
  end


  context "last of the month" do
    should "return the last day of the month" do
      date = Date.new(2014, 12, 30)
      @schedule = Schedule.new(
        kind: :monthly,
        monthly_pattern: [-1],
        start_date: date
      )
      enumerator = @schedule.enumerator.new(@schedule, date)
      assert_equal Date.new(2014, 12, 31), enumerator.next
      assert_equal Date.new(2015, 1, 31), enumerator.next
    end

    should "return the last sunday of the month" do
      date = Date.new(2014, 12, 31)
      @schedule = Schedule.new(
        kind: :monthly,
        monthly_pattern: [[-1, "Sunday"]],
        start_date: date
      )
      enumerator = @schedule.enumerator.new(@schedule, date)
      assert_equal Date.new(2015, 1, 25), enumerator.next
    end

    should "return the last monday of the month" do
      date = Date.new(2014, 12, 31)
      @schedule = Schedule.new(
        kind: :monthly,
        monthly_pattern: [[-1, "Monday"]],
        start_date: date
      )
      enumerator = @schedule.enumerator.new(@schedule, Date.new(2015, 2, 28))
      assert_equal Date.new(2015, 3, 30), enumerator.next
    end

    should "return the last tuesday of the month" do
      date = Date.new(2014, 12, 31)
      @schedule = Schedule.new(
        kind: :monthly,
        monthly_pattern: [[-1, "Tuesday"]],
        start_date: date
      )
      enumerator = @schedule.enumerator.new(@schedule, Date.new(2015, 1, 31))
      assert_equal Date.new(2015, 2, 24), enumerator.next
    end

    should "return the second to last tuesday of the month" do
      date = Date.new(2014, 12, 31)
      @schedule = Schedule.new(
        kind: :monthly,
        monthly_pattern: [[-2, "Tuesday"]],
        start_date: date
      )
      enumerator = @schedule.enumerator.new(@schedule, Date.new(2015, 2, 28))
      assert_equal Date.new(2015, 2, 17), enumerator.prev
    end

    should "find the correct occurence on or after a date in the same month" do
      date = Date.new(2016, 1, 1)
      @schedule = Schedule.new(
        kind: :monthly,
        monthly_pattern: [[-1, "Sunday"]],
        start_date: date
      )
      enumerator = @schedule.enumerator.new(@schedule, date)
      assert_equal Date.new(2016, 1, 31), enumerator.next
    end

    should "find the correct occurence on or before a date in a subsequent month" do
      @schedule = Schedule.new(
        kind: :monthly,
        monthly_pattern: [[-1, "Friday"]],
        start_date: Date.new(2016, 1, 1)
      )
      enumerator = @schedule.enumerator.new(@schedule, Date.new(2016, 2, 1))
      assert_equal Date.new(2016, 1, 29), enumerator.prev
    end
  end

  context "with an empty schedule" do
    setup do
      @schedule = Schedule.new(
        kind: :monthly,
        skip: 2,
        monthly_pattern: [],
        start_date: Date.new(2015, 1, 1))
    end

    should "always return nil" do
      date = Date.new(2015, 2, 1)
      enumerator = @schedule.enumerator.new(@schedule, date)
      assert_nil enumerator.next
    end
  end


end