diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..99526f3 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,83 @@ +name: CD - Docker Publish + +on: + push: + branches: + - main + - release/** + - hotfix/** + workflow_dispatch: + +permissions: + contents: read + packages: write + +env: + DOCKERHUB_IMAGE: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/eventmanagerapp + DOCKERHUB_COMPOSE: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/eventmanagerapp-compose + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + build-and-push: + name: Build and publish Docker image + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Resolve compose tag + id: compose_tag + shell: bash + run: | + SHA="${{ github.sha }}" + SHORT_SHA="${SHA:0:7}" + + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + echo "tag=main-${SHORT_SHA}" >> $GITHUB_OUTPUT + elif [ "${{ github.event_name }}" = "push" ]; then + BRANCH="${{ github.ref_name }}" + SAFE_BRANCH="${BRANCH//\//-}" + echo "tag=${SAFE_BRANCH}-${SHORT_SHA}" >> $GITHUB_OUTPUT + else + echo "tag=manual-${SHORT_SHA}" >> $GITHUB_OUTPUT + fi + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.DOCKERHUB_IMAGE }} + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=tag + type=sha,format=short + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Publish docker-compose.aws.yaml as OCI artifact + if: ${{ github.event_name == 'push' && (github.ref_name == 'main' || startsWith(github.ref_name, 'release/') || startsWith(github.ref_name, 'hotfix/')) }} + run: | + docker compose -f docker-compose.aws.yaml publish \ + ${{ env.DOCKERHUB_COMPOSE }}:${{ steps.compose_tag.outputs.tag }} + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0537fb..f15e7d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,54 +20,10 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: - unit: - name: Unit tests + ci: + name: Build + Unit + Selenium runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: '21' - cache: maven - - - name: Ensure Maven wrapper is executable - run: chmod +x mvnw - - - name: Run unit profile tests - run: ./mvnw -B -Punit test - - - name: Upload surefire reports (unit) - if: always() - uses: actions/upload-artifact@v4 - with: - name: surefire-unit - path: target/surefire-reports/** - if-no-files-found: ignore - - selenium: - name: Selenium tests (${{ matrix.suiteName }}) - runs-on: ubuntu-latest - timeout-minutes: 45 - strategy: - fail-fast: false - matrix: - include: - - suiteName: Auth - suiteInclude: "**/selenium/views/AuthViewsSeleniumTest.java" - - suiteName: Home - suiteInclude: "**/selenium/views/HomeViewSeleniumTest.java" - - suiteName: Event - suiteInclude: "**/selenium/views/EventViewsSeleniumTest.java" - - suiteName: User - suiteInclude: "**/selenium/views/UserViewsSeleniumTest.java" - - suiteName: GiftAndTicket - suiteInclude: "**/selenium/views/GiftAndTicketViewsSeleniumTest.java" + timeout-minutes: 20 services: postgres: @@ -93,80 +49,147 @@ jobs: MINIO_ACCESS_KEY: minioadmin MINIO_SECRET_KEY: minioadmin MINIO_BUCKET_NAME: event-manager-images + SPRING_JPA_HIBERNATE_DDL_AUTO: create-drop steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: '21' cache: maven - - name: Ensure Maven wrapper is executable - run: chmod +x mvnw + - name: Install Maven + run: | + sudo apt-get update + sudo apt-get install -y maven - name: Start MinIO run: | - docker run -d --name minio-ci \ + MINIO_CONTAINER_NAME="minio-ci-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${GITHUB_JOB}" + sudo docker rm -f minio-ci >/dev/null 2>&1 || true + sudo docker rm -f "$MINIO_CONTAINER_NAME" >/dev/null 2>&1 || true + sudo docker run -d --name "$MINIO_CONTAINER_NAME" \ -p 9000:9000 -p 9001:9001 \ -e MINIO_ROOT_USER=${MINIO_ACCESS_KEY} \ -e MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY} \ minio/minio:RELEASE.2024-01-16T16-07-38Z server /data --console-address ":9001" - - name: Run selenium profile tests (shard) - run: ./mvnw -B -Pselenium,selenium-ci -Dselenium.includes="${{ matrix.suiteInclude }}" test + - name: Build frontend (Vue + Vite) + run: | + cd frontend + npm ci + npm run build + cd .. - - name: Upload surefire reports (selenium) + - name: Copy frontend dist to backend static resources + run: | + rm -rf src/main/resources/static + mkdir -p src/main/resources/static + cp -r frontend/dist/* src/main/resources/static/ + + - name: Build backend with frontend (skip tests) + run: | + mvn -B -DskipTests clean package \ + -Dskip.frontend.node.install=true \ + -Dskip.frontend.npm.install=true \ + -Dskip.frontend.npm.build=true \ + -Dskip.frontend.copy.resources=true + + - name: Unit tests + JaCoCo + if: always() + run: | + mvn -B -Punit verify \ + -Dskip.openapi.generate=true \ + -Dskip.frontend.node.install=true \ + -Dskip.frontend.npm.install=true \ + -Dskip.frontend.npm.build=true \ + -Dskip.frontend.copy.resources=true \ + -Djacoco.destFile=target/jacoco-unit.exec \ + -Djacoco.dataFile=target/jacoco-unit.exec \ + -Djacoco.outputDirectory=target/site/jacoco-unit \ + | tee target/unit-test.log + exit ${PIPESTATUS[0]} + + - name: Upload surefire reports (unit) if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: - name: surefire-selenium + name: surefire-unit path: target/surefire-reports/** if-no-files-found: ignore - - name: Upload backend startup log + - name: Upload JaCoCo site (unit) if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: - name: backend-log-${{ matrix.suiteName }} - path: app.log - if-no-files-found: ignore - - coverage: - name: Coverage (JaCoCo) - runs-on: ubuntu-latest - timeout-minutes: 35 - - steps: - - name: Checkout - uses: actions/checkout@v4 + name: coverage-unit-site + path: target/site/jacoco-unit/** + if-no-files-found: error - - name: Set up JDK 21 - uses: actions/setup-java@v4 + - name: Upload JaCoCo XML (unit) + if: always() + uses: actions/upload-artifact@v7 with: - distribution: temurin - java-version: '21' - cache: maven + name: coverage-unit-xml + path: target/site/jacoco-unit/jacoco.xml + if-no-files-found: error - - name: Ensure Maven wrapper is executable - run: chmod +x mvnw + - name: Selenium tests + JaCoCo + if: always() + run: | + mvn -B -Pselenium verify \ + -Dskip.openapi.generate=true \ + -Dskip.frontend.node.install=true \ + -Dskip.frontend.npm.install=true \ + -Dskip.frontend.npm.build=true \ + -Dskip.frontend.copy.resources=true \ + -Djacoco.destFile=target/jacoco-selenium.exec \ + -Djacoco.dataFile=target/jacoco-selenium.exec \ + -Djacoco.outputDirectory=target/site/jacoco-selenium \ + | tee target/selenium-test.log + exit ${PIPESTATUS[0]} - - name: Run verify with unit profile (JaCoCo report) - run: ./mvnw -B -Punit verify + - name: Upload surefire reports (selenium) + if: always() + uses: actions/upload-artifact@v7 + with: + name: surefire-selenium + path: target/surefire-reports/** + if-no-files-found: ignore - - name: Upload JaCoCo site - uses: actions/upload-artifact@v4 + - name: Upload JaCoCo site (selenium) + if: always() + uses: actions/upload-artifact@v7 with: - name: jacoco-site - path: target/site/jacoco/** + name: coverage-selenium-site + path: target/site/jacoco-selenium/** if-no-files-found: error - - name: Upload JaCoCo XML - uses: actions/upload-artifact@v4 + - name: Upload JaCoCo XML (selenium) + if: always() + uses: actions/upload-artifact@v7 with: - name: jacoco-xml - path: target/site/jacoco/jacoco.xml + name: coverage-selenium-xml + path: target/site/jacoco-selenium/jacoco.xml if-no-files-found: error + + - name: Upload backend startup log + if: failure() + uses: actions/upload-artifact@v7 + with: + name: backend-log + path: | + app.log + target/unit-test.log + target/selenium-test.log + if-no-files-found: ignore + + - name: Cleanup MinIO + if: always() + run: | + sudo docker rm -f minio-ci >/dev/null 2>&1 || true + sudo docker rm -f "minio-ci-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${GITHUB_JOB}" >/dev/null 2>&1 || true diff --git a/.gitignore b/.gitignore index df9ca77..65e6350 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,7 @@ truststore* # Docker image exports *.tar + +# act (GitHub Actions local emulator) artifacts and logs +.act-artifacts/ +act-*.txt diff --git a/README.md b/README.md index 1db1703..a0ec6f7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Cobertura JaCoCo](https://img.shields.io/badge/coverage-JaCoCo%20artifact-blue)](https://github.com/codeurjc-students/2025-EventManager/actions/workflows/ci.yml) > **Descripción** -Plataforma de gestión de eventos con autenticación del usuario y gestión de: usuarios, eventos, entradas y regalos. +EventManager es una plataforma web para crear y administrar eventos de forma completa: permite a los usuarios registrarse, acceder, gestionar su perfil y participar en eventos, así como controlar sus entradas y regalos en estos. --- @@ -12,10 +12,10 @@ Plataforma de gestión de eventos con autenticación del usuario y gestión de: - **Autenticación**: registro, login, logout y refresh token. - **Perfil de usuario**: consulta y actualización de datos. -- **Gestión de eventos**: crear, listar, ver detalles y actualizar. +- **Gestión de eventos**: creación, listado, detalle y actualización. - **Entradas**: inscripción a eventos y gestión de tickets. -- **Regalos**: visualización y gestión. -- **SPA con routing**: vistas públicas/privadas en el frontend. +- **Regalos**: visualización y administración. +- **SPA con routing**: vistas públicas y privadas en el frontend. --- @@ -55,7 +55,9 @@ eventManager/ ├─ pom.xml ├─ mvnw ├─ mvnw.cmd -├─ docker-compose.yaml +├─ docker-compose-base.local.yaml +├─ docker-compose-detailed.local.yaml +├─ docker-compose.aws.yaml ├─ .env.example ├─ .gitignore ├─ src/ @@ -87,9 +89,10 @@ eventManager/ │ ├─ views/ │ └─ main.ts ├─ minio_data/ -└─ .github/ - └─ workflows/ - └─ ci.yml +├─ .github/ +│ └─ workflows/ + ├─ cd.yml +│ └─ ci.yml ``` --- @@ -122,7 +125,7 @@ El proyecto utiliza **MinIO** (compatible con S3) para desarrollo local. ### Iniciar MinIO ```bash -docker-compose up -d +docker compose -f docker-compose-base.local.yaml up -d ``` ### Acceso: @@ -135,6 +138,22 @@ docker-compose up -d El bucket `event-manager-images` se crea automáticamente al iniciar la aplicación con perfil `dev`. +### Stack local completo + +Si quieres levantar la aplicación completa en local junto con PostgreSQL y MinIO: + +```bash +docker compose -f docker-compose-detailed.local.yaml up -d --build +``` + +Este stack arranca: +- **App**: `http://localhost:8090` +- **PostgreSQL**: `localhost:5432` +- **MinIO API**: `http://localhost:9000` +- **MinIO Console**: `http://localhost:9001` + +La imagen de la app se construye con el `Dockerfile` de la raíz, que ya integra el frontend compilado dentro del backend Spring Boot. + ### Arquitectura ``` @@ -186,26 +205,39 @@ El proyecto incluye: Estos ficheros son artefactos de ejecución, no se versionan porque `target/` está ignorado en `.gitignore`. -### Workflow de CI (`.github/workflows/ci.yml`) +--- -El pipeline está dividido en tres jobs principales: -1. **unit** - - Ejecuta `./mvnw -B -Punit test`. - - Publica `target/surefire-reports/**` como artifact. +## CI/CD -2. **selenium** (matriz por suites) - - Levanta PostgreSQL como servicio. - - Arranca MinIO en el job. - - Ejecuta `./mvnw -B -Pselenium,selenium-ci -Dselenium.includes=... test`. - - Publica reportes de surefire y logs. +### Integración Continua (CI) -3. **coverage** - - Ejecuta `./mvnw -B -Punit verify`. - - Publica `target/site/jacoco/**` y `jacoco.xml` como artifacts. +**¿Qué tareas se ejecutan automáticamente?** +- Ejecución de tests unitarios, Selenium y generación de cobertura con JaCoCo. +- Publicación de reportes de tests y cobertura como artifacts. ---- +**¿Cuándo se ejecuta?** +- En *push* o *pull requests* hacia `main`, `develop`, `release/**` y `hotfix/**`. + +**¿Dónde se almacenan los artefactos?** +- En **GitHub Actions → Artifacts** del run correspondiente (`surefire-*`, `jacoco-*`, `backend-log-*`). + +### Entrega Continua (CD) + +**¿Qué tareas se ejecutan automáticamente?** +- Construcción de la imagen Docker usando `Dockerfile`. +- Publicación de la imagen en **DockerHub**. +- Publicación del `docker-compose.aws.yaml` como **OCI artifact** (solo en releases). -# Endpoint actual de la aplicación -Debido a que no se está utilizando una IP dinámica en la instancia EC2 la IP pública de instancia cambiará en caso de apagarse +**¿Cuándo se ejecuta?** +- En commits a `main` (por ejemplo tras merge de un pull request). +- En publicaciones de *release*. + +**¿Dónde se almacenan los artefactos?** +- En **DockerHub**: `docker.io//eventmanagerapp`. +- El compose OCI: `docker.io//eventmanagerapp-compose:`. +- Etiquetas típicas: `latest` (main), `sha` corto y etiqueta de release. + +--- -http://54.216.141.113:8090/ \ No newline at end of file +## Despliegue en AWS y endpoint actual de la aplicación +Actualmente el endpoint público usa la siguiente IP elástica: https://52.51.236.28/ \ No newline at end of file diff --git a/cloudformation.example.yml b/cloudformation.example.yml index b74e1a4..6794c0e 100644 --- a/cloudformation.example.yml +++ b/cloudformation.example.yml @@ -13,10 +13,10 @@ Parameters: Description: Key pair RSA (según TXT) Type: AWS::EC2::KeyPair::KeyName Default: EventManagerApplication - AmiSsmParameter: - Description: AMI AL2023 (según TXT) + AmiId: + Description: AMI ID para AL2023 (ej. ami-09fc5668766215f32) Type: String - Default: /aws/service/ami-amazon-linux-latest/al2023-ami-2023.11.20260406.2-kernel-6.1-x86_64 + Default: ami-09fc5668766215f32 DbInstanceIdentifier: Type: String Default: eventmanager-application-db @@ -25,7 +25,7 @@ Parameters: Default: db.t3.micro DbName: Type: String - Default: EventManagerDatabase + Default: eventmanagerdatabase DbUsername: Type: String Default: admin @@ -264,7 +264,7 @@ Resources: - EventsPublicSubnetRouteTableAssociation Properties: KeyName: !Ref KeyName - ImageId: !Sub "{{resolve:ssm:${AmiSsmParameter}}}" + ImageId: !Ref AmiId InstanceType: t3.micro IamInstanceProfile: !Ref EventsEC2IAMProfile # Opcional (no mencionado en el roadmap): Detailed monitoring @@ -299,6 +299,18 @@ Resources: EOF sudo docker compose --env-file /opt/eventmanager/.env -f oci://${AppComposeArtifact} up -d + # Elastic IP and association for EC2 (so the instance keeps a stable public IP) + EventsElasticIP: + Type: AWS::EC2::EIP + Properties: + Domain: vpc + + EventsEIPAssociation: + Type: AWS::EC2::EIPAssociation + Properties: + InstanceId: !Ref EventsEC2Instance + AllocationId: !GetAtt EventsElasticIP.AllocationId + Outputs: PublicDnsName: Value: !GetAtt EventsEC2Instance.PublicDnsName @@ -311,4 +323,8 @@ Outputs: Description: RDS endpoint address S3Bucket: Value: !Ref EventsImageBucket - Description: S3 bucket name \ No newline at end of file + Description: S3 bucket name + + ElasticIp: + Value: !GetAtt EventsElasticIP.PublicIp + Description: Elastic IP associated to the EC2 instance \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose-base.local.yaml similarity index 100% rename from docker-compose.yaml rename to docker-compose-base.local.yaml diff --git a/docker-compose-detailed.local.yaml b/docker-compose-detailed.local.yaml new file mode 100644 index 0000000..920de18 --- /dev/null +++ b/docker-compose-detailed.local.yaml @@ -0,0 +1,55 @@ +services: + db: + image: postgres:16 + container_name: eventmanager-db-dev + ports: + - "5432:5432" + environment: + POSTGRES_DB: bd_event_manager + POSTGRES_USER: event_manager_role + POSTGRES_PASSWORD: admin + volumes: + - eventmanager_pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U event_manager_role -d bd_event_manager"] + interval: 10s + timeout: 5s + retries: 10 + + minio: + image: minio/minio:RELEASE.2024-01-16T16-07-38Z + container_name: eventmanager-minio-dev + ports: + - "9000:9000" + - "9001:9001" + environment: + MINIO_ROOT_USER: dev-user + MINIO_ROOT_PASSWORD: dev-password + volumes: + - ./minio_data:/data + command: server /data --console-address ":9001" + + app: + build: + context: . + dockerfile: Dockerfile + container_name: eventmanager-app-dev + depends_on: + db: + condition: service_healthy + minio: + condition: service_started + ports: + - "8090:8090" + environment: + SPRING_PROFILES_ACTIVE: dev + DB_URL: jdbc:postgresql://db:5432/bd_event_manager + DB_USERNAME: event_manager_role + DB_PASSWORD: admin + MINIO_ENDPOINT: http://minio:9000 + MINIO_ACCESS_KEY: dev-user + MINIO_SECRET_KEY: dev-password + MINIO_BUCKET_NAME: event-manager-images + +volumes: + eventmanager_pgdata: \ No newline at end of file diff --git a/docker-compose.aws.yaml b/docker-compose.aws.yaml index d8ad14b..1a21842 100644 --- a/docker-compose.aws.yaml +++ b/docker-compose.aws.yaml @@ -3,6 +3,8 @@ services: image: ${APP_IMAGE:-eventmanagerapp} container_name: eventmanagerapp restart: unless-stopped + init: true + stop_grace_period: 30s ports: - "443:8090" volumes: @@ -16,4 +18,4 @@ services: AWS_REGION: ${AWS_REGION} SSL_KEYSTORE_PATH: ${SSL_KEYSTORE_PATH:-/opt/eventmanager/certs/eventmanager.p12} SSL_KEYSTORE_PASSWORD: ${SSL_KEYSTORE_PASSWORD} - SSL_KEY_ALIAS: ${SSL_KEY_ALIAS:-tomcat} + SSL_KEY_ALIAS: ${SSL_KEY_ALIAS:-tomcat} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8d98b02..7f6970a 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,9 @@ false false **/selenium/**/*Test.java + ${project.build.directory}/jacoco.exec + ${project.build.directory}/jacoco.exec + ${project.build.directory}/site/jacoco @@ -248,6 +251,9 @@ ${jacoco.version} ${skip.jacoco} + ${jacoco.destFile} + ${jacoco.dataFile} + ${jacoco.outputDirectory} @@ -370,6 +376,7 @@ install node and npm + generate-resources install-node-and-npm @@ -381,6 +388,7 @@ npm install + generate-resources npm @@ -391,6 +399,7 @@ npm run build + generate-resources npm @@ -414,7 +423,7 @@ ${skip.frontend.copy.resources} - ${project.basedir}/src/main/resources/static + ${project.build.directory}/classes/static ${project.basedir}/frontend/dist @@ -429,6 +438,7 @@ + unit @@ -448,6 +458,7 @@ + selenium @@ -464,16 +475,5 @@ - - selenium-ci - - true - true - true - true - true - true - - \ No newline at end of file diff --git a/src/test/java/eventManager/exception/GlobalExceptionHandlerTest.java b/src/test/java/eventManager/exception/GlobalExceptionHandlerTest.java index 03dd91b..25136d8 100644 --- a/src/test/java/eventManager/exception/GlobalExceptionHandlerTest.java +++ b/src/test/java/eventManager/exception/GlobalExceptionHandlerTest.java @@ -16,7 +16,8 @@ import static org.mockito.Mockito.*; /** - * Tests unitarios para GlobalExceptionHandler, que gestiona de forma centralizada las excepciones lanzadas por los controladores REST de la aplicacion. + * Unit tests for GlobalExceptionHandler, which centrally handles exceptions + * thrown by REST controllers. */ @DisplayName("GlobalExceptionHandler Tests") class GlobalExceptionHandlerTest { @@ -35,7 +36,8 @@ void setUp() { } /** - * Verifica que una CustomException con BAD_REQUEST devuelve estado 400 y el mensaje correcto. + * Verifies that a CustomException with BAD_REQUEST returns status 400 and the + * correct message. */ @Test @DisplayName("handleCustomException - BAD_REQUEST") @@ -49,7 +51,8 @@ void testHandleCustomException_BadRequest() { } /** - * Verifica que una CustomException con NOT_FOUND devuelve estado 404 y el mensaje correcto. + * Verifies that a CustomException with NOT_FOUND returns status 404 and the + * correct message. */ @Test @DisplayName("handleCustomException - NOT_FOUND") @@ -63,7 +66,7 @@ void testHandleCustomException_NotFound() { } /** - * Verifica que una CustomException con FORBIDDEN devuelve estado 403. + * Verifies that a CustomException with FORBIDDEN returns status 403. */ @Test @DisplayName("handleCustomException - FORBIDDEN") @@ -76,7 +79,8 @@ void testHandleCustomException_Forbidden() { } /** - * Verifica que una CustomException con INTERNAL_SERVER_ERROR devuelve estado 500. + * Verifies that a CustomException with INTERNAL_SERVER_ERROR returns status + * 500. */ @Test @DisplayName("handleCustomException - INTERNAL_SERVER_ERROR") @@ -89,10 +93,10 @@ void testHandleCustomException_InternalServerError() { } /** - * Verifica que una excepcion de credenciales incorrectas devuelve estado 401. + * Verifies that a bad credentials exception returns status 401. */ @Test - @DisplayName("handleBadCredentialsException - Devuelve 401") + @DisplayName("handleBadCredentialsException - Returns 401") void testHandleBadCredentialsException() { BadCredentialsException ex = new BadCredentialsException("Bad credentials"); ResponseEntity response = handler.handleBadCredentialsException(ex); @@ -103,10 +107,11 @@ void testHandleBadCredentialsException() { } /** - * Verifica que un error de validacion en el campo eventCode devuelve el mensaje apropiado. + * Verifies that a validation error on the eventCode field returns the + * appropriate message. */ @Test - @DisplayName("handleValidationExceptions - campo eventCode") + @DisplayName("handleValidationExceptions - EventCode field") void testHandleValidationExceptions_EventCodeField() { MethodArgumentNotValidException ex = mock(MethodArgumentNotValidException.class); BindingResult bindingResult = mock(BindingResult.class); @@ -121,10 +126,11 @@ void testHandleValidationExceptions_EventCodeField() { } /** - * Verifica que un error de validacion en el campo notes devuelve el mensaje apropiado. + * Verifies that a validation error on the notes field returns the appropriate + * message. */ @Test - @DisplayName("handleValidationExceptions - campo notes") + @DisplayName("handleValidationExceptions - Notes field") void testHandleValidationExceptions_NotesField() { MethodArgumentNotValidException ex = mock(MethodArgumentNotValidException.class); BindingResult bindingResult = mock(BindingResult.class); @@ -139,10 +145,11 @@ void testHandleValidationExceptions_NotesField() { } /** - * Verifica que un error de validacion en un campo generico devuelve el mensaje por defecto. + * Verifies that a validation error on a generic field returns the default + * message. */ @Test - @DisplayName("handleValidationExceptions - otro campo genérico") + @DisplayName("handleValidationExceptions - Other generic field") void testHandleValidationExceptions_OtherField() { MethodArgumentNotValidException ex = mock(MethodArgumentNotValidException.class); BindingResult bindingResult = mock(BindingResult.class); @@ -157,10 +164,11 @@ void testHandleValidationExceptions_OtherField() { } /** - * Verifica que una excepcion generica no controlada devuelve estado 500 con mensaje por defecto. + * Verifies that an unhandled generic exception returns status 500 with the + * default message. */ @Test - @DisplayName("handleGenericException - Devuelve 500") + @DisplayName("handleGenericException - Returns 500") void testHandleGenericException() { RuntimeException ex = new RuntimeException("Unexpected error"); ResponseEntity response = handler.handleGenericException(ex); diff --git a/src/test/java/eventManager/security/AccessControlUtilsTest.java b/src/test/java/eventManager/security/AccessControlUtilsTest.java index ddf18ca..86f1b56 100644 --- a/src/test/java/eventManager/security/AccessControlUtilsTest.java +++ b/src/test/java/eventManager/security/AccessControlUtilsTest.java @@ -29,7 +29,8 @@ import static org.mockito.Mockito.*; /** - * Tests unitarios para AccessControlUtils, que centraliza las validaciones de permisos y control de acceso sobre usuarios, eventos y tickets. + * Unit tests for AccessControlUtils, which centralizes permission and access + * control checks for users, events, and tickets. */ @DisplayName("AccessControlUtils Tests") class AccessControlUtilsTest { @@ -89,19 +90,20 @@ void tearDown() { } /** - * Verifica que se obtiene correctamente el nombre de usuario desde el SecurityContext. + * Verifies that the username is retrieved correctly from the SecurityContext. */ @Test - @DisplayName("getAuthenticatedUsername - Retorna username del SecurityContext") + @DisplayName("getAuthenticatedUsername - Returns username from SecurityContext") void testGetAuthenticatedUsername() { assertEquals("testuser", accessControlUtils.getAuthenticatedUsername()); } /** - * Verifica que se retorna el DTO del usuario autenticado consultando por su nombre de usuario. + * Verifies that the authenticated user's DTO is returned by looking up the + * username. */ @Test - @DisplayName("getAuthenticatedUser - Retorna UserDTO del usuario autenticado") + @DisplayName("getAuthenticatedUser - Returns authenticated UserDTO") void testGetAuthenticatedUser_Success() { when(userService.getUserInformationByUsername("testuser")).thenReturn(testUserDTO); UserDTO result = accessControlUtils.getAuthenticatedUser(); @@ -111,20 +113,21 @@ void testGetAuthenticatedUser_Success() { } /** - * Verifica que el propietario de la cuenta puede acceder sin que se lance excepcion. + * Verifies that the account owner can access without throwing an exception. */ @Test - @DisplayName("validateUserOwnership - Propietario, no lanza excepcion") + @DisplayName("validateUserOwnership - Owner access, no exception") void testValidateUserOwnership_Owner() { when(userRepository.findById(1)).thenReturn(Optional.of(testUser)); assertDoesNotThrow(() -> accessControlUtils.validateUserOwnership(1)); } /** - * Verifica que se lanza FORBIDDEN cuando el usuario autenticado no es propietario de la cuenta. + * Verifies that FORBIDDEN is thrown when the authenticated user is not the + * account owner. */ @Test - @DisplayName("validateUserOwnership - No propietario, lanza FORBIDDEN") + @DisplayName("validateUserOwnership - Non-owner access, throws FORBIDDEN") void testValidateUserOwnership_NonOwner() { User otherUser = User.builder().userId(2).username("otheruser").build(); when(userRepository.findById(2)).thenReturn(Optional.of(otherUser)); @@ -134,10 +137,11 @@ void testValidateUserOwnership_NonOwner() { } /** - * Verifica que se lanza NOT_FOUND cuando el usuario no existe en la base de datos. + * Verifies that NOT_FOUND is thrown when the user does not exist in the + * database. */ @Test - @DisplayName("validateUserOwnership - Usuario no encontrado, lanza NOT_FOUND") + @DisplayName("validateUserOwnership - User not found, throws NOT_FOUND") void testValidateUserOwnership_UserNotFound() { when(userRepository.findById(999)).thenReturn(Optional.empty()); CustomException ex = assertThrows(CustomException.class, () -> accessControlUtils.validateUserOwnership(999)); @@ -145,10 +149,11 @@ void testValidateUserOwnership_UserNotFound() { } /** - * Verifica que retorna true cuando el usuario autenticado es HOST del evento indicado. + * Verifies that it returns true when the authenticated user is HOST of the + * event. */ @Test - @DisplayName("isUserHostOfEvent - Es HOST, retorna true") + @DisplayName("isUserHostOfEvent - Is HOST, returns true") void testIsUserHostOfEvent_True() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(true); @@ -156,10 +161,11 @@ void testIsUserHostOfEvent_True() { } /** - * Verifica que retorna false cuando el usuario autenticado no es HOST del evento. + * Verifies that it returns false when the authenticated user is not HOST of the + * event. */ @Test - @DisplayName("isUserHostOfEvent - No es HOST, retorna false") + @DisplayName("isUserHostOfEvent - Not HOST, returns false") void testIsUserHostOfEvent_False() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(false); @@ -167,20 +173,22 @@ void testIsUserHostOfEvent_False() { } /** - * Verifica que se lanza excepcion cuando el usuario no existe al comprobar si es HOST. + * Verifies that an exception is thrown when the user does not exist while + * checking HOST status. */ @Test - @DisplayName("isUserHostOfEvent - Usuario no encontrado, lanza excepcion") + @DisplayName("isUserHostOfEvent - User not found, throws exception") void testIsUserHostOfEvent_UserNotFound() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.empty()); assertThrows(CustomException.class, () -> accessControlUtils.isUserHostOfEvent(1)); } /** - * Verifica que no se lanza excepcion cuando el usuario autenticado es HOST del evento. + * Verifies that no exception is thrown when the authenticated user is HOST of + * the event. */ @Test - @DisplayName("validateUserIsHost - Es HOST, no lanza excepcion") + @DisplayName("validateUserIsHost - Is HOST, no exception") void testValidateUserIsHost_IsHost() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(true); @@ -188,10 +196,10 @@ void testValidateUserIsHost_IsHost() { } /** - * Verifica que se lanza FORBIDDEN cuando el usuario no es HOST del evento. + * Verifies that FORBIDDEN is thrown when the user is not HOST of the event. */ @Test - @DisplayName("validateUserIsHost - No es HOST, lanza FORBIDDEN") + @DisplayName("validateUserIsHost - Not HOST, throws FORBIDDEN") void testValidateUserIsHost_NotHost() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(false); @@ -201,10 +209,11 @@ void testValidateUserIsHost_NotHost() { } /** - * Verifica que no se lanza excepcion cuando el usuario esta registrado en el evento. + * Verifies that no exception is thrown when the user is registered in the + * event. */ @Test - @DisplayName("validateUserRegisteredInEvent - Registrado, no lanza excepcion") + @DisplayName("validateUserRegisteredInEvent - Registered, no exception") void testValidateUserRegisteredInEvent_Registered() { when(userService.getUserInformationByUsername("testuser")).thenReturn(testUserDTO); TicketDTO ticketDTO = new TicketDTO(); @@ -213,23 +222,26 @@ void testValidateUserRegisteredInEvent_Registered() { } /** - * Verifica que se lanza FORBIDDEN cuando el usuario no esta registrado en el evento. + * Verifies that FORBIDDEN is thrown when the user is not registered in the + * event. */ @Test - @DisplayName("validateUserRegisteredInEvent - No registrado, lanza FORBIDDEN") + @DisplayName("validateUserRegisteredInEvent - Not registered, throws FORBIDDEN") void testValidateUserRegisteredInEvent_NotRegistered() { when(userService.getUserInformationByUsername("testuser")).thenReturn(testUserDTO); - when(ticketService.getTicketByEventAndUser(1, 1)).thenThrow(new CustomException(HttpStatus.NOT_FOUND, Constantes.MESSAGE_USER_NOT_REGISTERED_IN_EVENT)); - CustomException ex = assertThrows(CustomException.class, () -> accessControlUtils.validateUserRegisteredInEvent(1)); + when(ticketService.getTicketByEventAndUser(1, 1)) + .thenThrow(new CustomException(HttpStatus.NOT_FOUND, Constantes.MESSAGE_USER_NOT_REGISTERED_IN_EVENT)); + CustomException ex = assertThrows(CustomException.class, + () -> accessControlUtils.validateUserRegisteredInEvent(1)); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); assertEquals(Constantes.MESSAGE_USER_NOT_REGISTERED_IN_EVENT, ex.getMessage()); } /** - * Verifica que el propietario del ticket puede acceder sin que se lance excepcion. + * Verifies that the ticket owner can access without throwing an exception. */ @Test - @DisplayName("validateTicketAccess - Propietario del ticket, no lanza excepcion") + @DisplayName("validateTicketAccess - Ticket owner, no exception") void testValidateTicketAccess_Owner() { Event event = Event.builder().eventId(1).build(); Ticket ticket = Ticket.builder().ticketId(10).userId(testUser).eventId(event).build(); @@ -239,10 +251,10 @@ void testValidateTicketAccess_Owner() { } /** - * Verifica que el HOST del evento puede acceder al ticket aunque no sea su propietario. + * Verifies that the event HOST can access the ticket even if not the owner. */ @Test - @DisplayName("validateTicketAccess - HOST del evento, no lanza excepcion") + @DisplayName("validateTicketAccess - Event HOST, no exception") void testValidateTicketAccess_Host() { User otherUser = User.builder().userId(2).username("otheruser").build(); Event event = Event.builder().eventId(1).build(); @@ -254,10 +266,11 @@ void testValidateTicketAccess_Host() { } /** - * Verifica que se lanza FORBIDDEN cuando el usuario no es ni propietario del ticket ni HOST. + * Verifies that FORBIDDEN is thrown when the user is neither ticket owner nor + * HOST. */ @Test - @DisplayName("validateTicketAccess - Ni propietario ni HOST, lanza FORBIDDEN") + @DisplayName("validateTicketAccess - Neither owner nor HOST, throws FORBIDDEN") void testValidateTicketAccess_Unauthorized() { User otherUser = User.builder().userId(2).username("otheruser").build(); Event event = Event.builder().eventId(1).build(); @@ -270,10 +283,11 @@ void testValidateTicketAccess_Unauthorized() { } /** - * Verifica que se lanza NOT_FOUND cuando el ticket no existe en la base de datos. + * Verifies that NOT_FOUND is thrown when the ticket does not exist in the + * database. */ @Test - @DisplayName("validateTicketAccess - Ticket no encontrado, lanza NOT_FOUND") + @DisplayName("validateTicketAccess - Ticket not found, throws NOT_FOUND") void testValidateTicketAccess_TicketNotFound() { when(ticketRepository.findById(999)).thenReturn(Optional.empty()); CustomException ex = assertThrows(CustomException.class, () -> accessControlUtils.validateTicketAccess(999)); @@ -281,10 +295,11 @@ void testValidateTicketAccess_TicketNotFound() { } /** - * Verifica que no se lanza excepcion cuando el usuario es HOST del evento asociado al codigo. + * Verifies that no exception is thrown when the user is HOST of the event + * associated with the code. */ @Test - @DisplayName("validateUserIsHostByEventCode - Delega a validateUserIsHost") + @DisplayName("validateUserIsHostByEventCode - Delegates to validateUserIsHost") void testValidateUserIsHostByEventCode_IsHost() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(true); @@ -292,23 +307,25 @@ void testValidateUserIsHostByEventCode_IsHost() { } /** - * Verifica que se lanza FORBIDDEN cuando el usuario no es HOST del evento asociado al codigo. + * Verifies that FORBIDDEN is thrown when the user is not HOST of the event + * associated with the code. */ @Test - @DisplayName("validateUserIsHostByEventCode - No es HOST, lanza FORBIDDEN") + @DisplayName("validateUserIsHostByEventCode - Not HOST, throws FORBIDDEN") void testValidateUserIsHostByEventCode_NotHost() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(false); - CustomException ex = assertThrows(CustomException.class, () -> accessControlUtils.validateUserIsHostByEventCode("ABC123", 1)); + CustomException ex = assertThrows(CustomException.class, + () -> accessControlUtils.validateUserIsHostByEventCode("ABC123", 1)); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); assertEquals(Constantes.MESSAGE_USER_NOT_HOST, ex.getMessage()); } /** - * Verifica que el HOST del evento puede gestionar regalos sin que se lance excepcion. + * Verifies that the event HOST can manage gifts without throwing an exception. */ @Test - @DisplayName("validateHostOrGiftCreator - Es HOST, no lanza excepcion") + @DisplayName("validateHostOrGiftCreator - Is HOST, no exception") void testValidateHostOrGiftCreator_IsHost() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(true); @@ -316,10 +333,10 @@ void testValidateHostOrGiftCreator_IsHost() { } /** - * Verifica que el creador del regalo puede gestionarlo sin que se lance excepcion. + * Verifies that the gift creator can manage it without throwing an exception. */ @Test - @DisplayName("validateHostOrGiftCreator - Es creador del regalo, no lanza excepcion") + @DisplayName("validateHostOrGiftCreator - Is gift creator, no exception") void testValidateHostOrGiftCreator_IsCreator() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(false); @@ -327,14 +344,16 @@ void testValidateHostOrGiftCreator_IsCreator() { } /** - * Verifica que se lanza FORBIDDEN cuando el usuario no es ni HOST ni creador del regalo. + * Verifies that FORBIDDEN is thrown when the user is neither HOST nor gift + * creator. */ @Test - @DisplayName("validateHostOrGiftCreator - Ni HOST ni creador, lanza FORBIDDEN") + @DisplayName("validateHostOrGiftCreator - Neither HOST nor creator, throws FORBIDDEN") void testValidateHostOrGiftCreator_Neither() { when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(testUser)); when(ticketRepository.existsByEventId_EventIdAndUserId_UserIdAndRole(1, 1, "HOST")).thenReturn(false); - CustomException ex = assertThrows(CustomException.class, () -> accessControlUtils.validateHostOrGiftCreator(1, "otheruser")); + CustomException ex = assertThrows(CustomException.class, + () -> accessControlUtils.validateHostOrGiftCreator(1, "otheruser")); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); assertEquals(Constantes.MESSAGE_GIFT_UPDATE_FORBIDDEN, ex.getMessage()); } diff --git a/src/test/java/eventManager/security/JwtRequestFilterTest.java b/src/test/java/eventManager/security/JwtRequestFilterTest.java index d657de6..411aa33 100644 --- a/src/test/java/eventManager/security/JwtRequestFilterTest.java +++ b/src/test/java/eventManager/security/JwtRequestFilterTest.java @@ -27,7 +27,8 @@ import static org.mockito.Mockito.*; /** - * Tests unitarios para JwtRequestFilter, el filtro que intercepta las peticiones HTTP para verificar la autenticacion JWT y establecer el contexto de seguridad. + * Unit tests for JwtRequestFilter, the filter that intercepts HTTP requests to + * validate JWT authentication and set the security context. */ @ExtendWith(MockitoExtension.class) @DisplayName("JwtRequestFilter Tests") @@ -66,10 +67,10 @@ private void invokeFilter() throws ServletException, IOException { } /** - * Verifica que el endpoint de login es publico y no se valida ningun token JWT. + * Verifies that the login endpoint is public and no JWT token is validated. */ @Test - @DisplayName("Endpoint /api/auth/login es publico, no valida JWT") + @DisplayName("Endpoint /api/auth/login is public, does not validate JWT") void testPublicEndpoint_Login() throws Exception { when(request.getRequestURI()).thenReturn("/api/auth/login"); @@ -80,10 +81,11 @@ void testPublicEndpoint_Login() throws Exception { } /** - * Verifica que el endpoint de registro es publico y no requiere autenticacion. + * Verifies that the registration endpoint is public and does not require + * authentication. */ @Test - @DisplayName("Endpoint /api/auth/register es publico") + @DisplayName("Endpoint /api/auth/register is public") void testPublicEndpoint_Register() throws Exception { when(request.getRequestURI()).thenReturn("/api/auth/register"); @@ -94,10 +96,11 @@ void testPublicEndpoint_Register() throws Exception { } /** - * Verifica que el endpoint de recuperacion de contrasena es publico y no requiere autenticacion. + * Verifies that the password recovery endpoint is public and does not require + * authentication. */ @Test - @DisplayName("Endpoint /api/auth/forgot-password es publico") + @DisplayName("Endpoint /api/auth/forgot-password is public") void testPublicEndpoint_ForgotPassword() throws Exception { when(request.getRequestURI()).thenReturn("/api/auth/forgot-password"); @@ -108,10 +111,11 @@ void testPublicEndpoint_ForgotPassword() throws Exception { } /** - * Verifica que los recursos estaticos como archivos JavaScript son accesibles sin autenticacion. + * Verifies that static resources like JavaScript files are accessible without + * authentication. */ @Test - @DisplayName("Assets estaticos son publicos") + @DisplayName("Static assets are public") void testPublicEndpoint_StaticResource() throws Exception { when(request.getRequestURI()).thenReturn("/assets/main.js"); @@ -122,10 +126,10 @@ void testPublicEndpoint_StaticResource() throws Exception { } /** - * Verifica que las rutas del frontend SPA no requieren validacion JWT. + * Verifies that SPA frontend routes do not require JWT validation. */ @Test - @DisplayName("Ruta SPA (no /api) es publica") + @DisplayName("SPA route (non-/api) is public") void testPublicEndpoint_SpaRoute() throws Exception { when(request.getRequestURI()).thenReturn("/eventos"); @@ -136,10 +140,10 @@ void testPublicEndpoint_SpaRoute() throws Exception { } /** - * Verifica que la interfaz de Swagger UI es accesible sin autenticacion. + * Verifies that the Swagger UI is accessible without authentication. */ @Test - @DisplayName("Swagger UI es publico") + @DisplayName("Swagger UI is public") void testPublicEndpoint_SwaggerUI() throws Exception { when(request.getRequestURI()).thenReturn("/swagger-ui/index.html"); @@ -150,10 +154,11 @@ void testPublicEndpoint_SwaggerUI() throws Exception { } /** - * Verifica que un endpoint protegido con cookie JWT valida establece correctamente el SecurityContext. + * Verifies that a protected endpoint with a valid JWT cookie correctly sets the + * SecurityContext. */ @Test - @DisplayName("Endpoint protegido con cookie valida establece SecurityContext") + @DisplayName("Protected endpoint with valid cookie sets SecurityContext") void testProtectedEndpoint_ValidCookie() throws Exception { when(request.getRequestURI()).thenReturn("/api/events"); @@ -172,13 +177,15 @@ void testProtectedEndpoint_ValidCookie() throws Exception { } /** - * Verifica que sin cookie JWT presente, el SecurityContext permanece vacio y la peticion continua. + * Verifies that without a JWT cookie, the SecurityContext remains empty and the + * request continues. */ @Test - @DisplayName("Endpoint protegido sin cookie limpia SecurityContext") + @DisplayName("Protected endpoint without cookie clears SecurityContext") void testProtectedEndpoint_NoCookie() throws Exception { when(request.getRequestURI()).thenReturn("/api/events"); - when(jwtTokenProvider.validateToken(request, true)).thenThrow(new IllegalArgumentException("No token found in cookies")); + when(jwtTokenProvider.validateToken(request, true)) + .thenThrow(new IllegalArgumentException("No token found in cookies")); invokeFilter(); @@ -187,10 +194,11 @@ void testProtectedEndpoint_NoCookie() throws Exception { } /** - * Verifica que con una cookie JWT invalida, el SecurityContext se limpia y la peticion continua. + * Verifies that with an invalid JWT cookie, the SecurityContext is cleared and + * the request continues. */ @Test - @DisplayName("Endpoint protegido con cookie invalida limpia SecurityContext") + @DisplayName("Protected endpoint with invalid cookie clears SecurityContext") void testProtectedEndpoint_InvalidCookie() throws Exception { when(request.getRequestURI()).thenReturn("/api/events"); when(jwtTokenProvider.validateToken(request, true)).thenThrow(new RuntimeException("Invalid JWT")); diff --git a/src/test/java/eventManager/security/JwtTokenProviderTest.java b/src/test/java/eventManager/security/JwtTokenProviderTest.java index 94dc7af..bce8a76 100644 --- a/src/test/java/eventManager/security/JwtTokenProviderTest.java +++ b/src/test/java/eventManager/security/JwtTokenProviderTest.java @@ -17,7 +17,8 @@ import static org.mockito.Mockito.*; /** - * Tests unitarios para JwtTokenProvider, que se encarga de generar, validar y extraer tokens JWT utilizados en la autenticacion de la aplicacion. + * Unit tests for JwtTokenProvider, which generates, validates, and extracts JWT + * tokens used in application authentication. */ @DisplayName("JwtTokenProvider Tests") class JwtTokenProviderTest { @@ -32,10 +33,10 @@ void setUp() { } /** - * Verifica que se genera un token de acceso valido con los claims correctos. + * Verifies that a valid access token is generated with the correct claims. */ @Test - @DisplayName("generateAccessToken - Token válido con claims correctos") + @DisplayName("generateAccessToken - Valid token with correct claims") void testGenerateAccessToken_Valid() { String token = jwtTokenProvider.generateAccessToken(userDetails); @@ -50,10 +51,10 @@ void testGenerateAccessToken_Valid() { } /** - * Verifica que se genera un token de refresco con el tipo REFRESH en sus claims. + * Verifies that a refresh token is generated with type REFRESH in its claims. */ @Test - @DisplayName("generateRefreshToken - Token válido con type=REFRESH") + @DisplayName("generateRefreshToken - Valid token with type=REFRESH") void testGenerateRefreshToken_Valid() { String token = jwtTokenProvider.generateRefreshToken(userDetails); @@ -66,10 +67,10 @@ void testGenerateRefreshToken_Valid() { } /** - * Comprueba que un token valido retorna correctamente sus claims al ser validado. + * Verifies that a valid token returns its claims correctly when validated. */ @Test - @DisplayName("validateToken(String) - Token válido retorna claims") + @DisplayName("validateToken(String) - Valid token returns claims") void testValidateToken_ValidToken() { String token = jwtTokenProvider.generateAccessToken(userDetails); Claims claims = jwtTokenProvider.validateToken(token); @@ -79,40 +80,49 @@ void testValidateToken_ValidToken() { } /** - * Comprueba que un token con formato incorrecto lanza una excepcion al validarse. + * Verifies that a malformed token throws an exception when validated. */ @Test - @DisplayName("validateToken(String) - Token malformado lanza excepción") + @DisplayName("validateToken(String) - Malformed token throws exception") void testValidateToken_MalformedToken() { assertThrows(Exception.class, () -> jwtTokenProvider.validateToken("this.is.not.a.valid.jwt")); } /** - * Verifica que un token alterado manualmente es rechazado al intentar validarse. + * Verifies that a manually tampered token is rejected when validated. */ @Test - @DisplayName("validateToken(String) - Token manipulado lanza excepción") + @DisplayName("validateToken(String) - Tampered token throws exception") void testValidateToken_TamperedToken() { String token = jwtTokenProvider.generateAccessToken(userDetails); - String tampered = token.substring(0, token.length() - 1) + (token.charAt(token.length() - 1) == 'A' ? 'B' : 'A'); + String[] parts = token.split("\\."); + assertEquals(3, parts.length, "El JWT debería tener tres partes"); + + char[] payloadChars = parts[1].toCharArray(); + int tamperIndex = payloadChars.length / 2; + payloadChars[tamperIndex] = payloadChars[tamperIndex] == 'A' ? 'B' : 'A'; + String tamperedPayload = new String(payloadChars); + + String tampered = parts[0] + "." + tamperedPayload + "." + parts[2]; assertThrows(Exception.class, () -> jwtTokenProvider.validateToken(tampered)); } /** - * Comprueba que un string vacio es rechazado al intentar validarse como token. + * Verifies that an empty string is rejected when validated as a token. */ @Test - @DisplayName("validateToken(String) - String vacío lanza excepción") + @DisplayName("validateToken(String) - Empty string throws exception") void testValidateToken_EmptyString() { assertThrows(Exception.class, () -> jwtTokenProvider.validateToken("")); } /** - * Verifica que se extrae correctamente el token desde un header Authorization con prefijo Bearer. + * Verifies that the token is correctly extracted from an Authorization header + * with Bearer prefix. */ @Test - @DisplayName("tokenStringFromHeaders - Header Bearer válido") + @DisplayName("tokenStringFromHeaders - Valid Bearer header") void testTokenFromHeaders_ValidBearer() { HttpServletRequest request = mock(HttpServletRequest.class); when(request.getHeader("Authorization")).thenReturn("Bearer mytoken123"); @@ -122,64 +132,71 @@ void testTokenFromHeaders_ValidBearer() { } /** - * Comprueba que se lanza excepcion cuando no existe el header Authorization en la peticion. + * Verifies that an exception is thrown when the Authorization header is + * missing. */ @Test - @DisplayName("tokenStringFromHeaders - Header ausente lanza IllegalArgumentException") + @DisplayName("tokenStringFromHeaders - Missing header throws IllegalArgumentException") void testTokenFromHeaders_MissingHeader() { HttpServletRequest request = mock(HttpServletRequest.class); when(request.getHeader("Authorization")).thenReturn(null); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> jwtTokenProvider.tokenStringFromHeaders(request)); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> jwtTokenProvider.tokenStringFromHeaders(request)); assertTrue(ex.getMessage().contains("Missing Authorization header")); } /** - * Verifica que se lanza excepcion cuando el header Authorization usa un esquema distinto a Bearer. + * Verifies that an exception is thrown when the Authorization header uses a + * scheme other than Bearer. */ @Test - @DisplayName("tokenStringFromHeaders - Header Basic lanza IllegalArgumentException") + @DisplayName("tokenStringFromHeaders - Basic header throws IllegalArgumentException") void testTokenFromHeaders_NonBearerHeader() { HttpServletRequest request = mock(HttpServletRequest.class); when(request.getHeader("Authorization")).thenReturn("Basic dXNlcjpwYXNz"); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> jwtTokenProvider.tokenStringFromHeaders(request)); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> jwtTokenProvider.tokenStringFromHeaders(request)); assertTrue(ex.getMessage().contains("does not start with Bearer")); } /** - * Comprueba que se valida correctamente un token recibido a traves de una cookie. + * Verifies that a token received via a cookie is validated correctly. */ @Test - @DisplayName("validateToken(request, true) - Cookie válida") + @DisplayName("validateToken(request, true) - Valid cookie") void testValidateToken_FromCookie_Valid() { String token = jwtTokenProvider.generateAccessToken(userDetails); HttpServletRequest request = mock(HttpServletRequest.class); Cookie cookie = new Cookie("AuthToken", token); - when(request.getCookies()).thenReturn(new Cookie[]{cookie}); + when(request.getCookies()).thenReturn(new Cookie[] { cookie }); Claims claims = jwtTokenProvider.validateToken(request, true); assertEquals("carlos.martinez", claims.getSubject()); } /** - * Verifica que se lanza excepcion al intentar validar desde cookies cuando no hay ninguna. + * Verifies that an exception is thrown when validating from cookies and none + * are present. */ @Test - @DisplayName("validateToken(request, true) - Sin cookies lanza IllegalArgumentException") + @DisplayName("validateToken(request, true) - No cookies throws IllegalArgumentException") void testValidateToken_FromCookie_NoCookies() { HttpServletRequest request = mock(HttpServletRequest.class); when(request.getCookies()).thenReturn(null); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> jwtTokenProvider.validateToken(request, true)); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> jwtTokenProvider.validateToken(request, true)); assertTrue(ex.getMessage().contains("cookies")); } /** - * Comprueba que se valida correctamente un token recibido a traves del header Authorization. + * Verifies that a token received via the Authorization header is validated + * correctly. */ @Test - @DisplayName("validateToken(request, false) - Desde header válido") + @DisplayName("validateToken(request, false) - From valid header") void testValidateToken_FromHeader_Valid() { String token = jwtTokenProvider.generateAccessToken(userDetails); HttpServletRequest request = mock(HttpServletRequest.class); diff --git a/src/test/java/eventManager/security/UserDetailsServiceImplTest.java b/src/test/java/eventManager/security/UserDetailsServiceImplTest.java index 4c30565..fb9ec53 100644 --- a/src/test/java/eventManager/security/UserDetailsServiceImplTest.java +++ b/src/test/java/eventManager/security/UserDetailsServiceImplTest.java @@ -21,7 +21,8 @@ import static org.mockito.Mockito.*; /** - * Tests unitarios para UserDetailsServiceImpl, que carga los datos del usuario desde el repositorio para la autenticacion de Spring Security. + * Unit tests for UserDetailsServiceImpl, which loads user data from the + * repository for Spring Security authentication. */ @ExtendWith(MockitoExtension.class) @DisplayName("UserDetailsServiceImpl Tests") @@ -50,10 +51,11 @@ void setUp() { } /** - * Verifica que se retorna un UserDetails con el rol correcto cuando el usuario existe. + * Verifies that a UserDetails with the correct role is returned when the user + * exists. */ @Test - @DisplayName("loadUserByUsername - Usuario encontrado retorna UserDetails con ROLE_USER") + @DisplayName("loadUserByUsername - User found returns UserDetails with ROLE_USER") void testLoadUserByUsername_Success() { when(userRepository.findByUsername("carlos.martinez")).thenReturn(Optional.of(testUser)); @@ -68,14 +70,16 @@ void testLoadUserByUsername_Success() { } /** - * Verifica que se lanza UsernameNotFoundException cuando el usuario no existe en el repositorio. + * Verifies that UsernameNotFoundException is thrown when the user does not + * exist in the repository. */ @Test - @DisplayName("loadUserByUsername - Usuario no encontrado lanza UsernameNotFoundException") + @DisplayName("loadUserByUsername - User not found throws UsernameNotFoundException") void testLoadUserByUsername_NotFound() { when(userRepository.findByUsername("nonexistent")).thenReturn(Optional.empty()); - UsernameNotFoundException exception = assertThrows(UsernameNotFoundException.class, () -> userDetailsService.loadUserByUsername("nonexistent")); + UsernameNotFoundException exception = assertThrows(UsernameNotFoundException.class, + () -> userDetailsService.loadUserByUsername("nonexistent")); assertEquals(Constantes.MESSAGE_USER_NOT_REGISTERED, exception.getMessage()); verify(userRepository).findByUsername("nonexistent"); diff --git a/src/test/java/eventManager/selenium/BaseSeleniumTest.java b/src/test/java/eventManager/selenium/BaseSeleniumTest.java index 55e822a..73762f4 100644 --- a/src/test/java/eventManager/selenium/BaseSeleniumTest.java +++ b/src/test/java/eventManager/selenium/BaseSeleniumTest.java @@ -27,36 +27,38 @@ import java.util.concurrent.ConcurrentMap; /** - * Clase base abstracta para todos los tests de Selenium del proyecto. Centraliza la configuracion del WebDriver de Chrome, los tiempos de espera y ofrece metodos utilitarios comunes como login, registro, navegacion y manipulacion de elementos en la interfaz web. + * Abstract base class for all Selenium tests in the project. Centralizes Chrome + * WebDriver configuration, wait settings, and provides shared helper methods + * like login, registration, navigation, and UI element interactions. */ -@SpringBootTest( - webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, - properties = {"server.port=8090"} -) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = { "server.port=8090" }) @TestInstance(TestInstance.Lifecycle.PER_CLASS) public abstract class BaseSeleniumTest { protected WebDriver driver; protected WebDriverWait wait; protected static final String BASE_URL = "http://localhost:8090"; - protected static final int DEFAULT_WAIT_SECONDS = 15; + protected static final int DEFAULT_WAIT_SECONDS = 45; private String fallbackUsername; private boolean sessionValidatedInCurrentBrowser; private long lastSessionValidationAtMillis; private static final String FALLBACK_PASSWORD = "ClaveSegura2025"; - private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(8)) .build(); - private static final ConcurrentMap AUTH_COOKIE_CACHE = new ConcurrentHashMap<>(); - private static final ConcurrentMap FAILED_LOGIN_CACHE = new ConcurrentHashMap<>(); - private static final long AUTH_CACHE_TTL_MILLIS = Duration.ofMinutes(4).toMillis(); - private static final long FAILED_LOGIN_CACHE_TTL_MILLIS = Duration.ofMinutes(5).toMillis(); - private static final double SLEEP_MULTIPLIER = Double.parseDouble(System.getProperty("selenium.sleep.multiplier", "0.5")); - private static final Duration QUICK_CHECK_TIMEOUT = Duration.ofSeconds(2); - private static final boolean REUSE_BROWSER_PER_CLASS = Boolean.parseBoolean(System.getProperty("selenium.reuse.browser.per.class", "true")); + private static final ConcurrentMap AUTH_COOKIE_CACHE = new ConcurrentHashMap<>(); + private static final ConcurrentMap FAILED_LOGIN_CACHE = new ConcurrentHashMap<>(); + private static final long AUTH_CACHE_TTL_MILLIS = Duration.ofMinutes(4).toMillis(); + private static final long FAILED_LOGIN_CACHE_TTL_MILLIS = Duration.ofMinutes(5).toMillis(); + private static final double SLEEP_MULTIPLIER = Double + .parseDouble(System.getProperty("selenium.sleep.multiplier", "0.5")); + private static final Duration QUICK_CHECK_TIMEOUT = Duration.ofSeconds(2); + private static final boolean REUSE_BROWSER_PER_CLASS = Boolean + .parseBoolean(System.getProperty("selenium.reuse.browser.per.class", "true")); /** - * Descarga y configura el driver de Chrome mediante WebDriverManager antes de ejecutar cualquier test de la clase. + * Downloads and configures the Chrome driver via WebDriverManager before any + * tests in the class run. */ @BeforeAll public static void setupClass() { @@ -64,7 +66,8 @@ public static void setupClass() { } /** - * Inicializa el navegador Chrome en modo headless con las opciones necesarias para la ejecucion de tests y configura los tiempos de espera implicitos. + * Initializes headless Chrome with the options required for test execution and + * configures implicit waits. */ @BeforeEach public void setupTest() { @@ -89,7 +92,8 @@ private void initializeDriver() { driver = new ChromeDriver(options); setSessionValidated(false); - // Evitamos esperas implícitas globales para no multiplicar tiempos en findElement/findElements. + // Avoid global implicit waits so findElement/findElements do not multiply wait + // times. driver.manage().timeouts().implicitlyWait(Duration.ZERO); driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(20)); wait = new WebDriverWait(driver, Duration.ofSeconds(DEFAULT_WAIT_SECONDS)); @@ -122,7 +126,8 @@ private void resetBrowserState() { } try { - ((org.openqa.selenium.JavascriptExecutor) driver).executeScript("window.localStorage.clear(); window.sessionStorage.clear();"); + ((org.openqa.selenium.JavascriptExecutor) driver) + .executeScript("window.localStorage.clear(); window.sessionStorage.clear();"); } catch (Exception ignored) { // no-op } @@ -131,15 +136,16 @@ private void resetBrowserState() { } /** - * Permite controlar por suite si se conservan cookies entre tests para evitar logins redundantes. - * En vistas de autenticación se debe sobrescribir devolviendo false. + * Lets each suite control whether cookies are preserved between tests to avoid + * redundant logins. + * Auth views should override this and return false. */ protected boolean preserveCookiesBetweenTests() { return true; } /** - * Cierra el navegador y libera los recursos del WebDriver despues de cada test. + * Closes the browser and releases WebDriver resources after each test. */ @AfterEach public void tearDown() { @@ -158,7 +164,7 @@ public void tearDownClass() { } /** - * Navega a una URL relativa a la URL base + * Navigates to a URL relative to the base URL. */ protected void navigateTo(String path) { String fullUrl = BASE_URL + path; @@ -171,28 +177,28 @@ protected void navigateTo(String path) { } /** - * Espera a que un elemento sea visible en la página + * Waits until an element is visible on the page. */ protected WebElement waitForElement(By locator) { return wait.until(ExpectedConditions.visibilityOfElementLocated(locator)); } /** - * Espera a que un elemento sea clickeable + * Waits until an element is clickable. */ protected WebElement waitForClickableElement(By locator) { return wait.until(ExpectedConditions.elementToBeClickable(locator)); } /** - * Espera a que un elemento desaparezca de la página + * Waits for an element to disappear from the page. */ protected void waitForElementToDisappear(By locator) { wait.until(ExpectedConditions.invisibilityOfElementLocated(locator)); } /** - * Escribe texto en un campo de entrada + * Types text into an input field. */ protected void fillInput(By locator, String text) { WebElement element = waitForElement(locator); @@ -201,7 +207,7 @@ protected void fillInput(By locator, String text) { } /** - * Hace clic en un elemento + * Clicks an element. */ protected void clickElement(By locator) { WebElement element = waitForClickableElement(locator); @@ -209,7 +215,7 @@ protected void clickElement(By locator) { } /** - * Obtiene el texto de un elemento + * Gets the text of an element. */ protected String getElementText(By locator) { WebElement element = waitForElement(locator); @@ -217,7 +223,7 @@ protected String getElementText(By locator) { } /** - * Verifica si un elemento está presente en la página + * Checks whether an element is present on the page. */ protected boolean isElementPresent(By locator) { try { @@ -229,7 +235,7 @@ protected boolean isElementPresent(By locator) { } /** - * Verifica si un elemento está visible en la página + * Checks whether an element is visible on the page. */ protected boolean isElementVisible(By locator) { try { @@ -244,21 +250,21 @@ protected boolean isElementVisible(By locator) { } /** - * Espera a que la URL contenga un texto específico + * Waits for the URL to contain specific text. */ protected void waitForUrlContains(String text) { wait.until(ExpectedConditions.urlContains(text)); } /** - * Obtiene la URL actual + * Gets the current URL. */ protected String getCurrentUrl() { return driver.getCurrentUrl(); } /** - * Realiza un scroll hasta un elemento + * Scrolls to an element. */ protected void scrollToElement(By locator) { WebElement element = driver.findElement(locator); @@ -266,7 +272,7 @@ protected void scrollToElement(By locator) { } /** - * Espera un tiempo específico (usar solo cuando sea necesario) + * Waits for a specific time (use only when necessary). */ protected void sleep(int milliseconds) { try { @@ -278,14 +284,15 @@ protected void sleep(int milliseconds) { } /** - * Espera a que la UI quede estable (documento cargado y sin overlays de carga visibles). + * Waits for the UI to be stable (document loaded and no visible loading + * overlays). */ protected void waitForUiToSettle() { waitForUiToSettle(Duration.ofSeconds(2)); } /** - * Espera a que la UI quede estable con un timeout configurable. + * Waits for the UI to be stable with a configurable timeout. */ protected void waitForUiToSettle(Duration timeout) { try { @@ -298,8 +305,7 @@ protected void waitForUiToSettle(Duration timeout) { } List busyElements = d.findElements(By.cssSelector( - ".loading, .spinner, .loader, [aria-busy='true'], .swal2-container.swal2-backdrop-show" - )); + ".loading, .spinner, .loader, [aria-busy='true'], .swal2-container.swal2-backdrop-show")); for (WebElement element : busyElements) { if (element.isDisplayed()) { return false; @@ -312,12 +318,12 @@ protected void waitForUiToSettle(Duration timeout) { } }); } catch (Exception ignored) { - // Si no se cumple, se continúa para no bloquear el test por selectores opcionales. + // If it does not settle, continue so optional selectors do not block the test. } } /** - * Método auxiliar para login (reutilizable en otros tests) + * Helper method for login (reusable in other tests). */ protected void login(String username, String password) { if (hasAuthCookie() && hasActiveAuthenticatedSession()) { @@ -348,7 +354,7 @@ protected void login(String username, String password) { markCredentialAsFailed(loginKey); - // Si sigue en login, se crea un usuario de respaldo y se reintenta autenticación. + // If still on login, create a fallback user and retry authentication. if (getCurrentUrl().contains("/iniciar-sesion")) { ensureFallbackUserAndLogin(); } @@ -406,7 +412,8 @@ private boolean performApiLogin(String username, String password) { return applyCookies(cached.cookies); } - String payload = "{\"username\":\"" + escapeJson(username) + "\",\"password\":\"" + escapeJson(password) + "\"}"; + String payload = "{\"username\":\"" + escapeJson(username) + "\",\"password\":\"" + escapeJson(password) + + "\"}"; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/api/auth/login")) .timeout(Duration.ofSeconds(10)) @@ -439,12 +446,12 @@ private boolean applyCookies(List cookies) { for (Cookie cookie : cookies) { driver.manage().addCookie(cookie); } - // Validamos autenticación en una vista protegida para evitar falsos positivos. + // Validate authentication on a protected view to avoid false positives. navigateTo("/usuario/actualizar-perfil"); try { new WebDriverWait(driver, Duration.ofSeconds(3)).until(d -> !d.getCurrentUrl().contains("/iniciar-sesion")); } catch (Exception ignored) { - // Si no se cumple, se valida al final por URL. + // If it does not pass, validate by URL at the end. } boolean authenticated = !getCurrentUrl().contains("/iniciar-sesion"); setSessionValidated(authenticated); @@ -500,14 +507,16 @@ private CachedAuthCookies(List cookies, long createdAtMillis) { } private void ensureFallbackUserAndLogin() { - if (fallbackUsername != null && (performApiLogin(fallbackUsername, FALLBACK_PASSWORD) || performLogin(fallbackUsername, FALLBACK_PASSWORD))) { + if (fallbackUsername != null && (performApiLogin(fallbackUsername, FALLBACK_PASSWORD) + || performLogin(fallbackUsername, FALLBACK_PASSWORD))) { return; } fallbackUsername = "selenium" + System.currentTimeMillis(); String fallbackEmail = fallbackUsername + "@eventmanager.es"; - if (performApiRegisterAndApplyCookies(fallbackEmail, fallbackUsername, FALLBACK_PASSWORD, "Carlos", "Martinez", "612345678")) { + if (performApiRegisterAndApplyCookies(fallbackEmail, fallbackUsername, FALLBACK_PASSWORD, "Carlos", "Martinez", + "612345678")) { return; } @@ -525,14 +534,15 @@ private void ensureFallbackUserAndLogin() { } } - private boolean performApiRegisterAndApplyCookies(String email, String username, String password, String firstName, String lastName, String phone) { + private boolean performApiRegisterAndApplyCookies(String email, String username, String password, String firstName, + String lastName, String phone) { try { String payload = "{" - + "\"email\":\"" + escapeJson(email) + "\"," - + "\"username\":\"" + escapeJson(username) + "\"," - + "\"password\":\"" + escapeJson(password) + "\"," - + "\"firstName\":\"" + escapeJson(firstName) + "\"," - + "\"lastName\":\"" + escapeJson(lastName) + "\"," + + "\"email\":\"" + escapeJson(email) + "\"," + + "\"username\":\"" + escapeJson(username) + "\"," + + "\"password\":\"" + escapeJson(password) + "\"," + + "\"firstName\":\"" + escapeJson(firstName) + "\"," + + "\"lastName\":\"" + escapeJson(lastName) + "\"," + "\"phoneNumber\":\"" + escapeJson(phone) + "\"" + "}"; @@ -587,29 +597,34 @@ private void waitForLoginOutcome() { return isErrorMessagePresent(); }); } catch (Exception ignored) { - // Se evaluará por URL al finalizar. + // It will be evaluated by URL at the end. } } /** - * Método auxiliar para registrar un nuevo usuario + * Helper method to register a new user. */ - protected void register(String email, String username, String password, String firstName, String lastName, String phone) { + protected void register(String email, String username, String password, String firstName, String lastName, + String phone) { navigateTo("/registro"); fillInput(By.cssSelector(".register-container input[type='email']"), email); fillInput(By.cssSelector(".register-container input[type='text']:nth-of-type(1)"), username); fillInput(By.cssSelector(".register-container input[type='password']"), password); - java.util.List textInputs = driver.findElements(By.cssSelector(".register-container .form-group input[type='text']")); + java.util.List textInputs = driver + .findElements(By.cssSelector(".register-container .form-group input[type='text']")); if (textInputs.size() >= 4) { - textInputs.get(1).clear(); textInputs.get(1).sendKeys(firstName); - textInputs.get(2).clear(); textInputs.get(2).sendKeys(lastName); - textInputs.get(3).clear(); textInputs.get(3).sendKeys(phone); + textInputs.get(1).clear(); + textInputs.get(1).sendKeys(firstName); + textInputs.get(2).clear(); + textInputs.get(2).sendKeys(lastName); + textInputs.get(3).clear(); + textInputs.get(3).sendKeys(phone); } clickElement(By.cssSelector(".register-container button[type='submit']")); } /** - * Método auxiliar para logout + * Helper method for logout. */ protected void logout() { driver.manage().deleteAllCookies(); @@ -620,28 +635,28 @@ protected void logout() { } /** - * Verifica si un mensaje de error está presente + * Checks whether an error message is present. */ protected boolean isErrorMessagePresent() { return isElementVisible(By.cssSelector(".error-message, .alert-error, .text-red-500")); } /** - * Obtiene el texto del mensaje de error + * Gets the error message text. */ protected String getErrorMessage() { return getElementText(By.cssSelector(".error-message, .alert-error, .text-red-500")); } /** - * Verifica si un mensaje de éxito está presente + * Checks whether a success message is present. */ protected boolean isSuccessMessagePresent() { return isElementVisible(By.cssSelector(".success-message, .alert-success, .text-green-500")); } /** - * Obtiene el texto del mensaje de éxito + * Gets the success message text. */ protected String getSuccessMessage() { return getElementText(By.cssSelector(".success-message, .alert-success, .text-green-500")); diff --git a/src/test/java/eventManager/selenium/views/AuthViewsSeleniumTest.java b/src/test/java/eventManager/selenium/views/AuthViewsSeleniumTest.java index 4da1bee..4098c20 100644 --- a/src/test/java/eventManager/selenium/views/AuthViewsSeleniumTest.java +++ b/src/test/java/eventManager/selenium/views/AuthViewsSeleniumTest.java @@ -8,7 +8,8 @@ import static org.junit.jupiter.api.Assertions.*; /** - * Tests de Selenium para las vistas de autenticacion del sistema. Valida el comportamiento de las pantallas de login, registro, recuperacion de contrasena y cierre de sesion. + * Selenium tests for the authentication views. Validates the behavior of login, + * registration, password recovery, and logout screens. */ @DisplayName("Authentication Views Selenium Tests") public class AuthViewsSeleniumTest extends BaseSeleniumTest { @@ -19,20 +20,22 @@ protected boolean preserveCookiesBetweenTests() { } /** - * Verifica que el login exitoso con credenciales validas redirige al usuario a la pagina principal. + * Verifies that a successful login with valid credentials redirects the user to + * the home page. */ @Test - @DisplayName("Login View - Login exitoso con credenciales válidas") + @DisplayName("Login View - Successful login with valid credentials") public void testLoginView_SuccessfulLogin() { login("carlos.martinez", "ClaveSegura2025"); assertFalse(getCurrentUrl().contains("/iniciar-sesion"), "Debería salir de la página de login"); } /** - * Verifica que se muestra un mensaje de error cuando se introducen credenciales incorrectas. + * Verifies that an error message is shown when invalid credentials are + * provided. */ @Test - @DisplayName("Login View - Error con credenciales incorrectas") + @DisplayName("Login View - Error with invalid credentials") public void testLoginView_InvalidCredentials() { navigateTo("/iniciar-sesion"); @@ -46,10 +49,11 @@ public void testLoginView_InvalidCredentials() { } /** - * Verifica que el formulario no se envia y el usuario permanece en login cuando ambos campos estan vacios. + * Verifies that the form is not submitted and the user stays on login when both + * fields are empty. */ @Test - @DisplayName("Login View - Error con campos vacíos") + @DisplayName("Login View - Error with empty fields") public void testLoginView_EmptyFields() { navigateTo("/iniciar-sesion"); @@ -60,10 +64,10 @@ public void testLoginView_EmptyFields() { } /** - * Verifica que no se puede iniciar sesion sin introducir el nombre de usuario. + * Verifies that login is not possible without entering the username. */ @Test - @DisplayName("Login View - Campo username vacío") + @DisplayName("Login View - Empty username field") public void testLoginView_EmptyUsername() { navigateTo("/iniciar-sesion"); @@ -75,10 +79,10 @@ public void testLoginView_EmptyUsername() { } /** - * Verifica que no se puede iniciar sesion sin introducir la contrasena. + * Verifies that login is not possible without entering the password. */ @Test - @DisplayName("Login View - Campo password vacío") + @DisplayName("Login View - Empty password field") public void testLoginView_EmptyPassword() { navigateTo("/iniciar-sesion"); @@ -90,10 +94,10 @@ public void testLoginView_EmptyPassword() { } /** - * Verifica que el enlace de crear cuenta lleva correctamente a la pagina de registro. + * Verifies that the create account link navigates to the registration page. */ @Test - @DisplayName("Login View - Enlace a página de registro") + @DisplayName("Login View - Link to registration page") public void testLoginView_NavigateToRegister() { navigateTo("/iniciar-sesion"); @@ -104,10 +108,10 @@ public void testLoginView_NavigateToRegister() { } /** - * Verifica que el enlace de recuperar contrasena lleva a la pagina correspondiente. + * Verifies that the forgot password link navigates to the corresponding page. */ @Test - @DisplayName("Login View - Enlace a recuperar contraseña") + @DisplayName("Login View - Link to forgot password") public void testLoginView_NavigateToForgotPassword() { navigateTo("/iniciar-sesion"); @@ -118,20 +122,29 @@ public void testLoginView_NavigateToForgotPassword() { } /** - * Verifica que un nuevo usuario puede registrarse con datos validos y es redirigido a la pagina principal. + * Verifies that a new user can register with valid data and is redirected to + * the home page. */ @Test - @DisplayName("Register View - Registro exitoso con datos válidos") + @DisplayName("Register View - Successful registration with valid data") public void testRegisterView_SuccessfulRegistration() { navigateTo("/registro"); String timestamp = String.valueOf(System.currentTimeMillis()); fillInput(By.cssSelector(".register-container input[type='email']"), "user" + timestamp + "@example.com"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), "user" + timestamp); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + "user" + timestamp); fillInput(By.cssSelector(".register-container input[type='password']"), "password123"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), "Test"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), "User"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + "Test"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + "User"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); clickElement(By.cssSelector(".register-container button[type='submit']")); waitForUrlContains("/"); @@ -139,41 +152,85 @@ public void testRegisterView_SuccessfulRegistration() { } /** - * Verifica que se muestra un error al intentar registrarse con un email que ya esta en uso. + * Verifies that an error is shown when registering with an email already in + * use. */ @Test - @DisplayName("Register View - Error con email ya registrado") + @DisplayName("Register View - Error with email already registered") public void testRegisterView_EmailAlreadyExists() { + navigateTo("/registro"); + String timestamp = String.valueOf(System.currentTimeMillis()); + String duplicateEmail = "existing" + timestamp + "@example.com"; + + fillInput(By.cssSelector(".register-container input[type='email']"), duplicateEmail); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + "existinguser" + timestamp); + fillInput(By.cssSelector(".register-container input[type='password']"), "password123"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + "Test"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + "User"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); + clickElement(By.cssSelector(".register-container button[type='submit']")); + + waitForUrlContains("/"); + assertTrue(getCurrentUrl().contains("/"), "La primera creación debería ser exitosa"); + navigateTo("/registro"); - fillInput(By.cssSelector(".register-container input[type='email']"), "existing@example.com"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), "newuser123"); + fillInput(By.cssSelector(".register-container input[type='email']"), duplicateEmail); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + "anotheruser" + timestamp); fillInput(By.cssSelector(".register-container input[type='password']"), "password123"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), "Test"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), "User"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + "Test"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + "User"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); clickElement(By.cssSelector(".register-container button[type='submit']")); sleep(1000); assertTrue(isErrorMessagePresent(), "Debería mostrar un mensaje de error"); String errorMessage = getErrorMessage().toLowerCase(); - assertTrue(errorMessage.contains("email") || errorMessage.contains("existe") || errorMessage.contains("registrado"), "El mensaje de error debería indicar que el email ya existe"); + assertTrue( + errorMessage.contains("email") || errorMessage.contains("existe") + || errorMessage.contains("registrado"), + "El mensaje de error debería indicar que el email ya existe"); } /** - * Verifica que el registro falla cuando se introduce un email con formato invalido. + * Verifies that registration fails when an email with invalid format is + * provided. */ @Test - @DisplayName("Register View - Error con email inválido") + @DisplayName("Register View - Error with invalid email") public void testRegisterView_InvalidEmailFormat() { navigateTo("/registro"); fillInput(By.cssSelector(".register-container input[type='email']"), "invalid-email"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), "testuser"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + "testuser"); fillInput(By.cssSelector(".register-container input[type='password']"), "password123"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), "Test"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), "User"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + "Test"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + "User"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); clickElement(By.cssSelector(".register-container button[type='submit']")); sleep(500); @@ -181,115 +238,165 @@ public void testRegisterView_InvalidEmailFormat() { } /** - * Verifica que el registro rechaza un email que supera el limite maximo de caracteres permitidos. + * Verifies that registration rejects an email exceeding the maximum length. */ @Test - @DisplayName("Register View - Error con email demasiado largo") + @DisplayName("Register View - Error with email too long") public void testRegisterView_EmailTooLong() { navigateTo("/registro"); String longEmail = "a".repeat(45) + "@test.com"; fillInput(By.cssSelector(".register-container input[type='email']"), longEmail); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), "testuser"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + "testuser"); fillInput(By.cssSelector(".register-container input[type='password']"), "password123"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), "Test"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), "User"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + "Test"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + "User"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); clickElement(By.cssSelector(".register-container button[type='submit']")); sleep(1000); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), "Debería mostrar error o permanecer en registro"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), + "Debería mostrar error o permanecer en registro"); } /** - * Verifica que el registro rechaza una contrasena que supera el limite maximo de caracteres. + * Verifies that registration rejects a password that exceeds the maximum + * length. */ @Test - @DisplayName("Register View - Error con password demasiado largo") + @DisplayName("Register View - Error with password too long") public void testRegisterView_PasswordTooLong() { navigateTo("/registro"); String longPassword = "a".repeat(30); fillInput(By.cssSelector(".register-container input[type='email']"), "test@example.com"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), "testuser"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + "testuser"); fillInput(By.cssSelector(".register-container input[type='password']"), longPassword); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), "Test"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), "User"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + "Test"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + "User"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); clickElement(By.cssSelector(".register-container button[type='submit']")); sleep(1000); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), "Debería mostrar error o permanecer en registro"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), + "Debería mostrar error o permanecer en registro"); } /** - * Verifica que el registro rechaza un nombre de usuario que excede la longitud maxima permitida. + * Verifies that registration rejects a username that exceeds the maximum + * allowed length. */ @Test - @DisplayName("Register View - Error con username demasiado largo") + @DisplayName("Register View - Error with username too long") public void testRegisterView_UsernameTooLong() { navigateTo("/registro"); String longUsername = "a".repeat(30); fillInput(By.cssSelector(".register-container input[type='email']"), "test@example.com"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), longUsername); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + longUsername); fillInput(By.cssSelector(".register-container input[type='password']"), "password123"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), "Test"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), "User"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + "Test"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + "User"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); clickElement(By.cssSelector(".register-container button[type='submit']")); sleep(1000); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), "Debería mostrar error o permanecer en registro"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), + "Debería mostrar error o permanecer en registro"); } /** - * Verifica que el registro rechaza un nombre que excede la longitud maxima permitida. + * Verifies that registration rejects a first name that exceeds the maximum + * allowed length. */ @Test - @DisplayName("Register View - Error con firstName demasiado largo") + @DisplayName("Register View - Error with first name too long") public void testRegisterView_FirstNameTooLong() { navigateTo("/registro"); String longFirstName = "a".repeat(25); fillInput(By.cssSelector(".register-container input[type='email']"), "test@example.com"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), "testuser"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + "testuser"); fillInput(By.cssSelector(".register-container input[type='password']"), "password123"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), longFirstName); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), "User"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + longFirstName); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + "User"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); clickElement(By.cssSelector(".register-container button[type='submit']")); sleep(1000); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), "Debería mostrar error o permanecer en registro"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), + "Debería mostrar error o permanecer en registro"); } /** - * Verifica que el registro rechaza un apellido que excede la longitud maxima permitida. + * Verifies that registration rejects a last name that exceeds the maximum + * allowed length. */ @Test - @DisplayName("Register View - Error con lastName demasiado largo") + @DisplayName("Register View - Error with last name too long") public void testRegisterView_LastNameTooLong() { navigateTo("/registro"); String longLastName = "a".repeat(55); fillInput(By.cssSelector(".register-container input[type='email']"), "test@example.com"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), "testuser"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), + "testuser"); fillInput(By.cssSelector(".register-container input[type='password']"), "password123"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), "Test"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), longLastName); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), + "Test"); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), + longLastName); + fillInput( + By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), + "123456789"); clickElement(By.cssSelector(".register-container button[type='submit']")); sleep(1000); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), "Debería mostrar error o permanecer en registro"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), + "Debería mostrar error o permanecer en registro"); } /** - * Verifica que el formulario de registro no se envia cuando todos los campos obligatorios estan vacios. + * Verifies that the registration form is not submitted when all required fields + * are empty. */ @Test - @DisplayName("Register View - Error con campos obligatorios vacíos") + @DisplayName("Register View - Error with empty required fields") public void testRegisterView_RequiredFieldsEmpty() { navigateTo("/registro"); @@ -300,30 +407,11 @@ public void testRegisterView_RequiredFieldsEmpty() { } /** - * Verifica que el registro rechaza una contrasena demasiado corta que no cumple el minimo de caracteres. - */ - @Test - @DisplayName("Register View - Error con password demasiado corto") - public void testRegisterView_PasswordTooShort() { - navigateTo("/registro"); - - fillInput(By.cssSelector(".register-container input[type='email']"), "test@example.com"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][2]//input"), "testuser"); - fillInput(By.cssSelector(".register-container input[type='password']"), "123"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][4]//input"), "Test"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][5]//input"), "User"); - fillInput(By.xpath("//div[contains(@class,'register-container')]//div[contains(@class,'form-group')][6]//input"), "123456789"); - clickElement(By.cssSelector(".register-container button[type='submit']")); - - sleep(1000); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/registro"), "Debería mostrar error o permanecer en registro"); - } - - /** - * Verifica que el enlace para volver a login desde la pagina de registro funciona correctamente. + * Verifies that the link back to login from the registration page works + * correctly. */ @Test - @DisplayName("Register View - Enlace a página de login") + @DisplayName("Register View - Link to login page") public void testRegisterView_NavigateToLogin() { navigateTo("/registro"); @@ -334,10 +422,11 @@ public void testRegisterView_NavigateToLogin() { } /** - * Verifica que se puede cambiar la contrasena correctamente proporcionando email, usuario y nueva clave validos. + * Verifies that the password can be changed by providing a valid email, + * username, and new password. */ @Test - @DisplayName("Forgot Password View - Cambio exitoso de contraseña") + @DisplayName("Forgot Password View - Successful password change") public void testForgotPasswordView_SuccessfulPasswordChange() { navigateTo("/clave-olvidada"); @@ -349,16 +438,16 @@ public void testForgotPasswordView_SuccessfulPasswordChange() { sleep(1000); assertTrue( - isSuccessMessagePresent() || isErrorMessagePresent() || getCurrentUrl().contains("/iniciar-sesion"), - "Debería mostrar feedback de éxito/error o redirigir al login" - ); + isSuccessMessagePresent() || isErrorMessagePresent() || getCurrentUrl().contains("/iniciar-sesion"), + "Debería mostrar feedback de éxito/error o redirigir al login"); } /** - * Verifica que se muestra un error al intentar recuperar la contrasena con un email no registrado. + * Verifies that an error is shown when attempting password recovery with an + * unregistered email. */ @Test - @DisplayName("Forgot Password View - Error con email no registrado") + @DisplayName("Forgot Password View - Error with unregistered email") public void testForgotPasswordView_EmailNotFound() { navigateTo("/clave-olvidada"); @@ -370,16 +459,16 @@ public void testForgotPasswordView_EmailNotFound() { sleep(1000); assertTrue( - isErrorMessagePresent() || getCurrentUrl().contains("/clave-olvidada"), - "Debería mostrar error o mantenerse en la pantalla de recuperación" - ); + isErrorMessagePresent() || getCurrentUrl().contains("/clave-olvidada"), + "Debería mostrar error o mantenerse en la pantalla de recuperación"); } /** - * Verifica que la recuperacion de contrasena no avanza cuando se introduce un email con formato invalido. + * Verifies that password recovery does not proceed when an invalid email format + * is provided. */ @Test - @DisplayName("Forgot Password View - Error con email inválido") + @DisplayName("Forgot Password View - Error with invalid email") public void testForgotPasswordView_InvalidEmail() { navigateTo("/clave-olvidada"); @@ -392,10 +481,11 @@ public void testForgotPasswordView_InvalidEmail() { } /** - * Verifica que se rechaza una nueva contrasena que no cumple con la longitud minima requerida. + * Verifies that a new password that does not meet the minimum length is + * rejected. */ @Test - @DisplayName("Forgot Password View - Error con nueva contraseña demasiado corta") + @DisplayName("Forgot Password View - Error with new password too short") public void testForgotPasswordView_NewPasswordTooShort() { navigateTo("/clave-olvidada"); @@ -404,14 +494,15 @@ public void testForgotPasswordView_NewPasswordTooShort() { clickElement(By.cssSelector("button[type='submit']")); sleep(1000); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/clave-olvidada"), "Debería mostrar error o permanecer en la página"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/clave-olvidada"), + "Debería mostrar error o permanecer en la página"); } /** - * Verifica que el formulario de recuperacion no se envia cuando todos los campos estan vacios. + * Verifies that the recovery form is not submitted when all fields are empty. */ @Test - @DisplayName("Forgot Password View - Error con campos vacíos") + @DisplayName("Forgot Password View - Error with empty fields") public void testForgotPasswordView_EmptyFields() { navigateTo("/clave-olvidada"); @@ -422,10 +513,10 @@ public void testForgotPasswordView_EmptyFields() { } /** - * Verifica que el enlace para volver al login desde la pagina de recuperacion funciona correctamente. + * Verifies that the link back to login from the recovery page works correctly. */ @Test - @DisplayName("Forgot Password View - Enlace a página de login") + @DisplayName("Forgot Password View - Link to login page") public void testForgotPasswordView_NavigateToLogin() { navigateTo("/clave-olvidada"); @@ -436,10 +527,11 @@ public void testForgotPasswordView_NavigateToLogin() { } /** - * Verifica que tras cerrar sesion se redirige al login al intentar acceder a una ruta protegida. + * Verifies that after logout the user is redirected to login when accessing a + * protected route. */ @Test - @DisplayName("Auth - Logout desde la UI redirige a login") + @DisplayName("Auth - UI logout redirects to login") public void testAuth_LogoutFromUI() { login("carlos.martinez", "ClaveSegura2025"); sleep(1000); diff --git a/src/test/java/eventManager/selenium/views/EventViewsSeleniumTest.java b/src/test/java/eventManager/selenium/views/EventViewsSeleniumTest.java index 883ef5a..6d39572 100644 --- a/src/test/java/eventManager/selenium/views/EventViewsSeleniumTest.java +++ b/src/test/java/eventManager/selenium/views/EventViewsSeleniumTest.java @@ -11,13 +11,15 @@ import static org.junit.jupiter.api.Assertions.*; /** - * Tests de Selenium para las vistas de eventos. Cubre la lista de eventos con filtros y paginacion, la creacion de nuevos eventos y la modificacion de eventos existentes con sus modos de lectura y edicion. + * Selenium tests for the event views. Covers the event list with filters and + * pagination, creating new events, and updating existing events in read-only + * and edit modes. */ @DisplayName("Event Views Selenium Tests") public class EventViewsSeleniumTest extends BaseSeleniumTest { /** - * Autentica al usuario antes de cada test para tener acceso a las vistas de eventos. + * Authenticates the user before each test to access event views. */ @BeforeEach public void authenticateUser() { @@ -25,10 +27,11 @@ public void authenticateUser() { } /** - * Verifica que la pagina de lista de eventos se carga correctamente con el filtro de rol y el boton de busqueda. + * Verifies that the event list page loads correctly with the role filter and + * search button. */ @Test - @DisplayName("Event List View - Visualizar pagina de lista de eventos") + @DisplayName("Event List View - Display event list page") public void testEventListView_DisplayEvents() { navigateTo("/eventos"); @@ -38,10 +41,10 @@ public void testEventListView_DisplayEvents() { } /** - * Verifica que se pueden filtrar los eventos seleccionando el rol de anfitrion. + * Verifies that events can be filtered by selecting the Host role. */ @Test - @DisplayName("Event List View - Filtrar eventos por rol Anfitrion") + @DisplayName("Event List View - Filter events by Host role") public void testEventListView_FilterByHost() { navigateTo("/eventos"); @@ -50,14 +53,16 @@ public void testEventListView_FilterByHost() { clickElement(By.cssSelector(".search-btn")); waitForUiToSettle(); - assertTrue(isElementPresent(By.cssSelector(".event-table")) || isElementPresent(By.cssSelector(".error-message")), "Deberia mostrar tabla de eventos o un mensaje"); + assertTrue( + isElementPresent(By.cssSelector(".event-table")) || isElementPresent(By.cssSelector(".error-message")), + "Deberia mostrar tabla de eventos o un mensaje"); } /** - * Verifica que se pueden filtrar los eventos seleccionando el rol de invitado. + * Verifies that events can be filtered by selecting the Guest role. */ @Test - @DisplayName("Event List View - Filtrar eventos por rol Invitado") + @DisplayName("Event List View - Filter events by Guest role") public void testEventListView_FilterByGuest() { navigateTo("/eventos"); @@ -66,26 +71,29 @@ public void testEventListView_FilterByGuest() { clickElement(By.cssSelector(".search-btn")); waitForUiToSettle(); - assertTrue(isElementPresent(By.cssSelector(".event-table")) || isElementPresent(By.cssSelector(".error-message")), "Deberia mostrar tabla de eventos o un mensaje"); + assertTrue( + isElementPresent(By.cssSelector(".event-table")) || isElementPresent(By.cssSelector(".error-message")), + "Deberia mostrar tabla de eventos o un mensaje"); } /** - * Verifica que el boton de busqueda permanece deshabilitado hasta que se selecciona un rol. + * Verifies that the search button remains disabled until a role is selected. */ @Test - @DisplayName("Event List View - Boton buscar deshabilitado sin seleccionar rol") + @DisplayName("Event List View - Search button disabled without role") public void testEventListView_SearchDisabledWithoutRole() { navigateTo("/eventos"); org.openqa.selenium.WebElement searchBtn = waitForElement(By.cssSelector(".search-btn")); - assertFalse(searchBtn.isEnabled(), "El boton de busqueda deberia estar deshabilitado si no se selecciona un rol"); + assertFalse(searchBtn.isEnabled(), + "El boton de busqueda deberia estar deshabilitado si no se selecciona un rol"); } /** - * Verifica que al pulsar en el enlace de informacion de un evento se navega a sus detalles. + * Verifies that clicking the event info link navigates to event details. */ @Test - @DisplayName("Event List View - Ver detalles de un evento desde la tabla") + @DisplayName("Event List View - View event details from table") public void testEventListView_ViewEventDetails() { navigateTo("/eventos"); @@ -103,10 +111,10 @@ public void testEventListView_ViewEventDetails() { } /** - * Verifica que la paginacion con flechas funciona correctamente en la tabla de eventos. + * Verifies that arrow pagination works correctly in the events table. */ @Test - @DisplayName("Event List View - Paginacion con flechas") + @DisplayName("Event List View - Arrow pagination") public void testEventListView_Pagination() { navigateTo("/eventos"); @@ -119,15 +127,17 @@ public void testEventListView_Pagination() { clickElement(By.cssSelector(".pagination-arrow:last-child")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isElementPresent(By.cssSelector(".event-table")), "Deberia mostrar la segunda pagina de eventos"); + assertTrue(isElementPresent(By.cssSelector(".event-table")), + "Deberia mostrar la segunda pagina de eventos"); } } /** - * Verifica que se puede crear un evento rellenando todos los campos obligatorios con datos validos. + * Verifies that an event can be created by filling in all required fields with + * valid data. */ @Test - @DisplayName("Event Create View - Crear evento exitosamente") + @DisplayName("Event Create View - Create event successfully") public void testEventCreateView_CreateEventSuccessfully() { navigateTo("/crear-evento"); String timestamp = String.valueOf(System.currentTimeMillis()); @@ -141,16 +151,15 @@ public void testEventCreateView_CreateEventSuccessfully() { waitForUiToSettle(); assertTrue( - isSuccessMessagePresent() || isErrorMessagePresent(), - "Deberia mostrar feedback de éxito o error tras crear evento" - ); + isSuccessMessagePresent() || isErrorMessagePresent(), + "Deberia mostrar feedback de éxito o error tras crear evento"); } /** - * Verifica que la creacion de evento falla cuando el nombre excede la longitud maxima permitida. + * Verifies that event creation fails when the name exceeds the maximum length. */ @Test - @DisplayName("Event Create View - Error con nombre demasiado largo") + @DisplayName("Event Create View - Error with name too long") public void testEventCreateView_NameTooLong() { navigateTo("/crear-evento"); String longName = "A".repeat(105); @@ -163,14 +172,16 @@ public void testEventCreateView_NameTooLong() { clickElement(By.cssSelector("button[type='submit']")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/crear-evento"), "Deberia mostrar error o permanecer en la pagina"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/crear-evento"), + "Deberia mostrar error o permanecer en la pagina"); } /** - * Verifica que la creacion de evento falla cuando la descripcion excede la longitud maxima permitida. + * Verifies that event creation fails when the description exceeds the maximum + * length. */ @Test - @DisplayName("Event Create View - Error con descripcion demasiado larga") + @DisplayName("Event Create View - Error with description too long") public void testEventCreateView_DescriptionTooLong() { navigateTo("/crear-evento"); String longDescription = "A".repeat(505); @@ -183,14 +194,15 @@ public void testEventCreateView_DescriptionTooLong() { clickElement(By.cssSelector("button[type='submit']")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/crear-evento"), "Deberia mostrar error o permanecer en la pagina"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/crear-evento"), + "Deberia mostrar error o permanecer en la pagina"); } /** - * Verifica que el boton de envio esta deshabilitado mientras los campos obligatorios estan vacios. + * Verifies that the submit button is disabled while required fields are empty. */ @Test - @DisplayName("Event Create View - Boton deshabilitado con campos vacios") + @DisplayName("Event Create View - Submit disabled with empty fields") public void testEventCreateView_SubmitDisabledWithEmptyFields() { navigateTo("/crear-evento"); @@ -199,16 +211,15 @@ public void testEventCreateView_SubmitDisabledWithEmptyFields() { waitForUiToSettle(Duration.ofMillis(900)); assertTrue( - !submitBtn.isEnabled() || getCurrentUrl().contains("/crear-evento") || isErrorMessagePresent(), - "Con campos vacíos, no debería completar la creación del evento" - ); + !submitBtn.isEnabled() || getCurrentUrl().contains("/crear-evento") || isErrorMessagePresent(), + "Con campos vacíos, no debería completar la creación del evento"); } /** - * Verifica que la creacion de evento falla cuando se selecciona una fecha anterior a la actual. + * Verifies that event creation fails when a past date is selected. */ @Test - @DisplayName("Event Create View - Error con fecha en el pasado") + @DisplayName("Event Create View - Error with past date") public void testEventCreateView_PastDate() { navigateTo("/crear-evento"); @@ -220,14 +231,16 @@ public void testEventCreateView_PastDate() { clickElement(By.cssSelector("button[type='submit']")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/crear-evento"), "Deberia mostrar error con fecha en el pasado"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/crear-evento"), + "Deberia mostrar error con fecha en el pasado"); } /** - * Verifica que la vista de modificar evento inicia en modo solo lectura con los campos deshabilitados. + * Verifies that the event update view starts in read-only mode with disabled + * fields. */ @Test - @DisplayName("Event Update View - Vista inicia en modo solo lectura") + @DisplayName("Event Update View - Starts in read-only mode") public void testEventUpdateView_StartsInReadOnlyMode() { navigateTo("/eventos"); @@ -250,10 +263,10 @@ public void testEventUpdateView_StartsInReadOnlyMode() { } /** - * Verifica que al pulsar el boton Editar los campos se habilitan para su modificacion. + * Verifies that clicking Edit enables the fields for modification. */ @Test - @DisplayName("Event Update View - Habilitar edicion al pulsar Editar") + @DisplayName("Event Update View - Enable edit mode when clicking Edit") public void testEventUpdateView_EnableEditMode() { navigateTo("/eventos"); @@ -274,16 +287,18 @@ public void testEventUpdateView_EnableEditMode() { org.openqa.selenium.WebElement nameField = waitForElement(By.id("event-name")); assertTrue(nameField.isEnabled(), "El campo nombre deberia estar habilitado tras pulsar Editar"); - assertTrue(isElementPresent(By.xpath("//button[text()='Guardar cambios']")), "Deberia mostrar el boton Guardar cambios"); + assertTrue(isElementPresent(By.xpath("//button[text()='Guardar cambios']")), + "Deberia mostrar el boton Guardar cambios"); assertTrue(isElementPresent(By.xpath("//button[text()='Cancelar']")), "Deberia mostrar el boton Cancelar"); } } /** - * Verifica que los cambios en un evento se guardan correctamente al pulsar Guardar cambios. + * Verifies that changes to an event are saved correctly when clicking Save + * changes. */ @Test - @DisplayName("Event Update View - Actualizar evento exitosamente") + @DisplayName("Event Update View - Update event successfully") public void testEventUpdateView_UpdateEventSuccessfully() { navigateTo("/eventos"); @@ -312,10 +327,11 @@ public void testEventUpdateView_UpdateEventSuccessfully() { } /** - * Verifica que al cancelar la edicion los campos vuelven a modo solo lectura sin guardar cambios. + * Verifies that canceling the edit returns fields to read-only mode without + * saving changes. */ @Test - @DisplayName("Event Update View - Cancelar edicion") + @DisplayName("Event Update View - Cancel edit") public void testEventUpdateView_CancelEdit() { navigateTo("/eventos"); diff --git a/src/test/java/eventManager/selenium/views/GiftAndTicketViewsSeleniumTest.java b/src/test/java/eventManager/selenium/views/GiftAndTicketViewsSeleniumTest.java index 78d6743..e527029 100644 --- a/src/test/java/eventManager/selenium/views/GiftAndTicketViewsSeleniumTest.java +++ b/src/test/java/eventManager/selenium/views/GiftAndTicketViewsSeleniumTest.java @@ -11,13 +11,15 @@ import static org.junit.jupiter.api.Assertions.*; /** - * Tests de Selenium para las vistas de regalos y tickets. Cubre la inscripcion a eventos, la modificacion de datos de asistencia, la consulta de detalles de evento con acciones de anfitrion y la gestion de regalos (crear, editar y contribuir). + * Selenium tests for gift and ticket views. Covers event enrollment, attendance + * updates, event detail access with host actions, and gift management (create, + * edit, contribute). */ @DisplayName("Gift and Ticket Views Selenium Tests") public class GiftAndTicketViewsSeleniumTest extends BaseSeleniumTest { /** - * Autentica al usuario antes de cada test para tener acceso a las vistas de regalos y tickets. + * Authenticates the user before each test to access gift and ticket views. */ @BeforeEach public void authenticateUser() { @@ -25,10 +27,11 @@ public void authenticateUser() { } /** - * Verifica que el formulario de inscripcion muestra los campos de codigo de evento, invitados y notas. + * Verifies that the enrollment form shows event code, guest count, and notes + * fields. */ @Test - @DisplayName("Ticket Event Join View - Visualizar formulario de inscripcion") + @DisplayName("Ticket Event Join View - Display enrollment form") public void testTicketEventJoinView_DisplayForm() { navigateTo("/inscribirse-evento"); @@ -38,10 +41,11 @@ public void testTicketEventJoinView_DisplayForm() { } /** - * Verifica que se puede enviar el formulario de inscripcion con un codigo de evento y datos de asistencia. + * Verifies that the enrollment form can be submitted with an event code and + * attendance data. */ @Test - @DisplayName("Ticket Event Join View - Inscribirse a un evento exitosamente") + @DisplayName("Ticket Event Join View - Successfully join an event") public void testTicketEventJoinView_JoinEventSuccessfully() { navigateTo("/inscribirse-evento"); @@ -55,10 +59,11 @@ public void testTicketEventJoinView_JoinEventSuccessfully() { } /** - * Verifica que se muestra un error al intentar inscribirse con un codigo de evento que no existe. + * Verifies that an error is shown when enrolling with a non-existent event + * code. */ @Test - @DisplayName("Ticket Event Join View - Error con codigo de evento invalido") + @DisplayName("Ticket Event Join View - Error with invalid event code") public void testTicketEventJoinView_InvalidEventCode() { navigateTo("/inscribirse-evento"); @@ -70,10 +75,10 @@ public void testTicketEventJoinView_InvalidEventCode() { } /** - * Verifica que se rechaza un numero de invitados negativo al inscribirse a un evento. + * Verifies that a negative guest number is rejected when enrolling in an event. */ @Test - @DisplayName("Ticket Event Join View - Error con numero de invitados negativo") + @DisplayName("Ticket Event Join View - Error with negative guest number") public void testTicketEventJoinView_NegativeGuestNumber() { navigateTo("/inscribirse-evento"); @@ -82,28 +87,31 @@ public void testTicketEventJoinView_NegativeGuestNumber() { clickElement(By.cssSelector("button[type='submit']")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/inscribirse-evento"), "Deberia mostrar error con numero negativo"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/inscribirse-evento"), + "Deberia mostrar error con numero negativo"); } /** - * Verifica que el formulario no se envia si no se introduce un codigo de evento. + * Verifies that the form is not submitted if no event code is provided. */ @Test - @DisplayName("Ticket Event Join View - Inscripcion sin codigo de evento") + @DisplayName("Ticket Event Join View - Enrollment without event code") public void testTicketEventJoinView_EmptyEventCode() { navigateTo("/inscribirse-evento"); clickElement(By.cssSelector("button[type='submit']")); waitForUiToSettle(Duration.ofMillis(700)); - assertTrue(getCurrentUrl().contains("/inscribirse-evento"), "Deberia permanecer en la pagina sin codigo de evento"); + assertTrue(getCurrentUrl().contains("/inscribirse-evento"), + "Deberia permanecer en la pagina sin codigo de evento"); } /** - * Verifica que los campos de asistencia estan deshabilitados en modo solo lectura al acceder a la vista. + * Verifies that attendance fields are disabled in read-only mode when accessing + * the view. */ @Test - @DisplayName("Ticket Event Update View - Campos deshabilitados en modo lectura") + @DisplayName("Ticket Event Update View - Fields disabled in read-only mode") public void testTicketEventUpdateView_ReadOnlyMode() { navigateTo("/eventos"); Select roleSelect = new Select(waitForElement(By.id("role"))); @@ -120,17 +128,18 @@ public void testTicketEventUpdateView_ReadOnlyMode() { waitForUiToSettle(Duration.ofMillis(1200)); org.openqa.selenium.WebElement guestField = waitForElement(By.id("guest-number")); - assertFalse(guestField.isEnabled(), "El campo acompanantes deberia estar deshabilitado en modo lectura"); + assertFalse(guestField.isEnabled(), + "El campo acompanantes deberia estar deshabilitado en modo lectura"); assertTrue(isElementPresent(By.xpath("//button[text()='Editar']")), "Deberia mostrar el boton Editar"); } } } /** - * Verifica que se pueden editar y guardar los datos de asistencia de un ticket correctamente. + * Verifies that attendance data for a ticket can be edited and saved correctly. */ @Test - @DisplayName("Ticket Event Update View - Habilitar edicion y guardar cambios") + @DisplayName("Ticket Event Update View - Enable edit and save changes") public void testTicketEventUpdateView_EditAndSave() { navigateTo("/eventos"); Select roleSelect = new Select(waitForElement(By.id("role"))); @@ -160,16 +169,17 @@ public void testTicketEventUpdateView_EditAndSave() { clickElement(By.xpath("//button[text()='Guardar cambios']")); waitForUiToSettle(); - assertTrue(isSuccessMessagePresent() || getCurrentUrl().contains("/eventos"), "Deberia guardar los cambios exitosamente"); + assertTrue(isSuccessMessagePresent() || getCurrentUrl().contains("/eventos"), + "Deberia guardar los cambios exitosamente"); } } } /** - * Verifica que al cancelar la edicion de un ticket los campos vuelven a modo solo lectura. + * Verifies that canceling ticket edits returns fields to read-only mode. */ @Test - @DisplayName("Ticket Event Update View - Cancelar edicion") + @DisplayName("Ticket Event Update View - Cancel edit") public void testTicketEventUpdateView_CancelEdit() { navigateTo("/eventos"); Select roleSelect = new Select(waitForElement(By.id("role"))); @@ -199,10 +209,11 @@ public void testTicketEventUpdateView_CancelEdit() { } /** - * Verifica que los detalles de un evento se muestran con la tabla de informacion y la seccion de acciones. + * Verifies that event details are shown with the information table and actions + * section. */ @Test - @DisplayName("Ticket Event Detail View - Visualizar informacion del evento") + @DisplayName("Ticket Event Detail View - Display event information") public void testTicketEventDetailView_DisplayEventDetails() { navigateTo("/eventos"); Select roleSelect = new Select(waitForElement(By.id("role"))); @@ -214,16 +225,17 @@ public void testTicketEventDetailView_DisplayEventDetails() { clickElement(By.cssSelector(".info-link")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isElementPresent(By.cssSelector(".info-table")), "Deberia mostrar la tabla de informacion del evento"); + assertTrue(isElementPresent(By.cssSelector(".info-table")), + "Deberia mostrar la tabla de informacion del evento"); assertTrue(isElementPresent(By.cssSelector(".actions-section")), "Deberia mostrar la seccion de acciones"); } } /** - * Verifica que un anfitrion puede ver los botones de modificar evento y consultar invitados. + * Verifies that a host can see the edit event and view guests buttons. */ @Test - @DisplayName("Ticket Event Detail View - Botones de accion visibles para HOST") + @DisplayName("Ticket Event Detail View - Action buttons visible for HOST") public void testTicketEventDetailView_HostActionButtons() { navigateTo("/eventos"); Select roleSelect = new Select(waitForElement(By.id("role"))); @@ -235,18 +247,21 @@ public void testTicketEventDetailView_HostActionButtons() { clickElement(By.cssSelector(".info-link")); waitForUiToSettle(Duration.ofMillis(1200)); - boolean hostButtonsVisible = isElementPresent(By.xpath("//button[contains(text(),'Modificar informacion del evento')]")) + boolean hostButtonsVisible = isElementPresent( + By.xpath("//button[contains(text(),'Modificar informacion del evento')]")) && isElementPresent(By.xpath("//button[contains(text(),'Consultar invitados')]")); - boolean guestActionsVisible = isElementPresent(By.xpath("//button[contains(text(),'Consultar lista de regalos')]")); - assertTrue(hostButtonsVisible || guestActionsVisible, "Deberia mostrar acciones disponibles según el rol del usuario"); + boolean guestActionsVisible = isElementPresent( + By.xpath("//button[contains(text(),'Consultar lista de regalos')]")); + assertTrue(hostButtonsVisible || guestActionsVisible, + "Deberia mostrar acciones disponibles según el rol del usuario"); } } /** - * Verifica que al pulsar el boton de modificar evento se navega a la vista de edicion. + * Verifies that clicking the edit event button navigates to the edit view. */ @Test - @DisplayName("Ticket Event Detail View - Navegar a modificar evento") + @DisplayName("Ticket Event Detail View - Navigate to edit event") public void testTicketEventDetailView_NavigateToEditEvent() { navigateTo("/eventos"); Select roleSelect = new Select(waitForElement(By.id("role"))); @@ -268,10 +283,11 @@ public void testTicketEventDetailView_NavigateToEditEvent() { } /** - * Verifica que al pulsar el boton de consultar regalos se navega a la lista de regalos del evento. + * Verifies that clicking the view gifts button navigates to the event gift + * list. */ @Test - @DisplayName("Ticket Event Detail View - Navegar a lista de regalos") + @DisplayName("Ticket Event Detail View - Navigate to gift list") public void testTicketEventDetailView_NavigateToGifts() { navigateTo("/eventos"); Select roleSelect = new Select(waitForElement(By.id("role"))); @@ -293,10 +309,10 @@ public void testTicketEventDetailView_NavigateToGifts() { } /** - * Verifica que se puede crear un regalo desde el popup rellenando nombre y precio. + * Verifies that a gift can be created from the popup by filling name and price. */ @Test - @DisplayName("Gift Event List View - Crear regalo desde popup exitosamente") + @DisplayName("Gift Event List View - Create gift from popup successfully") public void testGiftEventListView_CreateGiftFromPopup() { String eventCode = "MADRID"; navigateTo("/evento/" + eventCode + "/regalos"); @@ -318,17 +334,18 @@ public void testGiftEventListView_CreateGiftFromPopup() { waitForUiToSettle(); assertTrue( - !isElementVisible(By.cssSelector(".popup-overlay")) || isErrorMessagePresent() || isElementPresent(By.cssSelector(".error-message-popup")), - "Tras guardar, el popup debería cerrarse o mostrar feedback de error" - ); + !isElementVisible(By.cssSelector(".popup-overlay")) || isErrorMessagePresent() + || isElementPresent(By.cssSelector(".error-message-popup")), + "Tras guardar, el popup debería cerrarse o mostrar feedback de error"); } } /** - * Verifica que el popup de creacion de regalo no se cierra si falta el campo nombre obligatorio. + * Verifies that the gift creation popup stays open if the required name field + * is missing. */ @Test - @DisplayName("Gift Event List View - Error al crear regalo sin nombre") + @DisplayName("Gift Event List View - Error creating gift without name") public void testGiftEventListView_CreateGiftValidationError() { String eventCode = "MADRID"; navigateTo("/evento/" + eventCode + "/regalos"); @@ -342,15 +359,16 @@ public void testGiftEventListView_CreateGiftValidationError() { clickElement(By.cssSelector(".save-btn")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isElementPresent(By.cssSelector(".popup-overlay")), "El popup deberia seguir visible si falta el nombre"); + assertTrue(isElementPresent(By.cssSelector(".popup-overlay")), + "El popup deberia seguir visible si falta el nombre"); } } /** - * Verifica que al cancelar la creacion de un regalo el popup se cierra sin guardar datos. + * Verifies that canceling gift creation closes the popup without saving data. */ @Test - @DisplayName("Gift Event List View - Cancelar creacion de regalo") + @DisplayName("Gift Event List View - Cancel gift creation") public void testGiftEventListView_CancelCreateGift() { String eventCode = "MADRID"; navigateTo("/evento/" + eventCode + "/regalos"); @@ -370,24 +388,28 @@ public void testGiftEventListView_CancelCreateGift() { } /** - * Verifica que se muestran los detalles de un regalo o un mensaje de error si no existe. + * Verifies that gift details are shown or an error message appears if it does + * not exist. */ @Test - @DisplayName("Gift Detail View - Visualizar detalles del regalo") + @DisplayName("Gift Detail View - Display gift details") public void testGiftDetailView_DisplayGiftDetails() { String eventCode = "MADRID"; int giftId = 1; navigateTo("/evento/" + eventCode + "/regalo/" + giftId); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isElementPresent(By.cssSelector(".gift-info-table")) || isElementPresent(By.cssSelector(".error-message")), "Deberia mostrar los detalles del regalo o mensaje de error"); + assertTrue( + isElementPresent(By.cssSelector(".gift-info-table")) + || isElementPresent(By.cssSelector(".error-message")), + "Deberia mostrar los detalles del regalo o mensaje de error"); } /** - * Verifica que se puede editar un regalo existente a traves del popup de modificacion. + * Verifies that an existing gift can be edited through the edit popup. */ @Test - @DisplayName("Gift Detail View - Editar regalo via popup exitosamente") + @DisplayName("Gift Detail View - Edit gift via popup successfully") public void testGiftDetailView_EditGiftViaPopup() { String eventCode = "MADRID"; int giftId = 1; @@ -395,7 +417,8 @@ public void testGiftDetailView_EditGiftViaPopup() { waitForUiToSettle(Duration.ofMillis(1200)); if (isElementPresent(By.xpath("//button[contains(text(),'Modificar regalo')]"))) { - org.openqa.selenium.WebElement editBtn = driver.findElement(By.xpath("//button[contains(text(),'Modificar regalo')]")); + org.openqa.selenium.WebElement editBtn = driver + .findElement(By.xpath("//button[contains(text(),'Modificar regalo')]")); if (editBtn.isEnabled()) { editBtn.click(); waitForUiToSettle(Duration.ofMillis(700)); @@ -406,17 +429,18 @@ public void testGiftDetailView_EditGiftViaPopup() { clickElement(By.cssSelector(".save-btn")); waitForUiToSettle(); - assertFalse(isElementVisible(By.cssSelector(".popup-overlay")), "El popup deberia cerrarse tras guardar la edicion"); + assertFalse(isElementVisible(By.cssSelector(".popup-overlay")), + "El popup deberia cerrarse tras guardar la edicion"); } } } } /** - * Verifica que al pulsar el boton de anadir aportacion se abre el popup de contribucion. + * Verifies that clicking add contribution opens the contribution popup. */ @Test - @DisplayName("Gift Detail View - Abrir popup de contribucion") + @DisplayName("Gift Detail View - Open contribution popup") public void testGiftDetailView_OpenContributionPopup() { String eventCode = "MADRID"; int giftId = 1; @@ -433,10 +457,10 @@ public void testGiftDetailView_OpenContributionPopup() { } /** - * Verifica que se puede realizar una contribucion economica a un regalo desde el popup. + * Verifies that a monetary contribution can be made to a gift from the popup. */ @Test - @DisplayName("Gift Detail View - Realizar contribucion exitosa") + @DisplayName("Gift Detail View - Make contribution successfully") public void testGiftDetailView_MakeContribution() { String eventCode = "MADRID"; int giftId = 1; @@ -452,16 +476,18 @@ public void testGiftDetailView_MakeContribution() { clickElement(By.cssSelector(".save-btn")); waitForUiToSettle(); - assertFalse(isElementVisible(By.cssSelector(".popup-overlay")), "El popup deberia cerrarse tras la contribucion"); + assertFalse(isElementVisible(By.cssSelector(".popup-overlay")), + "El popup deberia cerrarse tras la contribucion"); } } } /** - * Verifica que desde el popup de editar asistencia se puede confirmar la invitacion de un invitado. + * Verifies that a guest invitation can be confirmed from the edit attendance + * popup. */ @Test - @DisplayName("Ticket Event List View - EditAttendancePopup confirmar invitacion") + @DisplayName("Ticket Event List View - EditAttendancePopup confirm invitation") public void testTicketEventListView_EditAttendanceConfirm() { String eventCode = "MADRID"; navigateTo("/evento/" + eventCode + "/invitados"); @@ -476,7 +502,8 @@ public void testTicketEventListView_EditAttendanceConfirm() { clickElement(By.cssSelector(".edit-icon-btn")); waitForUiToSettle(Duration.ofMillis(700)); - assertTrue(isElementPresent(By.cssSelector(".popup-overlay, .popup")), "Deberia mostrar el popup de editar asistencia"); + assertTrue(isElementPresent(By.cssSelector(".popup-overlay, .popup")), + "Deberia mostrar el popup de editar asistencia"); if (isElementPresent(By.cssSelector(".edit-button"))) { clickElement(By.cssSelector(".edit-button")); @@ -494,10 +521,10 @@ public void testTicketEventListView_EditAttendanceConfirm() { } /** - * Verifica que desde el popup correspondiente se puede promover a un invitado a anfitrion del evento. + * Verifies that a guest can be promoted to host from the corresponding popup. */ @Test - @DisplayName("Ticket Event List View - MakeHostPopup promover a anfitrion") + @DisplayName("Ticket Event List View - MakeHostPopup promote to host") public void testTicketEventListView_MakeHostPopup() { String eventCode = "MADRID"; navigateTo("/evento/" + eventCode + "/invitados"); @@ -512,14 +539,16 @@ public void testTicketEventListView_MakeHostPopup() { clickElement(By.cssSelector(".make-host-btn")); waitForUiToSettle(Duration.ofMillis(700)); - assertTrue(isElementPresent(By.cssSelector(".popup-overlay, .popup-container")), "Deberia mostrar el popup de confirmacion"); + assertTrue(isElementPresent(By.cssSelector(".popup-overlay, .popup-container")), + "Deberia mostrar el popup de confirmacion"); if (isElementPresent(By.cssSelector(".btn-confirm"))) { clickElement(By.cssSelector(".btn-confirm")); waitForUiToSettle(); } - assertFalse(isElementVisible(By.cssSelector(".popup-container")), "El popup deberia cerrarse tras confirmar"); + assertFalse(isElementVisible(By.cssSelector(".popup-container")), + "El popup deberia cerrarse tras confirmar"); } } } diff --git a/src/test/java/eventManager/selenium/views/HomeViewSeleniumTest.java b/src/test/java/eventManager/selenium/views/HomeViewSeleniumTest.java index c222d12..bc4e970 100644 --- a/src/test/java/eventManager/selenium/views/HomeViewSeleniumTest.java +++ b/src/test/java/eventManager/selenium/views/HomeViewSeleniumTest.java @@ -9,13 +9,15 @@ import static org.junit.jupiter.api.Assertions.*; /** - * Tests de Selenium para la vista principal (Home/Dashboard). Comprueba que el dashboard se muestra correctamente tras el login, que los enlaces de navegacion funcionan y que las rutas protegidas redirigen al login cuando no hay sesion activa. + * Selenium tests for the main view (Home/Dashboard). Verifies that the + * dashboard is shown after login, navigation links work, and protected routes + * redirect to login when there is no active session. */ @DisplayName("Home View Selenium Tests") public class HomeViewSeleniumTest extends BaseSeleniumTest { /** - * Autentica al usuario antes de cada test para tener acceso a las vistas protegidas. + * Authenticates the user before each test to access protected views. */ @BeforeEach public void authenticateUser() { @@ -23,23 +25,25 @@ public void authenticateUser() { } /** - * Verifica que tras el login se muestra el dashboard con el mensaje de bienvenida. + * Verifies that after login the dashboard is shown with the welcome message. */ @Test - @DisplayName("Home View - Muestra dashboard tras login") + @DisplayName("Home View - Shows dashboard after login") public void testHomeView_DisplayAfterLogin() { navigateTo("/"); assertTrue(isElementPresent(By.cssSelector(".welcome-box")), "Deberia mostrar el cuadro de bienvenida"); String welcomeText = getElementText(By.cssSelector(".welcome-box h1")); - assertTrue(welcomeText.contains("Bienvenido") || welcomeText.contains("EventManager"), "Deberia mostrar el mensaje de bienvenida a EventManager"); + assertTrue(welcomeText.contains("Bienvenido") || welcomeText.contains("EventManager"), + "Deberia mostrar el mensaje de bienvenida a EventManager"); } /** - * Verifica que el menu de navegacion con sus secciones se muestra en la pagina principal. + * Verifies that the navigation menu and its sections are shown on the home + * page. */ @Test - @DisplayName("Home View - Muestra menu de navegacion") + @DisplayName("Home View - Shows navigation menu") public void testHomeView_DisplayNavigationMenu() { navigateTo("/"); @@ -48,10 +52,10 @@ public void testHomeView_DisplayNavigationMenu() { } /** - * Verifica que el enlace del menu lleva correctamente a la lista de eventos. + * Verifies that the menu link navigates to the event list. */ @Test - @DisplayName("Home View - Navegar a lista de eventos") + @DisplayName("Home View - Navigate to event list") public void testHomeView_NavigateToEventList() { navigateTo("/"); @@ -62,10 +66,10 @@ public void testHomeView_NavigateToEventList() { } /** - * Verifica que el enlace del menu lleva correctamente a la pagina de creacion de evento. + * Verifies that the menu link navigates to the event creation page. */ @Test - @DisplayName("Home View - Navegar a crear evento") + @DisplayName("Home View - Navigate to create event") public void testHomeView_NavigateToCreateEvent() { navigateTo("/"); @@ -76,10 +80,10 @@ public void testHomeView_NavigateToCreateEvent() { } /** - * Verifica que el enlace del menu lleva correctamente a la pagina de inscripcion en evento. + * Verifies that the menu link navigates to the event enrollment page. */ @Test - @DisplayName("Home View - Navegar a inscribirse en evento") + @DisplayName("Home View - Navigate to join event") public void testHomeView_NavigateToJoinEvent() { navigateTo("/"); @@ -90,10 +94,10 @@ public void testHomeView_NavigateToJoinEvent() { } /** - * Verifica que el enlace del menu lleva correctamente a la pagina de actualizacion de perfil. + * Verifies that the menu link navigates to the profile update page. */ @Test - @DisplayName("Home View - Navegar a actualizar perfil") + @DisplayName("Home View - Navigate to update profile") public void testHomeView_NavigateToUpdateProfile() { navigateTo("/"); @@ -104,10 +108,10 @@ public void testHomeView_NavigateToUpdateProfile() { } /** - * Verifica que el enlace del menu lleva correctamente a la pagina de cambio de contrasena. + * Verifies that the menu link navigates to the password change page. */ @Test - @DisplayName("Home View - Navegar a actualizar contrasena") + @DisplayName("Home View - Navigate to update password") public void testHomeView_NavigateToUpdatePassword() { navigateTo("/"); @@ -118,10 +122,11 @@ public void testHomeView_NavigateToUpdatePassword() { } /** - * Verifica que un usuario sin sesion activa es redirigido al login al intentar acceder a una ruta protegida. + * Verifies that a user without an active session is redirected to login when + * accessing a protected route. */ @Test - @DisplayName("Home View - Redirige a login si no autenticado") + @DisplayName("Home View - Redirects to login when unauthenticated") public void testHomeView_RedirectsUnauthenticated() { driver.manage().deleteAllCookies(); ((org.openqa.selenium.JavascriptExecutor) driver) @@ -130,6 +135,7 @@ public void testHomeView_RedirectsUnauthenticated() { navigateTo("/eventos"); sleep(2000); - assertTrue(getCurrentUrl().contains("/iniciar-sesion"), "Deberia redirigir a la pagina de login si no esta autenticado"); + assertTrue(getCurrentUrl().contains("/iniciar-sesion"), + "Deberia redirigir a la pagina de login si no esta autenticado"); } } diff --git a/src/test/java/eventManager/selenium/views/UserViewsSeleniumTest.java b/src/test/java/eventManager/selenium/views/UserViewsSeleniumTest.java index 284d34f..f586613 100644 --- a/src/test/java/eventManager/selenium/views/UserViewsSeleniumTest.java +++ b/src/test/java/eventManager/selenium/views/UserViewsSeleniumTest.java @@ -10,13 +10,15 @@ import static org.junit.jupiter.api.Assertions.*; /** - * Tests de Selenium para las vistas de configuracion de usuario. Cubre la actualizacion del perfil (nombre, apellidos, telefono) y el cambio de contrasena, incluyendo los modos de lectura, edicion y validaciones. + * Selenium tests for user settings views. Covers profile updates (name, last + * name, phone) and password changes, including read-only/edit modes and + * validations. */ @DisplayName("User Views Selenium Tests") public class UserViewsSeleniumTest extends BaseSeleniumTest { /** - * Autentica al usuario antes de cada test para tener acceso a las vistas de usuario. + * Authenticates the user before each test to access user views. */ @BeforeEach public void authenticateUser() { @@ -24,10 +26,11 @@ public void authenticateUser() { } /** - * Verifica que la pagina de perfil muestra todos los campos esperados: nombre, apellidos, email y telefono. + * Verifies that the profile page shows all expected fields: first name, last + * name, email, and phone. */ @Test - @DisplayName("User Update Profile View - Visualizar pagina de perfil") + @DisplayName("User Update Profile View - Display profile page") public void testUserUpdateProfileView_DisplayProfile() { navigateTo("/usuario/actualizar-perfil"); @@ -38,10 +41,10 @@ public void testUserUpdateProfileView_DisplayProfile() { } /** - * Verifica que los campos del perfil estan deshabilitados por defecto en modo solo lectura. + * Verifies that profile fields are disabled by default in read-only mode. */ @Test - @DisplayName("User Update Profile View - Campos deshabilitados en modo lectura") + @DisplayName("User Update Profile View - Fields disabled in read-only mode") public void testUserUpdateProfileView_ReadOnlyMode() { navigateTo("/usuario/actualizar-perfil"); waitForUiToSettle(Duration.ofMillis(1200)); @@ -51,10 +54,10 @@ public void testUserUpdateProfileView_ReadOnlyMode() { } /** - * Verifica que al pulsar Editar los campos del perfil se habilitan para su modificacion. + * Verifies that clicking Edit enables profile fields for modification. */ @Test - @DisplayName("User Update Profile View - Habilitar edicion al pulsar Editar") + @DisplayName("User Update Profile View - Enable edit mode when clicking Edit") public void testUserUpdateProfileView_EnableEditMode() { navigateTo("/usuario/actualizar-perfil"); waitForUiToSettle(Duration.ofMillis(1200)); @@ -69,14 +72,15 @@ public void testUserUpdateProfileView_EnableEditMode() { org.openqa.selenium.WebElement firstName = waitForElement(By.id("firstName")); assertTrue(firstName.isEnabled(), "El campo nombre deberia estar habilitado tras pulsar Editar"); - assertTrue(isElementPresent(By.xpath("//button[text()='Guardar cambios']")), "Deberia mostrar el boton Guardar cambios"); + assertTrue(isElementPresent(By.xpath("//button[text()='Guardar cambios']")), + "Deberia mostrar el boton Guardar cambios"); } /** - * Verifica que se pueden actualizar los datos del perfil y se muestra un mensaje de exito. + * Verifies that profile data can be updated and a success message is shown. */ @Test - @DisplayName("User Update Profile View - Actualizar perfil exitosamente") + @DisplayName("User Update Profile View - Update profile successfully") public void testUserUpdateProfileView_UpdateProfileSuccessfully() { navigateTo("/usuario/actualizar-perfil"); waitForUiToSettle(Duration.ofMillis(1200)); @@ -99,10 +103,11 @@ public void testUserUpdateProfileView_UpdateProfileSuccessfully() { } /** - * Verifica que la actualizacion del perfil falla cuando el nombre excede la longitud maxima. + * Verifies that the profile update fails when the first name exceeds the + * maximum length. */ @Test - @DisplayName("User Update Profile View - Error con firstName demasiado largo") + @DisplayName("User Update Profile View - Error with first name too long") public void testUserUpdateProfileView_FirstNameTooLong() { navigateTo("/usuario/actualizar-perfil"); waitForUiToSettle(Duration.ofMillis(1200)); @@ -121,14 +126,16 @@ public void testUserUpdateProfileView_FirstNameTooLong() { clickElement(By.xpath("//button[text()='Guardar cambios']")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/usuario/actualizar-perfil"), "Deberia mostrar error con nombre demasiado largo"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/usuario/actualizar-perfil"), + "Deberia mostrar error con nombre demasiado largo"); } /** - * Verifica que la actualizacion del perfil falla cuando el apellido excede la longitud maxima. + * Verifies that the profile update fails when the last name exceeds the maximum + * length. */ @Test - @DisplayName("User Update Profile View - Error con lastName demasiado largo") + @DisplayName("User Update Profile View - Error with last name too long") public void testUserUpdateProfileView_LastNameTooLong() { navigateTo("/usuario/actualizar-perfil"); waitForUiToSettle(Duration.ofMillis(1200)); @@ -147,14 +154,15 @@ public void testUserUpdateProfileView_LastNameTooLong() { clickElement(By.xpath("//button[text()='Guardar cambios']")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/usuario/actualizar-perfil"), "Deberia mostrar error con apellido demasiado largo"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/usuario/actualizar-perfil"), + "Deberia mostrar error con apellido demasiado largo"); } /** - * Verifica que al cancelar la edicion del perfil los campos vuelven a modo solo lectura. + * Verifies that canceling profile edits returns fields to read-only mode. */ @Test - @DisplayName("User Update Profile View - Cancelar edicion revierte cambios") + @DisplayName("User Update Profile View - Cancel edit reverts changes") public void testUserUpdateProfileView_CancelUpdate() { navigateTo("/usuario/actualizar-perfil"); waitForUiToSettle(Duration.ofMillis(1200)); @@ -176,10 +184,10 @@ public void testUserUpdateProfileView_CancelUpdate() { } /** - * Verifica que el formulario de cambio de contrasena muestra los tres campos requeridos. + * Verifies that the password change form shows the three required fields. */ @Test - @DisplayName("User Update Password View - Visualizar formulario de cambio de contrasena") + @DisplayName("User Update Password View - Display password change form") public void testUserUpdatePasswordView_DisplayPasswordForm() { navigateTo("/usuario/actualizar-clave"); @@ -189,10 +197,11 @@ public void testUserUpdatePasswordView_DisplayPasswordForm() { } /** - * Verifica que se puede cambiar la contrasena proporcionando la actual y una nueva valida. + * Verifies that the password can be changed by providing the current and a + * valid new password. */ @Test - @DisplayName("User Update Password View - Cambiar contrasena exitosamente") + @DisplayName("User Update Password View - Change password successfully") public void testUserUpdatePasswordView_ChangePasswordSuccessfully() { navigateTo("/usuario/actualizar-clave"); @@ -202,14 +211,15 @@ public void testUserUpdatePasswordView_ChangePasswordSuccessfully() { clickElement(By.cssSelector("button[type='submit']")); waitForUiToSettle(); - assertTrue(isSuccessMessagePresent() || getCurrentUrl().contains("/usuario"), "Deberia cambiar la contrasena exitosamente"); + assertTrue(isSuccessMessagePresent() || getCurrentUrl().contains("/usuario"), + "Deberia cambiar la contrasena exitosamente"); } /** - * Verifica que se muestra un error al introducir una contrasena actual incorrecta. + * Verifies that an error is shown when the current password is incorrect. */ @Test - @DisplayName("User Update Password View - Error con contrasena actual incorrecta") + @DisplayName("User Update Password View - Error with incorrect current password") public void testUserUpdatePasswordView_WrongCurrentPassword() { navigateTo("/usuario/actualizar-clave"); @@ -223,10 +233,11 @@ public void testUserUpdatePasswordView_WrongCurrentPassword() { } /** - * Verifica que se rechaza una nueva contrasena que no cumple con la longitud minima requerida. + * Verifies that a new password that does not meet the minimum length is + * rejected. */ @Test - @DisplayName("User Update Password View - Error con nueva contrasena demasiado corta") + @DisplayName("User Update Password View - Error with new password too short") public void testUserUpdatePasswordView_NewPasswordTooShort() { navigateTo("/usuario/actualizar-clave"); @@ -236,14 +247,16 @@ public void testUserUpdatePasswordView_NewPasswordTooShort() { clickElement(By.cssSelector("button[type='submit']")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/usuario/actualizar-clave"), "Deberia mostrar error con contrasena corta"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/usuario/actualizar-clave"), + "Deberia mostrar error con contrasena corta"); } /** - * Verifica que se muestra un error cuando la nueva contrasena y su confirmacion no coinciden. + * Verifies that an error is shown when the new password and confirmation do not + * match. */ @Test - @DisplayName("User Update Password View - Error con confirmacion no coincidente") + @DisplayName("User Update Password View - Error with mismatched confirmation") public void testUserUpdatePasswordView_PasswordMismatch() { navigateTo("/usuario/actualizar-clave"); @@ -253,14 +266,15 @@ public void testUserUpdatePasswordView_PasswordMismatch() { clickElement(By.cssSelector("button[type='submit']")); waitForUiToSettle(Duration.ofMillis(1200)); - assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/usuario/actualizar-clave"), "Deberia mostrar error si las contrasenas no coinciden"); + assertTrue(isErrorMessagePresent() || getCurrentUrl().contains("/usuario/actualizar-clave"), + "Deberia mostrar error si las contrasenas no coinciden"); } /** - * Verifica que el boton de envio esta deshabilitado cuando todos los campos estan vacios. + * Verifies that the submit button is disabled when all fields are empty. */ @Test - @DisplayName("User Update Password View - Boton deshabilitado con campos vacios") + @DisplayName("User Update Password View - Submit button disabled with empty fields") public void testUserUpdatePasswordView_SubmitDisabledWithEmptyFields() { navigateTo("/usuario/actualizar-clave"); @@ -269,16 +283,17 @@ public void testUserUpdatePasswordView_SubmitDisabledWithEmptyFields() { waitForUiToSettle(Duration.ofMillis(900)); assertTrue( - !submitBtn.isEnabled() || getCurrentUrl().contains("/usuario/actualizar-clave") || isErrorMessagePresent(), - "Con campos vacíos, no debería completarse el cambio de contraseña" - ); + !submitBtn.isEnabled() || getCurrentUrl().contains("/usuario/actualizar-clave") + || isErrorMessagePresent(), + "Con campos vacíos, no debería completarse el cambio de contraseña"); } /** - * Verifica que el boton de envio permanece deshabilitado si solo se rellena la contrasena actual. + * Verifies that the submit button remains disabled if only the current password + * is filled in. */ @Test - @DisplayName("User Update Password View - Error con campos parcialmente rellenos") + @DisplayName("User Update Password View - Error with partially filled fields") public void testUserUpdatePasswordView_OnlyCurrentPassword() { navigateTo("/usuario/actualizar-clave"); diff --git a/src/test/java/eventManager/service/impl/AuthServiceImplTest.java b/src/test/java/eventManager/service/impl/AuthServiceImplTest.java index eafeb05..1163a8a 100644 --- a/src/test/java/eventManager/service/impl/AuthServiceImplTest.java +++ b/src/test/java/eventManager/service/impl/AuthServiceImplTest.java @@ -38,7 +38,8 @@ import static org.mockito.Mockito.*; /** - * Pruebas unitarias del servicio de autenticacion, incluyendo registro, login, refresco de token, logout y cambio de contrasena olvidada. + * Unit tests for the authentication service, including registration, login, + * token refresh, logout, and forgotten password changes. */ @ExtendWith(MockitoExtension.class) @DisplayName("AuthServiceImpl Tests") @@ -108,12 +109,14 @@ void setUp() { } /** - * Verifica que el registro de un nuevo usuario se completa correctamente y genera tokens de acceso. + * Verifies that registering a new user completes successfully and generates + * access tokens. */ @Test - @DisplayName("registerUser - Registro exitoso") + @DisplayName("registerUser - Successful registration") void testRegister_Success() { - when(userRepository.existsByEmailOrUsername("carlos.martinez@eventmanager.es", "carlos.martinez")).thenReturn(false); + when(userRepository.existsByEmailOrUsername("carlos.martinez@eventmanager.es", "carlos.martinez")) + .thenReturn(false); when(passwordEncoder.encode("ClaveSegura2025")).thenReturn("encodedPassword"); when(userRepository.save(any(User.class))).thenReturn(testUser); when(userDetailsService.loadUserByUsername("carlos.martinez")).thenReturn(springUserDetails); @@ -129,38 +132,45 @@ void testRegister_Success() { } /** - * Verifica que registrar un usuario con email o username ya existente lanza una excepcion BAD_REQUEST. + * Verifies that registering a user with an existing email or username throws + * BAD_REQUEST. */ @Test - @DisplayName("registerUser - Usuario ya existe, lanza BAD_REQUEST") + @DisplayName("registerUser - User already exists, throws BAD_REQUEST") void testRegister_UserAlreadyExists() { - when(userRepository.existsByEmailOrUsername("carlos.martinez@eventmanager.es", "carlos.martinez")).thenReturn(true); + when(userRepository.existsByEmailOrUsername("carlos.martinez@eventmanager.es", "carlos.martinez")) + .thenReturn(true); - CustomException ex = assertThrows(CustomException.class, () -> authService.registerUser(userCreateDTO, response)); + CustomException ex = assertThrows(CustomException.class, + () -> authService.registerUser(userCreateDTO, response)); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); assertEquals(Constantes.MESSAGE_USER_ALREADY_REGISTERED, ex.getMessage()); } /** - * Verifica que un error inesperado durante el registro produce una excepcion INTERNAL_SERVER_ERROR. + * Verifies that an unexpected error during registration results in + * INTERNAL_SERVER_ERROR. */ @Test - @DisplayName("registerUser - Error inesperado, lanza INTERNAL_SERVER_ERROR") + @DisplayName("registerUser - Unexpected error, throws INTERNAL_SERVER_ERROR") void testRegister_UnexpectedError() { - when(userRepository.existsByEmailOrUsername("carlos.martinez@eventmanager.es", "carlos.martinez")).thenReturn(false); + when(userRepository.existsByEmailOrUsername("carlos.martinez@eventmanager.es", "carlos.martinez")) + .thenReturn(false); when(passwordEncoder.encode(anyString())).thenReturn("encoded"); when(userRepository.save(any(User.class))).thenThrow(new RuntimeException("DB error")); - CustomException ex = assertThrows(CustomException.class, () -> authService.registerUser(userCreateDTO, response)); + CustomException ex = assertThrows(CustomException.class, + () -> authService.registerUser(userCreateDTO, response)); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, ex.getStatus()); assertEquals("DB error", ex.getMessage()); } /** - * Verifica que el inicio de sesion con credenciales validas devuelve una respuesta exitosa con cookies. + * Verifies that login with valid credentials returns a successful response with + * cookies. */ @Test - @DisplayName("login - Login exitoso") + @DisplayName("login - Successful login") void testLogin_Success() { LoginRequest loginRequest = LoginRequest.builder() .username("carlos.martinez") @@ -181,10 +191,10 @@ void testLogin_Success() { } /** - * Verifica que las credenciales incorrectas durante el login provocan una excepcion UNAUTHORIZED. + * Verifies that invalid credentials during login raise UNAUTHORIZED. */ @Test - @DisplayName("login - Credenciales invalidas, lanza UNAUTHORIZED") + @DisplayName("login - Invalid credentials, throws UNAUTHORIZED") void testLogin_InvalidCredentials() { LoginRequest loginRequest = LoginRequest.builder() .username("wrong") @@ -199,10 +209,10 @@ void testLogin_InvalidCredentials() { } /** - * Verifica que un refresh token valido permite renovar el access token correctamente. + * Verifies that a valid refresh token allows access token renewal. */ @Test - @DisplayName("refreshToken - Token valido, renueva access token") + @DisplayName("refreshToken - Valid token, refreshes access token") void testRefreshToken_Success() { io.jsonwebtoken.Claims claims = mock(io.jsonwebtoken.Claims.class); when(claims.getSubject()).thenReturn("carlos.martinez"); @@ -217,10 +227,11 @@ void testRefreshToken_Success() { } /** - * Verifica que un refresh token invalido devuelve una respuesta FAILURE sin lanzar excepcion. + * Verifies that an invalid refresh token returns a FAILURE response without + * throwing an exception. */ @Test - @DisplayName("refreshToken - Token invalido, retorna FAILURE sin excepcion") + @DisplayName("refreshToken - Invalid token, returns FAILURE without exception") void testRefreshToken_InvalidToken() { when(jwtTokenProvider.validateToken("invalidToken")).thenThrow(new RuntimeException("Invalid token")); @@ -230,10 +241,10 @@ void testRefreshToken_InvalidToken() { } /** - * Verifica que el cierre de sesion limpia el contexto de seguridad y elimina las cookies. + * Verifies that logout clears the security context and removes cookies. */ @Test - @DisplayName("logout - Logout exitoso, limpia contexto y cookies") + @DisplayName("logout - Successful logout, clears context and cookies") void testLogout_Success() { AuthResponse result = authService.logout(response); @@ -242,10 +253,10 @@ void testLogout_Success() { } /** - * Verifica que el cambio de contrasena olvidada se realiza correctamente cuando los datos son validos. + * Verifies that forgotten password change succeeds when data is valid. */ @Test - @DisplayName("changeForgottenPassword - Cambio exitoso") + @DisplayName("changeForgottenPassword - Successful change") void testChangeForgottenPassword_Success() { String usernameValue = "carlos.martinez"; User userWithMatchingName = User.builder() @@ -262,7 +273,8 @@ void testChangeForgottenPassword_Success() { dto.setNewPassword("newPass123"); dto.setNewPasswordConfirm("newPass123"); - when(userRepository.findByEmail("carlos.martinez@eventmanager.es")).thenReturn(Optional.of(userWithMatchingName)); + when(userRepository.findByEmail("carlos.martinez@eventmanager.es")) + .thenReturn(Optional.of(userWithMatchingName)); when(passwordEncoder.encode("newPass123")).thenReturn("encodedNewPass"); when(userRepository.save(userWithMatchingName)).thenReturn(userWithMatchingName); when(userMapper.convertUserToUserDTO(userWithMatchingName)).thenReturn(testUserDTO); @@ -275,10 +287,10 @@ void testChangeForgottenPassword_Success() { } /** - * Verifica que si el email proporcionado no existe en el sistema se lanza NOT_FOUND. + * Verifies that NOT_FOUND is thrown when the provided email does not exist. */ @Test - @DisplayName("changeForgottenPassword - Email no encontrado, lanza NOT_FOUND") + @DisplayName("changeForgottenPassword - Email not found, throws NOT_FOUND") void testChangeForgottenPassword_EmailNotFound() { UserForgottenPassword dto = new UserForgottenPassword(); dto.setEmail("laura.sanchez@eventmanager.es"); @@ -290,10 +302,11 @@ void testChangeForgottenPassword_EmailNotFound() { } /** - * Verifica que si el nombre de usuario no coincide con el registrado se lanza BAD_REQUEST. + * Verifies that BAD_REQUEST is thrown when the username does not match the + * registered one. */ @Test - @DisplayName("changeForgottenPassword - Username no coincide, lanza BAD_REQUEST") + @DisplayName("changeForgottenPassword - Username mismatch, throws BAD_REQUEST") void testChangeForgottenPassword_UsernameMismatch() { UserForgottenPassword dto = new UserForgottenPassword(); dto.setEmail("carlos.martinez@eventmanager.es"); @@ -307,10 +320,11 @@ void testChangeForgottenPassword_UsernameMismatch() { } /** - * Verifica que si la nueva contrasena y su confirmacion no coinciden se lanza BAD_REQUEST. + * Verifies that BAD_REQUEST is thrown when the new password and confirmation do + * not match. */ @Test - @DisplayName("changeForgottenPassword - Passwords no coinciden, lanza BAD_REQUEST") + @DisplayName("changeForgottenPassword - Passwords do not match, throws BAD_REQUEST") void testChangeForgottenPassword_PasswordsMismatch() { String usernameValue = "carlos.martinez"; User userWithMatchingName = User.builder() @@ -327,7 +341,8 @@ void testChangeForgottenPassword_PasswordsMismatch() { dto.setNewPassword("newPass123"); dto.setNewPasswordConfirm("differentPass"); - when(userRepository.findByEmail("carlos.martinez@eventmanager.es")).thenReturn(Optional.of(userWithMatchingName)); + when(userRepository.findByEmail("carlos.martinez@eventmanager.es")) + .thenReturn(Optional.of(userWithMatchingName)); CustomException ex = assertThrows(CustomException.class, () -> authService.changeForgottenPassword(dto)); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); diff --git a/src/test/java/eventManager/service/impl/EventServiceImplTest.java b/src/test/java/eventManager/service/impl/EventServiceImplTest.java index 94e2618..672a38a 100644 --- a/src/test/java/eventManager/service/impl/EventServiceImplTest.java +++ b/src/test/java/eventManager/service/impl/EventServiceImplTest.java @@ -29,7 +29,10 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -/** Pruebas unitarias del servicio de eventos, incluyendo consulta, listado, creacion y actualizacion de eventos. */ +/** + * Unit tests for the event service, including retrieval, listing, creation, and + * updates. + */ @ExtendWith(MockitoExtension.class) @DisplayName("EventServiceImpl Tests") class EventServiceImplTest { @@ -77,10 +80,10 @@ void setUp() { } /** - * Verifica que se obtiene correctamente un evento existente a partir de su codigo. + * Verifies that an existing event is retrieved correctly by its code. */ @Test - @DisplayName("getEvent - Exitoso") + @DisplayName("getEvent - Success") void testGetEvent_Success() { when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); when(eventMapper.convertEventToEventDTO(testEvent)).thenReturn(testEventDTO); @@ -92,10 +95,10 @@ void testGetEvent_Success() { } /** - * Verifica que buscar un evento con un codigo inexistente lanza NOT_FOUND. + * Verifies that looking up an event with a non-existent code throws NOT_FOUND. */ @Test - @DisplayName("getEvent - No encontrado") + @DisplayName("getEvent - Not found") void testGetEvent_NotFound() { when(eventRepository.findByEventCode("NOTFND")).thenReturn(Optional.empty()); @@ -105,10 +108,10 @@ void testGetEvent_NotFound() { } /** - * Verifica que se recupera correctamente la entidad Event a partir de su codigo de evento. + * Verifies that the Event entity is retrieved correctly by its event code. */ @Test - @DisplayName("getEventByEventCode - Exitoso") + @DisplayName("getEventByEventCode - Success") void testGetEventByCode_Success() { when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); @@ -119,10 +122,11 @@ void testGetEventByCode_Success() { } /** - * Verifica que buscar una entidad Event con un codigo inexistente lanza NOT_FOUND. + * Verifies that looking up an Event entity with a non-existent code throws + * NOT_FOUND. */ @Test - @DisplayName("getEventByEventCode - No encontrado") + @DisplayName("getEventByEventCode - Not found") void testGetEventByCode_NotFound() { when(eventRepository.findByEventCode("NOTFND")).thenReturn(Optional.empty()); @@ -131,10 +135,10 @@ void testGetEventByCode_NotFound() { } /** - * Verifica que el listado de eventos devuelve resultados paginados correctamente. + * Verifies that event listing returns paginated results correctly. */ @Test - @DisplayName("getEvents - Con resultados") + @DisplayName("getEvents - With results") void testGetEvents_WithResults() { Map ticketMap = new HashMap<>(); ticketMap.put(1, 10); @@ -146,7 +150,8 @@ void testGetEvents_WithResults() { Page eventPage = new PageImpl<>(List.of(testEvent)); Specification specification = (root, query, builder) -> builder.conjunction(); - when(eventRepository.findAll(org.mockito.ArgumentMatchers.>any(), any(Pageable.class))).thenReturn(eventPage); + when(eventRepository.findAll(org.mockito.ArgumentMatchers.>any(), any(Pageable.class))) + .thenReturn(eventPage); when(eventSpecificationBuilder.build(anyList())).thenReturn(specification); ResultPaginationDTO result = eventService.getEvents(1, 10, "eventName", "ASC", null, 1, "HOST"); @@ -156,10 +161,10 @@ void testGetEvents_WithResults() { } /** - * Verifica que si el usuario no tiene tickets se devuelve una lista vacia de eventos. + * Verifies that if the user has no tickets, an empty event list is returned. */ @Test - @DisplayName("getEvents - Sin tickets, retorna lista vacia") + @DisplayName("getEvents - No tickets, returns empty list") void testGetEvents_EmptyTickets() { when(ticketService.getTicketsByUserAndRole(1, "HOST")).thenReturn(new HashMap<>()); @@ -171,23 +176,26 @@ void testGetEvents_EmptyTickets() { } /** - * Verifica que un error inesperado al listar eventos produce INTERNAL_SERVER_ERROR. + * Verifies that an unexpected error while listing events results in + * INTERNAL_SERVER_ERROR. */ @Test - @DisplayName("getEvents - Error inesperado lanza INTERNAL_SERVER_ERROR") + @DisplayName("getEvents - Unexpected error throws INTERNAL_SERVER_ERROR") void testGetEvents_InternalError() { when(ticketService.getTicketsByUserAndRole(1, "HOST")).thenThrow(new RuntimeException("DB error")); - CustomException ex = assertThrows(CustomException.class, () -> eventService.getEvents(1, 10, "eventName", "ASC", null, 1, "HOST")); + CustomException ex = assertThrows(CustomException.class, + () -> eventService.getEvents(1, 10, "eventName", "ASC", null, 1, "HOST")); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, ex.getStatus()); assertEquals(Constantes.MESSAGE_INTERNAL_SERVER_ERROR, ex.getMessage()); } /** - * Verifica que la creacion de un evento se realiza correctamente y se asigna el ticket de HOST al creador. + * Verifies that event creation completes correctly and assigns the HOST ticket + * to the creator. */ @Test - @DisplayName("createEvent - Creacion exitosa") + @DisplayName("createEvent - Successful creation") void testCreateEvent_Success() { UserDTO userDTO = new UserDTO(); userDTO.setUserId(1); @@ -210,22 +218,25 @@ void testCreateEvent_Success() { } /** - * Verifica que crear un evento con un usuario inexistente lanza BAD_REQUEST. + * Verifies that creating an event with a non-existent user throws BAD_REQUEST. */ @Test - @DisplayName("createEvent - Usuario no encontrado") + @DisplayName("createEvent - User not found") void testCreateEvent_UserNotFound() { - when(userService.getUserInformation(999)).thenThrow(new CustomException(HttpStatus.BAD_REQUEST, Constantes.MESSAGE_USER_NOT_REGISTERED)); + when(userService.getUserInformation(999)) + .thenThrow(new CustomException(HttpStatus.BAD_REQUEST, Constantes.MESSAGE_USER_NOT_REGISTERED)); - CustomException ex = assertThrows(CustomException.class, () -> eventService.createEvent(999, new CreateUpdateEventDTO())); + CustomException ex = assertThrows(CustomException.class, + () -> eventService.createEvent(999, new CreateUpdateEventDTO())); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); } /** - * Verifica que un error inesperado durante la creacion de un evento produce INTERNAL_SERVER_ERROR. + * Verifies that an unexpected error during event creation results in + * INTERNAL_SERVER_ERROR. */ @Test - @DisplayName("createEvent - Error inesperado lanza INTERNAL_SERVER_ERROR") + @DisplayName("createEvent - Unexpected error throws INTERNAL_SERVER_ERROR") void testCreateEvent_UnexpectedError() { UserDTO userDTO = new UserDTO(); userDTO.setUserId(1); @@ -243,10 +254,10 @@ void testCreateEvent_UnexpectedError() { } /** - * Verifica que la actualizacion de un evento existente se completa correctamente. + * Verifies that updating an existing event completes correctly. */ @Test - @DisplayName("updateEvent - Actualizacion exitosa") + @DisplayName("updateEvent - Successful update") void testUpdateEvent_Success() { when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); doNothing().when(accessControlUtils).validateUserIsHost(1); @@ -268,27 +279,30 @@ void testUpdateEvent_Success() { } /** - * Verifica que intentar actualizar un evento inexistente lanza NOT_FOUND. + * Verifies that attempting to update a non-existent event throws NOT_FOUND. */ @Test - @DisplayName("updateEvent - Evento no encontrado") + @DisplayName("updateEvent - Event not found") void testUpdateEvent_NotFound() { when(eventRepository.findByEventCode("NOTFND")).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> eventService.updateEvent("NOTFND", new CreateUpdateEventDTO())); + CustomException ex = assertThrows(CustomException.class, + () -> eventService.updateEvent("NOTFND", new CreateUpdateEventDTO())); assertEquals(HttpStatus.NOT_FOUND, ex.getStatus()); } /** - * Verifica que un usuario sin rol HOST no puede actualizar el evento. + * Verifies that a user without the HOST role cannot update the event. */ @Test - @DisplayName("updateEvent - No es HOST, lanza FORBIDDEN") + @DisplayName("updateEvent - Not HOST, throws FORBIDDEN") void testUpdateEvent_NotHost() { when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_USER_NOT_HOST)).when(accessControlUtils).validateUserIsHost(1); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_USER_NOT_HOST)).when(accessControlUtils) + .validateUserIsHost(1); - CustomException ex = assertThrows(CustomException.class, () -> eventService.updateEvent("ABC123", new CreateUpdateEventDTO())); + CustomException ex = assertThrows(CustomException.class, + () -> eventService.updateEvent("ABC123", new CreateUpdateEventDTO())); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } } diff --git a/src/test/java/eventManager/service/impl/GiftServiceImplTest.java b/src/test/java/eventManager/service/impl/GiftServiceImplTest.java index 9532ffd..323120e 100644 --- a/src/test/java/eventManager/service/impl/GiftServiceImplTest.java +++ b/src/test/java/eventManager/service/impl/GiftServiceImplTest.java @@ -33,7 +33,10 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -/** Pruebas unitarias del servicio de regalos, incluyendo creacion, consulta, listado, actualizacion, eliminacion y gestion de contribuciones. */ +/** + * Unit tests for the gift service, including creation, retrieval, listing, + * updates, deletion, and contribution management. + */ @ExtendWith(MockitoExtension.class) @DisplayName("GiftServiceImpl Tests") class GiftServiceImplTest { @@ -123,10 +126,11 @@ void setUp() { } /** - * Verifica que la creacion de un regalo se realiza correctamente y se persiste en base de datos. + * Verifies that gift creation completes correctly and is persisted in the + * database. */ @Test - @DisplayName("createGift - Creacion exitosa") + @DisplayName("createGift - Successful creation") void testCreateGift_Success() { GiftCreateDTO createDTO = new GiftCreateDTO(); createDTO.setName("New Gift"); @@ -154,10 +158,11 @@ void testCreateGift_Success() { } /** - * Verifica que crear un regalo con un nombre ya existente en el evento lanza BAD_REQUEST. + * Verifies that creating a gift with a name already in the event throws + * BAD_REQUEST. */ @Test - @DisplayName("createGift - Nombre duplicado") + @DisplayName("createGift - Duplicate name") void testCreateGift_DuplicateName() { GiftCreateDTO createDTO = new GiftCreateDTO(); createDTO.setName("Existing Gift"); @@ -174,10 +179,11 @@ void testCreateGift_DuplicateName() { } /** - * Verifica que crear un regalo con imagen que supera el tamaño máximo lanza BAD_REQUEST. + * Verifies that creating a gift with an image larger than the max size throws + * BAD_REQUEST. */ @Test - @DisplayName("createGift - Imagen demasiado grande") + @DisplayName("createGift - Image too large") void testCreateGift_ImageTooLarge() { GiftCreateDTO createDTO = new GiftCreateDTO(); createDTO.setName("New Gift"); @@ -209,27 +215,28 @@ void testCreateGift_ImageTooLarge() { } /** - * Verifica que un usuario no registrado en el evento no puede crear regalos. + * Verifies that a user not registered in the event cannot create gifts. */ @Test - @DisplayName("createGift - Usuario no registrado en evento") + @DisplayName("createGift - User not registered in event") void testCreateGift_UserNotRegistered() { GiftCreateDTO createDTO = new GiftCreateDTO(); createDTO.setName("Gift"); when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); when(eventService.getEventByEventCode("ABC123")).thenReturn(testEvent); - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_USER_NOT_REGISTERED_IN_EVENT)).when(accessControlUtils).validateUserRegisteredInEvent(1); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_USER_NOT_REGISTERED_IN_EVENT)) + .when(accessControlUtils).validateUserRegisteredInEvent(1); CustomException ex = assertThrows(CustomException.class, () -> giftService.createGift("ABC123", createDTO)); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } /** - * Verifica que se obtiene correctamente la informacion detallada de un regalo. + * Verifies that detailed gift information is retrieved correctly. */ @Test - @DisplayName("getGiftInformation - Exitoso") + @DisplayName("getGiftInformation - Success") void testGetGiftInfo_Success() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); @@ -242,10 +249,10 @@ void testGetGiftInfo_Success() { } /** - * Verifica que consultar un regalo inexistente lanza NOT_FOUND. + * Verifies that querying a non-existent gift throws NOT_FOUND. */ @Test - @DisplayName("getGiftInformation - Regalo no encontrado") + @DisplayName("getGiftInformation - Gift not found") void testGetGiftInfo_NotFound() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); when(giftRepository.findByGiftId(999)).thenReturn(Optional.empty()); @@ -255,16 +262,17 @@ void testGetGiftInfo_NotFound() { } /** - * Verifica que el listado de regalos devuelve resultados paginados correctamente. + * Verifies that gift listing returns paginated results correctly. */ @Test - @DisplayName("getGifts - Exitoso con resultados") + @DisplayName("getGifts - Success with results") void testGetGifts_Success() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); Page giftPage = new PageImpl<>(List.of(testGift)); Specification specification = (root, query, builder) -> builder.conjunction(); - when(giftRepository.findAll(org.mockito.ArgumentMatchers.>any(), any(Pageable.class))).thenReturn(giftPage); + when(giftRepository.findAll(org.mockito.ArgumentMatchers.>any(), any(Pageable.class))) + .thenReturn(giftPage); when(giftSpecificationBuilder.build(anyList())).thenReturn(specification); when(giftMapper.convertGiftToGiftDTO(testGift, 1)).thenReturn(testGiftDTO); @@ -275,37 +283,42 @@ void testGetGifts_Success() { } /** - * Verifica que listar regalos de un evento inexistente lanza NOT_FOUND. + * Verifies that listing gifts for a non-existent event throws NOT_FOUND. */ @Test - @DisplayName("getGifts - Evento no encontrado") + @DisplayName("getGifts - Event not found") void testGetGifts_EventNotFound() { - when(eventService.getEvent("NOTFND")).thenThrow(new CustomException(HttpStatus.NOT_FOUND, Constantes.MESSAGE_EVENT_DOES_NOT_EXIST)); + when(eventService.getEvent("NOTFND")) + .thenThrow(new CustomException(HttpStatus.NOT_FOUND, Constantes.MESSAGE_EVENT_DOES_NOT_EXIST)); - CustomException ex = assertThrows(CustomException.class, () -> giftService.getGifts("NOTFND", 1, 10, "name", "ASC", null)); + CustomException ex = assertThrows(CustomException.class, + () -> giftService.getGifts("NOTFND", 1, 10, "name", "ASC", null)); assertEquals(HttpStatus.NOT_FOUND, ex.getStatus()); } /** - * Verifica que un error inesperado al listar regalos produce INTERNAL_SERVER_ERROR. + * Verifies that an unexpected error while listing gifts results in + * INTERNAL_SERVER_ERROR. */ @Test - @DisplayName("getGifts - Error inesperado lanza INTERNAL_SERVER_ERROR") + @DisplayName("getGifts - Unexpected error throws INTERNAL_SERVER_ERROR") void testGetGifts_InternalError() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); Specification specification = (root, query, builder) -> builder.conjunction(); when(giftSpecificationBuilder.build(anyList())).thenReturn(specification); - when(giftRepository.findAll(org.mockito.ArgumentMatchers.>any(), any(Pageable.class))).thenThrow(new RuntimeException("DB error")); + when(giftRepository.findAll(org.mockito.ArgumentMatchers.>any(), any(Pageable.class))) + .thenThrow(new RuntimeException("DB error")); - CustomException ex = assertThrows(CustomException.class, () -> giftService.getGifts("ABC123", 1, 10, "name", "ASC", null)); + CustomException ex = assertThrows(CustomException.class, + () -> giftService.getGifts("ABC123", 1, 10, "name", "ASC", null)); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, ex.getStatus()); } /** - * Verifica que la actualizacion de un regalo existente se completa correctamente. + * Verifies that updating an existing gift completes correctly. */ @Test - @DisplayName("updateGift - Actualizacion exitosa") + @DisplayName("updateGift - Successful update") void testUpdateGift_Success() { GiftUpdateDTO updateDTO = new GiftUpdateDTO(); updateDTO.setName("Updated Gift"); @@ -327,10 +340,11 @@ void testUpdateGift_Success() { } /** - * Verifica que actualizar un regalo con imagen demasiado grande lanza BAD_REQUEST. + * Verifies that updating a gift with an image that is too large throws + * BAD_REQUEST. */ @Test - @DisplayName("updateGift - Imagen demasiado grande") + @DisplayName("updateGift - Image too large") void testUpdateGift_ImageTooLarge() { GiftUpdateDTO updateDTO = new GiftUpdateDTO(); updateDTO.setName("Updated Gift"); @@ -355,37 +369,41 @@ void testUpdateGift_ImageTooLarge() { } /** - * Verifica que intentar actualizar un regalo inexistente lanza NOT_FOUND. + * Verifies that attempting to update a non-existent gift throws NOT_FOUND. */ @Test - @DisplayName("updateGift - Regalo no encontrado") + @DisplayName("updateGift - Gift not found") void testUpdateGift_NotFound() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); when(giftRepository.findByGiftId(999)).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> giftService.updateGift("ABC123", 999, new GiftUpdateDTO())); + CustomException ex = assertThrows(CustomException.class, + () -> giftService.updateGift("ABC123", 999, new GiftUpdateDTO())); assertEquals(HttpStatus.NOT_FOUND, ex.getStatus()); } /** - * Verifica que un usuario sin permisos no puede actualizar el regalo. + * Verifies that a user without permissions cannot update the gift. */ @Test - @DisplayName("updateGift - No autorizado") + @DisplayName("updateGift - Not authorized") void testUpdateGift_Unauthorized() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_GIFT_UPDATE_FORBIDDEN)).when(accessControlUtils).validateHostOrGiftCreator(1, "testuser"); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_GIFT_UPDATE_FORBIDDEN)) + .when(accessControlUtils).validateHostOrGiftCreator(1, "testuser"); - CustomException ex = assertThrows(CustomException.class, () -> giftService.updateGift("ABC123", 1, new GiftUpdateDTO())); + CustomException ex = assertThrows(CustomException.class, + () -> giftService.updateGift("ABC123", 1, new GiftUpdateDTO())); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } /** - * Verifica que al actualizar un regalo se recalcula el monto recaudado a partir de las contribuciones. + * Verifies that updating a gift recalculates the collected amount from + * contributions. */ @Test - @DisplayName("updateGift - Recalcula collected desde contribuciones") + @DisplayName("updateGift - Recalculates collected from contributions") void testUpdateGift_RecalculateCollected() { GiftUpdateDTO updateDTO = new GiftUpdateDTO(); updateDTO.setName("Gift"); @@ -408,10 +426,11 @@ void testUpdateGift_RecalculateCollected() { } /** - * Verifica que la eliminacion de un regalo se completa correctamente junto con sus contribuciones. + * Verifies that deleting a gift completes correctly along with its + * contributions. */ @Test - @DisplayName("deleteGift - Eliminacion exitosa") + @DisplayName("deleteGift - Successful deletion") void testDeleteGift_Success() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); @@ -426,24 +445,26 @@ void testDeleteGift_Success() { } /** - * Verifica que un usuario sin rol HOST no puede eliminar un regalo del evento. + * Verifies that a user without the HOST role cannot delete a gift from the + * event. */ @Test - @DisplayName("deleteGift - No es HOST") + @DisplayName("deleteGift - Not HOST") void testDeleteGift_NotHost() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_GIFT_UPDATE_FORBIDDEN)).when(accessControlUtils).validateHostOrGiftCreator(1, "testuser"); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_GIFT_UPDATE_FORBIDDEN)) + .when(accessControlUtils).validateHostOrGiftCreator(1, "testuser"); CustomException ex = assertThrows(CustomException.class, () -> giftService.deleteGift("ABC123", 1)); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } /** - * Verifica que intentar eliminar un regalo inexistente lanza NOT_FOUND. + * Verifies that attempting to delete a non-existent gift throws NOT_FOUND. */ @Test - @DisplayName("deleteGift - Regalo no encontrado") + @DisplayName("deleteGift - Gift not found") void testDeleteGift_GiftNotFound() { when(eventService.getEvent("ABC123")).thenReturn(testEventDTO); when(giftRepository.findByGiftId(999)).thenReturn(Optional.empty()); @@ -453,10 +474,11 @@ void testDeleteGift_GiftNotFound() { } /** - * Verifica que se crea correctamente una nueva contribucion a un regalo y se actualiza el monto recaudado. + * Verifies that a new contribution to a gift is created correctly and the + * collected amount is updated. */ @Test - @DisplayName("createUpdateGiftContribution - Nueva contribucion") + @DisplayName("createUpdateGiftContribution - New contribution") void testContribution_New() { UserGiftDTO userGiftDTO = new UserGiftDTO(); userGiftDTO.setUserId(1); @@ -467,7 +489,8 @@ void testContribution_New() { when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); when(userService.getUser(1)).thenReturn(testUser); when(giftContributionRepository.findByGiftId_GiftIdAndUserId_UserId(1, 1)).thenReturn(Optional.empty()); - when(giftContributionRepository.save(any(GiftContribution.class))).thenReturn(GiftContribution.builder().build()); + when(giftContributionRepository.save(any(GiftContribution.class))) + .thenReturn(GiftContribution.builder().build()); when(giftRepository.save(testGift)).thenReturn(testGift); when(giftMapper.convertGiftToGiftExtendedDTO(testGift, 1)).thenReturn(testGiftExtendedDTO); when(giftContributionRepository.findByGiftId_GiftId(1)).thenReturn(Collections.emptyList()); @@ -479,10 +502,11 @@ void testContribution_New() { } /** - * Verifica que al actualizar una contribucion existente se recalcula correctamente el monto recaudado. + * Verifies that updating an existing contribution recalculates the collected + * amount correctly. */ @Test - @DisplayName("createUpdateGiftContribution - Actualizar contribucion existente") + @DisplayName("createUpdateGiftContribution - Update existing contribution") void testContribution_UpdateExisting() { UserGiftDTO userGiftDTO = new UserGiftDTO(); userGiftDTO.setUserId(1); @@ -500,7 +524,8 @@ void testContribution_UpdateExisting() { doNothing().when(accessControlUtils).validateUserRegisteredInEvent(1); when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); when(userService.getUser(1)).thenReturn(testUser); - when(giftContributionRepository.findByGiftId_GiftIdAndUserId_UserId(1, 1)).thenReturn(Optional.of(existingContrib)); + when(giftContributionRepository.findByGiftId_GiftIdAndUserId_UserId(1, 1)) + .thenReturn(Optional.of(existingContrib)); when(giftContributionRepository.save(existingContrib)).thenReturn(existingContrib); when(giftRepository.save(testGift)).thenReturn(testGift); when(giftMapper.convertGiftToGiftExtendedDTO(testGift, 1)).thenReturn(testGiftExtendedDTO); @@ -514,10 +539,10 @@ void testContribution_UpdateExisting() { } /** - * Verifica que una contribucion con monto negativo lanza BAD_REQUEST. + * Verifies that a contribution with a negative amount throws BAD_REQUEST. */ @Test - @DisplayName("createUpdateGiftContribution - Monto negativo") + @DisplayName("createUpdateGiftContribution - Negative amount") void testContribution_NegativeAmount() { UserGiftDTO userGiftDTO = new UserGiftDTO(); userGiftDTO.setUserId(1); @@ -527,16 +552,17 @@ void testContribution_NegativeAmount() { doNothing().when(accessControlUtils).validateUserRegisteredInEvent(1); when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); - CustomException ex = assertThrows(CustomException.class, () -> giftService.createUpdateGiftContribution("ABC123", 1, userGiftDTO)); + CustomException ex = assertThrows(CustomException.class, + () -> giftService.createUpdateGiftContribution("ABC123", 1, userGiftDTO)); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); assertEquals(Constantes.MESSAGE_GIFT_CONTRIBUTION_POSITIVE, ex.getMessage()); } /** - * Verifica que una contribucion con monto cero lanza BAD_REQUEST. + * Verifies that a contribution with a zero amount throws BAD_REQUEST. */ @Test - @DisplayName("createUpdateGiftContribution - Monto cero") + @DisplayName("createUpdateGiftContribution - Zero amount") void testContribution_ZeroAmount() { UserGiftDTO userGiftDTO = new UserGiftDTO(); userGiftDTO.setUserId(1); @@ -546,15 +572,16 @@ void testContribution_ZeroAmount() { doNothing().when(accessControlUtils).validateUserRegisteredInEvent(1); when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); - CustomException ex = assertThrows(CustomException.class, () -> giftService.createUpdateGiftContribution("ABC123", 1, userGiftDTO)); + CustomException ex = assertThrows(CustomException.class, + () -> giftService.createUpdateGiftContribution("ABC123", 1, userGiftDTO)); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); } /** - * Verifica que al alcanzar el precio total del regalo se marca como completamente pagado. + * Verifies that reaching the gift's total price marks it as paid in full. */ @Test - @DisplayName("createUpdateGiftContribution - paidInFull se activa al completar") + @DisplayName("createUpdateGiftContribution - PaidInFull set when completed") void testContribution_PaidInFull() { UserGiftDTO userGiftDTO = new UserGiftDTO(); userGiftDTO.setUserId(1); @@ -568,7 +595,8 @@ void testContribution_PaidInFull() { when(giftRepository.findByGiftId(1)).thenReturn(Optional.of(testGift)); when(userService.getUser(1)).thenReturn(testUser); when(giftContributionRepository.findByGiftId_GiftIdAndUserId_UserId(1, 1)).thenReturn(Optional.empty()); - when(giftContributionRepository.save(any(GiftContribution.class))).thenReturn(GiftContribution.builder().build()); + when(giftContributionRepository.save(any(GiftContribution.class))) + .thenReturn(GiftContribution.builder().build()); when(giftRepository.save(testGift)).thenReturn(testGift); when(giftMapper.convertGiftToGiftExtendedDTO(testGift, 1)).thenReturn(testGiftExtendedDTO); when(giftContributionRepository.findByGiftId_GiftId(1)).thenReturn(Collections.emptyList()); diff --git a/src/test/java/eventManager/service/impl/TicketServiceImplTest.java b/src/test/java/eventManager/service/impl/TicketServiceImplTest.java index f976094..99183f0 100644 --- a/src/test/java/eventManager/service/impl/TicketServiceImplTest.java +++ b/src/test/java/eventManager/service/impl/TicketServiceImplTest.java @@ -33,7 +33,10 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -/** Pruebas unitarias del servicio de tickets, incluyendo consulta de informacion de evento, listado de tickets, inscripcion de usuarios y actualizacion de tickets. */ +/** + * Unit tests for the ticket service, including event info retrieval, ticket + * listing, user enrollment, and ticket updates. + */ @ExtendWith(MockitoExtension.class) @DisplayName("TicketServiceImpl Tests") class TicketServiceImplTest { @@ -113,16 +116,17 @@ void setUp() { } /** - * Verifica que se obtiene correctamente la informacion de un evento a partir del ticket. + * Verifies that event information is retrieved correctly from a ticket. */ @Test - @DisplayName("getEventInformation - Exitoso") + @DisplayName("getEventInformation - Success") void testGetEventInfo_Success() { when(ticketRepository.findById(10)).thenReturn(Optional.of(testTicket)); when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); when(ticketMapper.convertTicketToTicketDTO(testTicket)).thenReturn(testTicketDTO); when(eventMapper.convertEventToEventDTO(testEvent)).thenReturn(testEventDTO); - when(ticketMapper.convertTicketDTOAndEventDTOToEventTicketDTO(testTicketDTO, testEventDTO)).thenReturn(testEventTicketDTO); + when(ticketMapper.convertTicketDTOAndEventDTOToEventTicketDTO(testTicketDTO, testEventDTO)) + .thenReturn(testEventTicketDTO); EventTicketDTO result = ticketService.getEventInformation("ABC123", 10, 1); @@ -130,55 +134,62 @@ void testGetEventInfo_Success() { } /** - * Verifica que consultar informacion con un ticket inexistente lanza NOT_FOUND. + * Verifies that querying information with a non-existent ticket throws + * NOT_FOUND. */ @Test - @DisplayName("getEventInformation - Ticket no encontrado") + @DisplayName("getEventInformation - Ticket not found") void testGetEventInfo_TicketNotFound() { when(ticketRepository.findById(999)).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> ticketService.getEventInformation("ABC123", 999, 1)); + CustomException ex = assertThrows(CustomException.class, + () -> ticketService.getEventInformation("ABC123", 999, 1)); assertEquals(HttpStatus.NOT_FOUND, ex.getStatus()); } /** - * Verifica que un usuario no registrado en el evento no puede consultar su informacion. + * Verifies that a user not registered in the event cannot access its + * information. */ @Test - @DisplayName("getEventInformation - Usuario no registrado en evento") + @DisplayName("getEventInformation - User not registered in event") void testGetEventInfo_UserNotRegistered() { when(ticketRepository.findById(10)).thenReturn(Optional.of(testTicket)); - CustomException ex = assertThrows(CustomException.class, () -> ticketService.getEventInformation("ABC123", 10, 999)); + CustomException ex = assertThrows(CustomException.class, + () -> ticketService.getEventInformation("ABC123", 10, 999)); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); assertEquals(Constantes.MESSAGE_USER_NOT_REGISTERED_IN_EVENT, ex.getMessage()); } /** - * Verifica que consultar informacion de un evento inexistente lanza NOT_FOUND. + * Verifies that querying information for a non-existent event throws NOT_FOUND. */ @Test - @DisplayName("getEventInformation - Evento no encontrado") + @DisplayName("getEventInformation - Event not found") void testGetEventInfo_EventNotFound() { when(ticketRepository.findById(10)).thenReturn(Optional.of(testTicket)); when(eventRepository.findByEventCode("NOTFND")).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> ticketService.getEventInformation("NOTFND", 10, 1)); + CustomException ex = assertThrows(CustomException.class, + () -> ticketService.getEventInformation("NOTFND", 10, 1)); assertEquals(HttpStatus.NOT_FOUND, ex.getStatus()); } /** - * Verifica que el listado de tickets de un evento se obtiene correctamente con paginacion. + * Verifies that ticket listing for an event is retrieved correctly with + * pagination. */ @Test - @DisplayName("getEventTickets - Exitoso") + @DisplayName("getEventTickets - Success") void testGetEventTickets_Success() { when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); doNothing().when(accessControlUtils).validateUserIsHost(1); Page ticketPage = new PageImpl<>(List.of(testTicket)); Specification specification = (root, query, builder) -> builder.conjunction(); - when(ticketRepository.findAll(org.mockito.ArgumentMatchers.>any(), any(Pageable.class))).thenReturn(ticketPage); + when(ticketRepository.findAll(org.mockito.ArgumentMatchers.>any(), any(Pageable.class))) + .thenReturn(ticketPage); when(ticketSpecificationBuilder.build(anyList())).thenReturn(specification); when(ticketMapper.convertTicketToTicketDTO(testTicket)).thenReturn(testTicketDTO); @@ -189,35 +200,39 @@ void testGetEventTickets_Success() { } /** - * Verifica que listar tickets de un evento inexistente lanza NOT_FOUND. + * Verifies that listing tickets for a non-existent event throws NOT_FOUND. */ @Test - @DisplayName("getEventTickets - Evento no encontrado") + @DisplayName("getEventTickets - Event not found") void testGetEventTickets_EventNotFound() { when(eventRepository.findByEventCode("NOTFND")).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> ticketService.getEventTickets("NOTFND", 1, 10, "ticketId", "ASC", null)); + CustomException ex = assertThrows(CustomException.class, + () -> ticketService.getEventTickets("NOTFND", 1, 10, "ticketId", "ASC", null)); assertEquals(HttpStatus.NOT_FOUND, ex.getStatus()); } /** - * Verifica que un usuario sin rol HOST no puede listar los tickets del evento. + * Verifies that a user without the HOST role cannot list tickets for the event. */ @Test - @DisplayName("getEventTickets - No es HOST") + @DisplayName("getEventTickets - Not HOST") void testGetEventTickets_NotHost() { when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_USER_NOT_HOST)).when(accessControlUtils).validateUserIsHost(1); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_USER_NOT_HOST)).when(accessControlUtils) + .validateUserIsHost(1); - CustomException ex = assertThrows(CustomException.class, () -> ticketService.getEventTickets("ABC123", 1, 10, "ticketId", "ASC", null)); + CustomException ex = assertThrows(CustomException.class, + () -> ticketService.getEventTickets("ABC123", 1, 10, "ticketId", "ASC", null)); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } /** - * Verifica que la inscripcion de un usuario como GUEST se realiza sin confirmaciones automaticas. + * Verifies that enrolling a user as GUEST happens without automatic + * confirmations. */ @Test - @DisplayName("enrollUserInEvent - Inscripcion exitosa como GUEST") + @DisplayName("enrollUserInEvent - Successful enrollment as GUEST") void testEnroll_SuccessAsGuest() { EnrollUserDTO enrollDTO = EnrollUserDTO.builder() .eventCode("ABC123") @@ -236,14 +251,15 @@ void testEnroll_SuccessAsGuest() { TicketDTO result = ticketService.enrollUserInEvent(enrollDTO); assertNotNull(result); - verify(ticketRepository).save(argThat(t -> t.getInvitationConfirmation() == null && t.getAssistConfirmation() == null)); + verify(ticketRepository) + .save(argThat(t -> t.getInvitationConfirmation() == null && t.getAssistConfirmation() == null)); } /** - * Verifica que la inscripcion como HOST auto-confirma tanto la invitacion como la asistencia. + * Verifies that enrolling as HOST auto-confirms both invitation and attendance. */ @Test - @DisplayName("enrollUserInEvent - Inscripcion como HOST auto-confirma") + @DisplayName("enrollUserInEvent - Enrollment as HOST auto-confirms") void testEnroll_SuccessAsHost() { EnrollUserDTO enrollDTO = EnrollUserDTO.builder() .eventCode("ABC123") @@ -261,14 +277,15 @@ void testEnroll_SuccessAsHost() { ticketService.enrollUserInEvent(enrollDTO); - verify(ticketRepository).save(argThat(t -> Boolean.TRUE.equals(t.getInvitationConfirmation()) && Boolean.TRUE.equals(t.getAssistConfirmation()))); + verify(ticketRepository).save(argThat(t -> Boolean.TRUE.equals(t.getInvitationConfirmation()) + && Boolean.TRUE.equals(t.getAssistConfirmation()))); } /** - * Verifica que inscribirse en un evento inexistente lanza NOT_FOUND. + * Verifies that enrolling in a non-existent event throws NOT_FOUND. */ @Test - @DisplayName("enrollUserInEvent - Evento no encontrado") + @DisplayName("enrollUserInEvent - Event not found") void testEnroll_EventNotFound() { EnrollUserDTO enrollDTO = EnrollUserDTO.builder().eventCode("NOTFND").userId(1).role("GUEST").build(); when(eventRepository.findByEventCode("NOTFND")).thenReturn(Optional.empty()); @@ -278,10 +295,10 @@ void testEnroll_EventNotFound() { } /** - * Verifica que un usuario ya registrado en el evento no puede inscribirse de nuevo. + * Verifies that a user already registered in the event cannot enroll again. */ @Test - @DisplayName("enrollUserInEvent - Ya registrado") + @DisplayName("enrollUserInEvent - Already registered") void testEnroll_AlreadyRegistered() { EnrollUserDTO enrollDTO = EnrollUserDTO.builder().eventCode("ABC123").userId(1).role("GUEST").build(); when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); @@ -294,10 +311,10 @@ void testEnroll_AlreadyRegistered() { } /** - * Verifica que si no se indica el numero de invitados se establece por defecto a 1. + * Verifies that if the guest number is not specified, it defaults to 1. */ @Test - @DisplayName("enrollUserInEvent - guestNumber nulo se establece a 1") + @DisplayName("enrollUserInEvent - Null guestNumber defaults to 1") void testEnroll_NullGuestNumber() { EnrollUserDTO enrollDTO = EnrollUserDTO.builder() .eventCode("ABC123").userId(1).role("GUEST").guestNumber(null).build(); @@ -314,10 +331,10 @@ void testEnroll_NullGuestNumber() { } /** - * Verifica que la actualizacion de un ticket existente se realiza correctamente. + * Verifies that updating an existing ticket completes correctly. */ @Test - @DisplayName("updateTicket - Actualizacion exitosa") + @DisplayName("updateTicket - Successful update") void testUpdateTicket_Success() { UpdateTicketDTO updateDTO = new UpdateTicketDTO(); updateDTO.setNotes("Updated notes"); @@ -328,7 +345,8 @@ void testUpdateTicket_Success() { when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); when(eventMapper.convertEventToEventDTO(testEvent)).thenReturn(testEventDTO); when(ticketMapper.convertTicketToTicketDTO(testTicket)).thenReturn(testTicketDTO); - when(ticketMapper.convertTicketDTOAndEventDTOToEventTicketDTO(testTicketDTO, testEventDTO)).thenReturn(testEventTicketDTO); + when(ticketMapper.convertTicketDTOAndEventDTOToEventTicketDTO(testTicketDTO, testEventDTO)) + .thenReturn(testEventTicketDTO); EventTicketDTO result = ticketService.updateTicket("ABC123", 10, updateDTO); @@ -337,35 +355,39 @@ void testUpdateTicket_Success() { } /** - * Verifica que un usuario sin autorizacion no puede actualizar un ticket. + * Verifies that a user without authorization cannot update a ticket. */ @Test - @DisplayName("updateTicket - No autorizado") + @DisplayName("updateTicket - Not authorized") void testUpdateTicket_NotAuthorized() { - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_FORBIDDEN_ACCESS)).when(accessControlUtils).validateTicketAccess(10); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_FORBIDDEN_ACCESS)).when(accessControlUtils) + .validateTicketAccess(10); - CustomException ex = assertThrows(CustomException.class, () -> ticketService.updateTicket("ABC123", 10, new UpdateTicketDTO())); + CustomException ex = assertThrows(CustomException.class, + () -> ticketService.updateTicket("ABC123", 10, new UpdateTicketDTO())); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } /** - * Verifica que intentar actualizar un ticket inexistente lanza NOT_FOUND. + * Verifies that attempting to update a non-existent ticket throws NOT_FOUND. */ @Test - @DisplayName("updateTicket - Ticket no encontrado") + @DisplayName("updateTicket - Ticket not found") void testUpdateTicket_TicketNotFound() { doNothing().when(accessControlUtils).validateTicketAccess(999); when(ticketRepository.findById(999)).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> ticketService.updateTicket("ABC123", 999, new UpdateTicketDTO())); + CustomException ex = assertThrows(CustomException.class, + () -> ticketService.updateTicket("ABC123", 999, new UpdateTicketDTO())); assertEquals(HttpStatus.NOT_FOUND, ex.getStatus()); } /** - * Verifica que la actualizacion parcial solo modifica los campos proporcionados, sin alterar los demas. + * Verifies that partial updates only modify provided fields without changing + * others. */ @Test - @DisplayName("updateTicket - Actualizacion parcial (solo campos no null)") + @DisplayName("updateTicket - Partial update (only non-null fields)") void testUpdateTicket_PartialUpdate() { UpdateTicketDTO updateDTO = new UpdateTicketDTO(); updateDTO.setGuestNumber(5); @@ -376,7 +398,8 @@ void testUpdateTicket_PartialUpdate() { when(eventRepository.findByEventCode("ABC123")).thenReturn(Optional.of(testEvent)); when(eventMapper.convertEventToEventDTO(testEvent)).thenReturn(testEventDTO); when(ticketMapper.convertTicketToTicketDTO(testTicket)).thenReturn(testTicketDTO); - when(ticketMapper.convertTicketDTOAndEventDTOToEventTicketDTO(testTicketDTO, testEventDTO)).thenReturn(testEventTicketDTO); + when(ticketMapper.convertTicketDTOAndEventDTOToEventTicketDTO(testTicketDTO, testEventDTO)) + .thenReturn(testEventTicketDTO); ticketService.updateTicket("ABC123", 10, updateDTO); @@ -386,10 +409,11 @@ void testUpdateTicket_PartialUpdate() { } /** - * Verifica que se obtiene correctamente el mapa de eventId a ticketId para un usuario y rol dados. + * Verifies that the eventId-to-ticketId map is retrieved correctly for a given + * user and role. */ @Test - @DisplayName("getTicketsByUserAndRole - Retorna mapa eventId->ticketId") + @DisplayName("getTicketsByUserAndRole - Returns eventId->ticketId map") void testGetTicketsByUserAndRole_Success() { when(userService.getUser(1)).thenReturn(testUser); when(ticketRepository.findByUserId_UserIdAndRole(1, "HOST")).thenReturn(List.of(testTicket)); @@ -402,10 +426,11 @@ void testGetTicketsByUserAndRole_Success() { } /** - * Verifica que si no existen tickets para el usuario y rol se devuelve un mapa vacio. + * Verifies that when no tickets exist for the user and role, an empty map is + * returned. */ @Test - @DisplayName("getTicketsByUserAndRole - Sin tickets, retorna mapa vacio") + @DisplayName("getTicketsByUserAndRole - No tickets, returns empty map") void testGetTicketsByUserAndRole_NoTickets() { when(userService.getUser(1)).thenReturn(testUser); when(ticketRepository.findByUserId_UserIdAndRole(1, "HOST")).thenReturn(Collections.emptyList()); @@ -417,10 +442,10 @@ void testGetTicketsByUserAndRole_NoTickets() { } /** - * Verifica que se obtiene correctamente el ticket de un usuario en un evento especifico. + * Verifies that a user's ticket for a specific event is retrieved correctly. */ @Test - @DisplayName("getTicketByEventAndUser - Exitoso") + @DisplayName("getTicketByEventAndUser - Success") void testGetTicketByEventAndUser_Success() { when(userService.getUser(1)).thenReturn(testUser); when(ticketRepository.findByEventId_EventIdAndUserId_UserId(1, 1)).thenReturn(Optional.of(testTicket)); @@ -432,10 +457,11 @@ void testGetTicketByEventAndUser_Success() { } /** - * Verifica que buscar un ticket inexistente para un evento y usuario lanza NOT_FOUND. + * Verifies that looking up a non-existent ticket for an event and user throws + * NOT_FOUND. */ @Test - @DisplayName("getTicketByEventAndUser - No encontrado") + @DisplayName("getTicketByEventAndUser - Not found") void testGetTicketByEventAndUser_NotFound() { when(userService.getUser(1)).thenReturn(testUser); when(ticketRepository.findByEventId_EventIdAndUserId_UserId(99, 1)).thenReturn(Optional.empty()); diff --git a/src/test/java/eventManager/service/impl/UserServiceImplTest.java b/src/test/java/eventManager/service/impl/UserServiceImplTest.java index c20603c..608a486 100644 --- a/src/test/java/eventManager/service/impl/UserServiceImplTest.java +++ b/src/test/java/eventManager/service/impl/UserServiceImplTest.java @@ -25,7 +25,8 @@ import static org.mockito.Mockito.*; /** - * Pruebas unitarias del servicio de usuarios, incluyendo consulta, actualizacion, eliminacion y cambio de contrasena. + * Unit tests for the user service, including retrieval, updates, deletion, and + * password changes. */ @ExtendWith(MockitoExtension.class) @DisplayName("UserServiceImpl Tests") @@ -72,10 +73,10 @@ void setUp() { } /** - * Verifica que se obtiene correctamente la informacion de un usuario buscando por su nombre de usuario. + * Verifies that user information is retrieved correctly by username. */ @Test - @DisplayName("getUserInformationByUsername - Exitoso") + @DisplayName("getUserInformationByUsername - Success") void testGetByUsername_Success() { when(userRepository.findByUsername("carlos.martinez")).thenReturn(Optional.of(testUser)); when(userMapper.convertUserToUserDTO(testUser)).thenReturn(testUserDTO); @@ -88,22 +89,23 @@ void testGetByUsername_Success() { } /** - * Verifica que buscar un nombre de usuario inexistente lanza una excepcion BAD_REQUEST. + * Verifies that looking up a non-existent username throws BAD_REQUEST. */ @Test - @DisplayName("getUserInformationByUsername - No encontrado") + @DisplayName("getUserInformationByUsername - Not found") void testGetByUsername_NotFound() { when(userRepository.findByUsername("nonexistent")).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> userService.getUserInformationByUsername("nonexistent")); + CustomException ex = assertThrows(CustomException.class, + () -> userService.getUserInformationByUsername("nonexistent")); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); } /** - * Verifica que se obtiene correctamente la informacion de un usuario por su identificador. + * Verifies that user information is retrieved correctly by ID. */ @Test - @DisplayName("getUserInformation - Exitoso") + @DisplayName("getUserInformation - Success") void testGetUserInfo_Success() { doNothing().when(accessControlUtils).validateUserOwnership(1); when(userRepository.findById(1)).thenReturn(Optional.of(testUser)); @@ -116,22 +118,23 @@ void testGetUserInfo_Success() { } /** - * Verifica que un usuario no propietario no puede consultar la informacion de otro usuario. + * Verifies that a non-owner user cannot access another user's information. */ @Test - @DisplayName("getUserInformation - No propietario, lanza FORBIDDEN") + @DisplayName("getUserInformation - Not owner, throws FORBIDDEN") void testGetUserInfo_NotOwner() { - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_FORBIDDEN_ACCESS)).when(accessControlUtils).validateUserOwnership(2); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_FORBIDDEN_ACCESS)).when(accessControlUtils) + .validateUserOwnership(2); CustomException ex = assertThrows(CustomException.class, () -> userService.getUserInformation(2)); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } /** - * Verifica que consultar un usuario inexistente por identificador lanza BAD_REQUEST. + * Verifies that looking up a non-existent user by ID throws BAD_REQUEST. */ @Test - @DisplayName("getUserInformation - No encontrado") + @DisplayName("getUserInformation - Not found") void testGetUserInfo_NotFound() { doNothing().when(accessControlUtils).validateUserOwnership(999); when(userRepository.findById(999)).thenReturn(Optional.empty()); @@ -141,10 +144,10 @@ void testGetUserInfo_NotFound() { } /** - * Verifica que se recupera correctamente la entidad User a partir de su identificador. + * Verifies that the User entity is retrieved correctly by ID. */ @Test - @DisplayName("getUser - Exitoso, retorna entidad User") + @DisplayName("getUser - Success, returns User entity") void testGetUser_Success() { when(userRepository.findById(1)).thenReturn(Optional.of(testUser)); @@ -155,10 +158,10 @@ void testGetUser_Success() { } /** - * Verifica que buscar un usuario inexistente por identificador lanza BAD_REQUEST. + * Verifies that looking up a non-existent user by ID throws BAD_REQUEST. */ @Test - @DisplayName("getUser - No encontrado") + @DisplayName("getUser - Not found") void testGetUser_NotFound() { when(userRepository.findById(999)).thenReturn(Optional.empty()); @@ -167,10 +170,10 @@ void testGetUser_NotFound() { } /** - * Verifica que la actualizacion de los datos de un usuario se realiza correctamente. + * Verifies that updating a user's data completes correctly. */ @Test - @DisplayName("updateUser - Actualizacion exitosa") + @DisplayName("updateUser - Successful update") void testUpdateUser_Success() { UserUpdateDTO updateDTO = UserUpdateDTO.builder() .firstName("Updated") @@ -192,35 +195,38 @@ void testUpdateUser_Success() { } /** - * Verifica que un usuario no propietario no puede actualizar los datos de otro usuario. + * Verifies that a non-owner user cannot update another user's data. */ @Test - @DisplayName("updateUser - No propietario") + @DisplayName("updateUser - Not owner") void testUpdateUser_NotOwner() { - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_FORBIDDEN_ACCESS)).when(accessControlUtils).validateUserOwnership(2); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_FORBIDDEN_ACCESS)).when(accessControlUtils) + .validateUserOwnership(2); - CustomException ex = assertThrows(CustomException.class, () -> userService.updateUser(2, UserUpdateDTO.builder().build())); + CustomException ex = assertThrows(CustomException.class, + () -> userService.updateUser(2, UserUpdateDTO.builder().build())); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } /** - * Verifica que intentar actualizar un usuario inexistente lanza BAD_REQUEST. + * Verifies that attempting to update a non-existent user throws BAD_REQUEST. */ @Test - @DisplayName("updateUser - Usuario no encontrado") + @DisplayName("updateUser - User not found") void testUpdateUser_NotFound() { doNothing().when(accessControlUtils).validateUserOwnership(999); when(userRepository.findById(999)).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> userService.updateUser(999, UserUpdateDTO.builder().build())); + CustomException ex = assertThrows(CustomException.class, + () -> userService.updateUser(999, UserUpdateDTO.builder().build())); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); } /** - * Verifica que la eliminacion de un usuario existente se completa correctamente. + * Verifies that deleting an existing user completes correctly. */ @Test - @DisplayName("deleteUser - Eliminacion exitosa") + @DisplayName("deleteUser - Successful deletion") void testDeleteUser_Success() { when(userRepository.findById(1)).thenReturn(Optional.of(testUser)); when(userMapper.convertUserToUserDTO(testUser)).thenReturn(testUserDTO); @@ -232,10 +238,10 @@ void testDeleteUser_Success() { } /** - * Verifica que intentar eliminar un usuario inexistente lanza BAD_REQUEST. + * Verifies that attempting to delete a non-existent user throws BAD_REQUEST. */ @Test - @DisplayName("deleteUser - No encontrado") + @DisplayName("deleteUser - Not found") void testDeleteUser_NotFound() { when(userRepository.findById(999)).thenReturn(Optional.empty()); @@ -244,10 +250,10 @@ void testDeleteUser_NotFound() { } /** - * Verifica que el cambio de contrasena se realiza correctamente cuando los datos son validos. + * Verifies that password change completes correctly when data is valid. */ @Test - @DisplayName("updateUserPassword - Cambio exitoso") + @DisplayName("updateUserPassword - Successful change") void testUpdatePassword_Success() { UserPasswordDTO passwordDTO = UserPasswordDTO.builder() .password("oldPassword") @@ -270,22 +276,24 @@ void testUpdatePassword_Success() { } /** - * Verifica que un usuario no propietario no puede cambiar la contrasena de otro usuario. + * Verifies that a non-owner user cannot change another user's password. */ @Test - @DisplayName("updateUserPassword - No propietario") + @DisplayName("updateUserPassword - Not owner") void testUpdatePassword_NotOwner() { - doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_FORBIDDEN_ACCESS)).when(accessControlUtils).validateUserOwnership(2); + doThrow(new CustomException(HttpStatus.FORBIDDEN, Constantes.MESSAGE_FORBIDDEN_ACCESS)).when(accessControlUtils) + .validateUserOwnership(2); - CustomException ex = assertThrows(CustomException.class, () -> userService.updateUserPassword(2, UserPasswordDTO.builder().build())); + CustomException ex = assertThrows(CustomException.class, + () -> userService.updateUserPassword(2, UserPasswordDTO.builder().build())); assertEquals(HttpStatus.FORBIDDEN, ex.getStatus()); } /** - * Verifica que proporcionar la contrasena actual incorrecta lanza BAD_REQUEST. + * Verifies that providing an incorrect current password throws BAD_REQUEST. */ @Test - @DisplayName("updateUserPassword - Password actual incorrecta") + @DisplayName("updateUserPassword - Incorrect current password") void testUpdatePassword_WrongCurrentPassword() { UserPasswordDTO passwordDTO = UserPasswordDTO.builder() .password("wrongOldPassword") @@ -303,10 +311,11 @@ void testUpdatePassword_WrongCurrentPassword() { } /** - * Verifica que si la nueva contrasena y su confirmacion no coinciden se lanza BAD_REQUEST. + * Verifies that BAD_REQUEST is thrown when the new password and confirmation do + * not match. */ @Test - @DisplayName("updateUserPassword - Nuevas passwords no coinciden") + @DisplayName("updateUserPassword - New passwords do not match") void testUpdatePassword_PasswordsMismatch() { UserPasswordDTO passwordDTO = UserPasswordDTO.builder() .password("oldPassword") @@ -324,15 +333,17 @@ void testUpdatePassword_PasswordsMismatch() { } /** - * Verifica que intentar cambiar la contrasena de un usuario inexistente lanza BAD_REQUEST. + * Verifies that attempting to change the password of a non-existent user throws + * BAD_REQUEST. */ @Test - @DisplayName("updateUserPassword - Usuario no encontrado") + @DisplayName("updateUserPassword - User not found") void testUpdatePassword_UserNotFound() { doNothing().when(accessControlUtils).validateUserOwnership(999); when(userRepository.findById(999)).thenReturn(Optional.empty()); - CustomException ex = assertThrows(CustomException.class, () -> userService.updateUserPassword(999, UserPasswordDTO.builder().password("old").newPassword("new").newPasswordConfirm("new").build())); + CustomException ex = assertThrows(CustomException.class, () -> userService.updateUserPassword(999, + UserPasswordDTO.builder().password("old").newPassword("new").newPasswordConfirm("new").build())); assertEquals(HttpStatus.BAD_REQUEST, ex.getStatus()); } } diff --git a/src/test/java/eventManager/web/controller/AuthControllerTest.java b/src/test/java/eventManager/web/controller/AuthControllerTest.java index a4811e6..433370d 100644 --- a/src/test/java/eventManager/web/controller/AuthControllerTest.java +++ b/src/test/java/eventManager/web/controller/AuthControllerTest.java @@ -27,10 +27,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** - * Pruebas unitarias del controlador de autenticacion. Cubre registro, login, refresco de token, logout y recuperacion de contrasena. + * Unit tests for the authentication controller. Covers registration, login, + * token refresh, logout, and password recovery. */ @WebMvcTest(AuthController.class) -@AutoConfigureMockMvc(addFilters = false) // Deshabilitamos filtros de seguridad para tests +@AutoConfigureMockMvc(addFilters = false) // Disable security filters for tests. @DisplayName("AuthController Tests") class AuthControllerTest { @@ -40,13 +41,13 @@ class AuthControllerTest { @Autowired private ObjectMapper objectMapper; - @MockitoBean + @MockitoBean private AuthService authService; - @MockitoBean + @MockitoBean private JwtTokenProvider jwtTokenProvider; - @MockitoBean + @MockitoBean private UserDetailsService userDetailsService; private UserCreateDTO validUserCreateDTO; @@ -55,7 +56,7 @@ class AuthControllerTest { @BeforeEach void setUp() { - // Usuario válido para registro + // Valid user for registration validUserCreateDTO = UserCreateDTO.builder() .email("carlos.martinez@eventmanager.es") .password("ClaveSegura2025") @@ -65,13 +66,13 @@ void setUp() { .phoneNumber("612345678") .build(); - // Respuesta de autenticación simulada + // Mock authentication response authResponse = AuthResponse.builder() .status(AuthResponse.Status.SUCCESS) .message("Success") .build(); - // Login request válido + // Valid login request validLoginRequest = LoginRequest.builder() .username("carlos.martinez") .password("ClaveSegura2025") @@ -79,16 +80,17 @@ void setUp() { } /** - * Verifica que el registro funciona correctamente con datos validos. + * Verifies that registration works correctly with valid data. */ @Test - @DisplayName("Register - Registro exitoso con datos válidos") + @DisplayName("Register - Successful registration with valid data") void testRegister_Success() throws Exception { - when(authService.registerUser(any(UserCreateDTO.class), any(HttpServletResponse.class))).thenReturn(authResponse); + when(authService.registerUser(any(UserCreateDTO.class), any(HttpServletResponse.class))) + .thenReturn(authResponse); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(validUserCreateDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(validUserCreateDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("$.status").value("SUCCESS")) .andExpect(jsonPath("$.message").value(authResponse.getMessage())); @@ -97,29 +99,31 @@ void testRegister_Success() throws Exception { } /** - * Verifica que el registro falla cuando el email ya esta registrado en el sistema. + * Verifies that registration fails when the email is already registered in the + * system. */ @Test - @DisplayName("Register - Email ya existe en el sistema") + @DisplayName("Register - Email already exists in the system") void testRegister_EmailAlreadyExists() throws Exception { - when(authService.registerUser(any(UserCreateDTO.class), any(HttpServletResponse.class))).thenThrow(new RuntimeException("El email ya está registrado")); + when(authService.registerUser(any(UserCreateDTO.class), any(HttpServletResponse.class))) + .thenThrow(new RuntimeException("El email ya está registrado")); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(validUserCreateDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(validUserCreateDTO))) .andExpect(status().is5xxServerError()); verify(authService, times(1)).registerUser(any(UserCreateDTO.class), any(HttpServletResponse.class)); } /** - * Verifica el comportamiento del registro cuando se proporciona un email con formato invalido. + * Verifies registration behavior when an email with invalid format is provided. */ @Test - @DisplayName("Register - Email con formato inválido") + @DisplayName("Register - Invalid email format") void testRegister_InvalidEmailFormat() throws Exception { UserCreateDTO invalidEmailDTO = UserCreateDTO.builder() - .email("invalid-email") // Email sin @ y dominio + .email("invalid-email") // Email without @ and domain .password("password123") .username("testuser") .firstName("Test") @@ -128,19 +132,20 @@ void testRegister_InvalidEmailFormat() throws Exception { .build(); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidEmailDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidEmailDTO))) .andExpect(status().isOk()); } /** - * Verifica que se rechaza el registro cuando el email excede la longitud maxima de 50 caracteres. + * Verifies that registration is rejected when the email exceeds the maximum + * length of 50 characters. */ @Test - @DisplayName("Register - Email excede longitud máxima (>50 caracteres)") + @DisplayName("Register - Email exceeds max length (>50 characters)") void testRegister_EmailTooLong() throws Exception { UserCreateDTO longEmailDTO = UserCreateDTO.builder() - .email("a".repeat(45) + "@test.com") // Email > 50 caracteres + .email("a".repeat(45) + "@test.com") // Email > 50 characters .password("password123") .username("testuser") .firstName("Test") @@ -149,20 +154,21 @@ void testRegister_EmailTooLong() throws Exception { .build(); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(longEmailDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(longEmailDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que se rechaza el registro cuando la contrasena excede la longitud maxima de 25 caracteres. + * Verifies that registration is rejected when the password exceeds the maximum + * length of 25 characters. */ @Test - @DisplayName("Register - Password excede longitud máxima (>25 caracteres)") + @DisplayName("Register - Password exceeds max length (>25 characters)") void testRegister_PasswordTooLong() throws Exception { UserCreateDTO longPasswordDTO = UserCreateDTO.builder() .email("test@example.com") - .password("a".repeat(30)) // Password > 25 caracteres + .password("a".repeat(30)) // Password > 25 characters .username("testuser") .firstName("Test") .lastName("User") @@ -170,79 +176,82 @@ void testRegister_PasswordTooLong() throws Exception { .build(); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(longPasswordDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(longPasswordDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que se rechaza el registro cuando el username excede la longitud maxima de 25 caracteres. + * Verifies that registration is rejected when the username exceeds the maximum + * length of 25 characters. */ @Test - @DisplayName("Register - Username excede longitud máxima (>25 caracteres)") + @DisplayName("Register - Username exceeds max length (>25 characters)") void testRegister_UsernameTooLong() throws Exception { UserCreateDTO longUsernameDTO = UserCreateDTO.builder() .email("test@example.com") .password("password123") - .username("a".repeat(30)) // Username > 25 caracteres + .username("a".repeat(30)) // Username > 25 characters .firstName("Test") .lastName("User") .phoneNumber("123456789") .build(); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(longUsernameDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(longUsernameDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que se rechaza el registro cuando el nombre excede la longitud maxima de 20 caracteres. + * Verifies that registration is rejected when the first name exceeds the + * maximum length of 20 characters. */ @Test - @DisplayName("Register - FirstName excede longitud máxima (>20 caracteres)") + @DisplayName("Register - First name exceeds max length (>20 characters)") void testRegister_FirstNameTooLong() throws Exception { UserCreateDTO longFirstNameDTO = UserCreateDTO.builder() .email("test@example.com") .password("password123") .username("testuser") - .firstName("a".repeat(25)) // FirstName > 20 caracteres + .firstName("a".repeat(25)) // FirstName > 20 characters .lastName("User") .phoneNumber("123456789") .build(); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(longFirstNameDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(longFirstNameDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que se rechaza el registro cuando el apellido excede la longitud maxima de 50 caracteres. + * Verifies that registration is rejected when the last name exceeds the maximum + * length of 50 characters. */ @Test - @DisplayName("Register - LastName excede longitud máxima (>50 caracteres)") + @DisplayName("Register - Last name exceeds max length (>50 characters)") void testRegister_LastNameTooLong() throws Exception { UserCreateDTO longLastNameDTO = UserCreateDTO.builder() .email("test@example.com") .password("password123") .username("testuser") .firstName("Test") - .lastName("a".repeat(55)) // LastName > 50 caracteres + .lastName("a".repeat(55)) // LastName > 50 characters .phoneNumber("123456789") .build(); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(longLastNameDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(longLastNameDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que se rechaza el registro cuando los campos obligatorios son null. + * Verifies that registration is rejected when required fields are null. */ @Test - @DisplayName("Register - Campos obligatorios null") + @DisplayName("Register - Required fields are null") void testRegister_RequiredFieldsNull() throws Exception { UserCreateDTO nullFieldsDTO = UserCreateDTO.builder() .email(null) @@ -254,16 +263,16 @@ void testRegister_RequiredFieldsNull() throws Exception { .build(); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(nullFieldsDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(nullFieldsDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica el comportamiento del registro cuando los campos obligatorios estan vacios. + * Verifies registration behavior when required fields are empty. */ @Test - @DisplayName("Register - Campos obligatorios vacíos") + @DisplayName("Register - Required fields are empty") void testRegister_RequiredFieldsEmpty() throws Exception { UserCreateDTO emptyFieldsDTO = UserCreateDTO.builder() .email("") @@ -275,22 +284,23 @@ void testRegister_RequiredFieldsEmpty() throws Exception { .build(); mockMvc.perform(post("/api/auth/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(emptyFieldsDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(emptyFieldsDTO))) .andExpect(status().isOk()); } /** - * Verifica que el login responde correctamente con credenciales validas. + * Verifies that login responds correctly with valid credentials. */ @Test - @DisplayName("Login - Login exitoso con credenciales válidas") + @DisplayName("Login - Successful login with valid credentials") void testLogin_Success() throws Exception { - when(authService.login(any(LoginRequest.class), any(HttpServletResponse.class))).thenReturn(authResponse); + when(authService.login(any(LoginRequest.class), any(HttpServletResponse.class))) + .thenReturn(authResponse); mockMvc.perform(post("/api/auth/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(validLoginRequest))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(validLoginRequest))) .andExpect(status().isOk()) .andExpect(jsonPath("$.status").value("SUCCESS")) .andExpect(jsonPath("$.message").value("Success")); @@ -299,74 +309,78 @@ void testLogin_Success() throws Exception { } /** - * Verifica que el login rechaza credenciales incorrectas con error de cliente. + * Verifies that login rejects invalid credentials with a client error. */ @Test - @DisplayName("Login - Credenciales incorrectas") + @DisplayName("Login - Invalid credentials") void testLogin_InvalidCredentials() throws Exception { - when(authService.login(any(LoginRequest.class), any(HttpServletResponse.class))).thenThrow(new BadCredentialsException("Invalid credentials")); + when(authService.login(any(LoginRequest.class), any(HttpServletResponse.class))) + .thenThrow(new BadCredentialsException("Invalid credentials")); mockMvc.perform(post("/api/auth/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(validLoginRequest))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(validLoginRequest))) .andExpect(status().is4xxClientError()); verify(authService, times(1)).login(any(LoginRequest.class), any(HttpServletResponse.class)); } /** - * Verifica que el login falla cuando el usuario no existe en el sistema. + * Verifies that login fails when the user does not exist in the system. */ @Test - @DisplayName("Login - Usuario no existe") + @DisplayName("Login - User does not exist") void testLogin_UserNotFound() throws Exception { LoginRequest nonExistentUserRequest = LoginRequest.builder() .username("nonexistentuser") .password("password123") .build(); - when(authService.login(any(LoginRequest.class), any(HttpServletResponse.class))).thenThrow(new BadCredentialsException("User not found")); + when(authService.login(any(LoginRequest.class), any(HttpServletResponse.class))) + .thenThrow(new BadCredentialsException("User not found")); mockMvc.perform(post("/api/auth/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(nonExistentUserRequest))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(nonExistentUserRequest))) .andExpect(status().is4xxClientError()); verify(authService, times(1)).login(any(LoginRequest.class), any(HttpServletResponse.class)); } /** - * Verifica que el login falla cuando la contrasena proporcionada es incorrecta. + * Verifies that login fails when the provided password is incorrect. */ @Test - @DisplayName("Login - Password incorrecto") + @DisplayName("Login - Incorrect password") void testLogin_WrongPassword() throws Exception { LoginRequest wrongPasswordRequest = LoginRequest.builder() .username("carlos.martinez") .password("ClaveIncorrecta2025") .build(); - when(authService.login(any(LoginRequest.class), any(HttpServletResponse.class))).thenThrow(new BadCredentialsException("Invalid password")); + when(authService.login(any(LoginRequest.class), any(HttpServletResponse.class))) + .thenThrow(new BadCredentialsException("Invalid password")); mockMvc.perform(post("/api/auth/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(wrongPasswordRequest))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(wrongPasswordRequest))) .andExpect(status().is4xxClientError()); verify(authService, times(1)).login(any(LoginRequest.class), any(HttpServletResponse.class)); } /** - * Verifica que el refresco de token funciona correctamente con un token valido. + * Verifies that token refresh works correctly with a valid token. */ @Test - @DisplayName("Refresh Token - Refresh exitoso con token válido") + @DisplayName("Refresh Token - Successful refresh with valid token") void testRefreshToken_Success() throws Exception { String refreshToken = "valid-refresh-token"; - when(authService.refreshToken(eq(refreshToken), any(HttpServletResponse.class))).thenReturn(authResponse); + when(authService.refreshToken(eq(refreshToken), any(HttpServletResponse.class))) + .thenReturn(authResponse); mockMvc.perform(post("/api/auth/refresh") - .cookie(new Cookie("RefreshToken", refreshToken))) + .cookie(new Cookie("RefreshToken", refreshToken))) .andExpect(status().isOk()) .andExpect(jsonPath("$.status").value("SUCCESS")); @@ -374,12 +388,13 @@ void testRefreshToken_Success() throws Exception { } /** - * Verifica que el refresco de token falla cuando no se proporciona token en la cookie. + * Verifies that token refresh fails when no token is provided in the cookie. */ @Test - @DisplayName("Refresh Token - Sin token de refresh en cookie") + @DisplayName("Refresh Token - Missing refresh token cookie") void testRefreshToken_NoToken() throws Exception { - when(authService.refreshToken(isNull(), any(HttpServletResponse.class))).thenThrow(new RuntimeException("Refresh token not provided")); + when(authService.refreshToken(isNull(), any(HttpServletResponse.class))) + .thenThrow(new RuntimeException("Refresh token not provided")); mockMvc.perform(post("/api/auth/refresh")) .andExpect(status().is5xxServerError()); @@ -388,26 +403,27 @@ void testRefreshToken_NoToken() throws Exception { } /** - * Verifica que el refresco de token falla cuando el token ha expirado. + * Verifies that token refresh fails when the token has expired. */ @Test - @DisplayName("Refresh Token - Token expirado") + @DisplayName("Refresh Token - Token expired") void testRefreshToken_ExpiredToken() throws Exception { String expiredToken = "expired-refresh-token"; - when(authService.refreshToken(eq(expiredToken), any(HttpServletResponse.class))).thenThrow(new RuntimeException("Refresh token expired")); + when(authService.refreshToken(eq(expiredToken), any(HttpServletResponse.class))) + .thenThrow(new RuntimeException("Refresh token expired")); mockMvc.perform(post("/api/auth/refresh") - .cookie(new Cookie("RefreshToken", expiredToken))) + .cookie(new Cookie("RefreshToken", expiredToken))) .andExpect(status().is5xxServerError()); verify(authService, times(1)).refreshToken(eq(expiredToken), any(HttpServletResponse.class)); } /** - * Verifica que el cierre de sesion se realiza correctamente. + * Verifies that logout is performed correctly. */ @Test - @DisplayName("Logout - Logout exitoso") + @DisplayName("Logout - Successful logout") void testLogout_Success() throws Exception { AuthResponse logoutResponse = AuthResponse.builder() .message("Logout successful") @@ -423,51 +439,54 @@ void testLogout_Success() throws Exception { } /** - * Verifica el flujo de cambio de contrasena olvidada con datos validos. + * Verifies the forgotten password flow with valid data. */ @Test - @DisplayName("Forgot Password - Cambio exitoso de contraseña olvidada") + @DisplayName("Forgot Password - Successful forgotten password change") void testForgotPassword_Success() throws Exception { eventManager.dto.UserDTO userDTO = new eventManager.dto.UserDTO(); when(authService.changeForgottenPassword(any(UserForgottenPassword.class))).thenReturn(userDTO); mockMvc.perform(post("/api/auth/forgot-password") - .param("email", "carlos.martinez@eventmanager.es") - .param("username", "carlos.martinez") - .param("newPassword", "ClaveNueva2025") - .param("newPasswordConfirm", "ClaveNueva2025")) + .param("email", "carlos.martinez@eventmanager.es") + .param("username", "carlos.martinez") + .param("newPassword", "ClaveNueva2025") + .param("newPasswordConfirm", "ClaveNueva2025")) .andExpect(status().is5xxServerError()); } /** - * Verifica que el cambio de contrasena falla cuando el email no existe en el sistema. + * Verifies that password change fails when the email does not exist in the + * system. */ @Test - @DisplayName("Forgot Password - Email no existe") + @DisplayName("Forgot Password - Email does not exist") void testForgotPassword_EmailNotFound() throws Exception { - when(authService.changeForgottenPassword(any(UserForgottenPassword.class))).thenThrow(new RuntimeException("Email not found")); + when(authService.changeForgottenPassword(any(UserForgottenPassword.class))) + .thenThrow(new RuntimeException("Email not found")); mockMvc.perform(post("/api/auth/forgot-password") - .param("email", "laura.sanchez@eventmanager.es") - .param("username", "carlos.martinez") - .param("newPassword", "ClaveNueva2025") - .param("newPasswordConfirm", "ClaveNueva2025")) + .param("email", "laura.sanchez@eventmanager.es") + .param("username", "carlos.martinez") + .param("newPassword", "ClaveNueva2025") + .param("newPasswordConfirm", "ClaveNueva2025")) .andExpect(status().is5xxServerError()); } /** - * Verifica que el cambio de contrasena falla cuando la nueva contrasena es demasiado corta. + * Verifies that password change fails when the new password is too short. */ @Test - @DisplayName("Forgot Password - Nueva contraseña inválida (demasiado corta)") + @DisplayName("Forgot Password - New password invalid (too short)") void testForgotPassword_InvalidNewPassword() throws Exception { - when(authService.changeForgottenPassword(any(UserForgottenPassword.class))).thenThrow(new RuntimeException("Invalid new password")); + when(authService.changeForgottenPassword(any(UserForgottenPassword.class))) + .thenThrow(new RuntimeException("Invalid new password")); mockMvc.perform(post("/api/auth/forgot-password") - .param("email", "carlos.martinez@eventmanager.es") - .param("username", "carlos.martinez") - .param("newPassword", "123") - .param("newPasswordConfirm", "123")) + .param("email", "carlos.martinez@eventmanager.es") + .param("username", "carlos.martinez") + .param("newPassword", "123") + .param("newPasswordConfirm", "123")) .andExpect(status().is5xxServerError()); } } diff --git a/src/test/java/eventManager/web/controller/EventControllerTest.java b/src/test/java/eventManager/web/controller/EventControllerTest.java index 84a71de..d7eaa27 100644 --- a/src/test/java/eventManager/web/controller/EventControllerTest.java +++ b/src/test/java/eventManager/web/controller/EventControllerTest.java @@ -28,7 +28,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** - * Pruebas unitarias del controlador de eventos. Cubre obtencion de eventos, creacion, actualizacion y consulta por codigo. + * Unit tests for the event controller. Covers event retrieval, creation, + * updates, and lookup by code. */ @WebMvcTest(EventController.class) @AutoConfigureMockMvc(addFilters = false) @@ -41,13 +42,13 @@ class EventControllerTest { @Autowired private ObjectMapper objectMapper; - @MockitoBean + @MockitoBean private EventService eventService; - @MockitoBean + @MockitoBean private JwtTokenProvider jwtTokenProvider; - @MockitoBean + @MockitoBean private UserDetailsService userDetailsService; private EventDTO eventDTO; @@ -56,7 +57,7 @@ class EventControllerTest { @BeforeEach void setUp() { - // EventDTO de respuesta + // Response EventDTO eventDTO = new EventDTO(); eventDTO.setEventCode("ABC123"); eventDTO.setEventName("Test Event"); @@ -65,7 +66,7 @@ void setUp() { eventDTO.setDate(LocalDateTime.now().plusDays(7)); eventDTO.setStatus("ACTIVO"); - // DTO para crear/actualizar evento + // DTO for create/update event createEventDTO = CreateUpdateEventDTO.builder() .eventName("Test Event") .description("Test Description") @@ -74,7 +75,7 @@ void setUp() { .status("ACTIVO") .build(); - // Resultado paginado + // Paginated result paginationDTO = new ResultPaginationDTO(); paginationDTO.setData(new ArrayList<>()); PaginationDTO pageDTO = new PaginationDTO(); @@ -86,134 +87,146 @@ void setUp() { } /** - * Verifica que se obtienen los eventos correctamente con paginacion y todos los parametros. + * Verifies that events are retrieved correctly with pagination and all + * parameters. */ @Test - @DisplayName("Get Events - Obtener eventos exitosamente con paginación") + @DisplayName("Get Events - Successfully retrieve events with pagination") @WithMockUser void testGetEvents_Success() throws Exception { - when(eventService.getEvents(anyInt(), anyInt(), anyString(), anyString(), anyString(), anyInt(), anyString())).thenReturn(paginationDTO); + when(eventService.getEvents(anyInt(), anyInt(), anyString(), anyString(), anyString(), anyInt(), + anyString())).thenReturn(paginationDTO); mockMvc.perform(get("/api/events") - .param("page", "0") - .param("userId", "1") - .param("role", "HOST") - .param("pageSize", "10") - .param("sortBy", "eventDate") - .param("sortDir", "ASC") - .param("search", "")) + .param("page", "0") + .param("userId", "1") + .param("role", "HOST") + .param("pageSize", "10") + .param("sortBy", "eventDate") + .param("sortDir", "ASC") + .param("search", "")) .andExpect(status().isOk()) .andExpect(jsonPath("$.page.number").value(0)) .andExpect(jsonPath("$.page.size").value(10)); - verify(eventService, times(1)).getEvents(anyInt(), anyInt(), anyString(), anyString(), anyString(), anyInt(), anyString()); + verify(eventService, times(1)).getEvents(anyInt(), anyInt(), anyString(), anyString(), anyString(), + anyInt(), anyString()); } /** - * Verifica que se obtienen los eventos proporcionando solo los parametros minimos requeridos. + * Verifies that events are retrieved when only the minimum required parameters + * are provided. */ @Test - @DisplayName("Get Events - Parámetros mínimos requeridos") + @DisplayName("Get Events - Minimum required parameters") @WithMockUser void testGetEvents_MinimalParams() throws Exception { - when(eventService.getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), nullable(String.class), anyInt(), anyString())).thenReturn(paginationDTO); + when(eventService.getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), + nullable(String.class), anyInt(), anyString())).thenReturn(paginationDTO); mockMvc.perform(get("/api/events") - .param("page", "0") - .param("userId", "1") - .param("role", "GUEST")) + .param("page", "0") + .param("userId", "1") + .param("role", "GUEST")) .andExpect(status().isOk()); - verify(eventService, times(1)).getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), nullable(String.class), anyInt(), anyString()); + verify(eventService, times(1)).getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), + nullable(String.class), anyInt(), anyString()); } /** - * Verifica el comportamiento de la consulta de eventos cuando se proporciona una pagina negativa. + * Verifies the behavior of event queries when a negative page is provided. */ @Test - @DisplayName("Get Events - Página inválida (negativa)") + @DisplayName("Get Events - Invalid page (negative)") @WithMockUser void testGetEvents_InvalidPage() throws Exception { mockMvc.perform(get("/api/events") - .param("page", "-1") - .param("userId", "1") - .param("role", "HOST")) - .andExpect(status().isOk()); + .param("page", "-1") + .param("userId", "1") + .param("role", "HOST")) + .andExpect(status().isOk()); } /** - * Verifica que la consulta de eventos falla cuando no se proporciona el userId. + * Verifies that the event query fails when userId is not provided. */ @Test - @DisplayName("Get Events - UserId faltante") + @DisplayName("Get Events - Missing userId") @WithMockUser void testGetEvents_MissingUserId() throws Exception { mockMvc.perform(get("/api/events") - .param("page", "0") - .param("role", "HOST")) - .andExpect(status().is5xxServerError()); + .param("page", "0") + .param("role", "HOST")) + .andExpect(status().is5xxServerError()); } /** - * Verifica que la consulta de eventos falla cuando se proporciona un rol invalido. + * Verifies that the event query fails when an invalid role is provided. */ @Test - @DisplayName("Get Events - Role inválido") + @DisplayName("Get Events - Invalid role") @WithMockUser void testGetEvents_InvalidRole() throws Exception { - when(eventService.getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), nullable(String.class), anyInt(), anyString())).thenThrow(new RuntimeException("Invalid role")); + when(eventService.getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), + nullable(String.class), anyInt(), anyString())) + .thenThrow(new RuntimeException("Invalid role")); mockMvc.perform(get("/api/events") - .param("page", "0") - .param("userId", "1") - .param("role", "INVALID_ROLE")) + .param("page", "0") + .param("userId", "1") + .param("role", "INVALID_ROLE")) .andExpect(status().is5xxServerError()); } /** - * Verifica que la busqueda de eventos por nombre funciona correctamente. + * Verifies that searching events by name works correctly. */ @Test - @DisplayName("Get Events - Con búsqueda por nombre") + @DisplayName("Get Events - With name search") @WithMockUser void testGetEvents_WithSearch() throws Exception { - when(eventService.getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), anyString(), anyInt(), anyString())).thenReturn(paginationDTO); + when(eventService.getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), anyString(), + anyInt(), anyString())).thenReturn(paginationDTO); mockMvc.perform(get("/api/events") - .param("page", "0") - .param("userId", "1") - .param("role", "HOST") - .param("search", "Birthday")) + .param("page", "0") + .param("userId", "1") + .param("role", "HOST") + .param("search", "Birthday")) .andExpect(status().isOk()); - verify(eventService, times(1)).getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), eq("Birthday"), anyInt(), anyString()); + verify(eventService, times(1)).getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), + eq("Birthday"), anyInt(), anyString()); } /** - * Verifica que la consulta de eventos funciona correctamente con ordenacion descendente. + * Verifies that event queries work correctly with descending sort order. */ @Test - @DisplayName("Get Events - Ordenar descendente") + @DisplayName("Get Events - Sort descending") @WithMockUser void testGetEvents_SortDescending() throws Exception { - when(eventService.getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), nullable(String.class), anyInt(), anyString())).thenReturn(paginationDTO); + when(eventService.getEvents(anyInt(), nullable(Integer.class), anyString(), anyString(), + nullable(String.class), anyInt(), anyString())).thenReturn(paginationDTO); mockMvc.perform(get("/api/events") - .param("page", "0") - .param("userId", "1") - .param("role", "HOST") - .param("sortBy", "name") - .param("sortDir", "DESC")) + .param("page", "0") + .param("userId", "1") + .param("role", "HOST") + .param("sortBy", "name") + .param("sortDir", "DESC")) .andExpect(status().isOk()); - verify(eventService, times(1)).getEvents(anyInt(), nullable(Integer.class), eq("name"), eq("DESC"), nullable(String.class), anyInt(), anyString()); + verify(eventService, times(1)).getEvents(anyInt(), nullable(Integer.class), eq("name"), eq("DESC"), + nullable(String.class), anyInt(), anyString()); } /** - * Verifica que se obtiene un evento correctamente a partir de su codigo. + * Verifies that an event is retrieved correctly by its code. */ @Test - @DisplayName("Get Event By Code - Exitoso") + @DisplayName("Get Event By Code - Success") @WithMockUser void testGetEventByCode_Success() throws Exception { when(eventService.getEvent("ABC123")).thenReturn(eventDTO); @@ -227,10 +240,10 @@ void testGetEventByCode_Success() throws Exception { } /** - * Verifica que la consulta por codigo falla cuando el evento no existe. + * Verifies that lookup by code fails when the event does not exist. */ @Test - @DisplayName("Get Event By Code - Evento no encontrado") + @DisplayName("Get Event By Code - Event not found") @WithMockUser void testGetEventByCode_NotFound() throws Exception { when(eventService.getEvent("NOTFOUND")).thenThrow(new RuntimeException("Event not found")); @@ -242,10 +255,10 @@ void testGetEventByCode_NotFound() throws Exception { } /** - * Verifica que la consulta por codigo falla cuando el formato del codigo es incorrecto. + * Verifies that lookup by code fails when the code format is incorrect. */ @Test - @DisplayName("Get Event By Code - Código inválido (formato incorrecto)") + @DisplayName("Get Event By Code - Invalid code (incorrect format)") @WithMockUser void testGetEventByCode_InvalidFormat() throws Exception { when(eventService.getEvent("abc")).thenThrow(new RuntimeException("Invalid event code format")); @@ -257,29 +270,29 @@ void testGetEventByCode_InvalidFormat() throws Exception { } /** - * Verifica que la consulta por codigo falla cuando se proporciona un codigo null. + * Verifies that lookup by code fails when a null code is provided. */ @Test - @DisplayName("Get Event By Code - Código null") + @DisplayName("Get Event By Code - Null code") @WithMockUser void testGetEventByCode_NullCode() throws Exception { mockMvc.perform(get("/api/events/{eventCode}", (Object) null)) - .andExpect(status().is5xxServerError()); + .andExpect(status().is5xxServerError()); } /** - * Verifica que la creacion de un evento funciona correctamente con datos validos. + * Verifies that event creation works correctly with valid data. */ @Test - @DisplayName("Create Event - Creación exitosa") + @DisplayName("Create Event - Successful creation") @WithMockUser void testCreateEvent_Success() throws Exception { when(eventService.createEvent(eq(1), any(CreateUpdateEventDTO.class))).thenReturn(eventDTO); mockMvc.perform(post("/api/events") - .param("userId", "1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(createEventDTO))) + .param("userId", "1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createEventDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.eventCode").value("ABC123")) .andExpect(jsonPath("$.eventName").value("Test Event")); @@ -288,14 +301,15 @@ void testCreateEvent_Success() throws Exception { } /** - * Verifica que la creacion se rechaza cuando el nombre del evento excede los 100 caracteres. + * Verifies that creation is rejected when the event name exceeds 100 + * characters. */ @Test - @DisplayName("Create Event - Nombre demasiado largo") + @DisplayName("Create Event - Name too long") @WithMockUser void testCreateEvent_NameTooLong() throws Exception { CreateUpdateEventDTO invalidDTO = CreateUpdateEventDTO.builder() - .eventName("A".repeat(105)) // > 100 caracteres + .eventName("A".repeat(105)) // > 100 characters .description("Test Description") .place("Test Location") .date(LocalDateTime.now().plusDays(7)) @@ -303,63 +317,65 @@ void testCreateEvent_NameTooLong() throws Exception { .build(); mockMvc.perform(post("/api/events") - .param("userId", "1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDTO))) + .param("userId", "1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que la creacion se rechaza cuando la descripcion excede los 500 caracteres. + * Verifies that creation is rejected when the description exceeds 500 + * characters. */ @Test - @DisplayName("Create Event - Descripción demasiado larga") + @DisplayName("Create Event - Description too long") @WithMockUser void testCreateEvent_DescriptionTooLong() throws Exception { CreateUpdateEventDTO invalidDTO = CreateUpdateEventDTO.builder() .eventName("Test Event") - .description("A".repeat(505)) // > 500 caracteres + .description("A".repeat(505)) // > 500 characters .place("Test Location") .date(LocalDateTime.now().plusDays(7)) .status("ACTIVO") .build(); mockMvc.perform(post("/api/events") - .param("userId", "1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDTO))) + .param("userId", "1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que la creacion falla cuando se proporciona una fecha en el pasado. + * Verifies that creation fails when a date in the past is provided. */ @Test - @DisplayName("Create Event - Fecha en el pasado") + @DisplayName("Create Event - Date in the past") @WithMockUser void testCreateEvent_PastDate() throws Exception { CreateUpdateEventDTO pastDateDTO = CreateUpdateEventDTO.builder() .eventName("Test Event") .description("Test Description") .place("Test Location") - .date(LocalDateTime.now().minusDays(1)) // Fecha pasada + .date(LocalDateTime.now().minusDays(1)) // Past date .status("ACTIVO") .build(); - when(eventService.createEvent(eq(1), any(CreateUpdateEventDTO.class))).thenThrow(new RuntimeException("Event date cannot be in the past")); + when(eventService.createEvent(eq(1), any(CreateUpdateEventDTO.class))) + .thenThrow(new RuntimeException("Event date cannot be in the past")); mockMvc.perform(post("/api/events") - .param("userId", "1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(pastDateDTO))) + .param("userId", "1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(pastDateDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica el comportamiento de la creacion cuando se proporciona una capacidad maxima invalida. + * Verifies creation behavior when an invalid maximum capacity is provided. */ @Test - @DisplayName("Create Event - Capacidad máxima inválida (negativa)") + @DisplayName("Create Event - Invalid max capacity (negative)") @WithMockUser void testCreateEvent_InvalidMaxCapacity() throws Exception { CreateUpdateEventDTO invalidCapacityDTO = CreateUpdateEventDTO.builder() @@ -371,17 +387,17 @@ void testCreateEvent_InvalidMaxCapacity() throws Exception { .build(); mockMvc.perform(post("/api/events") - .param("userId", "1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidCapacityDTO))) + .param("userId", "1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidCapacityDTO))) .andExpect(status().isCreated()); } /** - * Verifica que la creacion falla cuando la capacidad maxima es cero. + * Verifies that creation fails when the maximum capacity is zero. */ @Test - @DisplayName("Create Event - Capacidad máxima cero") + @DisplayName("Create Event - Max capacity zero") @WithMockUser void testCreateEvent_ZeroMaxCapacity() throws Exception { CreateUpdateEventDTO zeroCapacityDTO = CreateUpdateEventDTO.builder() @@ -392,20 +408,21 @@ void testCreateEvent_ZeroMaxCapacity() throws Exception { .status("ACTIVO") .build(); - when(eventService.createEvent(eq(1), any(CreateUpdateEventDTO.class))).thenThrow(new RuntimeException("Max capacity must be positive")); + when(eventService.createEvent(eq(1), any(CreateUpdateEventDTO.class))) + .thenThrow(new RuntimeException("Max capacity must be positive")); mockMvc.perform(post("/api/events") - .param("userId", "1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(zeroCapacityDTO))) + .param("userId", "1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(zeroCapacityDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica el comportamiento de la creacion cuando los campos obligatorios son null. + * Verifies creation behavior when required fields are null. */ @Test - @DisplayName("Create Event - Campos obligatorios null") + @DisplayName("Create Event - Required fields are null") @WithMockUser void testCreateEvent_RequiredFieldsNull() throws Exception { CreateUpdateEventDTO nullFieldsDTO = CreateUpdateEventDTO.builder() @@ -417,33 +434,34 @@ void testCreateEvent_RequiredFieldsNull() throws Exception { .build(); mockMvc.perform(post("/api/events") - .param("userId", "1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(nullFieldsDTO))) + .param("userId", "1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(nullFieldsDTO))) .andExpect(status().isCreated()); } /** - * Verifica que la creacion falla cuando el usuario asociado no existe. + * Verifies that creation fails when the associated user does not exist. */ @Test - @DisplayName("Create Event - Usuario no encontrado") + @DisplayName("Create Event - User not found") @WithMockUser void testCreateEvent_UserNotFound() throws Exception { - when(eventService.createEvent(eq(999), any(CreateUpdateEventDTO.class))).thenThrow(new RuntimeException("User not found")); + when(eventService.createEvent(eq(999), any(CreateUpdateEventDTO.class))) + .thenThrow(new RuntimeException("User not found")); mockMvc.perform(post("/api/events") - .param("userId", "999") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(createEventDTO))) + .param("userId", "999") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createEventDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica que la actualizacion de un evento funciona correctamente con datos validos. + * Verifies that updating an event works correctly with valid data. */ @Test - @DisplayName("Update Event - Actualización exitosa") + @DisplayName("Update Event - Successful update") @WithMockUser void testUpdateEvent_Success() throws Exception { EventDTO updatedEvent = new EventDTO(); @@ -454,8 +472,8 @@ void testUpdateEvent_Success() throws Exception { when(eventService.updateEvent(eq("ABC123"), any(CreateUpdateEventDTO.class))).thenReturn(updatedEvent); mockMvc.perform(put("/api/events/{eventCode}", "ABC123") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(createEventDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createEventDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("$.eventCode").value("ABC123")); @@ -463,31 +481,32 @@ void testUpdateEvent_Success() throws Exception { } /** - * Verifica que la actualizacion falla cuando el evento no existe. + * Verifies that update fails when the event does not exist. */ @Test - @DisplayName("Update Event - Evento no encontrado") + @DisplayName("Update Event - Event not found") @WithMockUser void testUpdateEvent_NotFound() throws Exception { - when(eventService.updateEvent(eq("NOTFOUND"), any(CreateUpdateEventDTO.class))).thenThrow(new RuntimeException("Event not found")); + when(eventService.updateEvent(eq("NOTFOUND"), any(CreateUpdateEventDTO.class))) + .thenThrow(new RuntimeException("Event not found")); mockMvc.perform(put("/api/events/{eventCode}", "NOTFOUND") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(createEventDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createEventDTO))) .andExpect(status().is5xxServerError()); verify(eventService, times(1)).updateEvent(eq("NOTFOUND"), any(CreateUpdateEventDTO.class)); } /** - * Verifica que la actualizacion se rechaza cuando los datos proporcionados son invalidos. + * Verifies that update is rejected when the provided data is invalid. */ @Test - @DisplayName("Update Event - Datos inválidos") + @DisplayName("Update Event - Invalid data") @WithMockUser void testUpdateEvent_InvalidData() throws Exception { CreateUpdateEventDTO invalidDTO = CreateUpdateEventDTO.builder() - .eventName("A".repeat(105)) // Nombre demasiado largo + .eventName("A".repeat(105)) // Name too long .description("Test") .place("Test") .date(LocalDateTime.now().plusDays(7)) @@ -495,16 +514,16 @@ void testUpdateEvent_InvalidData() throws Exception { .build(); mockMvc.perform(put("/api/events/{eventCode}", "ABC123") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que la actualizacion falla cuando se reduce la capacidad por debajo de los asistentes actuales. + * Verifies that update fails when reducing capacity below current attendees. */ @Test - @DisplayName("Update Event - Reducir capacidad por debajo de asistentes actuales") + @DisplayName("Update Event - Reduce capacity below current attendees") @WithMockUser void testUpdateEvent_CapacityBelowCurrentAttendees() throws Exception { CreateUpdateEventDTO reducedCapacityDTO = CreateUpdateEventDTO.builder() @@ -515,11 +534,12 @@ void testUpdateEvent_CapacityBelowCurrentAttendees() throws Exception { .status("ACTIVO") .build(); - when(eventService.updateEvent(eq("ABC123"), any(CreateUpdateEventDTO.class))).thenThrow(new RuntimeException("Cannot reduce capacity below current attendees")); + when(eventService.updateEvent(eq("ABC123"), any(CreateUpdateEventDTO.class))) + .thenThrow(new RuntimeException("Cannot reduce capacity below current attendees")); mockMvc.perform(put("/api/events/{eventCode}", "ABC123") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(reducedCapacityDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reducedCapacityDTO))) .andExpect(status().is5xxServerError()); } } diff --git a/src/test/java/eventManager/web/controller/GiftControllerTest.java b/src/test/java/eventManager/web/controller/GiftControllerTest.java index ac12604..fb63fd2 100644 --- a/src/test/java/eventManager/web/controller/GiftControllerTest.java +++ b/src/test/java/eventManager/web/controller/GiftControllerTest.java @@ -24,7 +24,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** - * Pruebas unitarias del controlador de regalos. Cubre creacion, obtencion, listado, actualizacion, eliminacion y contribuciones. + * Unit tests for the gift controller. Covers creation, retrieval, listing, + * updates, deletion, and contributions. */ @WebMvcTest(GiftController.class) @AutoConfigureMockMvc(addFilters = false) @@ -37,13 +38,13 @@ class GiftControllerTest { @Autowired private ObjectMapper objectMapper; - @MockitoBean + @MockitoBean private GiftService giftService; - @MockitoBean + @MockitoBean private JwtTokenProvider jwtTokenProvider; - @MockitoBean + @MockitoBean private UserDetailsService userDetailsService; private GiftDTO giftDTO; @@ -55,14 +56,14 @@ class GiftControllerTest { @BeforeEach void setUp() { - // GiftDTO básico + // Basic GiftDTO giftDTO = new GiftDTO(); giftDTO.setGiftId(1); giftDTO.setName("Juego de mesa cooperativo"); giftDTO.setPrice(100.00); giftDTO.setCollected(50.00); - // GiftExtendedDTO con información adicional + // GiftExtendedDTO with additional information giftExtendedDTO = new GiftExtendedDTO(); giftExtendedDTO.setGiftId(1); giftExtendedDTO.setName("Juego de mesa cooperativo"); @@ -71,7 +72,7 @@ void setUp() { giftExtendedDTO.setCollected(50.00); giftExtendedDTO.setUserContributionList(new ArrayList<>()); - // DTO para crear regalo + // DTO for gift creation giftCreateDTO = GiftCreateDTO.builder() .name("Juego de mesa cooperativo") .details("Regalo para la quedada de fin de semana") @@ -80,7 +81,7 @@ void setUp() { .image("https://imagenes.tienda-regalos.es/juego-mesa-cooperativo.jpg") .build(); - // DTO para actualizar regalo + // DTO for gift update giftUpdateDTO = GiftUpdateDTO.builder() .name("Juego de mesa premium") .details("Edicion coleccionista para regalo grupal") @@ -89,13 +90,13 @@ void setUp() { .image("https://imagenes.tienda-regalos.es/juego-mesa-premium.jpg") .build(); - // DTO para contribución + // DTO for contribution userGiftDTO = UserGiftDTO.builder() .userId(1) .amount(25.00) .build(); - // Resultado paginado + // Paginated result paginationDTO = new ResultPaginationDTO(); paginationDTO.setData(new ArrayList<>()); PaginationDTO pageDTO = new PaginationDTO(); @@ -107,18 +108,18 @@ void setUp() { } /** - * Verifica que la creacion de un regalo funciona correctamente con datos validos. + * Verifies that gift creation works correctly with valid data. */ @Test - @DisplayName("Create Gift - Creación exitosa") + @DisplayName("Create Gift - Successful creation") @WithMockUser void testCreateGift_Success() throws Exception { when(giftService.createGift(eq("MADRID"), any(GiftCreateDTO.class))) .thenReturn(giftDTO); mockMvc.perform(post("/api/events/{eventCode}/gifts", "MADRID") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(giftCreateDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(giftCreateDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.giftId").value(1)) .andExpect(jsonPath("$.name").value("Juego de mesa cooperativo")); @@ -127,68 +128,68 @@ void testCreateGift_Success() throws Exception { } /** - * Verifica que la creacion de un regalo falla cuando el evento no existe. + * Verifies that gift creation fails when the event does not exist. */ @Test - @DisplayName("Create Gift - Evento no encontrado") + @DisplayName("Create Gift - Event not found") @WithMockUser void testCreateGift_EventNotFound() throws Exception { when(giftService.createGift(eq("NOTFOUND"), any(GiftCreateDTO.class))) .thenThrow(new RuntimeException("Event not found")); mockMvc.perform(post("/api/events/{eventCode}/gifts", "NOTFOUND") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(giftCreateDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(giftCreateDTO))) .andExpect(status().is5xxServerError()); verify(giftService, times(1)).createGift(eq("NOTFOUND"), any(GiftCreateDTO.class)); } /** - * Verifica que la creacion se rechaza cuando el nombre del regalo excede los 100 caracteres. + * Verifies that creation is rejected when the gift name exceeds 100 characters. */ @Test - @DisplayName("Create Gift - Nombre demasiado largo") + @DisplayName("Create Gift - Name too long") @WithMockUser void testCreateGift_NameTooLong() throws Exception { GiftCreateDTO invalidDTO = GiftCreateDTO.builder() - .name("A".repeat(105)) // > 100 caracteres + .name("A".repeat(105)) // > 100 characters .details("Regalo") .price(100.00) .url("https://tienda-regalos.es") .build(); mockMvc.perform(post("/api/events/{eventCode}/gifts", "MADRID") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica el comportamiento de la creacion cuando se proporciona un precio objetivo negativo. + * Verifies creation behavior when a negative target price is provided. */ @Test - @DisplayName("Create Gift - Precio objetivo negativo") + @DisplayName("Create Gift - Negative target price") @WithMockUser void testCreateGift_NegativeTargetPrice() throws Exception { GiftCreateDTO invalidDTO = GiftCreateDTO.builder() .name("Juego de mesa cooperativo") .details("Regalo para evento familiar") - .price(-50.00) // Precio negativo + .price(-50.00) // Negative price .url("https://tienda-regalos.es") .build(); mockMvc.perform(post("/api/events/{eventCode}/gifts", "MADRID") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDTO))) .andExpect(status().isCreated()); } /** - * Verifica que la creacion falla cuando el precio objetivo es cero. + * Verifies that creation fails when the target price is zero. */ @Test - @DisplayName("Create Gift - Precio objetivo cero") + @DisplayName("Create Gift - Target price zero") @WithMockUser void testCreateGift_ZeroTargetPrice() throws Exception { GiftCreateDTO zeroDTO = GiftCreateDTO.builder() @@ -202,16 +203,16 @@ void testCreateGift_ZeroTargetPrice() throws Exception { .thenThrow(new RuntimeException("Target price must be positive")); mockMvc.perform(post("/api/events/{eventCode}/gifts", "MADRID") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(zeroDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(zeroDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica el comportamiento de la creacion cuando los campos obligatorios son null. + * Verifies creation behavior when required fields are null. */ @Test - @DisplayName("Create Gift - Campos obligatorios null") + @DisplayName("Create Gift - Required fields are null") @WithMockUser void testCreateGift_RequiredFieldsNull() throws Exception { GiftCreateDTO nullDTO = GiftCreateDTO.builder() @@ -221,36 +222,36 @@ void testCreateGift_RequiredFieldsNull() throws Exception { .build(); mockMvc.perform(post("/api/events/{eventCode}/gifts", "ABC123") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(nullDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(nullDTO))) .andExpect(status().isCreated()); } /** - * Verifica el comportamiento de la creacion cuando se proporciona una URL mal formada. + * Verifies creation behavior when a malformed URL is provided. */ @Test - @DisplayName("Create Gift - URL inválida") + @DisplayName("Create Gift - Invalid URL") @WithMockUser void testCreateGift_InvalidUrl() throws Exception { GiftCreateDTO invalidUrlDTO = GiftCreateDTO.builder() .name("Test Gift") .details("Test Description") .price(100.00) - .url("invalid-url") // URL mal formada + .url("invalid-url") // Malformed URL .build(); mockMvc.perform(post("/api/events/{eventCode}/gifts", "ABC123") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidUrlDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidUrlDTO))) .andExpect(status().isCreated()); } /** - * Verifica que se obtiene la informacion de un regalo correctamente. + * Verifies that gift information is retrieved correctly. */ @Test - @DisplayName("Get Gift - Obtener regalo exitosamente") + @DisplayName("Get Gift - Successfully retrieve gift") @WithMockUser void testGetGift_Success() throws Exception { when(giftService.getGiftInformation("ABC123", 1)) @@ -265,10 +266,10 @@ void testGetGift_Success() throws Exception { } /** - * Verifica que la consulta de un regalo falla cuando no existe. + * Verifies that retrieving a gift fails when it does not exist. */ @Test - @DisplayName("Get Gift - Regalo no encontrado") + @DisplayName("Get Gift - Gift not found") @WithMockUser void testGetGift_NotFound() throws Exception { when(giftService.getGiftInformation("ABC123", 999)) @@ -281,10 +282,11 @@ void testGetGift_NotFound() throws Exception { } /** - * Verifica que la consulta de un regalo falla cuando el evento asociado no existe. + * Verifies that retrieving a gift fails when the associated event does not + * exist. */ @Test - @DisplayName("Get Gift - Evento no encontrado") + @DisplayName("Get Gift - Event not found") @WithMockUser void testGetGift_EventNotFound() throws Exception { when(giftService.getGiftInformation("NOTFOUND", 1)) @@ -297,71 +299,74 @@ void testGetGift_EventNotFound() throws Exception { } /** - * Verifica que se listan los regalos de un evento correctamente con paginacion. + * Verifies that gifts are listed correctly for an event with pagination. */ @Test - @DisplayName("Get Gifts - Listar regalos exitosamente") + @DisplayName("Get Gifts - Successfully list gifts") @WithMockUser void testGetGifts_Success() throws Exception { when(giftService.getGifts(eq("ABC123"), anyInt(), anyInt(), anyString(), anyString(), anyString())) .thenReturn(paginationDTO); mockMvc.perform(get("/api/events/{eventCode}/gifts", "ABC123") - .param("page", "0") - .param("pageSize", "10") - .param("sortBy", "name") - .param("sortDir", "ASC") - .param("search", "")) + .param("page", "0") + .param("pageSize", "10") + .param("sortBy", "name") + .param("sortDir", "ASC") + .param("search", "")) .andExpect(status().isOk()) .andExpect(jsonPath("$.page.number").value(0)) .andExpect(jsonPath("$.page.size").value(10)); - verify(giftService, times(1)).getGifts(eq("ABC123"), anyInt(), anyInt(), anyString(), anyString(), anyString()); + verify(giftService, times(1)).getGifts(eq("ABC123"), anyInt(), anyInt(), anyString(), anyString(), + anyString()); } /** - * Verifica que la busqueda de regalos por nombre funciona correctamente. + * Verifies that searching gifts by name works correctly. */ @Test - @DisplayName("Get Gifts - Con búsqueda por nombre") + @DisplayName("Get Gifts - With name search") @WithMockUser void testGetGifts_WithSearch() throws Exception { - when(giftService.getGifts(eq("ABC123"), anyInt(), nullable(Integer.class), anyString(), anyString(), eq("Laptop"))) + when(giftService.getGifts(eq("ABC123"), anyInt(), nullable(Integer.class), anyString(), anyString(), + eq("Laptop"))) .thenReturn(paginationDTO); mockMvc.perform(get("/api/events/{eventCode}/gifts", "ABC123") - .param("page", "0") - .param("search", "Laptop")) + .param("page", "0") + .param("search", "Laptop")) .andExpect(status().isOk()); - verify(giftService, times(1)).getGifts(eq("ABC123"), anyInt(), nullable(Integer.class), anyString(), anyString(), eq("Laptop")); + verify(giftService, times(1)).getGifts(eq("ABC123"), anyInt(), nullable(Integer.class), anyString(), + anyString(), eq("Laptop")); } /** - * Verifica el comportamiento del listado de regalos cuando se proporciona una pagina invalida. + * Verifies gift listing behavior when an invalid page is provided. */ @Test - @DisplayName("Get Gifts - Página inválida") + @DisplayName("Get Gifts - Invalid page") @WithMockUser void testGetGifts_InvalidPage() throws Exception { mockMvc.perform(get("/api/events/{eventCode}/gifts", "ABC123") - .param("page", "-1")) - .andExpect(status().isOk()); + .param("page", "-1")) + .andExpect(status().isOk()); } /** - * Verifica que la actualizacion de un regalo funciona correctamente con datos validos. + * Verifies that a gift update works correctly with valid data. */ @Test - @DisplayName("Update Gift - Actualización exitosa") + @DisplayName("Update Gift - Successful update") @WithMockUser void testUpdateGift_Success() throws Exception { when(giftService.updateGift(eq("ABC123"), eq(1), any(GiftUpdateDTO.class))) .thenReturn(giftExtendedDTO); mockMvc.perform(put("/api/events/{eventCode}/gifts/{giftId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(giftUpdateDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(giftUpdateDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("$.giftId").value(1)); @@ -369,47 +374,47 @@ void testUpdateGift_Success() throws Exception { } /** - * Verifica que la actualizacion falla cuando el regalo no existe. + * Verifies that update fails when the gift does not exist. */ @Test - @DisplayName("Update Gift - Regalo no encontrado") + @DisplayName("Update Gift - Gift not found") @WithMockUser void testUpdateGift_NotFound() throws Exception { when(giftService.updateGift(eq("ABC123"), eq(999), any(GiftUpdateDTO.class))) .thenThrow(new RuntimeException("Gift not found")); mockMvc.perform(put("/api/events/{eventCode}/gifts/{giftId}", "ABC123", 999) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(giftUpdateDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(giftUpdateDTO))) .andExpect(status().is5xxServerError()); verify(giftService, times(1)).updateGift(eq("ABC123"), eq(999), any(GiftUpdateDTO.class)); } /** - * Verifica que la actualizacion se rechaza cuando los datos proporcionados son invalidos. + * Verifies that update is rejected when the provided data is invalid. */ @Test - @DisplayName("Update Gift - Datos inválidos") + @DisplayName("Update Gift - Invalid data") @WithMockUser void testUpdateGift_InvalidData() throws Exception { GiftUpdateDTO invalidDTO = GiftUpdateDTO.builder() - .name("A".repeat(105)) // Demasiado largo + .name("A".repeat(105)) // Too long .details("Test") .price(100.00) .build(); mockMvc.perform(put("/api/events/{eventCode}/gifts/{giftId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que la eliminacion de un regalo funciona correctamente. + * Verifies that deleting a gift works correctly. */ @Test - @DisplayName("Delete Gift - Eliminación exitosa") + @DisplayName("Delete Gift - Successful deletion") @WithMockUser void testDeleteGift_Success() throws Exception { when(giftService.deleteGift("ABC123", 1)) @@ -423,10 +428,10 @@ void testDeleteGift_Success() throws Exception { } /** - * Verifica que la eliminacion falla cuando el regalo no existe. + * Verifies that deletion fails when the gift does not exist. */ @Test - @DisplayName("Delete Gift - Regalo no encontrado") + @DisplayName("Delete Gift - Gift not found") @WithMockUser void testDeleteGift_NotFound() throws Exception { when(giftService.deleteGift("ABC123", 999)) @@ -439,10 +444,10 @@ void testDeleteGift_NotFound() throws Exception { } /** - * Verifica que la eliminacion falla cuando el regalo tiene contribuciones asociadas. + * Verifies that deletion fails when the gift has associated contributions. */ @Test - @DisplayName("Delete Gift - Regalo con contribuciones") + @DisplayName("Delete Gift - Gift with contributions") @WithMockUser void testDeleteGift_WithContributions() throws Exception { when(giftService.deleteGift("ABC123", 1)) @@ -455,18 +460,18 @@ void testDeleteGift_WithContributions() throws Exception { } /** - * Verifica que la creacion de una contribucion a un regalo funciona correctamente. + * Verifies that creating a contribution to a gift works correctly. */ @Test - @DisplayName("Create Contribution - Contribución exitosa") + @DisplayName("Create Contribution - Successful contribution") @WithMockUser void testCreateContribution_Success() throws Exception { when(giftService.createUpdateGiftContribution(eq("ABC123"), eq(1), any(UserGiftDTO.class))) .thenReturn(giftExtendedDTO); mockMvc.perform(post("/api/events/{eventCode}/gifts/{giftId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userGiftDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userGiftDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.giftId").value(1)); @@ -474,10 +479,10 @@ void testCreateContribution_Success() throws Exception { } /** - * Verifica el comportamiento de la contribucion cuando el monto es negativo. + * Verifies contribution behavior when the amount is negative. */ @Test - @DisplayName("Create Contribution - Monto negativo") + @DisplayName("Create Contribution - Negative amount") @WithMockUser void testCreateContribution_NegativeAmount() throws Exception { UserGiftDTO negativeDTO = UserGiftDTO.builder() @@ -486,37 +491,38 @@ void testCreateContribution_NegativeAmount() throws Exception { .build(); mockMvc.perform(post("/api/events/{eventCode}/gifts/{giftId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(negativeDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(negativeDTO))) .andExpect(status().isCreated()); } /** - * Verifica que la contribucion falla cuando el monto excede el precio objetivo del regalo. + * Verifies that the contribution fails when the amount exceeds the gift target + * price. */ @Test - @DisplayName("Create Contribution - Monto excede precio objetivo") + @DisplayName("Create Contribution - Amount exceeds target price") @WithMockUser void testCreateContribution_ExceedsTargetPrice() throws Exception { UserGiftDTO excessDTO = UserGiftDTO.builder() .userId(1) - .amount(200.00) // > targetPrice + .amount(200.00) // > targetPrice .build(); when(giftService.createUpdateGiftContribution(eq("ABC123"), eq(1), any(UserGiftDTO.class))) .thenThrow(new RuntimeException("Contribution exceeds remaining amount")); mockMvc.perform(post("/api/events/{eventCode}/gifts/{giftId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(excessDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(excessDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica que la contribucion falla cuando el usuario no existe. + * Verifies that the contribution fails when the user does not exist. */ @Test - @DisplayName("Create Contribution - Usuario no encontrado") + @DisplayName("Create Contribution - User not found") @WithMockUser void testCreateContribution_UserNotFound() throws Exception { UserGiftDTO invalidUserDTO = UserGiftDTO.builder() @@ -528,16 +534,16 @@ void testCreateContribution_UserNotFound() throws Exception { .thenThrow(new RuntimeException("User not found")); mockMvc.perform(post("/api/events/{eventCode}/gifts/{giftId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidUserDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidUserDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica que la contribucion falla cuando el monto es cero. + * Verifies that the contribution fails when the amount is zero. */ @Test - @DisplayName("Create Contribution - Monto cero") + @DisplayName("Create Contribution - Zero amount") @WithMockUser void testCreateContribution_ZeroAmount() throws Exception { UserGiftDTO zeroDTO = UserGiftDTO.builder() @@ -549,8 +555,8 @@ void testCreateContribution_ZeroAmount() throws Exception { .thenThrow(new RuntimeException("Contribution amount must be positive")); mockMvc.perform(post("/api/events/{eventCode}/gifts/{giftId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(zeroDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(zeroDTO))) .andExpect(status().is5xxServerError()); } } diff --git a/src/test/java/eventManager/web/controller/TicketControllerTest.java b/src/test/java/eventManager/web/controller/TicketControllerTest.java index a983868..5c5a64f 100644 --- a/src/test/java/eventManager/web/controller/TicketControllerTest.java +++ b/src/test/java/eventManager/web/controller/TicketControllerTest.java @@ -25,7 +25,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** - * Pruebas unitarias del controlador de tickets. Cubre obtencion de informacion del evento, listado de tickets, inscripcion y actualizacion. + * Unit tests for the ticket controller. Covers event information retrieval, + * ticket listing, enrollment, and updates. */ @WebMvcTest(TicketController.class) @AutoConfigureMockMvc(addFilters = false) @@ -38,13 +39,13 @@ class TicketControllerTest { @Autowired private ObjectMapper objectMapper; - @MockitoBean + @MockitoBean private TicketService ticketService; - @MockitoBean + @MockitoBean private JwtTokenProvider jwtTokenProvider; - @MockitoBean + @MockitoBean private UserDetailsService userDetailsService; private EventTicketDTO eventTicketDTO; @@ -57,17 +58,17 @@ class TicketControllerTest { void setUp() { // EventTicketDTO eventTicketDTO = new EventTicketDTO(); - EventDTO eventDTO = new EventDTO(); - eventDTO.setEventCode("MADRID"); - eventDTO.setEventName("Cena de Navidad"); - eventDTO.setDate(LocalDateTime.now().plusDays(7)); - TicketDTO eventTicket = new TicketDTO(); - eventTicket.setTicketId(1); - eventTicket.setRole("GUEST"); - eventTicket.setGuestNumber(2); - eventTicket.setAssistConfirmation(false); - eventTicketDTO.setEvent(eventDTO); - eventTicketDTO.setTicket(eventTicket); + EventDTO eventDTO = new EventDTO(); + eventDTO.setEventCode("MADRID"); + eventDTO.setEventName("Cena de Navidad"); + eventDTO.setDate(LocalDateTime.now().plusDays(7)); + TicketDTO eventTicket = new TicketDTO(); + eventTicket.setTicketId(1); + eventTicket.setRole("GUEST"); + eventTicket.setGuestNumber(2); + eventTicket.setAssistConfirmation(false); + eventTicketDTO.setEvent(eventDTO); + eventTicketDTO.setTicket(eventTicket); // TicketDTO ticketDTO = new TicketDTO(); @@ -94,7 +95,7 @@ void setUp() { .notes("Updated notes") .build(); - // Resultado paginado + // Paginated result paginationDTO = new ResultPaginationDTO(); paginationDTO.setData(new ArrayList<>()); PaginationDTO pageDTO = new PaginationDTO(); @@ -106,16 +107,16 @@ void setUp() { } /** - * Verifica que se obtiene la informacion del evento y ticket correctamente. + * Verifies that event and ticket information is retrieved correctly. */ @Test - @DisplayName("Get Event Information - Exitoso") + @DisplayName("Get Event Information - Success") @WithMockUser void testGetEventInformation_Success() throws Exception { when(ticketService.getEventInformation("MADRID", 1, 1)).thenReturn(eventTicketDTO); mockMvc.perform(get("/api/events/{eventCode}/tickets/{ticketId}", "MADRID", 1) - .param("userId", "1")) + .param("userId", "1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.event.eventCode").value("MADRID")) .andExpect(jsonPath("$.ticket.ticketId").value(1)) @@ -125,161 +126,174 @@ void testGetEventInformation_Success() throws Exception { } /** - * Verifica que la consulta de informacion falla cuando el evento no existe. + * Verifies that the information query fails when the event does not exist. */ @Test - @DisplayName("Get Event Information - Evento no encontrado") + @DisplayName("Get Event Information - Event not found") @WithMockUser void testGetEventInformation_EventNotFound() throws Exception { - when(ticketService.getEventInformation("NOTFOUND", 1, 1)).thenThrow(new RuntimeException("Event not found")); + when(ticketService.getEventInformation("NOTFOUND", 1, 1)) + .thenThrow(new RuntimeException("Event not found")); mockMvc.perform(get("/api/events/{eventCode}/tickets/{ticketId}", "NOTFOUND", 1) - .param("userId", "1")) + .param("userId", "1")) .andExpect(status().is5xxServerError()); verify(ticketService, times(1)).getEventInformation("NOTFOUND", 1, 1); } /** - * Verifica que la consulta de informacion falla cuando el ticket no existe. + * Verifies that the information query fails when the ticket does not exist. */ @Test - @DisplayName("Get Event Information - Ticket no encontrado") + @DisplayName("Get Event Information - Ticket not found") @WithMockUser void testGetEventInformation_TicketNotFound() throws Exception { - when(ticketService.getEventInformation("MADRID", 999, 1)).thenThrow(new RuntimeException("Ticket not found")); + when(ticketService.getEventInformation("MADRID", 999, 1)) + .thenThrow(new RuntimeException("Ticket not found")); mockMvc.perform(get("/api/events/{eventCode}/tickets/{ticketId}", "MADRID", 999) - .param("userId", "1")) + .param("userId", "1")) .andExpect(status().is5xxServerError()); verify(ticketService, times(1)).getEventInformation("MADRID", 999, 1); } /** - * Verifica que la consulta de informacion falla cuando el usuario no esta autorizado. + * Verifies that the information query fails when the user is not authorized. */ @Test - @DisplayName("Get Event Information - Usuario no autorizado") + @DisplayName("Get Event Information - User not authorized") @WithMockUser void testGetEventInformation_Unauthorized() throws Exception { - when(ticketService.getEventInformation("MADRID", 1, 999)).thenThrow(new RuntimeException("User not authorized")); + when(ticketService.getEventInformation("MADRID", 1, 999)) + .thenThrow(new RuntimeException("User not authorized")); mockMvc.perform(get("/api/events/{eventCode}/tickets/{ticketId}", "MADRID", 1) - .param("userId", "999")) + .param("userId", "999")) .andExpect(status().is5xxServerError()); verify(ticketService, times(1)).getEventInformation("MADRID", 1, 999); } /** - * Verifica que la consulta de informacion falla cuando no se proporciona el userId. + * Verifies that the information query fails when userId is not provided. */ @Test - @DisplayName("Get Event Information - UserId null") + @DisplayName("Get Event Information - UserId is null") @WithMockUser void testGetEventInformation_NullUserId() throws Exception { mockMvc.perform(get("/api/events/{eventCode}/tickets/{ticketId}", "MADRID", 1)) - .andExpect(status().is5xxServerError()); + .andExpect(status().is5xxServerError()); } /** - * Verifica que se listan los tickets de un evento correctamente con paginacion. + * Verifies that tickets for an event are listed correctly with pagination. */ @Test - @DisplayName("Get Event Tickets - Listar tickets exitosamente") + @DisplayName("Get Event Tickets - Successfully list tickets") @WithMockUser void testGetEventTickets_Success() throws Exception { - when(ticketService.getEventTickets(eq("MADRID"), anyInt(), anyInt(), anyString(), anyString(), anyString())).thenReturn(paginationDTO); + when(ticketService.getEventTickets(eq("MADRID"), anyInt(), anyInt(), anyString(), anyString(), + anyString())).thenReturn(paginationDTO); mockMvc.perform(get("/api/events/{eventCode}/tickets", "MADRID") - .param("page", "1") - .param("pageSize", "10") - .param("sortBy", "ticketId") - .param("sortDir", "ASC") - .param("search", "")) + .param("page", "1") + .param("pageSize", "10") + .param("sortBy", "ticketId") + .param("sortDir", "ASC") + .param("search", "")) .andExpect(status().isOk()) .andExpect(jsonPath("$.page.number").value(0)) .andExpect(jsonPath("$.page.size").value(10)); - verify(ticketService, times(1)).getEventTickets(eq("MADRID"), anyInt(), anyInt(), anyString(), anyString(), anyString()); + verify(ticketService, times(1)).getEventTickets(eq("MADRID"), anyInt(), anyInt(), anyString(), + anyString(), anyString()); } /** - * Verifica que el listado de tickets funciona correctamente con filtro de busqueda. + * Verifies that ticket listing works correctly with a search filter. */ @Test - @DisplayName("Get Event Tickets - Con búsqueda") + @DisplayName("Get Event Tickets - With search") @WithMockUser void testGetEventTickets_WithSearch() throws Exception { - when(ticketService.getEventTickets(eq("MADRID"), anyInt(), nullable(Integer.class), anyString(), anyString(), eq("Carlos"))).thenReturn(paginationDTO); + when(ticketService.getEventTickets(eq("MADRID"), anyInt(), nullable(Integer.class), anyString(), + anyString(), eq("Carlos"))).thenReturn(paginationDTO); mockMvc.perform(get("/api/events/{eventCode}/tickets", "MADRID") - .param("page", "1") - .param("search", "Carlos")) + .param("page", "1") + .param("search", "Carlos")) .andExpect(status().isOk()); - verify(ticketService, times(1)).getEventTickets(eq("MADRID"), anyInt(), nullable(Integer.class), anyString(), anyString(), eq("Carlos")); + verify(ticketService, times(1)).getEventTickets(eq("MADRID"), anyInt(), nullable(Integer.class), + anyString(), anyString(), eq("Carlos")); } /** - * Verifica que el listado de tickets falla cuando el evento no existe. + * Verifies that ticket listing fails when the event does not exist. */ @Test - @DisplayName("Get Event Tickets - Evento no encontrado") + @DisplayName("Get Event Tickets - Event not found") @WithMockUser void testGetEventTickets_EventNotFound() throws Exception { - when(ticketService.getEventTickets(eq("NOTFND"), anyInt(), nullable(Integer.class), anyString(), anyString(), nullable(String.class))).thenThrow(new RuntimeException("Event not found")); + when(ticketService.getEventTickets(eq("NOTFND"), anyInt(), nullable(Integer.class), anyString(), + anyString(), nullable(String.class))) + .thenThrow(new RuntimeException("Event not found")); mockMvc.perform(get("/api/events/{eventCode}/tickets", "NOTFND") - .param("page", "1")) + .param("page", "1")) .andExpect(status().is5xxServerError()); - verify(ticketService, times(1)).getEventTickets(eq("NOTFND"), anyInt(), nullable(Integer.class), anyString(), anyString(), nullable(String.class)); + verify(ticketService, times(1)).getEventTickets(eq("NOTFND"), anyInt(), nullable(Integer.class), + anyString(), anyString(), nullable(String.class)); } /** - * Verifica el comportamiento del listado de tickets cuando se proporciona una pagina invalida. + * Verifies ticket listing behavior when an invalid page is provided. */ @Test - @DisplayName("Get Event Tickets - Página inválida") + @DisplayName("Get Event Tickets - Invalid page") @WithMockUser void testGetEventTickets_InvalidPage() throws Exception { mockMvc.perform(get("/api/events/{eventCode}/tickets", "ABC123") - .param("page", "-1")) - .andExpect(status().is5xxServerError()); + .param("page", "-1")) + .andExpect(status().is5xxServerError()); } /** - * Verifica que el listado de tickets funciona correctamente al ordenar por rol de forma descendente. + * Verifies that ticket listing works correctly when sorting by role in + * descending order. */ @Test - @DisplayName("Get Event Tickets - Ordenar por rol") + @DisplayName("Get Event Tickets - Sort by role") @WithMockUser void testGetEventTickets_SortByRole() throws Exception { - when(ticketService.getEventTickets(eq("ABC123"), anyInt(), nullable(Integer.class), eq("role"), eq("DESC"), anyString())).thenReturn(paginationDTO); + when(ticketService.getEventTickets(eq("ABC123"), anyInt(), nullable(Integer.class), eq("role"), + eq("DESC"), anyString())).thenReturn(paginationDTO); mockMvc.perform(get("/api/events/{eventCode}/tickets", "ABC123") - .param("page", "1") - .param("sortBy", "role") - .param("sortDir", "DESC")) + .param("page", "1") + .param("sortBy", "role") + .param("sortDir", "DESC")) .andExpect(status().isOk()); - verify(ticketService, times(1)).getEventTickets(eq("ABC123"), anyInt(), nullable(Integer.class), eq("role"), eq("DESC"), nullable(String.class)); + verify(ticketService, times(1)).getEventTickets(eq("ABC123"), anyInt(), nullable(Integer.class), + eq("role"), eq("DESC"), nullable(String.class)); } /** - * Verifica que la inscripcion de un usuario en un evento funciona correctamente. + * Verifies that enrolling a user in an event works correctly. */ @Test - @DisplayName("Enroll User - Inscripción exitosa") + @DisplayName("Enroll User - Successful enrollment") @WithMockUser void testEnrollUser_Success() throws Exception { when(ticketService.enrollUserInEvent(any(EnrollUserDTO.class))).thenReturn(ticketDTO); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(enrollUserDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(enrollUserDTO))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.ticketId").value(1)) .andExpect(jsonPath("$.role").value("GUEST")); @@ -288,104 +302,107 @@ void testEnrollUser_Success() throws Exception { } /** - * Verifica que la inscripcion falla cuando el usuario ya esta inscrito en el evento. + * Verifies that enrollment fails when the user is already enrolled in the + * event. */ @Test - @DisplayName("Enroll User - Usuario ya inscrito") + @DisplayName("Enroll User - User already enrolled") @WithMockUser void testEnrollUser_AlreadyEnrolled() throws Exception { - when(ticketService.enrollUserInEvent(any(EnrollUserDTO.class))).thenThrow(new RuntimeException("User already enrolled in this event")); + when(ticketService.enrollUserInEvent(any(EnrollUserDTO.class))) + .thenThrow(new RuntimeException("User already enrolled in this event")); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(enrollUserDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(enrollUserDTO))) .andExpect(status().is5xxServerError()); verify(ticketService, times(1)).enrollUserInEvent(any(EnrollUserDTO.class)); } /** - * Verifica que la inscripcion falla cuando el evento esta lleno. + * Verifies that enrollment fails when the event is full. */ @Test - @DisplayName("Enroll User - Evento lleno") + @DisplayName("Enroll User - Event full") @WithMockUser void testEnrollUser_EventFull() throws Exception { - when(ticketService.enrollUserInEvent(any(EnrollUserDTO.class))).thenThrow(new RuntimeException("Event is at full capacity")); + when(ticketService.enrollUserInEvent(any(EnrollUserDTO.class))) + .thenThrow(new RuntimeException("Event is at full capacity")); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(enrollUserDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(enrollUserDTO))) .andExpect(status().is5xxServerError()); verify(ticketService, times(1)).enrollUserInEvent(any(EnrollUserDTO.class)); } /** - * Verifica que la inscripcion se rechaza cuando el codigo de evento es invalido. + * Verifies that enrollment is rejected when the event code is invalid. */ @Test - @DisplayName("Enroll User - Código de evento inválido") + @DisplayName("Enroll User - Invalid event code") @WithMockUser void testEnrollUser_InvalidEventCode() throws Exception { EnrollUserDTO invalidDTO = EnrollUserDTO.builder() .userId(1) - .eventCode("abc") // Código inválido (debe ser 6 caracteres mayúsculas) + .eventCode("abc") // Invalid code (must be 6 uppercase characters) .role("GUEST") .guestNumber(2) .build(); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica que la inscripcion se rechaza cuando el formato del codigo de evento es incorrecto. + * Verifies that enrollment is rejected when the event code format is incorrect. */ @Test - @DisplayName("Enroll User - Código de evento formato incorrecto") + @DisplayName("Enroll User - Event code wrong format") @WithMockUser void testEnrollUser_WrongFormatEventCode() throws Exception { EnrollUserDTO wrongFormatDTO = EnrollUserDTO.builder() .userId(1) - .eventCode("abc123") // No es mayúsculas + .eventCode("abc123") // Not uppercase .role("GUEST") .guestNumber(2) .build(); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(wrongFormatDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(wrongFormatDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica el comportamiento de la inscripcion cuando se proporciona un numero de invitados negativo. + * Verifies enrollment behavior when a negative guest count is provided. */ @Test - @DisplayName("Enroll User - Número de invitados negativo") + @DisplayName("Enroll User - Negative guest number") @WithMockUser void testEnrollUser_NegativeGuestNumber() throws Exception { EnrollUserDTO negativeGuestsDTO = EnrollUserDTO.builder() .userId(1) .eventCode("ABC123") .role("GUEST") - .guestNumber(-1) // Número negativo + .guestNumber(-1) // Negative number .build(); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(negativeGuestsDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(negativeGuestsDTO))) .andExpect(status().isCreated()); } /** - * Verifica que la inscripcion falla cuando el usuario no existe. + * Verifies that enrollment fails when the user does not exist. */ @Test - @DisplayName("Enroll User - Usuario no encontrado") + @DisplayName("Enroll User - User not found") @WithMockUser void testEnrollUser_UserNotFound() throws Exception { EnrollUserDTO invalidUserDTO = EnrollUserDTO.builder() @@ -399,18 +416,18 @@ void testEnrollUser_UserNotFound() throws Exception { .thenThrow(new RuntimeException("User not found")); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidUserDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidUserDTO))) .andExpect(status().is5xxServerError()); verify(ticketService, times(1)).enrollUserInEvent(any(EnrollUserDTO.class)); } /** - * Verifica que la inscripcion falla cuando el evento no existe. + * Verifies that enrollment fails when the event does not exist. */ @Test - @DisplayName("Enroll User - Evento no encontrado") + @DisplayName("Enroll User - Event not found") @WithMockUser void testEnrollUser_EventNotFound() throws Exception { EnrollUserDTO invalidEventDTO = EnrollUserDTO.builder() @@ -424,18 +441,18 @@ void testEnrollUser_EventNotFound() throws Exception { .thenThrow(new RuntimeException("Event not found")); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidEventDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidEventDTO))) .andExpect(status().is5xxServerError()); verify(ticketService, times(1)).enrollUserInEvent(any(EnrollUserDTO.class)); } /** - * Verifica que la inscripcion falla cuando se proporciona un rol invalido. + * Verifies that enrollment fails when an invalid role is provided. */ @Test - @DisplayName("Enroll User - Rol inválido") + @DisplayName("Enroll User - Invalid role") @WithMockUser void testEnrollUser_InvalidRole() throws Exception { EnrollUserDTO invalidRoleDTO = EnrollUserDTO.builder() @@ -449,24 +466,24 @@ void testEnrollUser_InvalidRole() throws Exception { .thenThrow(new RuntimeException("Invalid role")); mockMvc.perform(post("/api/events/enrollment") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidRoleDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRoleDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica que la actualizacion de un ticket funciona correctamente con datos validos. + * Verifies that ticket update works correctly with valid data. */ @Test - @DisplayName("Update Ticket - Actualización exitosa") + @DisplayName("Update Ticket - Successful update") @WithMockUser void testUpdateTicket_Success() throws Exception { when(ticketService.updateTicket(eq("ABC123"), eq(1), any(UpdateTicketDTO.class))) .thenReturn(eventTicketDTO); mockMvc.perform(put("/api/events/{eventCode}/tickets/{ticketId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateTicketDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateTicketDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("$.ticket.ticketId").value(1)); @@ -474,28 +491,28 @@ void testUpdateTicket_Success() throws Exception { } /** - * Verifica que la actualizacion falla cuando el ticket no existe. + * Verifies that update fails when the ticket does not exist. */ @Test - @DisplayName("Update Ticket - Ticket no encontrado") + @DisplayName("Update Ticket - Ticket not found") @WithMockUser void testUpdateTicket_NotFound() throws Exception { when(ticketService.updateTicket(eq("ABC123"), eq(999), any(UpdateTicketDTO.class))) .thenThrow(new RuntimeException("Ticket not found")); mockMvc.perform(put("/api/events/{eventCode}/tickets/{ticketId}", "ABC123", 999) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(updateTicketDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateTicketDTO))) .andExpect(status().is5xxServerError()); verify(ticketService, times(1)).updateTicket(eq("ABC123"), eq(999), any(UpdateTicketDTO.class)); } /** - * Verifica que se puede cambiar el rol de un ticket a HOST correctamente. + * Verifies that a ticket role can be changed to HOST correctly. */ @Test - @DisplayName("Update Ticket - Cambiar rol a HOST") + @DisplayName("Update Ticket - Change role to HOST") @WithMockUser void testUpdateTicket_ChangeToHost() throws Exception { UpdateTicketDTO hostDTO = UpdateTicketDTO.builder() @@ -508,18 +525,18 @@ void testUpdateTicket_ChangeToHost() throws Exception { .thenReturn(eventTicketDTO); mockMvc.perform(put("/api/events/{eventCode}/tickets/{ticketId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(hostDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(hostDTO))) .andExpect(status().isOk()); verify(ticketService, times(1)).updateTicket(eq("ABC123"), eq(1), any(UpdateTicketDTO.class)); } /** - * Verifica que se puede marcar la confirmacion de asistencia en un ticket. + * Verifies that attendance confirmation can be marked on a ticket. */ @Test - @DisplayName("Update Ticket - Marcar asistencia") + @DisplayName("Update Ticket - Mark attendance") @WithMockUser void testUpdateTicket_MarkAttendance() throws Exception { UpdateTicketDTO attendanceDTO = UpdateTicketDTO.builder() @@ -533,18 +550,18 @@ void testUpdateTicket_MarkAttendance() throws Exception { .thenReturn(eventTicketDTO); mockMvc.perform(put("/api/events/{eventCode}/tickets/{ticketId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(attendanceDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(attendanceDTO))) .andExpect(status().isOk()); verify(ticketService, times(1)).updateTicket(eq("ABC123"), eq(1), any(UpdateTicketDTO.class)); } /** - * Verifica que se pueden actualizar las notas de un ticket correctamente. + * Verifies that ticket notes can be updated correctly. */ @Test - @DisplayName("Update Ticket - Actualizar notas") + @DisplayName("Update Ticket - Update notes") @WithMockUser void testUpdateTicket_UpdateNotes() throws Exception { UpdateTicketDTO notesDTO = UpdateTicketDTO.builder() @@ -558,38 +575,38 @@ void testUpdateTicket_UpdateNotes() throws Exception { .thenReturn(eventTicketDTO); mockMvc.perform(put("/api/events/{eventCode}/tickets/{ticketId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(notesDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(notesDTO))) .andExpect(status().isOk()); verify(ticketService, times(1)).updateTicket(eq("ABC123"), eq(1), any(UpdateTicketDTO.class)); } /** - * Verifica que la actualizacion se rechaza cuando las notas exceden los 500 caracteres. + * Verifies that update is rejected when notes exceed 500 characters. */ @Test - @DisplayName("Update Ticket - Notas demasiado largas") + @DisplayName("Update Ticket - Notes too long") @WithMockUser void testUpdateTicket_NotesTooLong() throws Exception { UpdateTicketDTO longNotesDTO = UpdateTicketDTO.builder() .role("GUEST") .guestNumber(2) .assistConfirmation(false) - .notes("A".repeat(505)) // > 500 caracteres + .notes("A".repeat(505)) // > 500 characters .build(); mockMvc.perform(put("/api/events/{eventCode}/tickets/{ticketId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(longNotesDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(longNotesDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica el comportamiento de la actualizacion cuando el numero de invitados es negativo. + * Verifies update behavior when the guest number is negative. */ @Test - @DisplayName("Update Ticket - Número de invitados negativo") + @DisplayName("Update Ticket - Negative guest number") @WithMockUser void testUpdateTicket_NegativeGuestNumber() throws Exception { UpdateTicketDTO negativeDTO = UpdateTicketDTO.builder() @@ -599,16 +616,16 @@ void testUpdateTicket_NegativeGuestNumber() throws Exception { .build(); mockMvc.perform(put("/api/events/{eventCode}/tickets/{ticketId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(negativeDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(negativeDTO))) .andExpect(status().isOk()); } /** - * Verifica que la actualizacion falla cuando se proporciona un rol invalido. + * Verifies that update fails when an invalid role is provided. */ @Test - @DisplayName("Update Ticket - Rol inválido") + @DisplayName("Update Ticket - Invalid role") @WithMockUser void testUpdateTicket_InvalidRole() throws Exception { UpdateTicketDTO invalidRoleDTO = UpdateTicketDTO.builder() @@ -621,8 +638,8 @@ void testUpdateTicket_InvalidRole() throws Exception { .thenThrow(new RuntimeException("Invalid role")); mockMvc.perform(put("/api/events/{eventCode}/tickets/{ticketId}", "ABC123", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidRoleDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRoleDTO))) .andExpect(status().is5xxServerError()); } } diff --git a/src/test/java/eventManager/web/controller/UserControllerTest.java b/src/test/java/eventManager/web/controller/UserControllerTest.java index c4f8a24..45823d4 100644 --- a/src/test/java/eventManager/web/controller/UserControllerTest.java +++ b/src/test/java/eventManager/web/controller/UserControllerTest.java @@ -24,7 +24,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** - * Pruebas unitarias del controlador de usuarios. Cubre perfil autenticado, obtencion de usuario, actualizacion y cambio de contrasena. + * Unit tests for the user controller. Covers authenticated profile, user + * retrieval, updates, and password changes. */ @WebMvcTest(UserController.class) @AutoConfigureMockMvc(addFilters = false) @@ -37,13 +38,13 @@ class UserControllerTest { @Autowired private ObjectMapper objectMapper; - @MockitoBean + @MockitoBean private UserService userService; - @MockitoBean + @MockitoBean private JwtTokenProvider jwtTokenProvider; - @MockitoBean + @MockitoBean private UserDetailsService userDetailsService; private UserDTO userDTO; @@ -52,7 +53,7 @@ class UserControllerTest { @BeforeEach void setUp() { - // UserDTO de respuesta + // Response UserDTO userDTO = new UserDTO(); userDTO.setUserId(1); userDTO.setEmail("carlos.martinez@eventmanager.es"); @@ -61,14 +62,14 @@ void setUp() { userDTO.setLastName("Martinez"); userDTO.setPhoneNumber("612345678"); - // DTO para actualizar usuario + // DTO for user update userUpdateDTO = UserUpdateDTO.builder() .firstName("Updated") .lastName("Name") .phoneNumber("698765432") .build(); - // DTO para cambiar contraseña + // DTO for password change userPasswordDTO = UserPasswordDTO.builder() .password("ClaveAnterior2024") .newPassword("ClaveNueva2025") @@ -76,13 +77,13 @@ void setUp() { } /** - * Verifica que se obtiene correctamente el perfil del usuario autenticado. + * Verifies that the authenticated user profile is retrieved correctly. */ @Test - @DisplayName("Get Authenticated User Profile - Exitoso") - @WithMockUser(username = "carlos.martinez") + @DisplayName("Get Authenticated User Profile - Success") + @WithMockUser(username = "carlos.martinez") void testGetAuthenticatedUserProfile_Success() throws Exception { - when(userService.getUserInformationByUsername("carlos.martinez")).thenReturn(userDTO); + when(userService.getUserInformationByUsername("carlos.martinez")).thenReturn(userDTO); mockMvc.perform(get("/api/user/profile")) .andExpect(status().isOk()) @@ -94,26 +95,26 @@ void testGetAuthenticatedUserProfile_Success() throws Exception { } /** - * Verifica que la obtencion del perfil falla cuando el usuario no esta autenticado. + * Verifies that profile retrieval fails when the user is not authenticated. */ @Test - @DisplayName("Get Authenticated User Profile - Usuario no autenticado") + @DisplayName("Get Authenticated User Profile - User not authenticated") void testGetAuthenticatedUserProfile_NotAuthenticated() throws Exception { mockMvc.perform(get("/api/user/profile")) .andExpect(status().is5xxServerError()); } /** - * Verifica que se obtiene correctamente un usuario por su username. + * Verifies that a user is retrieved correctly by username. */ @Test - @DisplayName("Get User By Username - Exitoso") + @DisplayName("Get User By Username - Success") @WithMockUser void testGetUserByUsername_Success() throws Exception { when(userService.getUserInformationByUsername("carlos.martinez")).thenReturn(userDTO); mockMvc.perform(get("/api/user") - .param("username", "carlos.martinez")) + .param("username", "carlos.martinez")) .andExpect(status().isOk()) .andExpect(jsonPath("$.username").value("carlos.martinez")) .andExpect(jsonPath("$.email").value("carlos.martinez@eventmanager.es")); @@ -122,26 +123,27 @@ void testGetUserByUsername_Success() throws Exception { } /** - * Verifica que la busqueda por username falla cuando el usuario no existe. + * Verifies that lookup by username fails when the user does not exist. */ @Test - @DisplayName("Get User By Username - Usuario no encontrado") + @DisplayName("Get User By Username - User not found") @WithMockUser void testGetUserByUsername_NotFound() throws Exception { - when(userService.getUserInformationByUsername("nonexistent")).thenThrow(new RuntimeException("User not found")); + when(userService.getUserInformationByUsername("nonexistent")) + .thenThrow(new RuntimeException("User not found")); mockMvc.perform(get("/api/user") - .param("username", "nonexistent")) + .param("username", "nonexistent")) .andExpect(status().is5xxServerError()); verify(userService, times(1)).getUserInformationByUsername("nonexistent"); } /** - * Verifica que la busqueda por username falla cuando no se proporciona el parametro. + * Verifies that lookup by username fails when the parameter is not provided. */ @Test - @DisplayName("Get User By Username - Username null") + @DisplayName("Get User By Username - Username is null") @WithMockUser void testGetUserByUsername_NullUsername() throws Exception { mockMvc.perform(get("/api/user")) @@ -149,10 +151,10 @@ void testGetUserByUsername_NullUsername() throws Exception { } /** - * Verifica que se obtiene correctamente un usuario por su identificador. + * Verifies that a user is retrieved correctly by ID. */ @Test - @DisplayName("Get User By ID - Exitoso") + @DisplayName("Get User By ID - Success") @WithMockUser void testGetUserById_Success() throws Exception { when(userService.getUserInformation(1)).thenReturn(userDTO); @@ -166,10 +168,10 @@ void testGetUserById_Success() throws Exception { } /** - * Verifica que la busqueda por identificador falla cuando el usuario no existe. + * Verifies that lookup by ID fails when the user does not exist. */ @Test - @DisplayName("Get User By ID - Usuario no encontrado") + @DisplayName("Get User By ID - User not found") @WithMockUser void testGetUserById_NotFound() throws Exception { when(userService.getUserInformation(999)).thenThrow(new RuntimeException("User not found")); @@ -181,10 +183,10 @@ void testGetUserById_NotFound() throws Exception { } /** - * Verifica que la busqueda por identificador falla cuando se proporciona un ID negativo. + * Verifies that lookup by ID fails when a negative ID is provided. */ @Test - @DisplayName("Get User By ID - ID inválido (negativo)") + @DisplayName("Get User By ID - Invalid ID (negative)") @WithMockUser void testGetUserById_InvalidId() throws Exception { when(userService.getUserInformation(-1)).thenThrow(new RuntimeException("Invalid user ID")); @@ -196,10 +198,10 @@ void testGetUserById_InvalidId() throws Exception { } /** - * Verifica que la actualizacion de un usuario funciona correctamente con datos validos. + * Verifies that user update works correctly with valid data. */ @Test - @DisplayName("Update User - Actualización exitosa") + @DisplayName("Update User - Successful update") @WithMockUser void testUpdateUser_Success() throws Exception { UserDTO updatedUserDTO = new UserDTO(); @@ -211,8 +213,8 @@ void testUpdateUser_Success() throws Exception { when(userService.updateUser(eq(1), any(UserUpdateDTO.class))).thenReturn(updatedUserDTO); mockMvc.perform(put("/api/user/{userId}", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userUpdateDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userUpdateDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("$.userId").value(1)) .andExpect(jsonPath("$.firstName").value("Updated")) @@ -222,46 +224,47 @@ void testUpdateUser_Success() throws Exception { } /** - * Verifica que la actualizacion falla cuando el usuario no existe. + * Verifies that update fails when the user does not exist. */ @Test - @DisplayName("Update User - Usuario no encontrado") + @DisplayName("Update User - User not found") @WithMockUser void testUpdateUser_NotFound() throws Exception { - when(userService.updateUser(eq(999), any(UserUpdateDTO.class))).thenThrow(new RuntimeException("User not found")); + when(userService.updateUser(eq(999), any(UserUpdateDTO.class))) + .thenThrow(new RuntimeException("User not found")); mockMvc.perform(put("/api/user/{userId}", 999) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userUpdateDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userUpdateDTO))) .andExpect(status().is5xxServerError()); verify(userService, times(1)).updateUser(eq(999), any(UserUpdateDTO.class)); } /** - * Verifica que la actualizacion se rechaza cuando el nombre es demasiado largo. + * Verifies that update is rejected when the first name is too long. */ @Test - @DisplayName("Update User - Datos inválidos (firstName demasiado largo)") + @DisplayName("Update User - Invalid data (first name too long)") @WithMockUser void testUpdateUser_InvalidData() throws Exception { UserUpdateDTO invalidDTO = UserUpdateDTO.builder() - .firstName("A".repeat(25)) // > 20 caracteres + .firstName("A".repeat(25)) // > 20 characters .lastName("User") .phoneNumber("612345678") .build(); mockMvc.perform(put("/api/user/{userId}", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica el comportamiento de la actualizacion cuando los campos estan vacios. + * Verifies update behavior when fields are empty. */ @Test - @DisplayName("Update User - Campos vacíos") + @DisplayName("Update User - Empty fields") @WithMockUser void testUpdateUser_EmptyFields() throws Exception { UserUpdateDTO emptyDTO = UserUpdateDTO.builder() @@ -271,16 +274,16 @@ void testUpdateUser_EmptyFields() throws Exception { .build(); mockMvc.perform(put("/api/user/{userId}", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(emptyDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(emptyDTO))) .andExpect(status().isOk()); } /** - * Verifica que la actualizacion falla cuando el formato del numero de telefono es invalido. + * Verifies that update fails when the phone number format is invalid. */ @Test - @DisplayName("Update User - Número de teléfono inválido") + @DisplayName("Update User - Invalid phone number") @WithMockUser void testUpdateUser_InvalidPhoneNumber() throws Exception { UserUpdateDTO invalidPhoneDTO = UserUpdateDTO.builder() @@ -289,26 +292,27 @@ void testUpdateUser_InvalidPhoneNumber() throws Exception { .phoneNumber("invalid-phone") .build(); - when(userService.updateUser(eq(1), any(UserUpdateDTO.class))).thenThrow(new RuntimeException("Invalid phone number format")); + when(userService.updateUser(eq(1), any(UserUpdateDTO.class))) + .thenThrow(new RuntimeException("Invalid phone number format")); mockMvc.perform(put("/api/user/{userId}", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidPhoneDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidPhoneDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica que el cambio de contrasena funciona correctamente con datos validos. + * Verifies that password change works correctly with valid data. */ @Test - @DisplayName("Update Password - Cambio exitoso") + @DisplayName("Update Password - Successful change") @WithMockUser void testUpdatePassword_Success() throws Exception { when(userService.updateUserPassword(eq(1), any(UserPasswordDTO.class))).thenReturn(userDTO); mockMvc.perform(put("/api/user/{userId}/change-password", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userPasswordDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userPasswordDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("$.userId").value(1)); @@ -316,63 +320,65 @@ void testUpdatePassword_Success() throws Exception { } /** - * Verifica que el cambio de contrasena falla cuando la contrasena antigua es incorrecta. + * Verifies that password change fails when the old password is incorrect. */ @Test - @DisplayName("Update Password - Contraseña antigua incorrecta") + @DisplayName("Update Password - Incorrect old password") @WithMockUser void testUpdatePassword_WrongOldPassword() throws Exception { - when(userService.updateUserPassword(eq(1), any(UserPasswordDTO.class))).thenThrow(new RuntimeException("Old password is incorrect")); + when(userService.updateUserPassword(eq(1), any(UserPasswordDTO.class))) + .thenThrow(new RuntimeException("Old password is incorrect")); mockMvc.perform(put("/api/user/{userId}/change-password", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userPasswordDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userPasswordDTO))) .andExpect(status().is5xxServerError()); verify(userService, times(1)).updateUserPassword(eq(1), any(UserPasswordDTO.class)); } /** - * Verifica el comportamiento del cambio de contrasena cuando la nueva es demasiado corta. + * Verifies password change behavior when the new password is too short. */ @Test - @DisplayName("Update Password - Nueva contraseña demasiado corta") + @DisplayName("Update Password - New password too short") @WithMockUser void testUpdatePassword_NewPasswordTooShort() throws Exception { UserPasswordDTO shortPasswordDTO = UserPasswordDTO.builder() .password("ClaveAnterior2024") - .newPassword("123") // Muy corta + .newPassword("123") // Too short .build(); mockMvc.perform(put("/api/user/{userId}/change-password", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(shortPasswordDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(shortPasswordDTO))) .andExpect(status().isOk()); } /** - * Verifica que se rechaza el cambio de contrasena cuando la nueva excede los 25 caracteres. + * Verifies that password change is rejected when the new password exceeds 25 + * characters. */ @Test - @DisplayName("Update Password - Nueva contraseña demasiado larga") + @DisplayName("Update Password - New password too long") @WithMockUser void testUpdatePassword_NewPasswordTooLong() throws Exception { UserPasswordDTO longPasswordDTO = UserPasswordDTO.builder() .password("ClaveAnterior2024") - .newPassword("A".repeat(30)) // > 25 caracteres + .newPassword("A".repeat(30)) // > 25 characters .build(); mockMvc.perform(put("/api/user/{userId}/change-password", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(longPasswordDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(longPasswordDTO))) .andExpect(status().isBadRequest()); } /** - * Verifica el comportamiento del cambio de contrasena cuando los campos son null. + * Verifies password change behavior when fields are null. */ @Test - @DisplayName("Update Password - Campos null") + @DisplayName("Update Password - Fields are null") @WithMockUser void testUpdatePassword_NullFields() throws Exception { UserPasswordDTO nullFieldsDTO = UserPasswordDTO.builder() @@ -381,16 +387,17 @@ void testUpdatePassword_NullFields() throws Exception { .build(); mockMvc.perform(put("/api/user/{userId}/change-password", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(nullFieldsDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(nullFieldsDTO))) .andExpect(status().isOk()); } /** - * Verifica que el cambio de contrasena falla cuando la nueva es igual a la antigua. + * Verifies that password change fails when the new password is the same as the + * old one. */ @Test - @DisplayName("Update Password - Nueva contraseña igual a la antigua") + @DisplayName("Update Password - New password same as old") @WithMockUser void testUpdatePassword_SameAsOldPassword() throws Exception { UserPasswordDTO samePasswordDTO = UserPasswordDTO.builder() @@ -398,26 +405,28 @@ void testUpdatePassword_SameAsOldPassword() throws Exception { .newPassword("ClaveSegura2025") .build(); - when(userService.updateUserPassword(eq(1), any(UserPasswordDTO.class))).thenThrow(new RuntimeException("New password must be different from old password")); + when(userService.updateUserPassword(eq(1), any(UserPasswordDTO.class))) + .thenThrow(new RuntimeException("New password must be different from old password")); mockMvc.perform(put("/api/user/{userId}/change-password", 1) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(samePasswordDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(samePasswordDTO))) .andExpect(status().is5xxServerError()); } /** - * Verifica que el cambio de contrasena falla cuando el usuario no existe. + * Verifies that password change fails when the user does not exist. */ @Test - @DisplayName("Update Password - Usuario no encontrado") + @DisplayName("Update Password - User not found") @WithMockUser void testUpdatePassword_UserNotFound() throws Exception { - when(userService.updateUserPassword(eq(999), any(UserPasswordDTO.class))).thenThrow(new RuntimeException("User not found")); + when(userService.updateUserPassword(eq(999), any(UserPasswordDTO.class))) + .thenThrow(new RuntimeException("User not found")); mockMvc.perform(put("/api/user/{userId}/change-password", 999) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userPasswordDTO))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userPasswordDTO))) .andExpect(status().is5xxServerError()); verify(userService, times(1)).updateUserPassword(eq(999), any(UserPasswordDTO.class));