diff --git a/pandamonium-theorem-prover/build.gradle b/pandamonium-theorem-prover/build.gradle index a89060e..6690226 100644 --- a/pandamonium-theorem-prover/build.gradle +++ b/pandamonium-theorem-prover/build.gradle @@ -37,6 +37,14 @@ sonarqube { } allprojects { + sonarqube { + properties { + property "sonar.host.url", "https://sonarcloud.io" + property "sonar.projectKey", "atusa17_ptp" + property "sonar.organization", "atusa17-github" + property "sonar.login", "9dcc611cd79f175459248b053b25450e36e38463" + } + } apply plugin: 'java' apply plugin: 'jacoco' apply plugin: 'org.unbroken-dome.test-sets' @@ -45,6 +53,7 @@ allprojects { } subprojects { + apply plugin: 'java' repositories { mavenCentral() } @@ -108,6 +117,12 @@ dependencies { apt 'org.projectlombok:lombok:1.18.4' } +project(':persistence') { + dependencies { + compile project(':utilities') + } +} + test { if (System.properties['test.profile'] != 'integrationTest') { exclude '**/*integrationTest*' diff --git a/pandamonium-theorem-prover/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/AccountsIntegrationTest.java b/pandamonium-theorem-prover/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/AccountsIntegrationTest.java new file mode 100644 index 0000000..b30a252 --- /dev/null +++ b/pandamonium-theorem-prover/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/AccountsIntegrationTest.java @@ -0,0 +1,57 @@ +package edu.msudenver.tsp.persistence; + +import edu.msudenver.tsp.persistence.dto.AccountDto; +import edu.msudenver.tsp.persistence.repository.AccountsRepository; +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.junit4.SpringJUnit4ClassRunner; + +import java.util.Optional; + +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = PersistenceTestConfig.class) +public class AccountsIntegrationTest { + @Autowired private AccountsRepository accountsRepository; + + @Test + public void testCRUDFunctionality() { + final AccountDto accountDto = createAccount(); + final AccountDto savedAccount = accountsRepository.save(accountDto); + + assertNotNull(savedAccount); + assertEquals(Integer.valueOf(0), savedAccount.getVersion()); + + final int id = savedAccount.getId(); + + assertEquals("Test username", savedAccount.getUsername()); + assertEquals("test password", savedAccount.getPassword()); + assertTrue(savedAccount.isAdministratorStatus()); + + savedAccount.setPassword("Test Update"); + + final AccountDto updatedAccount = accountsRepository.save(savedAccount); + + assertEquals("Test username", savedAccount.getUsername()); + assertEquals("Test Update", savedAccount.getPassword()); + assertTrue(savedAccount.isAdministratorStatus()); + assertEquals(updatedAccount.getId(), id); + + accountsRepository.delete(accountDto); + final Optional deletedAccount = accountsRepository.findById(id); + assertFalse(deletedAccount.isPresent()); + } + + private AccountDto createAccount() { + final AccountDto accountDto = new AccountDto(); + accountDto.setUsername("Test username"); + accountDto.setPassword("test password"); + accountDto.setAdministratorStatus(true); + + return accountDto; + } + +} diff --git a/pandamonium-theorem-prover/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/DefinitionsIntegrationTest.java b/pandamonium-theorem-prover/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/DefinitionsIntegrationTest.java index 824d5a8..93db37e 100644 --- a/pandamonium-theorem-prover/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/DefinitionsIntegrationTest.java +++ b/pandamonium-theorem-prover/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/DefinitionsIntegrationTest.java @@ -23,7 +23,6 @@ public class DefinitionsIntegrationTest { @Test public void testCRUDFunctionality() { - // Create a new definition final DefinitionDto definitionDto = createDefinition(); final DefinitionDto savedDefinition = definitionRepository.save(definitionDto); @@ -45,7 +44,25 @@ public class DefinitionsIntegrationTest { assertEquals("Test definition 2", definitionsList.get(1)); assertEquals("\\testLaTeX", notationList.get(0)); - definitionRepository.delete(savedDefinition); + savedDefinition.setName("Test Update"); + + final DefinitionDto updatedDefinition = definitionRepository.save(savedDefinition); + + assertEquals("Test Update", updatedDefinition.getName()); + assertNotNull(updatedDefinition.getDefinition()); + assertNotNull(updatedDefinition.getNotation()); + + final List updatedDefinitionsList = updatedDefinition.getDefinition().getDefinitions(); + final List updatedNotationsList = updatedDefinition.getNotation().getNotations(); + + assertEquals(2, updatedDefinitionsList.size()); + assertEquals(1, updatedNotationsList.size()); + assertEquals("Test definition 1", updatedDefinitionsList.get(0)); + assertEquals("Test definition 2", updatedDefinitionsList.get(1)); + assertEquals("\\testLaTeX", updatedNotationsList.get(0)); + assertEquals(id, updatedDefinition.getId()); + + definitionRepository.delete(updatedDefinition); final Optional deletedDefinition = definitionRepository.findById(id); assertFalse(deletedDefinition.isPresent()); } diff --git a/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/AccountController.java b/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/AccountController.java index 8404acb..25f5368 100644 --- a/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/AccountController.java +++ b/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/AccountController.java @@ -1,12 +1,23 @@ package edu.msudenver.tsp.persistence.controller; -import edu.msudenver.tsp.persistence.dto.AccountsDto; +import edu.msudenver.tsp.persistence.dto.AccountDto; import edu.msudenver.tsp.persistence.repository.AccountsRepository; +import edu.msudenver.tsp.utilities.PersistenceUtilities; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StopWatch; +import org.springframework.validation.BindingResult; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; +import javax.validation.groups.Default; +import java.util.List; import java.util.Optional; +@Slf4j @RestController @AllArgsConstructor @RequestMapping("/accounts") @@ -14,13 +25,157 @@ public class AccountController { private final AccountsRepository accountsRepository; @GetMapping("/") - public @ResponseBody Iterable getListOfAccounts() { - return accountsRepository.findAll(); + public @ResponseBody + ResponseEntity> getListOfAccounts() { + LOG.info("Received request to list all accounts"); + + LOG.debug("Querying for list of accounts"); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final List listOfAccounts = (List) accountsRepository.findAll(); + + stopWatch.stop(); + + LOG.debug("Successfully completed query. Query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + LOG.info("Returning list of all accounts with size of " + listOfAccounts.size()); + + return new ResponseEntity<>(listOfAccounts, HttpStatus.OK); } @GetMapping("/{id}") public @ResponseBody - Optional getAccountById(@PathVariable("id") final Integer id) { - return accountsRepository.findById(id); + ResponseEntity getAccountById(@PathVariable("id") final Integer id) { + LOG.info("Received request to query for account with id " + id); + if (id == null) { + LOG.error("ERROR: ID was null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Querying for account with id " + id); + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final Optional account = accountsRepository.findById(id); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + return account.map(accountDto -> { + LOG.info("Returning account with id " + id); + return new ResponseEntity<>(accountDto, HttpStatus.OK); + }).orElseGet( + () -> { + LOG.warn("No account was found with id " + id); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + ); + } + + @PostMapping("/") + @Validated({AccountDto.Insert.class, Default.class}) + public @ResponseBody ResponseEntity insertAccount( + @Valid @RequestBody final AccountDto accountDto, final BindingResult bindingResult) { + + LOG.info("Received request to insert a new account"); + if (bindingResult.hasErrors()) { + LOG.error("Binding result is unprocessable"); + return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); + } + + if (accountDto == null) { + LOG.error("Passed account is unprocessable"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Saving new account"); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final AccountDto savedAccount = accountsRepository.save(accountDto); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + LOG.info("Returning the newly created account"); + return new ResponseEntity<>(savedAccount, HttpStatus.CREATED); + } + + @PatchMapping("/{id}") + public @ResponseBody ResponseEntity updateAccount( + @PathVariable("id") final Integer id, + @RequestBody final AccountDto accountDto, final BindingResult bindingResult) { + + LOG.info("Received request to update an account"); + if (bindingResult.hasErrors()) { + LOG.error("Binding result is unprocessable"); + return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); + } + + if (accountDto == null) { + LOG.error("Passed entity is null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + if (id == null) { + LOG.error("Account ID must be specified"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Checking for existence of account with id " + id); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final Optional existingAccount = accountsRepository.findById(id); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + + if (!existingAccount.isPresent()) { + LOG.error("No account associated with id " + id); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + PersistenceUtilities.copyNonNullProperties(accountDto, existingAccount.get()); + existingAccount.get().setVersion(existingAccount.get().getVersion()+ 1); + + LOG.info("Updating account with id " + id); + LOG.debug("Querying for account with ID " + id); + + stopWatch.start(); + + final AccountDto updatedAccount = accountsRepository.save(existingAccount.get()); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + + return new ResponseEntity<>(updatedAccount, HttpStatus.OK); + } + + @DeleteMapping("/{id}") + public @ResponseBody ResponseEntity deleteAccountById(@PathVariable("id") final Integer id) { + LOG.info("Received request to delete account with id " + id); + if (id == null) { + LOG.error("Specified Id is null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Deleting account with id " + id); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + accountsRepository.deleteById(id); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } } diff --git a/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/DefinitionController.java b/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/DefinitionController.java index cb74331..3b788a5 100644 --- a/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/DefinitionController.java +++ b/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/DefinitionController.java @@ -2,6 +2,7 @@ package edu.msudenver.tsp.persistence.controller; import edu.msudenver.tsp.persistence.dto.DefinitionDto; import edu.msudenver.tsp.persistence.repository.DefinitionRepository; +import edu.msudenver.tsp.utilities.PersistenceUtilities; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -102,6 +103,60 @@ public class DefinitionController { return new ResponseEntity<>(savedDefinition, HttpStatus.CREATED); } + @PatchMapping("/{id}") + public @ResponseBody ResponseEntity updateDefinition( + @PathVariable("id") final Integer id, + @RequestBody final DefinitionDto definitionDto, final BindingResult bindingResult) { + + LOG.info("Received request to update an account"); + if (bindingResult.hasErrors()) { + LOG.error("Binding result is unprocessable"); + return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); + } + + if (definitionDto == null) { + LOG.error("Passed entity is null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + if (id == null) { + LOG.error("Definition ID must be specified"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Checking for existence of definition with id " + id); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final Optional existingDefinition = definitionRepository.findById(id); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + + if (!existingDefinition.isPresent()) { + LOG.error("No definition associated with id " + id); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + PersistenceUtilities.copyNonNullProperties(definitionDto, existingDefinition.get()); + existingDefinition.get().setVersion(existingDefinition.get().getVersion()+ 1); + + LOG.info("Updating definition with id " + id); + LOG.debug("Querying for definition with ID " + id); + + stopWatch.start(); + + final DefinitionDto updatedDefinition = definitionRepository.save(existingDefinition.get()); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + + return new ResponseEntity<>(updatedDefinition, HttpStatus.OK); + } + @DeleteMapping("/{id}") public @ResponseBody ResponseEntity deleteDefinitionById(@PathVariable("id") final Integer id) { LOG.info("Received request to delete definition with id " + id); diff --git a/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountsDto.java b/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountDto.java similarity index 69% rename from pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountsDto.java rename to pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountDto.java index 6d689a5..717bc64 100644 --- a/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountsDto.java +++ b/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountDto.java @@ -9,6 +9,7 @@ import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.io.Serializable; @@ -18,11 +19,13 @@ import java.util.Date; @EntityListeners(AuditingEntityListener.class) @Data @EqualsAndHashCode(callSuper = true) -public class AccountsDto extends BaseDto implements Serializable { - @Size(max = 50) private String username; - @Size(max = 256) private String password; +public class AccountDto extends BaseDto implements Serializable { + @NotBlank(groups = Insert.class, message = "A username must be specified") @Size(max = 50) private String username; + @NotBlank(groups = Insert.class, message = "A password must be specified") @Size(max = 256) private String password; @NotNull @JsonProperty("administrator_status") private boolean administratorStatus; @Temporal(TemporalType.DATE) @JsonProperty("last_login") private Date lastLogin; public static final long serialVersionUID = 7095627971593953734L; + + public interface Insert {} } diff --git a/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/AccountsRepository.java b/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/AccountsRepository.java index a2d0704..454cd77 100644 --- a/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/AccountsRepository.java +++ b/pandamonium-theorem-prover/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/AccountsRepository.java @@ -1,9 +1,9 @@ package edu.msudenver.tsp.persistence.repository; -import edu.msudenver.tsp.persistence.dto.AccountsDto; -import org.springframework.data.jpa.repository.JpaRepository; +import edu.msudenver.tsp.persistence.dto.AccountDto; +import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository -public interface AccountsRepository extends JpaRepository { +public interface AccountsRepository extends CrudRepository { } diff --git a/pandamonium-theorem-prover/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/AccountControllerTest.java b/pandamonium-theorem-prover/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/AccountControllerTest.java new file mode 100644 index 0000000..0b88630 --- /dev/null +++ b/pandamonium-theorem-prover/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/AccountControllerTest.java @@ -0,0 +1,224 @@ +package edu.msudenver.tsp.persistence.controller; + +import edu.msudenver.tsp.persistence.dto.AccountDto; +import edu.msudenver.tsp.persistence.repository.AccountsRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +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 BindingResult bindingResult; + + @Test + public void testGetAllAccounts() { + final AccountDto accountDto = createAccount(); + final List accountDtoList = new ArrayList<>(); + accountDtoList.add(accountDto); + accountDtoList.add(accountDto); + + when(accountsRepository.findAll()).thenReturn(accountDtoList); + + final ResponseEntity> responseEntity = accountController.getListOfAccounts(); + + assertNotNull(responseEntity); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + + responseEntity.getBody().forEach(account -> assertEquals(account, accountDto)); + } + + @Test + public void testGetAccountById() { + final AccountDto accountDto = createAccount(); + when(accountsRepository.findById(anyInt())).thenReturn(Optional.ofNullable(accountDto)); + + final ResponseEntity responseEntity = accountController.getAccountById(1); + + assertNotNull(responseEntity); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals(accountDto, responseEntity.getBody()); + verify(accountsRepository).findById(anyInt()); + } + + @Test + public void testGetAccountById_nullId() { + final ResponseEntity responseEntity = accountController.getAccountById(null); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(accountsRepository); + } + + @Test + public void testGetAccountById_noAccountFound() { + when(accountsRepository.findById(anyInt())).thenReturn(Optional.empty()); + + final ResponseEntity responseEntity = accountController.getAccountById(1); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode()); + verify(accountsRepository).findById(anyInt()); + } + + @Test + public void testInsertAccount() { + final AccountDto accountDto = createAccount(); + when(accountsRepository.save(any(AccountDto.class))).thenReturn(accountDto); + + final ResponseEntity responseEntity = accountController.insertAccount(accountDto, bindingResult); + + assertNotNull(responseEntity); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + assertEquals(HttpStatus.CREATED, responseEntity.getStatusCode()); + assertEquals(accountDto, responseEntity.getBody()); + verify(accountsRepository).save(any(AccountDto.class)); + } + + @Test + public void testInsertAccount_accountsDtoIsNull() { + final ResponseEntity responseEntity = accountController.insertAccount(null, bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(accountsRepository); + } + + @Test + public void testInsertAccount_bindingResultHasErrors() { + final AccountDto definitionDto = createAccount(); + when(bindingResult.hasErrors()).thenReturn(true); + + final ResponseEntity responseEntity = accountController.insertAccount(definitionDto, bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode()); + verifyZeroInteractions(accountsRepository); + } + + @Test + public void testUpdateAccount() { + final AccountDto existingAccount = createAccount(); + existingAccount.setId(1); + existingAccount.setVersion(1); + final AccountDto accountUpdate = new AccountDto(); + accountUpdate.setUsername("Test Update"); + final AccountDto updatedAccount = existingAccount; + updatedAccount.setUsername("Test Update"); + when(accountsRepository.findById(anyInt())).thenReturn(Optional.of(existingAccount)); + when(accountsRepository.save(any(AccountDto.class))).thenReturn(updatedAccount); + + final ResponseEntity responseEntity = accountController.updateAccount(1, accountUpdate, bindingResult); + + assertNotNull(responseEntity); + assertTrue(responseEntity.hasBody()); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals(updatedAccount, responseEntity.getBody()); + verify(accountsRepository).findById(anyInt()); + verify(accountsRepository).save(any(AccountDto.class)); + } + + @Test + public void testUpdateAccount_bindingResultHasErrors() { + when(bindingResult.hasErrors()).thenReturn(true); + + final ResponseEntity responseEntity = accountController.updateAccount(1, createAccount(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode()); + verifyZeroInteractions(accountsRepository); + } + + @Test + public void testUpdateAccount_accountsDtoIsNull() { + final ResponseEntity responseEntity = accountController.updateAccount(1, null, bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(accountsRepository); + } + + @Test + public void testUpdateAccount_idIsNull() { + final ResponseEntity responseEntity = accountController.updateAccount(null, createAccount(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(accountsRepository); + } + + @Test + public void testUpdateAccount_accountDoesNotExist() { + when(accountsRepository.findById(anyInt())).thenReturn(Optional.empty()); + + final ResponseEntity responseEntity = accountController.updateAccount(1, createAccount(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verify(accountsRepository, times(0)).save(any(AccountDto.class)); + } + + @Test + public void testDeleteAccountById() { + doNothing().when(accountsRepository).deleteById(anyInt()); + + final ResponseEntity responseEntity = accountController.deleteAccountById(1); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.NO_CONTENT, responseEntity.getStatusCode()); + verify(accountsRepository).deleteById(anyInt()); + } + + @Test + public void testDeleteAccountById_idIsNull() { + final ResponseEntity responseEntity = accountController.deleteAccountById(null); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(accountsRepository); + } + + private AccountDto createAccount() { + final AccountDto accountDto = new AccountDto(); + accountDto.setUsername("Test username"); + accountDto.setPassword("test password"); + accountDto.setAdministratorStatus(true); + + return accountDto; + } +} \ No newline at end of file diff --git a/pandamonium-theorem-prover/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/DefinitionControllerTest.java b/pandamonium-theorem-prover/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/DefinitionControllerTest.java index df20891..b24d508 100644 --- a/pandamonium-theorem-prover/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/DefinitionControllerTest.java +++ b/pandamonium-theorem-prover/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/DefinitionControllerTest.java @@ -124,6 +124,72 @@ public class DefinitionControllerTest { verifyZeroInteractions(definitionRepository); } + @Test + public void testUpdateDefinition() { + final DefinitionDto existingDefinition = createDefinition(); + existingDefinition.setId(1); + existingDefinition.setVersion(1); + final DefinitionDto definitionUpdate = new DefinitionDto(); + definitionUpdate.setName("Test Update"); + final DefinitionDto updatedDefinition = existingDefinition; + updatedDefinition.setName("Test Update"); + when(definitionRepository.findById(anyInt())).thenReturn(Optional.of(existingDefinition)); + when(definitionRepository.save(any(DefinitionDto.class))).thenReturn(updatedDefinition); + + final ResponseEntity responseEntity = definitionController.updateDefinition(1, definitionUpdate, bindingResult); + + assertNotNull(responseEntity); + assertTrue(responseEntity.hasBody()); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals(updatedDefinition, responseEntity.getBody()); + verify(definitionRepository).findById(anyInt()); + verify(definitionRepository).save(any(DefinitionDto.class)); + } + + @Test + public void testUpdateDefinition_bindingResultErrors() { + when(bindingResult.hasErrors()).thenReturn(true); + + final ResponseEntity responseEntity = definitionController.updateDefinition(1, createDefinition(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode()); + verifyZeroInteractions(definitionRepository); + } + + @Test + public void testUpdateDefinition_definitionDtoIsNull() { + final ResponseEntity responseEntity = definitionController.updateDefinition(1, null, bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(definitionRepository); + } + + @Test + public void testUpdateDefinition_idIsNull() { + final ResponseEntity responseEntity = definitionController.updateDefinition(null, createDefinition(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(definitionRepository); + } + + @Test + public void testUpdateDefinition_definitionDoesntExist() { + when(definitionRepository.findById(anyInt())).thenReturn(Optional.empty()); + + final ResponseEntity responseEntity = definitionController.updateDefinition(1, createDefinition(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verify(definitionRepository, times(0)).save(any(DefinitionDto.class)); + } + @Test public void testDeleteDefinitionById() { doNothing().when(definitionRepository).deleteById(anyInt()); diff --git a/pandamonium-theorem-prover/utilities/build.gradle b/pandamonium-theorem-prover/utilities/build.gradle index 007e3e1..b9e2ce9 100644 --- a/pandamonium-theorem-prover/utilities/build.gradle +++ b/pandamonium-theorem-prover/utilities/build.gradle @@ -18,6 +18,7 @@ repositories { } dependencies { - testCompile group: 'junit', name: 'junit', version: '4.12' compile fileTree(dir: 'lib', include: '**/*.jar') + + testCompile group: 'junit', name: 'junit', version: '4.12' } diff --git a/pandamonium-theorem-prover/utilities/src/main/java/edu/msudenver/tsp/utilities/PersistenceUtilities.java b/pandamonium-theorem-prover/utilities/src/main/java/edu/msudenver/tsp/utilities/PersistenceUtilities.java new file mode 100644 index 0000000..4b39e24 --- /dev/null +++ b/pandamonium-theorem-prover/utilities/src/main/java/edu/msudenver/tsp/utilities/PersistenceUtilities.java @@ -0,0 +1,27 @@ +package edu.msudenver.tsp.utilities; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; + +import java.beans.FeatureDescriptor; +import java.util.stream.Stream; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public abstract class PersistenceUtilities { + + private static String[] getNullPropertyNames(final Object source) { + final BeanWrapper wrappedSource = new BeanWrapperImpl(source); + return Stream.of(wrappedSource.getPropertyDescriptors()) + .map(FeatureDescriptor::getName) + .filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null + || propertyName.equals("id")) + .toArray(String[]::new); + } + + public static void copyNonNullProperties(final Object source, final Object target) { + BeanUtils.copyProperties(source, target, getNullPropertyNames(source)); + } +}