/** * 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.model.dao.search.hibernate; import org.apache.commons.lang.StringUtils; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.search.FullTextSession; import org.hibernate.search.Search; import org.jtalks.jcommune.model.entity.*; import org.jtalks.jcommune.model.dto.PageRequest; import org.jtalks.jcommune.model.search.SearchRequestFilter; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.Arrays; import java.util.List; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; /** * @author Anuar Nurmakanov */ @ContextConfiguration(locations = {"classpath:/org/jtalks/jcommune/model/entity/applicationContext-dao.xml"}) @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) @Transactional public class TopicHibernateSearchDaoTest extends AbstractTransactionalTestNGSpringContextTests { private static final int PAGE_SIZE = 50; private static final String TOPIC_CONTENT = "topicContent"; private static final PageRequest DEFAULT_PAGE_REQUEST = new PageRequest("1", 50); @Autowired private SessionFactory sessionFactory; @Autowired private TopicHibernateSearchDao topicSearchDao; @Mock private SearchRequestFilter invalidCharactersFilter; @Mock private SearchRequestFilter stopWordsFilter; private FullTextSession fullTextSession; @BeforeMethod public void init() { Session session = sessionFactory.getCurrentSession(); PersistedObjectsFactory.setSession(session); MockitoAnnotations.initMocks(this); List<SearchRequestFilter> filters = Arrays.asList(invalidCharactersFilter, stopWordsFilter); topicSearchDao.setFilters(filters); } private void configureMocks(String searchText, String result) { Mockito.when(invalidCharactersFilter.filter(searchText)).thenReturn(result); Mockito.when(stopWordsFilter.filter(searchText)).thenReturn(result); } @BeforeMethod public void initHibernateSearch() throws InterruptedException { Session session = sessionFactory.getCurrentSession(); fullTextSession = Search.getFullTextSession(session); fullTextSession.createIndexer().startAndWait(); } @AfterMethod public void clearIndexes() { fullTextSession.purgeAll(Topic.class); fullTextSession.flushToIndexes(); } /*===== Paging testing =====*/ @Test public void testSearchPaging() { int totalSize = 50; int pageCount = 2; int pageSize = totalSize / pageCount; String searchText = "JCommune"; PageRequest pageRequest = new PageRequest("1", pageSize); List<Topic> topicList = PersistedObjectsFactory.createAndSaveTopicList(totalSize); for (Topic topic : topicList) { topic.setTitle(searchText); } saveAndFlushIndexes(topicList); configureMocks(searchText, searchText); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( searchText, pageRequest, Arrays.asList(topicList.get(0).getBranch().getId())); assertEquals(searchResultPage.getContent().size(), pageSize, "Incorrect count of topics in one page."); assertEquals(searchResultPage.getTotalElements(), totalSize, "Incorrect total count."); assertEquals(searchResultPage.getTotalPages(), pageCount, "Incorrect count of pages."); } /*===== Testing of different variations of the search. =====*/ @Test public void testSearchWithFullyDirtySearchText() { configureMocks(StringUtils.EMPTY, StringUtils.EMPTY); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( StringUtils.EMPTY, DEFAULT_PAGE_REQUEST, Arrays.asList(1L)); Assert.assertTrue(!searchResultPage.hasContent(), "Search result must be empty."); } @Test(dataProvider = "parameterFullPhraseSearch") public void testFullPhraseSearch(String content) { Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.setTitle(content); saveAndFlushIndexes(Arrays.asList(expectedTopic)); configureMocks(content, content); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( content, DEFAULT_PAGE_REQUEST, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertTrue(searchResultPage.hasContent(), "Search result must not be empty."); for (Topic topic : searchResultPage.getContent()) { Assert.assertEquals(expectedTopic.getTitle(), topic.getTitle(), "Content from the index should be the same as in the database."); } } @Test public void testFullPhraseSearchPageNumberTooLow() { Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.setTitle(TOPIC_CONTENT); saveAndFlushIndexes(Arrays.asList(expectedTopic)); configureMocks(TOPIC_CONTENT, TOPIC_CONTENT); PageRequest pageRequest = new PageRequest("-1", PAGE_SIZE); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( TOPIC_CONTENT, pageRequest, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertEquals(searchResultPage.getNumber(), 1); Assert.assertTrue(searchResultPage.hasContent(), "Search result must not be empty."); for (Topic topic : searchResultPage.getContent()) { Assert.assertEquals(expectedTopic.getTitle(), topic.getTitle(), "Content from the index should be the same as in the database."); } } @Test public void testFullPhraseSearchPageNumberTooBig() { Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.setTitle(TOPIC_CONTENT); saveAndFlushIndexes(Arrays.asList(expectedTopic)); configureMocks(TOPIC_CONTENT, TOPIC_CONTENT); PageRequest pageRequest = new PageRequest("1000", 50); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent(TOPIC_CONTENT, pageRequest, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertEquals(searchResultPage.getNumber(), 1); Assert.assertTrue(searchResultPage.hasContent(), "Search result must not be empty."); for (Topic topic : searchResultPage.getContent()) { Assert.assertEquals(expectedTopic.getTitle(), topic.getTitle(), "Content from the index should be the same as in the database."); } } @Test(dataProvider = "parameterFullPhraseSearch") public void testPostContentSearch(String content) { Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.getLastPost().setPostContent(content); saveAndFlushIndexes(Arrays.asList(expectedTopic)); configureMocks(content, content); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( content, DEFAULT_PAGE_REQUEST, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertTrue(searchResultPage.hasContent(), "Search result must not be empty."); for (Topic topic : searchResultPage.getContent()) { Assert.assertEquals(expectedTopic.getTitle(), topic.getTitle(), "Content from the index should be the same as in the database."); } } @DataProvider(name = "parameterFullPhraseSearch") public Object[][] parameterFullPhraseSearch() { return new Object[][]{ {"Содержимое темы."}, {"Topic content."} }; } @Test(dataProvider = "parameterPiecePhraseSearch") public void testPiecePhraseSearch(String firstPiece, char delimeter, String secondPiece) { String content = firstPiece + delimeter + secondPiece; Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.setTitle(content); saveAndFlushIndexes(Arrays.asList(expectedTopic)); for (String piece : Arrays.asList(firstPiece, secondPiece)) { configureMocks(piece, piece); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( piece, DEFAULT_PAGE_REQUEST, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertTrue(searchResultPage.hasContent(), "Search result must not be empty."); } } @DataProvider(name = "parameterPiecePhraseSearch") public Object[][] parameterPiecePhraseSearch() { return new Object[][]{ {"Содержимое", ' ', "топика"}, {"Topic", ' ', "content"} }; } @Test(dataProvider = "parameterIncorrectPhraseSearch") public void testIncorrectPhraseSearch(String correct, String incorrect) { Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.setTitle(correct); saveAndFlushIndexes(Arrays.asList(expectedTopic)); configureMocks(incorrect, incorrect); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( incorrect, DEFAULT_PAGE_REQUEST, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertTrue(!searchResultPage.hasContent(), "Search result must be empty."); } private <E> void saveAndFlushIndexes(List<E> entityList) { for (E entity : entityList) { fullTextSession.save(entity); } fullTextSession.flushToIndexes(); } @DataProvider(name = "parameterIncorrectPhraseSearch") public Object[][] parameterIncorrectPhraseSearch() { return new Object[][]{ {"Содержимое поста", "Железный человек"}, {"Post content", "Iron Man"} }; } @Test(dataProvider = "parameterSearchByRoot") public void testSearchByRoot(String word, String wordWithSameRoot) { Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.setTitle(word); saveAndFlushIndexes(Arrays.asList(expectedTopic)); configureMocks(wordWithSameRoot, wordWithSameRoot); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( wordWithSameRoot, DEFAULT_PAGE_REQUEST, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertTrue(searchResultPage.hasContent(), "Search result must not be empty."); } @DataProvider(name = "parameterSearchByRoot") public Object[][] parameterSearchByRoot() { return new Object[][]{ {"Keys", "Key"}, {"Key", "Keys"}, {"testing", "Tests"}, {"tests", "TeStIng"}, {"Полеты", "полет"}, {"барабан", "барабаны"} }; } @Test(dataProvider = "parameterSearchByBbCodes") public void testSearchByBbCodes(String content, String bbCode) { Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.getLastPost().setPostContent(content); saveAndFlushIndexes(Arrays.asList(expectedTopic)); configureMocks(bbCode, bbCode); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( bbCode, DEFAULT_PAGE_REQUEST, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertTrue(!searchResultPage.hasContent(), "Search result must be empty."); } @DataProvider(name = "parameterSearchByBbCodes") public Object[][] parameterSearchByBbCodes() { return new Object[][]{ {"[code=java]spring[/code]", "code"}, {"[b]gwt[/b]", "b"} }; } @Test(dataProvider = "parameterSearchByBbCodesContent") public void testSearchByBbCodesContent(String content, String bbCodeContent) { Topic expectedTopic = PersistedObjectsFactory.getDefaultTopic(); expectedTopic.getLastPost().setPostContent(content); saveAndFlushIndexes(Arrays.asList(expectedTopic)); configureMocks(bbCodeContent, bbCodeContent); Page<Topic> searchResultPage = topicSearchDao.searchByTitleAndContent( bbCodeContent, DEFAULT_PAGE_REQUEST, Arrays.asList(expectedTopic.getBranch().getId())); Assert.assertTrue(searchResultPage.hasContent(), "Search result must not be empty."); } @DataProvider(name = "parameterSearchByBbCodesContent") public Object[][] parameterSearchByBbCodesContent() { return new Object[][]{ {"[code=java]code[/code]", "code"}, {"[b]b[/b]", "b"} }; } }