Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package es.codeurjcstudents.pcmod.controller;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
Expand Down Expand Up @@ -54,4 +55,11 @@ public ResponseEntity<Resource> getComponentImage(@PathVariable long id) throws
return ResponseEntity.ok().contentType(mediaType).body(imageFile);
}

@DeleteMapping("/{id}")
public void deleteComponent(@PathVariable long id) {

componentService.delete(id);

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.math.BigDecimal;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand All @@ -22,7 +23,7 @@ public class Component {
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id = null;

@OneToOne
@OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
private Image image;

@NotBlank(message = "El nombre del componente es obligatorio")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -58,4 +58,17 @@ public Resource getImageFile(long id) throws SQLException {
return null;

}

@PreAuthorize("hasRole('ADMIN')")
public void delete(long id) {

Optional<Component> deletedComponent = componentRepository.findById(id);

if (deletedComponent.isPresent()) {

componentRepository.deleteById(id);

}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ public void init() throws IOException {
setComponentImage(component11, "/sample_images/kingston-fury.jpg");
componentsRepository.save(component11);

Component component12 = new Component("Corsair iCUE LINK 3500X RGB",
"ENSAMBLAJES SIN LÍMITES. No hace falta ocupar mucho espacio para montar un PC con un diseño de primera. El 3500X tiene espacio para la mayoría de tarjetas gráficas y refrigeradores de CPU grandes, radiadores de 360 mm e incluso placas base EATX, sin ocupar todo el escritorio. Para un diseño realmente impecable, el 3500X es compatible con las placas base ASUS BTF y MSI PROJECT ZERO, manteniendo las conexiones y cables ocultos.\r\n" + //
"INCLUYE VENTILADORES iCUE LINK RX RGB PERFORMANCE. Se incluye un iCUE LINK System Hub y tres ventiladores RX RGB, lo que hacen de este chasis una maravilla lista para montar dentro de un revolucionario ecosistema. iCUE LINK elimina el desorden de los cables y transforma su diseño en una elegante exposición.",
ComponentType.CASE, "Corsair", BigDecimal.valueOf(118.99), 5);
setComponentImage(component12, "/sample_images/corsair-icue-link.jpg");
componentsRepository.save(component12);

// Sample Users
userService.createUser(new User("user", "example", "user_example", "c/example_address 1", "user@example.com",
userPasswordEncoded, "REGISTERED_USER"));
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public void getComponents() {
.when().get("/components/")
.then().statusCode(200).contentType(ContentType.JSON)
// Global checks
.body("totalElements", equalTo(11))
.body("content", hasSize(11))
.body("totalElements", greaterThan(10))
.body("content", hasSize(greaterThan(10)))
.body("content.brand",
hasItems("AMD", "Gigabyte", "Corsair", "MSI", "Kingston", "Asus", "Seagate", "Lian Li", "Intel"))
.body("content.price", everyItem(greaterThanOrEqualTo(0.0f)))
Expand Down Expand Up @@ -67,7 +67,7 @@ public void getComponent() {
@Test
public void getComponentNotFound() {
given().header("Content-Type", "application/json")
.when().get("/components/12")
.when().get("/components/13")
.then().statusCode(404).contentType(ContentType.JSON)
.body("error", equalTo("Not Found"))
.body("message", equalTo("Componente no encontrado."));
Expand All @@ -88,4 +88,58 @@ public void getComponentImage() throws IOException {
assertArrayEquals(expectedImage, actualImage);
}

@Test
public void deleteComponentAsNotLoggedIn() {

given()
.when().delete("components/1")
.then().statusCode(401).contentType(ContentType.JSON)
.body("status", equalTo(401))
.body("error", equalTo("Unauthorized"));
}

@Test
public void deleteComponentAsLoggedInNoAdmin() {
String authToken = given()
.header("Content-Type", "application/json")
.body("""
{
"username": "user@example.com",
"password": "userpass"
}
""")
.when().post("auth/login")
.then().statusCode(200).contentType(ContentType.JSON)
.extract().cookie("AuthToken");

given()
.header("Content-Type", "application/json")
.cookie("AuthToken", authToken)
.when().delete("components/1")
.then().statusCode(403).contentType(ContentType.JSON)
.body("status", equalTo(403))
.body("error", equalTo("Forbidden"));
}

@Test
public void deleteComponentAsAdmin() {
String authToken = given()
.header("Content-Type", "application/json")
.body("""
{
"username": "admin@example.com",
"password": "adminpass"
}
""")
.when().post("auth/login")
.then().statusCode(200).contentType(ContentType.JSON)
.extract().cookie("AuthToken");

given()
.header("Content-Type", "application/json")
.cookie("AuthToken", authToken)
.when().delete("components/1")
.then().statusCode(200);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public void getComponents() {
// Global checks
.body("size()", equalTo(3))
// Specific checks
.body("[0].name", equalTo("Kingston FURY Beast"))
.body("[1].name", equalTo("Seagate BarraCuda 3.5"))
.body("[2].name", equalTo("AMD Radeon RX 9060 XT DUAL WHITE"));
.body("[0].name", equalTo("Corsair iCUE LINK 3500X RGB"))
.body("[1].name", equalTo("Kingston FURY Beast"))
.body("[2].name", equalTo("Seagate BarraCuda 3.5"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,98 @@ public void componentDetailOptionsAsAdminTest() {
assertThat(driver.findElements(By.id("delete-component-button"))).isNotEmpty();

}

@Test
public void deleteComponentTest() {

driver.get("http://localhost:5173/login");
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("email")));
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("password")));
driver.findElement(By.id("email")).sendKeys("admin@example.com");
driver.findElement(By.id("password")).sendKeys("adminpass");

scrollToAndClick(By.name("login-button"));

wait.until(ExpectedConditions.urlToBe("http://localhost:5173/"));

driver.get("http://localhost:5173/components/8");

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("delete-component-button")));
scrollToAndClick(By.id("delete-component-button"));
WebElement deleteModal = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".modal.show")));
assertThat(deleteModal.findElement(By.className("modal-title")).getText())
.isEqualTo("¿Está seguro de que desea borrar este componente?");
assertThat(deleteModal.findElement(By.className("modal-body")).getText())
.contains("Toda su información se perderá y no se podrá recuperar.")
.contains("Esta acción no se puede deshacer.");
scrollToAndClick(By.cssSelector(".modal-footer .pcmod-danger-button"));

driver.get("http://localhost:5173/components?page=0");
assertThat(driver.getPageSource()).doesNotContain("Intel Core i5-12400F");

}

@Test
public void cancelDeleteComponentTest() {

driver.get("http://localhost:5173/login");
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("email")));
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("password")));
driver.findElement(By.id("email")).sendKeys("admin@example.com");
driver.findElement(By.id("password")).sendKeys("adminpass");

scrollToAndClick(By.name("login-button"));

wait.until(ExpectedConditions.urlToBe("http://localhost:5173/"));

driver.get("http://localhost:5173/components/1");

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("delete-component-button")));
scrollToAndClick(By.id("delete-component-button"));
WebElement deleteModal = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".modal.show")));
assertThat(deleteModal.findElement(By.className("modal-title")).getText())
.isEqualTo("¿Está seguro de que desea borrar este componente?");
assertThat(deleteModal.findElement(By.className("modal-body")).getText())
.contains("Toda su información se perderá y no se podrá recuperar.")
.contains("Esta acción no se puede deshacer.");
scrollToAndClick(By.cssSelector(".modal-footer .btn-secondary"));

driver.get("http://localhost:5173/components");
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("component-1")));
assertThat(driver.findElement(By.id("component-1")).getText())
.isEqualTo("AMD Ryzen 7 7800X3D");

}

@Test
public void cancelDeleteComponentClosingModalTest() {

driver.get("http://localhost:5173/login");
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("email")));
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("password")));
driver.findElement(By.id("email")).sendKeys("admin@example.com");
driver.findElement(By.id("password")).sendKeys("adminpass");

scrollToAndClick(By.name("login-button"));

wait.until(ExpectedConditions.urlToBe("http://localhost:5173/"));

driver.get("http://localhost:5173/components/1");

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("delete-component-button")));
scrollToAndClick(By.id("delete-component-button"));
WebElement deleteModal = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".modal.show")));
assertThat(deleteModal.findElement(By.className("modal-title")).getText())
.isEqualTo("¿Está seguro de que desea borrar este componente?");
assertThat(deleteModal.findElement(By.className("modal-body")).getText())
.contains("Toda su información se perderá y no se podrá recuperar.")
.contains("Esta acción no se puede deshacer.");
scrollToAndClick(By.cssSelector(".modal-header .btn-close"));

driver.get("http://localhost:5173/components");
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("component-1")));
assertThat(driver.findElement(By.id("component-1")).getText())
.isEqualTo("AMD Ryzen 7 7800X3D");

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ public void loadHomePage() {
String logoMessage = driver.findElement(By.id("logo")).getAttribute("alt");
assertThat(logoMessage).isEqualTo("PCMod Logo");

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("component-9")));
String componentName9 = driver.findElement(By.id("component-9")).getText();
assertThat(componentName9).isEqualTo("AMD Radeon RX 9060 XT DUAL WHITE");

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("component-10")));
String componentName10 = driver.findElement(By.id("component-10")).getText();
assertThat(componentName10).isEqualTo("Seagate BarraCuda 3.5");
Expand All @@ -69,6 +65,10 @@ public void loadHomePage() {
String componentName11 = driver.findElement(By.id("component-11")).getText();
assertThat(componentName11).isEqualTo("Kingston FURY Beast");

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("component-12")));
String componentName12 = driver.findElement(By.id("component-12")).getText();
assertThat(componentName12).isEqualTo("Corsair iCUE LINK 3500X RGB");

wait.until(ExpectedConditions.presenceOfElementLocated(By.name("componentsButton")));
assertThat(driver.findElements(By.name("componentsButton"))).isNotEmpty();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
Expand All @@ -18,6 +19,13 @@
import org.springframework.core.io.Resource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.web.server.ResponseStatusException;
Expand All @@ -27,7 +35,9 @@

import es.codeurjcstudents.pcmod.enums.ComponentType;
import es.codeurjcstudents.pcmod.model.Component;
import es.codeurjcstudents.pcmod.model.User;
import es.codeurjcstudents.pcmod.repository.ComponentsRepository;
import es.codeurjcstudents.pcmod.repository.UsersRepository;
import es.codeurjcstudents.pcmod.service.ComponentsService;
import es.codeurjcstudents.pcmod.service.ImageService;

Expand Down Expand Up @@ -61,6 +71,11 @@ static void configureProperties(DynamicPropertyRegistry registry) {
@Autowired
private ImageService imageService;

@Autowired
private UsersRepository usersRepository;

private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

@BeforeEach
void setUp() {
componentsRepository.deleteAll();
Expand All @@ -77,6 +92,14 @@ void setUp() {
"DisplayPort 2.1 y HDMI 2.1b hasta 8K\r\n" + //
"Tensor Cores 5ª gen y Reflex 2 para IA avanzada",
ComponentType.GPU, "Gigabyte", BigDecimal.valueOf(359.85), 2));

SecurityContextHolder.clearContext();
usersRepository.deleteAll();
usersRepository.save(new User("user1", "test", "userTest1", "c/testaddress", "testuser1@example.com",
passwordEncoder.encode("pass"), "REGISTERED_USER"));
usersRepository.save(new User("admin", "test", "adminTest1", "c/testaddress", "testadmin1@example.com",
passwordEncoder.encode("adminpass"), "REGISTERED_USER", "ADMIN"));

}

@Test
Expand Down Expand Up @@ -129,4 +152,43 @@ public void testGetNullComponentImage() throws SQLException {
assertEquals(null, actualImage);
}

@Test
void deleteComponentAsNotLoggedIn() {

assertThrows(AuthenticationCredentialsNotFoundException.class, () -> componentsService.delete(1L));

}

@Test
void deleteComponentAsLoggedInNoAdmin() {
User user = usersRepository.findByEmail("testuser1@example.com").orElseThrow();
Component component = componentsRepository.findAll().get(0);

SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(
org.springframework.security.core.userdetails.User.withUsername(user.getId().toString())
.password("").authorities("ROLE_REGISTERED_USER").build(),
null, List.of(new SimpleGrantedAuthority("ROLE_REGISTERED_USER"))));

assertThrows(AuthorizationDeniedException.class, () -> componentsService.delete(component.getId()));
assertEquals(2, componentsRepository.count());

}

@Test
void deleteComponentAsAdmin() {
User admin = usersRepository.findByEmail("testadmin1@example.com").orElseThrow();
Component component = componentsRepository.findAll().get(0);

SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(
org.springframework.security.core.userdetails.User.withUsername(admin.getId().toString())
.password("").authorities("ROLE_ADMIN").build(),
null, java.util.List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))));

assertDoesNotThrow(() -> componentsService.delete(component.getId()));
assertEquals(1, componentsRepository.count());

}

}
Loading
Loading