Repository: Pawel-Mackiewicz/BankApp
Pull request #237: Add demo user registration functionality and improve logging
Author: login: pawel-mackiewicz
Created: 2025-04-15T11:20:22Z | Merged: 2025-04-15T13:24:07Z

```
### Summary
This pull request introduces features and improvements focused on demo user registration and logging configurations, ensuring more robust exception handling, streamlined user creation, and better logging practices.

### Changes
- Added `DemoRegistrationService` to handle demo user registrations, including custom exception handling with `DemoRegistrationException`.
- Created `DemoRegistrationRequest` DTO and implemented validation annotations for password and confirmation handling.
- Introduced `RegistrationRequestFactory` for generating pre-configured demo user registration requests.
- Added a new endpoint to `RegistrationController` for demo user registrations.
- Refactored method calls for demo-specific user registration logic, ensuring consistency across the codebase.
- Enhanced logging configurations:
  - Enabled configurable stack trace logging using `logging.stacktrace.enabled`.
  - Optimized reusable properties for logging patterns and added MDC visibility for better traceability.
- Minor refactors:
  - Removed unused `UsernameException` class.
  - Fixed redundant variable assignments in `ApiErrorLogger`.
  - Refactored user creation flow in `UserCreationService` to ensure proper validation flow.
  - Improved exception handling for `DemoRegistrationService`.
- Replaced `@Email` annotation with regex for email validation.
- Added unit tests for `DemoRegistrationService`

```

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features**
  - Introduced demo user registration endpoint with dedicated API and validation.
  - Added demo user registration request data structure with comprehensive validation.
- **Improvements**
  - Renamed regular user registration endpoint from `/register` to `/regular`.
  - Enhanced email validation using a stricter pattern.
  - Configurable stacktrace logging via application settings.
  - Standardized logging output patterns for console and file.
  - Refined error handling with specific exceptions for duplicate user attributes.
- **Bug Fixes**
  - Improved error handling for duplicate user data during registration.
- **Tests**
  - Added unit tests for demo registration service.
  - Extended integration and end-to-end tests for new demo and regular registration endpoints.
  - Added test data factories for demo registration scenarios.
- **Documentation**
  - Enhanced API documentation for registration endpoints and validation rules.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

resolves #191 #192 

Linked issue #191: make demo user creation.
Make user creation for demo purposses. 
something like:
1. Type your email adress.
2. Choose if you want to fully register or just use demo.
3a. If fully register, then just proceed with registration form.
3b. If demo, then proceed with creation of demo user.
demo users will be deleted after 24h

Diff:
diff --git a/src/main/java/info/mackiewicz/bankapp/shared/infrastructure/logging/ApiErrorLogger.java b/src/main/java/info/mackiewicz/bankapp/shared/infrastructure/logging/ApiErrorLogger.java
index 95aa5875..d324c734 100644
--- a/src/main/java/info/mackiewicz/bankapp/shared/infrastructure/logging/ApiErrorLogger.java
+++ b/src/main/java/info/mackiewicz/bankapp/shared/infrastructure/logging/ApiErrorLogger.java
@@ -1,12 +1,12 @@
 package info.mackiewicz.bankapp.shared.infrastructure.logging;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-import org.springframework.stereotype.Component;
-
 import info.mackiewicz.bankapp.shared.core.error.ErrorCode;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
 
 /**
  * Component responsible for standardized error logging across the application.
@@ -36,7 +36,11 @@ public class ApiErrorLogger {
      * Flag to determine whether to log the stack trace or not.
      * Set to true for development and false for production.
      */
-    private static final boolean LOG_STACKTRACE = false;
+    private final boolean logStacktrace;
+
+    public ApiErrorLogger(@Value("${logging.stacktrace.enabled:false}") boolean logStacktrace) {
+        this.logStacktrace = logStacktrace;
+    }
 
 
 
@@ -53,7 +57,7 @@ public class ApiErrorLogger {
      * @see ErrorCode#INTERNAL_ERROR
      */
     public void logError(ErrorCode error, Exception ex, String path) {
-        String message = formatErrorMessage(error, ex, path, LOG_STACKTRACE);
+        String message = formatErrorMessage(error, ex, path, logStacktrace);
 
         if (error == ErrorCode.INTERNAL_ERROR) {
             log.error(message, ex);
@@ -81,13 +85,12 @@ public void logError(ErrorCode error, Exception ex, String path) {
      */
     private String formatErrorMessage(ErrorCode error, Exception ex, String path, boolean logStackTrace) {
             
-            String message = String.format(
+            return String.format(
                     "Error occurred: %s, Path: %s, Message: %s\nStackTrace: %s",
                     error.name(),
                     path,
                     ex.getMessage(),
                     logStackTrace ? getStackTrace(ex) : "Stack trace logging is disabled.");
-            return message;
         }
 
     /**
@@ -100,14 +103,14 @@ private String formatErrorMessage(ErrorCode error, Exception ex, String path, bo
      * @param ex the exception to get the stack trace from
      * @return complete stack trace as a formatted string
      * @see StringWriter
-     * @see PrintWriter#printStackTrace()
+     * @see Throwable#printStackTrace()
      */
         private String getStackTrace(Exception ex) {
             StringWriter sw = new StringWriter();
             PrintWriter pw = new PrintWriter(sw);
             ex.printStackTrace(pw);
-            String stackTrace = sw.toString();
-            return stackTrace;
+
+            return sw.toString();
         }
 
 }
diff --git a/src/main/java/info/mackiewicz/bankapp/system/registration/controller/DefaultRegistrationController.java b/src/main/java/info/mackiewicz/bankapp/system/registration/controller/DefaultRegistrationController.java
index 26c25cb1..3080c788 100644
--- a/src/main/java/info/mackiewicz/bankapp/system/registration/controller/DefaultRegistrationController.java
+++ b/src/main/java/info/mackiewicz/bankapp/system/registration/controller/DefaultRegistrationController.java
@@ -1,7 +1,9 @@
 package info.mackiewicz.bankapp.system.registration.controller;
 
+import info.mackiewicz.bankapp.system.registration.dto.DemoRegistrationRequest;
 import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
 import info.mackiewicz.bankapp.system.registration.dto.RegistrationResponse;
+import info.mackiewicz.bankapp.system.registration.service.DemoRegistrationService;
 import info.mackiewicz.bankapp.system.registration.service.RegistrationService;
 import jakarta.validation.Valid;
 import lombok.RequiredArgsConstructor;
@@ -15,6 +17,7 @@
 public class DefaultRegistrationController  implements RegistrationController{
 
     private final RegistrationService registrationService;
+    private final DemoRegistrationService demoRegistrationService;
 
     @Override
     public ResponseEntity<RegistrationResponse> registerUser(@Valid @RequestBody RegistrationRequest request) {
@@ -26,4 +29,14 @@ public ResponseEntity<RegistrationResponse> registerUser(@Valid @RequestBody Reg
                 .body(response);
     }
 
+    @Override
+    public ResponseEntity<RegistrationResponse> registerDemoUser(@Valid @RequestBody DemoRegistrationRequest request) {
+
+        RegistrationResponse response = demoRegistrationService.registerDemoUser(request);
+
+        return ResponseEntity
+                .status(HttpStatus.CREATED)
+                .body(response);
+    }
+
 }
diff --git a/src/main/java/info/mackiewicz/bankapp/system/registration/controller/RegistrationController.java b/src/main/java/info/mackiewicz/bankapp/system/registration/controller/RegistrationController.java
index 14a3f74c..68b7cebf 100644
--- a/src/main/java/info/mackiewicz/bankapp/system/registration/controller/RegistrationController.java
+++ b/src/main/java/info/mackiewicz/bankapp/system/registration/controller/RegistrationController.java
@@ -2,6 +2,7 @@
 
 import info.mackiewicz.bankapp.shared.web.dto.BaseApiError;
 import info.mackiewicz.bankapp.shared.web.dto.ValidationApiError;
+import info.mackiewicz.bankapp.system.registration.dto.DemoRegistrationRequest;
 import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
 import info.mackiewicz.bankapp.system.registration.dto.RegistrationResponse;
 import io.swagger.v3.oas.annotations.Operation;
@@ -23,7 +24,6 @@ public interface RegistrationController {
             summary = "Register a new user",
             description = "Creates a new user in the system. The user will be registered with the provided details."
     )
-    @PostMapping("/register")
     @RequestBody(
             required = true,
             description = "User registration details",
@@ -62,10 +62,53 @@ public interface RegistrationController {
                                               "status": "CONFLICT",
                                               "title": "USER_ALREADY_EXISTS",
                                               "message": "User with these credentials already exists.",
-                                              "path": "/api/registration/register",
+                                              "path": "/api/registration/regular",
                                               "timestamp": "11-04-2025 16:18:29"
                                             }
                                             """)))
     })
+    @PostMapping("/regular")
     ResponseEntity<RegistrationResponse> registerUser(RegistrationRequest request);
+
+    @Operation(
+            summary = "Register a new demo user",
+            description = "Creates a new demo user in the system. The user will be registered with the provided email address."
+    )
+    @ApiResponses(value = {
+            @ApiResponse(
+                    responseCode = "201",
+                    description = "User created successfully",
+                    content = @Content(
+                            mediaType = "application/json",
+                            schema = @Schema(implementation = RegistrationResponse.class))),
+            @ApiResponse(
+                    responseCode = "400",
+                    description = "Invalid input data",
+                    content = @Content(
+                            mediaType = "application/json",
+                            schema = @Schema(implementation = BaseApiError.class))),
+            @ApiResponse(
+                    responseCode = "400",
+                    description = "Validation error",
+                    content = @Content(
+                            mediaType = "application/json",
+                            schema = @Schema(implementation = ValidationApiError.class))),
+            @ApiResponse(
+                    responseCode = "409",
+                    description = "User already exists",
+                    content = @Content(
+                            mediaType = "application/json",
+                            schema = @Schema(implementation = BaseApiError.class,
+                                    example = """
+                                            {
+                                              "status": "CONFLICT",
+                                              "title": "USER_ALREADY_EXISTS",
+                                              "message": "User with these credentials already exists.",
+                                              "path": "/api/registration/demo",
+                                              "timestamp": "11-04-2025 16:18:29"
+                                            }
+                                            """)))
+    })
+    @PostMapping("/demo")
+    ResponseEntity<RegistrationResponse> registerDemoUser(DemoRegistrationRequest request);
 }
diff --git a/src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java b/src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java
new file mode 100644
index 00000000..7dc1da57
--- /dev/null
+++ b/src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java
@@ -0,0 +1,41 @@
+package info.mackiewicz.bankapp.system.registration.dto;
+
+import info.mackiewicz.bankapp.presentation.auth.validation.Password;
+import info.mackiewicz.bankapp.presentation.auth.validation.PasswordMatches;
+import info.mackiewicz.bankapp.shared.validation.ValidationConstants;
+import info.mackiewicz.bankapp.shared.web.dto.interfaces.PasswordConfirmation;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "Registration request DTO for demo users")
+@PasswordMatches
+@AllArgsConstructor
+@NoArgsConstructor
+public class DemoRegistrationRequest implements PasswordConfirmation {
+
+    @NotBlank(message = "Email is required")
+    @Pattern(regexp = ValidationConstants.EMAIL_PATTERN, message = "Invalid email format")
+    @Schema(description = "Email must be a valid email address", example = "john.smith@example.com")
+    private String email;
+
+    @Password
+    @Schema(description = "Password must be at least 8 characters long, contain at least one digit, " +
+            "one lowercase letter, one uppercase letter, and one special character from the set: @$!%*?&", minLength = 8, pattern = ValidationConstants.PASSWORD_PATTERN, example = "StrongP@ss123")
+    private String password;
+
+    @NotBlank(message = "Password confirmation is required")
+    @Schema(description = "Password confirmation must match the password", example = "StrongP@ss123")
+    private String confirmPassword;
+
+    public EmailAddress getEmail() {
+        return new EmailAddress(email);
+    }
+}
diff --git a/src/main/java/info/mackiewicz/bankapp/system/registration/dto/RegistrationRequest.java b/src/main/java/info/mackiewicz/bankapp/system/registration/dto/RegistrationRequest.java
index b4c966d0..ff8a3631 100644
--- a/src/main/java/info/mackiewicz/bankapp/system/registration/dto/RegistrationRequest.java
+++ b/src/main/java/info/mackiewicz/bankapp/system/registration/dto/RegistrationRequest.java
@@ -10,7 +10,6 @@
 import info.mackiewicz.bankapp.user.model.vo.PhoneNumber;
 import info.mackiewicz.bankapp.user.validation.AgeRange;
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Email;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Pattern;
@@ -63,7 +62,7 @@ public class RegistrationRequest implements PasswordConfirmation {
     private String pesel;
 
     @NotBlank(message = "Email is required")
-    @Email(message = "Please provide a valid email address")
+    @Pattern(regexp = ValidationConstants.EMAIL_PATTERN, message = "Invalid email format")
     @Schema(description = "Email must be a valid email address", example = "john.smith@example.com")
     private String email;
 
diff --git a/src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java b/src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java
new file mode 100644
index 00000000..fcc888c7
--- /dev/null
+++ b/src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java
@@ -0,0 +1,64 @@
+package info.mackiewicz.bankapp.system.registration.dto.demo;
+
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.Random;
+
+/**
+ * Class that is responsible for creating random data for demo user creation.
+ */
+@Component
+public class RegistrationRequestFactory {
+
+
+    private static final Random RANDOM = new Random();
+    private static final int PESEL_LENGTH = 11;
+    private static final int PHONE_NUMBER_LENGTH = 9;
+    private static final int MAX_PHONE_NUMBER = 999999999;
+    private static final int RANDOM_DIGIT_BOUND = 10;
+    private static final LocalDate DEFAULT_DATE_OF_BIRTH = LocalDate.of(1990, 1, 1);
+    private static final String DEFAULT_FIRSTNAME = "Demo";
+    private static final String DEFAULT_LASTNAME = "User";
+
+
+    public RegistrationRequest createDemoRegistrationRequest(EmailAddress email, String password) {
+        RegistrationRequest request = new RegistrationRequest();
+
+        request.setEmail(email.getValue());
+        request.setPassword(password);
+        request.setConfirmPassword(password);
+        request.setFirstname(DEFAULT_FIRSTNAME);
+        request.setLastname(DEFAULT_LASTNAME);
+        request.setDateOfBirth(DEFAULT_DATE_OF_BIRTH);
+        request.setPesel(generateRandomPesel());
+        request.setPhoneNumber(generateRandomPhoneNumber());
+
+        return request;
+    }
+
+    /**
+     * Generates a random PESEL (Polish national identification number) consisting of a sequence
+     * of digits of a fixed length.
+     *
+     * @return a randomly generated PESEL number in string format
+     */
+    private static String generateRandomPesel() {
+        StringBuilder pesel = new StringBuilder();
+        for (int i = 0; i < PESEL_LENGTH; i++) {
+            pesel.append(RANDOM.nextInt(RANDOM_DIGIT_BOUND));
+        }
+        return pesel.toString();
+    }
+
+    /**
+     * Generates a random phone number consisting of a fixed number of digits.
+     *
+     * @return a randomly generated phone number in string format
+     */
+    private static String generateRandomPhoneNumber() {
+        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", RANDOM.nextInt(MAX_PHONE_NUMBER));
+    }
+}
diff --git a/src/main/java/info/mackiewicz/bankapp/system/registration/exception/UsernameException.java b/src/main/java/info/mackiewicz/bankapp/system/registration/exception/DemoRegistrationException.java
similarity index 51%
rename from src/main/java/info/mackiewicz/bankapp/system/registration/exception/UsernameException.java
rename to src/main/java/info/mackiewicz/bankapp/system/registration/exception/DemoRegistrationException.java
index a9cac49b..f369d4a3 100644
--- a/src/main/java/info/mackiewicz/bankapp/system/registration/exception/UsernameException.java
+++ b/src/main/java/info/mackiewicz/bankapp/system/registration/exception/DemoRegistrationException.java
@@ -3,14 +3,14 @@
 import info.mackiewicz.bankapp.shared.core.BankAppBaseException;
 import info.mackiewicz.bankapp.shared.core.error.ErrorCode;
 
-public class UsernameException extends BankAppBaseException {
-    private static final ErrorCode ERROR_CODE = ErrorCode.VALIDATION_ERROR;
+public class DemoRegistrationException extends BankAppBaseException {
 
-    public UsernameException(String message) {
+    private static final ErrorCode ERROR_CODE = ErrorCode.INTERNAL_ERROR;
+
+    public DemoRegistrationException(String message) {
         super(message, ERROR_CODE);
     }
-
-    public UsernameException(String message, Throwable cause) {
+    public DemoRegistrationException(String message, Throwable cause) {
         super(message, cause, ERROR_CODE);
     }
 }
diff --git a/src/main/java/info/mackiewicz/bankapp/system/registration/service/DefaultRegistrationService.java b/src/main/java/info/mackiewicz/bankapp/system/registration/service/DefaultRegistrationService.java
index 976ee271..8ae08fea 100644
--- a/src/main/java/info/mackiewicz/bankapp/system/registration/service/DefaultRegistrationService.java
+++ b/src/main/java/info/mackiewicz/bankapp/system/registration/service/DefaultRegistrationService.java
@@ -25,7 +25,6 @@ public class DefaultRegistrationService implements RegistrationService {
     @Value("${bankapp.registration.WelcomeBonusAmount:1000}")
     private BigDecimal defaultWelcomeBonusAmount;
 
-
     private final UserService userService;
     private final RegistrationMapper registrationMapper;
     private final AccountService accountService;
diff --git a/src/main/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationService.java b/src/main/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationService.java
new file mode 100644
index 00000000..da21639d
--- /dev/null
+++ b/src/main/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationService.java
@@ -0,0 +1,49 @@
+package info.mackiewicz.bankapp.system.registration.service;
+
+import info.mackiewicz.bankapp.system.registration.dto.DemoRegistrationRequest;
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationResponse;
+import info.mackiewicz.bankapp.system.registration.dto.demo.RegistrationRequestFactory;
+import info.mackiewicz.bankapp.system.registration.exception.DemoRegistrationException;
+import info.mackiewicz.bankapp.user.exception.DuplicatedEmailException;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * Service responsible for handling the registration of demo users.
+ * Encapsulates the logic for creating a demo registration request and interacting
+ * with the user registration handling system.
+ * <p>
+ * This service performs the following:
+ * <br>- Creates a demo user registration request using the {@link RegistrationRequestFactory}.
+ * <br>- Invokes the {@link RegistrationService#registerUser(RegistrationRequest)} method to
+ * register the user in the system.
+ * <br>- Handles exceptions that may occur during the registration process, including duplicate
+ * email errors and internal application errors.
+ * <p>
+ * Dependencies:
+ * <br>- {@link RegistrationService}: Handles the actual registration process.
+ * <br>- {@link RegistrationRequestFactory}: Creates a demo registration request with default
+ * placeholder values.
+ */
+@Service
+@RequiredArgsConstructor
+public class DemoRegistrationService {
+
+    private final RegistrationService registrationService;
+    private final RegistrationRequestFactory requestFactory;
+
+    public RegistrationResponse registerDemoUser(DemoRegistrationRequest demoRequest) {
+        RegistrationRequest request = requestFactory.createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword());
+        try {
+            return registrationService.registerUser(request);
+        } catch (Exception e) {
+            // Handle email duplication specifically as it's a common user error
+            // All other exceptions are wrapped as internal system errors
+            if (e instanceof DuplicatedEmailException) {
+                throw e;
+            }
+            throw new DemoRegistrationException("Registration failed", e);
+        }
+    }
+}
diff --git a/src/main/java/info/mackiewicz/bankapp/user/service/UserService.java b/src/main/java/info/mackiewicz/bankapp/user/service/UserService.java
index 5ea2f2e2..0b58e1d0 100644
--- a/src/main/java/info/mackiewicz/bankapp/user/service/UserService.java
+++ b/src/main/java/info/mackiewicz/bankapp/user/service/UserService.java
@@ -38,9 +38,12 @@ public class UserService {
      * @param user The user object containing the information for the new user
      * @return The created user with generated ID
      * @throws UserFieldNullException if any of the required fields in the User object is null
-     * @throws UserValidationException if the name validation fails for any name
-     * @throws DuplicatedUserException if the username, email address, PESEL, or phone number is already in use
+     * @throws UserValidationException if the name validation fails for firstname or lastname
      * @throws InvalidAgeException if the user's age is less than 18 or greater than 120 years old
+     * @throws DuplicatedEmailException if the email adress is already in use
+     * @throws DuplicatedUsernameException if the username is already in use
+     * @throws DuplicatedPeselException if the PESEL number is already in use
+     * @throws DuplicatedPhoneNumberException if the phone number is already in use
      */
     public User createUser(User user) {
         return userCreationService.createUser(user);
diff --git a/src/main/java/info/mackiewicz/bankapp/user/service/crud/UserCreationService.java b/src/main/java/info/mackiewicz/bankapp/user/service/crud/UserCreationService.java
index 34663718..8e0c4211 100644
--- a/src/main/java/info/mackiewicz/bankapp/user/service/crud/UserCreationService.java
+++ b/src/main/java/info/mackiewicz/bankapp/user/service/crud/UserCreationService.java
@@ -1,10 +1,7 @@
 package info.mackiewicz.bankapp.user.service.crud;
 
 import info.mackiewicz.bankapp.security.service.PasswordService;
-import info.mackiewicz.bankapp.user.exception.DuplicatedUserException;
-import info.mackiewicz.bankapp.user.exception.InvalidAgeException;
-import info.mackiewicz.bankapp.user.exception.UserFieldNullException;
-import info.mackiewicz.bankapp.user.exception.UserValidationException;
+import info.mackiewicz.bankapp.user.exception.*;
 import info.mackiewicz.bankapp.user.model.User;
 import info.mackiewicz.bankapp.user.repository.UserRepository;
 import info.mackiewicz.bankapp.user.service.util.UserValidationService;
@@ -50,24 +47,24 @@ public class UserCreationService {
      *
      * @param user The user object containing the information for the new user
      * @return The created user with generated ID and all fields processed
-     * @throws UserFieldNullException if any of the required fields in the User
-     *                                object is null.
-     * @throws UserValidationException if the name validation fails for any name.
-     * @throws DuplicatedUserException if the username, email address, PESEL, or
-     *                                 phone number is already in use.
-     * @throws InvalidAgeException if the user's age is less than 18 or greater
-     *                             than 120 years old.
+     * @throws UserFieldNullException if any of the required fields in the User object is null
+     * @throws UserValidationException if the name validation fails for firstname or lastname
+     * @throws InvalidAgeException if the user's age is less than 18 or greater than 120 years old
+     * @throws DuplicatedEmailException if the email adress is already in use
+     * @throws DuplicatedUsernameException if the username is already in use
+     * @throws DuplicatedPeselException if the PESEL number is already in use
+     * @throws DuplicatedPhoneNumberException if the phone number is already in use
      */
     @Transactional
     public User createUser(User user) {
 
-        User validatedUser = validateNewUser(user);
-        log.debug("Validated new user data");
+        User userWithUsername = generateUsername(user);
+        log.debug("Generated username: {}", userWithUsername.getUsername());
 
-        User userWithUsername = generateUsername(validatedUser);
-        log.debug("Generated username: {}", user.getUsername());
+        User validatedUser = validateNewUser(userWithUsername);
+        log.debug("Validated new user data");
 
-        User userWithPassword = passwordService.ensurePasswordEncoded(userWithUsername);
+        User userWithPassword = passwordService.ensurePasswordEncoded(validatedUser);
         log.debug("Password encoded");
 
         User savedUser = userRepository.save(userWithPassword);
diff --git a/src/main/java/info/mackiewicz/bankapp/user/service/util/UserValidationService.java b/src/main/java/info/mackiewicz/bankapp/user/service/util/UserValidationService.java
index 0bfb89a7..015eb1bf 100644
--- a/src/main/java/info/mackiewicz/bankapp/user/service/util/UserValidationService.java
+++ b/src/main/java/info/mackiewicz/bankapp/user/service/util/UserValidationService.java
@@ -50,13 +50,13 @@ public class UserValidationService {
      * @param user The User object to validate. It must contain all necessary data
      *             for registration, including first name, last name, username,
      *             email, PESEL, phone number, and date of birth.
-     * @throws UserFieldNullException if any of the required fields in the User
-     *                                object is null.
-     * @throws UserValidationException if the name validation fails for any name.
-     * @throws DuplicatedUserException if the username, email address, PESEL, or
-     *                                 phone number is already in use.
-     * @throws InvalidAgeException if the user's age is less than 18 or greater
-     *                             than 120 years old.
+     * @throws UserFieldNullException if any of the required fields in the User object is null
+     * @throws UserValidationException if the name validation fails for firstname or lastname
+     * @throws InvalidAgeException if the user's age is less than 18 or greater than 120 years old
+     * @throws DuplicatedEmailException if the email adress is already in use
+     * @throws DuplicatedUsernameException if the username is already in use
+     * @throws DuplicatedPeselException if the PESEL number is already in use
+     * @throws DuplicatedPhoneNumberException if the phone number is already in use
      */
     public void validateNewUser(User user) {
         log.info("Starting validation for new user registration");
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 830fc945..471b2a9b 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -1,86 +1,88 @@
 # Server configuration
 server:
-    address: 0.0.0.0
-    port: ${PORT:8080}
+  address: 0.0.0.0
+  port: ${PORT:8080}
 
 # Application variables
 spring:
-    profiles:
-        ## Active profile ([dev, test, prod])
-        active: ${SPRING_PROFILES_ACTIVE:prod}
-    # Spring Security
-    security:
-        user:
-            name: ${SPRING_SECURITY_USER_NAME}
-            password: ${SPRING_SECURITY_USER_PASSWORD}
-    # Database configuration
-    datasource:
-        url: ${DB_URL}
-        username: ${DB_USERNAME}
-        password: ${DB_PASSWORD}
-        driver-class-name: com.mysql.cj.jdbc.Driver  
-        # Connection pool settings
-        hikari:
-            maximum-pool-size: 5
-            minimum-idle: 2
-            connection-timeout: 20000
-            idle-timeout: 300000
-    # JPA / Hibernate settings
-    jpa:
-        hibernate.ddl-auto: update
-        show-sql: false
-        open-in-view: false
-        properties:
-            hibernate: 
-                dialect: org.hibernate.dialect.MySQL8Dialect
-                '[show_sql]': false
-                '[format_sql]': true
-                '[globally_quoted_identifiers]': true
-    # Flyway configuration
-    flyway:
-        enabled: true
-        locations: classpath:db/migration
-        baseline-on-migrate: true
-        baseline-version: 1.0
-        placeholder-replacement: true
-        placeholders:
-            bank_password: ${BANK_PASSWORD:password}
+  profiles:
+    ## Active profile ([dev, test, prod])
+    active: ${SPRING_PROFILES_ACTIVE:prod}
+  # Spring Security
+  security:
+    user:
+      name: ${SPRING_SECURITY_USER_NAME}
+      password: ${SPRING_SECURITY_USER_PASSWORD}
+  # Database configuration
+  datasource:
+    url: ${DB_URL}
+    username: ${DB_USERNAME}
+    password: ${DB_PASSWORD}
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    # Connection pool settings
+    hikari:
+      maximum-pool-size: 5
+      minimum-idle: 2
+      connection-timeout: 20000
+      idle-timeout: 300000
+  # JPA / Hibernate settings
+  jpa:
+    hibernate.ddl-auto: update
+    show-sql: false
+    open-in-view: false
+    properties:
+      hibernate:
+        dialect: org.hibernate.dialect.MySQL8Dialect
+        '[show_sql]': false
+        '[format_sql]': true
+        '[globally_quoted_identifiers]': true
+  # Flyway configuration
+  flyway:
+    enabled: true
+    locations: classpath:db/migration
+    baseline-on-migrate: true
+    baseline-version: 1.0
+    placeholder-replacement: true
+    placeholders:
+      bank_password: ${BANK_PASSWORD:password}
 
 # Swagger / OpenAPI configuration
 springdoc:
-    swagger-ui:
-        docExpansion: list
-    default-produces-media-type: application/json
-    show-actuator: false
-    model-and-view-allowed: true
-    
-app: 
-    # Email configuration
-    ## Resend API configuration
-    resend:
-        api-key: ${RESEND_API_KEY}
-    ## Application base URL (for generating links in emails)
-    base-url: ${APP_BASE_URL:http://localhost:8080}
-    ## Frontend configuration
-    thymeleaf:
-        enabled: ${THYMELEAF_ENABLED:true}
-    ## Account locking configuration
-    locking:
-        max-attempts: 5
-        base-delay: 100
-        max-delay: 2000
-        timeout: 200
+  swagger-ui:
+    docExpansion: list
+  default-produces-media-type: application/json
+  show-actuator: false
+  model-and-view-allowed: true
+
+app:
+  # Email configuration
+  ## Resend API configuration
+  resend:
+    api-key: ${RESEND_API_KEY}
+  ## Application base URL (for generating links in emails)
+  base-url: ${APP_BASE_URL:http://localhost:8080}
+  ## Frontend configuration
+  thymeleaf:
+    enabled: ${THYMELEAF_ENABLED:true}
+  ## Account locking configuration
+  locking:
+    max-attempts: 5
+    base-delay: 100
+    max-delay: 2000
+    timeout: 200
 
 bankapp:
-    registration:
-        WelcomeBonusAmount: 1000
-    bank:
-        account:
-            iban: PL66485112340000000000000000
+  registration:
+    WelcomeBonusAmount: 1000
+  bank:
+    account:
+      iban: PL66485112340000000000000000
 
 
-# # Logging configuration
-# logging:
+  # Logging configuration
+logging:
+  stacktrace:
+    enabled: false
 #     level:
 #         root: INFO
 #         info:
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
index 7f862e12..c6e6da29 100644
--- a/src/main/resources/logback-spring.xml
+++ b/src/main/resources/logback-spring.xml
@@ -1,17 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <configuration>
+
+    <!-- Static patterns definition -->
+    <property name="CONSOLE_LOG_PATTERN" value="%d{HH:mm:ss} %highlight(%-5level) %cyan(%logger{36}) [%X] - %msg%n" />
+    <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} [%X] - %msg%n" />
+    <property name="SQL_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] SQL %-5level %logger{36} [%X] - %msg%n" />
+
     <!-- Appenders for both profiles -->
     <!-- Console appender for both profiles -->
     <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
         <encoder>
-            <pattern>%d{HH:mm:ss} %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
         </encoder>
     </appender>
 
     <!-- Console SQL Appender  -->
     <appender name="SQL_MARKER" class="ch.qos.logback.core.ConsoleAppender">
         <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] SQL %-5level %logger{36} - %msg%n</pattern>
+            <pattern>${SQL_LOG_PATTERN}</pattern>
         </encoder>
     </appender>
 
@@ -23,7 +29,7 @@
             <maxHistory>7</maxHistory>
         </rollingPolicy>
         <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <pattern>${FILE_LOG_PATTERN}</pattern>
         </encoder>
     </appender>
 
@@ -34,7 +40,7 @@
             <maxHistory>7</maxHistory>
         </rollingPolicy>
         <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <pattern>${FILE_LOG_PATTERN}</pattern>
         </encoder>
     </appender>
 
@@ -45,7 +51,7 @@
             <maxHistory>7</maxHistory>
         </rollingPolicy>
         <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <pattern>${FILE_LOG_PATTERN}</pattern>
         </encoder>
     </appender>
 
@@ -56,7 +62,7 @@
             <maxHistory>7</maxHistory>
         </rollingPolicy>
         <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <pattern>${FILE_LOG_PATTERN}</pattern>
         </encoder>
     </appender>
 
@@ -68,7 +74,7 @@
             <maxHistory>7</maxHistory>
         </rollingPolicy>
         <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <pattern>${FILE_LOG_PATTERN}</pattern>
         </encoder>
     </appender>
 
@@ -79,7 +85,7 @@
             <maxHistory>7</maxHistory>
         </rollingPolicy>
         <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <pattern>${FILE_LOG_PATTERN}</pattern>
         </encoder>
     </appender>
 
diff --git a/src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java b/src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java
index cde410f8..8254427a 100644
--- a/src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java
+++ b/src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java
@@ -4,6 +4,7 @@
 import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import info.mackiewicz.bankapp.system.notification.email.EmailService;
+import info.mackiewicz.bankapp.system.registration.dto.DemoRegistrationRequest;
 import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
 import info.mackiewicz.bankapp.system.registration.dto.RegistrationResponse;
 import info.mackiewicz.bankapp.system.registration.service.BonusGrantingService;
@@ -39,10 +40,13 @@ class RegistrationControllerIntegrationTest {
     private static final String TEST_EMAIL = "jan.kowalski@example.com";
     private static final String TEST_FIRSTNAME = "Jan";
     private static final String TEST_LASTNAME = "Kowalski";
-    private static final String REGISTRATION_ENDPOINT = "/api/registration/register";
+    private static final String REGISTRATION_ENDPOINT = "/api/registration/regular";
+    private static final String DEMO_REGISTRATION_ENDPOINT = "/api/registration/demo";
     private static final String DATE_OF_BIRTH_FIELD = "dateOfBirth";
     private static final String FIRSTNAME_FIELD = "firstname";
     private static final String PESEL_FIELD = "pesel";
+    private static final String EMAIL_FIELD = "email";
+    private static final String PASSWORD_FIELD = "password";
 
     @Autowired
     private MockMvc mockMvc;
@@ -57,16 +61,21 @@ class RegistrationControllerIntegrationTest {
     private BonusGrantingService bonusGrantingService;
 
     private RegistrationRequest validRequest;
+    private DemoRegistrationRequest validDemoRequest;
 
     @BeforeEach
     void setUp() {
         logger.info("Setting up integration test for registration process");
-        
+
         // Prepare valid registration request using TestRequestFactory
         validRequest = TestRequestFactory.createValidRegistrationRequest();
         // Set constant email for duplicate checking tests
         validRequest.setEmail(TEST_EMAIL);
 
+        // Prepare valid demo registration request
+        validDemoRequest = TestRequestFactory.createValidDemoRegistrationRequest();
+        validDemoRequest.setEmail(TEST_EMAIL);
+
         objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
         objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
     }
@@ -76,18 +85,18 @@ void setUp() {
     void registerUser_WhenValidRequest_ThenReturnCreatedStatus() throws Exception {
         // Arrange
         String requestJson = objectMapper.writeValueAsString(validRequest);
-    
+
         // Act & Assert
         MvcResult result = mockMvc.perform(post(REGISTRATION_ENDPOINT)
-                .contentType(MediaType.APPLICATION_JSON)
-                .content(requestJson))
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(requestJson))
                 .andExpect(status().isCreated())
                 .andExpect(jsonPath("$.firstname").value(TEST_FIRSTNAME))
                 .andExpect(jsonPath("$.lastname").value(TEST_LASTNAME))
                 .andExpect(jsonPath("$.email").value(TEST_EMAIL))
                 .andExpect(jsonPath("$.username").exists())
                 .andReturn();
-        
+
         // Verify response
         String responseJson = result.getResponse().getContentAsString();
         RegistrationResponse response = objectMapper.readValue(responseJson, RegistrationResponse.class);
@@ -97,49 +106,118 @@ void registerUser_WhenValidRequest_ThenReturnCreatedStatus() throws Exception {
         assertEquals(TEST_EMAIL, response.email());
         assertNotNull(response.username());
     }
-    
+
     @Test
     @DisplayName("Should reject registration for minor with 400 status code")
     void registerUser_WhenMinor_ThenReturnBadRequest() throws Exception {
         // Arrange
         RegistrationRequest minorRequest = TestRequestFactory.createRegistrationRequestForMinor();
         String requestJson = objectMapper.writeValueAsString(minorRequest);
-        
+
         // Act & Assert
         mockMvc.perform(post(REGISTRATION_ENDPOINT)
-                .contentType(MediaType.APPLICATION_JSON)
-                .content(requestJson))
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(requestJson))
                 .andExpect(status().isBadRequest())
                 .andExpect(jsonPath("$.errors[?(@.field=='" + DATE_OF_BIRTH_FIELD + "')]").exists());
     }
-    
+
     @Test
     @DisplayName("Should reject registration with invalid firstname with 400 status code")
     void registerUser_WhenInvalidFirstname_ThenReturnBadRequest() throws Exception {
         // Arrange
         RegistrationRequest invalidFirstnameRequest = TestRequestFactory.createRegistrationRequestWithInvalidFirstname();
         String requestJson = objectMapper.writeValueAsString(invalidFirstnameRequest);
-        
+
         // Act & Assert
         mockMvc.perform(post(REGISTRATION_ENDPOINT)
-                .contentType(MediaType.APPLICATION_JSON)
-                .content(requestJson))
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(requestJson))
                 .andExpect(status().isBadRequest())
                 .andExpect(jsonPath("$.errors[?(@.field=='" + FIRSTNAME_FIELD + "')]").exists());
     }
-    
+
     @Test
     @DisplayName("Should reject registration with invalid PESEL with 400 status code")
     void registerUser_WhenInvalidPesel_ThenReturnBadRequest() throws Exception {
         // Arrange
         RegistrationRequest invalidPeselRequest = TestRequestFactory.createRegistrationRequestWithInvalidPesel();
         String requestJson = objectMapper.writeValueAsString(invalidPeselRequest);
-        
+
         // Act & Assert
         mockMvc.perform(post(REGISTRATION_ENDPOINT)
-                .contentType(MediaType.APPLICATION_JSON)
-                .content(requestJson))
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(requestJson))
                 .andExpect(status().isBadRequest())
                 .andExpect(jsonPath("$.errors[?(@.field=='" + PESEL_FIELD + "')]").exists());
     }
+
+    @Test
+    @DisplayName("Should register a new demo user and return 201 status code")
+    void registerDemoUser_WhenValidRequest_ThenReturnCreatedStatus() throws Exception {
+        // Arrange
+        String requestJson = objectMapper.writeValueAsString(validDemoRequest);
+
+        // Act & Assert
+        MvcResult result = mockMvc.perform(post(DEMO_REGISTRATION_ENDPOINT)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(requestJson))
+                .andExpect(status().isCreated())
+                .andExpect(jsonPath("$.email").value(TEST_EMAIL))
+                .andExpect(jsonPath("$.username").exists())
+                .andReturn();
+
+        // Verify response
+        String responseJson = result.getResponse().getContentAsString();
+        RegistrationResponse response = objectMapper.readValue(responseJson, RegistrationResponse.class);
+        assertNotNull(response);
+        assertEquals(TEST_EMAIL, response.email());
+        assertNotNull(response.username());
+    }
+
+    @Test
+    @DisplayName("Should reject demo registration with invalid email format with 400 status code")
+    void registerDemoUser_WhenInvalidEmail_ThenReturnBadRequest() throws Exception {
+        // Arrange
+        DemoRegistrationRequest invalidEmailRequest = TestRequestFactory.createDemoRegistrationRequestWithInvalidEmail();
+        String requestJson = objectMapper.writeValueAsString(invalidEmailRequest);
+
+        // Act & Assert
+        mockMvc.perform(post(DEMO_REGISTRATION_ENDPOINT)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(requestJson))
+                .andExpect(status().isBadRequest())
+                .andExpect(jsonPath("$.errors[?(@.field=='" + EMAIL_FIELD + "')]").exists());
+    }
+
+    @Test
+    @DisplayName("Should reject demo registration with invalid password format with 400 status code")
+    void registerDemoUser_WhenInvalidPassword_ThenReturnBadRequest() throws Exception {
+        // Arrange
+        DemoRegistrationRequest invalidPasswordRequest = TestRequestFactory.createDemoRegistrationRequestWithInvalidPassword();
+        String requestJson = objectMapper.writeValueAsString(invalidPasswordRequest);
+
+        // Act & Assert
+        mockMvc.perform(post(DEMO_REGISTRATION_ENDPOINT)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(requestJson))
+                .andExpect(status().isBadRequest())
+                .andExpect(jsonPath("$.errors[?(@.field=='" + PASSWORD_FIELD + "')]").exists());
+    }
+
+    @Test
+    @DisplayName("Should reject demo registration with non-matching passwords with 400 status code")
+    void registerDemoUser_WhenNonMatchingPasswords_ThenReturnBadRequest() throws Exception {
+        // Arrange
+        DemoRegistrationRequest nonMatchingPasswordsRequest = TestRequestFactory.createDemoRegistrationRequestWithNonMatchingPasswords();
+        String requestJson = objectMapper.writeValueAsString(nonMatchingPasswordsRequest);
+
+        // Act & Assert
+        mockMvc.perform(post(DEMO_REGISTRATION_ENDPOINT)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(requestJson))
+                .andExpect(status().isBadRequest())
+                .andExpect(jsonPath("$.errors[?(@.field=='confirmPassword')]").exists());
+
+    }
 }
diff --git a/src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationEndToEndTest.java b/src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationEndToEndTest.java
index 10533ed2..d7b18faa 100644
--- a/src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationEndToEndTest.java
+++ b/src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationEndToEndTest.java
@@ -53,7 +53,7 @@ class RegistrationEndToEndTest {
     private static final String TEST_FIRSTNAME = "Jan";
     private static final String TEST_LASTNAME = "Kowalski";
     private static final String TEST_FULLNAME = "Jan Kowalski";
-    private static final String REGISTRATION_ENDPOINT = "/api/registration/register";
+    private static final String REGISTRATION_ENDPOINT = "/api/registration/regular";
 
     @Value("${bankapp.registration.WelcomeBonusAmount:1000}")
     private BigDecimal welcomeBonusAmount;
diff --git a/src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java b/src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java
new file mode 100644
index 00000000..20ea1ae2
--- /dev/null
+++ b/src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java
@@ -0,0 +1,140 @@
+package info.mackiewicz.bankapp.system.registration.service;
+
+import info.mackiewicz.bankapp.system.registration.dto.DemoRegistrationRequest;
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationResponse;
+import info.mackiewicz.bankapp.system.registration.dto.demo.RegistrationRequestFactory;
+import info.mackiewicz.bankapp.system.registration.exception.DemoRegistrationException;
+import info.mackiewicz.bankapp.user.exception.DuplicatedEmailException;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+public class DemoRegistrationServiceTest {
+    private static final String VALID_EMAIL = "john.smith@example.com";
+    private static final String DUPLICATE_EMAIL = "duplicate@example.com";
+    private static final String INVALID_EMAIL = "invalid@example.com";
+    private static final String TEST_EMAIL = "test@example.com";
+
+    private static final String STRONG_PASSWORD = "StrongP@ss123";
+    private static final String WEAK_PASSWORD = "weak";
+
+    private static final String TEST_FIRSTNAME = "John";
+    private static final String TEST_LASTNAME = "Smith";
+    private static final String TEST_USERNAME = "john.smith12345";
+
+    @Mock
+    private RegistrationService registrationService;
+
+    @Mock
+    private RegistrationRequestFactory requestFactory;
+
+    @InjectMocks
+    private DemoRegistrationService demoRegistrationService;
+
+    public DemoRegistrationServiceTest() {
+        MockitoAnnotations.openMocks(this);
+    }
+
+    @Test
+    void givenValidDemoRequest_whenRegisterDemoUser_thenReturnRegistrationResponse() {
+        // Prepare demo registration request with valid credentials
+        DemoRegistrationRequest demoRequest = getDemoRegistrationRequest(VALID_EMAIL, STRONG_PASSWORD);
+
+        // Mock registration request and prepare expected response
+        RegistrationRequest registrationRequest = mock(RegistrationRequest.class);
+        RegistrationResponse registrationResponse = RegistrationResponse.builder()
+                .withFirstname(TEST_FIRSTNAME)
+                .withLastname(TEST_LASTNAME)
+                .withEmail(VALID_EMAIL)
+                .withUsername(TEST_USERNAME)
+                .build();
+
+        // Configure mocks behavior
+        when(requestFactory.createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword())).thenReturn(registrationRequest);
+        when(registrationService.registerUser(any(RegistrationRequest.class))).thenReturn(registrationResponse);
+
+        // Execute registration
+        RegistrationResponse result = demoRegistrationService.registerDemoUser(demoRequest);
+
+        // Verify response data
+        assertEquals(TEST_FIRSTNAME, result.firstname());
+        assertEquals(TEST_LASTNAME, result.lastname());
+        assertEquals(VALID_EMAIL, result.email());
+        assertEquals(TEST_USERNAME, result.username());
+
+        // Verify mock interactions
+        verify(requestFactory, times(1)).createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword());
+        verify(registrationService, times(1)).registerUser(registrationRequest);
+    }
+
+
+    @Test
+    void givenDuplicateEmail_whenRegisterDemoUser_thenThrowDuplicatedEmailException() {
+        // Prepare demo registration request with duplicate email
+        DemoRegistrationRequest demoRequest = getDemoRegistrationRequest(DUPLICATE_EMAIL, STRONG_PASSWORD);
+
+        // Mock registration request
+        RegistrationRequest registrationRequest = mock(RegistrationRequest.class);
+
+        // Configure mocks to simulate duplicate email scenario
+        when(requestFactory.createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword())).thenReturn(registrationRequest);
+        doThrow(DuplicatedEmailException.class).when(registrationService).registerUser(any(RegistrationRequest.class));
+
+        // Verify exception is thrown
+        assertThrows(DuplicatedEmailException.class, () -> demoRegistrationService.registerDemoUser(demoRequest));
+
+        // Verify mock interactions
+        verify(requestFactory, times(1)).createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword());
+        verify(registrationService, times(1)).registerUser(registrationRequest);
+    }
+
+    @Test
+    void givenUnexpectedException_whenRegisterDemoUser_thenThrowDemoRegistrationException() {
+        // Prepare demo registration request
+        DemoRegistrationRequest demoRequest = getDemoRegistrationRequest(INVALID_EMAIL, STRONG_PASSWORD);
+
+        // Mock registration request
+        RegistrationRequest registrationRequest = mock(RegistrationRequest.class);
+
+        // Configure mocks to simulate unexpected runtime exception
+        when(requestFactory.createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword())).thenReturn(registrationRequest);
+        doThrow(RuntimeException.class).when(registrationService).registerUser(any(RegistrationRequest.class));
+
+        // Verify DemoRegistrationException is thrown
+        assertThrows(DemoRegistrationException.class, () -> demoRegistrationService.registerDemoUser(demoRequest));
+
+        // Verify mock interactions
+        verify(requestFactory, times(1)).createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword());
+        verify(registrationService, times(1)).registerUser(registrationRequest);
+    }
+
+    @Test
+    void givenInvalidPassword_whenRegisterDemoUser_thenThrowDemoRegistrationException() {
+        // Prepare demo registration request with weak password
+        DemoRegistrationRequest demoRequest = getDemoRegistrationRequest(TEST_EMAIL, WEAK_PASSWORD);
+
+        // Configure mock to throw exception for invalid password
+        when(requestFactory.createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword()))
+                .thenThrow(DemoRegistrationException.class);
+
+        // Verify DemoRegistrationException is thrown
+        assertThrows(DemoRegistrationException.class, () -> demoRegistrationService.registerDemoUser(demoRequest));
+
+        // Verify mock interactions
+        verify(requestFactory, times(1)).createDemoRegistrationRequest(demoRequest.getEmail(), demoRequest.getPassword());
+        verifyNoInteractions(registrationService);
+    }
+
+    @NotNull
+    private static DemoRegistrationRequest getDemoRegistrationRequest(String email, String password) {
+        return new DemoRegistrationRequest(email, password, password);
+    }
+}
diff --git a/src/test/java/info/mackiewicz/bankapp/testutils/TestRequestFactory.java b/src/test/java/info/mackiewicz/bankapp/testutils/TestRequestFactory.java
index 64fa3124..30ab2a87 100644
--- a/src/test/java/info/mackiewicz/bankapp/testutils/TestRequestFactory.java
+++ b/src/test/java/info/mackiewicz/bankapp/testutils/TestRequestFactory.java
@@ -1,5 +1,6 @@
 package info.mackiewicz.bankapp.testutils;
 
+import info.mackiewicz.bankapp.system.registration.dto.DemoRegistrationRequest;
 import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
 
 import java.time.LocalDate;
@@ -42,6 +43,53 @@ public static RegistrationRequest createValidRegistrationRequest() {
         return request;
     }
 
+    /**
+     * Creates a valid demo registration request.
+     *
+     * @return DemoRegistrationRequest object with valid test data
+     */
+    public static DemoRegistrationRequest createValidDemoRegistrationRequest() {
+        DemoRegistrationRequest request = new DemoRegistrationRequest();
+        request.setEmail(TEST_EMAIL_BASE + random.nextInt(TEST_EMAIL_RANDOM_BOUND) + TEST_EMAIL_DOMAIN);
+        request.setPassword(TEST_PASSWORD);
+        request.setConfirmPassword(TEST_PASSWORD);
+        return request;
+    }
+
+    /**
+     * Creates a demo registration request with an invalid email.
+     *
+     * @return DemoRegistrationRequest object with an invalid email
+     */
+    public static DemoRegistrationRequest createDemoRegistrationRequestWithInvalidEmail() {
+        DemoRegistrationRequest request = createValidDemoRegistrationRequest();
+        request.setEmail("invalid-email"); // Invalid email format
+        return request;
+    }
+
+    /**
+     * Creates a demo registration request with an invalid password.
+     *
+     * @return DemoRegistrationRequest object with an invalid password
+     */
+    public static DemoRegistrationRequest createDemoRegistrationRequestWithInvalidPassword() {
+        DemoRegistrationRequest request = createValidDemoRegistrationRequest();
+        request.setPassword("short"); // Too short password
+        request.setConfirmPassword("short");
+        return request;
+    }
+
+    /**
+     * Creates a demo registration request with non-matching passwords.
+     *
+     * @return DemoRegistrationRequest object with non-matching password and confirmPassword
+     */
+    public static DemoRegistrationRequest createDemoRegistrationRequestWithNonMatchingPasswords() {
+        DemoRegistrationRequest request = createValidDemoRegistrationRequest();
+        request.setConfirmPassword(TEST_PASSWORD + "Different");
+        return request;
+    }
+
     /**
      * Creates a registration request with an invalid first name.
      *
diff --git a/src/test/java/info/mackiewicz/bankapp/testutils/config/TestConfig.java b/src/test/java/info/mackiewicz/bankapp/testutils/config/TestConfig.java
index 27e625d4..9167a81d 100644
--- a/src/test/java/info/mackiewicz/bankapp/testutils/config/TestConfig.java
+++ b/src/test/java/info/mackiewicz/bankapp/testutils/config/TestConfig.java
@@ -45,7 +45,7 @@ public RequestUriHandler requestUriHandler() {
 
     @Bean
     public ApiErrorLogger apiErrorLogger() {
-        return new ApiErrorLogger();
+        return new ApiErrorLogger(true);
     }
 
     @Bean


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>🧹 Nitpick comments (10)</summary><blockquote>

<details>
<summary>src/main/resources/application.yaml (1)</summary><blockquote>

`82-85`: **Fix indentation for consistency with the rest of the file.**

The logging configuration section has inconsistent indentation compared to other configuration sections in this file. The YAML linter also indicates an indentation issue on line 84.


```diff
 # Logging configuration
-    logging:
-     stacktrace:
-         enabled: false
+logging:
+    stacktrace:
+        enabled: false
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 84-84: wrong indentation: expected 8 but found 5

(indentation)

</details>

</details>

</blockquote></details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/user/service/crud/UserCreationService.java (1)</summary><blockquote>

`61-68`: **Fixed order of operations in user creation process**

The updated implementation now correctly generates the username before validation, ensuring the validation process can verify the uniqueness of the generated username.

However, there's a minor issue in the debug logging.

```diff
-        log.debug("Generated username: {}", user.getUsername());
+        log.debug("Generated username: {}", userWithUsername.getUsername());
```

</blockquote></details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationService.java (1)</summary><blockquote>

`36-48`: **Exception handling could be more specific**

The exception handling logic makes a reasonable distinction between duplicate email errors (user's fault) and other internal errors. However, the comment on line 41-42 is slightly limiting.


```diff
-            //Only duplicated email address can be thrown by user's fault
-            // Any other exception is internal problem
+            // Handle email duplication specifically as it's a common user error
+            // All other exceptions are wrapped as internal system errors
```

Consider whether there are other validation exceptions (like `UserValidationException` or `InvalidAgeException`) that might also be considered user-related errors rather than internal problems. If so, you might want to handle them specifically as well.

</blockquote></details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/controller/RegistrationController.java (1)</summary><blockquote>

`102-110`: **Update the example error path.**

The example JSON for error response still references the old endpoint path `/api/registration/register` which should be updated to `/api/registration/demo`.


```diff
                                             {
                                              "status": "CONFLICT",
                                              "title": "USER_ALREADY_EXISTS",
                                              "message": "User with these credentials already exists.",
-                                              "path": "/api/registration/register",
+                                              "path": "/api/registration/demo",
                                              "timestamp": "11-04-2025 16:18:29"
                                             }
```

</blockquote></details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java (1)</summary><blockquote>

`48-54`: **Consider implementing PESEL validation rules.**

Polish PESEL numbers follow specific formatting rules including encoded date of birth, gender, and a checksum digit. The current implementation generates random digits without adhering to these rules.


A more realistic implementation would:
1. Encode the date of birth in the first 6 digits
2. Include gender information in the appropriate digit
3. Calculate and include the checksum digit

If valid PESEL numbers are important for testing or other systems, consider implementing a more sophisticated generator.

</blockquote></details>
<details>
<summary>src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java (5)</summary><blockquote>

`31-32`: **Remove unnecessary blank line.**

There's an empty line at line 32 that could be removed for cleaner code.

```diff
    private static final String TEST_USERNAME = "john.smith12345";
    @Mock
-
-

+
```

---

`20-23`: **Clarify purpose of INVALID_EMAIL constant.**

The `INVALID_EMAIL` constant is used in the third test where a general `RuntimeException` is thrown, but it's not clear what makes this email "invalid" since the test isn't specifically testing email validation failure. Consider renaming it to better reflect its purpose in the test context.

```diff
-    private static final String INVALID_EMAIL = "invalid@example.com";
+    private static final String GENERAL_ERROR_EMAIL = "error@example.com";
```

---

`46-102`: **Consider refactoring duplicated DemoRegistrationRequest creation.**

The code to create and populate the `DemoRegistrationRequest` object is duplicated across all four tests. Consider extracting this to a helper method to reduce duplication and make the tests more maintainable.

```java
private DemoRegistrationRequest createDemoRequest(String email, String password) {
    DemoRegistrationRequest demoRequest = new DemoRegistrationRequest();
    demoRequest.setEmail(email);
    demoRequest.setPassword(password);
    demoRequest.setConfirmPassword(password);
    return demoRequest;
}
```

Then in each test, you could use:
```java
DemoRegistrationRequest demoRequest = createDemoRequest(VALID_EMAIL, STRONG_PASSWORD);
```

---

`71-74`: **Add explicit assertion messages for better test diagnostics.**

Add descriptive messages to your assertions to make test failures more informative. This helps identify what failed and why when tests don't pass.

```diff
-        assertEquals(TEST_FIRSTNAME, result.firstname());
-        assertEquals(TEST_LASTNAME, result.lastname());
-        assertEquals(VALID_EMAIL, result.email());
-        assertEquals(TEST_USERNAME, result.username());
+        assertEquals(TEST_FIRSTNAME, result.firstname(), "Firstname should match the test data");
+        assertEquals(TEST_LASTNAME, result.lastname(), "Lastname should match the test data");
+        assertEquals(VALID_EMAIL, result.email(), "Email should match the input email");
+        assertEquals(TEST_USERNAME, result.username(), "Username should match the test data");
```

---

`19-145`: **Consider adding a test for mismatched password confirmation.**

The test suite covers successful registration, duplicate email errors, unexpected runtime errors, and invalid password scenarios. However, there's no test for the scenario where the password and confirmation password don't match, which is a common validation case for registration forms.

```java
@Test
void givenMismatchedPasswords_whenRegisterDemoUser_thenThrowValidationException() {
    // Prepare demo registration request with mismatched passwords
    DemoRegistrationRequest demoRequest = new DemoRegistrationRequest();
    demoRequest.setEmail(TEST_EMAIL);
    demoRequest.setPassword(STRONG_PASSWORD);
    demoRequest.setConfirmPassword("DifferentP@ss123");

    // Verify validation exception is thrown
    assertThrows(ValidationException.class, () -> demoRegistrationService.registerDemoUser(demoRequest));

    // Verify no interactions with mocks
    verifyNoInteractions(requestFactory);
    verifyNoInteractions(registrationService);
}
```

Note: Replace `ValidationException` with the actual exception type your application throws for validation errors.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 5d21c858aa50034be5ff24cb7f14701f63684b20 and 69425b95ea99c6714b256d1058d6d9ed9bc1e9a8.

</details>

<details>
<summary>📒 Files selected for processing (17)</summary>

* `src/main/java/info/mackiewicz/bankapp/shared/infrastructure/logging/ApiErrorLogger.java` (5 hunks)
* `src/main/java/info/mackiewicz/bankapp/system/registration/controller/DefaultRegistrationController.java` (3 hunks)
* `src/main/java/info/mackiewicz/bankapp/system/registration/controller/RegistrationController.java` (2 hunks)
* `src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java` (1 hunks)
* `src/main/java/info/mackiewicz/bankapp/system/registration/dto/RegistrationRequest.java` (1 hunks)
* `src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java` (1 hunks)
* `src/main/java/info/mackiewicz/bankapp/system/registration/exception/DemoRegistrationException.java` (1 hunks)
* `src/main/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationService.java` (1 hunks)
* `src/main/java/info/mackiewicz/bankapp/user/service/UserService.java` (1 hunks)
* `src/main/java/info/mackiewicz/bankapp/user/service/crud/UserCreationService.java` (2 hunks)
* `src/main/java/info/mackiewicz/bankapp/user/service/util/UserValidationService.java` (1 hunks)
* `src/main/resources/application.yaml` (1 hunks)
* `src/main/resources/logback-spring.xml` (7 hunks)
* `src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java` (1 hunks)
* `src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationEndToEndTest.java` (1 hunks)
* `src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java` (1 hunks)
* `src/test/java/info/mackiewicz/bankapp/testutils/config/TestConfig.java` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationService.java (2)</summary><blockquote>

<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/exception/DemoRegistrationException.java (1)</summary>

* `DemoRegistrationException` (6-16)

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/user/exception/DuplicatedEmailException.java (1)</summary>

* `DuplicatedEmailException` (3-10)

</details>

</blockquote></details>

</details><details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>src/main/resources/application.yaml</summary>

[warning] 84-84: wrong indentation: expected 8 but found 5

(indentation)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (27)</summary><blockquote>

<details>
<summary>src/test/java/info/mackiewicz/bankapp/testutils/config/TestConfig.java (1)</summary>

`47-48`: **LGTM!**

The ApiErrorLogger initialization now correctly passes `true` to enable stack trace logging in test environments, which aligns with the newly introduced configuration property in application.yaml.

</details>
<details>
<summary>src/main/resources/logback-spring.xml (2)</summary>

`3-7`: **Good refactoring to centralize log patterns.**

Centralizing the log patterns as reusable properties improves maintainability and ensures consistency across different appenders. The addition of MDC context `[%X]` in the patterns will enhance log traceability.

---

`13-13`: **LGTM!**

All appenders now consistently reference the centralized pattern properties, which ensures uniform logging format throughout the application.



Also applies to: 20-20, 32-32, 43-43, 54-54, 65-65, 77-77, 88-88

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/shared/infrastructure/logging/ApiErrorLogger.java (4)</summary>

`39-43`: **Well implemented configuration injection.**

Converting the static flag to a configurable property is a good practice. This allows for environment-specific stacktrace logging configuration without code changes.

---

`60-60`: **LGTM!**

The method now correctly uses the instance variable to determine whether to include stack traces in error logs.

---

`86-94`: **Code simplification looks good.**

The direct return of the formatted string improves readability and removes unnecessary variable assignment.

---

`112-113`: **Good removal of redundant variable.**

Simplifying the return statement by removing the unnecessary temporary variable makes the code more concise.

</details>
<details>
<summary>src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationEndToEndTest.java (1)</summary>

`56-56`: **Endpoint path update is aligned with new registration flow**

The endpoint path update from `/api/registration/register` to `/api/registration/regular` correctly reflects the architectural changes introduced in this PR, where registration has been split into regular and demo flows.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/user/service/UserService.java (1)</summary>

`41-47`: **Improved exception documentation specificity**

The JavaDoc has been enhanced with more specific exception types, making the API contract clearer to consumers. Breaking down the generic `DuplicatedUserException` into specific exceptions for email, username, PESEL, and phone number improves error handling capabilities.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/dto/RegistrationRequest.java (1)</summary>

`65-65`: <details>
<summary>✅ Verification successful</summary>

**Email validation changed from @Email to regex pattern**

Replacing the standard `@Email` annotation with a custom regex pattern for consistent email validation across regular and demo registration flows.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check the email regex pattern to ensure it's comprehensive and follows best practices

# Find the EMAIL_PATTERN constant definition
rg "EMAIL_PATTERN\s*=" --type java

# Check if the new DemoRegistrationRequest uses the same pattern
rg "DemoRegistrationRequest.*email" --type java -A 5 -B 5
```

Length of output: 2018

---



</details>

**Email Validation Update – Custom Regex Approach Confirmed**

The change from using the standard `@Email` annotation to applying the custom regex pattern via the `@Pattern` annotation (with `ValidationConstants.EMAIL_PATTERN`) has been verified. The pattern, as defined in `src/main/java/info/mackiewicz/bankapp/shared/validation/ValidationConstants.java`, adheres to our expected specifications and is consistently applied across registration flows.

- The regex pattern meets our validation best practices.
- No discrepancies were found in related demo registration implementations.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/user/service/util/UserValidationService.java (1)</summary>

`53-59`: **Improved exception documentation clarity**

The updated Javadoc now clearly documents specific exceptions for each validation case, replacing the generic `DuplicatedUserException` with more granular exceptions like `DuplicatedEmailException`, `DuplicatedUsernameException`, etc. This provides better guidance for consumers of this API.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/user/service/crud/UserCreationService.java (2)</summary>

`4-4`: **LGTM: Wildcard import for user exceptions**

Using a wildcard import for all user exceptions simplifies import statements and makes it easier to update when adding new exception types.

---

`50-56`: **Improved exception documentation clarity**

Similar to the changes in UserValidationService, the Javadoc now documents specific exceptions for each validation failure case, replacing the generic `DuplicatedUserException`.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationService.java (1)</summary>

`1-35`: **Clean implementation of demo registration service**

The service is well-structured with clear documentation explaining its purpose and dependencies. The class follows the single responsibility principle by focusing solely on demo user registration.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/controller/DefaultRegistrationController.java (3)</summary>

`3-7`: **Added imports for demo registration**

Appropriate imports added for the new demo registration functionality.

---

`20-20`: **Added DemoRegistrationService dependency**

Good practice to use dependency injection for the new service.

---

`32-41`: **Clean implementation of demo registration endpoint**

The implementation follows the same pattern as the regular registration endpoint, maintaining consistency in the API design. The method appropriately delegates to the DemoRegistrationService and returns the response with a Created (201) status.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/exception/DemoRegistrationException.java (2)</summary>

`6-8`: **LGTM! The exception class is well-structured.**

The class name accurately describes its purpose and extends the appropriate base exception class. Using `ErrorCode.INTERNAL_ERROR` is appropriate if this exception represents unexpected internal errors rather than validation issues.

---

`10-15`: **LGTM! The constructors are properly implemented.**

Both constructors correctly pass their arguments to the parent class, along with the defined error code.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java (3)</summary>

`14-18`: **LGTM! Class structure and validation annotations look good.**

The class implements the appropriate interface and has proper documentation. The `@PasswordMatches` annotation at the class level ensures consistency between password fields.

---

`20-23`: **LGTM! Email validation is well-implemented.**

Using a regex pattern for email validation (rather than `@Email`) aligns with the PR's intention of updating the email validation approach.

---

`25-32`: **LGTM! Password validation looks good.**

The password field is properly annotated with `@Password` for validation, and both password fields have appropriate documentation.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/controller/RegistrationController.java (2)</summary>

`70-71`: **LGTM! The endpoint name change is clear.**

Renaming the path from `/register` to `/regular` clarifies the purpose of this endpoint while distinguishing it from the new demo registration endpoint.

---

`73-113`: **LGTM! The new demo registration endpoint is well-documented.**

The Swagger annotations provide clear documentation for the new endpoint, including comprehensive response descriptions.

</details>
<details>
<summary>src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java (2)</summary>

`10-14`: **LGTM! The class is well-documented.**

The component clearly indicates its purpose of generating demo user data.

---

`27-40`: **LGTM! The method creates a well-formed request.**

The method correctly sets all required fields for a demo registration request using appropriate default values and random data.

</details>
<details>
<summary>src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java (1)</summary>

`19-146`: **Well-structured test class with good coverage of happy path and error scenarios.**

The test class is well-organized with clear test method names that follow the given-when-then pattern. Each test has a distinct structure for setup, execution, and verification. The tests cover the happy path and various error scenarios, verifying both the behavior and interactions with dependencies.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: CHANGES_REQUESTED
author_association: NONE
submitted_at: 2025-04-15T11:24:08Z
user: login: pawel-mackiewicz
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-15T11:26:39Z
user: login: pawel-mackiewicz
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-15T11:27:00Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-15T11:27:09Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-15T11:27:38Z
user: login: pawel-mackiewicz
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-15T11:27:41Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-15T11:28:03Z
user: login: pawel-mackiewicz
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-15T11:28:58Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-15T11:29:20Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java (1)</summary><blockquote>

`43-44`: **Endpoint paths correctly updated to match controller changes.**

The update to the registration endpoint constants aligns with the changes to the controller interface, ensuring tests target the correct endpoints.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java (1)</summary><blockquote>

`208-220`: **Inconsistent error field validation in non-matching passwords test.**

Unlike the other validation tests which verify both the status code and specific error fields, this test only checks for the 400 status code without validating which field caused the error.

Consider enhancing the test to verify the error field like the other validation tests:

```diff
                .andExpect(status().isBadRequest())
+               .andExpect(jsonPath("$.errors[?(@.field=='confirmPassword')]").exists());
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 69425b95ea99c6714b256d1058d6d9ed9bc1e9a8 and 6a31db572594c782a7c5360748f29f929da8bbd5.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java` (5 hunks)
* `src/test/java/info/mackiewicz/bankapp/testutils/TestRequestFactory.java` (2 hunks)

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>src/test/java/info/mackiewicz/bankapp/testutils/TestRequestFactory.java (3)</summary>

`3-3`: **Appropriate import added for demo registration functionality.**

The import of `DemoRegistrationRequest` properly supports the new demo user registration feature mentioned in the PR objectives.

---

`46-57`: **Well-structured factory method for valid demo requests.**

The implementation follows the same pattern as other factory methods in this class, using the existing test constants for email and password. Good use of JavaDoc with clear description of the method's purpose.

---

`59-91`: **Comprehensive test cases for validation scenarios.**

These factory methods cover all key validation scenarios (invalid email, invalid password, non-matching passwords) that would be expected for the demo registration functionality. Each method is properly documented and implements the appropriate test case logic.

</details>
<details>
<summary>src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java (5)</summary>

`7-7`: **Appropriate import added for demo registration functionality.**

The import of `DemoRegistrationRequest` is correctly included to support testing the new demo user registration feature.

---

`48-50`: **Field constants added for validation testing.**

Good practice to define constants for field names used in validation testing, improving readability and maintainability.

---

`64-64`: **Proper initialization of demo registration test data.**

The test correctly initializes the demo registration request using the factory method and sets up a consistent test email, following the same pattern as the regular registration tests.



Also applies to: 75-78

---

`155-176`: **Well-structured test for successful demo registration.**

The test follows best practices:
1. Clear arrangement of test data
2. Comprehensive assertions checking both HTTP status and response body fields
3. Additional verification of the deserialized response object

This provides thorough coverage of the happy path for demo registration.

---

`178-206`: **Thorough validation tests for email and password.**

These tests effectively verify that the demo registration endpoint correctly validates the email and password fields, checking both the HTTP status code and the specific error field in the response.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-15T12:53:52Z
user: login: coderabbitai[bot]
state: APPROVED
author_association: NONE
submitted_at: 2025-04-15T12:53:55Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java (1)</summary><blockquote>

`208-222`: **Comprehensive test for password confirmation validation.**

The test properly verifies that non-matching passwords are rejected with a 400 status and that the error is correctly associated with the 'confirmPassword' field.


Consider adding one more validation test for duplicate email scenarios with the demo registration endpoint, similar to what you might have for the regular registration, to ensure consistent validation across both endpoints.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d9291d7f7dc777d014926a3c78ca0bdf9314530f and 8c22496313cf0037f7ed905775cc4328563faf61.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java` (1 hunks)
* `src/main/java/info/mackiewicz/bankapp/system/registration/service/DefaultRegistrationService.java` (0 hunks)
* `src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java` (5 hunks)
* `src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* src/main/java/info/mackiewicz/bankapp/system/registration/service/DefaultRegistrationService.java

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary>

* src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java
* src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java (1)</summary><blockquote>

<details>
<summary>src/test/java/info/mackiewicz/bankapp/testutils/TestRequestFactory.java (1)</summary>

* `TestRequestFactory` (12-125)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java (8)</summary>

`7-7`: **Proper import added for the new demo registration feature.**

Import of `DemoRegistrationRequest` DTO aligns with the demo registration feature being added.

---

`43-44`: **Endpoint paths updated correctly to reflect API structure change.**

The registration endpoint path has been properly updated to `/api/registration/regular` and a new demo registration endpoint path `/api/registration/demo` has been added, accurately reflecting the controller changes mentioned in the PR.

---

`48-49`: **Field constants appropriately defined for validation tests.**

The addition of `EMAIL_FIELD` and `PASSWORD_FIELD` constants prepares for the validation checks in the new demo registration tests, following the same pattern as existing field constants.

---

`64-64`: **New demo request test data field added.**

Added `validDemoRequest` field to support testing the new demo registration functionality.

---

`75-77`: **Test data setup properly implemented for demo registration.**

The setUp method correctly initializes the demo registration request using the TestRequestFactory, setting a consistent email for testing.

---

`155-176`: **Well-structured test for successful demo user registration.**

This test thoroughly verifies the happy path for demo user registration, checking both the HTTP status code and response content. The verification of the response object properties is comprehensive.

---

`178-191`: **Effective validation test for invalid email format.**

The test appropriately verifies that the API returns the expected 400 status and error field when an invalid email is provided in the demo registration request.

---

`193-206`: **Thorough validation test for invalid password format.**

This test correctly verifies password validation by ensuring a 400 status is returned with appropriate error details when an invalid password is submitted.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-15T13:20:02Z

Review comments:
diff_hunk: @@ -39,7 +39,7 @@ class RegistrationControllerIntegrationTest {
     private static final String TEST_EMAIL = "jan.kowalski@example.com";
     private static final String TEST_FIRSTNAME = "Jan";
     private static final String TEST_LASTNAME = "Kowalski";
-    private static final String REGISTRATION_ENDPOINT = "/api/registration/register";
+    private static final String REGISTRATION_ENDPOINT = "/api/registration/regular";
path: src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Endpoint path correctly updated to match controller changes**

The update to the registration endpoint constant aligns with the changes to the controller interface, ensuring tests target the correct endpoint.


You should consider adding integration tests for the new demo registration endpoint (`/api/registration/demo`) to ensure complete test coverage of the new functionality introduced in this PR.

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 6a31db5
created_at: 2025-04-15T11:24:06Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+package info.mackiewicz.bankapp.system.registration.dto;
+
+import info.mackiewicz.bankapp.presentation.auth.validation.Password;
+import info.mackiewicz.bankapp.presentation.auth.validation.PasswordMatches;
+import info.mackiewicz.bankapp.shared.validation.ValidationConstants;
+import info.mackiewicz.bankapp.shared.web.dto.interfaces.PasswordConfirmation;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "Registration request DTO for demo users")
+@PasswordMatches
+public class DemoRegistrationRequest implements PasswordConfirmation {
+
+    @NotBlank(message = "Email is required")
+    @Pattern(regexp = ValidationConstants.EMAIL_PATTERN, message = "Invalid email format")
+    @Schema(description = "Email must be a valid email address", example = "john.smith@example.com")
+    private String email;
+
+    @Password
+    @Schema(description = "Password must be at least 8 characters long, contain at least one digit, " +
+            "one lowercase letter, one uppercase letter, and one special character from the set: @$!%*?&", minLength = 8, pattern = ValidationConstants.PASSWORD_PATTERN, example = "StrongP@ss123")
+    private String password;
+
+    @NotBlank(message = "Password confirmation is required")
+    @Schema(description = "Password confirmation must match the password", example = "StrongP@ss123")
+    private String confirmPassword;
+
+    public EmailAddress getEmail() {
+        return new EmailAddress(email);
+    }
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Handle potential exceptions in the getEmail() method.**

The method creates a new `EmailAddress` object which might throw an exception if validation fails, but the method doesn't declare this.


```diff
-    public EmailAddress getEmail() {
-        return new EmailAddress(email);
-    }
+    public EmailAddress getEmail() {
+        try {
+            return new EmailAddress(email);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid email format", e);
+        }
+    }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    public EmailAddress getEmail() {
        try {
            return new EmailAddress(email);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid email format", e);
        }
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-15T11:24:06Z
author_association: NONE
diff_hunk: @@ -0,0 +1,64 @@
+package info.mackiewicz.bankapp.system.registration.dto.demo;
+
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.Random;
+
+/**
+ * Class that is responsible for creating random data for demo user creation.
+ */
+@Component
+public class RegistrationRequestFactory {
+
+
+    private static final Random RANDOM = new Random();
+    private static final int PESEL_LENGTH = 11;
+    private static final int PHONE_NUMBER_LENGTH = 9;
+    private static final int MAX_PHONE_NUMBER = 999999999;
+    private static final int RANDOM_DIGIT_BOUND = 10;
+    private static final LocalDate DEFAULT_DATE_OF_BIRTH = LocalDate.of(1990, 1, 1);
+    private static final String DEFAULT_FIRSTNAME = "Demo";
+    private static final String DEFAULT_LASTNAME = "User";
+
+
+    public RegistrationRequest createDemoRegistrationRequest(EmailAddress email, String password) {
+        RegistrationRequest request = new RegistrationRequest();
+
+        request.setEmail(email.getValue());
+        request.setPassword(password);
+        request.setConfirmPassword(password);
+        request.setFirstname(DEFAULT_FIRSTNAME);
+        request.setLastname(DEFAULT_LASTNAME);
+        request.setDateOfBirth(DEFAULT_DATE_OF_BIRTH);
+        request.setPesel(generateRandomPesel());
+        request.setPhoneNumber(generateRandomPhoneNumber());
+
+        return request;
+    }
+
+    /**
+     * Generates a random PESEL (Polish national identification number) consisting of a sequence
+     * of digits of a fixed length.
+     *
+     * @return a randomly generated PESEL number in string format
+     */
+    private static String generateRandomPesel() {
+        StringBuilder pesel = new StringBuilder();
+        for (int i = 0; i < PESEL_LENGTH; i++) {
+            pesel.append(RANDOM.nextInt(RANDOM_DIGIT_BOUND));
+        }
+        return pesel.toString();
+    }
+
+    /**
+     * Generates a random phone number consisting of a fixed number of digits.
+     *
+     * @return a randomly generated phone number in string format
+     */
+    private static String generateRandomPhoneNumber() {
+        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", RANDOM.nextInt(MAX_PHONE_NUMBER));
+    }
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Ensure generated phone numbers are valid.**

The current implementation could generate all zeros (`000000000`) if `nextInt(MAX_PHONE_NUMBER)` returns 0, which might not be a valid phone number.


```diff
-    private static String generateRandomPhoneNumber() {
-        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", RANDOM.nextInt(MAX_PHONE_NUMBER));
-    }
+    private static String generateRandomPhoneNumber() {
+        // Ensure the phone number isn't all zeros by using a minimum value
+        int minPhoneNumber = 100000000; // First digit at least 1
+        int randomNumber = java.util.concurrent.ThreadLocalRandom.current().nextInt(minPhoneNumber, MAX_PHONE_NUMBER);
+        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", randomNumber);
+    }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    private static String generateRandomPhoneNumber() {
        // Ensure the phone number isn't all zeros by using a minimum value
        int minPhoneNumber = 100000000; // First digit at least 1
        int randomNumber = java.util.concurrent.ThreadLocalRandom.current().nextInt(minPhoneNumber, MAX_PHONE_NUMBER);
        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", randomNumber);
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-15T11:24:07Z
author_association: NONE
diff_hunk: @@ -0,0 +1,64 @@
+package info.mackiewicz.bankapp.system.registration.dto.demo;
+
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.Random;
+
+/**
+ * Class that is responsible for creating random data for demo user creation.
+ */
+@Component
+public class RegistrationRequestFactory {
+
+
+    private static final Random RANDOM = new Random();
+    private static final int PESEL_LENGTH = 11;
+    private static final int PHONE_NUMBER_LENGTH = 9;
+    private static final int MAX_PHONE_NUMBER = 999999999;
+    private static final int RANDOM_DIGIT_BOUND = 10;
+    private static final LocalDate DEFAULT_DATE_OF_BIRTH = LocalDate.of(1990, 1, 1);
+    private static final String DEFAULT_FIRSTNAME = "Demo";
+    private static final String DEFAULT_LASTNAME = "User";
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider thread safety implications of static Random instance.**

Using a static `Random` instance could lead to thread safety issues if accessed concurrently by multiple threads.


```diff
-    private static final Random RANDOM = new Random();
+    // Use ThreadLocalRandom for better thread safety
+    // No need to make it a class field since we can access it directly
```

And then use it in the methods:

```diff
-            pesel.append(RANDOM.nextInt(RANDOM_DIGIT_BOUND));
+            pesel.append(java.util.concurrent.ThreadLocalRandom.current().nextInt(RANDOM_DIGIT_BOUND));
```

```diff
-        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", RANDOM.nextInt(MAX_PHONE_NUMBER));
+        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", java.util.concurrent.ThreadLocalRandom.current().nextInt(MAX_PHONE_NUMBER));
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
// Before:
//     private static final Random RANDOM = new Random();
    
// After:
    // Use ThreadLocalRandom for better thread safety
    // No need to make it a class field since we can access it directly
    private static final int PESEL_LENGTH = 11;
    private static final int PHONE_NUMBER_LENGTH = 9;
    private static final int MAX_PHONE_NUMBER = 999999999;
    private static final int RANDOM_DIGIT_BOUND = 10;
    private static final LocalDate DEFAULT_DATE_OF_BIRTH = LocalDate.of(1990, 1, 1);
    private static final String DEFAULT_FIRSTNAME = "Demo";
    private static final String DEFAULT_LASTNAME = "User";

// ... other parts of the class ...

// Example method updating pesel generation:
public static String generatePesel() {
    StringBuilder pesel = new StringBuilder();
    // Instead of: pesel.append(RANDOM.nextInt(RANDOM_DIGIT_BOUND));
    pesel.append(java.util.concurrent.ThreadLocalRandom.current().nextInt(RANDOM_DIGIT_BOUND));
    // ... rest of the implementation ...
    return pesel.toString();
}

// Example method updating phone number generation:
public static String generatePhoneNumber() {
    // Instead of:
    // return String.format("%0" + PHONE_NUMBER_LENGTH + "d", RANDOM.nextInt(MAX_PHONE_NUMBER));
    return String.format("%0" + PHONE_NUMBER_LENGTH + "d", 
           java.util.concurrent.ThreadLocalRandom.current().nextInt(MAX_PHONE_NUMBER));
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-15T11:24:07Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+package info.mackiewicz.bankapp.system.registration.dto;
+
+import info.mackiewicz.bankapp.presentation.auth.validation.Password;
+import info.mackiewicz.bankapp.presentation.auth.validation.PasswordMatches;
+import info.mackiewicz.bankapp.shared.validation.ValidationConstants;
+import info.mackiewicz.bankapp.shared.web.dto.interfaces.PasswordConfirmation;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "Registration request DTO for demo users")
+@PasswordMatches
+public class DemoRegistrationRequest implements PasswordConfirmation {
+
+    @NotBlank(message = "Email is required")
+    @Pattern(regexp = ValidationConstants.EMAIL_PATTERN, message = "Invalid email format")
+    @Schema(description = "Email must be a valid email address", example = "john.smith@example.com")
+    private String email;
+
+    @Password
+    @Schema(description = "Password must be at least 8 characters long, contain at least one digit, " +
+            "one lowercase letter, one uppercase letter, and one special character from the set: @$!%*?&", minLength = 8, pattern = ValidationConstants.PASSWORD_PATTERN, example = "StrongP@ss123")
+    private String password;
+
+    @NotBlank(message = "Password confirmation is required")
+    @Schema(description = "Password confirmation must match the password", example = "StrongP@ss123")
+    private String confirmPassword;
+
+    public EmailAddress getEmail() {
+        return new EmailAddress(email);
+    }
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java
user: login: pawel-mackiewicz
body: there are checks at variable declaration above. this object cant be invalid at that point.
created_at: 2025-04-15T11:26:38Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,37 @@
+package info.mackiewicz.bankapp.system.registration.dto;
+
+import info.mackiewicz.bankapp.presentation.auth.validation.Password;
+import info.mackiewicz.bankapp.presentation.auth.validation.PasswordMatches;
+import info.mackiewicz.bankapp.shared.validation.ValidationConstants;
+import info.mackiewicz.bankapp.shared.web.dto.interfaces.PasswordConfirmation;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "Registration request DTO for demo users")
+@PasswordMatches
+public class DemoRegistrationRequest implements PasswordConfirmation {
+
+    @NotBlank(message = "Email is required")
+    @Pattern(regexp = ValidationConstants.EMAIL_PATTERN, message = "Invalid email format")
+    @Schema(description = "Email must be a valid email address", example = "john.smith@example.com")
+    private String email;
+
+    @Password
+    @Schema(description = "Password must be at least 8 characters long, contain at least one digit, " +
+            "one lowercase letter, one uppercase letter, and one special character from the set: @$!%*?&", minLength = 8, pattern = ValidationConstants.PASSWORD_PATTERN, example = "StrongP@ss123")
+    private String password;
+
+    @NotBlank(message = "Password confirmation is required")
+    @Schema(description = "Password confirmation must match the password", example = "StrongP@ss123")
+    private String confirmPassword;
+
+    public EmailAddress getEmail() {
+        return new EmailAddress(email);
+    }
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java
user: login: pawel-mackiewicz
body: @coderabbitai there are checks at variable declaration above. this object cant be invalid at that point.
created_at: 2025-04-15T11:27:00Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,37 @@
+package info.mackiewicz.bankapp.system.registration.dto;
+
+import info.mackiewicz.bankapp.presentation.auth.validation.Password;
+import info.mackiewicz.bankapp.presentation.auth.validation.PasswordMatches;
+import info.mackiewicz.bankapp.shared.validation.ValidationConstants;
+import info.mackiewicz.bankapp.shared.web.dto.interfaces.PasswordConfirmation;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "Registration request DTO for demo users")
+@PasswordMatches
+public class DemoRegistrationRequest implements PasswordConfirmation {
+
+    @NotBlank(message = "Email is required")
+    @Pattern(regexp = ValidationConstants.EMAIL_PATTERN, message = "Invalid email format")
+    @Schema(description = "Email must be a valid email address", example = "john.smith@example.com")
+    private String email;
+
+    @Password
+    @Schema(description = "Password must be at least 8 characters long, contain at least one digit, " +
+            "one lowercase letter, one uppercase letter, and one special character from the set: @$!%*?&", minLength = 8, pattern = ValidationConstants.PASSWORD_PATTERN, example = "StrongP@ss123")
+    private String password;
+
+    @NotBlank(message = "Password confirmation is required")
+    @Schema(description = "Password confirmation must match the password", example = "StrongP@ss123")
+    private String confirmPassword;
+
+    public EmailAddress getEmail() {
+        return new EmailAddress(email);
+    }
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java
user: login: coderabbitai[bot]
body: @Pawel-Mackiewicz You're absolutely right. The field-level validations (`@NotBlank` and `@Pattern`) ensure that the email is already validated before `getEmail()` would be called, especially when the DTO is used with the `@Valid` annotation in controllers. My suggestion was redundant in this context. Thanks for the clarification.



(⌐■_■)
---
<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Pawel-Mackiewicz
PR: Pawel-Mackiewicz/BankApp#237
File: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java:34-36
Timestamp: 2025-04-15T11:27:07.927Z
Learning: In DTOs used with @Valid annotation in controllers, field-level validations (like @NotBlank and @Pattern) are sufficient as they're applied before the DTO's methods are called, making additional exception handling in getter methods unnecessary.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>



<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@Pawel-Mackiewicz You're absolutely right. The field-level validations (`@NotBlank` and `@Pattern`) ensure that the email is already validated before `getEmail()` would be called, especially when the DTO is used with the `@Valid` annotation in controllers. My suggestion was redundant in this context. Thanks for the clarification.



(⌐■_■)
---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Pawel-Mackiewicz
PR: Pawel-Mackiewicz/BankApp#237
File: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java:34-36
Timestamp: 2025-04-15T11:27:07.927Z
Learning: In DTOs used with @Valid annotation in controllers, field-level validations (like @NotBlank and @Pattern) are sufficient as they're applied before the DTO's methods are called, making additional exception handling in getter methods unnecessary.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details></answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-15T11:27:09Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+package info.mackiewicz.bankapp.system.registration.dto;
+
+import info.mackiewicz.bankapp.presentation.auth.validation.Password;
+import info.mackiewicz.bankapp.presentation.auth.validation.PasswordMatches;
+import info.mackiewicz.bankapp.shared.validation.ValidationConstants;
+import info.mackiewicz.bankapp.shared.web.dto.interfaces.PasswordConfirmation;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "Registration request DTO for demo users")
+@PasswordMatches
+public class DemoRegistrationRequest implements PasswordConfirmation {
+
+    @NotBlank(message = "Email is required")
+    @Pattern(regexp = ValidationConstants.EMAIL_PATTERN, message = "Invalid email format")
+    @Schema(description = "Email must be a valid email address", example = "john.smith@example.com")
+    private String email;
+
+    @Password
+    @Schema(description = "Password must be at least 8 characters long, contain at least one digit, " +
+            "one lowercase letter, one uppercase letter, and one special character from the set: @$!%*?&", minLength = 8, pattern = ValidationConstants.PASSWORD_PATTERN, example = "StrongP@ss123")
+    private String password;
+
+    @NotBlank(message = "Password confirmation is required")
+    @Schema(description = "Password confirmation must match the password", example = "StrongP@ss123")
+    private String confirmPassword;
+
+    public EmailAddress getEmail() {
+        return new EmailAddress(email);
+    }
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java
user: login: coderabbitai[bot]
body: @Pawel-Mackiewicz You're absolutely right. The field-level validation with `@NotBlank` and `@Pattern(regexp = ValidationConstants.EMAIL_PATTERN)` ensures that the email value is already validated before `getEmail()` is called. When this DTO is used with `@Valid` in a controller, those validations are enforced by the framework, so by the time `getEmail()` executes, the email string will have already passed validation. My suggestion to add exception handling was unnecessary. Thank you for the correction.
---
<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Pawel-Mackiewicz
PR: Pawel-Mackiewicz/BankApp#237
File: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java:34-36
Timestamp: 2025-04-15T11:27:36.687Z
Learning: In Spring/Jakarta validation flow, field-level annotations like @NotBlank and @Pattern in DTOs are sufficient for validation when the DTO is used with @Valid annotation in controllers. Additional exception handling in getter methods is unnecessary as validation occurs before these methods are called.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>



<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@Pawel-Mackiewicz You're absolutely right. The field-level validation with `@NotBlank` and `@Pattern(regexp = ValidationConstants.EMAIL_PATTERN)` ensures that the email value is already validated before `getEmail()` is called. When this DTO is used with `@Valid` in a controller, those validations are enforced by the framework, so by the time `getEmail()` executes, the email string will have already passed validation. My suggestion to add exception handling was unnecessary. Thank you for the correction.
---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Pawel-Mackiewicz
PR: Pawel-Mackiewicz/BankApp#237
File: src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java:34-36
Timestamp: 2025-04-15T11:27:36.687Z
Learning: In Spring/Jakarta validation flow, field-level annotations like @NotBlank and @Pattern in DTOs are sufficient for validation when the DTO is used with @Valid annotation in controllers. Additional exception handling in getter methods is unnecessary as validation occurs before these methods are called.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details></answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-15T11:27:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,64 @@
+package info.mackiewicz.bankapp.system.registration.dto.demo;
+
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.Random;
+
+/**
+ * Class that is responsible for creating random data for demo user creation.
+ */
+@Component
+public class RegistrationRequestFactory {
+
+
+    private static final Random RANDOM = new Random();
+    private static final int PESEL_LENGTH = 11;
+    private static final int PHONE_NUMBER_LENGTH = 9;
+    private static final int MAX_PHONE_NUMBER = 999999999;
+    private static final int RANDOM_DIGIT_BOUND = 10;
+    private static final LocalDate DEFAULT_DATE_OF_BIRTH = LocalDate.of(1990, 1, 1);
+    private static final String DEFAULT_FIRSTNAME = "Demo";
+    private static final String DEFAULT_LASTNAME = "User";
+
+
+    public RegistrationRequest createDemoRegistrationRequest(EmailAddress email, String password) {
+        RegistrationRequest request = new RegistrationRequest();
+
+        request.setEmail(email.getValue());
+        request.setPassword(password);
+        request.setConfirmPassword(password);
+        request.setFirstname(DEFAULT_FIRSTNAME);
+        request.setLastname(DEFAULT_LASTNAME);
+        request.setDateOfBirth(DEFAULT_DATE_OF_BIRTH);
+        request.setPesel(generateRandomPesel());
+        request.setPhoneNumber(generateRandomPhoneNumber());
+
+        return request;
+    }
+
+    /**
+     * Generates a random PESEL (Polish national identification number) consisting of a sequence
+     * of digits of a fixed length.
+     *
+     * @return a randomly generated PESEL number in string format
+     */
+    private static String generateRandomPesel() {
+        StringBuilder pesel = new StringBuilder();
+        for (int i = 0; i < PESEL_LENGTH; i++) {
+            pesel.append(RANDOM.nextInt(RANDOM_DIGIT_BOUND));
+        }
+        return pesel.toString();
+    }
+
+    /**
+     * Generates a random phone number consisting of a fixed number of digits.
+     *
+     * @return a randomly generated phone number in string format
+     */
+    private static String generateRandomPhoneNumber() {
+        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", RANDOM.nextInt(MAX_PHONE_NUMBER));
+    }
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java
user: login: pawel-mackiewicz
body: @coderabbitai its demo. it doesnt have to be valid.
created_at: 2025-04-15T11:27:40Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,64 @@
+package info.mackiewicz.bankapp.system.registration.dto.demo;
+
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.Random;
+
+/**
+ * Class that is responsible for creating random data for demo user creation.
+ */
+@Component
+public class RegistrationRequestFactory {
+
+
+    private static final Random RANDOM = new Random();
+    private static final int PESEL_LENGTH = 11;
+    private static final int PHONE_NUMBER_LENGTH = 9;
+    private static final int MAX_PHONE_NUMBER = 999999999;
+    private static final int RANDOM_DIGIT_BOUND = 10;
+    private static final LocalDate DEFAULT_DATE_OF_BIRTH = LocalDate.of(1990, 1, 1);
+    private static final String DEFAULT_FIRSTNAME = "Demo";
+    private static final String DEFAULT_LASTNAME = "User";
+
+
+    public RegistrationRequest createDemoRegistrationRequest(EmailAddress email, String password) {
+        RegistrationRequest request = new RegistrationRequest();
+
+        request.setEmail(email.getValue());
+        request.setPassword(password);
+        request.setConfirmPassword(password);
+        request.setFirstname(DEFAULT_FIRSTNAME);
+        request.setLastname(DEFAULT_LASTNAME);
+        request.setDateOfBirth(DEFAULT_DATE_OF_BIRTH);
+        request.setPesel(generateRandomPesel());
+        request.setPhoneNumber(generateRandomPhoneNumber());
+
+        return request;
+    }
+
+    /**
+     * Generates a random PESEL (Polish national identification number) consisting of a sequence
+     * of digits of a fixed length.
+     *
+     * @return a randomly generated PESEL number in string format
+     */
+    private static String generateRandomPesel() {
+        StringBuilder pesel = new StringBuilder();
+        for (int i = 0; i < PESEL_LENGTH; i++) {
+            pesel.append(RANDOM.nextInt(RANDOM_DIGIT_BOUND));
+        }
+        return pesel.toString();
+    }
+
+    /**
+     * Generates a random phone number consisting of a fixed number of digits.
+     *
+     * @return a randomly generated phone number in string format
+     */
+    private static String generateRandomPhoneNumber() {
+        return String.format("%0" + PHONE_NUMBER_LENGTH + "d", RANDOM.nextInt(MAX_PHONE_NUMBER));
+    }
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java
user: login: coderabbitai[bot]
body: @Pawel-Mackiewicz I understand. Since it's for demo purposes, the exact format of the phone number doesn't need to meet real-world validation requirements. Thanks for the clarification!



(\_/) 
( •ω•) 
/

>🧠  *thinking about this*
---
<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Pawel-Mackiewicz
PR: Pawel-Mackiewicz/BankApp#237
File: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java:61-63
Timestamp: 2025-04-15T11:28:01.544Z
Learning: The phone number generation in RegistrationRequestFactory is for demo purposes only and doesn't need to follow strict validation rules for real phone numbers.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>



<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@Pawel-Mackiewicz I understand. Since it's for demo purposes, the exact format of the phone number doesn't need to meet real-world validation requirements. Thanks for the clarification!



(\_/) 
( •ω•) 
/ >🧠  *thinking about this*
---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Pawel-Mackiewicz
PR: Pawel-Mackiewicz/BankApp#237
File: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java:61-63
Timestamp: 2025-04-15T11:28:01.544Z
Learning: The phone number generation in RegistrationRequestFactory is for demo purposes only and doesn't need to follow strict validation rules for real phone numbers.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details></answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-15T11:28:03Z
author_association: NONE
diff_hunk: @@ -0,0 +1,64 @@
+package info.mackiewicz.bankapp.system.registration.dto.demo;
+
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.Random;
+
+/**
+ * Class that is responsible for creating random data for demo user creation.
+ */
+@Component
+public class RegistrationRequestFactory {
+
+
+    private static final Random RANDOM = new Random();
+    private static final int PESEL_LENGTH = 11;
+    private static final int PHONE_NUMBER_LENGTH = 9;
+    private static final int MAX_PHONE_NUMBER = 999999999;
+    private static final int RANDOM_DIGIT_BOUND = 10;
+    private static final LocalDate DEFAULT_DATE_OF_BIRTH = LocalDate.of(1990, 1, 1);
+    private static final String DEFAULT_FIRSTNAME = "Demo";
+    private static final String DEFAULT_LASTNAME = "User";
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java
user: login: pawel-mackiewicz
body: this method isn't async.
created_at: 2025-04-15T11:28:57Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,64 @@
+package info.mackiewicz.bankapp.system.registration.dto.demo;
+
+import info.mackiewicz.bankapp.system.registration.dto.RegistrationRequest;
+import info.mackiewicz.bankapp.user.model.vo.EmailAddress;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.Random;
+
+/**
+ * Class that is responsible for creating random data for demo user creation.
+ */
+@Component
+public class RegistrationRequestFactory {
+
+
+    private static final Random RANDOM = new Random();
+    private static final int PESEL_LENGTH = 11;
+    private static final int PHONE_NUMBER_LENGTH = 9;
+    private static final int MAX_PHONE_NUMBER = 999999999;
+    private static final int RANDOM_DIGIT_BOUND = 10;
+    private static final LocalDate DEFAULT_DATE_OF_BIRTH = LocalDate.of(1990, 1, 1);
+    private static final String DEFAULT_FIRSTNAME = "Demo";
+    private static final String DEFAULT_LASTNAME = "User";
path: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java
user: login: coderabbitai[bot]
body: @Pawel-Mackiewicz You're right - since this method isn't asynchronous and won't be accessed concurrently from multiple threads, the thread safety concerns with using a static Random instance aren't applicable here. The current implementation is appropriate for your use case.
---
<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Pawel-Mackiewicz
PR: Pawel-Mackiewicz/BankApp#237
File: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java:16-24
Timestamp: 2025-04-15T11:29:18.467Z
Learning: Thread safety considerations for Random instances in RegistrationRequestFactory aren't necessary as the methods aren't executed asynchronously or concurrently by multiple threads.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>



<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@Pawel-Mackiewicz You're right - since this method isn't asynchronous and won't be accessed concurrently from multiple threads, the thread safety concerns with using a static Random instance aren't applicable here. The current implementation is appropriate for your use case.
---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Pawel-Mackiewicz
PR: Pawel-Mackiewicz/BankApp#237
File: src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java:16-24
Timestamp: 2025-04-15T11:29:18.467Z
Learning: Thread safety considerations for Random instances in RegistrationRequestFactory aren't necessary as the methods aren't executed asynchronously or concurrently by multiple threads.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details></answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-15T11:29:19Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-15T11:20:29Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Walkthrough

This update introduces a dedicated demo user registration flow to the application. It adds new controller endpoints, DTOs, and service classes to handle demo user registration, including validation and error handling. The registration controller interface and implementation are updated to support both regular and demo registrations, with endpoint paths adjusted accordingly. Supporting infrastructure, such as logging configuration and test setups, is updated for improved flexibility and clarity. Integration and unit tests are added or modified to cover the new functionality and ensure proper behavior.

## Changes

| File(s)                                                                                             | Change Summary |
|-----------------------------------------------------------------------------------------------------|---------------|
| `src/main/java/info/mackiewicz/bankapp/system/registration/dto/DemoRegistrationRequest.java`<br>`src/main/java/info/mackiewicz/bankapp/system/registration/dto/demo/RegistrationRequestFactory.java` | Added new DTO and factory class for demo user registration, including validation and random data generation. |
| `src/main/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationService.java`<br>`src/main/java/info/mackiewicz/bankapp/system/registration/exception/DemoRegistrationException.java` | Introduced new service and exception class for demo registration logic and error handling. |
| `src/main/java/info/mackiewicz/bankapp/system/registration/controller/RegistrationController.java`<br>`src/main/java/info/mackiewicz/bankapp/system/registration/controller/DefaultRegistrationController.java` | Updated registration controller interface and implementation: renamed regular registration endpoint, added demo registration endpoint and service dependency. |
| `src/main/java/info/mackiewicz/bankapp/system/registration/dto/RegistrationRequest.java`            | Changed email validation from `@Email` to a regex `@Pattern` for stricter format checking. |
| `src/main/java/info/mackiewicz/bankapp/shared/infrastructure/logging/ApiErrorLogger.java`<br>`src/main/resources/application.yaml`<br>`src/main/resources/logback-spring.xml` | Made stacktrace logging configurable via application property, updated logging configuration for flexibility, and refactored log patterns. |
| `src/main/java/info/mackiewicz/bankapp/user/service/UserService.java`<br>`src/main/java/info/mackiewicz/bankapp/user/service/crud/UserCreationService.java`<br>`src/main/java/info/mackiewicz/bankapp/user/service/util/UserValidationService.java` | Improved JavaDoc comments for user creation and validation methods, specifying distinct exceptions for duplicated fields. |
| `src/test/java/info/mackiewicz/bankapp/system/registration/service/DemoRegistrationServiceTest.java` | Added unit tests for the demo registration service, covering success and error scenarios. |
| `src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationControllerIntegrationTest.java`<br>`src/test/java/info/mackiewicz/bankapp/integration/registration/RegistrationEndToEndTest.java` | Updated test endpoint URLs to match the new regular registration path and added tests for demo registration endpoint. |
| `src/test/java/info/mackiewicz/bankapp/testutils/config/TestConfig.java`                            | Modified test config to instantiate `ApiErrorLogger` with stacktrace logging enabled. |
| `src/test/java/info/mackiewicz/bankapp/testutils/TestRequestFactory.java`                          | Added factory methods to create valid and invalid demo registration request test data. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant RegistrationController
    participant DemoRegistrationService
    participant RegistrationRequestFactory
    participant RegistrationService

    Client->>RegistrationController: POST /api/registration/demo (DemoRegistrationRequest)
    RegistrationController->>DemoRegistrationService: registerDemoUser(request)
    DemoRegistrationService->>RegistrationRequestFactory: createDemoRegistrationRequest(email, password)
    RegistrationRequestFactory-->>DemoRegistrationService: RegistrationRequest
    DemoRegistrationService->>RegistrationService: registerUser(RegistrationRequest)
    RegistrationService-->>DemoRegistrationService: RegistrationResponse
    DemoRegistrationService-->>RegistrationController: RegistrationResponse
    RegistrationController-->>Client: 201 Created + RegistrationResponse
```

## Assessment against linked issues

| Objective                                    | Addressed | Explanation                                      |
|----------------------------------------------|-----------|-------------------------------------------------|
| Support demo user creation flow (#191)       | ✅        |                                                 |
| Add demo registration endpoint and service   | ✅        |                                                 |
| Validate demo registration input and errors  | ✅        |                                                 |
| Update tests to cover demo registration      | ✅        |                                                 |

## Poem

> A demo bunny hops in with glee,  
> New endpoints and services—oh, what a spree!  
> Now demo users can easily appear,  
> With random PESELs and passwords clear.  
> Logging’s configurable, tests all pass,  
> This code is as fresh as springtime grass!  
> 🐇✨

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Pawel-Mackiewicz/BankApp&utm_content=237):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: pawel-mackiewicz
created_at: 2025-04-15T13:19:18Z
body: @coderabbitai review
and after you finish write `squash and merge` commit msg for that PR.
author_association: OWNER
user: login: coderabbitai[bot]
created_at: 2025-04-15T13:19:57Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
> [!TIP]
> For best results, initiate chat on the files or code changes.

@Pawel-Mackiewicz I've reviewed the PR in detail, and it looks excellent! Here's my analysis:

## Review:

The demo user registration functionality is well-implemented with proper separation of concerns:
- The `DemoRegistrationService` properly delegates to the `RegistrationService` and handles exceptions appropriately
- The `DemoRegistrationRequest` DTO has robust validation with proper error messages
- The `RegistrationRequestFactory` elegantly handles demo data generation
- The controller changes are clean with proper OpenAPI documentation

The logging improvements are valuable:
- Configurable stack trace logging is a great addition for environment-specific control
- The refactored log patterns in logback-spring.xml improve maintainability
- MDC visibility enhancements will help with request tracing

Test coverage is comprehensive with both unit and integration tests covering success and error scenarios.

## Suggested Squash and Merge Commit Message:

```
Add demo user registration functionality and improve logging

- Added demo user registration with dedicated endpoint (/api/registration/demo)
- Created DemoRegistrationService with proper exception handling
- Implemented RegistrationRequestFactory to generate demo user data
- Enhanced validation with regex-based email pattern checking
- Made stack trace logging configurable via logging.stacktrace.enabled
- Refactored logging patterns for better maintainability
- Improved exception handling and documentation
- Added comprehensive unit and integration tests

Resolves #191
```

(\_\_/) 
( • ᴗ •) 
/

>📊 Ready to be merged!



<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@Pawel-Mackiewicz I've reviewed the PR in detail, and it looks excellent! Here's my analysis:

## Review:

The demo user registration functionality is well-implemented with proper separation of concerns:
- The `DemoRegistrationService` properly delegates to the `RegistrationService` and handles exceptions appropriately
- The `DemoRegistrationRequest` DTO has robust validation with proper error messages
- The `RegistrationRequestFactory` elegantly handles demo data generation
- The controller changes are clean with proper OpenAPI documentation

The logging improvements are valuable:
- Configurable stack trace logging is a great addition for environment-specific control
- The refactored log patterns in logback-spring.xml improve maintainability
- MDC visibility enhancements will help with request tracing

Test coverage is comprehensive with both unit and integration tests covering success and error scenarios.

## Suggested Squash and Merge Commit Message:

```
Add demo user registration functionality and improve logging

- Added demo user registration with dedicated endpoint (/api/registration/demo)
- Created DemoRegistrationService with proper exception handling
- Implemented RegistrationRequestFactory to generate demo user data
- Enhanced validation with regex-based email pattern checking
- Made stack trace logging configurable via logging.stacktrace.enabled
- Refactored logging patterns for better maintainability
- Improved exception handling and documentation
- Added comprehensive unit and integration tests

Resolves #191
```

(\_\_/) 
( • ᴗ •) 
/ >📊 Ready to be merged!</answer></rawResChunk> -->
author_association: NONE