/**
* Copyright (C) 2011 JTalks.org Team
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jtalks.jcommune.web.controller;
import org.jtalks.jcommune.model.entity.*;
import org.jtalks.jcommune.service.*;
import org.jtalks.jcommune.plugin.api.exceptions.NotFoundException;
import org.jtalks.jcommune.service.dto.EntityToDtoConverter;
import org.jtalks.jcommune.service.nontransactional.BBCodeService;
import org.jtalks.jcommune.service.nontransactional.LocationService;
import org.jtalks.jcommune.plugin.api.web.dto.Breadcrumb;
import org.jtalks.jcommune.plugin.api.web.dto.PostDto;
import org.jtalks.jcommune.plugin.api.web.dto.PostDraftDto;
import org.jtalks.jcommune.plugin.api.web.dto.TopicDto;
import org.jtalks.jcommune.plugin.api.web.dto.json.JsonResponse;
import org.jtalks.jcommune.plugin.api.web.util.BreadcrumbBuilder;
import org.jtalks.jcommune.plugin.api.web.dto.json.JsonResponseStatus;
import org.mockito.ArgumentMatcher;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import static java.util.Arrays.asList;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.springframework.test.web.ModelAndViewAssert.*;
import static org.testng.Assert.assertEquals;
/**
* This is test for <code>PostController<code> class.
* Test should cover view resolution and logic validation.
*
* @author Osadchuck Eugeny
* @author Evgeniy Naumenko
*/
public class PostControllerTest {
@Mock
private PostService postService;
@Mock
private BBCodeService bbCodeService;
@Mock
private TopicModificationService topicModificationService;
@Mock
private TopicFetchService topicFetchService;
@Mock
private BreadcrumbBuilder breadcrumbBuilder;
@Mock
private LastReadPostService lastReadPostService;
@Mock
private UserService userService;
@Mock
private LocationService locationService;
@Mock
private EntityToDtoConverter converter;
@Mock
private HttpServletRequest request;
@Mock
private HttpSession session;
@Mock
private BindingResult result;
private RetryTemplate retryTemplate;
public static final long POST_ID = 1;
public static final long TOPIC_ID = 1L;
private static final Long BRANCH_ID = 1L;
private final String POST_CONTENT = "postContent";
private Post post;
private JCUser user = new JCUser("username", "email@mail.com", "password");
private PostController controller;
@BeforeMethod
public void init() throws NotFoundException {
initMocks(this);
Branch branch = new Branch("branch", "branch");
branch.setId(BRANCH_ID);
Topic topic = new Topic(user, "title");
topic.setBranch(branch);
topic.setId(TOPIC_ID);
post = new Post(user, POST_CONTENT);
post.setId(POST_ID);
post.setTopic(topic);
topic.getPosts().addAll(asList(post));
retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
when(postService.get(POST_ID)).thenReturn(post);
when(topicFetchService.get(TOPIC_ID)).thenReturn(topic);
when(breadcrumbBuilder.getForumBreadcrumb(topic)).thenReturn(new ArrayList<Breadcrumb>());
controller = new PostController(
postService, breadcrumbBuilder, topicFetchService, topicModificationService,
bbCodeService, lastReadPostService, userService, locationService, converter,
retryTemplate);
}
@Test
public void testInitBinder() {
WebDataBinder binder = mock(WebDataBinder.class);
controller.initBinder(binder);
verify(binder).registerCustomEditor(eq(String.class), any(StringTrimmerEditor.class));
}
@Test
public void testDeletePost() throws NotFoundException {
ModelAndView mav = controller.delete(POST_ID);
verify(postService).deletePost(post);
assertViewName(mav, "redirect:/posts/" + POST_ID);
}
@Test(expectedExceptions = NotFoundException.class)
public void testDeleteUnexistingPost() throws NotFoundException {
doThrow(new NotFoundException()).when(postService).get(anyLong());
controller.delete(POST_ID);
}
@Test
public void editPost() throws NotFoundException {
//invoke the object under test
ModelAndView actualMav = controller.editPage(POST_ID);
//check result
this.assertEditPostFormMavIsCorrect(actualMav);
PostDto dto = assertAndReturnModelAttributeOfType(actualMav, "postDto", PostDto.class);
assertEquals(dto.getId(), TOPIC_ID);
assertModelAttributeAvailable(actualMav, "breadcrumbList");
}
@Test
public void testUpdatePost() throws NotFoundException {
PostDto dto = getDto();
BindingResult bindingResult = new BeanPropertyBindingResult(dto, "postDto");
ModelAndView mav = controller.update(dto, bindingResult, POST_ID);
assertViewName(mav, "redirect:/posts/" + dto.getId());
verify(postService).updatePost(Matchers.<Post>any(), anyString());
}
@Test
public void updateWithError() throws NotFoundException {
PostDto dto = this.getDto();
BeanPropertyBindingResult resultWithErrors = mock(BeanPropertyBindingResult.class);
when(resultWithErrors.hasErrors()).thenReturn(true);
ModelAndView mav = controller.update(dto, resultWithErrors, POST_ID);
this.assertEditPostFormMavIsCorrect(mav);
verify(postService, never()).updatePost(Matchers.<Post>any(), anyString());
}
@Test
public void testGetQuotedAjax() throws NotFoundException {
String expected = "[quote=\"user\"]" + POST_CONTENT + "[/quote]";
when(bbCodeService.quote(POST_CONTENT, user)).thenReturn(expected);
JsonResponse response = controller.getQuote(post.getId(), null);
assertEquals(response.getResult(), expected);
}
@Test
public void testPartialQuotedAjax() throws NotFoundException {
String selection = "Content";
String expected = "[quote=\"user\"]" + selection + "[/quote]";
when(postService.get(anyLong())).thenReturn(post);
when(bbCodeService.quote(selection, user)).thenReturn(expected);
JsonResponse response = controller.getQuote(post.getId(), selection);
assertEquals(response.getResult(), expected);
}
@Test
public void testSubmitAnswerValidationPass() throws NotFoundException {
BeanPropertyBindingResult resultWithoutErrors = mock(BeanPropertyBindingResult.class);
TopicDto dto = new TopicDto(post.getTopic());
dto.setTopicUrl("/topics/" + TOPIC_ID);
when(converter.convertTopicToDto(any(Topic.class))).thenReturn(dto);
when(resultWithoutErrors.hasErrors()).thenReturn(false);
when(topicModificationService.replyToTopic(anyLong(), Matchers.<String>any(), eq(BRANCH_ID))).thenReturn(post);
when(postService.calculatePageForPost(post)).thenReturn(1);
//invoke the object under test
ModelAndView mav = controller.create(null, TOPIC_ID, getDto(), resultWithoutErrors, null);
//check expectations
verify(topicModificationService).replyToTopic(TOPIC_ID, POST_CONTENT, BRANCH_ID);
//check result
assertViewName(mav, "redirect:" + dto.getTopicUrl() + "?page=1#" + POST_ID);
}
@Test
public void testSubmitAnswerValidationFail() throws NotFoundException {
BeanPropertyBindingResult resultWithErrors = mock(BeanPropertyBindingResult.class);
when(resultWithErrors.hasErrors()).thenReturn(true);
//invoke the object under test
ModelAndView mav = controller.create(null, TOPIC_ID, getDto(), resultWithErrors, new RedirectAttributesModelMap());
//check expectations
verify(topicModificationService, never()).replyToTopic(anyLong(), anyString(), eq(BRANCH_ID));
//check result
assertEquals(mav.getViewName(), "redirect:/topics/error/" + TOPIC_ID + "?page=null");
}
@Test
public void testRedirectToPageWithPost() throws NotFoundException {
TopicDto dto = new TopicDto(post.getTopic());
dto.setTopicUrl("/topics/" + TOPIC_ID);
when(converter.convertTopicToDto(any(Topic.class))).thenReturn(dto);
when(postService.calculatePageForPost(post)).thenReturn(5);
String result = controller.redirectToPageWithPost(POST_ID);
assertEquals(result, "redirect:" + dto.getTopicUrl() + "?page=5#" + POST_ID);
}
@Test(expectedExceptions = NotFoundException.class)
public void testRedirectToPageWithPostNotFound() throws NotFoundException {
doThrow(new NotFoundException()).when(postService).get(anyLong());
controller.redirectToPageWithPost(POST_ID);
}
@Test
public void testPostPreview() throws Exception {
BeanPropertyBindingResult BindingResult = mock(BeanPropertyBindingResult.class);
ModelAndView mav = controller.preview(getDto(), BindingResult);
assertViewName(mav, "ajax/postPreview");
assertModelAttributeAvailable(mav, "errors");
assertModelAttributeAvailable(mav, "content");
assertModelAttributeAvailable(mav, "isInvalid");
}
@Test
public void testVoteUpSuccess() throws Exception {
JCUser user = new JCUser("username", "email@mail.com", "password");
Post post = new Post(user, "text");
when(postService.get(anyLong())).thenReturn(post);
when(request.getSession()).thenReturn(session);
JsonResponse response = controller.voteUp(1L, request);
assertEquals(response.getStatus(), JsonResponseStatus.SUCCESS);
verify(postService).vote(eq(post), argThat(new PostVoteMacher(true)));
}
@Test
public void testVoteDownSuccess() throws Exception{
JCUser user = new JCUser("username", "email@mail.com", "password");
Post post = new Post(user, "text");
when(postService.get(anyLong())).thenReturn(post);
when(request.getSession()).thenReturn(session);
JsonResponse response = controller.voteDown(1L, request);
assertEquals(response.getStatus(), JsonResponseStatus.SUCCESS);
verify(postService).vote(eq(post), argThat(new PostVoteMacher(false)));
}
@Test(expectedExceptions = NotFoundException.class)
public void voteUpShouldThrowExceptionIfPostNotFound() throws Exception {
when(postService.get(anyLong())).thenThrow(new NotFoundException());
when(request.getSession()).thenReturn(session);
controller.voteUp(1L, request);
}
@Test(expectedExceptions = NotFoundException.class)
public void voteDownShouldThrowExceptionIfPostNotFound() throws Exception {
when(postService.get(anyLong())).thenThrow(new NotFoundException());
when(request.getSession()).thenReturn(session);
controller.voteDown(1L, request);
}
@Test
public void testSaveDraft() throws Exception {
PostDraftDto dto = getPostDraftDto();
Topic topic = new Topic();
PostDraft saved = new PostDraft("content", new JCUser("name", null, null));
saved.setId(1);
when(topicFetchService.getTopicSilently(dto.getTopicId())).thenReturn(topic);
when(postService.saveOrUpdateDraft(topic, dto.getBodyText())).thenReturn(saved);
JsonResponse response = controller.saveDraft(dto, result);
assertEquals(response.getStatus(), JsonResponseStatus.SUCCESS);
assertEquals(1, (long)response.getResult());
}
@Test
public void saveDraftShouldReturnFailResponseIfValidationErrorsOccurred() throws Exception {
when(result.hasErrors()).thenReturn(true);
JsonResponse response = controller.saveDraft(getPostDraftDto(), result);
assertEquals(response.getStatus(), JsonResponseStatus.FAIL);
}
@Test(expectedExceptions = NotFoundException.class)
public void saveDraftShouldThrowExceptionIfTopicNotFound() throws Exception {
PostDraftDto dto = getPostDraftDto();
when(topicFetchService.getTopicSilently(dto.getTopicId())).thenThrow(new NotFoundException());
controller.saveDraft(dto, result);
}
@Test
public void testDeleteDraft() throws Exception {
controller.deleteDraft(1l);
verify(postService).deleteDraft(1l);
}
private class PostVoteMacher extends ArgumentMatcher<PostVote> {
private boolean isVotedUp;
public PostVoteMacher(boolean isVotedUp) {
this.isVotedUp = isVotedUp;
}
@Override
public boolean matches(Object o) {
//should be both true or both false
return o instanceof PostVote && !(isVotedUp ^ ((PostVote) o).isVotedUp());
}
}
/* @Test
public void testPreview() {
String postText = "[code]123[/code]";
String html = "<code>123</code>";
when(bbCodeService.convertBbToHtml(anyString())).thenReturn(html);
when(userService.getCurrentUser()).thenReturn(user);
String result = controller.preview(postText).getBody();
assertEquals(result, html);
verify(bbCodeService).convertBbToHtml(postText);
}*/
private void assertAnswerMavIsCorrect(ModelAndView mav) {
assertViewName(mav, "topic/answer");
assertAndReturnModelAttributeOfType(mav, "topic", Topic.class);
long topicId = assertAndReturnModelAttributeOfType(mav, "topicId", Long.class);
assertEquals(topicId, TOPIC_ID);
PostDto dto = assertAndReturnModelAttributeOfType(mav, "postDto", PostDto.class);
assertEquals(dto.getId(), 0);
assertModelAttributeAvailable(mav, "breadcrumbList");
}
private void assertEditPostFormMavIsCorrect(ModelAndView mav) {
assertViewName(mav, "topic/editPost");
long topicId = assertAndReturnModelAttributeOfType(mav, "topicId", Long.class);
long postId = assertAndReturnModelAttributeOfType(mav, "postId", Long.class);
assertEquals(topicId, TOPIC_ID);
assertEquals(postId, POST_ID);
}
private PostDto getDto() {
PostDto dto = new PostDto();
dto.setId(POST_ID);
dto.setBodyText(POST_CONTENT);
dto.setTopicId(TOPIC_ID);
return dto;
}
private PostDraftDto getPostDraftDto() {
PostDraftDto dto = new PostDraftDto();
dto.setBodyText(POST_CONTENT);
dto.setTopicId(TOPIC_ID);
return dto;
}
}