/*
* Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package och.comp.chats;
import static java.util.Collections.*;
import static och.api.model.PropKey.*;
import static och.api.model.chat.config.Key.*;
import static och.api.model.client.ClientInfo.*;
import static och.comp.chats.ChatsAccService.*;
import static och.comp.chats.backup.ActiveChatsLogs.*;
import static och.comp.chats.common.StoreOps.*;
import static och.util.DateUtil.*;
import static och.util.FileUtil.*;
import static och.util.Util.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import och.api.exception.ValidationException;
import och.api.exception.chat.MaxChatsForAccPerDayException;
import och.api.exception.chat.MaxChatsFromIpPerDayException;
import och.api.exception.chat.MaxFeedbacksForAccPerDayException;
import och.api.exception.chat.MaxFeedbacksFromIpPerDayException;
import och.api.exception.chat.MsgsPerChatLimitException;
import och.api.exception.chat.SimgleMsgsPerTimeLimitException;
import och.api.exception.user.AccessDeniedException;
import och.api.model.PropKey;
import och.api.model.chat.ChatLog;
import och.api.model.chat.ChatLogHist;
import och.api.model.chat.ChatOperator;
import och.api.model.chat.ChatUpdateData;
import och.api.model.chat.Feedback;
import och.api.model.client.ClientInfo;
import och.api.model.client.ClientSession;
import och.api.model.web.ReqInfo;
import och.comp.chats.backup.ActiveChatsLogs;
import och.comp.chats.history.LogsArchive;
import och.comp.chats.model.Chat;
import och.service.props.impl.MapProps;
import och.util.DateUtil;
import och.util.FileUtil;
import och.util.ReflectionsUtil;
import och.util.StringUtil;
import och.util.concurrent.AsyncListener;
import org.junit.Test;
import test.BaseTest;
public class ChatsAccServiceTest extends BaseTest implements AsyncListener {
String accId1 = randomUUID();
String accId2 = randomUUID();
String ip1 = "1.1.1.1";
String ip2 = "2.2.2.2";
String userAgent1 = "userAgent1";
String userAgent2 = "userAgent2";
String session1 = "1";
String session2 = "2";
String session3 = "3";
ClientSession client1 = new ClientSession(session1, ip1, userAgent1);
ClientSession client2 = new ClientSession(session2, ip1, userAgent2);
ClientSession client3 = new ClientSession(session3, ip2, userAgent1);
ChatOperator operator1 = new ChatOperator(1, "operator1");
ChatOperator operator2 = new ChatOperator(2, "operator2");
ChatOperator operator3 = new ChatOperator(3, "operator3");
String text1 = "привет";
String text2 = "\n123\t";
String text3 = "text3";
ArrayList<Future<?>> chatWriteFutures = new ArrayList<>();
MapProps props = new MapProps();
@Override
public void onFutureEvent(Future<?> future) {
chatWriteFutures.add(future);
}
void waitLastWrite() throws Exception{
int size = chatWriteFutures.size();
if(size > 1) chatWriteFutures.get(size-2).get();
chatWriteFutures.get(size-1).get();
}
@Test
public void test_activeLogs_cleanActiveChatsLogs() throws Exception {
ActiveChatsLogs activeLogs = new ActiveChatsLogs(TEST_DIR, props, this);
//create logs
{
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
chats.initActiveChat(client1);
chats.addComment(client1, "привет\n\n\n123");
waitLastWrite();
assertEquals(list(accId1), activeLogs.getCurAccIds());
}
//start new accs
Date now = addDays(new Date(), 1);
long closeDelta = 1L;
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
assertEquals(1, accs.cleanActiveChatsLogs(now, closeDelta));
assertEquals(0, accs.cleanActiveChatsLogs(now, closeDelta));
waitLastWrite();
assertEquals(emptyList(), activeLogs.getCurAccIds());
}
@Test
public void test_activeLogs_removeUnknownAccs() throws Exception{
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
chats.initActiveChat(client1);
chats.addComment(client1, "привет\n\n\n123");
waitLastWrite();
ActiveChatsLogs activeLogs = new ActiveChatsLogs(TEST_DIR, props, this);
assertEquals(list(accId1), activeLogs.getCurAccIds());
//create new logs
File logsDir = new File(TEST_DIR, ACTIVE_LOGS_DIR);
File newAccDir = new File(logsDir, "acc-.123");
newAccDir.mkdir();
new File(newAccDir, "temp").createNewFile();
assertEquals(set(accId1, ".123"), new HashSet<>(activeLogs.getCurAccIds()));
//start new accs - clean
new ChatsAccService(TEST_DIR, props, this);
assertEquals(list(accId1), activeLogs.getCurAccIds());
}
@Test
public void test_activeLogs_restore() throws Exception{
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
chats.putOperatorAndSetActive(operator2);
String chatId1 = chats.initActiveChat(client1).id;
String chatId2 = chats.initActiveChat(client2).id;
//chat1
{
chats.addComment(client1, "привет\n\n\n123");
chats.addComment(client1, "123 123 123");
chats.addOperator(chatId1, operator1.id, 0);
chats.addComment(chatId1, operator1.id, "some text\n\n\n123");
chats.addComment(client1, "dsfdf");
chats.addComment(chatId1, operator1.id, "dd");
}
//chat 2
{
chats.addComment(client2, "привет\n\n\n123");
chats.addOperator(chatId2, operator1.id, 0);
chats.addComment(chatId2, operator1.id, "dddddddd");
chats.addComment(chatId2, operator1.id, "dd");
chats.addOperator(chatId2, operator2.id, 1);
chats.addComment(chatId2, operator1.id, "sdf");
chats.addComment(client2, "zzzzz");
}
waitLastWrite();
ActiveChatsLogs activeLogs = new ActiveChatsLogs(TEST_DIR, props, this);
//restore logic
{
//1
{
assertNull(activeLogs.restoreChat(accId1, chatId1+"123", client1));
assertNull(activeLogs.restoreChat(accId1, chatId1, client2));
Chat log = activeLogs.restoreChat(accId1, chatId1, client1);
assertNotNull(log);
ChatLog chatLog = log.toLog();
assertEquals(5, chatLog.messages.size());
assertEquals(2, chatLog.users.size());
}
//2
{
Chat log = activeLogs.restoreChat(accId1, chatId2, client2);
assertNotNull(log);
ChatLog chatLog = log.toLog();
assertEquals(5, chatLog.messages.size());
assertEquals(3, chatLog.users.size());
}
}
//link restored to session
//old accs alredy contains chat
assertNull(accs.restoreOldChatIfNeed(accId1, client1, chatId1));
assertNull(accs.restoreOldChatIfNeed(accId1, client1, chatId1));
accs.shutdown();
//new accs
ChatsAccService accs2 = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc acc = accs2.getAcc(accId1);
assertNull(acc.getActiveChat(client1));
assertNull(acc.getActiveChat(client2));
//1
{
assertNull(accs2.restoreOldChatIfNeed(accId2, client1, chatId1));
assertNull(accs2.restoreOldChatIfNeed(accId1, client2, chatId1));
assertNull(accs2.restoreOldChatIfNeed(accId1, client1, chatId2));
ChatLog chatLog = accs2.restoreOldChatIfNeed(accId1, client1, chatId1);
assertNotNull(chatLog);
assertEquals(5, chatLog.messages.size());
assertEquals(2, chatLog.users.size());
assertNotNull(acc.getActiveChat(client1));
assertNull(acc.getActiveChat(client2));
}
//2
{
ChatLog chatLog = accs2.restoreOldChatIfNeed(accId1, client2, chatId2);
assertNotNull(chatLog);
assertEquals(5, chatLog.messages.size());
assertEquals(3, chatLog.users.size());
assertNotNull(acc.getActiveChat(client2));
}
//close new chats
acc.closeAllChats();
accs2.shutdown();
Thread.sleep(50);
//new accs have no logs
{
ChatsAccService accs3 = new ChatsAccService(TEST_DIR, props, this);
assertNull(accs3.restoreOldChatIfNeed(accId1, client1, chatId1));
assertNull(accs3.restoreOldChatIfNeed(accId1, client2, chatId2));
accs3.shutdown();
}
}
@Test
public void test_activeLogs_create() throws Exception{
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
File logsDir = new File(TEST_DIR, ACTIVE_LOGS_DIR);
String chatId = null;
File chatLogFile = null;
String curLogContent = "";
String newLogContent = "";
//create chat
{
chatId = chats.initActiveChat(client1).id;
waitLastWrite();
chatLogFile = getChatLogFile(logsDir, accId1, chatId);
assertTrue(chatLogFile.exists());
newLogContent = FileUtil.readFileUTF8(chatLogFile);
assertTrue(newLogContent.length() > curLogContent.length());
curLogContent = newLogContent;
}
//add user msg
{
chats.addComment(client1, "привет\n\n\n123");
waitLastWrite();
newLogContent = FileUtil.readFileUTF8(chatLogFile);
assertTrue(newLogContent.length() > curLogContent.length());
curLogContent = newLogContent;
}
//add operator
{
chats.addOperator(chatId, operator1.id, 0);
waitLastWrite();
newLogContent = FileUtil.readFileUTF8(chatLogFile);
assertTrue(newLogContent.length() > curLogContent.length());
curLogContent = newLogContent;
}
//add operator msg
{
chats.addComment(chatId, operator1.id, "some text\n\n\n123");
waitLastWrite();
newLogContent = FileUtil.readFileUTF8(chatLogFile);
assertTrue(newLogContent.length() > curLogContent.length());
curLogContent = newLogContent;
}
//close chat
{
chats.closeChat(client1);
waitLastWrite();
assertFalse(chatLogFile.exists());
assertFalse(chatLogFile.getParentFile().exists());
}
}
@Test
public void test_clientRefs() throws Exception{
ChatsAccService accs = new ChatsAccService(TEST_DIR, props);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
String chatId = chats.initActiveChat(client1).id;
chats.addOperator(chatId, operator1.id, 0);
chats.addComment(client1, "test1");
assertNull(chats.getActiveChat(client1).clientRefs);
try {
//ref
ReqInfo.putInfoToThreadLocal(new ReqInfo(null, null, "some1", null, null));
chats.addComment(client1, "test");
{
Map<Integer, String> clientRefs = chats.getActiveChat(client1).clientRefs;
assertEquals("some1", clientRefs.get(1));
}
//origRef
ReqInfo.putInfoToThreadLocal(new ReqInfo(null, null, null, "some2", null));
chats.addComment(client1, "test");
chats.addComment(client1, "test");
chats.addComment(client1, "test");
{
Map<Integer, String> clientRefs = chats.getActiveChat(client1).clientRefs;
assertEquals(2, clientRefs.size());
assertEquals("some1", clientRefs.get(1));
assertEquals("some2", clientRefs.get(2));
}
//empty ref
ReqInfo.putInfoToThreadLocal(new ReqInfo(null, null, null, null, null));
chats.addComment(client1, "test");
assertEquals(2, chats.getActiveChat(client1).clientRefs.size());
//use fromIndex
{
Map<Integer, String> clientRefs = chats.getActiveChat(client1, new ChatUpdateData(0, 1)).clientRefs;
assertEquals(2, clientRefs.size());
assertEquals("some1", clientRefs.get(1));
assertEquals("some2", clientRefs.get(2));
}
{
Map<Integer, String> clientRefs = chats.getActiveChat(client1, new ChatUpdateData(1, 1)).clientRefs;
assertEquals(2, clientRefs.size());
assertEquals("some1", clientRefs.get(1));
assertEquals("some2", clientRefs.get(2));
}
{
Map<Integer, String> clientRefs = chats.getActiveChat(client1, new ChatUpdateData(2, 1)).clientRefs;
assertEquals(1, clientRefs.size());
assertEquals("some2", clientRefs.get(2));
}
{
Map<Integer, String> clientRefs = chats.getActiveChat(client1, new ChatUpdateData(3, 1)).clientRefs;
assertEquals(null, clientRefs);
}
} finally {
ReqInfo.removeInfoFromThreadLocal();
}
}
@Test
public void test_crud_config() throws Exception{
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
accs.createAcc(accId1);
boolean initVal = feedback_notifyOpsByEmail.boolDefVal();
ChatsAcc chats = accs.getAcc(accId1);
assertEquals(initVal, chats.getBoolVal(feedback_notifyOpsByEmail));
chats.putConfig(feedback_notifyOpsByEmail, ! initVal);
waitLastWrite();
assertEquals( ! initVal, chats.getBoolVal(feedback_notifyOpsByEmail));
//read from file
ChatsAccService accs2 = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats2 = accs2.getAcc(accId1);
assertEquals( ! initVal, chats2.getBoolVal(feedback_notifyOpsByEmail));
}
@Test
public void test_client_feedbacks_day_limits_by_acc() throws Exception{
int maxByAcc = 10;
props.putVal(chats_maxFeedbacksForAccPerDay, maxByAcc);
ChatsAccService accs = new ChatsAccService(TEST_DIR, props);
accs.createAcc(accId1);
accs.createAcc(accId2);
String text = "123";
//acc1
for (int i = 0; i < maxByAcc; i++) {
accs.addFeedbackAsync(accId1, randomClientInfo(), text);
}
try {
accs.addFeedbackAsync(accId1, randomClientInfo(), text);
fail_exception_expected();
}catch(MaxFeedbacksForAccPerDayException e){
//ok
}
//acc2
for (int i = 0; i < maxByAcc; i++) {
accs.addFeedbackAsync(accId2, randomClientInfo(), text);
}
try {
accs.addFeedbackAsync(accId2, randomClientInfo(), text);
fail_exception_expected();
}catch(MaxFeedbacksForAccPerDayException e){
//ok
}
//custom limit
int customMax = maxByAcc+1;
props.putVal(chats_maxFeedbacksForAccPerDay+"_"+accId1, customMax);
accs.addFeedbackAsync(accId1, randomClientInfo(), text);
try {
accs.addFeedbackAsync(accId1, randomClientInfo(), text);
fail_exception_expected();
}catch(MaxFeedbacksForAccPerDayException e){
//ok
}
//спасает переход на след. день
ReflectionsUtil.setField(accs, "customCurDayPreset", addDays(new Date(), 1).getTime());
//acc1
for (int i = 0; i < customMax; i++) {
accs.addFeedbackAsync(accId1, randomClientInfo(), text);
}
try {
accs.addFeedbackAsync(accId1, randomClientInfo(), text);
fail_exception_expected();
}catch(MaxFeedbacksForAccPerDayException e){
//ok
}
//acc2
for (int i = 0; i < maxByAcc; i++) {
accs.addFeedbackAsync(accId2, randomClientInfo(), text);
}
try {
accs.addFeedbackAsync(accId2, randomClientInfo(), text);
fail_exception_expected();
}catch(MaxFeedbacksForAccPerDayException e){
//ok
}
}
@Test
public void test_client_feedbacks_day_limits_by_ip() throws Exception{
int maxByIp = 10;
props.putVal(chats_maxFeedbacksFromIpPerDay, maxByIp);
ChatsAccService accs = new ChatsAccService(TEST_DIR, props);
accs.createAcc(accId1);
ClientInfo clientInfo1 = randomClientInfo();
ClientInfo clientInfo2 = randomClientInfo();
String text = "123";
//ip1
for (int i = 0; i < maxByIp; i++) {
accs.addFeedbackAsync(accId1, clientInfo1, text);
}
try {
accs.addFeedbackAsync(accId1, clientInfo1, text);
fail_exception_expected();
}catch(MaxFeedbacksFromIpPerDayException e){
//ok
}
//ip2
for (int i = 0; i < maxByIp; i++) {
accs.addFeedbackAsync(accId1, clientInfo2, text);
}
try {
accs.addFeedbackAsync(accId1, clientInfo2, text);
fail_exception_expected();
}catch(MaxFeedbacksFromIpPerDayException e){
//ok
}
//custom limit by ip
int customMax = maxByIp+1;
props.putVal(chats_maxFeedbacksFromIpPerDay+"_"+clientInfo1.ip, customMax);
accs.addFeedbackAsync(accId1, clientInfo1, text);
try {
accs.addFeedbackAsync(accId1, clientInfo1, text);
fail_exception_expected();
}catch(MaxFeedbacksFromIpPerDayException e){
//ok
}
//спасает переход на след. день
ReflectionsUtil.setField(accs, "customCurDayPreset", addDays(new Date(), 1).getTime());
//ip1
for (int i = 0; i < customMax; i++) {
accs.addFeedbackAsync(accId1, clientInfo1, text);
}
try {
accs.addFeedbackAsync(accId1, clientInfo1, text);
fail_exception_expected();
}catch(MaxFeedbacksFromIpPerDayException e){
//ok
}
//ip2
for (int i = 0; i < maxByIp; i++) {
accs.addFeedbackAsync(accId1, clientInfo2, text);
}
try {
accs.addFeedbackAsync(accId1, clientInfo2, text);
fail_exception_expected();
}catch(MaxFeedbacksFromIpPerDayException e){
//ok
}
}
@Test
public void test_write_read_feedbacks() throws Exception{
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
accs.createAcc(accId1);
String text = "123";
String text2 = "345";
Date now = DateUtil.parseStandartDateTime("30.09.2014 12:00:00");
Date afterMinute = DateUtil.parseStandartDateTime("30.09.2014 12:10:00");
Date nextDay = DateUtil.parseStandartDate("01.10.2014");
Date prevDay = DateUtil.parseStandartDate("28.09.2014");
//no email
try {
accs.addFeedbackAsync(accId1, randomClientInfo(false), text, now);
fail_exception_expected();
}catch(ValidationException e){
//ok
}
//write
ClientInfo clientInfo = randomClientInfo();
{
accs.addFeedbackAsync(accId1, clientInfo, text, now);
accs.addFeedbackAsync(accId1, clientInfo, text2, afterMinute);
waitLastWrite();
}
//read now
{
List<Feedback> list = accs.getFeedbacks(accId1, now);
assertEquals(2, list.size());
assertEquals(clientInfo.email, list.get(0).user.userEmail);
assertEquals(clientInfo.name, list.get(0).user.userName);
assertEquals(text, list.get(0).text);
assertEquals(clientInfo.email, list.get(1).user.userEmail);
assertEquals(clientInfo.name, list.get(1).user.userName);
assertEquals(text2, list.get(1).text);
}
//empty day
{
assertEquals(0, accs.getFeedbacks(accId1, prevDay).size());
assertEquals(0, accs.getFeedbacks(accId1, nextDay).size());
assertEquals(null, accs.getFeedbacks(accId1+"123", prevDay));
}
}
@Test
public void test_client_ip_ban(){
ChatsAccService accs = new ChatsAccService(TEST_DIR, props);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
chats.initActiveChat(new ClientSession(session1, ip1, userAgent1));
props.putVal(chats_blockClientByIp+"_"+ip1, true);
try {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip1, userAgent1));
fail_exception_expected();
}catch(AccessDeniedException e){
//ok
}
props.putVal(chats_blockClientByIp+"_"+ip1, false);
chats.initActiveChat(new ClientSession(session1, ip1, userAgent1));
}
@Test
public void test_client_createChats_day_limits_by_acc() throws Exception{
int maxForAcc = 10;
props.putVal(chats_maxChatsForAccPerDay, maxForAcc);
ChatsAccService accs = new ChatsAccService(TEST_DIR, props);
ChatsAcc chats1 = accs.createAcc(accId1);
chats1.putOperatorAndSetActive(operator1);
ChatsAcc chats2 = accs.createAcc(accId2);
chats2.putOperatorAndSetActive(operator1);
//acc1
for (int i = 0; i < maxForAcc; i++) {
chats1.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
}
try {
chats1.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
fail_exception_expected();
}catch(MaxChatsForAccPerDayException e){
//ok
}
//acc2
for (int i = 0; i < maxForAcc; i++) {
chats2.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
}
try {
chats2.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
fail_exception_expected();
}catch(MaxChatsForAccPerDayException e){
//ok
}
//custom limit by ip
int customMax = maxForAcc+1;
props.putVal(chats_maxChatsForAccPerDay+"_"+accId1, customMax);
chats1.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
try {
chats1.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
fail_exception_expected();
}catch(MaxChatsForAccPerDayException e){
//ok
}
//закрытие всех текущих чатов не поможет
chats1.closeAllChats();
try {
chats1.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
fail_exception_expected();
}catch(MaxChatsForAccPerDayException e){
//ok
}
//спасает переход на след. день
ReflectionsUtil.setField(accs, "customCurDayPreset", addDays(new Date(), 1).getTime());
//acc1
for (int i = 0; i < customMax; i++) {
chats1.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
}
try {
chats1.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
fail_exception_expected();
}catch(MaxChatsForAccPerDayException e){
//ok
}
//acc2
for (int i = 0; i < maxForAcc; i++) {
chats2.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
}
try {
chats2.initActiveChat(new ClientSession(randomSimpleId(), randomClientInfo().ip, userAgent1));
fail_exception_expected();
}catch(MaxChatsForAccPerDayException e){
//ok
}
}
@Test
public void test_client_createChats_day_limits_by_ip() throws Exception{
int maxByIpCommon = 10;
props.putVal(chats_maxChatsFromIpPerDay, maxByIpCommon);
ChatsAccService accs = new ChatsAccService(TEST_DIR, props);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
//ip1
for (int i = 0; i < maxByIpCommon; i++) {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip1, userAgent1));
}
try {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip1, userAgent1));
fail_exception_expected();
}catch(MaxChatsFromIpPerDayException e){
//ok
}
//ip2
for (int i = 0; i < maxByIpCommon; i++) {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip2, userAgent1));
}
try {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip2, userAgent1));
fail_exception_expected();
}catch(MaxChatsFromIpPerDayException e){
//ok
}
//custom limit by ip
int customMax = maxByIpCommon+1;
props.putVal(chats_maxChatsFromIpPerDay+"_"+ip1, customMax);
chats.initActiveChat(new ClientSession(randomSimpleId(), ip1, userAgent1));
try {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip1, userAgent1));
fail_exception_expected();
}catch(MaxChatsFromIpPerDayException e){
//ok
}
//закрытие всех текущих чатов не поможет
chats.closeAllChats();
try {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip1, userAgent1));
fail_exception_expected();
}catch(MaxChatsFromIpPerDayException e){
//ok
}
//спасает переход на след. день
ReflectionsUtil.setField(accs, "customCurDayPreset", addDays(new Date(), 1).getTime());
//ip1
for (int i = 0; i < customMax; i++) {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip1, userAgent1));
}
try {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip1, userAgent1));
fail_exception_expected();
}catch(MaxChatsFromIpPerDayException e){
//ok
}
//ip2
for (int i = 0; i < maxByIpCommon; i++) {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip2, userAgent1));
}
try {
chats.initActiveChat(new ClientSession(randomSimpleId(), ip2, userAgent1));
fail_exception_expected();
}catch(MaxChatsFromIpPerDayException e){
//ok
}
}
@Test
public void test_msgSizeLimit() throws Exception {
int max = props.getIntVal(chats_maxMsgSize);
String maxMsg = StringUtil.randomStr(max);
ChatsAccService accs = new ChatsAccService(TEST_DIR, props);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
String chatId = chats.initActiveChat(client1).id;
chats.addOperator(chatId, operator1.id, 0);
chats.addComment(client1, maxMsg);
chats.addComment(chatId, operator1.id, maxMsg);
try {
chats.addComment(client1, maxMsg+"1");
fail_exception_expected();
}catch(ValidationException e){
//ok
}
try {
chats.addComment(chatId, operator1.id, maxMsg+"1");
fail_exception_expected();
}catch(ValidationException e){
//ok
}
}
@Test
public void test_msgsLimit() throws Exception {
String text = StringUtil.randomStr(props.getIntVal(chats_maxMsgSize));
int max = props.getIntVal(chats_maxMsgsPerChat);
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
String chatId = chats.initActiveChat(client1).id;
chats.addOperator(chatId, operator1.id, 0);
for (int i = 0; i < max; i++) {
if(i % 2 == 0) chats.addComment(client1, text);
else chats.addComment(chatId, operator1.id, text);
}
try {
chats.addComment(client1, text);
fail_exception_expected();
}catch(MsgsPerChatLimitException e){
//ok
}
try {
chats.addComment(chatId, operator1.id, text);
fail_exception_expected();
}catch(MsgsPerChatLimitException e){
//ok
}
chats.closeAllChats();
lastFrom(chatWriteFutures).get();
}
@Test
public void test_clientMsgLimit() throws Exception {
String text = "123";
int max = props.getIntVal(PropKey.chats_maxSingleMsgsPerTime);
ChatsAccService accs = new ChatsAccService(TEST_DIR, props);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
String chatId = chats.initActiveChat(client1).id;
chats.addOperator(chatId, operator1.id, 0);
for (int i = 0; i < max; i++) {
chats.addComment(client1, text);
}
try {
chats.addComment(client1, text);
fail_exception_expected();
}catch(SimgleMsgsPerTimeLimitException e){
//ok
}
//next round
chats.addComment(chatId, operator1.id, text);
for (int i = 0; i < max; i++) {
chats.addComment(client1, text);
}
try {
chats.addComment(client1, text);
fail_exception_expected();
}catch(SimgleMsgsPerTimeLimitException e){
//ok
}
}
@Test
public void test_removeReq() throws Exception {
String text = "123";
//создаем чат
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats1 = accs.createAcc(accId1);
chats1.putOperatorAndSetActive(operator1);
chats1.initActiveChat(client1);
chats1.addComment(client1, text);
ChatsAcc chats2 = accs.createAcc(accId1);
chats2.putOperatorAndSetActive(operator2);
chats2.initActiveChat(client2);
chats2.addComment(client2, text);
//проверяем что он не записался в файл
File accDir = accs.getAccDir(accId1);
assertEquals(0, getSubDirsCount(accDir));
//создаем флаг удаления и синхронизируем
createRemoveReqFlag(accDir);
accs.reloadAccs();
lastFrom(chatWriteFutures).get();
//проверяем что чаты сохранились перед удалением акка
File newDir = getDirToDeleted(accDir.getParent(), accDir.getName(), 0);
assertEquals(true, newDir.exists());
assertEquals(1, getSubDirsCount(newDir));
assertEquals(false, hasRemoveReqFlag(newDir));
}
private static int getSubDirsCount(File dir) {
File[] files = dir.listFiles();
if(isEmpty(files)) return 0;
int count = 0;
for (File file : files) {
if(file.isDirectory()) count++;
}
return count;
}
@Test
public void test_blockFlagUpdate(){
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
//заблокируем чат
accs.createAcc(accId1);
assertEquals(false, accs.isBlocked(accId1));
accs.setBlocked(accId1, true);
assertEquals(true, accs.isBlocked(accId1));
//удалим файл флага - рассинхрон
File accDir = accs.getAccDir(accId1);
removeBlockedFlag(accDir);
assertEquals(false, hasBlockedFlag(accDir));
assertEquals(true, accs.isBlocked(accId1));
//синхр
accs.reloadAccs();
assertEquals(false, hasBlockedFlag(accDir));
assertEquals(false, accs.isBlocked(accId1));
//создадим флаг блокировки - рассинхр
createBlockedFlag(accDir);
assertEquals(true, hasBlockedFlag(accDir));
assertEquals(false, accs.isBlocked(accId1));
//синхр
accs.reloadAccs();
assertEquals(true, hasBlockedFlag(accDir));
assertEquals(true, accs.isBlocked(accId1));
}
@Test
public void test_block() throws Exception {
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
//unknow chat
assertFalse(accs.isBlocked(accId1));
accs.setBlocked(accId1, true);
assertFalse(accs.isBlocked(accId1));
//known chat
accs.createAcc(accId1);
assertFalse(accs.isBlocked(accId1));
accs.setBlocked(accId1, true);
assertTrue(accs.isBlocked(accId1));
accs.setBlocked(accId1, false);
assertFalse(accs.isBlocked(accId1));
accs.setBlocked(accId1, true);
assertTrue(accs.isBlocked(accId1));
//restored
ChatsAccService resrored = new ChatsAccService(TEST_DIR, props, this);
assertTrue(resrored.isBlocked(accId1));
}
@Test
public void test_getHist()throws Exception{
ChatsAccService accs = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats = accs.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
chats.initActiveChat(client1);
chats.addComment(client1, "test");
chats.closeChat(client1);
String chatId2 = chats.initActiveChat(client1).id;
chats.addComment(client1, "test2");
chats.addOperator(chatId2, operator1.id, 0);
chats.addComment(chatId2, operator1.id, "anwer");
chats.closeChat(client1);
waitLastWrite();
Date now = new Date();
Date yesterday = DateUtil.addDays(now, -1);
assertTrue(isEmpty(accs.getHistory(accId1, yesterday)));
List<ChatLogHist> logs = accs.getHistory(accId1, now);
assertTrue( ! isEmpty(logs));
assertEquals(2, logs.size());
{
ChatLogHist log = logs.get(0);
assertEquals(1, log.messages.size());
}
{
ChatLogHist log = logs.get(1);
assertEquals(2, log.messages.size());
}
}
@Test
public void test_call_LogsArchive() throws Exception{
final AtomicInteger callCounter = new AtomicInteger();
LogsArchive arcStub = new LogsArchive() {
@Override
public synchronized Date tryCreateArcsIfNeed(File root, String accId, Date lastScanned) {
callCounter.addAndGet(1);
return lastScanned;
}
};
ChatsAccService accs1 = new ChatsAccService(TEST_DIR, props, this, arcStub);
assertEquals(0, callCounter.get());
accs1.createAcc("test1");
accs1.createAcc("test2");
assertEquals(0, callCounter.get());
accs1.shutdown();
ChatsAccService accs2 = new ChatsAccService(TEST_DIR, props, this, arcStub);
assertEquals(2, callCounter.get());
callCounter.set(0);
ChatsAcc chats = accs2.createAcc("test1");
chats.putOperatorAndSetActive(operator1);
chats.initActiveChat(client1);
chats.addComment(client1, "test");
chats.closeChat(client1);
waitLastWrite();
assertEquals(1, callCounter.get());
callCounter.set(0);
}
@Test
public void test_saveOperatorsToFile() throws Exception{
//set
{
ChatsAccService accountChats = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats1 = accountChats.createAcc(accId1);
chats1.putOperatorAndSetActive(operator1);
ChatsAcc chats2 = accountChats.createAcc(accId2);
chats2.putOperatorAndSetActive(operator1);
chats2.putOperatorAndSetActive(operator2);
assertNotNull(chats1.getOperator(operator1.id));
assertNull(chats1.getOperator(operator2.id));
assertNotNull(chats2.getOperator(operator1.id));
assertNotNull(chats2.getOperator(operator2.id));
}
waitLastWrite();
String newName = "new name of operator 1";
//load from file
{
ChatsAccService accountChats = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats1 = accountChats.createAcc(accId1);
ChatsAcc chats2 = accountChats.createAcc(accId2);
assertNotNull(chats1.getOperator(operator1.id));
assertNull(chats1.getOperator(operator2.id));
assertNotNull(chats2.getOperator(operator1.id));
assertNotNull(chats2.getOperator(operator2.id));
//change
chats1.putOperator(new ChatOperator(operator1.id, newName));
chats2.putOperator(operator3);
chats2.removeOperator(operator2.id);
assertFalse(chats2.isActiveOperator(operator2.id));
assertFalse(chats2.isOperator(operator2.id));
assertEquals(newName, chats1.getOperator(operator1.id).name);
assertNotNull(chats2.getOperator(operator3.id));
assertNull(chats2.getOperator(operator2.id));
}
waitLastWrite();
//load chages
{
ChatsAccService accountChats = new ChatsAccService(TEST_DIR, props, this);
ChatsAcc chats1 = accountChats.createAcc(accId1);
ChatsAcc chats2 = accountChats.createAcc(accId2);
assertEquals(newName, chats1.getOperator(operator1.id).name);
assertNotNull(chats2.getOperator(operator3.id));
assertNull(chats2.getOperator(operator2.id));
}
}
@Test
public void test_closeChat_and_removeDir() throws Exception{
File accountDir = new File(TEST_DIR, getAccountDirName(accId1));
ChatsAccService accountChats = new ChatsAccService(TEST_DIR, props, this);
{
ChatsAcc chats = accountChats.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
chats.initActiveChat(client1);
chats.addComment(client1, "test");
chats.closeChat(client1);
}
assertTrue(accountDir.exists());
File deletedDir = findDirToDeleted(accountDir);
assertFalse(deletedDir.exists());
accountChats.removeAcc(accId1).get();
assertFalse(accountDir.exists());
assertTrue(deletedDir.exists());
//create-delete again
File deletedDir2 = findDirToDeleted(accountDir);
{
ChatsAcc chats = accountChats.createAcc(accId1);
chats.putOperatorAndSetActive(operator1);
chats.initActiveChat(client1);
chats.addComment(client1, "test2");
chats.closeChat(client1);
}
assertTrue(accountDir.exists());
assertTrue(deletedDir.exists());
assertFalse(deletedDir2.exists());
accountChats.removeAcc(accId1).get();
assertFalse(accountDir.exists());
assertTrue(deletedDir.exists());
assertTrue(deletedDir2.exists());
}
@Test
public void test_load_and_reload(){
File accountDir = new File(TEST_DIR, getAccountDirName(accId1));
accountDir.mkdirs();
//load
ChatsAccService accountChats = new ChatsAccService(TEST_DIR, props, this);
assertNotNull(accountChats.getAcc(accId1));
//remove dir
assertTrue(deleteDirRecursive(accountDir));
assertNotNull(accountChats.getAcc(accId1));
//add dir
File accountDir2 = new File(TEST_DIR, getAccountDirName(accId2));
accountDir2.mkdir();
assertNull(accountChats.getAcc(accId2));
//reload
accountChats.reloadAccs();
assertNull(accountChats.getAcc(accId1));
assertNotNull(accountChats.getAcc(accId2));
}
@Test
public void test_save_closed_chat() throws Exception {
ChatsAccService accountChats = new ChatsAccService(TEST_DIR, props, this);
//server1
accountChats.createAcc(accId1);
assertTrue(accountChats.getAccDir(accId1).exists());
ChatsAcc chats = accountChats.getAcc(accId1);
chats.putOperatorAndSetActive(operator1);
chats.putOperatorAndSetActive(operator2);
String chatId1 = chats.initActiveChat(client1).id;
chats.addComment(client1, text1);
chats.addComment(chatId1, operator1.id, text2);
chats.addComment(client1, text2);
chats.addComment(chatId1, operator2.id, text3);
//write
chats.closeChat(client1);
waitLastWrite();
//server2
ChatsAcc chats2 = accountChats.createAcc(accId2);
chats2.putOperatorAndSetActive(operator1);
chats2.initActiveChat(client1);
chats2.addComment(client1, text1);
chats2.closeChat(client1);
waitLastWrite();
}
@Test
public void test_create_read_delete(){
ChatsAccService accountChats = new ChatsAccService(TEST_DIR, props);
//create
assertNull(accountChats.getAcc(accId1));
assertNotNull(accountChats.createAcc(accId1));
//read
assertNotNull(accountChats.getAcc(accId1));
//delete
accountChats.removeAcc(accId1);
assertNull(accountChats.getAcc(accId1));
//create again
accountChats.createAcc(accId1);
assertNotNull(accountChats.getAcc(accId1));
}
}