diff --git a/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/AccountControllerTest.java b/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/AccountControllerTest.java index 0d07045..456e5e6 100644 --- a/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/AccountControllerTest.java +++ b/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/AccountControllerTest.java @@ -27,10 +27,8 @@ import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) @WebMvcTest(controllers = AccountController.class) public class AccountControllerTest { - @Mock - private AccountsRepository accountsRepository; - @InjectMocks - private AccountController accountController; + @Mock private AccountsRepository accountsRepository; + @InjectMocks private AccountController accountController; @Mock private BindingResult bindingResult; @Test diff --git a/services/src/integrationTest/java/edu/msudenver/tsp/services/DefinitionServiceIntegrationTest.java b/services/src/integrationTest/java/edu/msudenver/tsp/services/DefinitionServiceIntegrationTest.java new file mode 100644 index 0000000..9aff2fc --- /dev/null +++ b/services/src/integrationTest/java/edu/msudenver/tsp/services/DefinitionServiceIntegrationTest.java @@ -0,0 +1,85 @@ +package edu.msudenver.tsp.services; + +import edu.msudenver.tsp.services.dto.Definition; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = ServicesTestConfig.class) +@TestPropertySource(locations = "classpath:test.properties") +public class DefinitionServiceIntegrationTest { + @Autowired private DefinitionService definitionService; + + @Test + public void testCRUD() { + final Definition testDefinition = createDefinition(); + final Optional createdDefinition = definitionService.createDefinition(testDefinition); + + assertTrue(createdDefinition.isPresent()); + assertNotEquals(0, createdDefinition.get().getId()); + assertThat(createdDefinition.get().getVersion(), is(0)); + assertThat(createdDefinition.get().getName(), is("Test Name")); + assertNotNull(createdDefinition.get().getDefinition()); + assertThat(createdDefinition.get().getDefinition().size(), is(1)); + assertThat(createdDefinition.get().getDefinition().get(0), is("Test definition 1")); + assertNotNull(createdDefinition.get().getNotation()); + assertThat(createdDefinition.get().getNotation().size(), is(1)); + assertThat(createdDefinition.get().getNotation().get(0), is("\\testLaTeX")); + + final Optional definitionFoundById = definitionService.findById(createdDefinition.get().getId()); + + assertThat(definitionFoundById.get(), is(equalTo(createdDefinition.get()))); + + final Definition definitionUpdate = new Definition(); + definitionUpdate.setId(createdDefinition.get().getId()); + definitionUpdate.setName("Test Update"); + + final Optional updatedDefinition = definitionService.updateDefinition(definitionUpdate); + + assertTrue(updatedDefinition.isPresent()); + assertNotEquals(0, updatedDefinition.get().getId()); + assertThat(updatedDefinition.get().getVersion(), is(1)); + assertThat(updatedDefinition.get().getName(), is("Test Update")); + assertNotNull(updatedDefinition.get().getDefinition()); + assertThat(updatedDefinition.get().getDefinition().size(), is(1)); + assertThat(updatedDefinition.get().getDefinition().get(0), is("Test definition 1")); + assertNotNull(updatedDefinition.get().getNotation()); + assertThat(updatedDefinition.get().getNotation().size(), is(1)); + assertThat(updatedDefinition.get().getNotation().get(0), is("\\testLaTeX")); + + final boolean deletionWasSuccessful = definitionService.deleteDefinition(updatedDefinition.get()); + + assertThat(deletionWasSuccessful, is(true)); + + final Optional deletedDefinitionFoundById = definitionService.findById(createdDefinition.get().getId()); + + assertFalse(deletedDefinitionFoundById.isPresent()); + } + + private Definition createDefinition() { + final List definitionList = new ArrayList<>(); + definitionList.add("Test definition 1"); + + final List notationList = new ArrayList<>(); + notationList.add("\\testLaTeX"); + + final Definition definition = new Definition(); + definition.setName("Test Name"); + definition.setDefinition(definitionList); + definition.setNotation(notationList); + + return definition; + } +} diff --git a/services/src/main/java/edu/msudenver/tsp/services/DefinitionService.java b/services/src/main/java/edu/msudenver/tsp/services/DefinitionService.java new file mode 100644 index 0000000..68472ea --- /dev/null +++ b/services/src/main/java/edu/msudenver/tsp/services/DefinitionService.java @@ -0,0 +1,186 @@ +package edu.msudenver.tsp.services; + +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import edu.msudenver.tsp.services.dto.Definition; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +@Slf4j +@Service +public class DefinitionService { + private final RestService restService; + @Value("${persistence.api.connection.timeout.milliseconds}") private int connectionTimeoutMilliseconds; + @Value("${persistence.api.socket.timeout.milliseconds}") private int socketTimeoutMilliseconds; + @Value("${persistence.api.base.url}") private String persistenceApiBaseUrl; + + @Autowired + public DefinitionService(final RestService restService) { + this.restService = restService; + } + + public Optional> getAllDefinitions() { + final Instant start = Instant.now(); + + try { + final TypeToken> typeToken = new TypeToken>(){}; + final Optional> persistenceApiResponse = + restService.get(persistenceApiBaseUrl + "definitions/", + typeToken, connectionTimeoutMilliseconds, socketTimeoutMilliseconds, null); + + if (persistenceApiResponse.isPresent()) { + LOG.info("Returning {}", persistenceApiResponse.get()); + } else { + LOG.info("Unable to get list of definitions"); + } + + return persistenceApiResponse; + } catch (final Exception e) { + LOG.error("Error getting list of definitions!", e); + return Optional.empty(); + } finally { + LOG.info("Get all definitions request took {}ms", Duration.between(start, Instant.now()).toMillis()); + } + } + + public Optional findById(final int id) { + if (id == 0) { + LOG.error("Null id specified; returning {}"); + return Optional.empty(); + } + + LOG.info("Sending request to find definition by id {}", id); + final Instant start = Instant.now(); + + try { + final TypeToken typeToken = new TypeToken(){}; + final Optional persistenceApiResponse = restService.get(persistenceApiBaseUrl + "definitions/" + id, + typeToken, connectionTimeoutMilliseconds, socketTimeoutMilliseconds, null); + + if (persistenceApiResponse.isPresent()) { + LOG.info("Returning {}", persistenceApiResponse.get()); + } else { + LOG.info("Unable to find definition with id {}", id); + } + + return persistenceApiResponse; + } catch (final Exception e) { + LOG.error("Error finding definition by id", e); + return Optional.empty(); + } finally { + LOG.info("Find by id request took {}ms", Duration.between(start, Instant.now()).toMillis()); + } + } + + public Optional createDefinition(final Definition definition) { + if (definition == null) { + LOG.error("Given null definition, returning {}"); + return Optional.empty(); + } + + LOG.info("Sending request to insert definition {}", definition); + final Instant start = Instant.now(); + + try { + final TypeToken typeToken = new TypeToken() {}; + final Optional persistenceApiResponse = restService.post(persistenceApiBaseUrl + "definitions/", + new GsonBuilder().create().toJson(definition), + typeToken, + connectionTimeoutMilliseconds, + socketTimeoutMilliseconds); + + if (persistenceApiResponse.isPresent()) { + LOG.info("Creation successful. Returning {}", persistenceApiResponse.get()); + } else { + LOG.info("Unable to create new definition {}", definition); + } + + return persistenceApiResponse; + } catch (final Exception e) { + LOG.error("Error creating new definition", e); + return Optional.empty(); + } finally { + LOG.info("Create new definition request took {}ms", Duration.between(start, Instant.now()).toMillis()); + } + } + + public Optional updateDefinition(final Definition definition) { + if (definition == null) { + LOG.error("Given null definition, returning {}"); + return Optional.empty(); + } + + if (definition.getId() == 0) { + LOG.error("Given invalid id 0, returning {}"); + return Optional.empty(); + } + + LOG.info("Sending request to update definition {}", definition); + final Instant start = Instant.now(); + + try { + final TypeToken typeToken = new TypeToken(){}; + final Optional persistenceApiResponse = restService.patch(persistenceApiBaseUrl + "definitions/" + definition.getId(), + new GsonBuilder().create().toJson(definition), + typeToken, + connectionTimeoutMilliseconds, + socketTimeoutMilliseconds); + + if (persistenceApiResponse.isPresent()) { + LOG.info("Update successful. Returning {}", persistenceApiResponse.get()); + } else { + LOG.info("Unable to update definition {}", definition); + } + + return persistenceApiResponse; + } catch (final Exception e) { + LOG.error("Error updating definition", e); + return Optional.empty(); + } finally { + LOG.info("Update definition request took {}ms", Duration.between(start, Instant.now()).toMillis()); + } + } + + public boolean deleteDefinition(final Definition definition) { + if (definition == null) { + LOG.error("Given null definition, returning false"); + return false; + } + + if (definition.getId() == 0) { + LOG.error("Given invalid id 0, returning false"); + return false; + } + + LOG.info("Sending request to delete definition {}", definition); + final Instant start = Instant.now(); + + try { + final boolean deleteIsSuccessful = restService.delete(persistenceApiBaseUrl + "definitions/" + definition.getId(), + connectionTimeoutMilliseconds, + socketTimeoutMilliseconds, + HttpStatus.NO_CONTENT); + + if (deleteIsSuccessful) { + LOG.info("Deletion successful. Returning true"); + } else { + LOG.info("Unable to delete definition {}", definition); + } + + return deleteIsSuccessful; + } catch (final Exception e) { + LOG.error("Error when deleting definition", e); + return false; + } finally { + LOG.info("Delete definition request took {}ms", Duration.between(start, Instant.now()).toMillis()); + } + } +} diff --git a/services/src/main/java/edu/msudenver/tsp/services/dto/Definition.java b/services/src/main/java/edu/msudenver/tsp/services/dto/Definition.java new file mode 100644 index 0000000..a208e83 --- /dev/null +++ b/services/src/main/java/edu/msudenver/tsp/services/dto/Definition.java @@ -0,0 +1,24 @@ +package edu.msudenver.tsp.services.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class Definition extends BaseDto implements Serializable { + @NotBlank(groups = Insert.class, message = "A name must be specified") + @Size(min = 1, max = 200, message = "Must be between 1 and 200 characters") + private String name; + @NotBlank(groups = Insert.class, message = "At least one (1) definition must be specified") + private List definition; + private List notation; + + private static final long serialVersionUID = 3412178112996807691L; + + public interface Insert {} +} diff --git a/services/src/test/java/edu/msudenver/tsp/services/DefinitionServiceTest.java b/services/src/test/java/edu/msudenver/tsp/services/DefinitionServiceTest.java new file mode 100644 index 0000000..c9c8f42 --- /dev/null +++ b/services/src/test/java/edu/msudenver/tsp/services/DefinitionServiceTest.java @@ -0,0 +1,290 @@ +package edu.msudenver.tsp.services; + +import com.google.gson.GsonBuilder; +import edu.msudenver.tsp.services.dto.Definition; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static junit.framework.TestCase.assertTrue; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class DefinitionServiceTest { + @Mock private RestService restService; + @InjectMocks private DefinitionService definitionService; + + @Test + public void testGetAllDefinitions() { + final List definitionList = new ArrayList<>(); + final Definition testDefinition = createDefinition(); + definitionList.add(testDefinition); + definitionList.add(testDefinition); + + when(restService.get(anyString(), any(), anyInt(), anyInt(), anyString())) + .thenReturn(Optional.of(definitionList)); + + final Optional> listOfDefinitions = definitionService.getAllDefinitions(); + + assertTrue(listOfDefinitions.isPresent()); + assertThat(listOfDefinitions.get().size(), is(2)); + listOfDefinitions.get().forEach(definition -> assertThat(definition, equalTo(testDefinition))); + verify(restService).get(anyString(), any(), anyInt(), anyInt(), anyString()); + } + + @Test + public void testGetAllDefinitions_RequestReturnsEmptyOptional() { + when(restService.get(anyString(), any(), anyInt(), anyInt(), anyString())) + .thenReturn(Optional.empty()); + + final Optional> listOfDefinitions = definitionService.getAllDefinitions(); + + assertFalse(listOfDefinitions.isPresent()); + verify(restService).get(anyString(), any(), anyInt(), anyInt(), anyString()); + } + + @Test + public void testGetAllDefinitions_ExceptionThrownWhenSendingRequest() { + when(restService.get(anyString(), any(), anyInt(), anyInt(), anyString())) + .thenThrow(new UnsupportedOperationException("Test exception")); + + final Optional> listOfDefinitions = definitionService.getAllDefinitions(); + + assertFalse(listOfDefinitions.isPresent()); + verify(restService).get(anyString(), any(), anyInt(), anyInt(), anyString()); + } + + @Test + public void testFindById() { + final Definition testDefinition = createDefinition(); + when(restService.get(anyString(), any(), anyInt(), anyInt(), anyString())) + .thenReturn(Optional.of(testDefinition)); + + final Optional foundDefinition = definitionService.findById(testDefinition.getId()); + + assertTrue(foundDefinition.isPresent()); + assertThat(foundDefinition.get().getId(), is(1)); + assertThat(foundDefinition.get(), is(equalTo(testDefinition))); + verify(restService).get(anyString(), any(), anyInt(), anyInt(), anyString()); + } + + @Test + public void testFindById_RequestReturnsEmptyOptional() { + when(restService.get(anyString(), any(), anyInt(), anyInt(), anyString())) + .thenReturn(Optional.empty()); + + final Optional nonExistentDefinition = definitionService.findById(1); + + assertFalse(nonExistentDefinition.isPresent()); + verify(restService).get(anyString(), any(), anyInt(), anyInt(), anyString()); + } + + @Test + public void testFindById_ExceptionThrownWhenSendingRequest() { + when(restService.get(anyString(), any(), anyInt(), anyInt(), anyString())) + .thenThrow(new UnsupportedOperationException("test exception")); + + final Optional exceptionThrowingDefinition = definitionService.findById(1); + + assertFalse(exceptionThrowingDefinition.isPresent()); + verify(restService).get(anyString(), any(), anyInt(), anyInt(), anyString()); + } + + @Test + public void testFindById_IdIsZero() { + final Optional impossibleDefinition = definitionService.findById(0); + + assertFalse(impossibleDefinition.isPresent()); + verifyZeroInteractions(restService); + } + + @Test + public void testCreateDefinition() { + final Definition testDefinition = createDefinition(); + final String testDefinitionJson = new GsonBuilder().create().toJson(testDefinition); + + when(restService.post(anyString(), anyString(), any(), anyInt(), anyInt())) + .thenReturn(Optional.of(testDefinition)); + + final Optional createdDefinition = definitionService.createDefinition(testDefinition); + + assertNotNull(createdDefinition); + assertTrue(createdDefinition.isPresent()); + assertEquals(testDefinition, createdDefinition.get()); + verify(restService).post(anyString(), eq(testDefinitionJson), any(), anyInt(), anyInt()); + } + + @Test + public void testCreateDefinition_NullDefinition() { + final Optional nullDefinition = definitionService.createDefinition(null); + + assertFalse(nullDefinition.isPresent()); + verifyZeroInteractions(restService); + } + + @Test + public void testCreateDefinition_UnableToCreateDefinition() { + final Definition testDefinition = createDefinition(); + final String testDefinitionJson = new GsonBuilder().create().toJson(testDefinition); + + when(restService.post(anyString(), anyString(), any(), anyInt(), anyInt())) + .thenReturn(Optional.empty()); + + final Optional createdDefinition = definitionService.createDefinition(testDefinition); + + assertNotNull(createdDefinition); + assertFalse(createdDefinition.isPresent()); + verify(restService).post(anyString(), eq(testDefinitionJson), any(), anyInt(), anyInt()); + } + + @Test + public void testCreateDefinition_RestServiceThrowsException() { + final Definition testDefinition = createDefinition(); + final String testDefinitionJson = new GsonBuilder().create().toJson(testDefinition); + + when(restService.post(anyString(), anyString(), any(), anyInt(), anyInt())) + .thenThrow(new UnsupportedOperationException("test exception")); + + final Optional createdDefinition = definitionService.createDefinition(testDefinition); + + assertNotNull(createdDefinition); + assertFalse(createdDefinition.isPresent()); + verify(restService).post(anyString(), eq(testDefinitionJson), any(), anyInt(), anyInt()); + } + + @Test + public void testUpdateDefinition() { + when(restService.patch(anyString(), anyString(), any(), anyInt(), anyInt())) + .thenReturn(Optional.of(createDefinition().setName("Test update"))); + + final Definition testDefinition = new Definition(); + testDefinition.setName("Test update"); + testDefinition.setId(1); + + final Optional updatedDefinition = definitionService.updateDefinition(testDefinition); + + assertTrue(updatedDefinition.isPresent()); + assertThat(updatedDefinition.get().getId(), is(1)); + assertThat(updatedDefinition.get().getName(), is(equalTo("Test update"))); + verify(restService).patch(anyString(), anyString(), any(), anyInt(), anyInt()); + } + + @Test + public void testUpdateDefinition_nullDefinition() { + final Optional testUpdate = definitionService.updateDefinition(null); + + assertFalse(testUpdate.isPresent()); + verifyZeroInteractions(restService); + } + + @Test + public void testUpdateDefinition_IdIsZero() { + final Definition impossibleDefinition = createDefinition(); + impossibleDefinition.setId(0); + + final Optional testUpdate = definitionService.updateDefinition(impossibleDefinition); + + assertFalse(testUpdate.isPresent()); + verifyZeroInteractions(restService); + } + + @Test + public void testUpdateDefinition_RequestReturnsEmptyOptional() { + when(restService.patch(anyString(), anyString(), any(), anyInt(), anyInt())) + .thenReturn(Optional.empty()); + + final Optional nonExistentDefinition = definitionService.updateDefinition(createDefinition()); + + assertFalse(nonExistentDefinition.isPresent()); + verify(restService).patch(anyString(), anyString(), any(), anyInt(), anyInt()); + } + + @Test + public void testUpdateDefinition_ExceptionThrownWhenSendingRequest() { + when(restService.patch(anyString(), anyString(), any(), anyInt(), anyInt())) + .thenThrow(new UnsupportedOperationException("test exception")); + + final Optional exceptionThrowingDefinition = definitionService.updateDefinition(createDefinition()); + + assertFalse(exceptionThrowingDefinition.isPresent()); + verify(restService).patch(anyString(), anyString(), any(), anyInt(), anyInt()); + } + + @Test + public void testDeleteDefinition() { + when(restService.delete(anyString(), anyInt(), anyInt(), any())) + .thenReturn(true); + + final boolean deleteIsSuccessful = definitionService.deleteDefinition(createDefinition()); + + assertTrue(deleteIsSuccessful); + verify(restService).delete(anyString(), anyInt(), anyInt(), any()); + } + + @Test + public void testDeleteDefinition_NullDefinition() { + final boolean deleteIsSuccessful = definitionService.deleteDefinition(null); + + assertFalse(deleteIsSuccessful); + verifyZeroInteractions(restService); + } + + @Test + public void testDeleteDefinition_IdIsZero() { + final Definition testDefinition = createDefinition(); + testDefinition.setId(0); + + final boolean deleteIsSuccessful = definitionService.deleteDefinition(testDefinition); + + assertFalse(deleteIsSuccessful); + verifyZeroInteractions(restService); + } + + @Test + public void testDeleteDefinition_RequestReturnsFalse() { + when(restService.delete(anyString(), anyInt(), anyInt(), any())) + .thenReturn(false); + + final boolean deleteIsSuccessful = definitionService.deleteDefinition(createDefinition()); + + assertFalse(deleteIsSuccessful); + verify(restService).delete(anyString(), anyInt(), anyInt(), any()); + } + + @Test + public void testDeleteDefinition_ExceptionThrownWhenSendingRequest() { + when(restService.delete(anyString(), anyInt(), anyInt(), any())) + .thenThrow(new UnsupportedOperationException("test exception")); + + final boolean deleteIsSuccessful = definitionService.deleteDefinition(createDefinition()); + + assertFalse(deleteIsSuccessful); + verify(restService).delete(anyString(), anyInt(), anyInt(), any()); + } + + private Definition createDefinition() { + final List definitionList = new ArrayList<>(); + definitionList.add("Test definition 1"); + + final List notationList = new ArrayList<>(); + notationList.add("\\testLaTeX"); + + final Definition definition = new Definition(); + definition.setName("Test Name"); + definition.setDefinition(definitionList); + definition.setNotation(notationList); + definition.setId(1); + + return definition; + } +} \ No newline at end of file