content: /**
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.aurora.scheduler.cron.quartz;
  
  import java.util.List;
  import java.util.Map;
  import java.util.Optional;
  import java.util.TimeZone;
  
  import com.google.common.collect.ImmutableList;
  import com.google.common.collect.ImmutableMap;
  import com.google.common.collect.ImmutableSet;
  
  import org.apache.aurora.common.testing.easymock.EasyMockTest;
  import org.apache.aurora.gen.CronCollisionPolicy;
  import org.apache.aurora.scheduler.base.TaskTestUtil;
  import org.apache.aurora.scheduler.cron.CronException;
  import org.apache.aurora.scheduler.cron.CronJobManager;
  import org.apache.aurora.scheduler.cron.CrontabEntry;
  import org.apache.aurora.scheduler.cron.SanitizedCronJob;
  import org.apache.aurora.scheduler.storage.Storage;
  import org.apache.aurora.scheduler.storage.Storage.MutateWork.NoResult;
  import org.apache.aurora.scheduler.storage.entities.IJobConfiguration;
  import org.apache.aurora.scheduler.storage.entities.IJobKey;
  import org.apache.aurora.scheduler.storage.mem.MemStorageModule;
  import org.easymock.EasyMock;
  import org.junit.Before;
  import org.junit.Test;
  import org.quartz.CronTrigger;
  import org.quartz.JobDetail;
  import org.quartz.Scheduler;
  import org.quartz.SchedulerException;
  import org.quartz.Trigger;
  
  import static org.easymock.EasyMock.anyObject;
  import static org.easymock.EasyMock.expect;
  import static org.easymock.EasyMock.expectLastCall;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  
  public class CronJobManagerImplTest extends EasyMockTest {
    private Storage storage;
    private Scheduler scheduler;
  
    private CronJobManager cronJobManager;
  
    @Before
    public void setUp() {
      storage = MemStorageModule.newEmptyStorage();
      scheduler = createMock(Scheduler.class);
  
      cronJobManager = new CronJobManagerImpl(storage, scheduler, TimeZone.getTimeZone("GMT"));
    }
  
    @Test
    public void testStartJobNowExistent() throws Exception {
      populateStorage();
      scheduler.triggerJob(QuartzTestUtil.QUARTZ_JOB_KEY);
  
      control.replay();
  
      cronJobManager.startJobNow(QuartzTestUtil.AURORA_JOB_KEY);
    }
  
    @Test(expected = CronException.class)
    public void testStartJobNowFails() throws Exception {
      populateStorage();
      scheduler.triggerJob(QuartzTestUtil.QUARTZ_JOB_KEY);
      expectLastCall().andThrow(new SchedulerException());
  
      control.replay();
  
      cronJobManager.startJobNow(QuartzTestUtil.AURORA_JOB_KEY);
    }
  
    @Test(expected = CronException.class)
    public void testStartJobNowNonexistent() throws Exception {
      control.replay();
  
      cronJobManager.startJobNow(QuartzTestUtil.AURORA_JOB_KEY);
    }
  
    @Test
    public void testUpdateExistingJob() throws Exception {
      SanitizedCronJob sanitizedCronJob = QuartzTestUtil.makeSanitizedCronJob();
  
      expect(scheduler.deleteJob(QuartzTestUtil.QUARTZ_JOB_KEY)).andReturn(true);
      expect(scheduler.scheduleJob(anyObject(JobDetail.class), anyObject(Trigger.class)))
         .andReturn(null);
  
      populateStorage();
  
      control.replay();
  
      cronJobManager.updateJob(sanitizedCronJob);
      assertEquals(
          sanitizedCronJob.getSanitizedConfig().getJobConfig(),
          fetchFromStorage().orElse(null));
    }
  
    @Test
    public void testUpdateNonexistentJob() throws Exception {
      control.replay();
  
      try {
        cronJobManager.updateJob(QuartzTestUtil.makeUpdatedJob());
        fail();
      } catch (CronException e) {
        // Expected.
      }
  
      assertEquals(Optional.empty(), fetchFromStorage());
    }
  
    @Test
    public void testCreateNonexistentJob() throws Exception {
      SanitizedCronJob sanitizedCronJob = QuartzTestUtil.makeSanitizedCronJob();
  
      expect(scheduler.scheduleJob(anyObject(JobDetail.class), anyObject(Trigger.class)))
          .andReturn(null);
  
      control.replay();
  
      cronJobManager.createJob(sanitizedCronJob);
  
      assertEquals(
          sanitizedCronJob.getSanitizedConfig().getJobConfig(),
          fetchFromStorage().orElse(null));
    }
  
    @Test(expected = CronException.class)
    public void testCreateNonexistentJobFails() throws Exception {
      SanitizedCronJob sanitizedCronJob = QuartzTestUtil.makeSanitizedCronJob();
  
      expect(scheduler.scheduleJob(anyObject(JobDetail.class), anyObject(Trigger.class)))
          .andThrow(new SchedulerException());
  
      control.replay();
  
      cronJobManager.createJob(sanitizedCronJob);
    }
  
    @Test(expected = CronException.class)
    public void testCreateExistingJobFails() throws Exception {
      SanitizedCronJob sanitizedCronJob = QuartzTestUtil.makeSanitizedCronJob();
      populateStorage();
      control.replay();
  
      cronJobManager.createJob(sanitizedCronJob);
    }
  
    @Test
    public void testNoRunOverlap() throws Exception {
      SanitizedCronJob runOverlapJob = SanitizedCronJob.fromUnsanitized(
          TaskTestUtil.CONFIGURATION_MANAGER,
          IJobConfiguration.build(QuartzTestUtil.JOB.newBuilder()
              .setCronCollisionPolicy(CronCollisionPolicy.RUN_OVERLAP)));
  
      control.replay();
  
      try {
        cronJobManager.createJob(runOverlapJob);
        fail();
      } catch (CronException e) {
        // Expected.
      }
  
      try {
        cronJobManager.updateJob(runOverlapJob);
      } catch (CronException e) {
        // Expected.
      }
  
      assertEquals(Optional.empty(), fetchFromStorage());
    }
  
    @Test
    public void testDeleteJob() throws Exception {
      expect(scheduler.deleteJob(QuartzTestUtil.QUARTZ_JOB_KEY)).andReturn(true);
  
      control.replay();
  
      assertFalse(cronJobManager.deleteJob(QuartzTestUtil.AURORA_JOB_KEY));
      populateStorage();
      assertTrue(cronJobManager.deleteJob(QuartzTestUtil.AURORA_JOB_KEY));
      assertEquals(Optional.empty(), fetchFromStorage());
    }
  
    @Test
    public void testFailedDeleteJobDoesNotThrow() throws Exception {
      expect(scheduler.deleteJob(QuartzTestUtil.QUARTZ_JOB_KEY)).andThrow(new SchedulerException());
  
      control.replay();
  
      populateStorage();
      cronJobManager.deleteJob(QuartzTestUtil.AURORA_JOB_KEY);
    }
  
    @Test
    public void testGetScheduledJobs() throws Exception {
      CronTrigger cronTrigger = createMock(CronTrigger.class);
      expect(scheduler.getJobKeys(anyObject()))
          .andReturn(ImmutableSet.of(QuartzTestUtil.QUARTZ_JOB_KEY));
      EasyMock.
          <List<? extends Trigger>>expect(scheduler.getTriggersOfJob(QuartzTestUtil.QUARTZ_JOB_KEY))
          .andReturn(ImmutableList.of(cronTrigger));
      expect(cronTrigger.getDescription()).andReturn("* * * * *");
  
      control.replay();
  
      Map<IJobKey, CrontabEntry> scheduledJobs = cronJobManager.getScheduledJobs();
      assertEquals(CrontabEntry.parse("* * * * *"), scheduledJobs.get(QuartzTestUtil.AURORA_JOB_KEY));
    }
  
    @Test
    public void testGetScheduledJobsEmpty() throws Exception {
      expect(scheduler.getJobKeys(anyObject()))
          .andReturn(ImmutableSet.of(QuartzTestUtil.QUARTZ_JOB_KEY));
      EasyMock.
          <List<? extends Trigger>>expect(scheduler.getTriggersOfJob(QuartzTestUtil.QUARTZ_JOB_KEY))
          .andReturn(ImmutableList.of());
  
      control.replay();
      assertEquals(ImmutableMap.of(), cronJobManager.getScheduledJobs());
    }
  
    @Test(expected = RuntimeException.class)
    public void testGetScheduledJobsFails() throws Exception {
      expect(scheduler.getJobKeys(anyObject()))
          .andReturn(ImmutableSet.of(QuartzTestUtil.QUARTZ_JOB_KEY));
      EasyMock.
          <List<? extends Trigger>>expect(scheduler.getTriggersOfJob(QuartzTestUtil.QUARTZ_JOB_KEY))
          .andThrow(new SchedulerException());
  
      control.replay();
      cronJobManager.getScheduledJobs();
    }
  
    private void populateStorage() throws Exception {
      storage.write((NoResult.Quiet) storeProvider -> storeProvider.getCronJobStore().saveAcceptedJob(
          QuartzTestUtil.makeSanitizedCronJob().getSanitizedConfig().getJobConfig()));
    }
  
    private Optional<IJobConfiguration> fetchFromStorage() {
      return storage.read(
          storeProvider -> storeProvider.getCronJobStore().fetchJob(QuartzTestUtil.AURORA_JOB_KEY));
    }
  }
  