diff --git a/EntitiesModel.puml b/EntitiesModel.puml index 08d033d7..876180c4 100644 --- a/EntitiesModel.puml +++ b/EntitiesModel.puml @@ -90,6 +90,11 @@ class MedicalRecord { date : Date } +class FavouritedPets { + id : Long + petId : Long +} + Pet "1" -- "*" MedicalRecord : has > @@ -104,4 +109,5 @@ Client "*" -> "*" Shelter : donate > Adoption "*" -> "1" Pet :adopted > Adoption "*" -> "1" User :adopt < ShelterVolunteer "1.*" -> "*" Shelter : works > +User "1" -> "*" FavouritedPets : hasFavourited > @enduml \ No newline at end of file diff --git a/src/main/java/cat/udl/eps/softarch/demo/controller/UserController.java b/src/main/java/cat/udl/eps/softarch/demo/controller/UserController.java new file mode 100644 index 00000000..2d41c428 --- /dev/null +++ b/src/main/java/cat/udl/eps/softarch/demo/controller/UserController.java @@ -0,0 +1,42 @@ +package cat.udl.eps.softarch.demo.controller; + +import cat.udl.eps.softarch.demo.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +public class UserController { + + @Autowired + UserRepository userRepository; + + @PreAuthorize("hasRole('ROLE_ADMIN')") + @PostMapping("/users/{username}/lock") + public ResponseEntity lockUser(@PathVariable String username) { + // If the user is present, lock it and save it. If not, return 404. + return this.userRepository.findById(username) + .map(user -> { + user.lock(); + this.userRepository.save(user); + return ResponseEntity.ok().build(); + }) + .orElse(ResponseEntity.notFound().build()); + } + + @PreAuthorize("hasRole('ROLE_ADMIN')") + @PostMapping("/users/{username}/unlock") + public ResponseEntity unlockUser(@PathVariable String username) { + // If the user is present, unlock it and save it. If not, return 404. + return this.userRepository.findById(username) + .map(user -> { + user.unlock(); + this.userRepository.save(user); + return ResponseEntity.ok().build(); + }) + .orElse(ResponseEntity.notFound().build()); + } +} diff --git a/src/main/java/cat/udl/eps/softarch/demo/domain/FavouritedPets.java b/src/main/java/cat/udl/eps/softarch/demo/domain/FavouritedPets.java new file mode 100644 index 00000000..7fa9eb4f --- /dev/null +++ b/src/main/java/cat/udl/eps/softarch/demo/domain/FavouritedPets.java @@ -0,0 +1,18 @@ +package cat.udl.eps.softarch.demo.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Entity +@Data +public class FavouritedPets extends UriEntity { + @Id + @NotNull + Long id; + + Long petId; +} diff --git a/src/main/java/cat/udl/eps/softarch/demo/domain/MedicalRecord.java b/src/main/java/cat/udl/eps/softarch/demo/domain/MedicalRecord.java index 1d83c87f..e3890d01 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/domain/MedicalRecord.java +++ b/src/main/java/cat/udl/eps/softarch/demo/domain/MedicalRecord.java @@ -1,5 +1,7 @@ package cat.udl.eps.softarch.demo.domain; +import com.fasterxml.jackson.annotation.JsonIdentityReference; +import com.fasterxml.jackson.annotation.JsonInclude; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -14,7 +16,7 @@ public class MedicalRecord extends UriEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue() private Long id; @NotBlank @@ -27,5 +29,7 @@ public class MedicalRecord extends UriEntity { private ZonedDateTime date; @ManyToOne + @JsonIdentityReference(alwaysAsId = true) // Only serialize the pet ID + @JsonInclude(JsonInclude.Include.NON_NULL) // Include pet only if it's not null private Pet pet; } \ No newline at end of file diff --git a/src/main/java/cat/udl/eps/softarch/demo/domain/Pet.java b/src/main/java/cat/udl/eps/softarch/demo/domain/Pet.java index cd82c311..23aeaf09 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/domain/Pet.java +++ b/src/main/java/cat/udl/eps/softarch/demo/domain/Pet.java @@ -12,6 +12,7 @@ public class Pet extends UriEntity { @GeneratedValue Long id; + String name; boolean isAdopted; String color; diff --git a/src/main/java/cat/udl/eps/softarch/demo/domain/ShelterCertificate.java b/src/main/java/cat/udl/eps/softarch/demo/domain/ShelterCertificate.java index 51d6a760..101553ad 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/domain/ShelterCertificate.java +++ b/src/main/java/cat/udl/eps/softarch/demo/domain/ShelterCertificate.java @@ -3,10 +3,7 @@ import java.time.ZonedDateTime; import com.fasterxml.jackson.annotation.JsonIdentityReference; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; @@ -24,6 +21,9 @@ public class ShelterCertificate extends UriEntity { @NotNull private ZonedDateTime expirationDate; + @NotNull + private Boolean validated; + @JsonIdentityReference(alwaysAsId = true) @ManyToOne private Shelter shelterServed; diff --git a/src/main/java/cat/udl/eps/softarch/demo/domain/User.java b/src/main/java/cat/udl/eps/softarch/demo/domain/User.java index a7e9fa07..e07f5637 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/domain/User.java +++ b/src/main/java/cat/udl/eps/softarch/demo/domain/User.java @@ -1,12 +1,10 @@ package cat.udl.eps.softarch.demo.domain; import com.fasterxml.jackson.annotation.JsonProperty; - +import com.fasterxml.jackson.annotation.JsonValue; import jakarta.persistence.*; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; - -import com.fasterxml.jackson.annotation.JsonValue; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.validator.constraints.Length; @@ -43,10 +41,21 @@ public class User extends UriEntity implements UserDetails { @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) private boolean passwordReset; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private boolean locked; + public void encodePassword() { this.password = passwordEncoder.encode(this.password); } + public void lock() { + this.locked = true; + } + + public void unlock() { + this.locked = false; + } + @Override public String getUsername() { return id; } @@ -64,7 +73,7 @@ public boolean isAccountNonExpired() { @Override public boolean isAccountNonLocked() { - return true; + return !locked; } @Override @@ -77,7 +86,7 @@ public boolean isEnabled() { return true; } - @ManyToMany - public List favouritedPets; + @OneToMany(fetch = FetchType.EAGER) + public List favouritedPets; } diff --git a/src/main/java/cat/udl/eps/softarch/demo/exceptions/UnauthorizedAccessException.java b/src/main/java/cat/udl/eps/softarch/demo/exceptions/UnauthorizedAccessException.java index 78308298..f92e9c12 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/exceptions/UnauthorizedAccessException.java +++ b/src/main/java/cat/udl/eps/softarch/demo/exceptions/UnauthorizedAccessException.java @@ -4,4 +4,4 @@ import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(code = HttpStatus.FORBIDDEN, reason = "Unauthorized access") -public class UnauthorizedAccessException extends RuntimeException {} \ No newline at end of file +public class UnauthorizedAccessException extends RuntimeException {} diff --git a/src/main/java/cat/udl/eps/softarch/demo/handler/MedicalRecordEventHandler.java b/src/main/java/cat/udl/eps/softarch/demo/handler/MedicalRecordEventHandler.java new file mode 100644 index 00000000..392ff37f --- /dev/null +++ b/src/main/java/cat/udl/eps/softarch/demo/handler/MedicalRecordEventHandler.java @@ -0,0 +1,76 @@ +package cat.udl.eps.softarch.demo.handler; + +import cat.udl.eps.softarch.demo.domain.MedicalRecord; +import cat.udl.eps.softarch.demo.exceptions.UnauthorizedAccessException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.rest.core.annotation.HandleAfterSave; +import org.springframework.data.rest.core.annotation.HandleBeforeCreate; +import org.springframework.data.rest.core.annotation.HandleBeforeSave; +import org.springframework.data.rest.core.annotation.RepositoryEventHandler; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +@Component +@RepositoryEventHandler() // Ensure this handler is for MedicalRecord entity +public class MedicalRecordEventHandler { + + private static final Logger logger = LoggerFactory.getLogger(MedicalRecordEventHandler.class); + + private static final String ROLE_SHELTER_VOLUNTEER = "ROLE_SHELTER_VOLUNTEER"; + private static final String ROLE_ADMIN = "ROLE_ADMIN"; + + /** + * Handles actions before creating a medical record. + * @param medicalRecord the medical record to be created + * @throws UnauthorizedAccessException if the user is not authorized to create a medical record + */ + @HandleBeforeCreate + public void handleMedicalRecordBeforeCreate(MedicalRecord medicalRecord) throws UnauthorizedAccessException { + checkAuthorization(); + logger.info("Authorized creation of a new medical record by user: {}", getCurrentUsername()); + } + + /** + * Handles actions before saving (updating) a medical record. + */ + @HandleBeforeSave + public void handleMedicalRecordBeforeSave(MedicalRecord medicalRecord) throws UnauthorizedAccessException { + checkAuthorization(); + logger.info("Authorized save of medical record by user: {}", getCurrentUsername()); + } + + /** + * Handles actions after saving a medical record. + * @param medicalRecord the saved medical record + */ + @HandleAfterSave + public void handleMedicalRecordPostSave(MedicalRecord medicalRecord) { + logger.info("Medical record for pet {} saved successfully", medicalRecord.getPet().getName()); + } + + private void checkAuthorization() throws UnauthorizedAccessException { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (!isAuthorized(authentication)) { + throw new UnauthorizedAccessException(); + } + } + + private boolean isAuthorized(Authentication authentication) { + if (authentication == null || !authentication.isAuthenticated()) { + return false; + } + List requiredAuthorities = Arrays.asList(ROLE_SHELTER_VOLUNTEER, ROLE_ADMIN); + return authentication.getAuthorities().stream() + .anyMatch(grantedAuthority -> requiredAuthorities.contains(grantedAuthority.getAuthority())); + } + + private String getCurrentUsername() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return authentication != null ? authentication.getName() : "anonymous"; + } +} diff --git a/src/main/java/cat/udl/eps/softarch/demo/repository/MedicalRecordRepository.java b/src/main/java/cat/udl/eps/softarch/demo/repository/MedicalRecordRepository.java new file mode 100644 index 00000000..3d829869 --- /dev/null +++ b/src/main/java/cat/udl/eps/softarch/demo/repository/MedicalRecordRepository.java @@ -0,0 +1,16 @@ +package cat.udl.eps.softarch.demo.repository; + +import cat.udl.eps.softarch.demo.domain.MedicalRecord; +import cat.udl.eps.softarch.demo.domain.Pet; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; + +import java.util.List; + +@RepositoryRestResource +public interface MedicalRecordRepository extends CrudRepository < MedicalRecord, Long > , PagingAndSortingRepository < MedicalRecord, Long > { + + List < MedicalRecord > findByPet(@Param("pet") Pet pet); +} \ No newline at end of file diff --git a/src/main/java/cat/udl/eps/softarch/demo/repository/ShelterCertificateRepository.java b/src/main/java/cat/udl/eps/softarch/demo/repository/ShelterCertificateRepository.java index 114d4322..2f498541 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/repository/ShelterCertificateRepository.java +++ b/src/main/java/cat/udl/eps/softarch/demo/repository/ShelterCertificateRepository.java @@ -3,7 +3,9 @@ import cat.udl.eps.softarch.demo.domain.ShelterCertificate; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; +import cat.udl.eps.softarch.demo.domain.Shelter; +import org.springframework.data.repository.query.Param; public interface ShelterCertificateRepository extends CrudRepository, PagingAndSortingRepository { - + ShelterCertificate findByShelterServed(@Param("shelterServed") Shelter shelter); } diff --git a/src/test/java/cat/udl/eps/softarch/demo/steps/AddMedicalRecordStepDefs.java b/src/test/java/cat/udl/eps/softarch/demo/steps/AddMedicalRecordStepDefs.java new file mode 100644 index 00000000..9d3fe53b --- /dev/null +++ b/src/test/java/cat/udl/eps/softarch/demo/steps/AddMedicalRecordStepDefs.java @@ -0,0 +1,89 @@ +package cat.udl.eps.softarch.demo.steps; + +import cat.udl.eps.softarch.demo.domain.MedicalRecord; +import cat.udl.eps.softarch.demo.domain.Pet; +import cat.udl.eps.softarch.demo.repository.PetRepository; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.When; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; + +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + + +public class AddMedicalRecordStepDefs { + + @Autowired + private StepDefs stepDefs; + + @Autowired + PetRepository petRepository; + + + @Given("a pet exists in the system") + public void aPetExistsInTheSystem() { + var pet = new Pet(); + + petRepository.save(pet); // Save the pet to the database + } + + @When("I add a new medical record for a pet with issue {string}, description {string}, and date {string}") + public void iAddANewMedicalRecordForAPetWithIssueDescriptionAndDate(String issue, String description, String date) throws Throwable { + MedicalRecord newRecord = new MedicalRecord(); + newRecord.setDescription(description); + newRecord.setIssue(issue); + newRecord.setDate(ZonedDateTime.parse(date)); + newRecord.setPet(petRepository.findAll().iterator().next()); + + // Mock a POST request to /medicalRecords + stepDefs.result = stepDefs.mockMvc.perform( + post("/medicalRecords") + .contentType(MediaType.APPLICATION_JSON) + .content(stepDefs.mapper.writeValueAsString(newRecord)) + .characterEncoding(StandardCharsets.UTF_8) + .with(AuthenticationStepDefs.authenticate())) + .andDo(print()); + + } + + + + @When("I add a new medical record for a pet with issue {string}, description {string} and no date") + public void iAddANewMedicalRecordForAPetWithIssueDescriptionAndNoDate(String issue, String description) throws Throwable { + MedicalRecord recordWithoutDate = new MedicalRecord(); + recordWithoutDate.setDescription(description); + recordWithoutDate.setIssue(issue); + //No data added + recordWithoutDate.setPet(petRepository.findAll().iterator().next()); + + stepDefs.result = stepDefs.mockMvc.perform( + post("/medicalRecords") + .contentType(MediaType.APPLICATION_JSON) + .content(stepDefs.mapper.writeValueAsString(recordWithoutDate)) + .characterEncoding(StandardCharsets.UTF_8) + .with(AuthenticationStepDefs.authenticate())) + .andDo(print()); + } + + @When("I try to add a medical record for a pet") + public void iTryToAddAMedicalRecordForAPet() throws Exception { + MedicalRecord medicalRecord = new MedicalRecord(); + medicalRecord.setDescription("Description"); + medicalRecord.setIssue("Issue"); + medicalRecord.setDate(ZonedDateTime.now()); + medicalRecord.setPet(petRepository.findAll().iterator().next()); + + stepDefs.result = stepDefs.mockMvc.perform( + post("/medicalRecords") + .contentType(MediaType.APPLICATION_JSON) + .content(stepDefs.mapper.writeValueAsString(medicalRecord)) + .characterEncoding(StandardCharsets.UTF_8) + .with(AuthenticationStepDefs.authenticate()) + ) + .andDo(print()); + } +} \ No newline at end of file diff --git a/src/test/java/cat/udl/eps/softarch/demo/steps/DisableUserStepdefs.java b/src/test/java/cat/udl/eps/softarch/demo/steps/DisableUserStepdefs.java new file mode 100644 index 00000000..49966f0a --- /dev/null +++ b/src/test/java/cat/udl/eps/softarch/demo/steps/DisableUserStepdefs.java @@ -0,0 +1,59 @@ +package cat.udl.eps.softarch.demo.steps; + +import io.cucumber.java.en.And; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import org.json.JSONObject; +import org.junit.Assert; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + +public class DisableUserStepdefs { + + @Autowired + StepDefs stepDefs; + + @When("I disable the user with username {string}") + public void iDisableTheUserWithUsername(String username) throws Exception { + stepDefs.result = stepDefs.mockMvc.perform( + post("/users/{username}/lock", username) + .accept("application/json") + .with(AuthenticationStepDefs.authenticate())).andDo(print()); + } + + @Then("The user with username {string} should be disabled") + public void theUserWithUsernameShouldBeDisabled(String username) throws Exception { + stepDefs.result = stepDefs.mockMvc.perform( + get("/users/{username}", username) + .accept("application/json") + .with(AuthenticationStepDefs.authenticate())); + + JSONObject jsonObject = new JSONObject(stepDefs.result.andReturn().getResponse().getContentAsString()); + boolean isUserLocked = jsonObject.getBoolean("locked"); + + Assert.assertTrue(isUserLocked); + } + + @And("I enable the user with username {string}") + public void iEnableTheUserWithUsername(String username) throws Exception { + stepDefs.result = stepDefs.mockMvc.perform( + post("/users/{username}/unlock", username) + .accept("application/json") + .with(AuthenticationStepDefs.authenticate())).andDo(print()); + } + + @Then("The user with username {string} should be enabled") + public void theUserWithUsernameShouldBeEnabled(String username) throws Exception { + stepDefs.result = stepDefs.mockMvc.perform( + get("/users/{username}", username) + .accept("application/json") + .with(AuthenticationStepDefs.authenticate())); + + JSONObject jsonObject = new JSONObject(stepDefs.result.andReturn().getResponse().getContentAsString()); + boolean isUserLocked = jsonObject.getBoolean("locked"); + + Assert.assertFalse(isUserLocked); + } +} diff --git a/src/test/java/cat/udl/eps/softarch/demo/steps/EditMedicalRecordStepDefs.java b/src/test/java/cat/udl/eps/softarch/demo/steps/EditMedicalRecordStepDefs.java new file mode 100644 index 00000000..19a470c8 --- /dev/null +++ b/src/test/java/cat/udl/eps/softarch/demo/steps/EditMedicalRecordStepDefs.java @@ -0,0 +1,88 @@ +package cat.udl.eps.softarch.demo.steps; + +import cat.udl.eps.softarch.demo.domain.MedicalRecord; +import cat.udl.eps.softarch.demo.domain.Pet; +import cat.udl.eps.softarch.demo.repository.MedicalRecordRepository; +import cat.udl.eps.softarch.demo.repository.PetRepository; +import io.cucumber.java.en.And; +import io.cucumber.java.en.When; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; + +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + +public class EditMedicalRecordStepDefs { + + @Autowired + private StepDefs stepDefs; + + @Autowired + MedicalRecordRepository medicalRecordRepository; + + @Autowired + PetRepository petRepository; + + @And("a medical record exists for the pet") + public void aMedicalRecordExistsForThePet() { + Pet pet = petRepository.findAll().iterator().next(); + + MedicalRecord existingMedicalRecord; + existingMedicalRecord = new MedicalRecord(); + existingMedicalRecord.setPet(pet); + existingMedicalRecord.setIssue("Initial Issue"); + existingMedicalRecord.setDescription("Initial Description"); + existingMedicalRecord.setDate(ZonedDateTime.now()); + medicalRecordRepository.save(existingMedicalRecord); + + } + + @When("I edit the medical record for a pet with new issue {string}, new description {string}, and new date {string}") + public void iEditTheMedicalRecordForAPetWithNewIssueNewDescriptionAndNewDate(String issue, String description, String date) throws Throwable { + MedicalRecord existingMedicalRecord = medicalRecordRepository.findAll().iterator().next(); + existingMedicalRecord.setIssue(issue); + existingMedicalRecord.setDescription(description); + existingMedicalRecord.setDate(ZonedDateTime.parse(date)); + + stepDefs.result = stepDefs.mockMvc.perform( + put("/medicalRecords/" + existingMedicalRecord.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(stepDefs.mapper.writeValueAsString(existingMedicalRecord)) + .characterEncoding(StandardCharsets.UTF_8) + .with(AuthenticationStepDefs.authenticate())) + .andDo(print()); + } + + @When("I edit the medical record for a pet with new issue {string}, new description {string}") + public void iEditTheMedicalRecordForAPetWithNewIssueNewDescription(String issue, String description) throws Throwable { + MedicalRecord existingMedicalRecord = medicalRecordRepository.findAll().iterator().next(); + + existingMedicalRecord.setIssue(issue); + existingMedicalRecord.setDescription(description); + // Keeping the original date + + stepDefs.result = stepDefs.mockMvc.perform( + put("/medicalRecords/" + existingMedicalRecord.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(stepDefs.mapper.writeValueAsString(existingMedicalRecord)) + .characterEncoding(StandardCharsets.UTF_8) + .with(AuthenticationStepDefs.authenticate())) + .andDo(print()); + } + + @When("I try to edit a medical record for a pet") + public void iTryToEditAMedicalRecordForAPet() throws Exception { + MedicalRecord existingMedicalRecord = medicalRecordRepository.findAll().iterator().next(); + + stepDefs.result = stepDefs.mockMvc.perform( + put("/medicalRecords/" + existingMedicalRecord.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(stepDefs.mapper.writeValueAsString(existingMedicalRecord)) + .characterEncoding(StandardCharsets.UTF_8) + .with(AuthenticationStepDefs.authenticate())) + .andDo(print()); + } +} diff --git a/src/test/java/cat/udl/eps/softarch/demo/steps/ValidateShelterCertificateStepDefs.java b/src/test/java/cat/udl/eps/softarch/demo/steps/ValidateShelterCertificateStepDefs.java new file mode 100644 index 00000000..785278eb --- /dev/null +++ b/src/test/java/cat/udl/eps/softarch/demo/steps/ValidateShelterCertificateStepDefs.java @@ -0,0 +1,161 @@ +package cat.udl.eps.softarch.demo.steps; + +import cat.udl.eps.softarch.demo.domain.Admin; +import cat.udl.eps.softarch.demo.domain.Location; +import cat.udl.eps.softarch.demo.domain.Shelter; +import cat.udl.eps.softarch.demo.domain.ShelterCertificate; +import cat.udl.eps.softarch.demo.repository.AdminRepository; +import cat.udl.eps.softarch.demo.repository.LocationRepository; +import cat.udl.eps.softarch.demo.repository.ShelterCertificateRepository; +import cat.udl.eps.softarch.demo.repository.ShelterRepository; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import org.json.JSONObject; +import org.junit.Assert; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; + +public class ValidateShelterCertificateStepDefs { + + @Autowired + private ShelterCertificateRepository shelterCertificateRepository; + + @Autowired + private ShelterRepository shelterRepository; + + @Autowired + private LocationRepository locationRepository; + + @Autowired + private AdminRepository adminRepository; + + @Autowired + private StepDefs stepDefs; + + @Given("^A shelter with name \"([^\"]*)\"$") + public void thereIsAShelterCreatedWithName(String name){ + Location location = new Location(); + locationRepository.save(location); + if(shelterRepository.findByName(name) != null){ + Shelter shelter = new Shelter(); + shelter.setName(name); + shelter.setEmail("test@test.com"); + shelter.setActive(true); + shelter.setLocatedAt(location); + shelter.setMobile("000000000"); + shelterRepository.save(shelter); + } + } + + @And("^An admin with username \"([^\"]*)\" and password \"([^\"]*)\"$") + public void anAdminWithUsername(String username, String password){ + Admin admin = new Admin(); + admin.setId(username); + admin.setEmail("admin@admin.com"); + admin.setPassword(password); + admin.encodePassword(); + adminRepository.save(admin); + } + + @Given("^a shelter certificate associated with a shelter with name \"([^\"]*)\" with valid information created by a shelter volunteer$") + public void aShelterCertificateWithValidInformationSentByAVolunteer(String name){ + Shelter shelter = shelterRepository.findByName(name).get(0); + ShelterCertificate shelterCertificate = new ShelterCertificate(); + shelterCertificate.setShelterServed(shelter); + shelterCertificate.setValidated(false); + shelterCertificate.setExpirationDate(ZonedDateTime.now().plusMonths(6)); + shelterCertificateRepository.save(shelterCertificate); + } + + @When("^the admin with username \"([^\"]*)\" and password \"([^\"]*)\" verifies the certificate validity associated with a shelter with name \"([^\"]*)\"$") + public void thenAdminShouldVerifyTheCertificateValidityAssociatedWithAShelterWithName(String username, String password, String name) throws Exception { + Shelter shelter = shelterRepository.findByName(name).get(0); + AuthenticationStepDefs.currentUsername = username; + AuthenticationStepDefs.currentPassword = password; + + stepDefs.result = stepDefs.mockMvc.perform( + get( "/shelterCertificates/search/findByShelterServed?shelterServed={id}", "/shelters/"+shelter.getId()) + .with(AuthenticationStepDefs.authenticate()) + .accept("application/json")); + + JSONObject jsonObject = new JSONObject(stepDefs.result.andReturn().getResponse().getContentAsString()); + + String shelterCertificateUri = jsonObject.getString("uri"); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX"); + LocalDate shelterCertificateExpirationDate = LocalDate.parse(jsonObject.getString("expirationDate"), formatter); + + boolean isShelterCertificateValid = shelterCertificateExpirationDate.isAfter(LocalDate.now()); + if (!isShelterCertificateValid) { + System.out.println("The Shelter Certificate IS not valid!" + stepDefs.result); + Assert.fail("The Shelter Certificate IS not valid!"); + } + + stepDefs.result = stepDefs.mockMvc.perform( + patch( shelterCertificateUri) + .with(AuthenticationStepDefs.authenticate()) + .content("{\"validated\":true}") + .accept("application/json")); + + jsonObject = new JSONObject(stepDefs.result.andReturn().getResponse().getContentAsString()); + boolean isShelterCertificateValidated = jsonObject.getBoolean("validated"); + if (!isShelterCertificateValidated) { + System.out.println("The Shelter Certificate IS not validated!" + stepDefs.result); + Assert.fail("The Shelter Certificate IS not validated!"); + } + } + + @Given("^a shelter certificate associated with a shelter with name \"([^\"]*)\" with invalid information created by a shelter volunteer$") + public void aShelterCertificateWithInvalidInformationSentByAVolunteer(String name){ + Shelter shelter = shelterRepository.findByName(name).get(0); + ShelterCertificate shelterCertificate = new ShelterCertificate(); + shelterCertificate.setShelterServed(shelter); + shelterCertificate.setValidated(false); + shelterCertificate.setExpirationDate(ZonedDateTime.now().minusMonths(6)); + shelterCertificateRepository.save(shelterCertificate); + } + + @When("^the admin with username \"([^\"]*)\" and password \"([^\"]*)\" rejects the certificate validity associated with a shelter with name \"([^\"]*)\"$") + public void thenAdminShouldVerifyTheCertificateValidityAssociatedWithAShelterWithName1(String username, String password, String name) throws Exception { + Shelter shelter = shelterRepository.findByName(name).get(0); + AuthenticationStepDefs.currentUsername = username; + AuthenticationStepDefs.currentPassword = password; + + stepDefs.result = stepDefs.mockMvc.perform( + get( "/shelterCertificates/search/findByShelterServed?shelterServed={id}", "/shelters/"+shelter.getId()) + .with(AuthenticationStepDefs.authenticate()) + .accept("application/json")); + + JSONObject jsonObject = new JSONObject(stepDefs.result.andReturn().getResponse().getContentAsString()); + + String certificateUri = jsonObject.getString("uri"); + + stepDefs.result = stepDefs.mockMvc.perform( + patch(certificateUri) + .content("{\"validated\":false}") + .with(AuthenticationStepDefs.authenticate()) + .accept("application/json")); + } + + @Then("The shelter certificate from shelter with name {string} is not validated") + public void theShelterCertificateFromShelterWithNameIsStillNotValidated(String shelterName) throws Exception { + Shelter shelter = shelterRepository.findByName(shelterName).get(0); + + stepDefs.result = stepDefs.mockMvc.perform( + get( "/shelterCertificates/search/findByShelterServed?shelterServed={id}", "/shelters/"+shelter.getId()) + .with(AuthenticationStepDefs.authenticate()) + .accept("application/json")); + + JSONObject jsonObject = new JSONObject(stepDefs.result.andReturn().getResponse().getContentAsString()); + boolean isValidated = jsonObject.getBoolean("validated"); + + Assert.assertFalse(isValidated); + } +} diff --git a/src/test/resources/features/AddMedicalRecord.feature b/src/test/resources/features/AddMedicalRecord.feature new file mode 100644 index 00000000..5454d2e2 --- /dev/null +++ b/src/test/resources/features/AddMedicalRecord.feature @@ -0,0 +1,42 @@ +Feature: Add Medical Record + In order to maintain an accurate health history for pets + As a shelter volunteer + I want to add medical records to pets' profiles + + Background: + Given a pet exists in the system + Given There is a registered user with username "user" and password "password" and email "user@sample.app" + Given There is a registered admin with name "admin" and password "password" and email "admin@sample.app" + Given There is a registered volunteer with name "volunteer" and password "password" and email "volunteer@sample.app" + + + Scenario: Add a valid medical record to an existing pet as Volunteer + Given I login as "volunteer" with password "password" + When I add a new medical record for a pet with issue "Allergy", description "Seasonal allergy", and date "2024-03-07T14:00:00Z" + Then The response code is 201 + + Scenario: Add medical record with empty issue as Volunteer + Given I login as "volunteer" with password "password" + When I add a new medical record for a pet with issue "", description "Missing vaccine", and date "2024-03-07T14:00:00Z" + Then The response code is 400 + + Scenario: Add medical record with empty description as Volunteer + Given I login as "volunteer" with password "password" + When I add a new medical record for a pet with issue "Vaccination", description "", and date "2024-03-07T14:00:00Z" + Then The response code is 400 + + Scenario: Add medical record without date as Volunteer + Given I login as "volunteer" with password "password" + When I add a new medical record for a pet with issue "Injury", description "Minor cut on paw" and no date + Then The response code is 400 + + Scenario: Attempt to add a medical record as a normal user + Given I login as "user" with password "password" + When I try to add a medical record for a pet + Then The response code is 403 + And The error message is "Unauthorized access" + + Scenario: Attempt to add a medical record as an admin + Given I login as "admin" with password "password" + When I try to add a medical record for a pet + Then The response code is 201 diff --git a/src/test/resources/features/DisableUser.feature b/src/test/resources/features/DisableUser.feature new file mode 100644 index 00000000..197a7d95 --- /dev/null +++ b/src/test/resources/features/DisableUser.feature @@ -0,0 +1,32 @@ +Feature: Disable User + In order to keep the user from using the system + As an admin + I want to disable a user + + Background: It exists an admin and two user accounts created + Given There is a registered admin with name "admin" and password "123456789" and email "admin@admin.com" + And There is a registered user with username "user1" and password "123456789" and email "user1@user1.com" + And There is a registered user with username "user2" and password "123456789" and email "user2@user.com" + + Scenario: Admin disables a user + Given I can login with username "admin" and password "123456789" + When I disable the user with username "user1" + Then The user with username "user1" should be disabled + And The response code is 200 + + Scenario: Admin disables a user that does not exist + Given I can login with username "admin" and password "123456789" + When I disable the user with username "nonexistent" + Then The response code is 404 + + Scenario: A non-admin tries to disable a user + Given I can login with username "user1" and password "123456789" + When I disable the user with username "user2" + Then The response code is 403 + + Scenario: Admin disables and enables a user + Given I can login with username "admin" and password "123456789" + When I disable the user with username "user1" + And I enable the user with username "user1" + Then The user with username "user1" should be enabled + And The response code is 200 \ No newline at end of file diff --git a/src/test/resources/features/EditMedicalRecord.feature b/src/test/resources/features/EditMedicalRecord.feature new file mode 100644 index 00000000..4480f341 --- /dev/null +++ b/src/test/resources/features/EditMedicalRecord.feature @@ -0,0 +1,43 @@ +Feature: Edit Medical Record + In order to keep pet health history up to date + As a shelter volunteer + I want to edit existing medical records in pets' profiles + + Background: + Given a pet exists in the system + And a medical record exists for the pet + Given There is a registered user with username "user" and password "password" and email "user@sample.app" + Given There is a registered admin with name "admin" and password "password" and email "admin@sample.app" + Given There is a registered volunteer with name "volunteer" and password "password" and email "volunteer@sample.app" + + + Scenario: Edit an existing medical record as Volunteer + Given I login as "volunteer" with password "password" + When I edit the medical record for a pet with new issue "Allergy Update", new description "Updated seasonal allergy treatment", and new date "2024-03-08T14:00:00Z" + Then The response code is 204 + + Scenario: Edit a medical record with empty issue as Volunteer + Given I login as "volunteer" with password "password" + When I edit the medical record for a pet with new issue "", new description "Revaccination", and new date "2024-03-08T14:00:00Z" + Then The response code is 400 + + Scenario: Edit a medical record with empty description as Volunteer + Given I login as "volunteer" with password "password" + When I edit the medical record for a pet with new issue "Vaccination Update", new description "", and new date "2024-03-08T14:00:00Z" + Then The response code is 400 + + Scenario: Edit a medical record without changing the date as Volunteer + Given I login as "volunteer" with password "password" + When I edit the medical record for a pet with new issue "Injury Update", new description "Healed cut on paw" + Then The response code is 204 + + Scenario: Attempt to edit a medical record as a normal user + Given I login as "user" with password "password" + When I try to edit a medical record for a pet + Then The response code is 403 + And The error message is "Unauthorized access" + + Scenario: Attempt to edit a medical record as an admin + Given I login as "admin" with password "password" + When I try to edit a medical record for a pet + Then The response code is 204 diff --git a/src/test/resources/features/ValidateShelterCertificate.feature b/src/test/resources/features/ValidateShelterCertificate.feature new file mode 100644 index 00000000..b6419490 --- /dev/null +++ b/src/test/resources/features/ValidateShelterCertificate.feature @@ -0,0 +1,20 @@ +Feature: Validate ShelterCertificate + As an Admin + I want to validate a ShelterCertificate + So that I can ensure Its validity and authenticity + + Background: Exists a shelter with name "test" + Given A shelter with name "test" + And An admin with username "admin" and password "123456789" + + Scenario: Validating a correct shelter certificate + Given a shelter certificate associated with a shelter with name "test" with valid information created by a shelter volunteer + When the admin with username "admin" and password "123456789" verifies the certificate validity associated with a shelter with name "test" + Then The response code is 200 + + Scenario: Validating a incorrect shelter certificate + Given a shelter certificate associated with a shelter with name "test" with invalid information created by a shelter volunteer + When the admin with username "admin" and password "123456789" rejects the certificate validity associated with a shelter with name "test" + Then The shelter certificate from shelter with name "test" is not validated + And The response code is 200 +