I get always that java.lang.AssertionError: Status expected:<200> but was:<404> - spring-boot

i added a mocked user this my class
#TestConfiguration
public class UserDetailsMock {
final String apiAccessGroup = "00000000-0000-0000-0000-000000000000";
#Bean
#Primary
public UserDetailsService userDetailsService() {
User ericssonUser = new User("ericsson-worker", "secret", Arrays.asList());
User msApiUser = new User("msApiUser", "StrongPass", Arrays.asList(new SimpleGrantedAuthority(apiAccessGroup)));
return new InMemoryUserDetailsManager(Arrays.asList(
ericssonUser, msApiUser
));
}
}
and this my test Class any help please!!!!
#SpringBootTest(
classes = UserDetailsMock.class
)
#AutoConfigureMockMvc
public class MuninServiceAuthorizationTest {
#Autowired
MockMvc mockMvc;
List<MockHttpServletRequestBuilder> endpoints;
#BeforeEach
public void setUp() throws IOException {
MockitoAnnotations.openMocks(this);
endpoints = Arrays.asList(
MockMvcRequestBuilders.post("/migrate/foss").with(csrf()),
MockMvcRequestBuilders.get("/swagger/ui/**"));
}
#Test
#WithUserDetails("msApiUser")
void userWithGroupWhichIsAllowedToUseApiShouldGet200() throws Exception {
for (MockHttpServletRequestBuilder mockHttpServletRequestBuilder : endpoints) {
mockMvc.perform(mockHttpServletRequestBuilder)
.andExpect(status().isOk());
}
}
}

Related

Spring boot SecurityContextHolder.getContext() NPE when using #Async in a #Scheduled service

I am using a scheduled service in spring boot app , i need to get the current connected user inside that service , my problem is that
SecurityContextHolder.getContext().getAuthentication()
returns the current connected user only once ( just after i am logged in ) , but in the next running tasks
SecurityContextHolder.getContext().getAuthentication()
returns NPE , i have searched and found that SecurityContextHolder is not shared outside the main thread.
My Service :
#Service
#EnableScheduling
public class SsePushNotificationService {
public void addEmitter(final SseEmitter emitter) {
emitters.add(emitter);
}
public void removeEmitter(final SseEmitter emitter) {
emitters.remove(emitter);
}
#Async("taskExecutor")
#Scheduled(fixedDelay = 5000)
public void doNotify() throws IOException {
System.out.println("------##### inside doNotify");
System.out.println("##### ---- curent thread /notification : " + Thread.currentThread().getName());
if (SecurityContextHolder.getContext().getAuthentication() != null) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails) principal).getUsername();
System.out.println("------##### principal instanceof UserDetails : " + username);
} else {
String username = principal.toString();
System.out.println("------##### principal : " + username);
}
}
}
}
the controller :
#Controller
#CrossOrigin(origins = "*")
public class SsePushNotificationRestController {
#Autowired
SsePushNotificationService service;
#Autowired
UserDetailsServiceImpl userService;
#Autowired
UserNotificationService userNotifService;
final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();
String username;
int nbrEvent;
#GetMapping(value = "/notification", produces = { MediaType.TEXT_EVENT_STREAM_VALUE })
public ResponseEntity<SseEmitter> doNotify() throws InterruptedException, IOException {
System.out.println("##### ---- curent thread /notification : " + Thread.currentThread().getName());
final SseEmitter emitter = new SseEmitter();
service.addEmitter(emitter);
service.doNotify();
emitter.onCompletion(() -> service.removeEmitter(emitter));
emitter.onTimeout(() -> service.removeEmitter(emitter));
return new ResponseEntity<>(emitter, HttpStatus.OK);
}
}
Javascript :
const eventSource = new EventSource('http://localhost:8080/notification');
eventSource.onmessage = e => {
const msg = e.data;
$("#notifCounter").text(msg);
$("#usrNotifCounter").text(msg);
};
eventSource.onopen = e => console.log('open');
eventSource.onerror = e => {
if (e.readyState == EventSource.CLOSED) {
console.log('close');
}
else {
console.log(e);
}
};
eventSource.addEventListener('second', function(e) {
console.log('second', e.data);
}, false);
WebSecurityConfig :
#Configuration
#EnableWebSecurity
public class WebSecurityConfig<S extends Session> extends WebSecurityConfigurerAdapter {
#Autowired
private FindByIndexNameSessionRepository<S> sessionRepository;
#Autowired
private MySessionExpiredStrategy sessionExpiredStrategy;
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Component
public class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {
#Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event)
throws IOException, ServletException {
HttpServletResponse response = event.getResponse();
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(
"Your account has been logged in elsewhere, and the current login has expired. If the password is leaked, please change it immediately!");
}
}
#Bean
public SpringSessionBackedSessionRegistry<S> sessionRegistry() {
return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
}
#Bean
public ConcurrentSessionControlAuthenticationStrategy sessionControlAuthenticationStrategy() {
ConcurrentSessionControlAuthenticationStrategy csas = new ConcurrentSessionControlAuthenticationStrategy(
sessionRegistry());
csas.setExceptionIfMaximumExceeded(true);
return csas;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/img/**", "/error");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
.anyRequest().access("#rbacService.hasPermission(request,authentication)")
.and().formLogin().loginPage("/login").defaultSuccessUrl("/", true).permitAll().and().logout()
.deleteCookies("JSESSIONID").invalidateHttpSession(true).clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login?logout")
.permitAll().and().exceptionHandling().accessDeniedPage("/static/403")
.and().sessionManagement().sessionFixation().migrateSession()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/static/invalidSession.html").maximumSessions(2).maxSessionsPreventsLogin(false)
.expiredSessionStrategy(sessionExpiredStrategy).sessionRegistry(sessionRegistry())
.expiredUrl("/login?invalid-session=true");
}
}
what is the best approch to share SecurityContextHolder between threads in that case.

MockMvc response returns 404, expected response 201

I am new to unit testing REST API in Spring Boot.
I am expecting response status as CREATED but instead I am getting a PAGE NOT FOUND error.
Below is the code for:-
UserControllerUnitTests
#SpringBootTest
#ContextConfiguration(classes = { CommonConfig.class, SecurityConfig.class})
#RunWith(SpringRunner.class)
class UserControllerUnitTests {
private static ObjectMapper mapper;
private static final String URI = "/users";
MockMvc mvc;
#Autowired
WebApplicationContext webAppContext;
#Mock
UserService userService;
MvcResult mvcResult;
#BeforeAll
static void setUp() {
mapper = new ObjectMapper();
}
#BeforeEach
void initialize() throws Exception {
mvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
....
....
....
void shouldReturnStatusCreatedIfValidUserPassedForPostUser(long index) throws Exception {
int expectedStatus = HttpStatus.CREATED.value();
UserDAO returnUser;
UserDAO user = userList.get(index);
userList.remove(index);
String jsonContent = mapper.writeValueAsString(user);
user.setId(index);
user.setEncryptedPassword(null);
Mockito.when(userService.addUser(Mockito.any())).thenReturn(user);
mvcResult = mvc.perform(MockMvcRequestBuilders.post(URI)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(jsonContent)).andReturn();
//Mockito.verify(userService, Mockito.times(1)).addUser(Mockito.any());
int actualStatus = mvcResult.getResponse().getStatus();
Assert.assertEquals("Response status should be CREATED", expectedStatus, actualStatus);
jsonContent = mvcResult.getResponse().getContentAsString();
returnUser = mapper.readValue(jsonContent, UserDAO.class);
Assert.assertEquals("EncryptedPassword should not be returned", null,
returnUser.getEncryptedPassword());
}
User Controller.class
#RestController
#RequestMapping("users/")
public class UserController {
UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
....
....
....
#PostMapping(path = "",
consumes = { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON },
produces = { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public ResponseEntity<UserDAO> createUser(#Valid #RequestBody UserDAO user) {
String password = user.getEncryptedPassword();
user.setEncryptedPassword(null);
UserDAO retreivedUser;
if(user.getId() != 0)
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
user.setEncryptedPassword(password);
retreivedUser = userService.addUser(user);
if(retreivedUser != null)
return new ResponseEntity<>(retreivedUser, HttpStatus.CREATED);
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
}
The full code can be found at https://github.com/vineethmaller/springboot-userservice
I spotted a few errors:
Get rid of ContextConfiguration
#SpringBootTest
// #ContextConfiguration(classes = { CommonConfig.class, SecurityConfig.class})
#RunWith(SpringRunner.class)
class UserControllerUnitTests {
Specify correct mapping on the controller (no slash)
#RestController
#RequestMapping("users")
public class UserController {
You setup your UserService mock which is not used in the test. Did you mean #MockBean?
#MockBean
UserService userService;

How to Mock ReloadableResourceBundleMessageSource in this case

I have a Rest controller class as shown below
#RestController
public class MyController {
#Autowired
private MYHelper myHelper;
#Autowired
#Qualifier("environmentMessageSource")
protected ReloadableResourceBundleMessageSource environmentMessageSource;
#PostMapping(value = {
employees
})
public #ResponseBody Map < String, Object > getEmployees(#RequestBody MyVO myvo) throws Exception {
Map < String, Object > responseMap = new HashMap < > ();
try {
responseMap = oeHelper.getDataService(environmentMessageSource.getMessage("MYPROP", null, null), myvo);
} catch (Exception e) {
}
return responseMap;
}
}
I am trying to write a Test Case for my controller . and its failing at ReloadableResourceBundleMessageSource
#RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
private MockMvc mockMvc;
#Mock
private OEHelper myHelper;
#InjectMocks
private MyController myController;
#Mock
protected ReloadableResourceBundleMessageSource environmentMessageSource;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
environmentMessageSource.setCacheSeconds(0);
}
#Test
public void testgetESIDAndCalendarDates() throws Exception {
Map < String, Object > responseMap = new HashMap < > ();
MyVO myVO = new MyVO();
Mockito.when(messageSource.getMessage(Mockito.anyString(), Mockito.any(Object[].class), Mockito.any(Locale.class)))
.thenReturn("");
Mockito
.when(myHelper.getResponseFromService(Mockito.any(), Mockito.any(myVO.class)))
.thenReturn(responseMap);
mockMvc.perform(post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(myVO))
)
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
Its saying , it cannot find the key MYPROP in environpropertiesfile for locale null
org.springframework.context.NoSuchMessageException: No message found under code 'MYPROP' for locale 'null'.

Spring MVC Testing with mixed calls through MockMvc and a Service using #WithMockUser fails - SecurityContext is cleared by MockMvc

I get An Authentication object was not found in the SecurityContext error when I call a #Service after calling a #RestController through MockMvc in a test method annotated with #WithMockUser.
Calling in the opposite order works fine as does calling the service and controller individually. The #Service is clearing the SecurityContextHolder. The only workaround I can think of is to set the context manually. Am I missing something?
I need to call the service to mutate state and then test that the controller returns the correct state.
#Service
public class SimpleService {
#Secured("ROLE_SIMPLE_USER")
public void doThis(Long id) {
System.out.println("dothis : " + id);
}
#Secured("ROLE_SIMPLE_USER")
public void doThat(Long id) {
System.out.println("dothat : " + id);
}
}
#RestController
#RequestMapping("/api")
#Secured("ROLE_SIMPLE_USER")
public class SimpleController {
#Autowired
private SimpleService simpleService;
#PostMapping(value = "{id}/dothis")
public ResponseEntity dothis(#PathVariable("id") Long id) {
simpleService.doThis(id);
return ResponseEntity.status(HttpStatus.OK).build();
}
#PostMapping(value = "{id}/dothat")
public ResponseEntity dothat(#PathVariable("id") Long id) {
simpleService.doThat(id);
return ResponseEntity.status(HttpStatus.OK).build();
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#ActiveProfiles({"devh2"})
public class SimpleControllerTest {
#Autowired
private SimpleService simpleService;
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUpLiabilities() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaControllers() throws Exception {
mockMvc.perform(post("/api/1/dothis").with(csrf())).andExpect(status().isOk());
mockMvc.perform(post("/api/1/dothat").with(csrf())).andExpect(status().isOk());
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaService() throws Exception {
simpleService.doThis(1L);
simpleService.doThat(1L);
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaServiceThenControllers() throws Exception {
simpleService.doThis(1L);
mockMvc.perform(post("/api/1/dothat").with(csrf())).andExpect(status().isOk());
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaControllerThenService() throws Exception {
mockMvc.perform(post("/api/1/dothis").with(csrf())).andExpect(status().isOk());
// ***FAILS*** because SecurityContextPersistenceFilter clears the SecurityContext ???
simpleService.doThat(1L);
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaControllerThenServiceWithWorkaround() throws Exception {
mockMvc.perform(post("/api/1/dothis").with(csrf())).andExpect(status().isOk());
setSecurityContextAndThenCallDoThat(1L, "simple.user", "******", "ROLE_SIMPLE_USER");
}
// Workaround - set context manually
private void setSecurityContextAndThenCallDoThat(long id, String username, String password, String... roles) {
try {
final SecurityContextImpl holder = new SecurityContextImpl();
holder.setAuthentication(new TestingAuthenticationToken(username, password, roles));
SecurityContextHolder.setContext(holder);
this.simpleService.doThat(id);
} finally {
SecurityContextHolder.clearContext();
}
}
}

Spring tests - mock returns null

I have a small issue with mocking methods . Where mock should return an predefined object, it returns null. here is test set up:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {
#Autowired
WebApplicationContext webContext;
#MockBean
UserTransferService userTransferService;
#MockBean
UserService userService;
MockMvc mockMvc;
private User user = DummyObjects.getDummyUser();
private User modifiedUser = DummyObjects.getModifiedUser();
private UserTO userTO = DummyObjects.getDummyUserTO();
private UserTO modifiedUserTO = DummyObjects.getModifiedUserTO();
#Before
public void setUp() throws Exception {
List<User> users = new ArrayList<>();
users.add(modifiedUser);
users.add(user);
given(this.userTransferService.getTO(user)).willReturn(userTO);
given(this.userTransferService.getObject(userTO)).willReturn(user);
given(this.userTransferService.getTO(modifiedUser)).willReturn(modifiedUserTO);
given(this.userTransferService.getObject(modifiedUserTO)).willReturn(modifiedUser);
given(this.userService.findAll()).willReturn(users);
given(this.userService.save(user)).willReturn(user);
given(this.userService.removeById(1)).willReturn(true);
given(this.userService.getById(1)).willReturn(user);
given(this.userService.getById(2)).willReturn(null);
given(this.userService.modify(modifiedUser)).willReturn(modifiedUser);
given(this.userService.findByLogin(user.getLogin())).willReturn(user);
given(this.userService.findByLogin("AAA")).willReturn(null);
mockMvc = MockMvcBuilders
.webAppContextSetup(webContext)
.apply(springSecurity())
.build();
}
test itself:
#Test
public void shouldAddUser() throws Exception {
mockMvc.perform(post("/api/user")
.content(TestingUtility.asJsonString(userTO))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.with(csrf().asHeader()))
.andExpect(status().is(200))
.andExpect(content().json(TestingUtility.asJsonString(userTO)));
}
and Controller method:
#RequestMapping(method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public UserTO modifyUser(#RequestBody UserTO userTO, UsernamePasswordAuthenticationToken principal) throws IllegalAccessException{
User user = userTransferService.getObject(userTO);
if (principal.getName().equals(userTO.getLogin()) || permissionService.hasPermission(principal,Permission.SU)) {
return userTransferService.getTO(userService.modify(user));
} else {
throw new IllegalAccessException("You are not allowed to modify user");
}
}
User is null, but UserTO is filled. So mocked method UserTransferService.getObject(userTO) is not working properly.

Resources