Commit e1465448 by Rubén Ramírez

fix: [TestRecursosController]:Mejorados todos los tests para hacer todas las comprobaciones

parent 4879cf8d
package com.ujaen.tfg.mangaffinity.rest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ujaen.tfg.mangaffinity.MangAffinityApplication;
import com.ujaen.tfg.mangaffinity.config.JpaTestConfig;
import com.ujaen.tfg.mangaffinity.entidades.Genero;
......@@ -11,50 +9,31 @@ import com.ujaen.tfg.mangaffinity.rest.DTO.DTOCapitulo;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTOFuenteCapitulo;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTOLoginRespuesta;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTORecurso;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.core.io.ByteArrayResource;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.time.LocalDate;
import java.util.*;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(classes = {MangAffinityApplication.class, JpaTestConfig.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class TestRecursosController {
@Autowired
private TestRestTemplate restTemplate;
private static final Logger logger = LoggerFactory.getLogger(TestRecursosController.class);
@Test
@DirtiesContext
......@@ -73,12 +52,12 @@ public class TestRecursosController {
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Caso 1: Intento crear un recurso sin autenticación
Recurso nuevoRecurso = new Recurso("Titulo Prueba", "Descripción de prueba", LocalDate.now(), "Autor Prueba", "");
// Intento crear un recurso sin autenticación
Recurso nuevoRecurso = new Recurso("Titulo Prueba", "Descripción de prueba", LocalDate.now(), "Autor Prueba", new byte[0]);
var respuestaNoAutenticado = restTemplate.postForEntity("/recursos/", nuevoRecurso, Void.class);
assertThat(respuestaNoAutenticado.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
// Caso 2: Intento crear un recurso correctamente con autenticación de admin
//Intento crear un recurso correctamente con autenticación de admin
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
formData.add("recurso", new DTORecurso(null, "Titulo Prueba", "Descripción de prueba", LocalDate.now(), "Autor Prueba", null, new HashSet<>()));
formData.add("foto", new ByteArrayResource(new byte[0]) {
......@@ -98,59 +77,106 @@ public class TestRecursosController {
@Test
@DirtiesContext
void testBuscarPorTitulo() {
// Registro de un administrador
void testObtenerCapitulosPorTipo() {
// Registro del admin
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
"contrasenia", "adminpassword"
), Void.class);
// Inicio sesión con el administrador
// Login del administrador
var authResponse = restTemplate.postForEntity("/usuarios/admin@example.com", Map.of(
"clave", "adminpassword"
), DTOLoginRespuesta.class);
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Creo un recurso
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
formData.add("recurso", new DTORecurso(null, "One Piece", "Manga de aventuras", LocalDate.of(1997, 7, 22), "Eiichiro Oda", null, new HashSet<>()));
formData.add("foto", new ByteArrayResource(new byte[0]) { // Imagen vacía
DTORecurso dtoRecurso = new DTORecurso(
null, "Recurso con tipos", "Desc", LocalDate.now(), "Autor", null, new HashSet<>()
);
formData.add("recurso", dtoRecurso);
formData.add("foto", new ByteArrayResource(new byte[0]) {
@Override
public String getFilename() {
return "dummy.jpg";
}
});
// Creo un recurso en la BD con autenticación
Recurso recurso = new Recurso("One Piece", "Manga de aventuras", LocalDate.of(1997, 7, 22), "Eiichiro Oda", "");
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(formData, headers);
var respuestaAdmin = restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
assertThat(respuestaAdmin.getStatusCode()).isEqualTo(HttpStatus.CREATED);
restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
// Caso 1: Busco por título que si encontraremos
ResponseEntity<DTORecurso[]> respuesta = restTemplate.exchange(
"/recursos/titulo/{titulo}", HttpMethod.GET, null, DTORecurso[].class, "One Piece"
// Busco el recurso por título
var resultado = restTemplate.getForEntity("/recursos/titulo/Recurso con tipos", DTORecurso[].class);
assertThat(resultado.getStatusCode()).isEqualTo(HttpStatus.OK);
Long recursoId = resultado.getBody()[0].getId();
// Añadir capítulos MANGA y ANIME
HttpHeaders jsonHeaders = new HttpHeaders();
jsonHeaders.set("Authorization", token);
jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
DTOCapitulo cap1 = new DTOCapitulo(null, 1, "Capítulo Manga", TipoRecurso.MANGA,
List.of(new DTOFuenteCapitulo("FuenteM", "https://manga.com")),
true
);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuesta.getBody()).isNotEmpty();
assertThat(respuesta.getBody()[0].getTitulo()).isEqualTo("One Piece");
DTOCapitulo cap2 = new DTOCapitulo(null, 1, "Capítulo Anime", TipoRecurso.ANIME,
List.of(new DTOFuenteCapitulo("FuenteA", "https://anime.com")),
true
);
restTemplate.exchange("/recursos/{id}/capitulos", HttpMethod.POST,
new HttpEntity<>(cap1, jsonHeaders), Void.class, recursoId);
restTemplate.exchange("/recursos/{id}/capitulos", HttpMethod.POST,
new HttpEntity<>(cap2, jsonHeaders), Void.class, recursoId);
// Caso 2: Busco por título que no encontraremos
ResponseEntity<DTORecurso[]> respuestaNoEncontrada = restTemplate.exchange(
"/recursos/titulo/{titulo}", HttpMethod.GET, null, DTORecurso[].class, "Naruto"
// Obtengo los capítulos por tipo correctamente
var respuesta = restTemplate.exchange(
"/recursos/{id}/capitulos/tipos", HttpMethod.GET, null,
new ParameterizedTypeReference<Map<String, List<DTOCapitulo>>>() {}, recursoId
);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuesta.getBody()).containsKeys("MANGA", "ANIME");
assertThat(respuesta.getBody().get("MANGA")).isNotEmpty();
assertThat(respuesta.getBody().get("ANIME")).isNotEmpty();
// Recurso que no exista
var respuesta404 = restTemplate.exchange(
"/recursos/{id}/capitulos/tipos", HttpMethod.GET, null,
Void.class, 9999L
);
assertThat(respuesta404.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(respuestaNoEncontrada.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
// Recurso sin capítulos
DTORecurso vacio = new DTORecurso(null, "Vacio", "No tiene caps", LocalDate.now(), "Autor2", null, new HashSet<>());
MultiValueMap<String, Object> datosVacios = new LinkedMultiValueMap<>();
datosVacios.add("recurso", vacio);
datosVacios.add("foto", new ByteArrayResource(new byte[0]) {
@Override
public String getFilename() {
return "dummy.jpg";
}
});
restTemplate.exchange("/recursos/", HttpMethod.POST,
new HttpEntity<>(datosVacios, headers), Void.class);
private static final Logger LOGGER = LoggerFactory.getLogger(TestRecursosController.class);
var busquedaVacio = restTemplate.getForEntity("/recursos/titulo/Vacio", DTORecurso[].class);
Long idVacio = busquedaVacio.getBody()[0].getId();
var respuesta204 = restTemplate.exchange(
"/recursos/{id}/capitulos/tipos", HttpMethod.GET, null,
Void.class, idVacio
);
assertThat(respuesta204.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
}
@Test
@DirtiesContext
......@@ -191,40 +217,39 @@ public class TestRecursosController {
var respuestaAdmin = restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
assertThat(respuestaAdmin.getStatusCode()).isEqualTo(HttpStatus.CREATED);
LOGGER.info("✅ Recurso creado: Título: {}, Géneros: {}", recursoDTO.getTitulo(), recursoDTO.getGeneros());
// 🔹 Esperar 1 segundo para que la transacción se complete
Thread.sleep(4000);
// 🔍 Buscar el recurso por título para verificar que tiene géneros asignados
ResponseEntity<DTORecurso[]> respuestaTitulo = restTemplate.exchange(
"/recursos/titulo/{titulo}", HttpMethod.GET, null, DTORecurso[].class, "Dragon Ball"
);
assertThat(respuestaTitulo.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuestaTitulo.getBody()).isNotEmpty();
LOGGER.info("📌 Recurso encontrado por título: {}", respuestaTitulo.getBody()[0]);
// 🔍 Buscar el recurso por género
// Busco el recurso por género
ResponseEntity<DTORecurso[]> respuesta = restTemplate.exchange(
"/recursos/genero/{genero}", HttpMethod.GET, null, DTORecurso[].class, "ACCION"
);
LOGGER.info("🔍 Consulta por género ACCION retornó: {}", Arrays.toString(respuesta.getBody()));
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
}
//Género válido pero sin resultados
ResponseEntity<DTORecurso[]> respuestaNoContent = restTemplate.exchange(
"/recursos/genero/{genero}", HttpMethod.GET, null, DTORecurso[].class, "DRAMA"
);
assertThat(respuestaNoContent.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
// Género inválido
ResponseEntity<Void> respuestaBadRequest = restTemplate.exchange(
"/recursos/genero/{genero}", HttpMethod.GET, null, Void.class, "xyz"
);
assertThat(respuestaBadRequest.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
}
@Test
@DirtiesContext
void testBuscarPorAutor() {
// Registro del admin
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
......@@ -237,6 +262,7 @@ public class TestRecursosController {
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Creo un recurso
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
DTORecurso dtoRecurso = new DTORecurso(
null, "Titulo Prueba", "Descripción de prueba", LocalDate.now(), "Autor Prueba",
......@@ -253,93 +279,33 @@ public class TestRecursosController {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(formData, headers);
restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
// Autor encontrado
ResponseEntity<DTORecurso[]> respuesta = restTemplate.exchange(
"/recursos/autor/{autor}", HttpMethod.GET, null, DTORecurso[].class, "Autor Prueba"
);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuesta.getBody()).isNotNull().isNotEmpty();
ResponseEntity<DTORecurso[]> respuestaNoEncontrada = restTemplate.exchange(
"/recursos/autor/{autor}", HttpMethod.GET, null, DTORecurso[].class, "Autor Desconocido"
);
assertThat(respuestaNoEncontrada.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuestaNoEncontrada.getBody()).isEmpty();
}
@Test
@DirtiesContext
void testBuscarPorRangoDeFechas() {
// Creo un administrador
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
"contrasenia", "adminpassword"
), Void.class);
// Inicio sesión al administrador
var authResponse = restTemplate.postForEntity("/usuarios/admin@example.com", Map.of(
"clave", "adminpassword"
), DTOLoginRespuesta.class);
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Creo varios recursos con distintas fechas
LocalDate fecha1 = LocalDate.of(2023, 1, 1);
LocalDate fecha2 = LocalDate.of(2023, 6, 15);
LocalDate fecha3 = LocalDate.of(2024, 1, 10);
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
for (LocalDate fecha : List.of(fecha1, fecha2, fecha3)) {
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
DTORecurso dtoRecurso = new DTORecurso(
null, "Titulo" + fecha.getYear(), "Desc" + fecha.getYear(), fecha, "Autor" + fecha.getYear(),
null,new HashSet<>()
);
formData.add("recurso", dtoRecurso);
formData.add("foto", new ByteArrayResource(new byte[0]) {
@Override
public String getFilename() {
return "dummy.jpg";
}
});
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(formData, headers);
restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
}
//Caso 1: Busco por fecha que si encontraremos
ResponseEntity<DTORecurso[]> respuesta = restTemplate.exchange(
"/recursos/fecha?inicio={inicio}&fin={fin}", HttpMethod.GET, null, DTORecurso[].class, "2023-01-01", "2023-12-31"
// Autor no encontrado
ResponseEntity<Void> respuestaNoEncontrada = restTemplate.exchange(
"/recursos/autor/{autor}", HttpMethod.GET, null, Void.class, "Autor Desconocido"
);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuesta.getBody()).isNotNull().hasSize(2);
assertThat(Arrays.stream(respuesta.getBody()).map(DTORecurso::getFechaPublicacion))
.containsExactlyInAnyOrder(fecha1, fecha2);
assertThat(respuestaNoEncontrada.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
//Caso 2: Busco por fecha que no encontraremos
ResponseEntity<DTORecurso[]> respuestaNoEncontrada = restTemplate.exchange(
"/recursos/fecha?inicio={inicio}&fin={fin}", HttpMethod.GET, null, DTORecurso[].class, "2010-01-01", "2010-12-31"
// Autor nulo o vacío
ResponseEntity<Void> respuestaBadRequest = restTemplate.exchange(
"/recursos/autor/{autor}", HttpMethod.GET, null, Void.class, " "
);
assertThat(respuestaNoEncontrada.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuestaNoEncontrada.getBody()).isNotNull().isEmpty();
assertThat(respuestaBadRequest.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
}
@Test
@DirtiesContext
void testBuscarRecursoPorId() {
// Crear un administrador
// Creo un administrador
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
......@@ -383,7 +349,7 @@ public class TestRecursosController {
Long recursoId = respuestaBusqueda.getBody()[0].getId();
//Caso 1: Busco por id que si encontraremos
//Busco por id que si encontraremos
ResponseEntity<DTORecurso> respuestaPorId = restTemplate.exchange(
"/recursos/{id}", HttpMethod.GET, null, DTORecurso.class, recursoId
);
......@@ -391,7 +357,7 @@ public class TestRecursosController {
assertThat(respuestaPorId.getBody()).isNotNull();
assertThat(respuestaPorId.getBody().getId()).isEqualTo(recursoId);
//Caso 1: Busco por id que no encontraremos
//Busco por id que no encontraremos
ResponseEntity<Void> respuestaNoEncontrada = restTemplate.exchange(
"/recursos/{id}", HttpMethod.GET, null, Void.class, 9999
);
......@@ -402,7 +368,7 @@ public class TestRecursosController {
@Test
@DirtiesContext
void testBorrarRecurso() {
// Crear un adminsitrador
// Creo un adminsitrador
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
......@@ -438,8 +404,6 @@ public class TestRecursosController {
var respuestaAdmin = restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
assertThat(respuestaAdmin.getStatusCode()).isEqualTo(HttpStatus.CREATED);
// Obtengo el recurso recién creado
ResponseEntity<DTORecurso[]> respuestaBusqueda = restTemplate.getForEntity(
"/recursos/titulo/Recurso para borrar", DTORecurso[].class
......@@ -447,20 +411,20 @@ public class TestRecursosController {
assertThat(respuestaBusqueda.getStatusCode()).isEqualTo(HttpStatus.OK);
Long recursoId = respuestaBusqueda.getBody()[0].getId();
// Caso 1: Intento borrar el recurso sin autenticación
// Intento borrar el recurso sin autenticación
ResponseEntity<Void> deleteUnauthorized = restTemplate.exchange(
"/recursos/" + recursoId, HttpMethod.DELETE, HttpEntity.EMPTY, Void.class
);
assertThat(deleteUnauthorized.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
// Caso 2: Intento borrar el recurso con autenticación
// Intento borrar el recurso con autenticación
HttpHeaders authHeaders = new HttpHeaders();
authHeaders.set("Authorization", token);
HttpEntity<Void> deleteRequest = new HttpEntity<>(authHeaders);
var deleteResponse = restTemplate.exchange("/recursos/" + recursoId, HttpMethod.DELETE, deleteRequest, Void.class);
assertThat(deleteResponse.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
// Caso 3: Intento borrarlo otra vez
// Intento borrarlo otra vez
ResponseEntity<Void> deleteAgainResponse = restTemplate.exchange(
"/recursos/" + recursoId, HttpMethod.DELETE, deleteRequest, Void.class
);
......@@ -470,7 +434,7 @@ public class TestRecursosController {
@Test
@DirtiesContext
void testModificarRecurso() {
// Crear un administrador
// Creo un administrador
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
......@@ -484,7 +448,7 @@ public class TestRecursosController {
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Crear un recurso con autenticación
// Creo un recurso con autenticación
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
......@@ -506,7 +470,7 @@ public class TestRecursosController {
var respuestaCreacion = restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
assertThat(respuestaCreacion.getStatusCode()).isEqualTo(HttpStatus.CREATED);
// Buscar el recurso por título
// Busco el recurso por título
ResponseEntity<DTORecurso[]> respuestaBusqueda = restTemplate.getForEntity(
"/recursos/titulo/{titulo}", DTORecurso[].class, "Titulo Original"
);
......@@ -515,15 +479,25 @@ public class TestRecursosController {
Long recursoId = respuestaBusqueda.getBody()[0].getId();
// Caso 1: Modificar todos los campos (SIN MODIFICAR FOTO)
// Modificar todos los campos (SIN modificar la foto)
DTORecurso nuevosDatos = new DTORecurso(recursoId, "Titulo Modificado", "Descripción Modificada",
LocalDate.of(2024, 1, 1), "Autor Modificado", null, new HashSet<>(Set.of(Genero.ACCION, Genero.AVENTURA)));
LocalDate.of(2024, 1, 1), "Autor Modificado", null,
new HashSet<>(Set.of(Genero.ACCION, Genero.AVENTURA)));
HttpHeaders jsonHeaders = new HttpHeaders();
jsonHeaders.set("Authorization", token);
jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
MultiValueMap<String, Object> updateFormData = new LinkedMultiValueMap<>();
updateFormData.add("recurso", nuevosDatos);
updateFormData.add("foto", new ByteArrayResource(new byte[0]) {
@Override
public String getFilename() {
return "dummy.jpg";
}
});
HttpEntity<DTORecurso> updateRequest = new HttpEntity<>(nuevosDatos, jsonHeaders);
HttpHeaders updateHeaders = new HttpHeaders();
updateHeaders.set("Authorization", token);
updateHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> updateRequest = new HttpEntity<>(updateFormData, updateHeaders);
ResponseEntity<DTORecurso> respuestaModificacion = restTemplate.exchange(
"/recursos/{id}", HttpMethod.PUT, updateRequest, DTORecurso.class, recursoId
);
......@@ -536,108 +510,324 @@ public class TestRecursosController {
assertThat(respuestaModificacion.getBody().getAutor()).isEqualTo("Autor Modificado");
assertThat(respuestaModificacion.getBody().getGeneros()).containsExactlyInAnyOrder(Genero.ACCION, Genero.AVENTURA);
// Caso 2: Intentar modificar un recurso que NO existe
// Intento modificar un recurso que NO existe
DTORecurso datosFalsos = new DTORecurso(null, "Titulo Inexistente", "Descripción",
LocalDate.of(2024, 1, 1), "Autor", null, new HashSet<>(Set.of(Genero.DRAMA)));
HttpEntity<DTORecurso> fakeUpdateRequest = new HttpEntity<>(datosFalsos, jsonHeaders);
MultiValueMap<String, Object> fakeFormData = new LinkedMultiValueMap<>();
fakeFormData.add("recurso", datosFalsos);
fakeFormData.add("foto", new ByteArrayResource(new byte[0]) {
@Override
public String getFilename() {
return "dummy.jpg";
}
});
HttpEntity<MultiValueMap<String, Object>> fakeUpdateRequest = new HttpEntity<>(fakeFormData, updateHeaders);
ResponseEntity<Void> respuestaNoEncontrado = restTemplate.exchange(
"/recursos/{id}", HttpMethod.PUT, fakeUpdateRequest, Void.class, 9999
);
assertThat(respuestaNoEncontrado.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
// Caso 3: Intentar modificar sin autenticación
HttpEntity<DTORecurso> noAuthRequest = new HttpEntity<>(nuevosDatos);
// Intento modificar sin autenticación
HttpEntity<MultiValueMap<String, Object>> noAuthRequest = new HttpEntity<>(updateFormData);
ResponseEntity<Void> respuestaNoAutenticado = restTemplate.exchange(
"/recursos/{id}", HttpMethod.PUT, noAuthRequest, Void.class, recursoId
);
assertThat(respuestaNoAutenticado.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
// Imagen con formato inválido
MultiValueMap<String, Object> formDataInvalido = new LinkedMultiValueMap<>();
formDataInvalido.add("recurso", nuevosDatos);
formDataInvalido.add("foto", new ByteArrayResource("contenido".getBytes()) {
@Override
public String getFilename() {
return "archivo.txt"; // Formato inválido
}
});
HttpEntity<MultiValueMap<String, Object>> invalidRequest = new HttpEntity<>(formDataInvalido, updateHeaders);
ResponseEntity<Void> respuestaBadRequest = restTemplate.exchange(
"/recursos/{id}", HttpMethod.PUT, invalidRequest, Void.class, recursoId
);
assertThat(respuestaBadRequest.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
}
@Test
@DirtiesContext
void testObtenerRecursos() {
// Registro un administrador
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
"contrasenia", "adminpassword"
), Void.class);
// Inicio sesión con el administrador
var authResponse = restTemplate.postForEntity("/usuarios/admin@example.com", Map.of(
"clave", "adminpassword"
), DTOLoginRespuesta.class);
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Creo un recurso con autenticación
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
DTORecurso recursoDTO = new DTORecurso(null, "Bleach", "Shinigamis y hollows",
LocalDate.of(2001, 8, 7), "Tite Kubo", null,
new HashSet<>(Set.of(Genero.ACCION)));
formData.add("recurso", recursoDTO);
formData.add("foto", new ByteArrayResource(new byte[0]) {
@Override
public String getFilename() {
return "dummy.jpg";
}
});
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(formData, headers);
var respuestaCreacion = restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
assertThat(respuestaCreacion.getStatusCode()).isEqualTo(HttpStatus.CREATED);
// Obtengo todos los recursos (debería haber al menos uno)
ResponseEntity<DTORecurso[]> respuesta = restTemplate.exchange(
"/recursos", HttpMethod.GET, null, DTORecurso[].class
);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuesta.getBody()).isNotEmpty();
}
@Test
@DirtiesContext
void testListarRecursos() {
// Registro del administrador
void testAnadirCapitulo() {
// Registro e inicio de sesión del admin
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
"contrasenia", "adminpassword"
), Void.class);
// Iniciar sesión con administrador
var authResponse = restTemplate.postForEntity("/usuarios/admin@example.com", Map.of(
"clave", "adminpassword"
), DTOLoginRespuesta.class);
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Headers con autenticación
// Creo un recurso
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// Crear recursos de prueba con imágenes simuladas
for (int i = 1; i <= 10; i++) {
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
DTORecurso dtoRecurso = new DTORecurso(
null, "Titulo " + i, "Descripción " + i, LocalDate.of(2020, 1, i), "Autor " + i,
null, "Naruto", "Ninjas", LocalDate.of(1999, 9, 21), "Masashi Kishimoto",
null, new HashSet<>()
);
formData.add("recurso", dtoRecurso);
formData.add("foto", new ByteArrayResource(new byte[0]) {
@Override
public String getFilename() {
return "dummy.jpg";
}
@Override public String getFilename() { return "dummy.jpg"; }
});
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(formData, headers);
ResponseEntity<Void> response = restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
}
HttpEntity<MultiValueMap<String, Object>> crearRequest = new HttpEntity<>(formData, headers);
var crearRespuesta = restTemplate.exchange("/recursos/", HttpMethod.POST, crearRequest, Void.class);
assertThat(crearRespuesta.getStatusCode()).isEqualTo(HttpStatus.CREATED);
// Obtengo el ID del recurso
var buscarRespuesta = restTemplate.getForEntity("/recursos/titulo/{titulo}", DTORecurso[].class, "Naruto");
assertThat(buscarRespuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
Long recursoId = buscarRespuesta.getBody()[0].getId();
// Creo un capítulo
DTOCapitulo capitulo = new DTOCapitulo(
null, 1, "Capítulo 1", TipoRecurso.MANGA,
List.of(new DTOFuenteCapitulo("MangaPlus", "https://mangaplus.com")), true
);
HttpHeaders capHeaders = new HttpHeaders();
capHeaders.set("Authorization", token);
capHeaders.setContentType(MediaType.APPLICATION_JSON);
// Esperar un momento para que los datos se confirmen en la BD
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
HttpEntity<DTOCapitulo> capRequest = new HttpEntity<>(capitulo, capHeaders);
var respuesta = restTemplate.exchange(
"/recursos/{id}/capitulos", HttpMethod.POST, capRequest, Void.class, recursoId
);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.CREATED);
// ID de recurso que no existe
var respuestaNotFound = restTemplate.exchange(
"/recursos/{id}/capitulos", HttpMethod.POST, capRequest, Void.class, 9999L
);
assertThat(respuestaNotFound.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
// Caso 1: Obtener recursos con autenticación de administrador
HttpHeaders jsonHeaders = new HttpHeaders();
jsonHeaders.set("Authorization", token);
jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
@Test
@DirtiesContext
void testObtenerCapitulosDeRecurso() {
// Registro e inicio de sesión del admin
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
"contrasenia", "adminpassword"
), Void.class);
var authResponse = restTemplate.postForEntity("/usuarios/admin@example.com", Map.of(
"clave", "adminpassword"
), DTOLoginRespuesta.class);
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Creo un recurso
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
DTORecurso dtoRecurso = new DTORecurso(
null, "Fullmetal Alchemist", "Alquimia y acción", LocalDate.of(2001, 1, 1), "Hiromu Arakawa",
null, new HashSet<>()
);
formData.add("recurso", dtoRecurso);
formData.add("foto", new ByteArrayResource(new byte[0]) {
@Override public String getFilename() { return "dummy.jpg"; }
});
HttpEntity<MultiValueMap<String, Object>> crearRequest = new HttpEntity<>(formData, headers);
var crearRespuesta = restTemplate.exchange("/recursos/", HttpMethod.POST, crearRequest, Void.class);
assertThat(crearRespuesta.getStatusCode()).isEqualTo(HttpStatus.CREATED);
HttpEntity<Void> authRequest = new HttpEntity<>(jsonHeaders);
ResponseEntity<DTORecurso[]> respuestaAdmin = restTemplate.exchange(
"/recursos", HttpMethod.GET, authRequest, DTORecurso[].class
// Obtengo el ID del recurso
var buscarRespuesta = restTemplate.getForEntity("/recursos/titulo/{titulo}", DTORecurso[].class, "Fullmetal Alchemist");
assertThat(buscarRespuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
Long recursoId = buscarRespuesta.getBody()[0].getId();
// Añado un capítulo
DTOCapitulo capitulo = new DTOCapitulo(
null, 1, "Capítulo 1", TipoRecurso.MANGA,
List.of(new DTOFuenteCapitulo("MangaPlus", "https://mangaplus.com")), true
);
assertThat(respuestaAdmin.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuestaAdmin.getBody()).isNotNull();
assertThat(respuestaAdmin.getBody().length).isGreaterThanOrEqualTo(10); // Debería haber 10 o más
HttpHeaders capHeaders = new HttpHeaders();
capHeaders.set("Authorization", token);
capHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<DTOCapitulo> capRequest = new HttpEntity<>(capitulo, capHeaders);
var respuestaCreacion = restTemplate.exchange(
"/recursos/{id}/capitulos", HttpMethod.POST, capRequest, Void.class, recursoId
);
assertThat(respuestaCreacion.getStatusCode()).isEqualTo(HttpStatus.CREATED);
// Caso 2: Obtener recursos sin autenticación
ResponseEntity<Void> respuestaNoAuth = restTemplate.exchange(
"/recursos", HttpMethod.GET, HttpEntity.EMPTY, Void.class
// Obtener los capítulos del recurso creado
ResponseEntity<DTOCapitulo[]> respuesta = restTemplate.getForEntity(
"/recursos/{id}/capitulos", DTOCapitulo[].class, recursoId
);
assertThat(respuestaNoAuth.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuesta.getBody()).isNotNull().isNotEmpty();
assertThat(respuesta.getBody()[0].getTitulo()).isEqualTo("Capítulo 1");
// Creo otro recurso sin añadir capítulos
DTORecurso sinCaps = new DTORecurso(null, "Vacío", "Sin capítulos", LocalDate.now(), "Autor", null, new HashSet<>());
formData.set("recurso", sinCaps);
var sinCapsResponse = restTemplate.exchange("/recursos/", HttpMethod.POST, crearRequest, Void.class);
assertThat(sinCapsResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
var buscarSinCaps = restTemplate.getForEntity("/recursos/titulo/{titulo}", DTORecurso[].class, "Vacío");
Long idSinCapitulos = buscarSinCaps.getBody()[0].getId();
ResponseEntity<DTOCapitulo[]> sinCapitulos = restTemplate.getForEntity(
"/recursos/{id}/capitulos", DTOCapitulo[].class, idSinCapitulos
);
assertThat(sinCapitulos.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
//Recurso que no existe
ResponseEntity<String> notFound = restTemplate.exchange(
"/recursos/{id}/capitulos", HttpMethod.GET, null, String.class, 9999
);
assertThat(notFound.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
}
@Test
@DirtiesContext
void testModificarCapitulo() {
// Creo e inicio sesión como admin
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
"nombreUsuario", "admin",
"contrasenia", "adminpassword"
), Void.class);
var authResponse = restTemplate.postForEntity("/usuarios/admin@example.com", Map.of(
"clave", "adminpassword"
), DTOLoginRespuesta.class);
String token = "Bearer " + authResponse.getBody().getToken();
// Creo un recurso
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
DTORecurso dtoRecurso = new DTORecurso(null, "Recurso ModCap", "desc", LocalDate.now(), "Autor", null, new HashSet<>());
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
formData.add("recurso", dtoRecurso);
formData.add("foto", new ByteArrayResource(new byte[0]) {
@Override public String getFilename() { return "foto.jpg"; }
});
restTemplate.exchange("/recursos/", HttpMethod.POST, new HttpEntity<>(formData, headers), Void.class);
// Busco recurso creado
Long recursoId = restTemplate.getForEntity("/recursos/titulo/{titulo}", DTORecurso[].class, "Recurso ModCap")
.getBody()[0].getId();
// Añado capítulo
DTOCapitulo capitulo = new DTOCapitulo(null, 1, "Cap 1", TipoRecurso.MANGA,
List.of(new DTOFuenteCapitulo("Fuente", "https://fuente.com")), true);
HttpHeaders jsonHeaders = new HttpHeaders();
jsonHeaders.set("Authorization", token);
jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
restTemplate.exchange("/recursos/" + recursoId + "/capitulos", HttpMethod.POST,
new HttpEntity<>(capitulo, jsonHeaders), Void.class);
// Obtengo ID del capítulo
Long capituloId = restTemplate.getForEntity("/recursos/" + recursoId + "/capitulos", DTOCapitulo[].class)
.getBody()[0].getId();
//Modificación válida
DTOCapitulo actualizado = new DTOCapitulo(capituloId, 2, "Capítulo Modificado", TipoRecurso.MANGA,
List.of(new DTOFuenteCapitulo("NuevaFuente", "https://nueva.com")), false);
ResponseEntity<DTOCapitulo> respuesta = restTemplate.exchange(
"/recursos/{recursoId}/capitulos/{id}", HttpMethod.PUT,
new HttpEntity<>(actualizado, jsonHeaders), DTOCapitulo.class,
recursoId, capituloId
);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuesta.getBody()).isNotNull();
assertThat(respuesta.getBody().getTitulo()).isEqualTo("Capítulo Modificado");
// ID del body distinto al de la URL (400)
actualizado.setId(capituloId + 1); // ID incorrecto
ResponseEntity<Void> respuesta400 = restTemplate.exchange(
"/recursos/{recursoId}/capitulos/{id}", HttpMethod.PUT,
new HttpEntity<>(actualizado, jsonHeaders), Void.class,
recursoId, capituloId
);
assertThat(respuesta400.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
// recurso o capítulo inexistente (404)
actualizado.setId(9999L);
ResponseEntity<Void> respuesta404 = restTemplate.exchange(
"/recursos/{recursoId}/capitulos/{id}", HttpMethod.PUT,
new HttpEntity<>(actualizado, jsonHeaders), Void.class,
9999, 9999
);
assertThat(respuesta404.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
@Test
@DirtiesContext
void testObtenerCapitulosDeRecurso() {
void testObtenerCapituloPorId() {
// Registro un administrador
restTemplate.postForEntity("/usuarios/", Map.of(
"email", "admin@example.com",
......@@ -645,24 +835,22 @@ public class TestRecursosController {
"contrasenia", "adminpassword"
), Void.class);
// Inicio sesión con el administrador
// Inicio sesión
var authResponse = restTemplate.postForEntity("/usuarios/admin@example.com", Map.of(
"clave", "adminpassword"
), DTOLoginRespuesta.class);
assertThat(authResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
String token = "Bearer " + authResponse.getBody().getToken();
// Creo un recurso con autenticación
// Creo un recurso
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
DTORecurso recursoDTO = new DTORecurso(null, "One Piece", "Manga",
LocalDate.of(1997, 7, 22), "Eiichiro Oda", null, new HashSet<>());
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
DTORecurso dtoRecurso = new DTORecurso(
null, "Manga Ejemplo", "Sinopsis", LocalDate.now(), "Autor X",
null, new HashSet<>()
);
formData.add("recurso", dtoRecurso);
formData.add("recurso", recursoDTO);
formData.add("foto", new ByteArrayResource(new byte[0]) {
@Override
public String getFilename() {
......@@ -671,50 +859,69 @@ public class TestRecursosController {
});
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(formData, headers);
var respuestaCreacion = restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
assertThat(respuestaCreacion.getStatusCode()).isEqualTo(HttpStatus.CREATED);
restTemplate.exchange("/recursos/", HttpMethod.POST, request, Void.class);
// Busco el recurso por título para obtener su ID
// Busco el recurso para obtener su ID
ResponseEntity<DTORecurso[]> respuestaBusqueda = restTemplate.getForEntity(
"/recursos/titulo/Manga Ejemplo", DTORecurso[].class
"/recursos/titulo/{titulo}", DTORecurso[].class, "One Piece"
);
assertThat(respuestaBusqueda.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuestaBusqueda.getBody()).isNotEmpty();
Long recursoId = respuestaBusqueda.getBody()[0].getId();
// Agrego capítulos al recurso
HttpHeaders jsonHeaders = new HttpHeaders();
jsonHeaders.set("Authorization", token);
jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
// Creo un capítulo
DTOCapitulo capituloDTO = new DTOCapitulo(null, 1, "Capítulo 1", TipoRecurso.MANGA,
List.of(new DTOFuenteCapitulo("MangaPlus", "https://manga.com/1")), true);
HttpHeaders capHeaders = new HttpHeaders();
capHeaders.set("Authorization", token);
capHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<DTOCapitulo> capRequest = new HttpEntity<>(capituloDTO, capHeaders);
restTemplate.postForEntity("/recursos/" + recursoId + "/capitulos", capRequest, Void.class);
// Obtengo los capítulos del recurso para sacar el ID del capítulo
ResponseEntity<DTOCapitulo[]> capsResponse = restTemplate.getForEntity(
"/recursos/" + recursoId + "/capitulos", DTOCapitulo[].class);
Long capituloId = capsResponse.getBody()[0].getId();
// Obtener capítulo por ID correctamente
ResponseEntity<DTOCapitulo> getCapitulo = restTemplate.getForEntity(
"/recursos/{recursoId}/capitulos/{capituloId}",
DTOCapitulo.class, recursoId, capituloId
);
assertThat(getCapitulo.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(getCapitulo.getBody()).isNotNull();
assertThat(getCapitulo.getBody().getTitulo()).isEqualTo("Capítulo 1");
// Capítulo no pertenece al recurso
ResponseEntity<Void> notBelonging = restTemplate.getForEntity(
"/recursos/{recursoId}/capitulos/{capituloId}",
Void.class, recursoId + 1, capituloId
);
assertThat(notBelonging.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
restTemplate.exchange("/recursos/{id}/capitulos", HttpMethod.POST,
new HttpEntity<>(new DTOCapitulo(null, 1, "Capítulo 1", TipoRecurso.MANGA,
List.of(new DTOFuenteCapitulo("Crunchyroll", "https://crunchyroll.com/cap1"))), jsonHeaders),
Void.class, recursoId);
// Recurso no existe
ResponseEntity<Void> recursoInexistente = restTemplate.getForEntity(
"/recursos/{recursoId}/capitulos/{capituloId}",
Void.class, 9999, capituloId
);
assertThat(recursoInexistente.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
restTemplate.exchange("/recursos/{id}/capitulos", HttpMethod.POST,
new HttpEntity<>(new DTOCapitulo(null, 2, "Capítulo 2", TipoRecurso.MANGA,
List.of(new DTOFuenteCapitulo("AnimeFLV", "https://animeflv.com/cap2"))), jsonHeaders),
Void.class, recursoId);
// Obtengo capítulos del recurso
ResponseEntity<DTOCapitulo[]> respuestaCapitulos = restTemplate.exchange(
"/recursos/{id}/capitulos", HttpMethod.GET, null, DTOCapitulo[].class, recursoId);
assertThat(respuestaCapitulos.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuestaCapitulos.getBody()).isNotNull();
assertThat(respuestaCapitulos.getBody()[0].getNumero()).isEqualTo(1);
assertThat(respuestaCapitulos.getBody()[0].getTitulo()).isEqualTo("Capítulo 1");
assertThat(respuestaCapitulos.getBody()[0].getFuentes().get(0).getNombreFuente()).isEqualTo("Crunchyroll");
// Caso de recurso sin capítulos
ResponseEntity<DTOCapitulo[]> respuestaSinCapitulos = restTemplate.exchange(
"/recursos/{id}/capitulos", HttpMethod.GET, null, DTOCapitulo[].class, 9999);
assertThat(respuestaSinCapitulos.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(respuestaSinCapitulos.getBody()).isNull();
//Capítulo no existe
ResponseEntity<Void> capituloInexistente = restTemplate.getForEntity(
"/recursos/{recursoId}/capitulos/{capituloId}",
Void.class, recursoId, 9999
);
assertThat(capituloInexistente.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
@Test
@DirtiesContext
void testObtenerGeneros() {
// Hay géneros disponibles
ResponseEntity<String[]> respuesta = restTemplate.getForEntity(
"/recursos/generos", String[].class
);
assertThat(respuesta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuesta.getBody()).isNotEmpty();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment