Skip to content

Commit

Permalink
Add integration tests for GET /v1/skills/requests route (#169)
Browse files Browse the repository at this point in the history
* add integration tests for GET v1/skills, add test DB configuration

* fix format violation

* Update test configuration to create schema using Flyway

* Add integration test for v1/skills/requests route

* Add custom result matcher for mockMvc responses

* Add security config for test environment to mock logged in users

* Add Use of CustomMockUser instead of AuthCookie

* Add spring-security test dependency
  • Loading branch information
Shyam-Vishwakarma authored Nov 27, 2024
1 parent 36f6a57 commit 7e27cc5
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 19 deletions.
5 changes: 5 additions & 0 deletions skill-tree/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>

<!--Start io.jsonwebtoken: todo: remove the below block when rolling back to new version -->
<dependency>
Expand Down
6 changes: 5 additions & 1 deletion skill-tree/src/main/resources/application-test.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ cookieName=rds-session-v2-development
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/skilltreetestdb
spring.datasource.username=${MYSQL_DB_USERNAME}
spring.datasource.password=${MYSQL_DB_PASSWORD}

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create-drop

spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migrations

spring.jpa.show-sql=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
package com.RDS.skilltree.skills;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.RDS.skilltree.dtos.RdsGetUserDetailsResDto;
import com.RDS.skilltree.enums.SkillTypeEnum;
import com.RDS.skilltree.enums.UserSkillStatusEnum;
import com.RDS.skilltree.models.Endorsement;
import com.RDS.skilltree.models.Skill;
import com.RDS.skilltree.models.UserSkills;
import com.RDS.skilltree.repositories.EndorsementRepository;
import com.RDS.skilltree.repositories.SkillRepository;
import com.RDS.skilltree.repositories.UserSkillRepository;
import com.RDS.skilltree.services.external.RdsService;
import com.RDS.skilltree.utils.JWTUtils;
import com.RDS.skilltree.viewmodels.RdsUserViewModel;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.Cookie;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import utils.CustomResultMatchers;
import utils.WithCustomMockUser;

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class GetAllSkillRequestIntegrationTest {
@Autowired private MockMvc mockMvc;

@Autowired private UserSkillRepository userSkillRepository;

@Autowired private EndorsementRepository endorsementRepository;

@MockBean private RdsService rdsService;

@Autowired private SkillRepository skillRepository;

@MockBean private JWTUtils jwtUtils;

private final String route = "/v1/skills/requests";

@BeforeEach
void setUp() {
// Clean up repositories
skillRepository.deleteAll();
endorsementRepository.deleteAll();
userSkillRepository.deleteAll();

// Setup super-user detail
RdsUserViewModel superUser = new RdsUserViewModel();
superUser.setId("super-user-id");
RdsUserViewModel.Roles roles = new RdsUserViewModel.Roles();
roles.setSuper_user(true);
superUser.setRoles(roles);

RdsGetUserDetailsResDto superUserDetails = new RdsGetUserDetailsResDto();
superUserDetails.setUser(superUser);

// Setup normal-users detail
RdsUserViewModel normalUser = new RdsUserViewModel();
normalUser.setId("user-id");
RdsUserViewModel.Roles normalUserRoles = new RdsUserViewModel.Roles();
normalUserRoles.setSuper_user(false);
normalUser.setRoles(normalUserRoles);

RdsGetUserDetailsResDto normalUserDetails = new RdsGetUserDetailsResDto();
normalUserDetails.setUser(normalUser);

RdsUserViewModel normalUser2 = new RdsUserViewModel();
normalUser2.setId("user-id-2");
normalUser2.setRoles(normalUserRoles);
RdsGetUserDetailsResDto normalUser2Details = new RdsGetUserDetailsResDto();
normalUser2Details.setUser(normalUser2);

// Setup mock skills
Skill skill1 = new Skill();
skill1.setName("Java");
skill1.setType(SkillTypeEnum.ATOMIC);
skill1.setCreatedBy("super-user-id");

Skill skill2 = new Skill();
skill2.setName("Springboot");
skill2.setType(SkillTypeEnum.ATOMIC);
skill2.setCreatedBy("super-user-id");

skillRepository.save(skill1);
skillRepository.save(skill2);

// Setup mock user-skills
UserSkills userSkills1 = new UserSkills();
userSkills1.setSkill(skill1);
userSkills1.setUserId("user-id");
userSkills1.setStatus(UserSkillStatusEnum.PENDING);

UserSkills userSkills2 = new UserSkills();
userSkills2.setSkill(skill2);
userSkills2.setUserId("user-id");
userSkills2.setStatus(UserSkillStatusEnum.APPROVED);

UserSkills userSkills3 = new UserSkills();
userSkills3.setSkill(skill2);
userSkills3.setUserId("user-id-2");
userSkills3.setStatus(UserSkillStatusEnum.PENDING);

userSkillRepository.save(userSkills1);
userSkillRepository.save(userSkills2);
userSkillRepository.save(userSkills3);

// Setup mock endorsements
Endorsement endorsement1 = new Endorsement();
endorsement1.setId(1);
endorsement1.setEndorserId("super-user-id");
endorsement1.setEndorseId("user-id");
endorsement1.setSkill(skill1);
endorsement1.setMessage("endorsement message");

Endorsement endorsement2 = new Endorsement();
endorsement2.setId(3);
endorsement2.setEndorserId("user-id-2");
endorsement2.setEndorseId("user-id");
endorsement2.setSkill(skill2);
endorsement2.setMessage("skill2 for user-id");

Endorsement endorsement3 = new Endorsement();
endorsement3.setId(4);
endorsement3.setEndorserId("super-user-id");
endorsement3.setEndorseId("user-id-2");
endorsement3.setSkill(skill2);
endorsement3.setMessage("skill2 for user-id-2");

endorsementRepository.save(endorsement1);
endorsementRepository.save(endorsement2);
endorsementRepository.save(endorsement3);

// Setup RDS service mock responses
when(rdsService.getUserDetails("super-user-id")).thenReturn(superUserDetails);
when(rdsService.getUserDetails("user-id-2")).thenReturn(normalUser2Details);
when(rdsService.getUserDetails("user-id")).thenReturn(normalUserDetails);

// Mock JWTUtils to bypass actual JWT verification
Claims mockClaims = mock(Claims.class);
when(mockClaims.get("userId", String.class)).thenReturn("super-user-id");
when(jwtUtils.validateToken(anyString())).thenReturn(mockClaims);
}

@Test
@DisplayName("Happy flow for SuperUser - should return all requests")
@WithCustomMockUser(
username = "super-user-id",
authorities = {"SUPERUSER"})
public void getAllRequests_asSuperUser_shouldReturnAllRequests() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.get(route).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(CustomResultMatchers.hasSkillRequest("Java", "user-id", "PENDING"))
.andExpect(
CustomResultMatchers.hasEndorsement(
"Java", "user-id", "super-user-id", "endorsement message"))
.andExpect(CustomResultMatchers.hasSkillRequest("Springboot", "user-id", "APPROVED"))
.andExpect(
CustomResultMatchers.hasEndorsement(
"Springboot", "user-id", "user-id-2", "skill2 for user-id"))
.andExpect(CustomResultMatchers.hasSkillRequest("Springboot", "user-id-2", "PENDING"))
.andExpect(
CustomResultMatchers.hasEndorsement(
"Springboot", "user-id-2", "super-user-id", "skill2 for user-id-2"))
.andExpect(CustomResultMatchers.hasUser("user-id", " "))
.andExpect(CustomResultMatchers.hasUser("user-id-2", " "))
.andExpect(CustomResultMatchers.hasUser("super-user-id", " "));
}

@Test
@DisplayName("Happy flow for normal user - Get all requests where user is endorser")
@WithCustomMockUser(
username = "user-id-2",
authorities = {"USER"})
public void getAllRequests_asNormalUser_shouldReturnAllRequestsByEndorser() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.get(route).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(CustomResultMatchers.hasSkillRequest("Springboot", "user-id", "APPROVED"))
.andExpect(
CustomResultMatchers.hasEndorsement(
"Springboot", "user-id", "user-id-2", "skill2 for user-id"))
.andExpect(CustomResultMatchers.hasUser("user-id", " "))
.andExpect(CustomResultMatchers.hasUser("user-id-2", " "));
}

@Test
@DisplayName("Filter requests by status - should return filtered requests")
@WithCustomMockUser(
username = "super-user-id",
authorities = {"SUPERUSER"})
public void getAllRequests_ByStatus_ShouldReturnFilteredRequests() throws Exception {
mockMvc
.perform(
MockMvcRequestBuilders.get(route + "?status=APPROVED")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(CustomResultMatchers.hasSkillRequest("Springboot", "user-id", "APPROVED"))
.andExpect(
CustomResultMatchers.hasEndorsement(
"Springboot", "user-id", "user-id-2", "skill2 for user-id"))
.andExpect(CustomResultMatchers.hasUser("user-id", " "))
.andExpect(CustomResultMatchers.hasUser("user-id-2", " "));
}

@Test
@DisplayName("If no skill Requests endorsed by user then return empty lists")
@WithCustomMockUser(
username = "user-id",
authorities = {"USER"})
public void noSkillRequestsEndorsedByUser_ShouldReturnEmptyLists() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.get(route).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.requests").isEmpty())
.andExpect(MockMvcResultMatchers.jsonPath("$.users").isEmpty());
}

@Test
@DisplayName("If no matching skill requests by status then return empty lists")
@WithCustomMockUser(
username = "user-id",
authorities = {"USER"})
public void noMatchingRequestsByStatus_ShouldReturnEmptyLists() throws Exception {
mockMvc
.perform(
MockMvcRequestBuilders.get(route + "?status=REJECTED")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.requests").isEmpty())
.andExpect(MockMvcResultMatchers.jsonPath("$.users").isEmpty());
}

@Test
@DisplayName("If no skill requests in DB - return empty lists")
@WithCustomMockUser(
username = "super-user-id",
authorities = {"SUPERUSER"})
public void getAllRequests_NoData_ShouldReturnEmptyLists() throws Exception {
skillRepository.deleteAll();
endorsementRepository.deleteAll();
userSkillRepository.deleteAll();

mockMvc
.perform(MockMvcRequestBuilders.get(route).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.requests").isEmpty())
.andExpect(MockMvcResultMatchers.jsonPath("$.users").isEmpty());
}

@Test
@DisplayName("if invalid cookie, return 401")
public void ifInvalidCoolie_ShouldReturnUnauthorized() throws Exception {
Cookie authCookie =
new Cookie(
"cookie",
"eyJhbGciOiJSUzI1NiIsInR5cCI.eyJ1c2VySWQiOiI2N2lSeXJOTWQ.E-EtcPOj7Ca5l8JuE0hwky0rRikYSNZBvC");

mockMvc
.perform(
MockMvcRequestBuilders.get(route)
.cookie(authCookie)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isUnauthorized());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import utils.WithCustomMockUser;

@SpringBootTest
@AutoConfigureMockMvc
Expand All @@ -30,15 +31,10 @@ public class GetAllSkillsIntegrationTest {

@Autowired private MockMvc mockMvc;

private Cookie authCookie;
private final String route = "/v1/skills";

@BeforeEach
public void setUp() {
authCookie =
new Cookie(
"rds-session-v2-development",
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJzOXpRVW00WGJWRXo3eHpSa2FadiIsInJvbGUiOiJzdXBlcl91c2VyIiwiaWF0IjoxNzI4NjY0NjA2LCJleHAiOjE3MzEyNTY2MDZ9.EyOFKrVcbleuTjUGic3GzOzYRDoLU4IShyoboe0MHlvWFOAfU2pchpXLE4NcyvdGUZ_tvoUecHd4kUkR8MkhxnkRNU3HE7N-1c1tFeYXZL0KfScJE9YzDXAl113Hx3eZVvYbhNjNUttbDlH4s_kR6YABC3sdbLGKEiLfmp9VeAs");

skillRepository.deleteAll();
Skill skill1 = new Skill();
skill1.setName("Java");
Expand All @@ -54,14 +50,13 @@ public void setUp() {
}

@Test
@WithCustomMockUser(
username = "rds-user",
authorities = {"SUPERUSER"})
@DisplayName("happy flow - returns all skills that are in db")
public void getAllSkillsHappyFlow() throws Exception {

mockMvc
.perform(
MockMvcRequestBuilders.get("/v1/skills")
.cookie(authCookie)
.accept(MediaType.APPLICATION_JSON))
.perform(MockMvcRequestBuilders.get(route).accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$[0].name").value("Java"))
.andExpect(MockMvcResultMatchers.jsonPath("$[1].name").value("Springboot"))
Expand All @@ -70,26 +65,29 @@ public void getAllSkillsHappyFlow() throws Exception {

@Test
@DisplayName("if no skills available, return empty list")
@WithCustomMockUser(
username = "rds-user",
authorities = {"SUPERUSER"})
public void noSkillsAvailable_shouldReturnEmptyList() throws Exception {
skillRepository.deleteAll();

mockMvc
.perform(
MockMvcRequestBuilders.get("/v1/skills")
.cookie(authCookie)
.accept(MediaType.APPLICATION_JSON))
.perform(MockMvcRequestBuilders.get(route).accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$").isEmpty());
}

@Test
@DisplayName("if invalid cookie, return 401")
public void ifInvalidCoolie_returnUnauthorized() throws Exception {
Cookie authCookie =
new Cookie(
"cookie",
"eyJhbGciOiJSUzI1NiIsInR5cCI.eyJ1c2VySWQiOiI2N2lSeXJOTWQ.E-EtcPOj7Ca5l8JuE0hwky0rRikYSNZBvC");

mockMvc
.perform(
MockMvcRequestBuilders.get("/v1/skills")
.cookie(new Cookie("cookie1", "eyJhbGciOiJSUz.eyJhbGciOiJSUz.EyJhbGciOiJSUz"))
.accept(MediaType.APPLICATION_JSON))
MockMvcRequestBuilders.get(route).cookie(authCookie).accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isUnauthorized());
}
}
Loading

0 comments on commit 7e27cc5

Please sign in to comment.