/**
* 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.hibernate;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.joda.time.DateTime;
import org.jtalks.jcommune.model.entity.PersistedObjectsFactory;
import org.jtalks.jcommune.model.dao.LastReadPostDao;
import org.jtalks.jcommune.model.entity.JCUser;
import org.jtalks.jcommune.model.entity.LastReadPost;
import org.jtalks.jcommune.model.entity.Topic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
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.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.*;
import static org.testng.Assert.*;
/**
* @author Evgeniy Naumenko
* @author Anuar Nurmakanov
*/
@ContextConfiguration(locations = {"classpath:/org/jtalks/jcommune/model/entity/applicationContext-dao.xml"})
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public class LastReadPostHibernateDaoTest extends AbstractTransactionalTestNGSpringContextTests {
@Autowired
private LastReadPostDao lastReadPostDao;
@Autowired
private SessionFactory sessionFactory;
private Session session;
@BeforeMethod
public void setUp() {
session = sessionFactory.getCurrentSession();
PersistedObjectsFactory.setSession(session);
}
/*===== Common methods =====*/
@Test
public void testGet() {
LastReadPost expected = PersistedObjectsFactory.getDefaultLastReadPost();
session.save(expected);
LastReadPost actual = lastReadPostDao.get(expected.getId());
assertNotNull(actual, "Get returns null.");
assertEquals(actual.getId(), expected.getId(),
"Get return incorrect object");
}
@Test
public void dateOfTheLastReadPostShouldBeUpdated() {
LastReadPost post = PersistedObjectsFactory.getDefaultLastReadPost();
post.setPostCreationDate(new DateTime());
session.save(post);
DateTime newPostDate = post.getPostCreationDate().plusMinutes(34);
post.setPostCreationDate(newPostDate);
lastReadPostDao.saveOrUpdate(post);
LastReadPost updatedPost = (LastReadPost) session.get(LastReadPost.class, post.getId());
assertEquals(updatedPost.getPostCreationDate(), newPostDate,
"Update doesn't work, because field value didn't change.");
}
@Test(expectedExceptions = DataIntegrityViolationException.class)
public void saveOrUpdateShouldFailWhenSavingDuplicate() {
LastReadPost post = PersistedObjectsFactory.getDefaultLastReadPost();
session.save(post);
// Create entity with the same user_id and post_id
LastReadPost otherPost = new LastReadPost(post.getUser(), post.getTopic(), new DateTime());
lastReadPostDao.saveOrUpdate(otherPost);
}
@Test
public void testMarkAsReadTopicsToUser() {
List<Topic> topics = PersistedObjectsFactory.createAndSaveTopicListWithPosts(10);
JCUser user = PersistedObjectsFactory.getDefaultUser();
//records of posts in topics
Map<Long, DateTime> listCountPostsToTopics = markAllTopicsASRead(topics, user);
//records in database about read topic
Map<Long, DateTime> actualCountPostsToTopics = getActualListCountPostsToTopics(topics, user);
assertEquals(actualCountPostsToTopics, listCountPostsToTopics);
}
@Test
public void testDeleteMarksTopicsToUser() {
List<Topic> topics = PersistedObjectsFactory.createAndSaveTopicListWithPosts(10);
JCUser user = PersistedObjectsFactory.getDefaultUser();
SQLQuery deletedEntities = (SQLQuery) session.getNamedQuery("deleteAllMarksReadToUser");
deletedEntities
.addSynchronizedEntityClass(LastReadPost.class)
.setParameter("user", user.getId())
.setParameter("branch", topics.get(0).getBranch().getId())
.setCacheable(false)
.executeUpdate();
List<LastReadPost> lastReadPostList = lastReadPostDao.getLastReadPosts(user, topics);
//check delete record about read posts for user
assertTrue(lastReadPostList.isEmpty());
}
@Test
public void testGetTopicAndLatestPostDateInBranch() {
List<Topic> topics = PersistedObjectsFactory.createAndSaveTopicListWithPosts(10);
//records of posts in topics
Map<Long, DateTime> actualCountOfPosts = getTopicAndLatestPostDateInBranch(topics);
Map<Long, DateTime> resultOfGetTopics = new HashMap<>();
@SuppressWarnings("unchecked")
List<Object[]> resultCheckGetTopics = session.getNamedQuery("getTopicAndLatestPostDateInBranch")
.setParameter("branch", topics.get(0).getBranch().getId())
.setCacheable(false)
.list();
for (Object[] record : resultCheckGetTopics) {
resultOfGetTopics.put(new Long(record[0].toString()), (DateTime) record[1]);
}
assertEquals(resultOfGetTopics, actualCountOfPosts);
}
@Test
public void testMarkAllReadToUserInTwoBranches() {
JCUser user = PersistedObjectsFactory.getDefaultUser();
List<Topic> topicsOfFirstBranch = PersistedObjectsFactory.createAndSaveTopicListWithPosts(10);
List<Topic> topicsOfSecondBranch = PersistedObjectsFactory.createAndSaveTopicListWithPosts(10);
//records of posts in topics
Map<Long, DateTime> listCountPostsToTopicsInFBranch = new HashMap<>();
Map<Long, DateTime> listCountPostsToTopicsInSBranch = new HashMap<>();
//records in database about read topic
Map<Long, DateTime> actualCountPostsToTopicsInFBranch = new HashMap<>();
Map<Long, DateTime> actualCountPostsToTopicsInSBranch = new HashMap<>();
listCountPostsToTopicsInFBranch = markAllTopicsASRead(topicsOfFirstBranch, user);
listCountPostsToTopicsInSBranch = markAllTopicsASRead(topicsOfSecondBranch, user);
actualCountPostsToTopicsInFBranch = getActualListCountPostsToTopics(topicsOfFirstBranch, user);
actualCountPostsToTopicsInSBranch = getActualListCountPostsToTopics(topicsOfSecondBranch, user);
//concatenate results from first and second branches
listCountPostsToTopicsInFBranch.putAll(listCountPostsToTopicsInSBranch);
actualCountPostsToTopicsInFBranch.putAll(actualCountPostsToTopicsInSBranch);
assertEquals(listCountPostsToTopicsInFBranch, actualCountPostsToTopicsInFBranch);
}
/*===== Specific methods =====*/
@Test
public void testListLastReadPostsForTopic() {
LastReadPost post = PersistedObjectsFactory.getDefaultLastReadPost();
session.save(post);
List<LastReadPost> lastReadPosts = lastReadPostDao.getLastReadPostsInTopic(post.getTopic());
assertTrue(lastReadPosts.size() == 1, "Result list has incorrect size");
assertEquals(lastReadPosts.get(0).getId(), post.getId(),
"Results contains invalid data.");
}
@Test
public void testGetLastReadPost() {
LastReadPost expected = PersistedObjectsFactory.getDefaultLastReadPost();
session.save(expected);
LastReadPost actual = lastReadPostDao.getLastReadPost(expected.getUser(), expected.getTopic());
assertEquals(actual.getId(), expected.getId(),
"Found incorrect last read post.");
}
@Test
public void getLastReadPostsForUserInTopicsShouldReturnThem() {
int topicsSize = 10;
JCUser user = PersistedObjectsFactory.getDefaultUser();
List<Topic> userTopics = PersistedObjectsFactory.createAndSaveTopicListWithPosts(topicsSize);
markAllTopicsASRead(userTopics, user);
List<LastReadPost> lastReadPosts = lastReadPostDao.getLastReadPosts(user, userTopics);
assertEquals(lastReadPosts.size(), topicsSize,
"For every passed topic it should return last read post.");
}
@Test
public void getLastReadPostsForUserShouldReturnEmptyListForEmptyListOfTopics() {
List<Topic> userTopics = Collections.emptyList();
JCUser user = new JCUser("user", "user@gmail.com", "password");
List<LastReadPost> lastReadPosts = lastReadPostDao.getLastReadPosts(user, userTopics);
assertTrue(lastReadPosts.isEmpty(), "For passed empty list of topics it should return empty list.");
}
/**
* Method marks topics as read to user
*
* @param topics List of topics to mark
* @param user User for which threads are marked as read
* @return list of count posts for each topic, for verification
*/
private Map<Long, DateTime> markAllTopicsASRead(List<Topic> topics, JCUser user) {
SQLQuery insertQuery = (SQLQuery) session.getNamedQuery("markAllTopicsRead");
insertQuery.setCacheable(false);
Map<Long, DateTime> listCountPostsToTopics = new HashMap<Long, DateTime>();
for (Topic tp : topics) {
insertQuery.setParameter("uuid", UUID.randomUUID().toString())
.setParameter("user", user.getId())
.setParameter("lastPostDate", ((DateTime) tp.getLastPost().getCreationDate()).toDate())
.setParameter("topic", tp.getId())
.executeUpdate();
listCountPostsToTopics.put(tp.getId(), tp.getLastPost().getCreationDate());
}
return listCountPostsToTopics;
}
/**
* Method returns the data read topics that are stored in the database
*
* @param topics List of topics, which marked as read to user
* @param user User for which threads are marked as read
* @return List of last read dates for each topic, for verification, stored in the database
*/
private Map<Long, DateTime> getActualListCountPostsToTopics(List<Topic> topics, JCUser user) {
Map<Long, DateTime> listCountPostsToTopics = new HashMap<>();
SQLQuery checkInsert = (SQLQuery) session.createSQLQuery("select TOPIC_ID, " +
"LAST_READ_POST_DATE FROM LAST_READ_POSTS where TOPIC_ID IN (select TOPIC_ID from TOPIC where " +
"BRANCH_ID=:branch) and USER_ID = :user");
checkInsert.setParameter("user", user.getId());
checkInsert.setParameter("branch", topics.get(0).getBranch().getId());
@SuppressWarnings("unchecked")
List<Object[]> resultCheckInsert = checkInsert.list();
for (Object[] record : resultCheckInsert) {
listCountPostsToTopics.put(new Long(record[0].toString()), new DateTime(record[1]));
}
return listCountPostsToTopics;
}
/**
* Method returns the data for each topics, which marked as read
*
* @param topics List of topics in branch
* @return List of count posts for each topic
*/
private Map<Long, DateTime> getTopicAndLatestPostDateInBranch(List<Topic> topics) {
Map<Long, DateTime> result = new HashMap<>();
for (Topic topic : topics) {
//second parameter it's index of last post
result.put(topic.getId(), topic.getLastPost().getCreationDate());
}
return result;
}
}