@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 shouldFloatTheAlertsForDelayedEnrollmentInTheTimeLeftTriggeringThemAtReferenceTime()
      throws SchedulerException, URISyntaxException, IOException {
    addSchedule("schedulingIT", "schedule_with_floating_alerts.json");

    try {
      AlertListener alertListener = new AlertListener();
      eventListenerRegistry.registerListener(alertListener, EventSubjects.MILESTONE_ALERT);
      TestScheduleUtil.fakeNow(newDateTime(2050, 5, 22, 10, 0, 0));
      Long enrollmentId =
          scheduleTrackingService.enroll(
              new EnrollmentRequest()
                  .setExternalId("abcde")
                  .setScheduleName("schedule_with_floating_alerts")
                  .setPreferredAlertTime(null)
                  .setReferenceDate(newDate(2050, 5, 10))
                  .setReferenceTime(new Time(9, 0))
                  .setEnrollmentDate(newDate(2050, 5, 19))
                  .setEnrollmentTime(new Time(9, 0))
                  .setStartingMilestoneName("milestone1")
                  .setMetadata(null));

      assertEquals(newDateTime(2050, 5, 22, 10, 0, 0), alertListener.getTriggerTime());

      List<DateTime> fireTimes =
          getFireTimes(
              format(
                  "org.motechproject.scheduletracking.milestone.alert-%s.0-repeat", enrollmentId));
      assertEquals(singletonList(newDateTime(2050, 5, 23, 9, 0, 0)), fireTimes);
    } finally {
      TestScheduleUtil.stopFakingTime();
      eventListenerRegistry.clearListenersForBean("alertsTestListener");
    }
  }
  @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);
    }
  }
  @Before
  public void setUp() throws InterruptedException, ConceptNameAlreadyInUseException {
    mrsListener = new MrsListener();
    eventListenerRegistry.registerListener(
        mrsListener,
        Arrays.asList(
            EventKeys.CREATED_NEW_PATIENT_SUBJECT,
            EventKeys.UPDATED_PATIENT_SUBJECT,
            EventKeys.PATIENT_DECEASED_SUBJECT,
            EventKeys.DELETED_PATIENT_SUBJECT));

    String uuid = savePatient(preparePatient()).getPatientId();
    patient = patientAdapter.getPatientByUuid(uuid);
    prepareConceptOfDeath();
  }
  @After
  public void tearDown() throws InterruptedException, PatientNotFoundException {

    String uuid = patient.getFacility().getFacilityId();

    deletePatient(patient);

    if (uuid != null) {
      facilityAdapter.deleteFacility(uuid);
    }

    conceptAdapter.deleteConcept(causeOfDeath.getUuid());

    eventListenerRegistry.clearListenersForBean("mrsTestListener");
  }