@Test(expected = MotechSchedulerException.class)
  public void shouldNotResumeJobIfItIsNotUiDefined() throws Exception {
    try {
      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");

      JobBasicInfo info =
          new JobBasicInfo(
              JobBasicInfo.ACTIVITY_ACTIVE,
              JobBasicInfo.STATUS_PAUSED,
              "test_event-job_id",
              "default",
              "start-time",
              "nex-fire-time",
              "end-time",
              JobBasicInfo.JOBTYPE_CRON,
              "test-info",
              false);

      schedulerService.scheduleJob(
          new CronSchedulableJob(new MotechEvent("test_event", params), "0 0 12 * * ?"));
      scheduler.pauseJob(new JobKey(info.getName(), info.getGroup()));

      assertEquals(PAUSED, scheduler.getTriggerState(triggerKey("test_event-job_id", "default")));

      schedulerService.resumeJob(info);
    } finally {
      assertEquals(PAUSED, scheduler.getTriggerState(triggerKey("test_event-job_id", "default")));
    }
  }
  @Test
  public void shouldPauseJobIfItIsUiDefined() throws Exception {
    Map<String, Object> params = new HashMap<>();
    params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");

    JobBasicInfo info =
        new JobBasicInfo(
            JobBasicInfo.ACTIVITY_ACTIVE,
            JobBasicInfo.STATUS_OK,
            "test_event-job_id",
            "default",
            "start-time",
            "nex-fire-time",
            "end-time",
            JobBasicInfo.JOBTYPE_CRON,
            "test-info",
            false);

    CronSchedulableJob job =
        new CronSchedulableJob(new MotechEvent("test_event", params), "0 0 12 * * ?");
    job.setUiDefined(true);

    schedulerService.scheduleJob(job);

    assertEquals(NORMAL, scheduler.getTriggerState(triggerKey("test_event-job_id", "default")));

    schedulerService.pauseJob(info);

    assertEquals(PAUSED, scheduler.getTriggerState(triggerKey("test_event-job_id", "default")));
  }
  @Test(expected = MotechSchedulerException.class)
  public void shouldNotDeleteJobIfItIsNotUiDefined() throws Exception {
    try {
      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");

      JobBasicInfo info =
          new JobBasicInfo(
              JobBasicInfo.ACTIVITY_ACTIVE,
              JobBasicInfo.STATUS_OK,
              "test_event-job_id",
              "default",
              "start-time",
              "nex-fire-time",
              "end-time",
              JobBasicInfo.JOBTYPE_CRON,
              "test-info",
              true);

      schedulerService.scheduleJob(
          new CronSchedulableJob(new MotechEvent("test_event", params), "0 0 12 * * ?"));

      schedulerService.deleteJob(info);
    } finally {
      assertNotNull(scheduler.getTrigger(triggerKey("test_event-job_id", "default")));
    }
  }
  @Test
  public void shouldRescheduleCronJob() throws SchedulerException {
    try {
      fakeNow(new DateTime(2020, 7, 15, 10, 0, 0));

      Map<String, Object> params = new HashMap<>();
      final String jobId = id("jobId");
      params.put(MotechSchedulerService.JOB_ID_KEY, jobId);
      schedulerService.scheduleJob(
          new CronSchedulableJob(new MotechEvent("test_event", params), "0 0 10 * * ?"));

      schedulerService.scheduleJob(
          new CronSchedulableJob(new MotechEvent("test_event", params), "0 0 14 * * ?"));

      List<DateTime> first3FireTimes = getFireTimes("test_event-" + jobId).subList(0, 3);
      assertEquals(
          asList(
              newDateTime(2020, 7, 15, 14, 0, 0),
              newDateTime(2020, 7, 16, 14, 0, 0),
              newDateTime(2020, 7, 17, 14, 0, 0)),
          first3FireTimes);
    } finally {
      stopFakingTime();
    }
  }
  @Test
  // org.quartz.jobStore.misfireThreshold=1000 (in quartz.properties) makes the test reliable.
  // See http://quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigJobStoreTX
  public void shouldNotIgnoreFiresInPastWhenSchedulingCronJob()
      throws InterruptedException, SchedulerException {
    final String eventSubject = id("eve");
    try {
      TestEventListener listener = new TestEventListener();
      eventRegistry.registerListener(listener, eventSubject);

      DateTime now = findSuitableTimeToScheduleWithSafeBufferFromTriggerTime();
      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
      DateTime jobStartTimeInPast = now.minusMinutes(3);
      schedulerService.scheduleJob(
          new CronSchedulableJob(
              new MotechEvent(eventSubject, params),
              "0 0/1 * 1/1 * ? *",
              jobStartTimeInPast,
              null,
              false));

      synchronized (listener.getReceivedEvents()) {
        listener.getReceivedEvents().wait(5000);
      }
      assertTrue(
          "Listener didn't receive misfired events.", listener.getReceivedEvents().size() > 0);
    } finally {
      eventRegistry.clearListenersForBean(eventSubject);
      schedulerService.unscheduleAllJobs(eventSubject + "-job_id");
    }
  }
  @Test
  public void shouldIgnoreFiresInPastWhenSchedulingCronJob()
      throws InterruptedException, SchedulerException {
    String subject = "cron_ignore_misfire";
    try {
      TestEventListener listener = new TestEventListener();
      eventRegistry.registerListener(listener, subject);

      DateTime now;
      for (now = now();
          now.getSecondOfMinute() > 55 || now.getSecondOfMinute() < 5;
          now = now()) { // we don't want triggers now, only misfires
        Thread.sleep(1000);
      }
      DateTime jobStartTime = now.minusMinutes(3);
      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
      schedulerService.scheduleJob(
          new CronSchedulableJob(
              new MotechEvent(subject, params), "0 0/1 * 1/1 * ? *", jobStartTime, null, true));

      synchronized (listener.getReceivedEvents()) {
        listener.getReceivedEvents().wait(2000);
      }
      assertTrue(listener.getReceivedEvents().size() == 0);
    } finally {
      eventRegistry.clearListenersForBean("test");
      schedulerService.unscheduleAllJobs(subject);
    }
  }
 @Test(expected = MotechSchedulerException.class)
 public void shouldThrowExceptionForInvalidCronExpressionWhenreschedulingJob() throws Exception {
   Map<String, Object> params = new HashMap<>();
   params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
   schedulerService.scheduleJob(
       new CronSchedulableJob(new MotechEvent("test_event", params), "0 0 10 * * ?"));
   schedulerService.rescheduleJob("test_event", "job_id", "invalidCronExpression");
 }
  @Test
  public void shouldUnscheduleJob() throws SchedulerException {
    Map<String, Object> params = new HashMap<>();
    final String jobId = id("jobId");
    params.put(MotechSchedulerService.JOB_ID_KEY, jobId);
    schedulerService.scheduleJob(
        new CronSchedulableJob(new MotechEvent("test_event", params), "0 0 12 * * ?"));

    schedulerService.unscheduleJob("test_event", jobId);

    assertNull(scheduler.getTrigger(triggerKey("test_event-" + jobId, "default")));
  }
  @Test
  public void shouldScheduleRepeatJobBoundByEndDate() throws SchedulerException {
    try {
      fakeNow(newDateTime(2020, 7, 15, 10, 0, 0));
      final String jobId = id("jobId");

      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, jobId);
      schedulerService.scheduleRepeatingJob(
          new RepeatingSchedulableJob(
              new MotechEvent("test_event", params),
              DateTimeConstants.SECONDS_PER_DAY,
              newDateTime(2020, 7, 15, 12, 0, 0),
              newDateTime(2020, 7, 18, 12, 0, 0),
              false));

      List<DateTime> fireTimes = getFireTimes("test_event-" + jobId + "-repeat");
      assertEquals(
          asList(
              newDateTime(2020, 7, 15, 12, 0, 0),
              newDateTime(2020, 7, 16, 12, 0, 0),
              newDateTime(2020, 7, 17, 12, 0, 0)),
          fireTimes);
    } finally {
      stopFakingTime();
    }
  }
  @Test
  public void shouldScheduleInterveningRepeatJobWithoutEndDate() throws SchedulerException {
    try {
      fakeNow(newDateTime(2020, 7, 15, 10, 0, 0));

      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
      RepeatingSchedulableJob repeatJob =
          new RepeatingSchedulableJob(
              new MotechEvent("test_event", params),
              3,
              DateTimeConstants.SECONDS_PER_DAY,
              newDateTime(2020, 7, 13, 12, 0, 0),
              null,
              true);
      repeatJob.setUseOriginalFireTimeAfterMisfire(false);
      schedulerService.scheduleRepeatingJob(repeatJob);

      List<DateTime> fireTimes = getFireTimes("test_event-job_id-repeat");
      assertEquals(
          asList(newDateTime(2020, 7, 15, 12, 0, 0), newDateTime(2020, 7, 16, 12, 0, 0)),
          fireTimes);
    } finally {
      stopFakingTime();
    }
  }
  @Test
  public void shouldScheduleDayOfWeekJob() throws SchedulerException {
    try {
      fakeNow(newDateTime(2020, 7, 15, 10, 0, 0));

      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
      schedulerService.scheduleDayOfWeekJob(
          new DayOfWeekSchedulableJob(
              new MotechEvent("test_event", params),
              newDateTime(2020, 7, 10), // friday
              newDateTime(2020, 7, 22),
              asList(DayOfWeek.Monday, DayOfWeek.Thursday),
              new Time(10, 10),
              false));

      List<DateTime> fireTimes = getFireTimes("test_event-job_id");
      assertEquals(
          asList(
              newDateTime(2020, 7, 13, 10, 10, 0),
              newDateTime(2020, 7, 16, 10, 10, 0),
              newDateTime(2020, 7, 20, 10, 10, 0)),
          fireTimes);
    } finally {
      stopFakingTime();
    }
  }
  @Test
  public void shouldScheduleRepeatingPeriodSchedulableJob() throws SchedulerException {
    try {
      fakeNow(new DateTime(2020, 7, 15, 10, 0, 0));

      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
      schedulerService.scheduleRepeatingPeriodJob(
          new RepeatingPeriodSchedulableJob(
              new MotechEvent("test_event_3", params),
              newDateTime(2020, 7, 15, 12, 0, 0),
              newDateTime(2020, 7, 16, 12, 0, 0),
              new Period(4, 0, 0, 0),
              true));

      List<DateTime> fireTimes = getFireTimes("test_event_3-job_id-period");
      assertEquals(
          asList(
              new DateTime(2020, 7, 15, 12, 0, 0),
              new DateTime(2020, 7, 15, 16, 0, 0),
              new DateTime(2020, 7, 15, 20, 0, 0),
              new DateTime(2020, 7, 16, 0, 0, 0),
              new DateTime(2020, 7, 16, 4, 0, 0),
              new DateTime(2020, 7, 16, 8, 0, 0),
              new DateTime(2020, 7, 16, 12, 0, 0)),
          fireTimes);
    } finally {
      stopFakingTime();
    }
  }
  @Override
  public void scheduleRunOnceJob(
      String subject, Map<Object, Object> parameters, DateTime startDate) {
    MotechEvent motechEvent = new MotechEvent(subject, createMotechEventParameters(parameters));
    RunOnceSchedulableJob job = new RunOnceSchedulableJob(motechEvent, startDate.toDate());

    scheduler.scheduleRunOnceJob(job);
  }
  @Test
  public void shouldUnscheduleAllJobsWithAGivenJobIdPrefix() throws SchedulerException {
    Map<String, Object> params = new HashMap<>();
    params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");

    schedulerService.scheduleJob(
        new CronSchedulableJob(new MotechEvent("test_event1", params), "0 0 12 * * ?"));
    schedulerService.scheduleJob(
        new CronSchedulableJob(new MotechEvent("test_event2", params), "0 0 13 * * ?"));
    schedulerService.scheduleJob(
        new CronSchedulableJob(new MotechEvent("test_event3", params), "0 0 14 * * ?"));

    schedulerService.unscheduleAllJobs("test_event");

    assertNull(scheduler.getTrigger(triggerKey("test_event1-job_id", "default")));
    assertNull(scheduler.getTrigger(triggerKey("test_event2-job_id", "default")));
    assertNull(scheduler.getTrigger(triggerKey("test_event3-job_id", "default")));
  }
  @Test
  public void shouldGetNextFireTime() {
    try {
      DateTime fireDate = new DateTime(2020, 7, 15, 10, 0, 0);
      fakeNow(fireDate);

      Map<String, Object> params = new HashMap<>();
      MotechEvent event = new MotechEvent("test_event", params);
      final String jobId = id("jobId");
      params.put(MotechSchedulerService.JOB_ID_KEY, jobId);
      schedulerService.scheduleJob(new CronSchedulableJob(event, "0 0 10 * * ?"));

      DateTime dateTime = schedulerService.getNextFireDate(new CronJobId(event));
      assertEquals(fireDate, dateTime);
    } finally {
      stopFakingTime();
    }
  }
 @Test(expected = IllegalArgumentException.class)
 public void shouldThrowExceptionForNullStartTime() throws SchedulerException {
   Map<String, Object> params = new HashMap<>();
   params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
   schedulerService.scheduleRepeatingJob(
       new RepeatingSchedulableJob(
           new MotechEvent("test_event", params),
           DateTimeConstants.SECONDS_PER_DAY,
           null,
           newDateTime(2020, 7, 18, 12, 0, 0),
           false));
 }
  @Test
  public void shouldGetPreviousFireTime() throws InterruptedException {
    try {
      fakeNow(new DateTime());

      Map<String, Object> params = new HashMap<>();
      MotechEvent event = new MotechEvent("test_event", params);
      final String jobId = id("jobId");
      params.put(MotechSchedulerService.JOB_ID_KEY, jobId);
      DateTime now = new DateTime();
      StringBuilder cron = new StringBuilder();
      cron.append(now.getSecondOfMinute()).append(" ").append(now.getMinuteOfHour()).append(" ");
      cron.append(now.getHourOfDay()).append(" * * ?");
      schedulerService.scheduleJob(new CronSchedulableJob(event, cron.toString()));
      Thread.sleep(1000);
      DateTime dateTime = schedulerService.getPreviousFireDate(new CronJobId(event));
      assertEquals(dateTime.getHourOfDay(), now.getHourOfDay());
      assertEquals(dateTime.getMinuteOfHour(), now.getMinuteOfHour());
      assertEquals(dateTime.getSecondOfMinute(), now.getSecondOfMinute());
    } finally {
      stopFakingTime();
    }
  }
  @Test(expected = MotechSchedulerException.class)
  public void shouldNotScheduleRunOnceJobInThePast() throws SchedulerException {
    try {
      fakeNow(newDateTime(2020, 7, 15, 10, 0, 0));

      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
      schedulerService.scheduleRunOnceJob(
          new RunOnceSchedulableJob(
              new MotechEvent("test_event", params), newDateTime(2020, 6, 15, 12, 0, 0)));
    } finally {
      stopFakingTime();
    }
  }
  @Test
  public void shouldScheduleRunOnceJob() throws SchedulerException {
    try {
      fakeNow(newDateTime(2020, 7, 15, 10, 0, 0));

      Map<String, Object> params = new HashMap<>();
      params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
      schedulerService.scheduleRunOnceJob(
          new RunOnceSchedulableJob(
              new MotechEvent("test_event", params), newDateTime(2020, 7, 15, 12, 0, 0)));

      List<DateTime> fireTimes = getFireTimes("test_event-job_id-runonce");
      assertEquals(asList(newDateTime(2020, 7, 15, 12, 0, 0)), fireTimes);
    } finally {
      stopFakingTime();
    }
  }
  @Test(expected = MotechSchedulerException.class)
  public void shouldNotDeleteJobIfJobDoesNotExist() throws Exception {

    JobBasicInfo info =
        new JobBasicInfo(
            JobBasicInfo.ACTIVITY_ACTIVE,
            JobBasicInfo.STATUS_OK,
            "test_event-job_id",
            "default",
            "start-time",
            "nex-fire-time",
            "end-time",
            JobBasicInfo.JOBTYPE_CRON,
            "test-info",
            true);

    schedulerService.deleteJob(info);
  }
  @Override
  public void scheduleCronJob(
      String subject,
      Map<Object, Object> parameters,
      String cronExpression,
      DateTime startTime,
      DateTime endTime,
      Boolean ignorePastFiresAtStart) {
    MotechEvent motechEvent = new MotechEvent(subject, createMotechEventParameters(parameters));
    CronSchedulableJob job =
        new CronSchedulableJob(
            motechEvent,
            cronExpression,
            startTime.toDate(),
            endTime.toDate(),
            ignorePastFiresAtStart);

    scheduler.scheduleJob(job);
  }
  @Override
  public void scheduleRepeatingPeriodJob(
      String subject,
      Map<Object, Object> parameters,
      DateTime startTime,
      DateTime endTime,
      Period repeatPeriod,
      Boolean ignorePastFiresAtStart,
      Boolean useOriginalFireTimeAfterMisfire) {
    MotechEvent motechEvent = new MotechEvent(subject, createMotechEventParameters(parameters));
    RepeatingPeriodSchedulableJob job =
        new RepeatingPeriodSchedulableJob(
            motechEvent,
            startTime.toDate(),
            endTime.toDate(),
            repeatPeriod,
            ignorePastFiresAtStart);
    job.setUseOriginalFireTimeAfterMisfire(useOriginalFireTimeAfterMisfire);

    scheduler.scheduleRepeatingPeriodJob(job);
  }
  @Override
  public void scheduleRepeatingJob(
      String subject, // NO CHECKSTYLE More than 7 parameters (found 8).
      Map<Object, Object> parameters,
      DateTime startTime,
      DateTime endTime,
      Integer repeatCount,
      Integer repeatIntervalInSeconds,
      Boolean ignorePastFiresAtStart,
      Boolean useOriginalFireTimeAfterMisfire) {
    MotechEvent motechEvent = new MotechEvent(subject, createMotechEventParameters(parameters));
    RepeatingSchedulableJob job =
        new RepeatingSchedulableJob()
            .setMotechEvent(motechEvent)
            .setStartTime(startTime.toDate())
            .setEndTime(endTime.toDate())
            .setRepeatCount(repeatCount)
            .setRepeatIntervalInSeconds(repeatIntervalInSeconds)
            .setIgnorePastFiresAtStart(ignorePastFiresAtStart)
            .setUseOriginalFireTimeAfterMisfire(useOriginalFireTimeAfterMisfire);

    scheduler.scheduleRepeatingJob(job);
  }
  @Override
  public void scheduleDayOfWeekJob(
      String subject,
      Map<Object, Object> parameters,
      DateTime start,
      DateTime end,
      List<Object> days,
      DateTime time,
      Boolean ignorePastFiresAtStart) {
    MotechEvent motechEvent = new MotechEvent(subject, createMotechEventParameters(parameters));
    Time jobTime = new Time(time.getHourOfDay(), time.getMinuteOfHour());
    List<DayOfWeek> jobDayOfWeeks = createDayOfWeeks(days);

    DayOfWeekSchedulableJob job =
        new DayOfWeekSchedulableJob(
            motechEvent,
            start.toLocalDate(),
            end.toLocalDate(),
            jobDayOfWeeks,
            jobTime,
            ignorePastFiresAtStart);

    scheduler.scheduleDayOfWeekJob(job);
  }
 @Test(expected = IllegalArgumentException.class)
 public void shouldThrowExceptionForNullRepeatJob() throws Exception {
   schedulerService.scheduleRepeatingJob(null);
 }
 @Override
 public void unscheduleJobs(String subject) {
   scheduler.safeUnscheduleAllJobs(subject);
 }
 @After
 public void teardown() {
   schedulerService.unscheduleAllJobs("org.motechproject.scheduletracking");
   allEnrollments.removeAll();
   allSchedules.removeAll();
 }
 @After
 public void teardown() {
   schedulerService.unscheduleAllJobs("org.motechproject.scheduletracking");
   enrollmentDataService.deleteAll();
   scheduleDataService.deleteAll();
 }
 @After
 public void tearDown() throws SchedulerException {
   schedulerService.unscheduleAllJobs("test_event");
   schedulerService.unscheduleAllJobs("test_event_2");
   schedulerService.unscheduleAllJobs("test_event_3");
 }
 @Test(expected = IllegalArgumentException.class)
 public void shouldThrowExceptionForNullCronJob() throws Exception {
   schedulerService.scheduleJob((CronSchedulableJob) null);
 }