Skip to content

Commit

Permalink
Additional testing
Browse files Browse the repository at this point in the history
  • Loading branch information
flawmop committed Dec 17, 2024
1 parent ae55e1f commit 704659c
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 35 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ tasks.register('codeCoverageReport', JacocoReport) {
}
}

tasks.named('check') {
tasks.named('check').configure {
dependsOn(test)
dependsOn(testing.suites.test_i)
dependsOn(testing.suites.test_e2e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public class Simulation {
Simulation() {}

/**
* Initialising constructor.
* Initialising <b>and {@code verify()}ing</b> constructor.
*
* @param submissionId Submission identifier.
* @param modelId Model identifier.
Expand All @@ -109,6 +109,8 @@ public Simulation(final long submissionId, final int modelId, final BigDecimal p
this.pacingFrequency = pacingFrequency;
this.pacingMaxTime = pacingMaxTime;
this.plasmaPoints = plasmaPoints;

verify();
}

//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public void process(final long submissionId, final byte[] file) throws FileProce
}
switch (sectionField) {
case simulations:
parseSimulations(submissionId, jsonParser, simulations);
parseSimulation(submissionId, jsonParser, simulations);
break;
default:
final String message = "No implementation yet for document section '" + sectionName + "'";
Expand Down Expand Up @@ -153,8 +153,15 @@ public void process(final long submissionId, final byte[] file) throws FileProce
}
}

void parseSimulations(long submissionId, JsonParser jsonParser, List<Simulation> simulations)
throws FileProcessingException, IOException {
private void parseSimulation(long submissionId, JsonParser jsonParser, List<Simulation> simulations)
throws FileProcessingException, IOException {
JsonToken current = jsonParser.nextToken();
if (current != JsonToken.START_ARRAY) {
final String message = "'simulations' value must be an array";
log.warn("~process() : ".concat(message));
throw new FileProcessingException(message);
}

while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
Integer modelId = null;
BigDecimal pacingFrequency = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ static void dynamicProperties(DynamicPropertyRegistry registry) {
() -> keycloak.getAuthServerUrl() + "realms/PolarBookshop");
}

// TODO: Remove assertions that test for residual data in db!
@AfterEach
void afterEach() {
assertThat(JdbcTestUtils.countRowsInTable(jdbcTemplate, "simulation")).isEqualTo(0);
Expand Down Expand Up @@ -104,9 +105,10 @@ void failOnSubmissionNotFound() {
headers.setBearerAuth(bjornTokens.accessToken);
})
.exchange()
.expectStatus().isNotFound()
.expectHeader().contentType(textWithCharset)
.expectBody(String.class).isEqualTo("Submission with identifier '1' was not found");
.expectAll(
rsc -> rsc.expectStatus().isNotFound(),
rsc -> rsc.expectHeader().contentType(textWithCharset),
rsc -> rsc.expectBody(String.class).isEqualTo("Submission with identifier '1' was not found"));
}
}

Expand All @@ -125,9 +127,10 @@ void failOnSubmissionNotFound() {
headers.setBearerAuth(bjornTokens.accessToken);
})
.exchange()
.expectStatus().isNotFound()
.expectHeader().contentType(textWithCharset)
.expectBody(String.class).isEqualTo("Submission with identifier '1' was not found");
.expectAll(
rsc -> rsc.expectStatus().isNotFound(),
rsc -> rsc.expectHeader().contentType(textWithCharset),
rsc -> rsc.expectBody(String.class).isEqualTo("Submission with identifier '1' was not found"));
}
}

Expand Down Expand Up @@ -156,8 +159,9 @@ void failOnMultipartException() {
headers.addAll(httpHeaders);
})
.exchange()
.expectStatus().is5xxServerError()
.expectBody(String.class).isEqualTo("Error occurred during file upload - MultipartException");
.expectAll(
rsc -> rsc.expectStatus().is5xxServerError(),
rsc -> rsc.expectBody(String.class).isEqualTo("Error occurred during file upload - MultipartException"));
}

@DisplayName("Fail on expected request param not supplied")
Expand All @@ -172,8 +176,9 @@ void failOnBadParamName() {
})
.body(fromMultipartData(multipartBodyBuilder.build()))
.exchange()
.expectStatus().isBadRequest()
.expectBody(String.class).isEqualTo(message);
.expectAll(
rsc -> rsc.expectStatus().isBadRequest(),
rsc -> rsc.expectBody(String.class).isEqualTo(message));
}
}

Expand All @@ -196,9 +201,10 @@ void successOnSubmissionLifecycle() {
})
.body(fromMultipartData(multipartBodyBuilder.build()))
.exchange()
.expectStatus().isCreated()
.expectHeader().location("http://localhost:" + port + postUrl + "/" + submissionId)
.expectBody().isEmpty();
.expectAll(
rsc -> rsc.expectStatus().isCreated(),
rsc -> rsc.expectHeader().location("http://localhost:" + port + postUrl + "/" + submissionId),
rsc -> rsc.expectBody().isEmpty());

webTestClient.get()
.uri(url, submissionId)
Expand All @@ -207,18 +213,20 @@ void successOnSubmissionLifecycle() {
headers.setBearerAuth(bjornTokens.accessToken);
})
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(applicationJson)
.expectBody().jsonPath("$.entityId").isEqualTo(submissionId);
.expectAll(
rsc -> rsc.expectStatus().isOk(),
rsc -> rsc.expectHeader().contentType(applicationJson),
rsc -> rsc.expectBody().jsonPath("$.entityId").isEqualTo(submissionId));

webTestClient.delete()
.uri(url, submissionId)
.headers(headers -> {
headers.setBearerAuth(bjornTokens.accessToken);
})
.exchange()
.expectStatus().isNoContent()
.expectBody().isEmpty();
.expectAll(
rsc -> rsc.expectStatus().isNoContent(),
rsc -> rsc.expectBody().isEmpty());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ void failOnSubmissionNotFound() throws Exception {

mockMvc.perform(get(url).with(jwt().authorities(userRole)))
.andDo(print())
.andExpect(status().isNotFound())
.andExpect(content().contentType(textWithCharset))
.andExpect(content().string("Entity with identifier '1' was not found"));
.andExpectAll(
status().isNotFound(),
content().contentType(textWithCharset),
content().string("Entity with identifier '1' was not found"));

verify(mockSubmissionService).retrieve(submissionId);
}
Expand All @@ -81,10 +82,11 @@ void successOnSubmissionFound() throws Exception {

mockMvc.perform(get(url).with(jwt().authorities(userRole)))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType(applicationJson))
.andExpect(jsonPath("$.entityId").isEmpty())
.andExpect(jsonPath("$.state").value(State.CREATED.toString()));
.andExpectAll(
status().isOk(),
content().contentType(applicationJson),
jsonPath("$.entityId").isEmpty(),
jsonPath("$.state").value(State.CREATED.toString()));

verify(mockSubmissionService).retrieve(submissionId);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.insilicosoft.portal.svc.submission.controller;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.web.reactive.server.WebTestClient;

import com.insilicosoft.portal.svc.submission.SubmissionIdentifiers;
import com.insilicosoft.portal.svc.submission.exception.EntityNotAccessibleException;
import com.insilicosoft.portal.svc.submission.service.InputProcessorService;
import com.insilicosoft.portal.svc.submission.service.SubmissionService;

@ExtendWith(MockitoExtension.class)
public class SubmissionControllerIT2 {

@Mock
private InputProcessorService mockInputProcessorService;
@Mock
private SubmissionService mockSubmissionService;

private WebTestClient client;

@BeforeEach
void beforeEach() {
client = WebTestClient.bindToController(new SubmissionController(mockInputProcessorService,
mockSubmissionService)).build();
}

@Test
void test() throws EntityNotAccessibleException {
var submissionId = 1l;
var url = SubmissionIdentifiers.REQUEST_MAPPING_SUBMISSION + "/" + String.valueOf(submissionId);

//when(mockSubmissionService.retrieve(submissionId))
// .thenThrow(new EntityNotAccessibleException("Entity", "1"));

client.get().uri(url)
.exchange()
.expectStatus().is2xxSuccessful();

//verifyNoInteractions(mockInputProcessorService, mockSubmissionService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.context.request.RequestContextHolder;
Expand Down Expand Up @@ -103,13 +104,15 @@ void successOnUpload() throws FileProcessingException, InputVerificationExceptio
when(mockSubmission.getEntityId()).thenReturn(submissionEntityId);
doNothing().when(mockInputProcessorService).process(anyLong(), any(byte[].class));

var response = controller.createSimulation(file);
final ResponseEntity<Void> response = controller.createSimulation(file);

verify(mockInputProcessorService, only()).process(captorLong.capture(), captorBytes.capture());
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(response.getHeaders().getLocation()).isEqualTo(new URI("http://localhost/1"));
assertThat(captorLong.getValue()).isSameAs(submissionEntityId);
assertThat(captorBytes.getValue()).isEqualTo(bytes);

assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(response.getHeaders().getLocation()).isEqualTo(new URI("http://localhost/1"));
assertThat(response.getBody()).isNull();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
package com.insilicosoft.portal.svc.submission.service;

import static com.insilicosoft.portal.svc.submission.SubmissionIdentifiers.BINDING_NAME_SIMULATION_INPUT;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Arrays;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.stream.function.StreamBridge;

import com.insilicosoft.portal.svc.submission.event.SimulationMessage;
import com.insilicosoft.portal.svc.submission.exception.FileProcessingException;
import com.insilicosoft.portal.svc.submission.exception.InputVerificationException;
import com.insilicosoft.portal.svc.submission.persistence.entity.Simulation;
import com.insilicosoft.portal.svc.submission.persistence.repository.SimulationRepository;

Expand All @@ -21,9 +33,16 @@ public class InputProcessorServiceTest {
private InputProcessorService inputProcessorService;
private final long submissionEntityId = 1l;

@Captor
private ArgumentCaptor<String> captorString;
@Captor
private ArgumentCaptor<SimulationMessage> captorSimulationMessage;

@Mock
private Simulation mockSimulation;
@Mock
private SimulationMessage mockSimulationMessage;
@Mock
private SimulationRepository mockSimulationRepository;
@Mock
private StreamBridge mockStreamBridge;
Expand Down Expand Up @@ -59,12 +78,74 @@ void testFailIfInvalidFileContent() {
inputProcessorService.process(submissionEntityId, file3);
});
assertThat(e.getMessage()).isEqualTo("Could not generate any simulations");

final String fileContent = "{ \"%s\" : %s }";

String section = "fish"; // bad section (expecting 'simulations')
String value = "[]"; // good value

final byte[] file4 = String.format(fileContent, section, value).getBytes();
e = assertThrows(FileProcessingException.class, () -> {
inputProcessorService.process(submissionEntityId, file4);
});
assertThat(e.getMessage()).isEqualTo("Unrecognised document section '" + section + "', expected values are '" + Arrays.toString(FieldsSections.values()) + "'");

section = FieldsSections.simulations.toString(); // good section
value = "\"chips\""; // bad value

final byte[] file5 = String.format(fileContent, section, value).getBytes();
e = assertThrows(FileProcessingException.class, () -> {
inputProcessorService.process(submissionEntityId, file5);
});
assertThat(e.getMessage()).isEqualTo("'simulations' value must be an array");

// Array expected for simulations
value = "[]"; // expected structure for simulations, but no content

final byte[] file6 = String.format(fileContent, section, value).getBytes();
e = assertThrows(FileProcessingException.class, () -> {
inputProcessorService.process(submissionEntityId, file6);
});
assertThat(e.getMessage()).isEqualTo("Could not generate any simulations");

final String badFileContent = "{ \"simulations\": [ { \"modelIdd\" : 1, \"pacingMaxTime\": 5, \"plasmaPoints\": [ 0, 0.3, 1, 3, 10 ], \"pacingFrequency\": 0.5 } ] }";

final byte[] file7 = badFileContent.getBytes();
e = assertThrows(FileProcessingException.class, () -> {
inputProcessorService.process(submissionEntityId, file7);
});
assertThat(e.getMessage()).isEqualTo("Unrecognised simulation section 'modelIdd', expected values are '[modelId, pacingFrequency, pacingMaxTime, plasmaPoints]'");

}

@DisplayName("Fail if content is not valid.")
@Test
void testFailOnInputVerification() {
final String fileContent = "{ \"simulations\": [ { \"modelId\" : 0, \"pacingMaxTime\": -5, \"plasmaPoints\": [ -10, 0.3, 1, 3, 10 ], \"pacingFrequency\": -1.5 } ] }";
final byte[] file = fileContent.getBytes();

final InputVerificationException e = assertThrows(InputVerificationException.class, () -> {
inputProcessorService.process(submissionEntityId, file);
});
assertThat(e.getMessage()).isEqualTo("Problems encountered translating the simulation input");
}

@DisplayName("Success if content is valid.")
@Test
void testSuccessIfValidFileContent() throws FileProcessingException {
// No simulations generated!
void testSuccessIfValidFileContent() throws FileProcessingException, InputVerificationException {
final String fileContent = "{ \"simulations\": [ { \"modelId\" : 1, \"pacingMaxTime\": 5, \"plasmaPoints\": [ 0, 0.3, 1, 3, 10 ], \"pacingFrequency\": 0.5 } ] }";
final byte[] file = fileContent.getBytes();

when(mockSimulationRepository.save(any(Simulation.class))).thenReturn(mockSimulation);
when(mockSimulation.toMessage()).thenReturn(mockSimulationMessage);
when(mockStreamBridge.send(any(String.class), any(SimulationMessage.class))).thenReturn(true);

inputProcessorService.process(submissionEntityId, file);

verify(mockSimulation, only()).toMessage(); // We're not calling getMessages() on the mock!
verify(mockStreamBridge, only()).send(captorString.capture(), captorSimulationMessage.capture());
assertThat(captorString.getValue()).isEqualTo(BINDING_NAME_SIMULATION_INPUT);
assertThat(captorSimulationMessage.getValue()).isEqualTo(mockSimulationMessage);
}

}
Loading

0 comments on commit 704659c

Please sign in to comment.