/**
* 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.poulpe.service.transactional;
import org.apache.commons.lang.RandomStringUtils;
import org.hibernate.validator.engine.ConstraintViolationImpl;
import org.jtalks.common.model.entity.Component;
import org.jtalks.common.model.entity.ComponentType;
import org.jtalks.common.model.entity.Group;
import org.jtalks.common.model.entity.User;
import org.jtalks.common.security.acl.AclManager;
import org.jtalks.common.security.acl.GroupAce;
import org.jtalks.common.service.exceptions.NotFoundException;
import org.jtalks.poulpe.model.dao.ComponentDao;
import org.jtalks.poulpe.model.dao.UserDao;
import org.jtalks.poulpe.model.entity.PoulpeUser;
import org.jtalks.poulpe.model.logic.UserBanner;
import org.jtalks.poulpe.model.pages.Pages;
import org.jtalks.poulpe.model.sorting.UserSearchRequest;
import org.jtalks.poulpe.service.exceptions.ValidationException;
import org.mockito.MockitoAnnotations;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.*;
import static io.qala.datagen.RandomShortApi.alphanumeric;
import static io.qala.datagen.RandomShortApi.english;
import static io.qala.datagen.RandomShortApi.nullOrEmpty;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.jtalks.common.model.entity.User.PASSWORD_MAX_LENGTH;
import static org.jtalks.common.model.entity.User.USERNAME_MAX_LENGTH;
import static org.jtalks.poulpe.model.sorting.UserSearchRequest.BY_USERNAME;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.testng.Assert.*;
/**
* Class for testing {@code TransactionalUserService} functionality.
*
* @author Guram Savinov
* @author Vyacheslav Zhivaev
* @author maxim reshetov
*/
public class TransactionalUserServiceTest{
private static final String USERNAME = "username";
// sut
private TransactionalUserService userService;
// dependencies
private UserDao userDao;
private final String searchString = "searchString";
private ComponentDao componentDaoMock;
private AclManager aclManagerMock;
@BeforeMethod
public void setUp() {
MockitoAnnotations.initMocks(this);
userDao = mock(UserDao.class);
componentDaoMock = mock(ComponentDao.class);
aclManagerMock = mock(AclManager.class);
userService = new TransactionalUserService(userDao, mock(UserBanner.class), aclManagerMock, componentDaoMock);
}
@Test
public void getAll() {
String NO_FILTER = "";
userService.getAll();
verify(userDao).findPoulpeUsersPaginated(NO_FILTER, Pages.NONE);
}
@Test
public void countUsernameMatches() {
userService.countUsernameMatches(searchString);
verify(userDao).countUsernameMatches(searchString);
}
@Test
public void findUsersPaginated() {
int page = 1, limit = 10;
userService.findUsersPaginated(searchString, page, limit);
verify(userDao).findPoulpeUsersPaginated(searchString, Pages.paginate(page, limit));
}
@Test
public void testFindUsersBySearchRequest(){
int page = 1, limit = 10;
UserSearchRequest request = new UserSearchRequest(true,Pages.paginate(page, limit),BY_USERNAME,"");
userService.findUsersBySearchRequest(request);
verify(userDao).findPoulpeUsersBySearchRequest(request);
}
@Test
public void testGetUsersByUsernameWord() {
userService.withUsernamesMatching(searchString);
verify(userDao).findPoulpeUsersPaginated(searchString, Pages.NONE);
}
@Test
public void testUpdateUser() {
PoulpeUser user = user();
userService.updateUser(user);
verify(userDao).saveOrUpdate(user);
}
@Test
public void TestFindUsersNotInList(){
List<PoulpeUser> users = new ArrayList<PoulpeUser>();
users.add(user());
userService.findUsersNotInList(searchString,users);
verify(userDao).findUsersNotInList(searchString,users, Pages.NONE);
}
@Test
public void testGetAllBannedUsers() {
List<PoulpeUser> bannedUsers = userService.getAllBannedUsers();
assertEquals(bannedUsers, new ArrayList<PoulpeUser>());
}
@Test
public void testGetNonBannedByUsername() {
List<PoulpeUser> nonBannedUsers = userService.loadNonBannedUsersByUsername(searchString, Pages.paginate(0, 1000));
assertEquals(nonBannedUsers, new ArrayList<PoulpeUser>());
}
@Test
public void accessNotAllowedBecauseNoComponentTypeRegistered() {
ComponentType componentType = ComponentType.FORUM;
when(componentDaoMock.getByType(eq(componentType))).thenReturn(null);
assertFalse(userService.accessAllowedToComponentType(USERNAME, componentType));
}
@Test
public void accessNotAllowedBecauseNoGroupPermissionsAssociatedWithComponent() {
ComponentType componentType = ComponentType.FORUM;
Component component = mock(Component.class);
when(componentDaoMock.getByType(eq(componentType))).thenReturn(component);
when(aclManagerMock.getGroupPermissionsOn(eq(component))).thenReturn(Collections.<GroupAce>emptyList());
assertFalse(userService.accessAllowedToComponentType(USERNAME, componentType));
}
@Test
public void accessNotAllowedBecausePermissionNotFound() {
ComponentType componentType = ComponentType.FORUM;
Component component = mock(Component.class);
when(userDao.getByUsername(eq(USERNAME))).thenReturn(createPoulpeUserWithPredefinedNameAndGroups(Collections.<Group>emptyList()));
when(componentDaoMock.getByType(eq(componentType))).thenReturn(component);
when(aclManagerMock.getGroupPermissionsOn(eq(component))).thenReturn(Collections.<GroupAce>emptyList());
assertFalse(userService.accessAllowedToComponentType(USERNAME, componentType));
}
@Test
public void accessNotAllowedBecausePermissionIsNotGranting() {
long groupId = 42L;
ComponentType componentType = ComponentType.FORUM;
Component component = mock(Component.class);
GroupAce groupAce = mock(GroupAce.class);
Group group = createGroupWithId(groupId);
when(groupAce.getGroupId()).thenReturn(groupId);
when(userDao.getByUsername(eq(USERNAME))).thenReturn(createPoulpeUserWithPredefinedNameAndGroups(singletonList(group)));
when(componentDaoMock.getByType(eq(componentType))).thenReturn(component);
when(aclManagerMock.getGroupPermissionsOn(eq(component))).thenReturn(singletonList(groupAce));
when(groupAce.isGranting()).thenReturn(false);
assertFalse(userService.accessAllowedToComponentType(USERNAME, componentType));
}
@Test
public void accessNotAllowedBecauseAtLeastOneOfTheGroupsIsRestrictiong(){
long groupId = 42L;
long groupId2 = 666L;
ComponentType componentType = ComponentType.FORUM;
Component component = mock(Component.class);
GroupAce groupAce = mock(GroupAce.class);
GroupAce groupAce2 = mock(GroupAce.class);
Group group = createGroupWithId(groupId);
Group group2 = createGroupWithId(groupId2);
when(groupAce.getGroupId()).thenReturn(groupId);
when(groupAce2.getGroupId()).thenReturn(groupId2);
when(userDao.getByUsername(eq(USERNAME))).thenReturn(createPoulpeUserWithPredefinedNameAndGroups(asList(group, group2)));
when(componentDaoMock.getByType(eq(componentType))).thenReturn(component);
when(aclManagerMock.getGroupPermissionsOn(eq(component))).thenReturn(asList(groupAce, groupAce2));
when(groupAce.isGranting()).thenReturn(true);
when(groupAce2.isGranting()).thenReturn(false);
assertFalse(userService.accessAllowedToComponentType(USERNAME, componentType));
}
@Test
public void accessAllowed() {
long groupId = 42L;
ComponentType componentType = ComponentType.FORUM;
Component component = mock(Component.class);
GroupAce groupAce = mock(GroupAce.class);
Group group = createGroupWithId(groupId);
when(groupAce.getGroupId()).thenReturn(groupId);
when(userDao.getByUsername(eq(USERNAME))).thenReturn(createPoulpeUserWithPredefinedNameAndGroups(singletonList(group)));
when(componentDaoMock.getByType(eq(componentType))).thenReturn(component);
when(aclManagerMock.getGroupPermissionsOn(eq(component))).thenReturn(singletonList(groupAce));
when(groupAce.isGranting()).thenReturn(true);
assertTrue(userService.accessAllowedToComponentType(USERNAME, componentType));
}
@Test
public void testUpdateUsers(){
doNothing().when(userDao).saveOrUpdate((PoulpeUser)any());
Group group = createGroupWithId(1);
List<User> users = new ArrayList<User>();
for(int i=0; i<5; i++){
users.add(user());
}
group.setUsers(users);
userService.updateUsersAtGroup(users, group);
for(User u :users){
assertEquals(u.getGroups().get(0),group);
}
users = new ArrayList<User>();
for(int i=0; i<5; i++){
users.add(user());
users.get(i).getGroups().add(group);
}
group.setUsers(new ArrayList<User>());
userService.updateUsersAtGroup(users, group);
for(User u :users){
assertTrue(u.getGroups().isEmpty());
}
}
@Test
public void returnsUserIfUniqueResultFound() throws NotFoundException {
String username = alphanumeric(USERNAME_MAX_LENGTH);
String password = alphanumeric(PASSWORD_MAX_LENGTH);
PoulpeUser created = user(username, password);
when(userDao.findUsersByUsernameAndPasswordHash(anyString(), anyString()))
.thenReturn(Collections.singletonList(created));
assertEquals(userService.authenticate(username, password), created);
}
@Test
public void performCaseSensitiveSearchWhenMultipleUsersFound() throws NotFoundException {
String username = english(USERNAME_MAX_LENGTH);
String password = alphanumeric(PASSWORD_MAX_LENGTH);
PoulpeUser created = user(username.toUpperCase(), password);
when(userDao.findUsersByUsernameAndPasswordHash(anyString(), anyString()))
.thenReturn(Arrays.asList(
user(username, password),
user(username.toLowerCase(), password),
created));
assertEquals(userService.authenticate(username.toUpperCase(), password), created);
}
@Test(expectedExceptions = NotFoundException.class)
public void throwExceptionWhenMultipleUsersFoundButNoOneIsEqual() throws NotFoundException {
String username = english(USERNAME_MAX_LENGTH);
String password = alphanumeric(PASSWORD_MAX_LENGTH);
when(userDao.findUsersByUsernameAndPasswordHash(anyString(), anyString()))
.thenReturn(Arrays.asList(
user(username.toLowerCase(), password),
user(username.toUpperCase(), password)));
userService.authenticate(username, password);
}
@Test(expectedExceptions = NotFoundException.class)
public void throwExceptionIfUserNotFound() throws NotFoundException {
String username = alphanumeric(USERNAME_MAX_LENGTH);
String password = alphanumeric(PASSWORD_MAX_LENGTH);
when(userDao.findUsersByUsernameAndPasswordHash(anyString(), anyString()))
.thenReturn(Collections.<PoulpeUser>emptyList());
userService.authenticate(username, password);
}
@Test(expectedExceptions = NotFoundException.class)
public void throwExceptionIfUsernameIsNullOrEmpty() throws NotFoundException {
String password = alphanumeric(PASSWORD_MAX_LENGTH);
when(userDao.findUsersByUsernameAndPasswordHash(anyString(), anyString()))
.thenReturn(Collections.<PoulpeUser>emptyList());
userService.authenticate(nullOrEmpty(), password);
}
@Test
public void testRegistration() throws Exception {
when(userDao.getByUsername(any(String.class))).thenReturn(null);
when(userDao.getByEmail(any(String.class))).thenReturn(null);
doNothing().when(userDao).save(any(PoulpeUser.class));
userService.registration(user());
}
@Test(expectedExceptions = org.springframework.transaction.NoTransactionException.class)
public void testDryRunRegistration() throws Exception {
when(userDao.getByUsername(any(String.class))).thenReturn(null);
when(userDao.getByEmail(any(String.class))).thenReturn(null);
doNothing().when(userDao).save(any(PoulpeUser.class));
userService.dryRunRegistration(user());
}
@Test(expectedExceptions = ValidationException.class)
public void testRegistrationUsernameAlreadyExist() throws Exception {
when(userDao.getByUsername(any(String.class))).thenReturn(user());
when(userDao.getByEmail(any(String.class))).thenReturn(null);
doNothing().when(userDao).save(any(PoulpeUser.class));
userService.registration(user());
}
@Test(expectedExceptions = ValidationException.class)
public void testDryRunRegistrationUsernameAlreadyExist() throws Exception {
when(userDao.getByUsername(any(String.class))).thenReturn(user());
when(userDao.getByEmail(any(String.class))).thenReturn(null);
doNothing().when(userDao).save(any(PoulpeUser.class));
userService.dryRunRegistration(user());
}
@Test(expectedExceptions = ValidationException.class)
public void testRegistrationEmailAlreadyExist() throws Exception {
when(userDao.getByUsername(any(String.class))).thenReturn(null);
when(userDao.getByEmail(any(String.class))).thenReturn(user());
doNothing().when(userDao).save(any(PoulpeUser.class));
userService.registration(user());
}
@Test(expectedExceptions = ValidationException.class)
public void testDryRunRegistrationEmailAlreadyExist() throws Exception {
when(userDao.getByUsername(any(String.class))).thenReturn(null);
when(userDao.getByEmail(any(String.class))).thenReturn(user());
doNothing().when(userDao).save(any(PoulpeUser.class));
userService.dryRunRegistration(user());
}
@Test(expectedExceptions = ValidationException.class)
public void testRegistrationAnyValidationErrors() throws Exception {
when(userDao.getByUsername(any(String.class))).thenReturn(null);
when(userDao.getByEmail(any(String.class))).thenReturn(null);
ConstraintViolationException ex = new ConstraintViolationException(constraintViolations());
doThrow(ex).when(userDao).save(any(PoulpeUser.class));
userService.registration(user());
}
@Test(expectedExceptions = ValidationException.class)
public void testDryRunRegistrationAnyValidationErrors() throws Exception {
when(userDao.getByUsername(any(String.class))).thenReturn(null);
when(userDao.getByEmail(any(String.class))).thenReturn(null);
ConstraintViolationException ex = new ConstraintViolationException(constraintViolations());
doThrow(ex).when(userDao).save(any(PoulpeUser.class));
userService.dryRunRegistration(user());
}
private Group createGroupWithId(long groupId) {
Group group = new Group();
group.setId(groupId);
return group;
}
private PoulpeUser createPoulpeUserWithPredefinedNameAndGroups(List<Group> groups) {
PoulpeUser user = new PoulpeUser();
user.setUsername(USERNAME);
user.setGroups(groups);
return user;
}
public static PoulpeUser user() {
return new PoulpeUser(RandomStringUtils.randomAlphanumeric(10), "USERNAME@mail.com", "PASSWORD", "salt");
}
private PoulpeUser user(String username, String password) {
String email = alphanumeric(5) + "@email.com";
return new PoulpeUser(username, email, password, "");
}
private static Set<ConstraintViolation<?>> constraintViolations(){
Set<ConstraintViolation<?>> result = new HashSet<ConstraintViolation<?>>();
for(int i=1; i<=5; i++){
result.add(new ConstraintViolationImpl("message"+i,"message"+i,PoulpeUser.class,null,null,null,null,null,null));
}
return result;
}
}