/* * Copyright (C) 2008-2017, Juick * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package com.juick.www; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.css.StyleElement; import com.gargoylesoftware.htmlunit.html.DomElement; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.juick.Message; import com.juick.Tag; import com.juick.User; import com.juick.configuration.RepositoryConfiguration; import com.juick.service.ImagesService; import com.juick.service.MessagesService; import com.juick.service.MockImagesService; import com.juick.service.UserService; import com.juick.util.MessageUtils; import com.juick.www.configuration.SapeConfiguration; import com.juick.www.configuration.WebSecurityConfig; import com.juick.www.configuration.WwwAppConfiguration; import com.juick.www.configuration.WwwServletConfiguration; import com.mitchellbosecke.pebble.PebbleEngine; import com.mitchellbosecke.pebble.error.PebbleException; import com.mitchellbosecke.pebble.template.PebbleTemplate; import org.apache.commons.text.StringEscapeUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.mock.web.MockMultipartFile; 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.htmlunit.MockMvcWebClientBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import javax.inject.Inject; import java.io.FileInputStream; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.Collections; import java.util.stream.IntStream; import java.util.stream.StreamSupport; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.startsWith; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * Created by vitalyster on 12.01.2017. */ @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(classes = { WwwServletConfiguration.class, WwwAppConfiguration.class, SapeConfiguration.class, RepositoryConfiguration.class, WebSecurityConfig.class }) public class WebAppTests { @Configuration @ComponentScan(basePackages = "com.juick.www.controllers") static class Config { @Bean public ImagesService imagesService() { return new MockImagesService(); } } @Inject private WebApplicationContext wac; @Inject private WebApp webApp; private static MockMvc mockMvc; private static WebClient webClient; @Inject UserService userService; @Inject MessagesService messagesService; @Inject JdbcTemplate jdbcTemplate; @Inject PebbleEngine pebbleEngine; private static User ugnich, freefd; private static String ugnichName, ugnichPassword, freefdName, freefdPassword; private static boolean isSetUp = false; @Before public void setup() { if (!isSetUp) { mockMvc = MockMvcBuilders.webAppContextSetup(wac) .apply(springSecurity()) .build(); webClient = MockMvcWebClientBuilder.mockMvcSetup(mockMvc).build(); webClient.getOptions().setJavaScriptEnabled(false); webClient.getOptions().setCssEnabled(false); webClient.getOptions().setThrowExceptionOnFailingStatusCode(false); ugnichName = "ugnich"; ugnichPassword = "MyPassw0rd!"; freefdName = "freefd"; freefdPassword = "MyPassw0rd!"; int ugnichId = userService.createUser(ugnichName, ugnichPassword); ugnich = userService.getUserByUID(ugnichId).orElseThrow(IllegalStateException::new); int freefdId = userService.createUser(freefdName, freefdPassword); freefd = userService.getUserByUID(freefdId).orElseThrow(IllegalStateException::new); isSetUp = true; } } @Test public void postWithoutTagsShouldNotHaveAsteriskInTitle() throws Exception { String msgText = "Привет, я - Угнич"; int mid = messagesService.createMessage(ugnich.getUid(), msgText, null, null); HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); assertThat(threadPage.getTitleText(), equalTo("ugnich:")); } @Test public void bannedUserBlogandPostShouldReturn404() throws IOException { String userName = "isilmine"; String userPassword = "secret"; String msgText = "автор этого поста был забанен"; String hash = "12345678"; User isilmine = userService.getUserByUID(userService.createUser(userName, userPassword)).orElseThrow(IllegalStateException::new); int mid = messagesService.createMessage(isilmine.getUid(), msgText, null, null); jdbcTemplate.update("UPDATE users SET banned=1 WHERE id=?", isilmine.getUid()); Page blogPage = webClient.getPage("http://localhost:8080/isilmine"); Page threadPage = webClient.getPage(String.format("http://localhost:8080/isilmine/%d", mid)); assertThat(blogPage.getWebResponse().getStatusCode(), equalTo(404)); assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(404)); } @Test public void repliesTree() throws IOException { int mid = messagesService.createMessage(ugnich.getUid(), "hello", null, null); IntStream.range(1, 15).forEach(i -> messagesService.createReply(mid, i-1, freefd.getUid(), String.valueOf(i-1), null )); HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200)); Long visibleItems = StreamSupport.stream(threadPage.getHtmlElementById("replies") .getChildElements().spliterator(), false).filter(e -> { StyleElement display = e.getStyleElement("display"); return display == null || !display.getValue().equals("none"); }).count(); assertThat(visibleItems, equalTo(1L)); } @Test public void correctTagsEscaping() throws PebbleException, IOException { PebbleTemplate template = pebbleEngine.getTemplate("views/test"); Writer writer = new StringWriter(); template.evaluate(writer, Collections.singletonMap("tagsList", Collections.singletonList(StringEscapeUtils.escapeHtml4(new Tag(">_<").getName())))); String output = writer.toString().trim(); assertThat(output, equalTo(">_<")); } public DomElement fetchMeta(String url, String name) throws IOException { HtmlPage page = webClient.getPage(url); DomElement emptyMeta = new DomElement("", "meta", null, null); return page.getElementsByTagName("meta").stream() .filter(t -> t.getAttribute("name").equals(name)).findFirst().orElse(emptyMeta); } @Test public void testTwitterCards() throws Exception { int mid = messagesService.createMessage(ugnich.getUid(), "without image", null, null); assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid), "twitter:card") .getAttribute("content"), equalTo("summary")); int mid2 = messagesService.createMessage(ugnich.getUid(), "with image", "png", null); Message message = messagesService.getMessage(mid2); assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid2), "twitter:card") .getAttribute("content"), equalTo("summary_large_image")); assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid2), "og:description") .getAttribute("content"), startsWith(StringEscapeUtils.escapeHtml4(MessageUtils.getMessageHashTags(message)))); } @Test public void postMessageTests() throws Exception { mockMvc.perform(post("/post").param("body", "yo")).andExpect(redirectedUrl("http://localhost/login")); MvcResult loginResult = mockMvc.perform(post("/login") .param("username", ugnichName) .param("password", ugnichPassword)).andReturn(); mockMvc.perform(post("/post") .cookie(loginResult.getResponse().getCookies()) .param("wrong_param", "yo")).andExpect(status().isBadRequest()); mockMvc.perform(post("/post") .cookie(loginResult.getResponse().getCookies()) .param("body", "yo")).andExpect(status().isOk()); mockMvc.perform(post("/post") .cookie(loginResult.getResponse().getCookies()) .param("img", "http://static.juick.com/settings/facebook.png")).andExpect(status().isOk()); mockMvc.perform(post("/post") .cookie(loginResult.getResponse().getCookies()) .param("img", "bad_url")).andExpect(status().isBadRequest()); FileInputStream fi = new FileInputStream(new ClassPathResource("tagscloud.png").getFile()); MockMultipartFile file = new MockMultipartFile("attach", fi); mockMvc.perform(multipart("/post") .file(file) .cookie(loginResult.getResponse().getCookies())).andExpect(status().isOk()); int mid = messagesService.createMessage(ugnich.getUid(), "dummy message", null, null); mockMvc.perform(post("/comment") .param("mid", String.valueOf(mid)) .param("body", "yo")).andExpect(redirectedUrl("http://localhost/login")); mockMvc.perform(post("/comment") .cookie(loginResult.getResponse().getCookies()) .param("wrong_param", "yo")).andExpect(status().isBadRequest()); mockMvc.perform(post("/comment") .cookie(loginResult.getResponse().getCookies()) .param("mid", String.valueOf(mid)) .param("wrong_param", "yo")).andExpect(status().isBadRequest()); mockMvc.perform(post("/comment") .cookie(loginResult.getResponse().getCookies()) .param("mid", String.valueOf(mid)) .param("img", "http://static.juick.com/settings/facebook.png")).andExpect(status().isFound()); mockMvc.perform(multipart("/comment") .file(file) .cookie(loginResult.getResponse().getCookies()) .param("mid", String.valueOf(mid))).andExpect(status().isFound()); mockMvc.perform(post("/comment") .cookie(loginResult.getResponse().getCookies()) .param("mid", String.valueOf(mid)) .param("body", "yo")).andExpect(redirectedUrl(String.format("/%s/%d#%d", ugnichName, mid, 3))); } }