JUnit Security Test Error for Service Test of Spring Boot - java

I have to write a test function for findInContextUser based on JUnit Mock in Spring Boot but I have no idea how to write it.
How can I write findInContextUser for Junit Test?
Here are my code defined in UserService shown below.
public UserDto getUserDto(String username) {
var user = findUserByUsername(username);
return UserDto.builder()
.id(user.getId())
.username(user.getUsername())
.role(user.getRole())
.build();
}
public UserDto findInContextUser() {
final Authentication authentication = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).orElseThrow(notFoundUser(HttpStatus.UNAUTHORIZED));
final UserDetails details = Optional.ofNullable((UserDetails) authentication.getPrincipal()).orElseThrow(notFoundUser(HttpStatus.UNAUTHORIZED));
return getUserDto(details.getUsername());
}
private static Supplier<GenericException> notFoundUser(HttpStatus unauthorized) {
return () -> GenericException.builder().httpStatus(unauthorized).errorMessage("user not found!").build();
}
Here is my test class shown below.
#Test
void itShouldFindInContextUser(){
// given - precondition or setup
User user = User.builder()
.username("username")
.password("password")
.role(Role.USER)
.build();
UserDto expected = UserDto.builder()
.id(user.getId())
.username(user.getUsername())
.role(user.getRole())
.build();
var roles = Stream.of(user.getRole())
.map(x -> new SimpleGrantedAuthority(x.name()))
.collect(Collectors.toList());
UserDetails details = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), roles);
Authentication authentication = Mockito.mock(Authentication.class);
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
// when - action or the behaviour that we are going test
when(securityContext.getAuthentication()).thenReturn(authentication);
when(securityContext.getAuthentication().getPrincipal()).thenReturn(details);
// then - verify the output
UserDto actual = userService.findInContextUser(); // ERROR IS HERE
assertEquals(expected, actual);
assertEquals(expected.getUsername(), actual.getUsername());
verify(userService, times(1)).findInContextUser();
}
Here is the error message shown below.
com.example.lib.exception.GenericException
Debug Part : 401 UNAUTHORIZED
I also added #WithMockUser(username = "username", password = "password", roles = "USER") but nothing changed.

Here is the solution shown below.
Add this line shown below underneath when parts
SecurityContextHolder.setContext(securityContext);

Related

How do I write a unit test for a controller class that has a Post method?

public class ReportEmailController {
#ApiImplicitParam(name = "Authorization", value = "JWT Token", required = true, dataType = "string", paramType = "header", example = Constants.SAMPLE_JWT_TOKEN)
#ApiOperation(value = "Details reporter email-service ", notes = "This method shows you thats sending summary id email info.")
#PostMapping(value = "/id/email-info", produces = "application/json")
public ResponseEntity<EmailInfoIdResponse> idEmailInfo(
#Valid #RequestHeader(value = "companyId", required = true) #ApiIgnore String companyId,
#Valid #RequestHeader(value = "user", required = true) #ApiIgnore String user,
#RequestBody IdEmailInfoRequest request) {
request.setCompanyId(companyId);
request.setAuthUser(user);
log.info("New ID Email Info Report request:{}", request);
List<EmailInfoIdParams> responseInfo = this.reporterService.getIdEmailInfo(request);
EmailInfoIdResponse response = new EmailInfoIdResponse();
response.setEmailInfoIdParams(responseInfo);
response.setStatus("SUCCESS");
response.setStatusCode(202);
response.setStatusDescription("SUCCESS");
return ResponseEntity.accepted().body(response);
}
}
#RunWith(MockitoJUnitRunner.class)
public class ReportEmailControllerTest {
#InjectMocks
private ReporterService reporterService;
#Mock
private EmailInfoRepository emailInfoRepository;
#InjectMocks
private ReportEmailController reportEmailController = new ReportEmailController();
#Test
public void testIdEmailInfo_whenRequestIsValidReturnValidList() {
getIdEmailInfoRequest();
List<EmailInfo> emailInfoList = new ArrayList<>();
EmailInfo emailInfo = new EmailInfo();
emailInfo.setMessage("test");
emailInfoList.add(emailInfo);
when(emailInfoRepository.findIdEmailInfos(idEmailInfoRequest)).thenReturn(emailInfoList);
log.info("emailInfoList:", emailInfoList);
ResponseEntity<EmailInfoIdResponse> response = reportEmailController.idEmailInfo("1","user,",idEmailInfoRequest);
assertNotEquals(response.getBody().getEmailInfoIdParams().size(),0);
}
}
My controller class and test class are as above. When I run this test, I get a NullPointerException error on the following lines in the test and controller class, but I do not expect this error to return. Could it be a problem with the definitions, can you please help, I couldn't solve it in any way.
-> Test class : ResponseEntity response = reportEmailController.idEmailInfo("1","user,",idEmailInfoRequest);
-> Controller class : List responseInfo = this.reporterService.getIdEmailInfo(request);
While unit testing controller, I would suggest you use Webclient. Also I see you are trying to test both the service as well as the controller in one go, which should not be done, you should unit test only one thing at a time.
As far as NPE go,
#InjectMocks
private ReporterService reporterService;
I believe it should be,
#Mock
private ReporterService reporterService;

authenticate a user inside a test class

I am new to spring boot and testing and I have spring boot app (generated with JHipster) that uses authentication. I need to get the id of the current user.
so this method inside userRepository returns the current user
#Query(value = "select u.* from user u where u.username=?#{principal.username}", nativeQuery = true)
User findConnectedUser();
here is the method I want to test in my controller:
#PostMapping("/rdvs")
public ResponseEntity<Rdv> createRdv(#RequestBody Rdv rdv) throws URISyntaxException {
log.debug("REST request to save Rdv : {}", rdv);
if (rdv.getId() != null) {
throw new BadRequestAlertException("A new rdv cannot already have an ID", ENTITY_NAME, "idexists");
}
User user = userRepository.findConnectedUser();
rdv.setIdUser(user.getId());
Rdv result = rdvRepository.save(rdv);
return ResponseEntity
.created(new URI("/api/rdvs/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString()))
.body(result);
}
here is my test method
#Autowired
private RdvRepository rdvRepository;
#Autowired
private UserRepository userRepository;
....
#Test
#Transactional
void createRdv() throws Exception {
int databaseSizeBeforeCreate = rdvRepository.findAll().size();
// Create the Rdv
restRdvMockMvc
.perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(rdv)))
.andExpect(status().isCreated());
// Validate the Rdv in the database
User user = userRepository.findConnectedUser();// this generate the NullPointer exception
List<Rdv> rdvList = rdvRepository.findAll();
assertThat(rdvList).hasSize(databaseSizeBeforeCreate + 1);
Rdv testRdv = rdvList.get(rdvList.size() - 1);
assertThat(testRdv.getDescription()).isEqualTo(DEFAULT_DESCRIPTION);
assertThat(testRdv.getIdUser()).isEqualTo(user.getId());
}
So this method generate a NullPointer, I guess because the method can't find the current user which should be authenticated first. So how can I authenticate a user inside that test method please I spend a lot of time with it but nothing seems to be working
note: I tried to call this api that authenticate users
#PostMapping("/authenticate")
public ResponseEntity<JWTToken> authorize(#Valid #RequestBody LoginVM loginVM) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginVM.getUsername(),
loginVM.getPassword()
);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.createToken(authentication, loginVM.isRememberMe());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}
like this in test method
User user = new User();
user.setLogin("user-jwt-controller");
user.setEmail("user-jwt-controller#example.com");
user.setActivated(true);
user.setPassword(passwordEncoder.encode("test"));
userRepository.saveAndFlush(user);
LoginVM loginVM = new LoginVM();
loginVM.setUsername("user-jwt-controller");
loginVM.setPassword("test");
//I don't know how to call the api #PostMapping("/authenticate")
Thanks in advance
Have a look at #WithMockUser annotation, see https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/test-method.html
You can see an example in the project that was generated by JHipster:
#AutoConfigureMockMvc
#WithMockUser(value = TEST_USER_LOGIN)
#IntegrationTest
class AccountResourceIT {

MockMvc SpringBoot integration tests passing individually but failing when running consecutively? (DataIntegrityViolation)

I have developed integration tests using MockMvc to send requests to the API server.
When I run each test individually they pass, but if I run the entire test class, the first passes and the rest fail due to:
DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PUBLIC.UK_SB8BBOUER5WAK8VYIIY4PF2BX_INDEX_2 ON PUBLIC.USER(USERNAME) VALUES 4"; SQL statement:
I understand that this means the tests are trying to add another User entity to the repo where one already exists, but I don't understand why the repo is not being reset after each test?
The tests rely on methods to avoid repeated code in each test, and this must be having an effect because only my test classes with these methods are failing like this, but I cannot think why or how to fix it.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class GameControllerTest {
#Autowired
MockMvc mvc;
#Autowired
GameService gameService;
#Autowired
GameRepository gameRepository;
#Autowired
UserRepository userRepository;
#Autowired
UserService userService;
public String username1 = "ss";
public String username2 = "dd";
public String username3 = "ff";
public String location = "London";
public String password = "Password72-";
public String apiVersion = "v1";
public final String gamesUrl = "/api/" + apiVersion + "/games/";
public final String addGameUrl = gamesUrl + "add-game";
public final String gameHistoryUrl = gamesUrl + "your-game-history";
public final String signupUrl = "/api/" + apiVersion + "/users/signup";
public final String loginUrl = "/api/" + apiVersion + "/login";
#Test
#Transactional
public void addGameTest() throws Exception {
String token = signupAndLogin();
Set<User> users = new HashSet<>();
User user = userRepository.findByUsername(username1);
User user2 = userRepository.findByUsername(username2);
users.add(user);
users.add(user2);
Game game = new Game(users, username1, username2, ResultForWhite.LOSE.getUrl(), "1.e4e5", username2);
MvcResult mvcResult = mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game)).header("Authorization", token))
.andExpect(status().isOk()).andReturn();
//Test JSON response is correct.
String jsonResult = mvcResult.getResponse().getContentAsString();
assertThat(jsonResult.contains(username1));
assertThat(jsonResult.contains(username2));
//Test that Game stored in DB correctly.
Optional<Game> savedGame = gameRepository.findById(1L);
assertThat(savedGame.get().getGameId() == 1);
//Test that user victories and defeats have been incremented correctly.
//Should be 1.
User savedUser = userRepository.findByUsername(username1);
User savedUser2 = userRepository.findByUsername(username2);
UserStats savedUserStats = savedUser.getUserStats().get(0);
UserStats savedUserStats2 = savedUser2.getUserStats().get(0);
Integer user1Victories = savedUserStats.getVictories();
Integer user1Defeats = savedUserStats.getDefeats();
Integer user2Victories = savedUserStats2.getVictories();
Integer user2Defeats = savedUserStats2.getDefeats();
//Assert that magnus lost the game and his defeats was incremented, and victories unchanged.
assertThat(user1Victories == 0 && user1Defeats == 1);
//Assert that hikaru won the game and his victories was incremented, and defeats unchanged.
assertThat(user2Victories == 1 && user2Defeats == 0);
}
#Test
#Transactional
public void getAUsersCompleteGameHistory() throws Exception {
String token = signupAndLogin();
createGames(token);
String content = "{\"keyword\":\"null\"}";
MvcResult mvcResult = mvc.perform(post(gameHistoryUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(content).header("Authorization", token))
.andExpect(status().isOk()).andReturn();
String jsonResult = mvcResult.getResponse().getContentAsString();
System.out.println(jsonResult);
//Ensure the JSON object returned contains all the game entities.
assertTrue(jsonResult.contains("{\"gameId\":1") || jsonResult.contains("{\"gameId\":2") || jsonResult.contains("{\"gameId\":3") || jsonResult.contains("{\"gameId\":4") || jsonResult.contains("{\"gameId\":5") || jsonResult.contains("{\"gameId\":6"));
}
public String signupAndLogin() throws Exception {
User user = new User(1L, username1, password, location);
mvc.perform(post(signupUrl)
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user)))
.andExpect(status().isOk());
Optional<User> savedUser = userService.getUser(1L);
System.out.println(savedUser.toString());
//2nd User
User user2 = new User(2L, username2, password, location);
mvc.perform(post(signupUrl)
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user2)))
.andExpect(status().isOk());
Optional<User> savedUser2 = userService.getUser(2L);
System.out.println(savedUser2.toString());
//3rd User
User user3 = new User(3L, username3, password, "Bristol");
mvc.perform(post(signupUrl)
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user3)))
.andExpect(status().isOk());
Optional<User> savedUser3 = userService.getUser(3L);
System.out.println(savedUser3.toString());
//LOGIN
MvcResult mvcResult = mvc.perform(post(loginUrl)
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user)))
.andExpect(status().isOk())
.andReturn();
String token = mvcResult.getResponse().getHeader("Authorization");
System.out.println("Bearer Token: " + token);
return token;
}
#Transactional
public void createGames(String token) throws Exception {
Set<User> users = new HashSet<>();
Set<User> users2 = new HashSet<>();
User user = userRepository.findByUsername(username1);
User user2 = userRepository.findByUsername(username2);
User user3 = userRepository.findByUsername(username3);
System.out.println("ss: " + username1 + " dd " + username2 + " ff: " + username3);
//Add the User objects
users.add(user);
users.add(user2);
//Add the User objects
users2.add(user);
users2.add(user3);
System.out.println(users2.toString());
//Create 4 new Game objec
Game game = new Game(users, username1, username2username2);
Game game2 = new Game(users, username1, username2, username2);
Game game3 = new Game(users, username2, username1, username2);
Game game4 = new Game(users, username2, username1,username2);
//Create 2 new Game objects
Game game5 = new Game(users2, username3, username1, username3);
Game game6 = new Game(users2, username1, username3, username3);
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game)).header("Authorization", token))
.andExpect(status().isOk());
System.out.println("DEFEATS TEST : //////// :" + userRepository.findByUsername(username2).getUserStats().get(0).getDefeats());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game2)).header("Authorization", token))
.andExpect(status().isOk());
System.out.println(userRepository.findByUsername(username2).getUserStats().get(0).getVictoryScore());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game3)).header("Authorization", token))
.andExpect(status().isOk());
System.out.println(userRepository.findByUsername(username2).getUserStats().get(0).getVictoryScore());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game4)).header("Authorization", token))
.andExpect(status().isOk());
System.out.println(userRepository.findByUsername(username2).getUserStats().get(0).getVictoryScore());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game5)).header("Authorization", token))
.andExpect(status().isOk());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game6)).header("Authorization", token))
.andExpect(status().isOk());
}
I am not sure but perchance if I refactored this to use #BeforeEach it would work? I just don't know if it will work though.
I have looked into #BeforeEach and I am unsure how I could get the token from the signUp method call e.g:
#BeforeEach
void setup( WebApplicationContext wac) throws Exception {
this.mvc = MockMvcBuilders.webAppContextSetup( wac )
.apply(SecurityMockMvcConfigurers.springSecurity())
.alwaysDo( signupAndLogin() )
.build();
}
Thanks
Edit:
I have added this to the 2nd test:
#Test
public void getAUsersCompleteGameHistory() throws Exception {
userRepository.deleteAll();
gameRepository.deleteAll();
//This doesn't print anything.
for (User u: userRepository.findAll()
) {
System.out.println();
System.out.println(u.getUsername());
System.out.println();
}
//This passes, user Repo.findAll IS empty
assertTrue(userRepository.findAll().isEmpty());
assertTrue(gameRepository.findAll().isEmpty());
String token = signupAndLogin();
createGames(token);
ETC...
}
So the repo are empty, it must be something in signUpAndLogin?
You can put #Transactional on your test class, instead of every test case, if all of the test require a rollback. I am not sure why it doesn't work in your case, so i will offer you some workarounds.
You can do a cleanup after every test:
#AfterEach
public void cleanup() {
gameRepository.deleteAll();
userRepository.deleteAll();
}
That is assuming that your repositories do not contain previous data required to run tests correctly. Or create custom method to delete by username in your repo, to delete only users created for tests, for example:
userRepository.deleteByUsername(username);
In order to initialize the token in BeforeEach method, and then access it, you need to keep it in an instance variable:
#BeforeEach
public void setup() throws Exception {
//instance variable
this.token = signupAndLogin();
}
But that will also lead to constraint violation if the DB is not cleared. If it's the username constraint which causes the problem, you can somewhat hack it by assigning random values to usernames in tests - UUID.randomUUID().toString() for example.
As a side note, i believe assertions using assertThat should look like that:
assertThat(user2Victories == 1 && user2Defeats == 0).isEqualTo(true);
assertThat(user2Victories == 1 && user2Defeats == 0); actually does not assert anything.

Role permissions with keycloak instance

I have a keycloak instance which I use for CRUD operations with roles. Is there any way to get role permissions? I ve tried to search eveerything about it, but I cant find how to get permissions assigned to a role...
Here is an example of my code:
#RestController
#RequestMapping("/roles")
public class RolesController {
// must use "master" realm and "admin-cli" to connect to the instance
// although other realms and clients can be modified
private Keycloak keycloak = KeycloakBuilder.builder()
.serverUrl("http://localhost:8437/auth")
.realm("master")
.clientId("admin-cli")
.username("admin")
.password("admin")
.build();
#GetMapping
public ResponseEntity<List<RoleRepresentation>> getRoles() throws IOException {
return new ResponseEntity<>(keycloak.realm("dashing-data").roles().list(), HttpStatus.OK);
}
#PostMapping
public ResponseEntity<RoleRepresentation> createRole(#RequestBody RoleRepresentation role) throws IOException {
List<RoleRepresentation> roleList = keycloak.realm("dashing-data").roles().list();
boolean roleAlreadyExist = roleList.stream().anyMatch(r -> r.getName().contains(role.getName()));
RoleRepresentation newRole = new RoleRepresentation();
if (!roleAlreadyExist){
newRole.setName(role.getName());
newRole.setDescription(role.getDescription());
keycloak.realm("dashing-data").roles().create(newRole);
}
return new ResponseEntity<>(newRole, HttpStatus.OK);
}
#DeleteMapping("/{id}")
public ResponseEntity<String> deleteRole(#PathVariable String id){
RoleByIdResource role = keycloak.realm("dashing-data").rolesById();
if (role == null){
return new ResponseEntity<>("Could not find the role!", HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>("Role successfully deleted!", HttpStatus.OK);
}
}
I think you are adding complexity where you don't need it. A keycloak Role should be enough for what you are looking for. If you have a CRUD operation that only managers can do (like a DELETE) you simply create a Role in keycloak called "manager" and assign it to the user that you want. In your backend application, you just have to compare the role name to the required role. On the other hand for a Read operation, you can match if the user has the manager or read Role from keycloak.

Service call into Controller in Test not updating field correctly

I try some tests on my UserController for updating the email field like this :
#Test
public void testUpdateUserNominal() throws Exception {
savedUser.setEmail(EMAIL_2);
UserDTORequest updatedUserDTORequest = modelMapper.map(savedUser, UserDTORequest.class);
String jsonMessage = gson.toJson(updatedUserDTORequest);
mvc.perform(put(USER_ROUTE)
.contentType(APPLICATION_JSON)
.accept(APPLICATION_JSON)
.content(jsonMessage))
.andExpect(status().isOk())
.andReturn();
assertThat(userService.findAll().size()).isEqualTo(1);
assertThat(userService.findAll().get(0).getEmail()).isEqualTo(EMAIL_2);
}
Here is the code on my controller :
#Override
public ResponseEntity<UserDTOResponse> update(UserDTORequest userDTORequest) throws ParseException {
Optional<User> optUser = userService.findByUri(userDTORequest.getUri());
if(optUser.isEmpty()){
return ResponseEntity
.notFound()
.build();
}
User user = convertToEntity(userDTORequest);
User userUpdated = userService.save(user);
UserDTOResponse userDTOResponse = convertToDto(userUpdated);
return ResponseEntity
.ok(userDTOResponse);
}
The response from the mockMvc is correct : the new email set is the good one.
But on the second assertThat :
assertThat(userService.findAll().get(0).getEmail()).isEqualTo(EMAIL_2);
The email is not the good one, the email is not updated.
What I do wrong ?
Thanks :)

Categories