diff --git a/backend/src/main/java/com/greenfoxacademy/backend/config/ResponseEntityErrorHandler.java b/backend/src/main/java/com/greenfoxacademy/backend/config/ResponseEntityErrorHandler.java index 334cfe06..91df172b 100644 --- a/backend/src/main/java/com/greenfoxacademy/backend/config/ResponseEntityErrorHandler.java +++ b/backend/src/main/java/com/greenfoxacademy/backend/config/ResponseEntityErrorHandler.java @@ -3,10 +3,8 @@ import com.greenfoxacademy.backend.errors.CannotUpdateUserException; import com.greenfoxacademy.backend.errors.UnableToDeleteProfileError; import com.greenfoxacademy.backend.errors.UserAlreadyExistsError; - import java.util.HashMap; import java.util.Map; - import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; @@ -75,6 +73,7 @@ public ResponseEntity> handleCannotUpdateUserExceptions( * * @return ResponseEntity with BAD_REQUEST and error key-value pair */ + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(UnableToDeleteProfileError.class) public ResponseEntity handleUnableToDeleteProfileError(UnableToDeleteProfileError ex) { diff --git a/backend/src/main/java/com/greenfoxacademy/backend/controller/UserController.java b/backend/src/main/java/com/greenfoxacademy/backend/controller/UserController.java index 3e55024f..a9a5be7d 100644 --- a/backend/src/main/java/com/greenfoxacademy/backend/controller/UserController.java +++ b/backend/src/main/java/com/greenfoxacademy/backend/controller/UserController.java @@ -9,11 +9,9 @@ import com.greenfoxacademy.backend.errors.CannotUpdateUserException; import com.greenfoxacademy.backend.errors.UnableToDeleteProfileError; import com.greenfoxacademy.backend.errors.UserAlreadyExistsError; -import com.greenfoxacademy.backend.services.user.UserService; - +import com.greenfoxacademy.backend.services.user.owner.OwnerService; import java.security.Principal; import java.util.UUID; - import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -32,7 +30,7 @@ @RestController @RequiredArgsConstructor public class UserController { - private final UserService userService; + private final OwnerService ownerService; /** * This method registers a new user. @@ -44,7 +42,7 @@ public class UserController { public ResponseEntity registerUser( @Validated @RequestBody RegisterRequestDto registerRequestDto )throws UserAlreadyExistsError { - return ResponseEntity.status(HttpStatus.OK).body(userService.register(registerRequestDto)); + return ResponseEntity.status(HttpStatus.OK).body(ownerService.register(registerRequestDto)); } /** @@ -59,7 +57,7 @@ public ResponseEntity registerUser( @PostMapping("/login") public ResponseEntity loginUser(@RequestBody LoginRequestDto loginRequestDto) { try { - return ResponseEntity.status(HttpStatus.OK).body(userService.login(loginRequestDto)); + return ResponseEntity.status(HttpStatus.OK).body(ownerService.login(loginRequestDto)); } catch (Exception e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } @@ -80,7 +78,7 @@ public ResponseEntity userProfileUpdate( ) throws CannotUpdateUserException { return ResponseEntity .status(HttpStatus.OK) - .body(userService.profileUpdate(principal.getName(), profileUpdateRequestDto)); + .body(ownerService.profileUpdate(principal.getName(), profileUpdateRequestDto)); } /** @@ -91,14 +89,14 @@ public ResponseEntity userProfileUpdate( */ @DeleteMapping("/delete-profile") public ResponseEntity deleteProfile(Principal principal) throws UnableToDeleteProfileError { - userService.deleteProfile(principal.getName()); + ownerService.deleteProfile(principal.getName()); return ResponseEntity.status(HttpStatus.ACCEPTED).build(); } // http://localhost:8080/verification?code=56565-55656-56-56-5-65-6-56 @GetMapping("/verification") public ResponseEntity verificationPage(@RequestParam(value = "code") UUID verificationCode) { - userService.verifyUser(verificationCode); + ownerService.verifyUser(verificationCode); return ResponseEntity.status(HttpStatus.OK).build(); } } \ No newline at end of file diff --git a/backend/src/main/java/com/greenfoxacademy/backend/models/ClinicAddress.java b/backend/src/main/java/com/greenfoxacademy/backend/models/ClinicAddress.java new file mode 100644 index 00000000..cf1020bb --- /dev/null +++ b/backend/src/main/java/com/greenfoxacademy/backend/models/ClinicAddress.java @@ -0,0 +1,46 @@ +package com.greenfoxacademy.backend.models; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Id; +import lombok.Data; + +/** + * Represents an address for a clinic. + *

+ * The {@code ClinicAddress} class is used as an embeddable type in JPA entities to store address + * details of a clinic. It includes information such as the street address, city, postal code, + * and clinic name. + *

+ *

+ * This class is marked with {@code @Embeddable} to indicate that it can be embedded in other JPA + * entities. + *

+ *

+ * The {@code id} field is used as a unique identifier for the address. This field is optional and + * is provided for cases where a unique identifier is necessary. + *

+ *

+ * The {@code zip} field represents the postal code for the clinic address and is limited to a + * length of 4 digits. + *

+ *

+ * The {@code clinicName} field specifies the name of the clinic associated with the address. + *

+ * + * @see jakarta.persistence.Embeddable + */ + +@Embeddable +@Data +public class ClinicAddress { + @Id + private Long id; + private String city; + @Column(length = 4) + private int zip; + private String street; + private String clinicName; + + +} diff --git a/backend/src/main/java/com/greenfoxacademy/backend/models/ClinicDetails.java b/backend/src/main/java/com/greenfoxacademy/backend/models/ClinicDetails.java new file mode 100644 index 00000000..fbc1b5fe --- /dev/null +++ b/backend/src/main/java/com/greenfoxacademy/backend/models/ClinicDetails.java @@ -0,0 +1,27 @@ +package com.greenfoxacademy.backend.models; + +import jakarta.persistence.Embeddable; +import lombok.Data; + +/** + * Represents the details of a clinic. + *

+ * The {@code ClinicDetails} class is used as an embeddable type in JPA entities to store detailed + * information about a clinic. This class contains the address of the clinic as well as any + * additional details that may be relevant for clinic management. + *

+ *

+ * This class is annotated with {@code @Embeddable} to indicate that it can be embedded within + * other JPA entities. + *

+ * + * @see ClinicAddress + */ + +@Data +@Embeddable +public class ClinicDetails { + + private ClinicAddress clinicAddress; + +} diff --git a/backend/src/main/java/com/greenfoxacademy/backend/models/Owner.java b/backend/src/main/java/com/greenfoxacademy/backend/models/Owner.java new file mode 100644 index 00000000..819e1872 --- /dev/null +++ b/backend/src/main/java/com/greenfoxacademy/backend/models/Owner.java @@ -0,0 +1,49 @@ +package com.greenfoxacademy.backend.models; + +import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import java.util.Collection; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +/** + * Represents an owner in the system who is also a user. + *

+ * The {@code Owner} class extends the {@code User} class and includes specific information related + * to an owner, such as the list of pets they own. It is marked with {@code @Entity} to indicate + * that it is a JPA entity and is mapped to a database table. + *

+ *

+ * The {@code @Table(name = "_owner")} annotation specifies the name of the database table + * associated with this entity. The table name is prefixed with an underscore to avoid conflicts + * with reserved SQL keywords. + *

+ *

+ * The {@code getAuthorities} method overrides the method from {@code User} to provide specific + * authorities for an owner, granting the role {@code ROLE_OWNER}. + *

+ * + * @see User + */ + +@Entity +@SuperBuilder +@RequiredArgsConstructor +@AllArgsConstructor +@Table(name = "_owner") +public class Owner extends User { + @OneToMany(mappedBy = "petOwner") + private List pets; + + @Override + @Transient + public Collection getAuthorities() { + return List.of(new SimpleGrantedAuthority("ROLE_OWNER")); + } +} diff --git a/backend/src/main/java/com/greenfoxacademy/backend/models/Pet.java b/backend/src/main/java/com/greenfoxacademy/backend/models/Pet.java new file mode 100644 index 00000000..6fd1d341 --- /dev/null +++ b/backend/src/main/java/com/greenfoxacademy/backend/models/Pet.java @@ -0,0 +1,44 @@ +package com.greenfoxacademy.backend.models; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Represents a pet entity in the system. This class is a data model + * that maps to the pet table in the database. + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "_pet") +public class Pet { + + @Id + @GeneratedValue + private Integer id; + + @Column(nullable = false) + private String petName; + private String petBreed; + private String petSex; + private Date petBirthDate; + private Date lastCheckUp; + private Date nextCheckUp; + + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "petOwner_Id") + private Owner petOwner; +} diff --git a/backend/src/main/java/com/greenfoxacademy/backend/models/User.java b/backend/src/main/java/com/greenfoxacademy/backend/models/User.java index 2d40c708..26c536d7 100644 --- a/backend/src/main/java/com/greenfoxacademy/backend/models/User.java +++ b/backend/src/main/java/com/greenfoxacademy/backend/models/User.java @@ -1,19 +1,14 @@ package com.greenfoxacademy.backend.models; import jakarta.persistence.Column; -import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; -import jakarta.persistence.Table; -import java.util.Collection; -import java.util.List; +import jakarta.persistence.MappedSuperclass; import java.util.UUID; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import lombok.experimental.SuperBuilder; import org.springframework.security.core.userdetails.UserDetails; /** @@ -21,12 +16,11 @@ * that maps to the user table in the database. */ @Data -@Builder +@SuperBuilder +@MappedSuperclass @AllArgsConstructor @NoArgsConstructor -@Entity -@Table(name = "_user") -public class User implements UserDetails { +public abstract class User implements UserDetails { @Id @GeneratedValue @@ -38,11 +32,6 @@ public class User implements UserDetails { private String password; private UUID verificationId; - @Override - public Collection getAuthorities() { - return List.of(new SimpleGrantedAuthority("ROLE_USER")); - } - @Override public String getUsername() { return email; diff --git a/backend/src/main/java/com/greenfoxacademy/backend/models/Vet.java b/backend/src/main/java/com/greenfoxacademy/backend/models/Vet.java new file mode 100644 index 00000000..d99bd34c --- /dev/null +++ b/backend/src/main/java/com/greenfoxacademy/backend/models/Vet.java @@ -0,0 +1,47 @@ +package com.greenfoxacademy.backend.models; + +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import java.util.Collection; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +/** + * Represents a veterinarian who is also a user in the system. + *

+ * The {@code Vet} class extends the {@code User} class and adds specific information related to + * a veterinarian, such as the clinic address. It is annotated with {@code @Entity} to indicate + * that it is a JPA entity and will be mapped to a database table. + *

+ *

+ * This class is annotated with {@code @Table(name = "_vet")} to specify the name of the database + * table to which this entity is mapped. The table name is prefixed with an underscore to avoid + * conflicts with reserved SQL keywords. + *

+ *

+ * The {@code getAuthorities} method overrides the method from {@code User} to provide specific + * authorities for a veterinarian, in this case, granting the role {@code ROLE_VET}. + *

+ * + * @see User + */ + +@SuperBuilder +@RequiredArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "_vet") +public class Vet extends User { + private ClinicDetails clinicDetails; + + @Override + @Transient + public Collection getAuthorities() { + return List.of(new SimpleGrantedAuthority("ROLE_VET")); + } +} diff --git a/backend/src/main/java/com/greenfoxacademy/backend/repositories/UserRepository.java b/backend/src/main/java/com/greenfoxacademy/backend/repositories/OwnerRepository.java similarity index 61% rename from backend/src/main/java/com/greenfoxacademy/backend/repositories/UserRepository.java rename to backend/src/main/java/com/greenfoxacademy/backend/repositories/OwnerRepository.java index 877531cf..4af6ca2e 100644 --- a/backend/src/main/java/com/greenfoxacademy/backend/repositories/UserRepository.java +++ b/backend/src/main/java/com/greenfoxacademy/backend/repositories/OwnerRepository.java @@ -1,20 +1,20 @@ package com.greenfoxacademy.backend.repositories; +import com.greenfoxacademy.backend.models.Owner; import com.greenfoxacademy.backend.models.User; import java.util.Optional; import java.util.UUID; - import org.springframework.data.jpa.repository.JpaRepository; /** * Repository to manage {@link User} entities. */ -public interface UserRepository extends JpaRepository { +public interface OwnerRepository extends JpaRepository { boolean existsByEmail(String email); - Optional findByEmail(String email); + Optional findByEmail(String email); void deleteByEmail(String email); - Optional findByVerificationId(UUID id); + Optional findByVerificationId(UUID id); } diff --git a/backend/src/main/java/com/greenfoxacademy/backend/repositories/PetRepository.java b/backend/src/main/java/com/greenfoxacademy/backend/repositories/PetRepository.java new file mode 100644 index 00000000..e53bb6fe --- /dev/null +++ b/backend/src/main/java/com/greenfoxacademy/backend/repositories/PetRepository.java @@ -0,0 +1,10 @@ +package com.greenfoxacademy.backend.repositories; + +import com.greenfoxacademy.backend.models.Pet; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Repository to manage Pet entities. + */ +public interface PetRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/greenfoxacademy/backend/services/user/UserService.java b/backend/src/main/java/com/greenfoxacademy/backend/services/user/owner/OwnerService.java similarity index 85% rename from backend/src/main/java/com/greenfoxacademy/backend/services/user/UserService.java rename to backend/src/main/java/com/greenfoxacademy/backend/services/user/owner/OwnerService.java index 94bc5a9b..68dba21e 100644 --- a/backend/src/main/java/com/greenfoxacademy/backend/services/user/UserService.java +++ b/backend/src/main/java/com/greenfoxacademy/backend/services/user/owner/OwnerService.java @@ -1,4 +1,4 @@ -package com.greenfoxacademy.backend.services.user; +package com.greenfoxacademy.backend.services.user.owner; import com.greenfoxacademy.backend.dtos.LoginRequestDto; import com.greenfoxacademy.backend.dtos.LoginResponseDto; @@ -6,20 +6,20 @@ import com.greenfoxacademy.backend.dtos.ProfileUpdateResponseDto; import com.greenfoxacademy.backend.dtos.RegisterRequestDto; import com.greenfoxacademy.backend.dtos.RegisterResponseDto; -import com.greenfoxacademy.backend.errors.CannotVerifyUserError; import com.greenfoxacademy.backend.errors.CannotUpdateUserException; +import com.greenfoxacademy.backend.errors.CannotVerifyUserError; import com.greenfoxacademy.backend.errors.UnableToDeleteProfileError; import com.greenfoxacademy.backend.errors.UserAlreadyExistsError; -import com.greenfoxacademy.backend.models.User; +import com.greenfoxacademy.backend.models.Owner; import java.util.UUID; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; /** - * Service to manage {@link User} related actions. + * Service to manage {@link Owner} related actions. */ @Service -public interface UserService extends UserDetailsService { +public interface OwnerService extends UserDetailsService { RegisterResponseDto register(RegisterRequestDto userDto) throws UserAlreadyExistsError; LoginResponseDto login(LoginRequestDto loginRequestDto) throws Exception; diff --git a/backend/src/main/java/com/greenfoxacademy/backend/services/user/UserServiceImpl.java b/backend/src/main/java/com/greenfoxacademy/backend/services/user/owner/OwnerServiceImpl.java similarity index 71% rename from backend/src/main/java/com/greenfoxacademy/backend/services/user/UserServiceImpl.java rename to backend/src/main/java/com/greenfoxacademy/backend/services/user/owner/OwnerServiceImpl.java index 937de714..b449a429 100644 --- a/backend/src/main/java/com/greenfoxacademy/backend/services/user/UserServiceImpl.java +++ b/backend/src/main/java/com/greenfoxacademy/backend/services/user/owner/OwnerServiceImpl.java @@ -1,4 +1,4 @@ -package com.greenfoxacademy.backend.services.user; +package com.greenfoxacademy.backend.services.user.owner; import com.greenfoxacademy.backend.config.FeatureFlags; import com.greenfoxacademy.backend.dtos.LoginRequestDto; @@ -9,10 +9,11 @@ import com.greenfoxacademy.backend.dtos.RegisterResponseDto; import com.greenfoxacademy.backend.errors.CannotUpdateUserException; import com.greenfoxacademy.backend.errors.UserAlreadyExistsError; -import com.greenfoxacademy.backend.models.User; -import com.greenfoxacademy.backend.repositories.UserRepository; +import com.greenfoxacademy.backend.models.Owner; +import com.greenfoxacademy.backend.repositories.OwnerRepository; import com.greenfoxacademy.backend.services.auth.AuthService; import com.greenfoxacademy.backend.services.mail.EmailService; +import com.greenfoxacademy.backend.services.user.owner.OwnerService; import jakarta.transaction.Transactional; import java.util.UUID; @@ -26,12 +27,12 @@ import org.springframework.stereotype.Service; /** - * Service implementation to manage {@link UserService}. + * Service implementation to manage {@link OwnerService}. */ @Service @RequiredArgsConstructor -public class UserServiceImpl implements UserService { - private final UserRepository userRepository; +public class OwnerServiceImpl implements OwnerService { + private final OwnerRepository ownerRepository; private final PasswordEncoder passwordEncoder; private final AuthService authService; private final EmailService emailService; @@ -44,7 +45,7 @@ public RegisterResponseDto register(RegisterRequestDto registerRequestDto) boolean isEmailEnabled = featureFlags.isEmailVerificationEnabled(); UUID verificationId = isEmailEnabled ? UUID.randomUUID() : null; // @formatter:off - User user = User.builder() + Owner owner = Owner.builder() .email(registerRequestDto.email()) .firstName(registerRequestDto.firstName()) .lastName(registerRequestDto.lastName()) @@ -53,7 +54,7 @@ public RegisterResponseDto register(RegisterRequestDto registerRequestDto) .build(); // @formatter:on try { - User saved = userRepository.save(user); + Owner saved = ownerRepository.save(owner); if (isEmailEnabled) { emailService.sendRegistrationEmail( saved.getEmail(), @@ -68,40 +69,43 @@ public RegisterResponseDto register(RegisterRequestDto registerRequestDto) @Override public LoginResponseDto login(LoginRequestDto loginRequestDto) throws Exception { - User user = userRepository.findByEmail(loginRequestDto.email()) + Owner owner = ownerRepository.findByEmail(loginRequestDto.email()) .orElseThrow(() -> new Exception("User not found")); - if (!user.isEnabled()) { + if (!owner.isEnabled()) { throw new Exception("User's email is not verified"); - } else if (!passwordEncoder.matches(loginRequestDto.password(), user.getPassword())) { + } else if (!passwordEncoder.matches(loginRequestDto.password(), owner.getPassword())) { throw new Exception("Invalid password"); } - return new LoginResponseDto(authService.generateToken(user)); + return new LoginResponseDto(authService.generateToken(owner)); } @CacheEvict(value = "update-profile-cache", key = "#email") @Override public ProfileUpdateResponseDto profileUpdate( String email, - ProfileUpdateRequestDto profileUpdateRequestDto) throws CannotUpdateUserException { - User user = userRepository.findByEmail(email) + ProfileUpdateRequestDto profileUpdateRequestDto + ) throws CannotUpdateUserException { + Owner owner = ownerRepository.findByEmail(email) .orElseThrow(() -> new UsernameNotFoundException("User not found")); - if (userRepository.existsByEmail(profileUpdateRequestDto.email()) - && !email.equals(profileUpdateRequestDto.email())) { + if ( + ownerRepository.existsByEmail(profileUpdateRequestDto.email()) + && !email.equals(profileUpdateRequestDto.email()) + ) { throw new CannotUpdateUserException("Email is already taken!"); } - user.setEmail(profileUpdateRequestDto.email()); - user.setFirstName(profileUpdateRequestDto.firstName()); - user.setLastName(profileUpdateRequestDto.lastName()); - user.setPassword(passwordEncoder.encode(profileUpdateRequestDto.password())); + owner.setEmail(profileUpdateRequestDto.email()); + owner.setFirstName(profileUpdateRequestDto.firstName()); + owner.setLastName(profileUpdateRequestDto.lastName()); + owner.setPassword(passwordEncoder.encode(profileUpdateRequestDto.password())); - User updatedUser = userRepository.save(user); + Owner updatedUser = ownerRepository.save(owner); return new ProfileUpdateResponseDto(authService.generateToken(updatedUser)); } @Cacheable(value = "profile-cache", key = "#username") @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - return userRepository.findByEmail(username) + return ownerRepository.findByEmail(username) .orElseThrow(() -> new UsernameNotFoundException("No such user!")); } @@ -112,18 +116,18 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx @Transactional @Override public void deleteProfile(String username) { - userRepository.deleteByEmail(username); + ownerRepository.deleteByEmail(username); } /** * Verify the user by id sent as email. */ public void verifyUser(UUID id) { + Owner userWithId = ownerRepository.findByVerificationId(id).orElseThrow(); if (!featureFlags.isEmailVerificationEnabled()) { return; } - User userWithId = userRepository.findByVerificationId(id).orElseThrow(); userWithId.setVerificationId(null); - userRepository.save(userWithId); + ownerRepository.save(userWithId); } } \ No newline at end of file diff --git a/backend/src/test/java/com/greenfoxacademy/backend/controller/UserControllerTest.java b/backend/src/test/java/com/greenfoxacademy/backend/controller/UserControllerTest.java index 74efb5d1..5602bf02 100644 --- a/backend/src/test/java/com/greenfoxacademy/backend/controller/UserControllerTest.java +++ b/backend/src/test/java/com/greenfoxacademy/backend/controller/UserControllerTest.java @@ -15,8 +15,8 @@ import com.greenfoxacademy.backend.dtos.LoginRequestDto; import com.greenfoxacademy.backend.dtos.LoginResponseDto; import com.greenfoxacademy.backend.errors.UserAlreadyExistsError; -import com.greenfoxacademy.backend.models.User; -import com.greenfoxacademy.backend.repositories.UserRepository; +import com.greenfoxacademy.backend.models.Owner; +import com.greenfoxacademy.backend.repositories.OwnerRepository; import com.greenfoxacademy.backend.services.mail.EmailService; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -40,7 +40,7 @@ class UserControllerTest { * The UserRepository is mocked, so we can define its behavior in each test. */ @MockBean - private UserRepository userRepository; + private OwnerRepository ownerRepository; /** * The MockMvc is used to perform a request to the controller and validate the response. @@ -225,7 +225,7 @@ void shouldReturnEmailIsInvalidWhenEmailIsInvalid() throws Exception { @DisplayName("Should return email is duplicated when email is duplicated") @Test void shouldReturnEmailIsIsDuplicatedWhenEmailIsDuplicated() throws Exception { - when(userRepository.save(Mockito.any())) + when(ownerRepository.save(Mockito.any())) .thenThrow(new UserAlreadyExistsError("Email is already taken!")); String content = """ { @@ -246,7 +246,7 @@ void shouldReturnEmailIsIsDuplicatedWhenEmailIsDuplicated() throws Exception { @Test void shouldReturnUserSuccessfullyCreatedIfEverythingIsCorrect() throws Exception { - when(userRepository.save(Mockito.any())).thenReturn(User.builder().id(1).build()); + when(ownerRepository.save(Mockito.any())).thenReturn(Owner.builder().id(1).build()); when(emailService.sendRegistrationEmail(anyString(), anyString(), Mockito.any())) .thenReturn(new EmailSentDto()); String content = """ @@ -268,10 +268,10 @@ void shouldReturnUserSuccessfullyCreatedIfEverythingIsCorrect() throws Exception @Test void shouldReturnTokenWhenUserIsSuccessfullyLoggedInWithGoodCredentials() throws Exception { - when(userRepository.existsByEmail("johndoe@gmail.com")).thenReturn(true); - when(userRepository.findByEmail("johndoe@gmail.com")) + when(ownerRepository.existsByEmail("johndoe@gmail.com")).thenReturn(true); + when(ownerRepository.findByEmail("johndoe@gmail.com")) .thenReturn(Optional - .of(User.builder() + .of(Owner.builder() .id(1) .firstName("John") .lastName("Doe") @@ -296,7 +296,7 @@ void shouldReturnTokenWhenUserIsSuccessfullyLoggedInWithGoodCredentials() throws @Test void shouldReturnUnauthenticatedWhenEmailIsNotFound() throws Exception { - when(userRepository.existsByEmail("johndoe@gmail.com")).thenReturn(false); + when(ownerRepository.existsByEmail("johndoe@gmail.com")).thenReturn(false); String content = """ { "email": "johndoe@gmail.com", @@ -323,8 +323,8 @@ void shouldNotBeAbleToUpdateProfileIfNotLoggedIn() throws Exception { @WithMockUser(username = "john.doe@gmail.com", password = "password", roles = "USER") void shouldBeAbleToUpdateProfileIfLoggedIn() throws Exception { String email = "john.doe@gmail.com"; - when(userRepository.findByEmail("john.doe@gmail.com")) - .thenReturn(Optional.of(User.builder() + when(ownerRepository.findByEmail("john.doe@gmail.com")) + .thenReturn(Optional.of(Owner.builder() .id(1) .email(email) .firstName("John") @@ -332,8 +332,8 @@ void shouldBeAbleToUpdateProfileIfLoggedIn() throws Exception { .password(passwordEncoder.encode("password")) .build())); - when(userRepository.save(Mockito.any())) - .thenReturn(User.builder() + when(ownerRepository.save(Mockito.any())) + .thenReturn(Owner.builder() .id(1) .email(email) .firstName("John") @@ -379,9 +379,9 @@ void shouldBeAbleToUpdateProfileIfLoggedIn() throws Exception { void deleteProfile() throws Exception { LoginRequestDto loginRequestDto = new LoginRequestDto("john.doe@gmail.com", "password"); - when(userRepository.existsByEmail(loginRequestDto.email())).thenReturn(true); - when(userRepository.findByEmail(loginRequestDto.email())) - .thenReturn(Optional.of(User.builder() + when(ownerRepository.existsByEmail(loginRequestDto.email())).thenReturn(true); + when(ownerRepository.findByEmail(loginRequestDto.email())) + .thenReturn(Optional.of(Owner.builder() .id(1) .email(loginRequestDto.email()) .firstName("John") @@ -407,7 +407,7 @@ void deleteProfile() throws Exception { ) .andExpect(status().isAccepted()); - Mockito.verify(userRepository, Mockito.times(1)) + Mockito.verify(ownerRepository, Mockito.times(1)) .deleteByEmail(loginRequestDto.email()); } } diff --git a/backend/src/test/java/com/greenfoxacademy/backend/services/UserServiceTest.java b/backend/src/test/java/com/greenfoxacademy/backend/services/OwnerServiceTest.java similarity index 73% rename from backend/src/test/java/com/greenfoxacademy/backend/services/UserServiceTest.java rename to backend/src/test/java/com/greenfoxacademy/backend/services/OwnerServiceTest.java index 68495a22..728f4fcb 100644 --- a/backend/src/test/java/com/greenfoxacademy/backend/services/UserServiceTest.java +++ b/backend/src/test/java/com/greenfoxacademy/backend/services/OwnerServiceTest.java @@ -6,8 +6,8 @@ import com.greenfoxacademy.backend.controller.UserController; import com.greenfoxacademy.backend.dtos.RegisterRequestDto; import com.greenfoxacademy.backend.errors.UserAlreadyExistsError; -import com.greenfoxacademy.backend.repositories.UserRepository; -import com.greenfoxacademy.backend.services.user.UserService; +import com.greenfoxacademy.backend.repositories.OwnerRepository; +import com.greenfoxacademy.backend.services.user.owner.OwnerService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -18,12 +18,12 @@ * This class runs a test to verify if the register method in the userService is properly called. */ @ExtendWith(MockitoExtension.class) -public class UserServiceTest { +public class OwnerServiceTest { @Mock - private UserService userService; + private OwnerService ownerService; @Mock - private UserRepository userRepository; + private OwnerRepository ownerRepository; @InjectMocks private UserController userController; @@ -37,9 +37,9 @@ public void registerMethodIsSuccessfullyCalled() throws Exception, UserAlreadyEx "password" ); - userService.register(registerRequestDto); + ownerService.register(registerRequestDto); - verify(userService, times(1)).register(registerRequestDto); + verify(ownerService, times(1)).register(registerRequestDto); } diff --git a/backend/src/test/java/com/greenfoxacademy/backend/services/UserRegistrationTest.java b/backend/src/test/java/com/greenfoxacademy/backend/services/UserRegistrationTest.java index a92435e1..a2778194 100644 --- a/backend/src/test/java/com/greenfoxacademy/backend/services/UserRegistrationTest.java +++ b/backend/src/test/java/com/greenfoxacademy/backend/services/UserRegistrationTest.java @@ -8,9 +8,9 @@ import com.greenfoxacademy.backend.dtos.RegisterRequestDto; import com.greenfoxacademy.backend.dtos.RegisterResponseDto; import com.greenfoxacademy.backend.errors.UserAlreadyExistsError; -import com.greenfoxacademy.backend.repositories.UserRepository; +import com.greenfoxacademy.backend.repositories.OwnerRepository; import com.greenfoxacademy.backend.services.mail.EmailService; -import com.greenfoxacademy.backend.services.user.UserService; +import com.greenfoxacademy.backend.services.user.owner.OwnerService; import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -29,10 +29,10 @@ public class UserRegistrationTest { @Autowired - private UserService userService; + private OwnerService ownerService; @Autowired - private UserRepository userRepository; + private OwnerRepository ownerRepository; @MockBean private EmailService emailService; @@ -49,11 +49,11 @@ public void userIsSuccessfulRegisteredInDatabase() throws Exception, UserAlready "password" ); - RegisterResponseDto registeredUserDto = userService.register(newUser); + RegisterResponseDto registeredUserDto = ownerService.register(newUser); Assertions.assertEquals(1, registeredUserDto.id()); - boolean isRegistered = userRepository.existsByEmail("john.doe@example.com"); + boolean isRegistered = ownerRepository.existsByEmail("john.doe@example.com"); Assertions.assertTrue(isRegistered); } } diff --git a/backend/src/test/java/com/greenfoxacademy/backend/services/mail/EmailServiceImplTest.java b/backend/src/test/java/com/greenfoxacademy/backend/services/mail/EmailServiceImplTest.java index 6a154b1c..616bdc12 100644 --- a/backend/src/test/java/com/greenfoxacademy/backend/services/mail/EmailServiceImplTest.java +++ b/backend/src/test/java/com/greenfoxacademy/backend/services/mail/EmailServiceImplTest.java @@ -5,7 +5,7 @@ import static org.mockito.Mockito.when; import com.greenfoxacademy.backend.config.EmailConfiguration; -import com.greenfoxacademy.backend.services.user.UserService; +import com.greenfoxacademy.backend.services.user.owner.OwnerService; import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import java.util.UUID; @@ -34,14 +34,14 @@ class EmailServiceImplTest { private EmailConfiguration emailConfiguration; @MockBean - private UserService userService; + private OwnerService ownerService; private EmailServiceImpl emailService; @BeforeEach void setUp() { emailSender = mock(JavaMailSender.class); - userService = mock(UserService.class); + ownerService = mock(OwnerService.class); emailService = new EmailServiceImpl(emailConfiguration, emailSender); } diff --git a/backend/src/test/java/com/greenfoxacademy/backend/services/user/UserServiceImplTest.java b/backend/src/test/java/com/greenfoxacademy/backend/services/user/OwnerServiceImplTest.java similarity index 75% rename from backend/src/test/java/com/greenfoxacademy/backend/services/user/UserServiceImplTest.java rename to backend/src/test/java/com/greenfoxacademy/backend/services/user/OwnerServiceImplTest.java index ace03a8a..bcdde253 100644 --- a/backend/src/test/java/com/greenfoxacademy/backend/services/user/UserServiceImplTest.java +++ b/backend/src/test/java/com/greenfoxacademy/backend/services/user/OwnerServiceImplTest.java @@ -10,14 +10,13 @@ import com.greenfoxacademy.backend.dtos.RegisterRequestDto; import com.greenfoxacademy.backend.errors.CannotUpdateUserException; import com.greenfoxacademy.backend.errors.UserAlreadyExistsError; -import com.greenfoxacademy.backend.models.User; -import com.greenfoxacademy.backend.repositories.UserRepository; +import com.greenfoxacademy.backend.models.Owner; +import com.greenfoxacademy.backend.repositories.OwnerRepository; import com.greenfoxacademy.backend.services.auth.AuthService; import com.greenfoxacademy.backend.services.mail.EmailService; - +import com.greenfoxacademy.backend.services.user.owner.OwnerServiceImpl; import java.util.Optional; import java.util.UUID; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -26,18 +25,17 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @ExtendWith(MockitoExtension.class) -class UserServiceImplTest { - private UserServiceImpl userService; +class OwnerServiceImplTest { + private OwnerServiceImpl userService; private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); @Mock - private UserRepository userRepository; + private OwnerRepository ownerRepository; @Mock private AuthService authService; @@ -49,9 +47,9 @@ class UserServiceImplTest { @BeforeEach void setUp() { - Mockito.reset(userRepository); - userService = new UserServiceImpl( - userRepository, + Mockito.reset(ownerRepository); + userService = new OwnerServiceImpl( + ownerRepository, passwordEncoder, authService, emailService, @@ -62,7 +60,7 @@ void setUp() { @Test void register() { // Given - User asSaved = User.builder().id(1).build(); + Owner asSaved = Owner.builder().id(1).build(); RegisterRequestDto registerRequestDto = new RegisterRequestDto( "fistName", "lastName", @@ -70,11 +68,11 @@ void register() { "SomePassword123"); // When - when(userRepository.save(any())).thenReturn(asSaved); + when(ownerRepository.save(any())).thenReturn(asSaved); userService.register(registerRequestDto); // Then - Mockito.verify(userRepository, Mockito.times(1)).save(any()); + Mockito.verify(ownerRepository, Mockito.times(1)).save(any()); } @DisplayName("Does not register a new user if email is taken") @@ -88,7 +86,7 @@ void registerFails() { "SomePassword123"); // When - when(userRepository.save(any())) + when(ownerRepository.save(any())) .thenThrow(new UserAlreadyExistsError("Email is already taken!")); Assertions.assertThrows( @@ -102,7 +100,7 @@ void registerFails() { @Test void login() throws Exception { // Given - User user = User.builder() + Owner owner = Owner.builder() .id(1) .email("email") .password(passwordEncoder.encode("password")) @@ -110,14 +108,14 @@ void login() throws Exception { LoginRequestDto userLoginRequestDto = new LoginRequestDto("email", "password"); // When - when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(user)); + when(ownerRepository.findByEmail(anyString())).thenReturn(Optional.of(owner)); when(authService.generateToken(any())).thenReturn("token"); userService.login(userLoginRequestDto); // Then Mockito.verify( - userRepository, + ownerRepository, Mockito.times(1)).findByEmail(anyString() ); } @@ -126,7 +124,7 @@ void login() throws Exception { @Test void loginUnsuccessful() throws Exception { // Given - User user = User.builder() + Owner owner = Owner.builder() .id(1) .email("email") .password(passwordEncoder.encode("passwordNOOP")) @@ -137,13 +135,13 @@ void loginUnsuccessful() throws Exception { ); // When - when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(user)); + when(ownerRepository.findByEmail(anyString())).thenReturn(Optional.of(owner)); Assertions.assertThrows(Exception.class, () -> userService.login(userLoginRequestDto)); // Then Mockito.verify( - userRepository, + ownerRepository, Mockito.times(1)).findByEmail(anyString() ); } @@ -152,7 +150,7 @@ void loginUnsuccessful() throws Exception { @Test void profileUpdate() throws Exception { // Given - User user = User.builder() + Owner owner = Owner.builder() .id(1) .email("email") .password(passwordEncoder.encode("password")) @@ -165,14 +163,14 @@ void profileUpdate() throws Exception { "newPassword"); // When - when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(user)); - when(userRepository.save(any())).thenReturn(user); + when(ownerRepository.findByEmail(anyString())).thenReturn(Optional.of(owner)); + when(ownerRepository.save(any())).thenReturn(owner); userService.profileUpdate(email, profileUpdateRequestDto); // Then Mockito - .verify(userRepository, Mockito.times(1)) + .verify(ownerRepository, Mockito.times(1)) .findByEmail(anyString()); } @@ -180,7 +178,7 @@ void profileUpdate() throws Exception { @Test void profileUpdateUnsuccessful() throws Exception { // Given - User user = User.builder().id(1).email("email") + Owner owner = Owner.builder().id(1).email("email") .password(passwordEncoder.encode("password")) .build(); String email = "email"; @@ -194,8 +192,8 @@ void profileUpdateUnsuccessful() throws Exception { // @formatter:on // When - when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(user)); - when(userRepository.existsByEmail("new@email.com")).thenReturn(true); + when(ownerRepository.findByEmail(anyString())).thenReturn(Optional.of(owner)); + when(ownerRepository.existsByEmail("new@email.com")).thenReturn(true); // Then Assertions.assertThrows( @@ -207,18 +205,18 @@ void profileUpdateUnsuccessful() throws Exception { @Test void loadUserByUsername() { // Given - User user = User.builder() + Owner owner = Owner.builder() .id(1) .email("email") .password(passwordEncoder.encode("password")) .build(); String email = "email"; // When - when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(user)); + when(ownerRepository.findByEmail(anyString())).thenReturn(Optional.of(owner)); userService.loadUserByUsername(email); // Then Mockito.verify( - userRepository, + ownerRepository, Mockito.times(1) ).findByEmail(anyString()); } @@ -227,35 +225,35 @@ void loadUserByUsername() { void verifyUserById() { when(featureFlags.isEmailVerificationEnabled()).thenReturn(true); UUID id = UUID.randomUUID(); - User user = User.builder() + Owner owner = Owner.builder() .id(1) .email("email") .password(passwordEncoder.encode("password")) .verificationId(id) .build(); // When - when(userRepository.findByVerificationId(id)).thenReturn(Optional.of(user)); + when(ownerRepository.findByVerificationId(id)).thenReturn(Optional.of(owner)); userService.verifyUser(id); // Then Mockito.verify( - userRepository, + ownerRepository, Mockito.times(1) - ).save(any(User.class)); + ).save(any(Owner.class)); } @Test void throwsExceptionEmailIsNotVerified() { UUID id = UUID.randomUUID(); - User user = User.builder() + Owner owner = Owner.builder() .id(1) .email("email") .password(passwordEncoder.encode("password")) .verificationId(id) .build(); - LoginRequestDto loginRequestDto = new LoginRequestDto(user.getEmail(), "password"); + LoginRequestDto loginRequestDto = new LoginRequestDto(owner.getEmail(), "password"); - when(userRepository.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); + when(ownerRepository.findByEmail(owner.getEmail())).thenReturn(Optional.of(owner)); Assertions.assertThrows(Exception.class, () -> userService.login(loginRequestDto)); }