Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TSPS-150 add tests for JobApiUtils #51

Merged
merged 2 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
package bio.terra.pipelines.controller;

import static org.junit.jupiter.api.Assertions.*;

import bio.terra.common.exception.ErrorReportException;
import bio.terra.pipelines.app.controller.JobApiUtils;
import bio.terra.pipelines.dependencies.stairway.JobMapKeys;
import bio.terra.pipelines.dependencies.stairway.exception.InternalStairwayException;
import bio.terra.pipelines.dependencies.stairway.exception.InvalidResultStateException;
import bio.terra.pipelines.dependencies.stairway.model.EnumeratedJobs;
import bio.terra.pipelines.generated.model.ApiErrorReport;
import bio.terra.pipelines.generated.model.ApiGetJobsResponse;
import bio.terra.pipelines.generated.model.ApiJobReport;
import bio.terra.pipelines.testutils.StairwayTestUtils;
import bio.terra.pipelines.testutils.TestUtils;
import bio.terra.stairway.FlightMap;
import bio.terra.stairway.FlightState;
import bio.terra.stairway.FlightStatus;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;

class JobApiUtilsTest {

@Test
void testMapEnumeratedJobsToApi() {
EnumeratedJobs bothJobs = StairwayTestUtils.ENUMERATED_JOBS;

ApiGetJobsResponse mappedResponse = JobApiUtils.mapEnumeratedJobsToApi(bothJobs);

assertEquals(bothJobs.getTotalResults(), mappedResponse.getTotalResults());
assertEquals(bothJobs.getPageToken(), mappedResponse.getPageToken());
assertEquals(bothJobs.getResults().size(), mappedResponse.getResults().size());
assertEquals(
bothJobs.getResults().get(0).getFlightState().getFlightId(),
mappedResponse.getResults().get(0).getId());
assertEquals(
bothJobs.getResults().get(1).getFlightState().getFlightId(),
mappedResponse.getResults().get(1).getId());
}

@Test
void testMapFlightStateToApiJobReportSucceeded() {
FlightState flightState = StairwayTestUtils.FLIGHT_STATE_DONE_SUCCESS_1;

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);

assertEquals(TestUtils.TEST_NEW_UUID.toString(), apiJobReport.getId());
assertEquals(StairwayTestUtils.TEST_DESCRIPTION, apiJobReport.getDescription());
assertEquals(
"SUCCEEDED", apiJobReport.getStatus().name()); // "SUCCESS" gets mapped to "SUCCEEDED"
assertEquals(StairwayTestUtils.TIME_SUBMITTED_1.toString(), apiJobReport.getSubmitted());
assertEquals(StairwayTestUtils.TIME_COMPLETED_1.toString(), apiJobReport.getCompleted());
assertEquals("", apiJobReport.getResultURL());
// if there is no status code in the working map, we assume it's a success/200
assertEquals(200, apiJobReport.getStatusCode());
}

@Test
void testMapFlightStateToApiJobReportSucceededNoCompletedTime() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.SUCCESS,
TestUtils.TEST_NEW_UUID,
StairwayTestUtils.CREATE_JOB_INPUT_PARAMS,
StairwayTestUtils.EMPTY_WORKING_MAP,
StairwayTestUtils.TIME_SUBMITTED_1,
null);

// a completed job should have a completed time, otherwise it's an error
assertThrows(
InvalidResultStateException.class,
() -> JobApiUtils.mapFlightStateToApiJobReport(flightState));
}

@Test
void testMapFlightStateToApiJobReportSucceededWithStatusCode() {
// Ensure the custom status code in the working map gets extracted and used
HttpStatus httpStatus = HttpStatus.I_AM_A_TEAPOT; // status code 418
FlightMap flightMapWithStatusCode = new FlightMap();
flightMapWithStatusCode.put(JobMapKeys.STATUS_CODE.getKeyName(), httpStatus);

FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.SUCCESS,
TestUtils.TEST_NEW_UUID,
StairwayTestUtils.CREATE_JOB_INPUT_PARAMS,
flightMapWithStatusCode,
StairwayTestUtils.TIME_SUBMITTED_1,
StairwayTestUtils.TIME_COMPLETED_1);

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);

assertEquals(
"SUCCEEDED", apiJobReport.getStatus().name()); // "SUCCESS" gets mapped to "SUCCEEDED"
assertEquals(418, apiJobReport.getStatusCode());
}

@Test
void testMapFlightStateToApiJobReportFailed() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.ERROR, TestUtils.TEST_NEW_UUID);
flightState.setException(new InternalStairwayException("some message"));

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);

assertEquals(TestUtils.TEST_NEW_UUID.toString(), apiJobReport.getId());
assertEquals("FAILED", apiJobReport.getStatus().name()); // ERROR gets mapped to FAILED
// InternalServerError is a 500
assertEquals(500, apiJobReport.getStatusCode());
}

@Test
void testMapFlightStateToApiJobReportFailedMissingException() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.ERROR,
TestUtils.TEST_NEW_UUID,
StairwayTestUtils.CREATE_JOB_INPUT_PARAMS,
StairwayTestUtils.EMPTY_WORKING_MAP,
StairwayTestUtils.TIME_SUBMITTED_1,
StairwayTestUtils.TIME_COMPLETED_1);

assertThrows(
InvalidResultStateException.class,
() -> JobApiUtils.mapFlightStateToApiJobReport(flightState));
}

@Test
void testMapFlightStateToApiJobReportRunning() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.RUNNING, TestUtils.TEST_NEW_UUID);

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);

assertEquals(TestUtils.TEST_NEW_UUID.toString(), apiJobReport.getId());
assertEquals(StairwayTestUtils.TEST_DESCRIPTION, apiJobReport.getDescription());
assertEquals("RUNNING", apiJobReport.getStatus().name());
assertNull(apiJobReport.getCompleted());
assertEquals("", apiJobReport.getResultURL());
assertEquals(202, apiJobReport.getStatusCode());
}

// the following tests are effectively tests of mapFlightStatusToApi(), which is private

@Test
void testMapFlightStateToApiJobReportRunningQueued() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.QUEUED, TestUtils.TEST_NEW_UUID);

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);

assertEquals("RUNNING", apiJobReport.getStatus().name());
assertNull(apiJobReport.getCompleted());
assertEquals(202, apiJobReport.getStatusCode());
}

@Test
void testMapFlightStateToApiJobReportRunningWaiting() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.WAITING, TestUtils.TEST_NEW_UUID);

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);

assertEquals("RUNNING", apiJobReport.getStatus().name());
assertNull(apiJobReport.getCompleted());
assertEquals(202, apiJobReport.getStatusCode());
}

@Test
void testMapFlightStateToApiJobReportRunningReady() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.READY, TestUtils.TEST_NEW_UUID);

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);

assertEquals("RUNNING", apiJobReport.getStatus().name());
assertNull(apiJobReport.getCompleted());
assertEquals(202, apiJobReport.getStatusCode());
}

@Test
void testMapFlightStateToApiJobReportRunningReadyToRestart() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.READY_TO_RESTART, TestUtils.TEST_NEW_UUID);

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);

assertEquals("RUNNING", apiJobReport.getStatus().name());
assertNull(apiJobReport.getCompleted());
assertEquals(202, apiJobReport.getStatusCode());
}

@Test
void testMapFlightStateToApiJobReportFailedFatal() {
FlightState flightState =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.FATAL, TestUtils.TEST_NEW_UUID);
flightState.setException(new InternalStairwayException("some message"));

ApiJobReport apiJobReport = JobApiUtils.mapFlightStateToApiJobReport(flightState);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh im kind of surprised the ApiJobReport doesnt report the exception message.

Copy link
Collaborator Author

@mmorgantaylor mmorgantaylor Jan 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was too. the spec defines the JobReport as the response for "get job status", and you have to call "get job result" to get the ErrorReport (that contains the error message and causes). we could consider adding the error message to JobReport, or we could create the "get job result" endpoint such that it can be used for polling, and if you use that one you'll get the ErrorReport. I'd lean toward the latter but we can discuss and make another ticket if needed.


assertEquals("FAILED", apiJobReport.getStatus().name());
assertEquals(500, apiJobReport.getStatusCode());
}

@Test
void testBuildApiErrorReportErrorReportExceptionCustomStatus() {
String errorMessage = "some message";
ErrorReportException exception =
new ErrorReportException(errorMessage, null, HttpStatus.I_AM_A_TEAPOT) {};

ApiErrorReport apiErrorReport = JobApiUtils.buildApiErrorReport(exception);

assertEquals(errorMessage, apiErrorReport.getMessage());
assertEquals(418, apiErrorReport.getStatusCode());
assertTrue(apiErrorReport.getCauses().isEmpty());
}

@Test
void testBuildApiErrorReportErrorReportExceptionCustomStatusWithCauses() {
String errorMessage = "some message";
List<String> causes = List.of("cause 1", "cause 2");
ErrorReportException exception =
new ErrorReportException(errorMessage, causes, HttpStatus.I_AM_A_TEAPOT) {};

ApiErrorReport apiErrorReport = JobApiUtils.buildApiErrorReport(exception);

assertEquals(errorMessage, apiErrorReport.getMessage());
assertEquals(418, apiErrorReport.getStatusCode());
assertEquals(causes, apiErrorReport.getCauses());
}

@Test
void testBuildApiErrorReport() {
String errorMessage = "some message";
InternalStairwayException exception = new InternalStairwayException(errorMessage);

ApiErrorReport apiErrorReport = JobApiUtils.buildApiErrorReport(exception);

assertEquals(errorMessage, apiErrorReport.getMessage());
assertEquals(500, apiErrorReport.getStatusCode());
assertTrue(apiErrorReport.getCauses().isEmpty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@
import bio.terra.pipelines.app.configuration.external.SamConfiguration;
import bio.terra.pipelines.app.controller.GlobalExceptionHandler;
import bio.terra.pipelines.app.controller.JobsApiController;
import bio.terra.pipelines.common.utils.PipelinesEnum;
import bio.terra.pipelines.db.exception.ImputationJobNotFoundException;
import bio.terra.pipelines.dependencies.sam.SamService;
import bio.terra.pipelines.dependencies.stairway.JobService;
import bio.terra.pipelines.dependencies.stairway.exception.JobUnauthorizedException;
import bio.terra.pipelines.dependencies.stairway.model.EnumeratedJob;
import bio.terra.pipelines.dependencies.stairway.model.EnumeratedJobs;
import bio.terra.pipelines.generated.model.ApiGetJobsResponse;
import bio.terra.pipelines.generated.model.ApiJobReport;
Expand All @@ -27,11 +25,9 @@
import bio.terra.pipelines.testutils.MockMvcUtils;
import bio.terra.pipelines.testutils.StairwayTestUtils;
import bio.terra.pipelines.testutils.TestUtils;
import bio.terra.stairway.FlightMap;
import bio.terra.stairway.FlightState;
import bio.terra.stairway.FlightStatus;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Instant;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -58,41 +54,6 @@ class JobsApiControllerTest {
@Autowired private MockMvc mockMvc;
private final SamUser testUser = MockMvcUtils.TEST_SAM_USER;
private final String testUserId = testUser.getSubjectId();
private final PipelinesEnum pipelineId = PipelinesEnum.IMPUTATION;
private final String pipelineVersion = TestUtils.TEST_PIPELINE_VERSION_1;
// should be updated once we do more thinking on what this will look like
private final Object pipelineInputs = Collections.emptyMap();
private final Instant timeSubmittedOne = Instant.now();
private final Instant timeSubmittedTwo = Instant.now();
private final Instant timeCompletedOne = Instant.now();
private final Instant timeCompletedTwo = Instant.now();
private final UUID jobIdOkDone = TestUtils.TEST_NEW_UUID;
private final UUID secondJobId = UUID.randomUUID();
private final FlightMap createJobInputParameters =
StairwayTestUtils.constructCreateJobInputs(
pipelineId, pipelineVersion, testUserId, pipelineInputs);
private final FlightMap createJobWorkingMap = new FlightMap();
private final FlightState flightStateDoneSuccess =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.SUCCESS,
jobIdOkDone,
createJobInputParameters,
createJobWorkingMap,
timeSubmittedOne,
timeCompletedOne);
private final FlightState secondFlightStateDoneSuccess =
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.SUCCESS,
secondJobId,
createJobInputParameters,
createJobWorkingMap,
timeSubmittedTwo,
timeCompletedTwo);

private final EnumeratedJob jobDoneSuccess =
new EnumeratedJob().flightState(flightStateDoneSuccess);
private final EnumeratedJob secondJobDoneSuccess =
new EnumeratedJob().flightState(secondFlightStateDoneSuccess);

@BeforeEach
void beforeEach() {
Expand All @@ -101,7 +62,9 @@ void beforeEach() {

@Test
void testGetJobOk() throws Exception {
when(jobServiceMock.retrieveJob(jobIdOkDone, testUserId)).thenReturn(flightStateDoneSuccess);
UUID jobIdOkDone = TestUtils.TEST_NEW_UUID;
when(jobServiceMock.retrieveJob(jobIdOkDone, testUserId))
.thenReturn(StairwayTestUtils.FLIGHT_STATE_DONE_SUCCESS_1);

MvcResult result =
mockMvc
Expand All @@ -124,10 +87,11 @@ void testGetErrorJobOk() throws Exception {
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.ERROR,
jobId,
createJobInputParameters,
createJobWorkingMap,
timeSubmittedOne,
timeCompletedOne);
StairwayTestUtils.CREATE_JOB_INPUT_PARAMS,
StairwayTestUtils.EMPTY_WORKING_MAP,
StairwayTestUtils.TIME_SUBMITTED_1,
StairwayTestUtils.TIME_COMPLETED_1);
flightStateDoneError.setException(new Exception("Test exception"));

when(jobServiceMock.retrieveJob(jobId, testUserId)).thenReturn(flightStateDoneError);

Expand Down Expand Up @@ -180,8 +144,7 @@ void testGetJob_badId() throws Exception {

@Test
void testGetMultipleJobs() throws Exception {
EnumeratedJobs bothJobs =
new EnumeratedJobs().results(List.of(jobDoneSuccess, secondJobDoneSuccess)).totalResults(2);
EnumeratedJobs bothJobs = StairwayTestUtils.ENUMERATED_JOBS;

// the mocks
when(jobServiceMock.enumerateJobs(testUser.getSubjectId(), 10, null, null))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import bio.terra.pipelines.testutils.MockMvcUtils;
import bio.terra.pipelines.testutils.StairwayTestUtils;
import bio.terra.pipelines.testutils.TestUtils;
import bio.terra.stairway.FlightState;
import bio.terra.stairway.FlightStatus;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down Expand Up @@ -339,10 +340,10 @@ void testGetPipelineJobs() throws Exception {
.flightState(
StairwayTestUtils.constructFlightStateWithStatusAndId(
FlightStatus.SUCCESS, jobId2));
EnumeratedJob job3Error =
new EnumeratedJob()
.flightState(
StairwayTestUtils.constructFlightStateWithStatusAndId(FlightStatus.ERROR, jobId3));
FlightState flightStateError =
StairwayTestUtils.constructFlightStateWithStatusAndId(FlightStatus.ERROR, jobId3);
flightStateError.setException(new Exception("Test exception"));
EnumeratedJob job3Error = new EnumeratedJob().flightState(flightStateError);

EnumeratedJobs allJobs =
new EnumeratedJobs().results(List.of(job1Running, job2Success, job3Error)).totalResults(3);
Expand Down
Loading
Loading