package de.asideas.crowdsource.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.asideas.crowdsource.domain.exception.NotAuthorizedException;
import de.asideas.crowdsource.domain.exception.ResourceNotFoundException;
import de.asideas.crowdsource.domain.model.UserEntity;
import de.asideas.crowdsource.presentation.Comment;
import de.asideas.crowdsource.service.CommentService;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import javax.annotation.Resource;
import java.util.Collections;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = CommentControllerTest.Config.class)
public class CommentControllerTest {
public static final String EXISTING_USER_MAIL = "test.name@test.de";
public static final String NON_EXISTING_USER_MAIL = "i_dont_exist@test.de";
private final static String EXISTING_PROJECT_ID = "TEST_PROJECT_ID";
private final static String NON_EXISTING_PROJECT_ID = "I_DONT_EXIST";
private final ObjectMapper mapper = new ObjectMapper();
private Comment aComment;
@Autowired
private CommentService commentService;
@Resource
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void init() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
reset(commentService);
final UserEntity userEntity = new UserEntity("test.name@test.de", "password");
aComment = new Comment(new DateTime(), userEntity.fullNameFromEmail(), "some comment");
when(commentService.loadCommentsByProject(EXISTING_PROJECT_ID)).thenReturn(Collections.singletonList(aComment));
}
@Test
public void comments() throws Exception {
final MvcResult mvcResult = mockMvc.perform(get("/project/" + EXISTING_PROJECT_ID + "/comments"))
.andExpect(status().isOk())
.andReturn();
assertThat(
mvcResult.getResponse().getContentAsString(),
is("[{\"created\":" + aComment.getCreated().getMillis()+ ",\"userName\":\"Test Name\",\"comment\":\"some comment\"}]"));
}
@Test
public void storeComment_validComment() throws Exception {
Comment comment = new Comment();
comment.setComment("message");
mockMvc.perform(post("/project/" + EXISTING_PROJECT_ID + "/comment")
.principal(new UsernamePasswordAuthenticationToken(EXISTING_USER_MAIL, "password"))
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(comment)))
.andExpect(status().isCreated());
verify(commentService).addComment(comment, EXISTING_PROJECT_ID, EXISTING_USER_MAIL);
}
@Test
public void storeComment_returnsBadRequestOnEmptyCommentMessage() throws Exception {
Comment comment = new Comment(null, null, "");
final MvcResult mvcResult = mockMvc.perform(post("/project/" + EXISTING_PROJECT_ID + "/comment")
.principal(new UsernamePasswordAuthenticationToken(EXISTING_USER_MAIL, "password"))
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(comment)))
.andExpect(status().isBadRequest()).andReturn();
assertThat(mvcResult.getResponse().getContentAsString(), is("{\"errorCode\":\"field_errors\",\"fieldViolations\":{\"comment\":\"may not be empty\"}}"));
verify(commentService, never()).addComment(any(Comment.class), any(String.class), any(String.class));
}
@Test
public void storeComment_shouldThrowNotFoundExceptionOnUnknownProjectId() throws Exception {
Comment comment = new Comment();
comment.setComment("this is an example comment that respects the length constraint");
doThrow(new ResourceNotFoundException()).when(commentService).addComment(any(Comment.class), any(String.class), any(String.class));
mockMvc.perform(post("/project/" + NON_EXISTING_PROJECT_ID + "/comment")
.principal(new UsernamePasswordAuthenticationToken(EXISTING_USER_MAIL, "password"))
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(comment)))
.andExpect(status().isNotFound());
}
@Test
public void storeComment_ShouldThrowUnauthorizedIfUserNotFound() throws Exception {
Comment comment = new Comment();
comment.setComment("this is an example comment that respects the length constraint");
doThrow(new NotAuthorizedException("No user found with email " + NON_EXISTING_USER_MAIL)).when(commentService)
.addComment(any(Comment.class), any(String.class), any(String.class));
mockMvc.perform(post("/project/" + EXISTING_PROJECT_ID + "/comment")
.principal(new UsernamePasswordAuthenticationToken(NON_EXISTING_USER_MAIL, "password"))
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(comment)))
.andExpect(status().isUnauthorized());
}
@Configuration
@EnableWebMvc
static class Config {
@Bean
public ControllerExceptionAdvice controllerExceptionAdvice() {
return new ControllerExceptionAdvice();
}
@Bean
public CommentController commentController() {
return new CommentController();
}
@Bean
public CommentService commentService(){
return mock(CommentService.class);
}
}
}