diff --git a/persistence/scripts/mysql/local_development.sql b/persistence/scripts/mysql/local_development.sql index 1f885cc..87984f0 100644 --- a/persistence/scripts/mysql/local_development.sql +++ b/persistence/scripts/mysql/local_development.sql @@ -22,4 +22,14 @@ name varchar(200) not null, definition json not null, notation json, version int default 1 -); \ No newline at end of file +); +create table theorems ( +id int not null auto_increment primary key unique, +name varchar(512) not null, +theorem_type enum ('THEOREM', 'PROPOSITION', 'LEMMA', 'COROLLARY') not null, +branch varchar(512) not null, +referenced_definitions json, +referenced_theorems json, +proven_status boolean default false, +version int default 1 +) \ No newline at end of file diff --git a/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/PersistenceTestConfig.java b/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/PersistenceTestConfig.java index 7427fa2..9680dbe 100644 --- a/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/PersistenceTestConfig.java +++ b/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/PersistenceTestConfig.java @@ -56,7 +56,7 @@ public class PersistenceTestConfig { .create() .username("panda") .password("secret") - .url("jdbc:mysql://127.0.0.1:3306/pandamonium?autoReconnect=true&useSSL=false") + .url("jdbc:mysql://127.0.0.1:3306/pandamonium?autoReconnect=true&useSSL=false&serverTimezone=UTC") .driverClassName("com.mysql.cj.jdbc.Driver") .build(); } diff --git a/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/TheoremsIntegrationTest.java b/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/TheoremsIntegrationTest.java new file mode 100644 index 0000000..a6109f2 --- /dev/null +++ b/persistence/src/integrationTest/java/edu/msudenver/tsp/persistence/TheoremsIntegrationTest.java @@ -0,0 +1,84 @@ +package edu.msudenver.tsp.persistence; + +import edu.msudenver.tsp.persistence.dto.TheoremDto; +import edu.msudenver.tsp.persistence.dto.TheoremType; +import edu.msudenver.tsp.persistence.repository.TheoremRepository; +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.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = PersistenceTestConfig.class) +public class TheoremsIntegrationTest { + @Autowired private TheoremRepository theoremRepository; + + @Test + public void testCRUDFunctionality() { + final TheoremDto theoremDto = createTheorem(); + final TheoremDto savedTheorem = theoremRepository.save(theoremDto); + + assertNotNull(savedTheorem); + assertEquals(Integer.valueOf(0), savedTheorem.getVersion()); + + final int id = savedTheorem.getId(); + + assertEquals("Test theorem", savedTheorem.getName()); + assertEquals("Test branch", savedTheorem.getBranch()); + assertTrue(savedTheorem.getProvenStatus()); + assertEquals(2, savedTheorem.getReferencedTheorems().size()); + assertEquals(2, savedTheorem.getReferencedDefinitions().size()); + assertEquals("test theorem 1", savedTheorem.getReferencedTheorems().get(0)); + assertEquals("test theorem 2", savedTheorem.getReferencedTheorems().get(1)); + assertEquals("test definition 1", savedTheorem.getReferencedDefinitions().get(0)); + assertEquals("test definition 2", savedTheorem.getReferencedDefinitions().get(1)); + + savedTheorem.setBranch("Test Update"); + + final TheoremDto updatedTheorem = theoremRepository.save(savedTheorem); + + assertNotNull(updatedTheorem); + assertEquals(Integer.valueOf(0), updatedTheorem.getVersion()); + assertEquals("Test theorem", updatedTheorem.getName()); + assertEquals("Test Update", updatedTheorem.getBranch()); + assertTrue(updatedTheorem.getProvenStatus()); + assertEquals(2, updatedTheorem.getReferencedTheorems().size()); + assertEquals(2, updatedTheorem.getReferencedDefinitions().size()); + assertEquals("test theorem 1", updatedTheorem.getReferencedTheorems().get(0)); + assertEquals("test theorem 2", updatedTheorem.getReferencedTheorems().get(1)); + assertEquals("test definition 1", updatedTheorem.getReferencedDefinitions().get(0)); + assertEquals("test definition 2", updatedTheorem.getReferencedDefinitions().get(1)); + assertEquals(updatedTheorem.getId(), id); + + theoremRepository.delete(theoremDto); + final Optional deletedTheorem = theoremRepository.findById(id); + assertFalse(deletedTheorem.isPresent()); + } + + private TheoremDto createTheorem() { + final List referencedTheoremsList = new ArrayList<>(); + referencedTheoremsList.add("test theorem 1"); + referencedTheoremsList.add("test theorem 2"); + + final List referencedDefinitionsList = new ArrayList<>(); + referencedDefinitionsList.add("test definition 1"); + referencedDefinitionsList.add("test definition 2"); + + final TheoremDto theoremDto = new TheoremDto(); + theoremDto.setName("Test theorem"); + theoremDto.setBranch("Test branch"); + theoremDto.setProvenStatus(true); + theoremDto.setTheoremType(TheoremType.THEOREM); + theoremDto.setReferencedTheorems(referencedTheoremsList); + theoremDto.setReferencedDefinitions(referencedDefinitionsList); + + return theoremDto; + } +} diff --git a/persistence/src/main/java/edu/msudenver/tsp/persistence/PersistenceApi.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/PersistenceApi.java index 39a3f11..f309965 100644 --- a/persistence/src/main/java/edu/msudenver/tsp/persistence/PersistenceApi.java +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/PersistenceApi.java @@ -105,5 +105,6 @@ public class PersistenceApi { properties.setProperty("hibernate.id.new_generator_mappings","false"); return properties; + } } diff --git a/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/AccountController.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/AccountController.java index 25f5368..2f60c56 100644 --- a/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/AccountController.java +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/AccountController.java @@ -14,6 +14,8 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.groups.Default; +import java.time.Duration; +import java.time.Instant; import java.util.List; import java.util.Optional; @@ -73,6 +75,35 @@ public class AccountController { ); } + @GetMapping("/{username}") + public @ResponseBody + ResponseEntity getAccountByUsername(@PathVariable("username") final String username) { + LOG.info("Received request to query for account with username " + username); + if (username == null) { + LOG.error("ERROR: username was null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Querying for account with username " + username); + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final Optional account = accountsRepository.findByUsername(username); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + return account.map(accountDto -> { + LOG.info("Returning account with username " + username); + return new ResponseEntity<>(accountDto, HttpStatus.OK); + }).orElseGet( + () -> { + LOG.warn("No account was found with username " + username); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + ); + } + @PostMapping("/") @Validated({AccountDto.Insert.class, Default.class}) public @ResponseBody ResponseEntity insertAccount( @@ -89,6 +120,21 @@ public class AccountController { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } + LOG.info("Checking for any existing users with username {}", accountDto.getUsername()); + + final Instant start = Instant.now(); + + LOG.debug("Querying for existing accounts"); + + final Optional existingAccount = accountsRepository.findByUsername(accountDto.getUsername()); + + LOG.debug("Received response from the server: query took {} ms", Duration.between(start, Instant.now()).toMillis()); + + if (existingAccount.isPresent()) { + LOG.warn("An account already exists with username {}", accountDto.getUsername()); + return new ResponseEntity<>(HttpStatus.CONFLICT); + } + LOG.debug("Saving new account"); final StopWatch stopWatch = new StopWatch(); diff --git a/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/DefinitionController.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/DefinitionController.java index 3b788a5..3c8b4d1 100644 --- a/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/DefinitionController.java +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/DefinitionController.java @@ -20,7 +20,7 @@ import java.util.Optional; @Slf4j @RestController @AllArgsConstructor -@RequestMapping(path = "/definitions/") +@RequestMapping(path = "/definitions") public class DefinitionController { private final DefinitionRepository definitionRepository; @@ -85,7 +85,7 @@ public class DefinitionController { } if (definitionDto == null) { - LOG.error("Passed entity is unprocessable"); + LOG.error("Passed entity is null"); return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } diff --git a/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/TheoremController.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/TheoremController.java index 9c26063..9a0f699 100644 --- a/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/TheoremController.java +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/controller/TheoremController.java @@ -1,11 +1,238 @@ package edu.msudenver.tsp.persistence.controller; +import edu.msudenver.tsp.persistence.dto.TheoremDto; import edu.msudenver.tsp.persistence.repository.TheoremRepository; +import edu.msudenver.tsp.utilities.PersistenceUtilities; import lombok.AllArgsConstructor; -import org.springframework.stereotype.Component; +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.web.bind.annotation.*; -@Component +import javax.validation.Valid; +import java.util.List; +import java.util.Optional; + +@Slf4j +@RestController @AllArgsConstructor +@RequestMapping(path = "/theorems") public class TheoremController { private final TheoremRepository theoremRepository; + + @GetMapping("/") + public @ResponseBody + ResponseEntity> getAllTheorems() { + LOG.info("Received request to list all theorems"); + + LOG.debug("Querying for list of all theorems"); + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final List listOfTheorems = theoremRepository.findAll(); + + stopWatch.stop(); + + LOG.debug("Successfully completed query. Query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + LOG.info("Returning list of all theorems with size " + listOfTheorems.size()); + + return new ResponseEntity<>(listOfTheorems, HttpStatus.OK); + } + + @GetMapping("/{branch}") + public @ResponseBody + ResponseEntity> getAllTheoremsByBranch(@PathVariable("branch") final String branch) { + LOG.info("Received request to query for theorems related to the " + branch + " branch of mathematics"); + if (branch == null) { + LOG.error("ERROR: branch was null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Querying for theorems with branch " + branch); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final List listOfTheorems = theoremRepository.findByBranch(branch); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + LOG.info("Returning list of all theorems with size " + listOfTheorems.size()); + + if (listOfTheorems.isEmpty()) { + LOG.warn("No theorems were found for branch {}", branch); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + LOG.info("Returning list of theorems with branch {}", branch); + return new ResponseEntity<>(listOfTheorems, HttpStatus.OK); + } + + @GetMapping("/{proven_status}") + public @ResponseBody + ResponseEntity> getAllTheoremsByProvenStatus(@PathVariable("proven_status") final Boolean provenStatus) { + LOG.info("Received request to query for theorems whose proven status is " + provenStatus); + if (provenStatus == null) { + LOG.error("ERROR: status was null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Querying for theorems with proven status " + provenStatus); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final List listOfTheorems = theoremRepository.findByProvenStatus(provenStatus); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + LOG.info("Returning list of all theorems with size " + listOfTheorems.size()); + + if (listOfTheorems.isEmpty()) { + LOG.warn("No theorems were found for proven status {}", provenStatus); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + LOG.info("Returning list of theorems with proven status {}", provenStatus); + return new ResponseEntity<>(listOfTheorems, HttpStatus.OK); + } + + @GetMapping("/{id}") + public @ResponseBody + ResponseEntity getTheoremById(@PathVariable("id") final Integer id) { + LOG.info("Received request to query for theorem with id " + id); + if (id == null) { + LOG.error("ERROR: ID was null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Querying for theorem with id " + id); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final Optional theorem = theoremRepository.findById(id); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + return theorem.map(theoremDto -> { + LOG.info("Returning theorem with id " + id); + return new ResponseEntity<>(theoremDto, HttpStatus.OK); + }).orElseGet( + () -> { + LOG.warn("No theorem was found with id " + id); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + }); + + } + + @PostMapping("/") + public @ResponseBody ResponseEntity insertTheorem( + @Valid @RequestBody final TheoremDto theoremDto, + final BindingResult bindingResult) { + LOG.info("Received request to insert a new theorem"); + if (bindingResult.hasErrors()) { + LOG.error("Binding result is unprocessable"); + return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); + } + + if (theoremDto == null) { + LOG.error("Passed entity is null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Saving new theorem"); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final TheoremDto savedTheorem = theoremRepository.save(theoremDto); + + stopWatch.stop(); + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + + LOG.info("Returning the newly created theorem with id " + savedTheorem.getId()); + return new ResponseEntity<>(savedTheorem, HttpStatus.CREATED); + } + + @PatchMapping("/{id}") + public @ResponseBody ResponseEntity updateTheorem( + @PathVariable("id") final Integer id, + @RequestBody final TheoremDto theoremDto, 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 (theoremDto == null) { + LOG.error("Passed entity is null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + if (id == null) { + LOG.error("Theorem ID must be specified"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Checking for existence of theorem with id " + id); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + final Optional existingTheorem = theoremRepository.findById(id); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + + if (!existingTheorem.isPresent()) { + LOG.error("No theorem associated with id " + id); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + PersistenceUtilities.copyNonNullProperties(theoremDto, existingTheorem.get()); + existingTheorem.get().setVersion(existingTheorem.get().getVersion()+ 1); + + LOG.info("Updating theorem with id " + id); + LOG.debug("Querying for theorem with ID " + id); + + stopWatch.start(); + + final TheoremDto updatedTheorem = theoremRepository.save(existingTheorem.get()); + + stopWatch.stop(); + + LOG.debug("Received response from server: query took " + stopWatch.getTotalTimeMillis() + "ms to complete"); + + return new ResponseEntity<>(updatedTheorem, HttpStatus.OK); + } + + @DeleteMapping("/{id}") + public @ResponseBody ResponseEntity deleteTheoremById(@PathVariable("id") final Integer id) { + LOG.info("Received request to delete theorem with id " + id); + if (id == null) { + LOG.error("Specified id is null"); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + LOG.debug("Deleting theorem with id " + id); + + final StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + theoremRepository.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/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountDto.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountDto.java index 882826c..2f71c71 100644 --- a/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountDto.java +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/AccountDto.java @@ -5,10 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import javax.persistence.Entity; -import javax.persistence.EntityListeners; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; +import javax.persistence.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @@ -22,8 +19,8 @@ import java.util.Date; 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 private boolean administratorStatus; - @Temporal(TemporalType.DATE) private Date lastLogin; + @NotNull @Column(name = "administrator_status") private boolean administratorStatus; + @Temporal(TemporalType.DATE) @Column(name = "last_login") private Date lastLogin; private static final long serialVersionUID = 7095627971593953734L; @@ -48,4 +45,9 @@ public class AccountDto extends BaseDto implements Serializable { } public interface Insert {} + + @PrePersist + public void prePersist() { + lastLogin = new Date(); + } } diff --git a/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/TheoremDto.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/TheoremDto.java index 0761432..6c48e32 100644 --- a/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/TheoremDto.java +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/TheoremDto.java @@ -1,4 +1,71 @@ package edu.msudenver.tsp.persistence.dto; -public class TheoremDto extends BaseDto { +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.List; + +@Entity(name = "theorems") +@EntityListeners(AuditingEntityListener.class) +@Data +@EqualsAndHashCode(callSuper = true) +public class TheoremDto extends BaseDto implements Serializable { + @NotBlank @Size(min = 1, max = 512, message = "theorem name must be between 1 and 512 characters") private String name; + @NotNull @Column(name = "theorem_type") private TheoremType theoremType; + @NotNull(message = "a branch of mathematics that this theorem is associated with must be specified") private String branch; + @Type(type = "json") @Column(name = "referenced_definitions", columnDefinition = "jsonb") private List referencedDefinitions; + @Type(type = "json") @Column(name = "referenced_theorems", columnDefinition = "jsonb") private List referencedTheorems; + @NotNull @Column(name = "proven_status") private boolean provenStatus; + + @JsonProperty("theorem_type") + public TheoremType getTheoremType() { + return theoremType; + } + + @JsonProperty("theorem_type") + public void setTheoremType(final TheoremType theoremType) { + this.theoremType = theoremType; + } + + @JsonProperty("referenced_definitions") + public List getReferencedDefinitions() { + return referencedDefinitions; + } + + @JsonProperty("referenced_definitions") + public void setReferencedDefinitions(final List referencedDefinitions) { + this.referencedDefinitions = referencedDefinitions; + } + + @JsonProperty("referenced_theorems") + public List getReferencedTheorems() { + return referencedTheorems; + } + + @JsonProperty("referenced_theorems") + public void setReferencedTheorems(final List referencedTheorems) { + this.referencedTheorems = referencedTheorems; + } + + @JsonProperty("proven_status") + public boolean getProvenStatus() { + return provenStatus; + } + + @JsonProperty("proven_status") + public void setProvenStatus(final boolean provenStatus) { + this.provenStatus = provenStatus; + } + + private static final long serialVersionUID = 1545568391140364425L; } diff --git a/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/TheoremType.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/TheoremType.java new file mode 100644 index 0000000..c69b4e7 --- /dev/null +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/dto/TheoremType.java @@ -0,0 +1,5 @@ +package edu.msudenver.tsp.persistence.dto; + +public enum TheoremType { + THEOREM, PROPOSITION, LEMMA, COROLLARY +} diff --git a/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/AccountsRepository.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/AccountsRepository.java index 454cd77..add1182 100644 --- a/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/AccountsRepository.java +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/AccountsRepository.java @@ -4,6 +4,9 @@ import edu.msudenver.tsp.persistence.dto.AccountDto; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; +import java.util.Optional; + @Repository public interface AccountsRepository extends CrudRepository { + Optional findByUsername(String username); } diff --git a/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/TheoremRepository.java b/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/TheoremRepository.java index c4237ce..75df2ee 100644 --- a/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/TheoremRepository.java +++ b/persistence/src/main/java/edu/msudenver/tsp/persistence/repository/TheoremRepository.java @@ -1,7 +1,15 @@ package edu.msudenver.tsp.persistence.repository; -import edu.msudenver.tsp.persistence.dto.BaseDto; -import org.springframework.data.repository.CrudRepository; +import edu.msudenver.tsp.persistence.dto.TheoremDto; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; -public interface TheoremRepository extends CrudRepository { +import java.util.List; + +@Repository +public interface TheoremRepository extends JpaRepository { + + List findByBranch(String branch); + + List findByProvenStatus(Boolean provenStatus); } 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 0b88630..05a6ba9 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 @@ -47,7 +47,7 @@ public class AccountControllerTest { assertTrue(responseEntity.hasBody()); assertNotNull(responseEntity.getBody()); - responseEntity.getBody().forEach(account -> assertEquals(account, accountDto)); + responseEntity.getBody().forEach(account -> assertEquals(accountDto, account)); } @Test @@ -87,10 +87,48 @@ public class AccountControllerTest { verify(accountsRepository).findById(anyInt()); } + @Test + public void testGetAccountByUsername() { + final AccountDto accountDto = createAccount(); + when(accountsRepository.findByUsername(anyString())).thenReturn(Optional.ofNullable(accountDto)); + + final ResponseEntity responseEntity = accountController.getAccountByUsername("Test username"); + + assertNotNull(responseEntity); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals(accountDto, responseEntity.getBody()); + verify(accountsRepository).findByUsername(anyString()); + } + + @Test + public void testGetAccountById_nullUsername() { + final ResponseEntity responseEntity = accountController.getAccountByUsername(null); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(accountsRepository); + } + + @Test + public void testGetAccountByUsername_noAccountFound() { + when(accountsRepository.findByUsername(anyString())).thenReturn(Optional.empty()); + + final ResponseEntity responseEntity = accountController.getAccountByUsername("Test username"); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode()); + verify(accountsRepository).findByUsername(anyString()); + } + @Test public void testInsertAccount() { final AccountDto accountDto = createAccount(); when(accountsRepository.save(any(AccountDto.class))).thenReturn(accountDto); + when(accountsRepository.findByUsername(anyString())).thenReturn(Optional.empty()); final ResponseEntity responseEntity = accountController.insertAccount(accountDto, bindingResult); @@ -102,6 +140,20 @@ public class AccountControllerTest { verify(accountsRepository).save(any(AccountDto.class)); } + @Test + public void testInsertAccount_usernameAlreadyExists() { + final AccountDto accountDto = createAccount(); + when(accountsRepository.findByUsername(anyString())).thenReturn(Optional.of(accountDto)); + + final ResponseEntity responseEntity = accountController.insertAccount(accountDto, bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.CONFLICT, responseEntity.getStatusCode()); + verify(accountsRepository).findByUsername(anyString()); + verify(accountsRepository, times(0)).save(any(AccountDto.class)); + } + @Test public void testInsertAccount_accountsDtoIsNull() { final ResponseEntity responseEntity = accountController.insertAccount(null, bindingResult); @@ -114,10 +166,10 @@ public class AccountControllerTest { @Test public void testInsertAccount_bindingResultHasErrors() { - final AccountDto definitionDto = createAccount(); + final AccountDto accountDto = createAccount(); when(bindingResult.hasErrors()).thenReturn(true); - final ResponseEntity responseEntity = accountController.insertAccount(definitionDto, bindingResult); + final ResponseEntity responseEntity = accountController.insertAccount(accountDto, bindingResult); assertNotNull(responseEntity); assertFalse(responseEntity.hasBody()); diff --git a/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/DefinitionControllerTest.java b/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/DefinitionControllerTest.java index b24d508..e28cbb8 100644 --- a/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/DefinitionControllerTest.java +++ b/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/DefinitionControllerTest.java @@ -46,7 +46,7 @@ public class DefinitionControllerTest { assertTrue(responseEntity.hasBody()); assertNotNull(responseEntity.getBody()); - responseEntity.getBody().forEach(definition -> assertEquals(definition, definitionDto)); + responseEntity.getBody().forEach(definition -> assertEquals(definitionDto, definition)); } @Test diff --git a/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/TheoremControllerTest.java b/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/TheoremControllerTest.java new file mode 100644 index 0000000..f3993cc --- /dev/null +++ b/persistence/src/test/java/edu/msudenver/tsp/persistence/controller/TheoremControllerTest.java @@ -0,0 +1,314 @@ +package edu.msudenver.tsp.persistence.controller; + +import edu.msudenver.tsp.persistence.dto.TheoremDto; +import edu.msudenver.tsp.persistence.dto.TheoremType; +import edu.msudenver.tsp.persistence.repository.TheoremRepository; +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.Collections; +import java.util.List; +import java.util.Optional; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +@WebMvcTest(controllers = TheoremController.class) +public class TheoremControllerTest { + @Mock private TheoremRepository theoremRepository; + @InjectMocks private TheoremController theoremController; + @Mock private BindingResult bindingResult; + + @Test + public void testGetAllTheorems() { + final TheoremDto theoremDto = createTheorem(); + final List listOfTheorems = new ArrayList<>(); + listOfTheorems.add(theoremDto); + listOfTheorems.add(theoremDto); + + when(theoremRepository.findAll()).thenReturn(listOfTheorems); + + final ResponseEntity> responseEntity = theoremController.getAllTheorems(); + + assertNotNull(responseEntity); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + + responseEntity.getBody().forEach(theorem -> assertEquals(theoremDto, theorem)); + } + + @Test + public void testGetTheoremById() { + final TheoremDto theoremDto = createTheorem(); + when(theoremRepository.findById(anyInt())).thenReturn(Optional.ofNullable(theoremDto)); + + final ResponseEntity responseEntity = theoremController.getTheoremById(1); + + assertNotNull(responseEntity); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals(theoremDto, responseEntity.getBody()); + verify(theoremRepository).findById(anyInt()); + } + + @Test + public void testGetTheoremById_nullId() { + final ResponseEntity responseEntity = theoremController.getTheoremById(null); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + @Test + public void testGetTheoremById_noTheoremFound() { + when(theoremRepository.findById(anyInt())).thenReturn(Optional.empty()); + + final ResponseEntity responseEntity = theoremController.getTheoremById(1); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode()); + verify(theoremRepository).findById(anyInt()); + } + + @Test + public void testGetAllTheoremsByBranch() { + final TheoremDto theoremDto = createTheorem(); + final List listOfTheorems = new ArrayList<>(); + listOfTheorems.add(theoremDto); + listOfTheorems.add(theoremDto); + + when(theoremRepository.findByBranch(anyString())).thenReturn(listOfTheorems); + + final ResponseEntity> responseEntity = theoremController.getAllTheoremsByBranch("test"); + + assertNotNull(responseEntity); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + + responseEntity.getBody().forEach(theorem -> assertEquals(theoremDto, theorem)); + } + + @Test + public void testGetAllTheoremsByBranch_nullBranch() { + final ResponseEntity> responseEntity = theoremController.getAllTheoremsByBranch(null); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + @Test + public void testGetAllTheoremsByBranch_noTheoremsFound() { + when(theoremRepository.findByBranch(anyString())).thenReturn(Collections.emptyList()); + + final ResponseEntity> responseEntity = theoremController.getAllTheoremsByBranch("test nonexistent branch"); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode()); + } + + @Test + public void testGetAllTheoremsByProvenStatus() { + final TheoremDto theoremDto = createTheorem(); + final List listOfTheorems = new ArrayList<>(); + listOfTheorems.add(theoremDto); + listOfTheorems.add(theoremDto); + + when(theoremRepository.findByProvenStatus(anyBoolean())).thenReturn(listOfTheorems); + + final ResponseEntity> responseEntity = theoremController.getAllTheoremsByProvenStatus(true); + + assertNotNull(responseEntity); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + + responseEntity.getBody().forEach(theorem -> assertEquals(theoremDto, theorem)); + } + + @Test + public void testGetAllTheoremsByProvenStatus_nullProvenStatus() { + final ResponseEntity> responseEntity = theoremController.getAllTheoremsByProvenStatus(null); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + @Test + public void testGetAllTheoremsByProvenStatus_noTheoremsFound() { + when(theoremRepository.findByProvenStatus(anyBoolean())).thenReturn(Collections.emptyList()); + + final ResponseEntity> responseEntity = theoremController.getAllTheoremsByProvenStatus(false); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode()); + } + + @Test + public void testInsertTheorem() { + final TheoremDto theoremDto = createTheorem(); + when(theoremRepository.save(any(TheoremDto.class))).thenReturn(theoremDto); + + final ResponseEntity responseEntity = theoremController.insertTheorem(theoremDto, bindingResult); + + assertNotNull(responseEntity); + assertTrue(responseEntity.hasBody()); + assertNotNull(responseEntity.getBody()); + assertEquals(HttpStatus.CREATED, responseEntity.getStatusCode()); + assertEquals(theoremDto, responseEntity.getBody()); + verify(theoremRepository).save(any(TheoremDto.class)); + } + + @Test + public void testInsertTheorem_theoremDtoIsNull() { + final ResponseEntity responseEntity = theoremController.insertTheorem(null, bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + @Test + public void testInsertTheorem_bindingResultHasErrors() { + final TheoremDto theoremDto = createTheorem(); + when(bindingResult.hasErrors()).thenReturn(true); + + final ResponseEntity responseEntity = theoremController.insertTheorem(theoremDto, bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + @Test + public void testUpdateTheorem() { + final TheoremDto existingTheorem = createTheorem(); + existingTheorem.setId(1); + existingTheorem.setVersion(1); + final TheoremDto theoremUpdate = new TheoremDto(); + theoremUpdate.setName("Test Update"); + final TheoremDto updatedTheorem = existingTheorem; + updatedTheorem.setName("Test Update"); + when(theoremRepository.findById(anyInt())).thenReturn(Optional.of(existingTheorem)); + when(theoremRepository.save(any(TheoremDto.class))).thenReturn(updatedTheorem); + + final ResponseEntity responseEntity = theoremController.updateTheorem(1, theoremUpdate, bindingResult); + + assertNotNull(responseEntity); + assertTrue(responseEntity.hasBody()); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + assertEquals(updatedTheorem, responseEntity.getBody()); + verify(theoremRepository).findById(anyInt()); + verify(theoremRepository).save(any(TheoremDto.class)); + } + + @Test + public void testUpdateTheorem_bindingResultHasErrors() { + when(bindingResult.hasErrors()).thenReturn(true); + + final ResponseEntity responseEntity = theoremController.updateTheorem(1, createTheorem(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + @Test + public void testUpdateTheorem_theoremDtoIsNull() { + final ResponseEntity responseEntity = theoremController.updateTheorem(1, null, bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + @Test + public void testUpdateTheorem_idIsNull() { + final ResponseEntity responseEntity = theoremController.updateTheorem(null, createTheorem(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + @Test + public void testUpdateTheorem_theoremDoesNotExist() { + when(theoremRepository.findById(anyInt())).thenReturn(Optional.empty()); + + final ResponseEntity responseEntity = theoremController.updateTheorem(1, createTheorem(), bindingResult); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verify(theoremRepository, times(0)).save(any(TheoremDto.class)); + } + + @Test + public void testDeleteTheoremById() { + doNothing().when(theoremRepository).deleteById(anyInt()); + + final ResponseEntity responseEntity = theoremController.deleteTheoremById(1); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.NO_CONTENT, responseEntity.getStatusCode()); + verify(theoremRepository).deleteById(anyInt()); + } + + @Test + public void testDeleteTheoremById_idIsNull() { + final ResponseEntity responseEntity = theoremController.deleteTheoremById(null); + + assertNotNull(responseEntity); + assertFalse(responseEntity.hasBody()); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + verifyZeroInteractions(theoremRepository); + } + + private TheoremDto createTheorem() { + final List referencedTheoremsList = new ArrayList<>(); + referencedTheoremsList.add("test theorem 1"); + referencedTheoremsList.add("test theorem 2"); + + final List referencedDefinitionsList = new ArrayList<>(); + referencedDefinitionsList.add("test definition 1"); + referencedDefinitionsList.add("test definition 2"); + + final TheoremDto theoremDto = new TheoremDto(); + theoremDto.setName("Test theorem"); + theoremDto.setBranch("Test branch"); + theoremDto.setProvenStatus(true); + theoremDto.setTheoremType(TheoremType.THEOREM); + theoremDto.setReferencedTheorems(referencedTheoremsList); + theoremDto.setReferencedDefinitions(referencedDefinitionsList); + + return theoremDto; + } +} \ No newline at end of file