@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(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(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 shouldThrowExceptionForInvalidCronExpression() throws SchedulerException {
   Map<String, Object> params = new HashMap<>();
   params.put(MotechSchedulerService.JOB_ID_KEY, "job_id");
   schedulerService.scheduleJob(
       new CronSchedulableJob(new MotechEvent("test_event", params), "invalidCronExpression"));
 }
  @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
  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 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 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();
    }
  }
  @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);
  }
  @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 = IllegalArgumentException.class)
 public void shouldThrowExceptionForNullCronJob() throws Exception {
   schedulerService.scheduleJob((CronSchedulableJob) null);
 }