och.front.service.FrontAppTest.java Source code

Java tutorial

Introduction

Here is the source code for och.front.service.FrontAppTest.java

Source

 /*
  * 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.front.service;

 import static java.math.BigDecimal.*;
 import static java.util.Collections.*;
 import static och.api.model.PropKey.*;
 import static och.api.model.RemoteCache.*;
 import static och.api.model.billing.PaymentBase.*;
 import static och.api.model.billing.PaymentProvider.*;
 import static och.api.model.billing.PaymentStatus.*;
 import static och.api.model.billing.PaymentType.*;
 import static och.api.model.chat.account.ChatAccountPrivileges.*;
 import static och.api.model.chat.account.PrivilegeType.*;
 import static och.api.model.tariff.Tariff.*;
 import static och.api.model.tariff.TariffMath.*;
 import static och.api.model.user.SecurityContext.*;
 import static och.api.model.user.UserRole.*;
 import static och.api.model.user.UserStatus.*;
 import static och.comp.db.main.table.MainTables.*;
 import static och.comp.ops.BillingOps.*;
 import static och.comp.paypal.PaypalClientStub.*;
 import static och.front.service.SecurityService.*;
 import static och.util.DateUtil.*;
 import static och.util.StringUtil.*;
 import static och.util.Util.*;
 import static och.util.sql.Dialect.*;
 import static och.util.sql.SingleTx.*;

 import java.io.File;
 import java.math.BigDecimal;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;

 import javax.servlet.http.Cookie;

 import och.api.exception.InvalidInputException;
 import och.api.exception.ValidationException;
 import och.api.exception.billing.NoDataToConfirmPaymentException;
 import och.api.exception.chat.AccountsLimitException;
 import och.api.exception.chat.ChatAccountBlockedException;
 import och.api.exception.chat.ChatAccountNotPausedException;
 import och.api.exception.chat.ChatAccountPausedException;
 import och.api.exception.chat.HostBlockedException;
 import och.api.exception.chat.NoAvailableServerException;
 import och.api.exception.chat.NoChatAccountException;
 import och.api.exception.tariff.ChangeTariffLimitException;
 import och.api.exception.tariff.NotPublicTariffException;
 import och.api.exception.tariff.OperatorsLimitException;
 import och.api.exception.tariff.TariffNotFoundException;
 import och.api.exception.tariff.UpdateTariffOperatorsLimitException;
 import och.api.exception.user.AccessDeniedException;
 import och.api.exception.user.BannedUserException;
 import och.api.exception.user.DuplicateUserDataException;
 import och.api.exception.user.InvalidLoginDataForUpdateException;
 import och.api.exception.user.InvalidUserActivationCodeException;
 import och.api.exception.user.NotActivatedUserException;
 import och.api.exception.user.UnmodifiableAdminUserException;
 import och.api.exception.user.UserActivationExpiredException;
 import och.api.exception.user.UserNotFoundException;
 import och.api.exception.user.UserSessionAlreadyExistsException;
 import och.api.model.PropKey;
 import och.api.model.billing.AdminSyncResp;
 import och.api.model.billing.PayData;
 import och.api.model.billing.PaymentBase;
 import och.api.model.billing.PaymentExt;
 import och.api.model.billing.PaymentProvider;
 import och.api.model.billing.PaymentStatus;
 import och.api.model.billing.PaymentType;
 import och.api.model.chat.account.ChatAccount;
 import och.api.model.chat.account.PrivilegeType;
 import och.api.model.chat.config.Key;
 import och.api.model.chat.host.ClientHost;
 import och.api.model.remtoken.ClientRemToken;
 import och.api.model.remtoken.RemToken;
 import och.api.model.server.ServerRow;
 import och.api.model.tariff.Tariff;
 import och.api.model.user.LoginUserReq;
 import och.api.model.user.UpdateUserReq;
 import och.api.model.user.User;
 import och.api.model.user.UserExt;
 import och.api.model.user.UserRole;
 import och.comp.billing.standalone.BillingSyncService;
 import och.comp.billing.standalone.HostMultiOwnersAlarmService;
 import och.comp.cache.Cache;
 import och.comp.cache.client.CacheClient;
 import och.comp.cache.impl.CacheImpl;
 import och.comp.cache.server.CacheServerContext;
 import och.comp.cache.server.CacheSever;
 import och.comp.db.base.universal.UniversalQueries;
 import och.comp.db.main.MainDb;
 import och.comp.db.main.table.MainTables;
 import och.comp.db.main.table._f.IsFull;
 import och.comp.db.main.table._f.TariffLastPay;
 import och.comp.db.main.table._f.TariffStart;
 import och.comp.db.main.table.billing.CreatePayment;
 import och.comp.db.main.table.billing.GetPaymentByExternalId;
 import och.comp.db.main.table.billing.GetPaymentById;
 import och.comp.db.main.table.billing.GetStartBonusByUserId;
 import och.comp.db.main.table.billing.SelectUserBalanceById;
 import och.comp.db.main.table.billing.UpdateUserAccsBlocked;
 import och.comp.db.main.table.billing.UpdateUserBalance;
 import och.comp.db.main.table.billing.UpdateUserBalanceUnsafe;
 import och.comp.db.main.table.chat.GetAllChatAccounts;
 import och.comp.db.main.table.chat.GetChatAccount;
 import och.comp.db.main.table.chat.UpdateAllChatAccounts;
 import och.comp.db.main.table.chat.UpdateChatAccountByUid;
 import och.comp.db.main.table.chat.host.CreateClientHost;
 import och.comp.db.main.table.chat.host.CreateClientHostAccOwner;
 import och.comp.db.main.table.chat.host.DeleteClientHostAccOwner;
 import och.comp.db.main.table.chat.host.UpdateClientHostImportant;
 import och.comp.db.main.table.chat.privilege.GetChatAccountPrivileges;
 import och.comp.db.main.table.remtoken.SelectRemTokensByUser;
 import och.comp.db.main.table.server.GetAllServers;
 import och.comp.db.main.table.server.UpdateServerById;
 import och.comp.db.main.table.tariff.GetAllTariffs;
 import och.comp.db.main.table.user.SelectUserById;
 import och.comp.mail.stub.MailServiceStub;
 import och.comp.mail.stub.SenderStub;
 import och.comp.ops.BillingOps;
 import och.comp.paypal.PaypalClientStub;
 import och.comp.paypal.standalone.PaypalPaymentsSynchService;
 import och.email.parser.ActivationEmailParser;
 import och.email.parser.RestorePswEmailParser;
 import och.front.service.ChatService.UpdateTariffOps;
 import och.front.service.model.UserAccInfo;
 import och.service.props.WriteProps;
 import och.service.props.impl.MapProps;
 import och.util.DateUtil;
 import och.util.StringUtil;
 import och.util.concurrent.ExecutorsUtil;
 import och.util.model.Pair;
 import och.util.servlet.WebUtil;
 import och.util.sql.ConcurrentUpdateSqlException;

 import org.apache.commons.logging.Log;
 import org.h2.tools.Server;
 import org.junit.Before;
 import org.junit.Test;

 import test.BaseTest;
 import test.TestException;
 import web.MockHttpServletRequest;
 import web.MockHttpServletResponse;
 import web.MockServletContext;

 public class FrontAppTest extends BaseTest {

     public static final String TEMPLATES_PATH = "./server-front/web/WEB-INF/templates";

     private static Log log = getLog(FrontAppTest.class);

     SenderStub mailSender = new SenderStub();
     int cachePort = 12159;
     MockServletContext servletContext = new MockServletContext();
     MockHttpServletRequest req = new MockHttpServletRequest();
     MockHttpServletResponse resp = new MockHttpServletResponse();
     ArrayList<Future<?>> asyncFutures = new ArrayList<>();
     ArrayList<Future<?>> scheduleFutures = new ArrayList<>();
     PaypalClientStub paypalClient = new PaypalClientStub();

     MapProps props;
     FrontApp app;
     FrontAppContext c;
     MainDb db;
     UniversalQueries universal;
     ChatService chats;
     SecurityService security;
     CacheSever cacheSever;
     UserService users;
     BillingService billing;
     CacheClient cacheClient;

     long userId1;
     String login1 = "user";
     String mail1 = "user@user.ru";
     String psw1 = "123";

     long userId2;
     String login2 = "user2";
     String mail2 = "user2@user.ru";
String psw2 = StringUtil.createStr('?', 200);

     long userId3;
     String login3 = "user3";
     String mail3 = "user3@user.ru";
     String psw3 = "123";

     long userId4;
     String login4 = "user4";
     String mail4 = "user4@user.ru";
     String psw4 = "123";

     String cacheSecureKey = "some132";
     String server1HttpUrl = "http://test1";
     String server1HttpsUrl = "https://test1";
     String server2HttpUrl = "http://test2";
     String server2HttpsUrl = "https://test2";
     String server3HttpUrl = "http://test3";
     String server3HttpsUrl = "https://test3";

     long serverId1 = 1;
     long serverId2 = 2;
     long serverId3;

     @Before
     public void before() throws Exception {

         props = baseFrontProps(TEST_DIR);
         props.putVal(cache_remote_port, cachePort);
         props.putVal(db_reinit, true);
         props.putVal(chats_server_init_urls,
                 server1HttpUrl + " " + server1HttpsUrl + "," + server2HttpUrl + " " + server2HttpsUrl);
         props.putVal(app_debug_createRemoteData, false);
         props.putVal(billing_sync_debug_DisableTimer, true);
         props.putVal(mail_storeToDisc, false);
         props.putVal(billing_sync_fillBlockedCacheOnStartDelay, 0);
         props.putVal(billing_sync_lastSyncStore, false);
         props.putVal(db_debug_LogSql, true);
         props.putVal(cache_encyptedKey, cacheSecureKey);

         cacheSever = new CacheSever(cachePort, 2, 10000, cacheSecureKey, props);
         cacheSever.runAsync();

         app = FrontApp.create(props, servletContext, new MailServiceStub(mailSender, props), paypalClient);
         c = app.c;
         db = c.db;
         universal = db.universal;
         chats = app.chats;
         users = app.users;
         security = app.security;
         cacheClient = c.cache;
         billing = app.billing;

         c.async.addListener((future) -> asyncFutures.add(future));
         c.async.addScheduleListener((future) -> scheduleFutures.add(future));
         cacheClient.addListener((future) -> asyncFutures.add(future));

     }

     public static MapProps baseFrontProps(File testDir) throws Exception {

         MapProps props = new MapProps();

         putDbProps(props, testDir);

         props.putVal(admin_Emails, "admin@host.com");
         props.putVal(users_activationUrl, "http://test");
         props.putVal(cache_remote_maxConns, 2);
         props.putVal(cache_remote_idleConns, 2);
         props.putVal(templates_path, TEMPLATES_PATH);
         props.putVal(captcha_publicKey, "test");
         props.putVal(captcha_privateKey, "test");

         props.putVal("", "");
         return props;
     }

     public static void putDbProps(WriteProps props, File testDir) throws Exception {

         boolean usePostgre = false;
         if (usePostgre) {
             props.putVal(db_dialect, DB_POSTGRESQL);
             props.putVal(db_driver, "org.postgresql.Driver");
             props.putVal(db_url, "jdbc:postgresql://localhost/och-test");
             props.putVal(db_user, "");
             props.putVal(db_psw, "");
             props.putVal(db_maxConnections, 10);
             props.putVal(db_idleConnections, 5);
         }
         //h2
         else {

             int serverPort = 9400;

             runH2ServerIfNeed(testDir, serverPort);

             Class.forName("org.h2.Driver");

             int logMode = 1; //1-err,2-info,3-debug
             //String url = "jdbc:h2:mem:db-"+randomSimpleId() + ";DB_CLOSE_DELAY=-1;TRACE_LEVEL_SYSTEM_OUT="+logMode;
             //String url = "jdbc:h2:"+new File(testDir, "test.db").getAbsolutePath();
             String url = "jdbc:h2:tcp://localhost:" + serverPort + "/~/test";

             props.putVal(db_dialect, DB_H2);
             props.putVal(db_driver, "org.h2.Driver");
             props.putVal(db_url, url);
             props.putVal(db_user, "sa");
             props.putVal(db_psw, "");
             props.putVal(db_maxConnections, 10);
             props.putVal(db_idleConnections, 5);
         }
     }

     private static void runH2ServerIfNeed(File testDir, int port) {
         try {

             Server.createTcpServer("-tcpPort", "" + port, "-tcpAllowOthers").start();

         } catch (Exception e) {
             log.error("can't runH2Server: " + e);
         }
     }

     @Test
     public void test_all() throws Exception {

         //DB
         test_init_db();
         test_db_getServers();

         //USER
         test_user_create();
         test_user_get();
         test_user_duplicates();
         test_user_invalid_inputs();
         test_user_unexists();
         test_user_activate();
         test_user_ban();
         test_user_unban();
         test_user_activate_from_email_text();
         test_user_invalid_activation_states();
         test_user_send_activation_email_again();
         test_user_change_psw();
         test_user_update();
         test_user_psw_secure();
         test_ban_user_runtime_with_exists_session();

         //ROLE
         test_set_get_roles();

         //SECURITY
         test_create_restore_remMe();
         test_logout_remMe();
         test_create_without_remMe();
         test_remMeToken_secure();
         test_remMeToken_maxCount();
         test_invalidLoginsCount();

         //chats
         test_chats_createServerAndAcc_BySystem();
         test_chats_createAccs_byUser();
         test_chats_renameAccs_BySystem_ByUser();
         test_chats_changePrivsForUser();
         test_chats_operatorsLimits();
         test_chats_accsLimits();
         test_chats_accPauseLimits();
         test_chats_nicknames();

         //billing
         test_billing_atomicChangeBalance();
         test_billing_put_balances_to_cache();
         test_billing_get_balance_from_cache_or_db();
         test_billing_paypal_paySteps();
         test_billing_paySync();
         test_billing_payBill();
         test_billing_monthBill();
         test_billing_sendErrorsWithEmails();
         test_billing_callFromCacheFlag();
         test_billing_blockUserAccs();
         test_billing_promoStartBonus();
         test_billing_2checkout_paySteps();

         //tariffs
         test_tariff_defaultTariffs();
         test_tariff_update_invalidInputs_limitations();
         test_tariff_update_maxInDayLimit();
         test_tariff_update();

         //tariffs, billing concurrents
         test_tariff_billing_concurrent();

         //client hosts
         test_clientHosts_Crud();
         test_clientHosts_AlarmDetect();
         test_clientHosts_Block();
     }

     private void test_billing_promoStartBonus() throws Exception {

         long userId = userId1;
         BigDecimal initBalance = null;

         pushToSecurityContext_SYSTEM_USER();
         try {
             initBalance = billing.getUserBalance(userId);
         } finally {
             popUserFromSecurityContext();
         }

         assertFalse(universal.selectOne(new GetStartBonusByUserId(userId)).startBonusAdded);

         //first call
         {
             assertTrue(billing.addStartBonus(userId));
             assertTrue(universal.selectOne(new GetStartBonusByUserId(userId)).startBonusAdded);
         }

         //second call
         {
             assertFalse(billing.addStartBonus(userId));
             assertTrue(universal.selectOne(new GetStartBonusByUserId(userId)).startBonusAdded);
         }

         lastFrom(asyncFutures).get();

         //check balance
         pushToSecurityContext_SYSTEM_USER();
         try {
             BigDecimal curBalance = billing.getUserBalance(userId);
             int deltaVal = promo_startBonus.bigDecimalDefVal().intValue();
             assertEquals(deltaVal, curBalance.subtract(initBalance).intValue());

             BigDecimal cacheVal = tryParseBigDecimal(cacheClient.tryGetVal(getBalanceCacheKey(userId)), null);
             assertEquals(deltaVal, cacheVal.subtract(initBalance).intValue());

         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_clientHosts_Block() {

         MockHttpServletRequest req = new MockHttpServletRequest();
         req.addHeader("Referer", "http://ya.ru");

         chats.checkAndLogReferer(req, null, "demo");

         //by host
         {
             props.putVal(chats_blockByHost + "_ya.ru", true);
             try {
                 chats.checkAndLogReferer(req, null, "demo");
                 fail_exception_expected();
             } catch (HostBlockedException e) {
                 //ok
             }

             props.putVal(chats_blockByHost + "_ya.ru", false);
             chats.checkAndLogReferer(req, null, "demo");
         }

         //by host and uid
         {
             props.putVal(chats_blockByHost + "_ya.ru_demo", true);
             try {
                 chats.checkAndLogReferer(req, null, "demo");
                 fail_exception_expected();
             } catch (HostBlockedException e) {
                 //ok
             }

             props.putVal(chats_blockByHost + "_ya.ru_demo", false);
             chats.checkAndLogReferer(req, null, "demo");
         }

         //by host and accs owner
         {
             props.putVal(chats_blockByHost + "_ya.ru_owner_100", true);
             try {
                 chats.checkAndLogReferer(req, null, "demo");
                 fail_exception_expected();
             } catch (HostBlockedException e) {
                 //ok
             }

             props.putVal(chats_blockByHost + "_ya.ru_owner_100", false);
             chats.checkAndLogReferer(req, null, "demo");
         }

     }

     private void test_clientHosts_AlarmDetect() throws Exception {

         boolean important = true;
         long userId = 100;
         long userId2 = 101;

         String name1 = "1.ru";
         String refer1 = "http://" + name1;
         String refer2 = "http://2.ru";

         //create stat
         pushToSecurityContext_SYSTEM_USER();
         try {

             universal.update(new DeleteClientHostAccOwner(userId));
             universal.update(new DeleteClientHostAccOwner(userId2));
             assertEquals(0, db.clientHosts.getHostsWithOwners(important, 1).size());

             String acc1 = chats.getOwnerAccIds(userId).get(0);
             String acc2 = chats.getOwnerAccIds(userId2).get(0);

             assertFalse(chats.saveClientsHostsStat());
             chats.checkAndLogReferer(req, refer1, acc1).get();
             chats.checkAndLogReferer(req, refer1, acc2).get();
             chats.checkAndLogReferer(req, refer2, acc1).get();
             assertTrue(chats.saveClientsHostsStat());

         } finally {
             popUserFromSecurityContext();
         }

         props.putVal(chats_hosts_multiOwners_DisableSendErrors, false);
         MailServiceStub mailService = new MailServiceStub(mailSender, props);
         mailSender.tasks.clear();

         HostMultiOwnersAlarmService alarm = new HostMultiOwnersAlarmService();
         alarm.setCacheServerContext(new CacheServerContext(props, cacheSever, db, mailService));
         alarm.init();

         //?   - ? ?
         {
             assertEquals(2, db.clientHosts.getHostsWithOwners(important, 1).size());
             assertEquals(1, db.clientHosts.getHostsWithOwners(important, 2).size());

             alarm.doCheckWork();
             assertEquals(1, mailSender.tasks.size());
         }

         //?  ? -  ?
         {
             universal.update(new UpdateClientHostImportant(name1, false));
             mailSender.tasks.clear();

             assertEquals(0, db.clientHosts.getHostsWithOwners(important, 2).size());
             assertEquals(1, db.clientHosts.getHostsWithOwners(important, 1).size());

             alarm.doCheckWork();
             assertEquals(0, mailSender.tasks.size());
         }

         //  ? - ? ?
         {
             universal.update(new UpdateClientHostImportant(name1, true));
             mailSender.tasks.clear();

             assertEquals(1, db.clientHosts.getHostsWithOwners(important, 2).size());
             assertEquals(2, db.clientHosts.getHostsWithOwners(important, 1).size());

             alarm.doCheckWork();
             assertEquals(1, mailSender.tasks.size());
         }

         //   -  ?
         {
             universal.update(new DeleteClientHostAccOwner(userId));
             mailSender.tasks.clear();

             assertEquals(0, db.clientHosts.getHostsWithOwners(important, 2).size());
             assertEquals(1, db.clientHosts.getHostsWithOwners(important, 1).size());

             alarm.doCheckWork();
             assertEquals(0, mailSender.tasks.size());
         }

     }

     private void test_clientHosts_Crud() throws Exception {

         long hostId1 = 0;
         long hostId2 = 0;
         String name1 = "ya.ru";
         String name2 = "ya.ru2";
         boolean important = true;
         long userId = 100;
         long userId2 = 101;

         //create host
         {
             hostId1 = universal.nextSeqFor(MainTables.client_hosts);
             universal.update(new CreateClientHost(new ClientHost(hostId1, name1, important)));

             List<ClientHost> list = db.clientHosts.getHostsWithOwners(important, 0);
             assertEquals(1, list.size());
             assertEquals(name1, list.get(0).name);

             assertEquals(0, db.clientHosts.getHostsWithOwners(!important, 0).size());
             assertEquals(0, db.clientHosts.getHostsWithOwners(important, 1).size());

             hostId2 = universal.nextSeqFor(MainTables.client_hosts);
             universal.update(new CreateClientHost(new ClientHost(hostId2, name2, important)));
             assertEquals(2, db.clientHosts.getHostsWithOwners(important, 0).size());
         }

         //create link
         {
             universal.update(new CreateClientHostAccOwner(hostId1, userId));

             try {
                 universal.update(new CreateClientHostAccOwner(hostId1, userId));
                 fail_exception_expected();
             } catch (SQLException e) {
                 //ok
             }

             universal.update(new CreateClientHostAccOwner(hostId1, userId2));

             List<ClientHost> hosts = db.clientHosts.getHostsWithOwners(important, 0);
             assertEquals(2, hosts.size());

             ClientHost host = hosts.get(0);
             assertNotNull(host.owners);
             assertEquals(2, host.owners.size());
             assertEquals(userId, host.owners.get(0).id);
             assertEquals(userId2, host.owners.get(1).id);

             assertEquals(1, db.clientHosts.getHostsWithOwners(important, 1).size());
             assertEquals(1, db.clientHosts.getHostsWithOwners(important, 2).size());
         }

         //create stat
         pushToSecurityContext_SYSTEM_USER();
         try {

             universal.update(new DeleteClientHostAccOwner(userId));
             universal.update(new DeleteClientHostAccOwner(userId2));
             assertEquals(0, db.clientHosts.getHostsWithOwners(important, 1).size());

             String acc1 = chats.getOwnerAccIds(userId).get(0);
             String acc2 = chats.getOwnerAccIds(userId2).get(0);

             assertFalse(chats.saveClientsHostsStat());
             chats.checkAndLogReferer(req, "http://1.ru", acc1).get();
             chats.checkAndLogReferer(req, "http://1.ru", acc2).get();
             chats.checkAndLogReferer(req, "http://2.ru", acc1).get();
             assertTrue(chats.saveClientsHostsStat());

             assertEquals(1, db.clientHosts.getHostsWithOwners(important, 2).size());
             assertEquals(2, db.clientHosts.getHostsWithOwners(important, 1).size());

         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_chats_nicknames() throws Exception {

         long ownerId = 100;
         String accUid = "demo";
         String nick = "some test nick";

         pushToSecurityContext_SYSTEM_USER(() -> {

             ChatAccount acc = chats.getAccByUid(accUid, false);
             assertEquals(null, db.universal.selectOne(new GetChatAccountPrivileges(acc.id, ownerId)).nickname);

             //set
             chats.setNickname(accUid, ownerId, nick);

             //check model
             Map<Long, UserAccInfo> map = chats.getAccOperators(accUid);
             assertTrue(map.size() > 0);
             UserAccInfo info = map.get(ownerId);
             assertNotNull(info);
             assertEquals(nick, info.nickname);

             //check db
             assertEquals(nick, db.universal.selectOne(new GetChatAccountPrivileges(acc.id, ownerId)).nickname);

             //check to long
             try {
                 chats.setNickname(accUid, ownerId, randomStr(MAX_NICKNAME_SIZE + 1));
                 fail_exception_expected();
             } catch (ValidationException e) {
                 //ok
             }

             pushToSecurityContext(new User(userId1));
             try {

                 //  ? 
                 chats.setNickname(accUid, nick);

                 //    ?   
                 chats.setNickname(accUid, ownerId, nick);
                 fail_exception_expected();
             } catch (AccessDeniedException e) {
                 //ok
             } finally {
                 popUserFromSecurityContext();
             }

         });

     }

     private void test_chats_accPauseLimits() throws Exception {

         String userLogin = "u_PauseLimits";
         String userMail = userLogin + "@dd.dd";
         long userId = -1;
         int maxChangesInDay = props.getIntVal(tariffs_maxChangedInDay);

         //? 
         pushToSecurityContext_SYSTEM_USER();
         try {
             userId = users.createUser(new User(userLogin, userMail), "123", false);
         } finally {
             popUserFromSecurityContext();
         }

         pushToSecurityContext(new User(userId));
         try {

             String uid = chats.createAccByUser("pauseLimits_1");

             long initTariff = chats.getAccsForOperator(userId).get(0).tariffId;

             //?  ?  ? 
             try {
                 chats.unpauseAccByUser(uid);
                 fail_exception_expected();
             } catch (ChatAccountNotPausedException e) {
                 //ok
             }

             //? ??    ? 
             try {
                 chats.updateAccTariffByUser(uid, PAUSE_TARIFF_ID);
                 fail_exception_expected();
             } catch (InvalidInputException e) {
                 //ok
             }

             // ? ? 
             for (int i = 0; i < maxChangesInDay; i++) {

                 chats.pauseAccByUser(uid);

                 assertEquals(PAUSE_TARIFF_ID, chats.getAccsForOperator(userId).get(0).tariffId);

                 chats.unpauseAccByUser(uid);

                 assertEquals(initTariff, chats.getAccsForOperator(userId).get(0).tariffId);
             }

             // ?
             try {
                 chats.pauseAccByUser(uid);
                 fail_exception_expected();
             } catch (ChangeTariffLimitException e) {
                 //ok
             }
             assertEquals(initTariff, chats.getAccsForOperator(userId).get(0).tariffId);

         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_chats_accsLimits() throws Exception {

         String userLogin = "u_accsLimits";
         String userMail = userLogin + "@dd.dd";
         long userId = -1;
         int defaultMaxAccs = props.getIntVal(chats_maxAccsForUser);

         //? 
         pushToSecurityContext_SYSTEM_USER();
         try {
             userId = users.createUser(new User(userLogin, userMail), "123", false);
         } finally {
             popUserFromSecurityContext();
         }

         pushToSecurityContext(new User(userId));
         try {

             //? ? - 
             {
                 for (int i = 0; i < defaultMaxAccs; i++) {
                     chats.createAccByUser("some");
                 }
             }

             //?.   
             try {
                 chats.createAccByUser("some");
                 fail_exception_expected();
             } catch (AccountsLimitException e) {
                 //ok
             }

             // ? ?  
             props.putVal(chats_maxAccsForUser + "-" + userId, defaultMaxAccs + 1);
             chats.createAccByUser("some");
             try {
                 chats.createAccByUser("some");
                 fail_exception_expected();
             } catch (AccountsLimitException e) {
                 //ok
             }

         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_billing_blockUserAccs() throws Exception {

         MailServiceStub mailService = new MailServiceStub(mailSender, props);

         // ?    
         Date longFuture = parseStandartDateTime("02.09.2040 3:00:00");
         universal.update(new UpdateAllChatAccounts(new TariffStart(longFuture), new TariffLastPay(longFuture)));

         MapProps paypalProps = new MapProps();
         paypalProps.putVal(paypal_sync_debug_DisableTimer, true);

         PaypalPaymentsSynchService paySync = new PaypalPaymentsSynchService();
         paySync.setCacheServerContext(new CacheServerContext(paypalProps, cacheSever, db, mailService));
         paySync.setClient(paypalClient);
         paySync.init();

         BillingSyncService billingSync = new BillingSyncService();
         billingSync.setCacheServerContext(new CacheServerContext(props, cacheSever, db, mailService));
         billingSync.init();

         ArrayList<Pair<Long, Boolean>> blockReqs = new ArrayList<>();
         BillingOps.SEND_ACCS_BLOCKED_LISTENER = (ownerId, val) -> blockReqs
                 .add(new Pair<Long, Boolean>(ownerId, val));

         pushToSecurityContext_SYSTEM_USER();
         try {

             int tariffId = 2;
             long userId = userId4;
             String accUid = "billing_blockUserAccs";

             List<String> oldAccs = db.chats.getOwnerAccs(userId);
             assertTrue(oldAccs.size() > 0);

             //create acc
             chats.createAcc(serverId1, accUid, userId, "test_monthBill", tariffId);
             chats.setOperatorForAcc(accUid, userId);
             assertEquals(1, chats.getAccOperators(accUid).size());

             //  ? - 
             {
                 correctBalance(userId, new BigDecimal(4.99d));
                 assertEquals("4.99", billing.getUserBalance(userId).toString());

                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-5.00";
                 assertFalse(findBalance(universal, userId).accsBlocked);

                 Date pastPay = parseStandartDateTime("01.08.2014 00:00:00");
                 Date now = parseStandartDateTime("02.09.2014 3:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, billingSync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //
                 assertEquals("-0.01", billing.getUserBalance(userId).toString());
                 assertTrue(findBalance(universal, userId).accsBlocked);

                 //?  ??
                 assertEquals(parseStandartDateTime("01.09.2014 00:00:00"),
                         universal.selectOne(new GetChatAccount(accUid)).tariffLastPay);

                 //?     , ..     
                 for (String uid : oldAccs)
                     assertEquals(longFuture, universal.selectOne(new GetChatAccount(uid)).tariffLastPay);

                 //? ?  
                 assertEquals(1, blockReqs.size());
                 assertTrue(blockReqs.get(0).second);
                 blockReqs.clear();

                 // 
                 pushToSecurityContext(new User(userId));
                 try {

                     //  ?  
                     try {
                         chats.updateAccTariffByUser(accUid, tariffId);
                         fail_exception_expected();
                     } catch (ChatAccountBlockedException e) {
                         //ok
                     }

                     //  ?  
                     try {
                         chats.createAccByUser("some");
                         fail_exception_expected();
                     } catch (ChatAccountBlockedException e) {
                         //ok
                     }

                     //  
                     try {
                         chats.pauseAccByUser(accUid);
                         fail_exception_expected();
                     } catch (ChatAccountBlockedException e) {
                         //ok
                     }

                     //  
                     try {
                         chats.unpauseAccByUser(accUid);
                         fail_exception_expected();
                     } catch (ChatAccountBlockedException e) {
                         //ok
                     }

                 } finally {
                     popUserFromSecurityContext();
                 }

                 //  ?   ?  
                 for (String uid : db.chats.getOwnerAccs(userId)) {
                     assertEquals("accUid=" + uid, true, isAccBlockedFromCache(cacheClient, uid));
                 }

                 //?  ? ? ?,    
                 {
                     Cache newCache = new CacheImpl(0);
                     BillingSyncService otherBillingSync = new BillingSyncService();
                     otherBillingSync
                             .setCacheServerContext(new CacheServerContext(props, newCache, db, mailService));
                     otherBillingSync.init();
                     for (String uid : db.chats.getOwnerAccs(userId)) {
                         assertEquals(true, isAccBlockedFromCache(newCache, uid));
                     }
                 }

                 //  ?     
                 pushToSecurityContext(new User(userId));
                 try {
                     assertTrue(chats.getBlockedAccs().size() > 0);
                     for (ChatAccount acc : chats.getAccsForOperator(userId)) {
                         assertEquals(true, acc.blocked);
                     }
                 } finally {
                     popUserFromSecurityContext();
                 }

             }

             // ?? -- ?   -- ?   
             {
                 assertTrue(findBalance(universal, userId).accsBlocked);
                 BigDecimal initBalance = billing.getUserBalance(userId);

                 Date now = parseStandartDateTime("01.10.2014 3:00:00");
                 assertEquals(1, billingSync.doSyncWork(false, now));
                 assertEquals("0.00", getDeltaVal(userId, initBalance));
                 assertEquals("-0.01", billing.getUserBalance(userId).toString());

                 // ?  ??
                 assertTrue(findBalance(universal, userId).accsBlocked);
                 assertEquals(parseStandartDateTime("01.10.2014 00:00:00"),
                         universal.selectOne(new GetChatAccount(accUid)).tariffLastPay);

                 //?     , ..     
                 for (String uid : oldAccs)
                     assertEquals(longFuture, universal.selectOne(new GetChatAccount(uid)).tariffLastPay);

                 //  ?  
                 assertEquals(0, blockReqs.size());

                 //  ?   ?  
                 for (String uid : db.chats.getOwnerAccs(userId)) {
                     assertEquals(true, BillingOps.isAccBlockedFromCache(cacheClient, uid));
                 }
             }

             //   ???      
             {
                 Date now = parseStandartDateTime("12.10.2014 15:45:00");
                 pushToSecurityContext(new User(userId));
                 try {
                     paypalClient.payAmount = new BigDecimal("0.01");
                     billing.sendPayReq(paypal_key.strDefVal(), paypalClient.payAmount);
                     billing.paypal_preparePayConfirm(randomSimpleId(), STUB_TOKEN);
                     billing.paypal_finishPayment(now);
                 } finally {
                     popUserFromSecurityContext();
                 }
                 assertEquals("0.00", billing.getUserBalance(userId).toString());
                 assertFalse(findBalance(universal, userId).accsBlocked);
                 assertEquals(now, universal.selectOne(new GetChatAccount(accUid)).tariffLastPay);

                 //?     , ..     
                 for (String uid : oldAccs)
                     assertEquals(longFuture, universal.selectOne(new GetChatAccount(uid)).tariffLastPay);

                 //? ?  
                 assertEquals(1, blockReqs.size());
                 assertFalse(blockReqs.get(0).second);
                 blockReqs.clear();

                 // ?  
                 pushToSecurityContext(new User(userId));
                 try {
                     chats.updateAccTariffByUser(accUid, tariffId);
                 } finally {
                     popUserFromSecurityContext();
                 }

                 //     ?  
                 for (String uid : db.chats.getOwnerAccs(userId)) {
                     assertEquals(false, BillingOps.isAccBlockedFromCache(cacheClient, uid));
                 }

                 //?  ? ? ?,    
                 {
                     Cache newCache = new CacheImpl(0);
                     BillingSyncService otherBillingSync = new BillingSyncService();
                     otherBillingSync
                             .setCacheServerContext(new CacheServerContext(props, newCache, db, mailService));
                     otherBillingSync.init();
                     for (String uid : db.chats.getOwnerAccs(userId)) {
                         assertEquals(false, isAccBlockedFromCache(newCache, uid));
                     }
                 }

                 //  ?     
                 pushToSecurityContext(new User(userId));
                 try {
                     assertTrue(chats.getBlockedAccs().size() == 0);
                     for (ChatAccount acc : chats.getAccsForOperator(userId)) {
                         assertEquals(false, acc.blocked);
                     }
                 } finally {
                     popUserFromSecurityContext();
                 }
             }

             // ??, ? ??   
             {
                 assertFalse(findBalance(universal, userId).accsBlocked);
                 BigDecimal initBalance = billing.getUserBalance(userId);

                 Date now = parseStandartDateTime("01.11.2014 5:15:00");
                 assertEquals(1, billingSync.doSyncWork(false, now));
                 assertEquals("-3.12", getDeltaVal(userId, initBalance));
                 assertEquals("-3.12", billing.getUserBalance(userId).toString());

                 // ?  ??
                 assertTrue(findBalance(universal, userId).accsBlocked);
                 assertEquals(parseStandartDateTime("01.11.2014 00:00:00"),
                         universal.selectOne(new GetChatAccount(accUid)).tariffLastPay);

                 //?     , ..     
                 for (String uid : oldAccs)
                     assertEquals(longFuture, universal.selectOne(new GetChatAccount(uid)).tariffLastPay);

                 //? ?  
                 assertEquals(1, blockReqs.size());
                 assertTrue(blockReqs.get(0).second);
                 blockReqs.clear();

                 //  ?  
                 pushToSecurityContext(new User(userId));
                 try {
                     chats.updateAccTariffByUser(accUid, tariffId);
                     fail_exception_expected();
                 } catch (ChatAccountBlockedException e) {
                     //ok
                 } finally {
                     popUserFromSecurityContext();
                 }
             }

             //       
             {
                 assertTrue(findBalance(universal, userId).accsBlocked);
                 Date oldLastPay = universal.selectOne(new GetChatAccount(accUid)).tariffLastPay;

                 Date now = parseStandartDateTime("03.11.2014 13:12:00");
                 pushToSecurityContext(new User(userId));
                 try {
                     paypalClient.payAmount = new BigDecimal("3.11");
                     billing.sendPayReq(paypal_key.strDefVal(), paypalClient.payAmount);
                     billing.paypal_preparePayConfirm(randomSimpleId(), STUB_TOKEN);
                     billing.paypal_finishPayment(now);
                 } finally {
                     popUserFromSecurityContext();
                 }

                 //? ??,     ?? 
                 assertEquals("-0.01", billing.getUserBalance(userId).toString());
                 assertTrue(findBalance(universal, userId).accsBlocked);
                 assertEquals(oldLastPay, universal.selectOne(new GetChatAccount(accUid)).tariffLastPay);

                 assertEquals(0, blockReqs.size());
             }

             //  ? ??    ? 
             {
                 assertTrue(findBalance(universal, userId).accsBlocked);

                 BigDecimal payAmount = new BigDecimal("5.01");
                 Date prev = parseStandartDateTime("04.11.2014 11:00:00");
                 Date now = parseStandartDateTime("05.11.2014 13:12:00");
                 long payId = universal.nextSeqFor(payments);
                 universal.update(new CreatePayment(
                         new PaymentExt(payId, userId, PAYPAL, "somePay", WAIT, prev, prev, payAmount)));

                 paypalClient.payAmount = payAmount;
                 paypalClient.paymentHistory = list(new PaymentBase("somePay", COMPLETED));
                 paypalClient.paymentId = "somePay";
                 paypalClient.payment = new PaymentBase(paypalClient.paymentId, COMPLETED);
                 paySync.doSyncWork(now);

                 // 
                 assertEquals(COMPLETED, universal.selectOne(new GetPaymentById(payId)).paymentStatus);

                 // ?
                 assertEquals("5.00", billing.getUserBalance(userId).toString());
                 assertFalse(findBalance(universal, userId).accsBlocked);
                 assertEquals(now, universal.selectOne(new GetChatAccount(accUid)).tariffLastPay);

                 //?     , ..     
                 for (String uid : oldAccs)
                     assertEquals(longFuture, universal.selectOne(new GetChatAccount(uid)).tariffLastPay);

                 //? ?  
                 assertEquals(1, blockReqs.size());
                 assertFalse(blockReqs.get(0).second);
                 blockReqs.clear();

             }

         } finally {
             popUserFromSecurityContext();
         }

         //?  
         Date now = new Date();
         universal.update(new UpdateAllChatAccounts(new TariffStart(now), new TariffLastPay(now)));
     }

     private void test_billing_callFromCacheFlag() throws Exception {

         MailServiceStub mailService = new MailServiceStub(mailSender, props);
         mailSender.tasks.clear();

         Date prevMonth = monthStart(addMonths(new Date(), -1));

         BillingSyncService sync = new BillingSyncService();
         sync.setCacheServerContext(new CacheServerContext(props, cacheSever, db, mailService));
         sync.init();

         assertEquals(null, cacheClient.getVal(BILLING_SYNC_REQ));
         assertEquals(null, cacheClient.getVal(BILLING_SYNC_RESP));

         universal.update(
                 new UpdateChatAccountByUid("demo", new TariffStart(prevMonth), new TariffLastPay(prevMonth)));

         //call
         String reqId = "123";
         cacheClient.putCache(BILLING_SYNC_REQ, reqId);
         sync.checkTasksFromCache();

         AdminSyncResp resp = tryParseJson(cacheClient.getVal(BILLING_SYNC_RESP), AdminSyncResp.class);
         assertNotNull(resp);
         assertEquals(reqId, resp.reqId);
         assertEquals(1, resp.updatedCount);

     }

     private void test_billing_sendErrorsWithEmails() throws Exception {

         MailServiceStub mailService = new MailServiceStub(mailSender, props);
         mailSender.tasks.clear();

         Date prevMonth = monthStart(addMonths(new Date(), -1));

         BillingSyncService sync = new BillingSyncService();
         sync.setCacheServerContext(new CacheServerContext(props, cacheSever, db, mailService));
         sync.init();

         //no errors
         universal.update(
                 new UpdateChatAccountByUid("demo", new TariffStart(prevMonth), new TariffLastPay(prevMonth)));
         assertEquals(1, sync.doSyncWork(false));
         assertEquals(0, mailSender.tasks.size());

         //with errors
         universal.update(
                 new UpdateChatAccountByUid("demo", new TariffStart(prevMonth), new TariffLastPay(prevMonth)));
         sync.syncAccsListener = () -> {
             throw new TestException();
         };
         assertEquals(1, sync.doSyncWork(false));
         assertEquals(1, mailSender.tasks.size());

     }

     private void test_tariff_billing_concurrent() throws Exception {

         ExecutorService async = ExecutorsUtil.newSingleThreadExecutor("billing_concurrent");

         MailServiceStub mailService = new MailServiceStub(mailSender, props);
         mailSender.tasks.clear();

         // ?    
         Date longFuture = parseStandartDateTime("02.09.2040 3:00:00");
         universal.update(new UpdateAllChatAccounts(new TariffStart(longFuture), new TariffLastPay(longFuture)));

         pushToSecurityContext_SYSTEM_USER();
         try {

             int tariffId = 2;
             long userId = userId4;
             String accUid = "tariff_billing_concurrent";
             BigDecimal tariffPrice = new BigDecimal("5.00");
             BigDecimal correctVal = new BigDecimal(100);

             //create acc
             chats.createAcc(serverId1, accUid, userId, "billing_concurrent", tariffId);
             chats.setOperatorForAcc(accUid, userId);
             universal.update(new UpdateUserAccsBlocked(userId, false));

             BillingSyncService sync = new BillingSyncService();
             sync.setCacheServerContext(new CacheServerContext(props, cacheSever, db, mailService));
             sync.init();

             //?? ?    ?  ? ? 
             {
                 correctBalance(userId, correctVal);

                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-5.00";

                 Date pastPay = parseStandartDateTime("01.08.2014 00:00:00");
                 Date tariffStart = parseStandartDateTime("01.08.2014 00:00:00");
                 Date now = parseStandartDateTime("03.09.2014 3:00:00");
                 universal.update(new UpdateChatAccountByUid(accUid, new TariffStart(tariffStart),
                         new TariffLastPay(pastPay)));

                 //race
                 try {
                     chats.updateAccTariff(accUid, 4,
                             new UpdateTariffOps(true, false, tariffStart, pastPay, now, tariffPrice, () -> {

                                 //  ? 
                                 //?     ?  ? 
                                 async.submit(() -> {

                                     pushToSecurityContext_SYSTEM_USER();
                                     try {
                                         assertEquals(1, sync.doSyncWork(false, now));
                                         assertEquals(expAmount, getDeltaVal(userId, initBalance));
                                         return null;
                                     } finally {
                                         popUserFromSecurityContext();
                                     }

                                 }).get();

                             }));

                     fail_exception_expected();

                 } catch (ConcurrentUpdateSqlException e) {
                     //ok
                 }

                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //?? ?    ?  ? ? 
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-5.34";

                 Date pastPay = parseStandartDateTime("01.08.2014 00:00:00");
                 Date tariffStart = parseStandartDateTime("01.08.2014 00:00:00");
                 Date now = parseStandartDateTime("03.09.2014 3:00:00");
                 universal.update(new UpdateChatAccountByUid(accUid, new TariffStart(tariffStart),
                         new TariffLastPay(pastPay)));

                 //race
                 int syncCount = sync.doSyncWork(false, now, () -> {

                     //  ? 
                     //?     ?  
                     async.submit(() -> {

                         pushToSecurityContext_SYSTEM_USER();
                         try {
                             chats.updateAccTariff(accUid, 4,
                                     new UpdateTariffOps(true, false, tariffStart, pastPay, now, tariffPrice));
                             assertEquals(expAmount, getDeltaVal(userId, initBalance));
                             return null;
                         } finally {
                             popUserFromSecurityContext();
                         }

                     }).get();

                 });
                 assertEquals(0, syncCount);
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  ?
                 Date newNow = parseStandartDateTime("03.09.2014 4:00:00");
                 BigDecimal newInitBalance = billing.getUserBalance(userId);
                 assertEquals(0, sync.doSyncWork(false, newNow));
                 assertEquals("0.00", getDeltaVal(userId, newInitBalance));
             }

         } finally {
             popUserFromSecurityContext();
         }

         //?  
         Date now = new Date();
         universal.update(new UpdateAllChatAccounts(new TariffStart(now), new TariffLastPay(now)));
     }

     private void test_billing_monthBill() throws Exception {

         MailServiceStub mailService = new MailServiceStub(mailSender, props);

         // ?    
         Date longFuture = parseStandartDateTime("02.09.2040 3:00:00");
         universal.update(new UpdateAllChatAccounts(new TariffStart(longFuture), new TariffLastPay(longFuture)));

         pushToSecurityContext_SYSTEM_USER();
         try {

             int tariffId = 2;
             long userId = userId4;
             String accUid = "billing_monthBill";
             BigDecimal correctVal = new BigDecimal(100);

             //create acc
             assertEquals(0, chats.getAccsForOperator(userId).size());
             chats.createAcc(serverId1, accUid, userId, "test_monthBill", tariffId);
             assertEquals(1, chats.getAccsForOperator(userId).size());
             assertEquals(0, chats.getAccOperators(accUid).size());

             //set operator
             chats.setOperatorForAcc(accUid, userId);
             assertEquals(1, chats.getAccOperators(accUid).size());
             assertEquals("0.00", billing.getUserBalance(userId).toString());

             mailSender.tasks.clear();

             BillingSyncService sync = new BillingSyncService();
             sync.setCacheServerContext(new CacheServerContext(props, cacheSever, db, mailService));
             sync.init();

             //  ?? (31)
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-5.00";

                 Date pastPay = parseStandartDateTime("01.08.2014 00:00:00");
                 Date now = parseStandartDateTime("02.09.2014 3:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  --  ?
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //  ?? (28)
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-5.00";

                 Date pastPay = parseStandartDateTime("01.02.2014 00:00:00");
                 Date now = parseStandartDateTime("02.03.2014 3:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  --  ?
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //  ??
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-2.72";

                 Date pastPay = parseStandartDateTime("15.08.2014 3:00:00");
                 Date now = parseStandartDateTime("02.09.2014 3:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  --  ?
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //  ??
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-2.24";

                 Date pastPay = parseStandartDateTime("15.02.2014 3:00:00");
                 Date now = parseStandartDateTime("20.03.2014 3:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  --  ?
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //? ?   ?
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "0.00";

                 Date pastPay = parseStandartDateTime("01.03.2014 2:00:00");
                 Date now = parseStandartDateTime("01.03.2014 3:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //1,5 ?? ( ?  ?? ?  ?  ??)
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-5.00";

                 Date pastPay = parseStandartDateTime("01.07.2014 00:00:00");
                 Date now = parseStandartDateTime("20.08.2014 12:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  --  ?
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //2  ?? ( ?  ?? ? ? )
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-10.00";

                 Date pastPay = parseStandartDateTime("01.07.2014 00:00:00");
                 Date now = parseStandartDateTime("02.09.2014 3:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  --  ?
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //3  ?? -  ?? ?
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-14.84"; //not 15.00

                 Date pastPay = parseStandartDateTime("01.07.2014 00:00:00");
                 Date now = parseStandartDateTime("15.10.2014 11:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  --  ?
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             // - ? ?
             {
                 correctBalance(userId, correctVal);
                 BigDecimal initBalance = billing.getUserBalance(userId);
                 String expAmount = "-58.87"; //12*5 = 60

                 Date pastPay = parseStandartDateTime("01.07.2014 00:00:00");
                 Date now = parseStandartDateTime("03.07.2015 11:00:00");
                 universal.update(
                         new UpdateChatAccountByUid(accUid, new TariffStart(pastPay), new TariffLastPay(pastPay)));
                 assertEquals(1, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));

                 //  --  ?
                 assertEquals(0, sync.doSyncWork(false, now));
                 assertEquals(expAmount, getDeltaVal(userId, initBalance));
             }

             //  ?  
             assertEquals(0, mailSender.tasks.size());

             //?   
             {
                 //before
                 assertEquals(-3, sync.doSyncWork(true, parseStandartDateTime("31.08.2014 12:35:00")));
                 assertEquals(-3, sync.doSyncWork(true, parseStandartDateTime("31.08.2014 23:59:00")));
                 assertEquals(-2, sync.doSyncWork(true, parseStandartDateTime("01.08.2014 00:00:00")));
                 assertEquals(-2, sync.doSyncWork(true, parseStandartDateTime("01.08.2014 05:00:00")));
                 assertEquals(-2, sync.doSyncWork(true, parseStandartDateTime("01.08.2014 05:59:59")));

                 //in
                 assertEquals(0, sync.doSyncWork(true, parseStandartDateTime("01.08.2014 06:00:00")));
                 assertEquals(0, sync.doSyncWork(true, parseStandartDateTime("02.08.2014 00:00:00")));
                 assertEquals(0, sync.doSyncWork(true, parseStandartDateTime("03.08.2014 00:00:00")));
                 assertEquals(0, sync.doSyncWork(true, parseStandartDateTime("03.08.2014 23:59:00")));
                 assertEquals(0, sync.doSyncWork(true, parseStandartDateTime("03.08.2014 23:59:01")));
                 assertEquals(0, sync.doSyncWork(true, parseStandartDateTime("03.08.2014 23:59:59")));

                 //after
                 assertEquals(-3, sync.doSyncWork(true, parseStandartDateTime("04.08.2014 00:00:00")));
             }

         } finally {
             popUserFromSecurityContext();
         }

         //?  
         Date now = new Date();
         universal.update(new UpdateAllChatAccounts(new TariffStart(now), new TariffLastPay(now)));

     }

     private String getDeltaVal(long userId, BigDecimal oldVal) throws Exception {
         BigDecimal curBalance = billing.getUserBalance(userId);
         return getDeltaVal(curBalance, oldVal);
     }

     private String getDeltaVal(BigDecimal newVal, BigDecimal oldVal) throws Exception {
         return round(newVal.subtract(oldVal)).toString();
     }

     private void correctBalance(long userId, BigDecimal val) throws Exception {
         universal.update(new UpdateUserBalanceUnsafe(userId, val));
         billing.updateUserBalanceCache(userId, false);
     }

     private void test_tariff_update_maxInDayLimit() throws Exception {

         long ownerId = 100;

         pushToSecurityContext_SYSTEM_USER();
         try {

             //reset day to now
             chats.updateAccTariff("demo2", 2, true);

             long t1 = chats.createTariff(new BigDecimal(1), true, 10);
             long t2 = chats.createTariff(new BigDecimal(2), true, 10);
             long t3 = chats.createTariff(new BigDecimal(3), true, 10);

             //day limitations
             pushToSecurityContext(new User(ownerId));
             try {
                 assertEquals(0, chats.getAccsForOperator(ownerId).get(1).tariffChangedInDay);
                 int maxLimitations = PropKey.tariffs_maxChangedInDay.intDefVal();
                 for (int i = 0; i < maxLimitations; i++) {
                     chats.updateAccTariffByUser("demo2", i % 2 == 0 ? t1 : t2);
                     assertEquals(i + 1, chats.getAccsForOperator(ownerId).get(1).tariffChangedInDay);
                 }
                 try {
                     chats.updateAccTariffByUser("demo2", t3);
                     fail_exception_expected();
                 } catch (ChangeTariffLimitException e) {
                     //ok
                 }
             } finally {
                 popUserFromSecurityContext();
             }

             //next day
             boolean canUseNotPublic = true;
             boolean checkChangedInDay = true;
             Date nextDay = addDays(new Date(), 1);
             chats.updateAccTariff("demo2", t3,
                     new UpdateTariffOps(canUseNotPublic, checkChangedInDay, null, null, nextDay, null));
             assertEquals(1, chats.getAccsForOperator(ownerId).get(1).tariffChangedInDay);

         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_tariff_update() throws Exception {

         //?    
         Date createDate = parseStandartDateTime("21.08.2014 12:00:00");
         universal.update(new UpdateAllChatAccounts(new TariffStart(createDate), new TariffLastPay(createDate)));

         BigDecimal price = new BigDecimal("5.00");

         pushToSecurityContext_SYSTEM_USER();
         try {

             long ownerId = 100;
             {
                 List<ChatAccount> accs = chats.getAccsForOperator(ownerId);
                 ChatAccount acc = accs.get(0);
                 assertEquals("demo", acc.uid);
                 assertEquals(1L, acc.tariffId);
                 assertEquals(null, acc.tariffPrevId);
             }

             //? ??? 
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "0.00";

                 Date tariffStart = createDate;
                 Date now = new Date(createDate.getTime() + 1000 * 60);

                 chats.updateAccTariff("demo", 3,
                         new UpdateTariffOps(true, false, tariffStart, tariffStart, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));

                 //test cache update
                 BigDecimal fromCache = tryParseBigDecimal(cacheClient.tryGetVal(getBalanceCacheKey(ownerId)), null);
                 assertEquals(expAmount, getDeltaVal(fromCache, initBalance));

                 List<ChatAccount> accs = chats.getAccsForOperator(ownerId);
                 ChatAccount acc = accs.get(0);
                 assertEquals("demo", acc.uid);
                 assertEquals(3L, acc.tariffId);
                 assertEquals(Long.valueOf(1L), acc.tariffPrevId);
             }

             //? ??? ?
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-0.01";

                 Date tariffStart = createDate;
                 Date now = new Date(createDate.getTime() + 1000 * 60 * 60);

                 chats.updateAccTariff("demo", 2,
                         new UpdateTariffOps(true, false, tariffStart, tariffStart, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));

                 //test cache update
                 BigDecimal fromCache = tryParseBigDecimal(cacheClient.tryGetVal(getBalanceCacheKey(ownerId)), null);
                 assertEquals(expAmount, getDeltaVal(fromCache, initBalance));
             }

             //? ??? 
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-0.16";

                 Date tariffStart = createDate;
                 Date now = new Date(createDate.getTime() + 1000 * 60 * 60 * 24);

                 chats.updateAccTariff("demo", 3,
                         new UpdateTariffOps(true, false, tariffStart, tariffStart, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));

                 //test cache update
                 BigDecimal fromCache = tryParseBigDecimal(cacheClient.tryGetVal(getBalanceCacheKey(ownerId)), null);
                 assertEquals(expAmount, getDeltaVal(fromCache, initBalance));
             }

             //? ???   ??
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-4.53";

                 Date tariffStart = parseStandartDateTime("01.08.2014 10:45:13");
                 Date now = parseStandartDateTime("29.08.2014 12:15:00");

                 chats.updateAccTariff("demo", 2,
                         new UpdateTariffOps(true, false, tariffStart, tariffStart, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));

                 //test cache update
                 BigDecimal fromCache = tryParseBigDecimal(cacheClient.tryGetVal(getBalanceCacheKey(ownerId)), null);
                 assertEquals(expAmount, getDeltaVal(fromCache, initBalance));
             }

             //? ???   ??
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-5.00";

                 Date tariffStart = parseStandartDateTime("01.08.2014 00:00:00");
                 Date now = parseStandartDateTime("31.08.2014 23:59:59");

                 chats.updateAccTariff("demo", 3,
                         new UpdateTariffOps(true, false, tariffStart, tariffStart, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));
             }

             //? ???   ??
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-4.52";

                 Date tariffStart = parseStandartDateTime("01.02.2014 00:00:00");
                 Date now = parseStandartDateTime("28.02.2014 23:59:59");

                 chats.updateAccTariff("demo", 2,
                         new UpdateTariffOps(true, false, tariffStart, tariffStart, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));
             }

             //? ??? ?? ? ?  (   ?  ?  )
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-5.24";

                 Date tariffStart = parseStandartDateTime("01.08.2014 00:00:00");
                 Date now = parseStandartDateTime("02.09.2014 12:15:00");

                 chats.updateAccTariff("demo", 3,
                         new UpdateTariffOps(true, false, tariffStart, tariffStart, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));
             }

             //? ??? 2 ?? (??? ? )
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-10.24";

                 Date tariffStart = parseStandartDateTime("01.08.2014 00:00:00");
                 Date now = parseStandartDateTime("03.10.2014 12:15:00");

                 chats.updateAccTariff("demo", 2,
                         new UpdateTariffOps(true, false, tariffStart, tariffStart, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));
             }

             //? ???  ?   ??
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-0.24";

                 Date tariffStart = parseStandartDateTime("01.08.2014 00:00:00");
                 Date lastPay = parseStandartDateTime("01.09.2014 00:00:00");
                 Date now = parseStandartDateTime("02.09.2014 12:15:00");

                 chats.updateAccTariff("demo", 3,
                         new UpdateTariffOps(true, false, tariffStart, lastPay, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));
             }

             //?    ? ??  ?
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "-0.24";

                 Date tariffStart = parseStandartDateTime("01.08.2014 00:00:00");
                 Date lastPay = parseStandartDateTime("01.09.2014 00:00:00");
                 Date now = parseStandartDateTime("02.09.2014 12:15:00");

                 chats.pauseAcc("demo", new UpdateTariffOps(true, false, tariffStart, lastPay, now, price));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));
             }

             //? ?     
             pushToSecurityContext(new User(ownerId));
             try {
                 chats.updateAccTariffByUser("demo", 3);
                 fail_exception_expected();
             } catch (ChatAccountPausedException e) {
                 //ok
             } finally {
                 popUserFromSecurityContext();
             }

             //?? ?    ?
             {
                 BigDecimal initBalance = billing.getUserBalance(ownerId);
                 String expAmount = "0.00";

                 Date tariffStart = parseStandartDateTime("01.08.2014 00:00:00");
                 Date lastPay = parseStandartDateTime("01.09.2014 00:00:00");
                 Date now = parseStandartDateTime("02.09.2014 12:15:00");

                 chats.unpauseAcc("demo", 3, new UpdateTariffOps(true, false, tariffStart, lastPay, now, null));
                 assertEquals(expAmount, getDeltaVal(ownerId, initBalance));
             }

         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_tariff_update_invalidInputs_limitations() throws Exception {

         pushToSecurityContext_SYSTEM_USER();
         try {

             try {
                 chats.updateAccTariff("demo-nono", 2, false);
                 fail_exception_expected();
             } catch (NoChatAccountException e) {
                 //ok
             }

             try {
                 chats.updateAccTariff("demo", -99, false);
                 fail_exception_expected();
             } catch (TariffNotFoundException e) {
                 //ok
             }

             try {
                 chats.updateAccTariff("demo", 6, false);
                 fail_exception_expected();
             } catch (NotPublicTariffException e) {
                 //ok
             }

             //limitations
             long storngTariffId = chats.createTariff(ZERO, true, 1);
             try {
                 chats.updateAccTariff("demo", storngTariffId, false);
                 fail_exception_expected();
             } catch (UpdateTariffOperatorsLimitException e) {
                 //ok
             }

         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_tariff_defaultTariffs() throws Exception {

         List<Tariff> tariffs = universal.select(new GetAllTariffs());
         assertTrue(tariffs.size() > 1);

         assertEquals(Tariff.PAUSE_TARIFF_ID, tariffs.get(0).id);
         assertEquals(0, tariffs.get(0).maxOperators);
         assertEquals(false, tariffs.get(0).isPublic);
         assertEquals("0.00", tariffs.get(0).price.toString());

         assertEquals(0, tariffs.get(1).maxOperators);
         assertEquals(false, tariffs.get(1).isPublic);

         assertEquals(2, tariffs.get(2).maxOperators);
         assertEquals(true, tariffs.get(2).isPublic);

         assertEquals(10, tariffs.get(4).maxOperators);
         assertEquals(false, tariffs.get(4).isPublic);

         assertEquals(10, tariffs.get(5).maxOperators);
         assertEquals(true, tariffs.get(5).isPublic);

         List<Tariff> publicTariffs = chats.getPublicTariffs();
         assertEquals(2, publicTariffs.size());
         assertEquals(2L, publicTariffs.get(0).id);
         assertEquals(5L, publicTariffs.get(1).id);

     }

     private void test_billing_atomicChangeBalance() throws Exception {

         long userId = 101L;
         assertEquals(0, findBalance(universal, userId).balance.intValue());

         assertEquals(1, appendBalance(universal, userId, new BigDecimal(1L)).intValue());
         assertEquals(1, findBalance(universal, userId).balance.intValue());

         assertEquals(2, appendBalance(universal, userId, new BigDecimal(1L)).intValue());
         assertEquals(2, findBalance(universal, userId).balance.intValue());

         assertEquals(1, appendBalance(universal, userId, new BigDecimal(-1L)).intValue());
         assertEquals(1, findBalance(universal, userId).balance.intValue());

         //manual check of atomic
         universal.update(new UpdateUserBalance(userId, new BigDecimal(100), new BigDecimal(99)));
         assertEquals(1, findBalance(universal, userId).balance.intValue());
         universal.update(new UpdateUserBalance(userId, new BigDecimal(1), new BigDecimal(99)));
         assertEquals(99, findBalance(universal, userId).balance.intValue());

         //?,    ?   ? 
         //  ? ?.
         {
             ExecutorService singleExecutor = ExecutorsUtil.newSingleThreadExecutor("test-optimistic-lock");
             Future<?> f = null;

             setSingleTxMode();
             try {

                 final BigDecimal oldVal = new BigDecimal(99);

                 int updated = universal.updateOne(new UpdateUserBalance(userId, oldVal, new BigDecimal(100)));
                 assertEquals(1, updated);

                 f = singleExecutor.submit(() -> {

                     setSingleTxMode();
                     try {
                         int updatedInOtherTx = universal
                                 .updateOne(new UpdateUserBalance(userId, oldVal, new BigDecimal(98)));
                         assertEquals(0, updatedInOtherTx);
                     } catch (Exception e) {
                         rollbackSingleTx();
                         throw e;
                     } finally {
                         closeSingleTx();
                     }
                     return null;

                 });

             } catch (Exception e) {
                 rollbackSingleTx();
                 throw e;
             } finally {
                 closeSingleTx();
             }

             //?  f.get    tx,   2 ?,         1 ( )
             //?   2  userId+1,  ??  
             //  ? ,   : ?    1      ,   ?
             f.get();

             //   ??  
             assertEquals(100, findBalance(universal, userId).balance.intValue());

             singleExecutor.shutdown();
         }

     }

     private void test_billing_payBill() throws Exception {

         pushToSecurityContext_SYSTEM_USER();
         try {

             BigDecimal balance = billing.getUserBalance(userId1);
             List<PaymentExt> payments = billing.getPayments(userId1, 100, 0);

             String desc = "test bill";
             BigDecimal delta = TEN;
             billing.payBill(userId1, delta, new Date(), SYSTEM_OUT_CORRECTION, desc);

             BigDecimal newBalance = billing.getUserBalance(userId1);
             assertEquals(balance.subtract(TEN).doubleValue(), newBalance.doubleValue(), 0.01);

             List<PaymentExt> newPayments = billing.getPayments(userId1, 100, 0);
             assertEquals(payments.size() + 1, newPayments.size());

             PaymentExt item = newPayments.get(0);
             assertEquals(desc, item.details);
             assertEquals(PaymentType.SYSTEM_OUT_CORRECTION, item.payType);

         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_billing_paySync() throws Exception {

         MapProps props = new MapProps();
         props.putVal(admin_Emails, "some@host");
         props.putVal(paypal_sync_debug_DisableTimer, true);
         props.putVal(mail_storeToDisc, false);

         MailServiceStub mailService = new MailServiceStub(mailSender, props);

         PaypalClientStub clientStub = new PaypalClientStub();
         ArrayList<PaymentExt> syncPaymentsByDate = new ArrayList<PaymentExt>();
         ArrayList<PaymentExt> syncPaymentsById = new ArrayList<PaymentExt>();
         boolean[] canSync = { false };

         //service
         PaypalPaymentsSynchService syncService = new PaypalPaymentsSynchService() {
             @Override
             protected void syncPaymentsByDate(List<PaymentExt> list, Date now) {
                 syncPaymentsByDate.clear();
                 syncPaymentsByDate.addAll(list);
                 if (canSync[0])
                     super.syncPaymentsByDate(list, now);
             }

             @Override
             protected void syncPaymentsByIdAsync(List<PaymentExt> list, Date now) {
                 syncPaymentsById.clear();
                 syncPaymentsById.addAll(list);
                 if (canSync[0])
                     super.syncPaymentsByIdAsync(list, now);
             }
         };
         syncService.setCacheServerContext(new CacheServerContext(props, cacheSever, db, mailService));
         syncService.setClient(clientStub);
         syncService.init();

         Date now = new Date();
         Date minuteAgo = new Date(now.getTime() - DateUtil.ONE_MINUTE);
         Date twoHoursAgo = new Date(now.getTime() - DateUtil.ONE_HOUR * 2);
         Date monthAgo = DateUtil.addDays(now, -31);

         long userId = 103L;
         //fill db
         universal.update(new CreatePayment(new PaymentExt(universal.nextSeqFor(payments), userId, PAYPAL, "p1",
                 WAIT, minuteAgo, minuteAgo, BigDecimal.ONE)));
         universal.update(new CreatePayment(new PaymentExt(universal.nextSeqFor(payments), userId, PAYPAL, "p2",
                 WAIT, twoHoursAgo, twoHoursAgo, BigDecimal.ONE)));
         universal.update(new CreatePayment(new PaymentExt(universal.nextSeqFor(payments), userId, PAYPAL, "p3",
                 WAIT, monthAgo, monthAgo, BigDecimal.ONE)));

         //check filter logic
         pushToSecurityContext_SYSTEM_USER();
         try {

             assertEquals(0, billing.getUserBalance(userId).intValue());

             //first call -- all payments to sync
             {
                 syncService.doSyncWork(now);
                 assertEquals(2, syncPaymentsByDate.size());
                 assertEquals(1, syncPaymentsById.size());
                 assertEquals("p1", syncPaymentsByDate.get(0).externalId);
                 assertEquals("p2", syncPaymentsByDate.get(1).externalId);
                 assertEquals("p3", syncPaymentsById.get(0).externalId);
                 syncPaymentsByDate.clear();
                 syncPaymentsById.clear();
             }

             //second call -- only new
             now = new Date(now.getTime() + 1000);
             {
                 syncService.doSyncWork(now);
                 assertEquals(1, syncPaymentsByDate.size());
                 assertEquals(0, syncPaymentsById.size());
                 assertEquals("p1", syncPaymentsByDate.get(0).externalId);
                 syncPaymentsByDate.clear();
                 syncPaymentsById.clear();
             }

             //next call -- same
             now = new Date(now.getTime() + 1000);
             {
                 syncService.doSyncWork(now);
                 assertEquals(1, syncPaymentsByDate.size());
                 assertEquals(0, syncPaymentsById.size());
                 assertEquals("p1", syncPaymentsByDate.get(0).externalId);
                 syncPaymentsByDate.clear();
                 syncPaymentsById.clear();
             }

             //after long delta -- all
             now = new Date(now.getTime() + 1000);
             props.putVal(paypal_sync_longUpdateDelta, 0);
             {
                 syncService.doSyncWork(now);
                 assertEquals(2, syncPaymentsByDate.size());
                 assertEquals(1, syncPaymentsById.size());
                 syncPaymentsByDate.clear();
                 syncPaymentsById.clear();
             }

             now = new Date(now.getTime() + 1000);
             props.putVal(paypal_sync_longUpdateDelta, paypal_sync_longUpdateDelta.longDefVal());
             {
                 syncService.doSyncWork(now);
                 assertEquals(1, syncPaymentsByDate.size());
                 assertEquals(0, syncPaymentsById.size());
                 assertEquals("p1", syncPaymentsByDate.get(0).externalId);
                 syncPaymentsByDate.clear();
                 syncPaymentsById.clear();
             }
         } finally {
             popUserFromSecurityContext();
         }

         //sync by 
         pushToSecurityContext_SYSTEM_USER();
         try {

             canSync[0] = true;

             //update by date
             now = new Date(now.getTime() + 1000);
             assertEquals(WAIT, universal.selectOne(new GetPaymentByExternalId(PAYPAL, "p1")).paymentStatus);
             clientStub.paymentHistory = list(new PaymentBase("p1", COMPLETED));
             syncService.doSyncWork(now);
             assertEquals(COMPLETED, universal.selectOne(new GetPaymentByExternalId(PAYPAL, "p1")).paymentStatus);
             assertEquals(1, universal.selectOne(new SelectUserBalanceById(userId)).balance.intValue());
             assertEquals(1, billing.getUserBalance(userId).intValue());

             //update by ids
             now = new Date(now.getTime() + 1000);
             props.putVal(paypal_sync_longUpdateDelta, 0);
             clientStub.paymentHistory = list(new PaymentBase("p2", COMPLETED));
             clientStub.paymentId = "p3";
             clientStub.payment = new PaymentBase(clientStub.paymentId, COMPLETED);
             syncService.doSyncWork(now);
             assertEquals(COMPLETED, universal.selectOne(new GetPaymentByExternalId(PAYPAL, "p2")).paymentStatus);
             assertEquals(COMPLETED, universal.selectOne(new GetPaymentByExternalId(PAYPAL, "p3")).paymentStatus);
             assertEquals(3, billing.getUserBalance(userId).intValue());

             now = new Date(now.getTime() + 1000);
             syncPaymentsByDate.clear();
             syncPaymentsById.clear();
             syncService.doSyncWork(now);
             assertEquals(0, syncPaymentsByDate.size());
             assertEquals(0, syncPaymentsById.size());

         } finally {
             popUserFromSecurityContext();
         }

         //test timers
         props.putVal(paypal_sync_debug_DisableTimer, false);
         props.putVal(paypal_sync_timerDelay, 1L);
         props.putVal(paypal_sync_timerDelta, 20L);
         syncService.stop();
         syncService.init();
         pushToSecurityContext_SYSTEM_USER();
         try {
             canSync[0] = true;
             clientStub.paymentHistory = list(new PaymentBase("p4", COMPLETED));
             universal.update(new CreatePayment(new PaymentExt(universal.nextSeqFor(payments), userId, PAYPAL, "p4",
                     WAIT, minuteAgo, minuteAgo, BigDecimal.ONE)));

             Thread.sleep(50);

             assertEquals(COMPLETED, universal.selectOne(new GetPaymentByExternalId(PAYPAL, "p4")).paymentStatus);
             assertEquals(4, billing.getUserBalance(userId).intValue());
         } finally {
             syncService.stop();
             popUserFromSecurityContext();
         }

         //test send errors to admin
         props.putVal(paypal_sync_sendErrorsDelay, 1L);
         props.putVal(paypal_sync_sendErrorsDelta, 20L);
         syncService.init();
         try {
             universal.update(new CreatePayment(new PaymentExt(universal.nextSeqFor(payments), userId, PAYPAL, "p5",
                     WAIT, minuteAgo, minuteAgo, BigDecimal.ONE)));

             mailSender.tasks.clear();
             clientStub.paymentHistory = null;

             Thread.sleep(100);
             assertTrue(mailSender.tasks.size() > 0);

             //dissable send errors
             props.putVal(paypal_sync_debug_DisableSendErrors, true);
             Thread.sleep(50);
             mailSender.tasks.clear();
             Thread.sleep(50);
             assertTrue(mailSender.tasks.size() == 0);

             //enable again
             props.putVal(paypal_sync_debug_DisableSendErrors, false);
             Thread.sleep(50);
             assertTrue(mailSender.tasks.size() > 0);

             //set filter
             props.putVal(paypal_sync_skipErrorTerms, "empty history by daysBefore");
             Thread.sleep(50);
             mailSender.tasks.clear();
             Thread.sleep(50);
             assertTrue(mailSender.tasks.size() == 0);

             //remove filter
             props.putVal(paypal_sync_skipErrorTerms, (String) null);
             Thread.sleep(50);
             assertTrue(mailSender.tasks.size() > 0);

         } finally {
             syncService.stop();
         }

     }

     private void test_billing_paypal_paySteps() throws Exception {

         User user = new User(100, "some", "dd@dd.dd", ACTIVATED);
         pushToSecurityContext(user);
         try {

             //show paypal page
             {
                 assertNull(cacheClient.getVal(BillingService.getPayReqCacheKey(user.id)));

                 PayData data = billing.sendPayReq(paypal_key.strDefVal(), new BigDecimal(12));
                 assertNotNull(data);

                 assertNotNull(cacheClient.getVal(BillingService.getPayReqCacheKey(user.id)));
             }

             //paypal wrong resp
             try {
                 billing.paypal_preparePayConfirm("some", STUB_TOKEN + "123");
             } catch (NoDataToConfirmPaymentException e) {
                 //ok
             }

             //paypal resp
             {
                 assertNull(cacheClient.getVal(BillingService.getConfirmPayCacheKey(user.id)));

                 String userPayId = "some-" + randomSimpleId();
                 billing.paypal_preparePayConfirm(userPayId, STUB_TOKEN);

                 assertNotNull(cacheClient.getVal(BillingService.getPayReqCacheKey(user.id)));
             }

             //confirm pay
             {
                 mailSender.tasks.clear();
                 assertEquals(0, billing.getCurUserBalance().intValue());

                 billing.paypal_finishPayment();
                 lastFrom(asyncFutures).get();

                 assertTrue(billing.getCurUserBalance().intValue() > 0);
                 assertNull(cacheClient.getVal(BillingService.getPayReqCacheKey(user.id)));
                 assertNull(cacheClient.getVal(BillingService.getConfirmPayCacheKey(user.id)));

                 //check db
                 List<PaymentExt> payments = billing.getPayments(10, 0);
                 assertEquals(1, payments.size());

                 //check email sended
                 assertEquals(1, mailSender.tasks.size());

             }

         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_billing_2checkout_paySteps() throws Exception {

         User user = new User(100, "some", "dd@dd.dd", ACTIVATED);
         pushToSecurityContext(user);
         try {

             int initBalance = billing.getCurUserBalance().intValue();
             int payCount = 12;

             String token = null;
             //pay req
             {
                 assertNull(cacheClient.getVal(BillingService.getPayReqCacheKey(user.id)));

                 PayData data = billing.sendPayReq(toCheckout_key.strDefVal(), new BigDecimal(payCount));
                 assertNotNull(data);

                 assertNotNull(cacheClient.getVal(BillingService.getPayReqCacheKey(user.id)));

                 token = data.token;
             }

             //wrong resp
             try {
                 billing.tochekout_finishPayment("some", "some");
             } catch (NoDataToConfirmPaymentException e) {
                 //ok
             }

             //confirm pay
             {
                 assertNotNull(cacheClient.getVal(BillingService.getPayReqCacheKey(user.id)));

                 mailSender.tasks.clear();
                 assertEquals(initBalance, billing.getCurUserBalance().intValue());

                 String txId = "some-" + randomSimpleId();
                 billing.tochekout_finishPayment(token, txId);
                 lastFrom(asyncFutures).get();

                 assertEquals(initBalance + payCount, billing.getCurUserBalance().intValue());
                 assertNull(cacheClient.getVal(BillingService.getPayReqCacheKey(user.id)));
                 assertNull(cacheClient.getVal(BillingService.getConfirmPayCacheKey(user.id)));

                 //check db
                 List<PaymentExt> payments = billing.getPayments(10, 0);
                 assertEquals(2, payments.size());

                 PaymentExt payment = payments.get(0);
                 assertEquals(payCount, payment.amount.intValue());
                 assertEquals(txId, payment.externalId);
                 assertEquals(PaymentStatus.COMPLETED, payment.paymentStatus);
                 assertEquals(PaymentType.REPLENISHMENT, payment.payType);
                 assertEquals(PaymentProvider.TO_CHECKOUT, payment.provider);

                 //check email sended
                 assertEquals(1, mailSender.tasks.size());

             }

         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_billing_get_balance_from_cache_or_db() throws Exception {

         pushToSecurityContext_SYSTEM_USER();
         try {

             //from cache
             {
                 long user = 100;
                 assertEquals("0.00", cacheClient.getVal("balance-" + user));
                 assertEquals("0.00", billing.getUserBalance(user).toString());
             }

             //from db and put to cache
             {
                 long user = 102;
                 assertEquals(null, cacheClient.getVal("balance-" + user));
                 assertEquals(0, billing.getUserBalance(user).intValue());
                 lastFrom(asyncFutures).get();
                 assertEquals("0.00", cacheClient.getVal("balance-" + user));
             }

             //unknown user
             try {
                 billing.getUserBalance(-99);
                 fail_exception_expected();
             } catch (UserNotFoundException e) {
                 //ok
             }

         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_billing_put_balances_to_cache() throws Exception {

         long user1 = 100;
         long user2 = 101;

         //in start
         assertEquals("0.00", cacheClient.getVal("balance-" + user1));
         assertEquals(null, cacheClient.getVal("balance-" + user2));

         //after creation
         pushToSecurityContext_SYSTEM_USER();
         try {
             String accountId = "demo-balance";
             chats.createAcc(serverId3, accountId, user2, null, 1);
         } finally {
             popUserFromSecurityContext();
         }
         lastFrom(asyncFutures).get();
         assertEquals(null, cacheClient.getVal("balance-" + user2));

     }

     private void test_chats_operatorsLimits() throws Exception {

         long tariffId = 2;
         String name = "operatorsLimits";
         User user = new User(userId1);

         pushToSecurityContext(user);
         try {

             String accUid = chats.createAccByUser(name, tariffId);

             chats.setOperatorForAcc(accUid, userId1);
             chats.setOperatorForAcc(accUid, userId2);

             //   ? ? 
             try {
                 chats.setOperatorForAcc(accUid, userId3);
                 fail_exception_expected();
             } catch (OperatorsLimitException e) {
                 //ok
             }

             chats.removeUserPrivileges(accUid, userId2, set(PrivilegeType.CHAT_OPERATOR));
             chats.setOperatorForAcc(accUid, userId3);

             //   ? ?  ?
             try {
                 chats.setOperatorForAcc(accUid, userId2);
                 fail_exception_expected();
             } catch (OperatorsLimitException e) {
                 //ok
             }

             //  
             {
                 chats.removeUserPrivileges(accUid, userId3, set(PrivilegeType.CHAT_OPERATOR));
                 chats.pauseAccByUser(accUid);

                 try {
                     chats.setOperatorForAcc(accUid, userId2);
                     fail_exception_expected();
                 } catch (ChatAccountPausedException e) {
                     //ok
                 }

                 chats.unpauseAccByUser(accUid);
                 chats.setOperatorForAcc(accUid, userId2);
             }

         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_chats_changePrivsForUser() throws Exception {

         long tariffId = 5;
         User user1 = new User(userId1);
         User user2 = new User(userId2);
         String name = "changePrivsForUser";
         String accUid = null;

         //create acc
         pushToSecurityContext(user1);
         try {
             accUid = chats.createAccByUser(name, tariffId);
         } finally {
             popUserFromSecurityContext();
         }
         assertEquals(set(CHAT_OWNER), chats.getAccPrivilegesForUser(accUid, userId1));

         //add empty privs
         {
             chats.addUserPrivileges(accUid, userId2, set());
             assertEquals(set(), chats.getAccPrivilegesForUser(accUid, userId2));
         }

         //add with no role
         {
             try {
                 chats.addUserPrivileges(accUid, userId2, set(CHAT_OPERATOR));
                 fail_exception_expected();
             } catch (AccessDeniedException e) {
                 //ok
             }
         }

         //add with wrong role
         {
             pushToSecurityContext(user2);
             try {
                 chats.addUserPrivileges(accUid, userId2, set(CHAT_OPERATOR));
                 fail_exception_expected();
             } catch (AccessDeniedException e) {
                 //ok
             } finally {
                 popUserFromSecurityContext();
             }
         }

         //add like owner
         {
             pushToSecurityContext(user1);
             try {
                 chats.addUserPrivileges(accUid, userId2, set(CHAT_OPERATOR));
             } finally {
                 popUserFromSecurityContext();
             }
             assertEquals(set(CHAT_OPERATOR), chats.getAccPrivilegesForUser(accUid, userId2));
         }

         //remove empty privs
         {
             chats.removeUserPrivileges(accUid, userId2, set());
             assertEquals(set(CHAT_OPERATOR), chats.getAccPrivilegesForUser(accUid, userId2));
         }

         //remove with no role
         {
             try {
                 chats.removeUserPrivileges(accUid, userId2, set(CHAT_OPERATOR));
                 fail_exception_expected();
             } catch (AccessDeniedException e) {
                 //ok
             }
         }

         //remove with wrong role
         {
             pushToSecurityContext(user2);
             try {
                 chats.removeUserPrivileges(accUid, userId2, set(CHAT_OPERATOR));
                 fail_exception_expected();
             } catch (AccessDeniedException e) {
                 //ok
             } finally {
                 popUserFromSecurityContext();
             }
         }

         //remove like owner
         {
             pushToSecurityContext(user1);
             try {
                 chats.removeUserPrivileges(accUid, userId2, set(CHAT_OPERATOR));
             } finally {
                 popUserFromSecurityContext();
             }
             assertEquals(set(), chats.getAccPrivilegesForUser(accUid, userId2));
         }

         //add moder and self operator
         {
             pushToSecurityContext(user1);
             try {
                 chats.addUserPrivileges(accUid, userId1, set(CHAT_OPERATOR));
                 chats.addUserPrivileges(accUid, userId2, set(CHAT_MODER));
             } finally {
                 popUserFromSecurityContext();
             }
             assertEquals(set(CHAT_OWNER, CHAT_OPERATOR), chats.getAccPrivilegesForUser(accUid, userId1));
             assertEquals(set(CHAT_MODER), chats.getAccPrivilegesForUser(accUid, userId2));
         }

         //try add moder by other moder
         {
             pushToSecurityContext(user2);
             try {
                 chats.addUserPrivileges(accUid, userId3, set(CHAT_MODER));
                 fail_exception_expected();
             } catch (AccessDeniedException e) {
                 //ok
             } finally {
                 popUserFromSecurityContext();
             }
         }

         //try remove moder by other moder
         {
             pushToSecurityContext(user2);
             try {
                 chats.removeUserPrivileges(accUid, userId2, set(CHAT_MODER));
                 fail_exception_expected();
             } catch (AccessDeniedException e) {
                 //ok
             } finally {
                 popUserFromSecurityContext();
             }
         }

         //add operator by moder
         {
             pushToSecurityContext(user2);
             try {
                 chats.addUserPrivileges(accUid, userId2, set(CHAT_OPERATOR));
                 chats.addUserPrivileges(accUid, userId3, set(CHAT_OPERATOR));
             } finally {
                 popUserFromSecurityContext();
             }
             assertEquals(set(CHAT_MODER, CHAT_OPERATOR), chats.getAccPrivilegesForUser(accUid, userId2));
             assertEquals(set(CHAT_OPERATOR), chats.getAccPrivilegesForUser(accUid, userId3));
         }

         //remove operator by moder
         {
             pushToSecurityContext(user2);
             try {
                 chats.removeUserPrivileges(accUid, userId2, set(CHAT_OPERATOR));
                 chats.removeUserPrivileges(accUid, userId3, set(CHAT_OPERATOR));
             } finally {
                 popUserFromSecurityContext();
             }
             assertEquals(set(CHAT_MODER), chats.getAccPrivilegesForUser(accUid, userId2));
             assertEquals(set(), chats.getAccPrivilegesForUser(accUid, userId3));
         }

         //add op with unexists user
         pushToSecurityContext(user2);
         try {
             chats.addUserPrivileges(accUid, -100, set(CHAT_OPERATOR));
         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_chats_renameAccs_BySystem_ByUser() throws Exception {

         int userId = 100;
         String uid = null;

         pushToSecurityContext_SYSTEM_USER();
         try {
             List<ChatAccount> accs = chats.getAccsForOperator(userId);
             assertTrue(accs.size() > 0);
             uid = accs.get(0).uid;
         } finally {
             popUserFromSecurityContext();
         }

         //rename by system
         String newNameA = "newName-123";
         pushToSecurityContext_SYSTEM_USER();
         try {
             chats.putAccConfigByUser(uid, Key.name, newNameA);
             assertEquals(newNameA, chats.getAccsForOperator(userId).get(0).name);
         } finally {
             popUserFromSecurityContext();
         }

         //rename by user
         String newNameB = "newName-345";
         pushToSecurityContext(new User(100));
         try {
             chats.putAccConfigByUser(uid, Key.name, newNameB);
             assertEquals(newNameB, chats.getAccsForOperator(userId).get(0).name);
         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_chats_createAccs_byUser() throws Exception {

         pushToSecurityContext_SYSTEM_USER();
         try {

             List<ServerRow> servers = chats.getServers();
             assertEquals(3, servers.size());

             String accName = "test_chats_createAccs_byUser";
             User user = new User(100);

             //create by System
             try {
                 chats.createAccByUser(accName);
                 fail_exception_expected();
             } catch (UserNotFoundException e) {
                 //ok
             }

             //no servers exception
             {
                 for (ServerRow server : servers)
                     chats.setServerFull(server.id, true);

                 pushToSecurityContext(user);
                 try {
                     chats.createAccByUser(accName);
                     fail_exception_expected();
                 } catch (NoAvailableServerException e) {
                     //ok
                 } finally {
                     popUserFromSecurityContext();
                 }
             }

             //set server to acc
             long serverId = servers.get(1).id;
             chats.setServerFull(serverId, false);
             pushToSecurityContext(user);
             try {
                 chats.createAccByUser(accName);
             } finally {
                 popUserFromSecurityContext();
             }

             List<ChatAccount> accs = chats.getAccsForOperator(user.id);
             ChatAccount acc = find(accs, (c) -> accName.equals(c.name));
             assertNotNull(acc);
             assertNotNull(acc.created);

         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_chats_createServerAndAcc_BySystem() throws Exception {

         mailSender.tasks.clear();

         pushToSecurityContext_SYSTEM_USER();
         try {

             //dublicate
             try {
                 chats.createServer(server1HttpUrl, server1HttpsUrl);
                 fail_exception_expected();
             } catch (SQLException e) {
                 //ok
             }

             assertEquals(0, mailSender.tasks.size());

             serverId3 = chats.createServer(server3HttpUrl, server3HttpsUrl);

             int chatOwnerId = 100;

             //dublicate
             try {
                 chats.createAcc(serverId3, "demo", chatOwnerId, null, 1);
                 fail_exception_expected();
             } catch (SQLException e) {
                 //ok
             }

             assertEquals(0, mailSender.tasks.size());

             String accountId = "demo3";
             chats.createAcc(serverId3, accountId, chatOwnerId, null, 1);
             assertEquals(1, mailSender.tasks.size());

             int chatOperatorId = 2;
             chats.setOperatorForAcc(accountId, chatOperatorId);

             //test get
             ServerRow server = chats.getServerByAcc(accountId);
             assertNotNull(server);
             assertEquals(server3HttpUrl, server.httpUrl);

         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_set_get_roles() throws Exception {

         try {
             pushToSecurityContext_SYSTEM_USER();
             assertEquals(emptySet(), users.getUserById(userId1).getRoles());

             users.setRoles(userId1, set(ADMIN, MODERATOR));
             assertEquals(set(ADMIN, MODERATOR), users.getUserById(userId1).getRoles());

             users.setRoles(userId1, set(ADMIN));
             assertEquals(set(ADMIN), users.getUserById(userId1).getRoles());

             users.setRoles(userId1, new HashSet<UserRole>(0));
             assertEquals(emptySet(), users.getUserById(userId1).getRoles());

             users.setRoles(userId1, null);
             assertEquals(emptySet(), users.getUserById(userId1).getRoles());
         } finally {
             popUserFromSecurityContext();
         }

     }

     private void test_ban_user_runtime_with_exists_session() throws Exception {

         LoginUserReq loginReq = new LoginUserReq(mail1, psw1, false);

         //login
         security.createUserSession(req, resp, loginReq);
         {
             User user = security.getUserFromSession(req);
             assertNotNull(user);
             assertEquals(login1, user.login);
         }

         //ban
         try {
             pushToSecurityContext_SYSTEM_USER();
             users.banUser(userId1);
         } finally {
             popUserFromSecurityContext();
         }
         assertNull(security.getUserFromSession(req));

         //unban
         try {
             pushToSecurityContext_SYSTEM_USER();
             users.activateUser(userId1);
         } finally {
             popUserFromSecurityContext();
         }
         {
             User user = security.getUserFromSession(req);
             assertNotNull(user);
             assertEquals(login1, user.login);
         }

         //ban and try login
         try {
             pushToSecurityContext_SYSTEM_USER();
             users.banUser(userId1);
         } finally {
             popUserFromSecurityContext();
         }
         assertNull(security.getUserFromSession(req));
         req.clearSessionAttrs();
         try {
             security.createUserSession(req, resp, loginReq);
             fail_exception_expected();
         } catch (BannedUserException e) {
             //ok
         }
         assertNull(security.getUserFromSession(req));

         //unban and login
         try {
             pushToSecurityContext_SYSTEM_USER();
             users.activateUser(userId1);
         } finally {
             popUserFromSecurityContext();
         }
         security.createUserSession(req, resp, loginReq);
         assertNotNull(security.getUserFromSession(req));

     }

     private void test_invalidLoginsCount() throws Exception {

         String login = "test";
         LoginUserReq data = new LoginUserReq(login, "123", false);
         String cacheKey = getInvalidLoginsKey(req, data);

         //init state
         assertEquals(0, security.getInvalidLoginsCount(req, data, 100));
         assertNull(cacheClient.getVal(cacheKey));

         //put-get
         security.setInvalidLoginsCountAsync(req, data, 3).get();
         assertEquals(3, security.getInvalidLoginsCount(req, data, 100));
         assertEquals("3", cacheClient.getVal(cacheKey));
         assertEquals(INVALID_LOGINS_CACHE_LIVETIME_MS, cacheSever.getItemLivetime(cacheKey).intValue());

         //remove
         security.setInvalidLoginsCountAsync(req, data, 0).get();
         assertEquals(0, security.getInvalidLoginsCount(req, data, 100));
         assertNull(cacheClient.getVal(cacheKey));

     }

     private void test_remMeToken_maxCount() throws Exception {

         int maxRemCount = security.maxRemCount();
         int deleteRemCount = security.deleteRemCount();
         for (int i = 0; i < maxRemCount + deleteRemCount; i++) {
             resetWeb();
             security.createUserSession(req, resp, new LoginUserReq(mail4, psw4, true));
             asyncFutures.get(0).get();
         }

         List<RemToken> stored = db.universal.select(new SelectRemTokensByUser(userId4));
         assertEquals(maxRemCount - deleteRemCount, stored.size());

     }

     private void test_user_psw_secure() throws Exception {

         UserExt stored = db.universal.selectOne(new SelectUserById(userId1));
         assertEquals(stored.pswHash, WebUtil.getHash(psw1, stored.pswSalt));

     }

     private void test_remMeToken_secure() throws Exception {

         //login   
         resetWeb();
         security.createUserSession(req, resp, new LoginUserReq(mail2, psw2, true));
         asyncFutures.get(0).get();
         ClientRemToken clientToken = ClientRemToken.decodeFromCookieVal(resp.cookies.get(0).getValue());

         //check db secure
         List<RemToken> stored = db.universal.select(new SelectRemTokensByUser(userId2));
         RemToken storedToken = stored.get(0);
         assertEquals(storedToken.tokenHash, WebUtil.getHash(clientToken.random, storedToken.tokenSalt));

     }

     private void test_create_without_remMe() throws Exception {

         resetWeb();

         //login      
         security.createUserSession(req, resp, new LoginUserReq(mail1, psw1, true));
         asyncFutures.get(0).get();

         //clear session and login again with FALSE flag
         req.clearSessionAttrs();
         req.setCookies(resp);
         resp.clearCookies();
         security.createUserSession(req, resp, new LoginUserReq(mail1, psw1, false));
         assertEmptyRemCookie();

     }

     private void test_logout_remMe() throws Exception {

         resetWeb();

         //login      
         security.createUserSession(req, resp, new LoginUserReq(mail1, psw1, true));
         asyncFutures.get(0).get();

         assertNotNull(req.session.getAttribute(SESSION_OBJ_KEY));
         Cookie cookie1 = resp.cookies.get(0);

         //logout with no cookie in req
         req.clearCookies();
         resp.clearCookies();
         security.logout(req, resp);
         assertNull(req.session.getAttribute(SESSION_OBJ_KEY));
         assertEmptyRemCookie();

         //restore
         req.setCookies(cookie1);
         security.restoreUserSession(req, resp);
         assertNotNull(req.session.getAttribute(SESSION_OBJ_KEY));

         //logout with cookie in req
         req.setCookies(cookie1);
         resp.clearCookies();
         security.logout(req, resp);
         assertNull(req.session.getAttribute(SESSION_OBJ_KEY));
         assertEmptyRemCookie();

     }

     private void test_create_restore_remMe() throws Exception {

         resetWeb();

         //try restore with no cookie
         assertNull(security.restoreUserSession(req, resp));

         //first login
         asyncFutures.clear();
         security.createUserSession(req, resp, new LoginUserReq(mail1, psw1, true));
         asyncFutures.get(0).get();

         assertNotNull(req.session.getAttribute(SESSION_OBJ_KEY));
         assertEquals(1, resp.cookies.size());
         Cookie cookie1 = resp.cookies.get(0);
         assertEquals(REM_TOKEN, cookie1.getName());

         //try restore with full session and NO cookie
         assertNull(security.restoreUserSession(req, resp));

         //try restore with empty session and NO cookie
         req.clearSessionAttrs();
         assertNull(security.restoreUserSession(req, resp));

         //restore session with cookie
         asyncFutures.clear();
         req.clearSessionAttrs();
         req.setCookies(cookie1);
         resp.clearCookies();
         assertNotNull(security.restoreUserSession(req, resp));
         assertNotNull(req.session.getAttribute(SESSION_OBJ_KEY));
         asyncFutures.get(0).get();

         //after restored it has new cookie
         assertEquals(1, resp.cookies.size());
         Cookie cookie_AfterResore = resp.cookies.get(0);

         //try restore with old cookie
         req.clearSessionAttrs();
         req.clearCookies();
         req.setCookies(cookie1);
         resp.clearCookies();
         assertNull(security.restoreUserSession(req, resp));

         //restore with new cookie
         req.clearCookies();
         req.setCookies(cookie_AfterResore);
         resp.clearCookies();
         assertNotNull(security.restoreUserSession(req, resp));
         asyncFutures.get(0).get();

         cookie1 = resp.cookies.get(0);

         //create agian with exists session
         assertNotNull(req.session.getAttribute(SESSION_OBJ_KEY));
         try {
             security.createUserSession(req, resp, new LoginUserReq(login1, psw1, true));
             fail_exception_expected();
         } catch (UserSessionAlreadyExistsException e) {
             //ok
         }

         //create again with clear inputs
         //it will be TWO tokens in DB
         asyncFutures.clear();
         req.clearSessionAttrs();
         req.clearCookies();
         resp.clearCookies();
         security.createUserSession(req, resp, new LoginUserReq(mail1, psw1, true));
         asyncFutures.get(0).get();

         Cookie cookie2 = resp.cookies.get(0);
         assertFalse(cookie1.getValue().equals(cookie2.getValue()));
         //   try to restore with old cookie
         req.clearSessionAttrs();
         req.setCookies(cookie1);
         assertNotNull(security.restoreUserSession(req, resp));
         //   restore with new cookie
         req.setCookies(cookie2);
         req.clearSessionAttrs();
         assertNotNull(security.restoreUserSession(req, resp));
     }

     private void test_user_update() throws Exception {

         pushToSecurityContext_SYSTEM_USER();
         try {

             //correct
             String newMail = "new@mail.com";
             String newLogin = "'; and 1=1; newUser";
             String newPsw = "newPsw";
             users.updateUser(userId1, psw1, new UpdateUserReq(newMail, newLogin, newPsw));

             User updated = users.getUserById(userId1);
             assertEquals(newMail, updated.email);
             assertEquals(newLogin, updated.login);
             assertNotNull(users.checkEmailOrLoginAndPsw(newMail, newPsw));

             users.updateUser(userId1, newPsw, new UpdateUserReq(mail1, login1, psw1));
             updated = users.getUserById(userId1);
             assertEquals(mail1, updated.email);
             assertEquals(login1, updated.login);
             assertNotNull(users.checkEmailOrLoginAndPsw(mail1, psw1));

             //wrong psw
             try {
                 users.updateUser(userId1, null, new UpdateUserReq(newMail, newLogin, newPsw));
                 fail_exception_expected();
             } catch (ValidationException e) {
                 //ok
             }
             try {
                 users.updateUser(userId1, psw2, new UpdateUserReq(newMail, newLogin, newPsw));
                 fail_exception_expected();
             } catch (InvalidLoginDataForUpdateException e) {
                 //ok
             }

             //wrong login
             try {
                 users.updateUser(userId1, psw1, new UpdateUserReq(newMail, User.INVALID_LOGIN_CHARS, newPsw));
                 fail_exception_expected();
             } catch (ValidationException e) {
                 //ok
             }

             //wrong email
             try {
                 users.updateUser(userId1, psw1, new UpdateUserReq("wrongMail", newLogin, newPsw));
                 fail_exception_expected();
             } catch (ValidationException e) {
                 //ok
             }

         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_user_change_psw() throws Exception {

         mailSender.tasks.clear();

         //for exists
         assertNotNull(users.checkEmailOrLoginAndPsw(mail3, psw3));
         assertNotNull(users.checkEmailOrLoginAndPsw(login3, psw3));
         users.generateNewPassword(mail3);
         assertNull(users.checkEmailOrLoginAndPsw(mail3, psw3));
         assertNull(users.checkEmailOrLoginAndPsw(login3, psw3));

         RestorePswEmailParser fromEmail = new RestorePswEmailParser(mailSender.tasks.get(0).msg.text);
         assertEquals(login3, fromEmail.login);
         assertNotNull(users.checkEmailOrLoginAndPsw(mail3, fromEmail.psw));

         //by login
         users.generateNewPassword(login3);
         assertEquals(1, mailSender.tasks.size());

         //by unknown
         users.generateNewPassword("unknown data");
         assertEquals(1, mailSender.tasks.size());

     }

     private void test_user_send_activation_email_again() throws Exception {

         try {
             pushToSecurityContext_SYSTEM_USER();

             users.sendActivationEmailAgain(mail4 + "123");
             assertEquals(4, mailSender.tasks.size());

             users.sendActivationEmailAgain(mail3);
             assertEquals(4, mailSender.tasks.size());

             props.putVal(users_expiredTime, -1);
             try {
                 users.sendActivationEmailAgain(mail4);
                 fail_exception_expected();
             } catch (UserActivationExpiredException e) {
                 //ok
             }
             props.removeVal(users_expiredTime);

             users.sendActivationEmailAgain(mail4);
             assertEquals(5, mailSender.tasks.size());
             ActivationEmailParser fromEmail = new ActivationEmailParser(mailSender.tasks.get(4).msg.text);
             users.activateUser(fromEmail.email, fromEmail.code);
             assertNotNull(users.checkEmailOrLoginAndPsw(mail4, psw4));
         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_user_invalid_activation_states() throws Exception {

         ActivationEmailParser fromEmail = new ActivationEmailParser(mailSender.tasks.get(2).msg.text);
         users.activateUser(fromEmail.email + "123", fromEmail.code);
         checkNotActiveUser(users, login3, mail3, psw3);
         try {
             users.activateUser(fromEmail.email, fromEmail.code + "123");
             fail_exception_expected();
         } catch (InvalidUserActivationCodeException e) {
             //ok
         }
         props.putVal(users_expiredTime, -1);
         try {
             users.activateUser(fromEmail.email, fromEmail.code);
             fail_exception_expected();
         } catch (UserActivationExpiredException e) {
             //ok
         }
         props.removeVal(users_expiredTime);
         users.activateUser(fromEmail.email, fromEmail.code);
         assertNotNull(users.checkEmailOrLoginAndPsw(mail3, psw3));

     }

     private void test_user_activate_from_email_text() throws Exception {

         checkNotActiveUser(users, login2, mail2, psw2);
         ActivationEmailParser fromEmail = new ActivationEmailParser(mailSender.tasks.get(1).msg.text);
         users.activateUser(fromEmail.email, fromEmail.code);
         assertNotNull(users.checkEmailOrLoginAndPsw(mail2, psw2));
         assertNotNull(users.checkEmailOrLoginAndPsw(login2, psw2));

         //second activation
         users.activateUser(fromEmail.email, fromEmail.code);

     }

     private void test_user_unban() throws Exception {
         try {
             pushToSecurityContext_SYSTEM_USER();
             users.activateUser(userId1);
             assertNotNull(users.checkEmailOrLoginAndPsw(mail1, psw1));
         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_user_ban() throws Exception {

         try {
             pushToSecurityContext_SYSTEM_USER();
             users.banUser(userId1);
             try {
                 users.checkEmailOrLoginAndPsw(mail1, psw1);
                 fail_exception_expected();
             } catch (BannedUserException e) {
                 //ok
             }
             try {
                 users.checkEmailOrLoginAndPsw(login1, psw1);
                 fail_exception_expected();
             } catch (BannedUserException e) {
                 //ok
             }

             //try ban ADMIN user
             try {
                 users.banUser(100);
                 fail_exception_expected();
             } catch (UnmodifiableAdminUserException e) {
                 //ok
             }
         } finally {
             popUserFromSecurityContext();
         }
     }

     private void test_user_activate() throws Exception {
         test_user_unban();
         assertNull(users.checkEmailOrLoginAndPsw(mail1, psw2));
         assertNull(users.checkEmailOrLoginAndPsw(login1, psw2));
     }

     private void test_user_unexists() throws Exception {
         assertNull(users.checkEmailOrLoginAndPsw("unknown", "unknown"));
         checkNotActiveUser(users, login1, mail1, psw2);
     }

     private void test_user_invalid_inputs() throws Exception {
         createUserWithInvalidData(mail1, "login@", psw1, users);
         createUserWithInvalidData(mail1, "login<", psw1, users);
         createUserWithInvalidData(mail1, "login&", psw1, users);
         createUserWithInvalidData(mail1, "login#", psw1, users);
         createUserWithInvalidData("new-" + login2, "invalid-email", psw1, users);
     }

     private void test_user_duplicates() throws Exception {
         try {
             users.createUser(new User(login1, "new-" + mail1), psw1);
             fail_exception_expected();
         } catch (DuplicateUserDataException e) {
             //ok
         }
         try {
             users.createUser(new User("new-" + login1, mail1), psw1);
             fail_exception_expected();
         } catch (DuplicateUserDataException e) {
             //ok
         }
     }

     private void test_user_get() throws Exception {
         User user1 = users.getUserByLogin(login1);
         assertEquals(NEW, user1.getStatus());
         assertEquals(login1, user1.login);
         assertEquals(mail1, user1.email);

         User user2 = users.getUserByLogin(login1);
         assertEquals(NEW, user2.getStatus());
         assertEquals(login1, user2.login);
         assertEquals(mail1, user2.email);
     }

     private void test_user_create() throws Exception {

         int firstId = 104;

         mailSender.tasks.clear();

         userId1 = users.createUser(new User(login1, mail1), psw1);
         userId2 = users.createUser(new User(login2, mail2), psw2);
         userId3 = users.createUser(new User(login3, mail3), psw3);
         userId4 = users.createUser(new User(login4, mail4), psw4);
         assertEquals(firstId, userId1);
         assertEquals(firstId + 1, userId2);

         //mail sended
         assertEquals(4, mailSender.tasks.size());

     }

     private void createUserWithInvalidData(String login, String mail, String psw, UserService users)
             throws Exception {
         try {
             users.createUser(new User(login, mail), psw);
             fail_exception_expected();
         } catch (ValidationException e) {
             //ok
         }
     }

     private void resetWeb() {
         asyncFutures.clear();
         req.clearCookies();
         req.clearSessionAttrs();
         resp.clearCookies();
     }

     private void assertEmptyRemCookie() {

         boolean foundEmptyRemCookie = false;

         for (Cookie cookie : resp.cookies) {
             if (cookie.getName().equals(REM_TOKEN)) {
                 assertEquals("", cookie.getValue());
                 assertEquals(0, cookie.getMaxAge());
                 foundEmptyRemCookie = true;
                 break;
             }
         }

         assertTrue(String.valueOf(resp.cookies), foundEmptyRemCookie);
     }

     private void checkNotActiveUser(UserService users, String login, String email, String psw) throws Exception {
         try {
             users.checkEmailOrLoginAndPsw(email, psw);
             fail_exception_expected();
         } catch (NotActivatedUserException e) {
             //ok
         }

         try {
             users.checkEmailOrLoginAndPsw(login, psw);
             fail_exception_expected();
         } catch (NotActivatedUserException e) {
             //ok
         }
     }

     private void test_db_getServers() throws Exception {

         long serverId = -1;
         //init
         {
             List<ServerRow> servers = universal.select(new GetAllServers());
             assertEquals(2, servers.size());
             assertFalse(servers.get(0).isFull);
             assertFalse(servers.get(1).isFull);

             serverId = servers.get(1).id;
         }

         //update
         {
             universal.updateOne(new UpdateServerById(serverId, new IsFull(true)));
             List<ServerRow> servers = universal.select(new GetAllServers());
             assertFalse(servers.get(0).isFull);
             assertTrue(servers.get(1).isFull);
         }

         //revert to init
         {
             universal.updateOne(new UpdateServerById(serverId, new IsFull(false)));
             List<ServerRow> servers = universal.select(new GetAllServers());
             assertFalse(servers.get(0).isFull);
             assertFalse(servers.get(1).isFull);
         }

     }

     private void test_init_db() throws Exception {

         //init db check
         {
             List<ServerRow> servers = universal.select(new GetAllServers());
             assertEquals(2, servers.size());
             assertEquals(server1HttpUrl, servers.get(0).httpUrl);
             assertEquals(server1HttpsUrl, servers.get(0).httpsUrl);
             assertEquals(server2HttpUrl, servers.get(1).httpUrl);
             assertEquals(server2HttpsUrl, servers.get(1).httpsUrl);

             List<ChatAccount> chatsList = universal.select(new GetAllChatAccounts());
             assertEquals(2, chatsList.size());
             assertEquals("demo", chatsList.get(0).uid);
             assertEquals(1, chatsList.get(0).serverId);
             assertNotNull(chatsList.get(0).tariffStart);
             assertEquals(0, chatsList.get(0).tariffChangedInDay);

             assertEquals("demo2", chatsList.get(1).uid);
             assertEquals(1, chatsList.get(1).serverId);

             //operators
             assertEquals(set(CHAT_OWNER, CHAT_OPERATOR), chats.getAccPrivilegesForUser(chatsList.get(0).id, 100L));
             assertEquals(set(CHAT_OPERATOR), chats.getAccPrivilegesForUser(chatsList.get(0).id, 101L));
         }

         //check model
         {
             ServerRow server = chats.getServerByAcc("demo");
             assertNotNull(server);
             assertEquals(server1HttpUrl, server.httpUrl);
             assertEquals(server1HttpsUrl, server.httpsUrl);
         }
     }

 }