Skip to content

Commit

Permalink
Merge pull request #58 from nastiausenko/link-controller-integration-…
Browse files Browse the repository at this point in the history
…test

Link controller integration test
  • Loading branch information
IvanShalaev1990 authored Apr 20, 2024
2 parents 29dcfeb + 8d392ae commit b4f88f5
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 42 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ dependencies {
testImplementation 'org.springframework.security:spring-security-test'
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.3.0'
implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.3.0'
testImplementation 'org.testcontainers:postgresql:1.19.7'
testImplementation 'org.testcontainers:postgresql'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.linkurlshorter.urlshortener.exception;

import com.linkurlshorter.urlshortener.auth.exception.EmailAlreadyTakenException;
import com.linkurlshorter.urlshortener.link.ForbiddenException;
import com.linkurlshorter.urlshortener.link.NoLinkFoundByIdException;
import com.linkurlshorter.urlshortener.user.NoSuchEmailFoundException;
import com.linkurlshorter.urlshortener.user.NoUserFoundByEmailException;
import com.linkurlshorter.urlshortener.user.NoUserFoundByIdException;
Expand Down Expand Up @@ -110,5 +112,27 @@ public ResponseEntity<Object> handleNotFoundExceptions(
private ErrorResponse buildErrorResponse(HttpStatus status, String message, String requestURI) {
return new ErrorResponse(LocalDateTime.now(), status.value(), message, requestURI);
}
/**
* Handles Forbidden (403) exceptions for different types of requests.
* Returns a response with a 403 status and the corresponding error message.
*
* @param ex forbidden exception
* @param request HttpServletRequest object representing the HTTP request
* @return {@link ResponseEntity} object with the corresponding status and error message
*/
@ExceptionHandler(ForbiddenException.class)
public ResponseEntity<Object> handleForbiddenException(
ForbiddenException ex, HttpServletRequest request) {
ErrorResponse errorResponse = buildErrorResponse(HttpStatus.FORBIDDEN,
ex.getMessage(), request.getRequestURI());
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(errorResponse);
}
@ExceptionHandler(NoLinkFoundByIdException.class)
public ResponseEntity<Object> handleNoLinkFoundByIdException(
NoLinkFoundByIdException ex, HttpServletRequest request) {
ErrorResponse errorResponse = buildErrorResponse(HttpStatus.NOT_FOUND,
ex.getMessage(), request.getRequestURI());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.linkurlshorter.urlshortener.user.User;
import com.linkurlshorter.urlshortener.user.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
Expand Down Expand Up @@ -56,6 +58,8 @@ public class LinkController {
* @see InternalServerLinkException
*/
@PostMapping("/create")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Create new link")
public ResponseEntity<CreateLinkResponse> createLink(@RequestBody @Valid CreateLinkRequest createRequest) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findByEmail(authentication.getName());
Expand Down Expand Up @@ -83,9 +87,11 @@ public ResponseEntity<CreateLinkResponse> createLink(@RequestBody @Valid CreateL
* @throws ForbiddenException if the authenticated user does not have rights to delete the link
*/
@PostMapping("/delete")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Delete link by ID")
public ResponseEntity<LinkModifyingResponse> deleteLink(@RequestParam UUID id) {
if (doesUserHaveRightsForLinkById(id)) {
linkService.deleteById(id);
linkService.deleteById(id);//TODO: add validations (id not null etc)
return ResponseEntity.ok(new LinkModifyingResponse("ok"));
} else {
throw new ForbiddenException(OPERATION_FORBIDDEN_MSG);
Expand All @@ -101,6 +107,8 @@ public ResponseEntity<LinkModifyingResponse> deleteLink(@RequestParam UUID id) {
* @throws LinkStatusException if the status of the link is not ACTIVE
*/
@PostMapping("/edit/content")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Edit link content")
public ResponseEntity<LinkModifyingResponse> editLinkContent(@RequestBody EditLinkContentRequest request) {
if (doesUserHaveRightsForLinkById(request.getId())) {
Link oldLink = linkService.findById(request.getId());
Expand All @@ -124,6 +132,8 @@ public ResponseEntity<LinkModifyingResponse> editLinkContent(@RequestBody EditLi
* @throws DeletedLinkException if the link is already deleted
*/
@PostMapping("/edit/refresh")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Refresh link expiration time")
public ResponseEntity<LinkModifyingResponse> refreshLink(@RequestParam UUID id) {
if (doesUserHaveRightsForLinkById(id)) {
Link oldLink = linkService.findById(id);
Expand All @@ -147,6 +157,8 @@ public ResponseEntity<LinkModifyingResponse> refreshLink(@RequestParam UUID id)
* @throws ForbiddenException if the authenticated user does not have rights to access the link
*/
@GetMapping("/info")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Get link info")
public ResponseEntity<LinkInfoResponse> getInfoByShortLink(@RequestParam String shortLink) {
Link link = linkService.findByShortLink(shortLink);
if (doesUserHaveRightsForLinkById(link.getId())) {
Expand All @@ -164,6 +176,8 @@ public ResponseEntity<LinkInfoResponse> getInfoByShortLink(@RequestParam String
* @return a ResponseEntity containing the response object with information about all links for the user
*/
@GetMapping("/all-links-info")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Get all links info")
public ResponseEntity<LinkInfoResponse> getAllLinksForUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UUID requesterUserId = userService.findByEmail(authentication.getName()).getId();
Expand All @@ -183,6 +197,8 @@ public ResponseEntity<LinkInfoResponse> getAllLinksForUser() {
* links are sorted in descending order
*/
@GetMapping("/url-usage-top-for-user")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Get all links usage statistics")
public ResponseEntity<LinkStatisticsResponse> getLinksStatsForUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User requesterUser = userService.findByEmail(authentication.getName());
Expand All @@ -198,15 +214,15 @@ public ResponseEntity<LinkStatisticsResponse> getLinksStatsForUser() {
*/
private String generateShortLink() {
return RandomStringUtils.randomAlphanumeric(8);
}
} //TODO: extracted into a servicethis method into service.

/**
* Checks if the authenticated user has rights to perform operations on a given link.
*
* @param linkId the UUID of the link to check
* @return true if the user has rights, false otherwise
*/
private boolean doesUserHaveRightsForLinkById(UUID linkId) { //TODO: may be transformed into @?
private boolean doesUserHaveRightsForLinkById(UUID linkId) { //TODO: may be transformed into @? and extracted into a servicethis method into service.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UUID linkUserId = linkService.findById(linkId).getUser().getId();
UUID currentUserId = userService.findByEmail(authentication.getName()).getId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.transaction.annotation.Transactional;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
Expand All @@ -32,6 +34,8 @@
@AutoConfigureMockMvc
@ExtendWith(MockitoExtension.class)
@Testcontainers
@Transactional
@Rollback
class AuthControllerIntegrationTest {
@Container
@ServiceConnection
Expand Down Expand Up @@ -71,8 +75,8 @@ void loginFailedWhenUserDoesNotExistTest() throws Exception {
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(authRequest)))
.andExpect(MockMvcResultMatchers.status().is4xxClientError())
.andExpect(MockMvcResultMatchers.jsonPath("$.statusCode").value(401))
.andExpect(MockMvcResultMatchers.jsonPath("$.message").value("No user by provided email found"));
.andExpect(MockMvcResultMatchers.jsonPath("$.statusCode").value(400))
.andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Email address entered incorrectly!"));
}

/**
Expand Down Expand Up @@ -122,8 +126,8 @@ void loginFailedWhenInvalidPasswordGivenTest(String password) throws Exception {
" [email protected]",
"user-test%@example.com",
"user-test#@example.com",
"user-test.example.com"})
// TODO: add more email to test "user-test@example"
"user-test.example.com",
"user-test@example"})
void loginFailedWhenInvalidEmailGivenTest(String email) throws Exception {
authRequest = new AuthRequest(email, "Pass1234");
mockMvc.perform(post(baseUrl + "login")
Expand All @@ -134,6 +138,22 @@ void loginFailedWhenInvalidEmailGivenTest(String email) throws Exception {
.andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Email address entered incorrectly!"));
}

/**
* Test case to verify successful user registration.
*
* @throws Exception if any error occurs during the test
*/
@Test
void registerSuccessfulTest() throws Exception {
authRequest = new AuthRequest("[email protected]", "Pass1234");
this.mockMvc.perform(MockMvcRequestBuilders.post(baseUrl + "register")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(authRequest)))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.message").value("User registered successfully!"))
.andExpect(MockMvcResultMatchers.jsonPath("$.jwtToken").exists());
}

/**
* Parameterized test to verify registration failure with invalid passwords.
*
Expand Down Expand Up @@ -165,8 +185,8 @@ void registerFailedWhenInvalidPasswordGivenTest(String password) throws Exceptio
" [email protected]",
"user-test%@example.com",
"user-test#@example.com",
"user-test.example.com"})
// TODO: add more email to test "user-test@example"
"user-test.example.com",
"user-test@example"})
void registerFailedWhenInvalidEmailGivenTest(String email) throws Exception {
authRequest = new AuthRequest(email, "Pass1234");
mockMvc.perform(post(baseUrl + "register")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.linkurlshorter.urlshortener.auth.exception.EmailAlreadyTakenException;
import com.linkurlshorter.urlshortener.TestConfig;
import com.linkurlshorter.urlshortener.security.SecurityConfig;
import com.linkurlshorter.urlshortener.security.UnauthorizedException;
//import com.linkurlshorter.urlshortener.security.UnauthorizedException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
Expand Down Expand Up @@ -91,15 +91,15 @@ void loginSuccessfulTest() throws Exception {
/**
* Test case for the {@link AuthController#login(AuthRequest)} method when the user is not registered.
*/
@Test
void loginFailedTest() throws Exception {
AuthRequest request = new AuthRequest("[email protected]", "Password1");
when(authService.loginUser(request)).thenThrow(UnauthorizedException.class);

ResultActions resultActions = mockMvc.perform(post("/api/V1/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)));

resultActions.andExpect(status().isUnauthorized());
}
// @Test
// void loginFailedTest() throws Exception {
// AuthRequest request = new AuthRequest("[email protected]", "Password1");
// when(authService.loginUser(request)).thenThrow(UnauthorizedException.class);
//
// ResultActions resultActions = mockMvc.perform(post("/api/V1/auth/login")
// .contentType(MediaType.APPLICATION_JSON)
// .content(objectMapper.writeValueAsString(request)));
//
// resultActions.andExpect(status().isUnauthorized());
// }
}
Loading

0 comments on commit b4f88f5

Please sign in to comment.