package de.otto.edison.jobs.status;
import de.otto.edison.jobs.definition.JobDefinition;
import de.otto.edison.jobs.domain.JobInfo;
import de.otto.edison.jobs.repository.JobRepository;
import de.otto.edison.status.domain.Status;
import de.otto.edison.status.domain.StatusDetail;
import org.junit.Before;
import org.junit.Test;
import java.time.OffsetDateTime;
import java.util.List;
import static de.otto.edison.jobs.definition.DefaultJobDefinition.fixedDelayJobDefinition;
import static de.otto.edison.jobs.domain.JobInfo.JobStatus.DEAD;
import static de.otto.edison.jobs.domain.JobInfo.JobStatus.ERROR;
import static de.otto.edison.jobs.domain.JobInfo.JobStatus.OK;
import static de.otto.edison.jobs.domain.JobInfo.JobStatus.SKIPPED;
import static de.otto.edison.jobs.status.JobStatusCalculator.errorOnLastJobFailed;
import static de.otto.edison.jobs.status.JobStatusCalculator.errorOnLastNumJobsFailed;
import static de.otto.edison.jobs.status.JobStatusCalculator.warningOnLastJobFailed;
import static java.time.Duration.ofSeconds;
import static java.time.OffsetDateTime.now;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.not;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class JobStatusCalculatorTest {
private final JobDefinition jobDefinition = fixedDelayJobDefinition(
"test",
"test",
"",
ofSeconds(10),
0,
of(ofSeconds(10))
);
private JobRepository jobRepository;
private JobStatusCalculator warningOnLastJobFailed;
private JobStatusCalculator errorOnLastJobFailed;
private JobStatusCalculator errorOnLastTwoJobsFailed;
@Before
public void setUp() throws Exception {
jobRepository = mock(JobRepository.class);
warningOnLastJobFailed = warningOnLastJobFailed("test", jobRepository);
errorOnLastJobFailed = errorOnLastJobFailed("test", jobRepository);
errorOnLastTwoJobsFailed = errorOnLastNumJobsFailed("test", 2, jobRepository);
}
@Test
public void shouldIndicateOkIfLastJobOk() {
// given
final List<JobInfo> jobs = singletonList(someStoppedJob(OK, 1));
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobs);
// when
StatusDetail first = errorOnLastJobFailed.statusDetail(jobDefinition);
StatusDetail second = warningOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(first.getStatus(), is(Status.OK));
assertThat(first.getMessage(), is("Last job was successful"));
assertThat(second.getStatus(), is(Status.OK));
}
@Test
public void shouldIndicateOkIfLastJobSkipped() {
// given
final List<JobInfo> jobs = singletonList(someStoppedJob(SKIPPED, 1));
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobs);
// when
StatusDetail first = errorOnLastJobFailed.statusDetail(jobDefinition);
StatusDetail second = warningOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(first.getStatus(), is(Status.OK));
assertThat(first.getMessage(), is("Last job was successful"));
assertThat(second.getStatus(), is(Status.OK));
}
@Test
public void shouldReturnStatusDetailWithJobLink() {
// given
final JobInfo jobInfo = someStoppedJob(OK, 1);
final List<JobInfo> jobs = singletonList(jobInfo);
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobs);
// when
StatusDetail first = errorOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(first.getLinks().size(), is(1));
assertThat(first.getLinks().get(0).href, is("/internal/jobs/" + jobInfo.getJobId()));
}
@Test
public void shouldIndicateStateIfLastJobFailed() {
// given
final List<JobInfo> jobInfos = singletonList(someStoppedJob(ERROR, 1));
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobInfos);
// when
StatusDetail first = errorOnLastJobFailed.statusDetail(jobDefinition);
StatusDetail second = warningOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(first.getStatus(), is(Status.ERROR));
assertThat(first.getMessage(), is("Job had an error"));
assertThat(second.getStatus(), is(Status.WARNING));
assertThat(second.getMessage(), is("Job had an error"));
}
@Test
public void shouldIndicateOkIfLastOfTwoJobsOk() {
// given
final List<JobInfo> jobInfos = asList(
someStoppedJob(OK, 1),
someStoppedJob(ERROR, 2));
when(jobRepository.findLatestBy(anyString(), eq(2))).thenReturn(jobInfos);
// when
StatusDetail detail = errorOnLastTwoJobsFailed.statusDetail(jobDefinition);
// then
assertThat(detail.getStatus(), is(Status.OK));
assertThat(detail.getMessage(), is("Last job was successful"));
}
@Test
public void shouldIndicateWarningIfOneOfTwoJobsOk() {
// given
final List<JobInfo> jobInfos = asList(
someStoppedJob(ERROR, 1),
someStoppedJob(OK, 2));
when(jobRepository.findLatestBy(anyString(), eq(2))).thenReturn(jobInfos);
// when
StatusDetail detail = errorOnLastTwoJobsFailed.statusDetail(jobDefinition);
// then
assertThat(detail.getStatus(), is(Status.WARNING));
assertThat(detail.getMessage(), is("1 out of 2 job executions failed"));
}
@Test
public void shouldIndicateWarningIfTwoOfThreeJobsFailed() {
// given
final List<JobInfo> jobInfos = asList(
someStoppedJob(OK, 1),
someStoppedJob(ERROR, 2),
someStoppedJob(ERROR, 2));
when(jobRepository.findLatestBy(anyString(), eq(3))).thenReturn(jobInfos);
// when
JobStatusCalculator maxOneOfThree = new JobStatusCalculator("test", 3, 1, jobRepository);
StatusDetail detail = maxOneOfThree.statusDetail(jobDefinition);
// then
assertThat(detail.getStatus(), is(Status.WARNING));
assertThat(detail.getMessage(), is("2 out of 3 job executions failed"));
}
@Test
public void shouldIndicateErrorIfTwoOfThreeJobsFailed() {
// given
final List<JobInfo> jobInfos = asList(
someStoppedJob(ERROR, 1),
someStoppedJob(OK, 2),
someStoppedJob(ERROR, 2));
when(jobRepository.findLatestBy(anyString(), eq(3))).thenReturn(jobInfos);
// when
JobStatusCalculator maxOneOfThree = new JobStatusCalculator("test", 3, 1, jobRepository);
StatusDetail detail = maxOneOfThree.statusDetail(jobDefinition);
// then
assertThat(detail.getStatus(), is(Status.ERROR));
assertThat(detail.getMessage(), is("2 out of 3 job executions failed"));
}
@Test
public void shouldIndicateErrorIfTwoJobsFailed() {
// given
final List<JobInfo> jobInfos = asList(
someStoppedJob(ERROR, 1),
someStoppedJob(ERROR, 2));
when(jobRepository.findLatestBy(anyString(), eq(2))).thenReturn(jobInfos);
// when
StatusDetail detail = errorOnLastTwoJobsFailed.statusDetail(jobDefinition);
// then
assertThat(detail.getStatus(), is(Status.ERROR));
assertThat(detail.getMessage(), is("2 out of 2 job executions failed"));
}
@Test
public void shouldIndicateWarningIfLastJobRunWasTooLongAgo() {
// given
final List<JobInfo> jobInfo = singletonList(someStoppedJob(OK, 11));
final List<JobInfo> jobInfos = asList(
someStoppedJob(OK, 11),
someStoppedJob(OK, 12));
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobInfo);
when(jobRepository.findLatestBy(anyString(), eq(2))).thenReturn(jobInfos);
// when
StatusDetail first = errorOnLastJobFailed.statusDetail(jobDefinition);
StatusDetail second = warningOnLastJobFailed.statusDetail(jobDefinition);
StatusDetail third = errorOnLastTwoJobsFailed.statusDetail(jobDefinition);
// then
assertThat(first.getStatus(), is(Status.WARNING));
assertThat(first.getMessage(), is("Job didn't run in the past 10 seconds"));
assertThat(second.getStatus(), is(Status.WARNING));
assertThat(second.getMessage(), is("Job didn't run in the past 10 seconds"));
assertThat(third.getStatus(), is(Status.WARNING));
assertThat(third.getMessage(), is("Job didn't run in the past 10 seconds"));
}
@Test
public void shouldNotHaveUriOrRunningIfNoJobPresent() {
// given
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(emptyList());
// when
StatusDetail statusDetail = errorOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(statusDetail.getLinks(), is(emptyList()));
assertThat(statusDetail.getDetails(), not(hasKey("Running")));
}
@Test
@SuppressWarnings("unchecked")
public void shouldIndicateThatJobIsNotRunning() {
// given
final List<JobInfo> jobInfos = singletonList(someStoppedJob(OK, 1));
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobInfos);
// when
StatusDetail statusDetail = errorOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(statusDetail.getDetails(), not(hasKey("Running")));
assertThat(statusDetail.getDetails(), hasKey("Stopped"));
}
@Test
public void shouldIndicateWarningIfJobRunWasDead() {
// given
final List<JobInfo> jobInfos = singletonList(someRunningJob(DEAD, 1));
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobInfos);
// when
StatusDetail statusDetail = errorOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(statusDetail.getStatus(), is(Status.WARNING));
assertThat(statusDetail.getMessage(), is("Job died"));
}
@Test
public void shouldIndicateWarningIfLastJobRunWasDead() {
// given
final List<JobInfo> jobInfos = singletonList(someStoppedJob(DEAD, 1));
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobInfos);
// when
StatusDetail statusDetail = errorOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(statusDetail.getStatus(), is(Status.WARNING));
assertThat(statusDetail.getMessage(), is("Job died"));
}
@Test
@SuppressWarnings("unchecked")
public void shouldIndicateErrorIfJobCouldNotBeRetievedFromRepository() {
// given
when(jobRepository.findLatestBy(anyString(), eq(1))).thenThrow(RuntimeException.class);
// when
StatusDetail statusDetail = errorOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(statusDetail.getStatus(), is(Status.ERROR));
}
@Test
public void shouldAcceptIfNoJobRan() {
// given
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(emptyList());
// when
StatusDetail statusDetail = errorOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(statusDetail.getStatus(), is(Status.OK));
}
@Test
public void shouldHaveName() {
// given
final List<JobInfo> jobInfos = singletonList(someStoppedJob(OK, 1));
when(jobRepository.findLatestBy(anyString(), eq(1))).thenReturn(jobInfos);
// when
StatusDetail statusDetail = errorOnLastJobFailed.statusDetail(jobDefinition);
// then
assertThat(statusDetail.getName(), is("test"));
}
private JobInfo someStoppedJob(final JobInfo.JobStatus jobStatus, int startedSecondsAgo) {
OffsetDateTime now = now();
JobInfo someJob = mock(JobInfo.class);
when(someJob.getJobType()).thenReturn("someJobType");
when(someJob.getJobId()).thenReturn("someId");
when(someJob.getStarted()).thenReturn(now.minusSeconds(startedSecondsAgo));
when(someJob.getStopped()).thenReturn(of(now.minusSeconds(startedSecondsAgo-1)));
when(someJob.getStatus()).thenReturn(jobStatus);
return someJob;
}
private JobInfo someRunningJob(final JobInfo.JobStatus jobStatus, int startedSecondsAgo) {
OffsetDateTime now = now();
JobInfo someJob = mock(JobInfo.class);
when(someJob.getJobType()).thenReturn("someJobType");
when(someJob.getJobId()).thenReturn("someJobId");
when(someJob.getStarted()).thenReturn(now.minusSeconds(startedSecondsAgo));
when(someJob.getStopped()).thenReturn(empty());
when(someJob.getStatus()).thenReturn(jobStatus);
return someJob;
}
}