// 
// 
// Copyright (C) 2006 SIPez LLC.
// Licensed to SIPfoundry under a Contributor Agreement.
// 
// Copyright (C) 2006 SIPfoundry Inc.
// License by SIPfoundry under the LGPL license.
// 
// Copyright (C) 2006 Pingtel Corp.
// Licensed to SIPfoundry under a Contributor Agreement.
// 
// $$
//////////////////////////////////////////////////////////////////////////////

// SYSTEM INCLUDES

// APPLICATION INCLUDES
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <sipxunit/TestUtilities.h>

#include "utl/UtlSList.h"
#include "utl/UtlSListIterator.h"
#include "net/Url.h"
#include "os/OsDateTime.h"

#include "sipdb/RegistrationDB.h"
#include "sipdb/RegistrationBinding.h"
#include "sipdb/ResultSet.h"
#include "testlib/RegistrationDbTestContext.h"
//#include "sipdb/SIPDBManager.h"

// DEFINES
// CONSTANTS
// TYPEDEFS
// FORWARD DECLARATIONS

class RegistryDbTest : public CppUnit::TestCase
{
   CPPUNIT_TEST_SUITE(RegistryDbTest);
   CPPUNIT_TEST(testIsOutOfSequence);
   CPPUNIT_TEST(testGetMaxUpdateNumberForRegistrar);
   CPPUNIT_TEST(testGetNextUpdateNumberForRegistrar);
   CPPUNIT_TEST(testGetNextUpdateForRegistrar);
   CPPUNIT_TEST(testGetNewUpdatesForRegistrar);  
   CPPUNIT_TEST(testGetUnexpiredContacts);
   CPPUNIT_TEST(testUpdateExistingBinding);
   CPPUNIT_TEST(testUpdateNewBinding);
   CPPUNIT_TEST(testUpdateSameBinding);
   CPPUNIT_TEST(testUpdateExistingUriNewContact);  
   CPPUNIT_TEST(testExpireAllBindings); 
   CPPUNIT_TEST(testExpireOldBindings); 
   //CPPUNIT_TEST(testCleanAndPersist);   
   CPPUNIT_TEST_SUITE_END();

public:

   void tearDown()
      {
         RegistrationDB::getInstance()->releaseInstance();
      }

   void testGetMaxUpdateNumberForRegistrar()
      {
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("getMaxUpdate.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();
         
         INT64 seqOneMax = regDb->getMaxUpdateNumberForRegistrar("seqOne");
         CPPUNIT_ASSERT_EQUAL(5LL, seqOneMax);
         
         // Get maximum number for the other registrar
         INT64 seqTwoMax = regDb->getMaxUpdateNumberForRegistrar("seqTwo");
         CPPUNIT_ASSERT_EQUAL(7LL, seqTwoMax);         
        
         // Pass in unknown registrar
         INT64 seqThreeMax = regDb->getMaxUpdateNumberForRegistrar("seqThree");
         CPPUNIT_ASSERT_EQUAL(0LL, seqThreeMax);         
      }

   void testGetNextUpdateNumberForRegistrar()
      {
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("getMaxUpdate.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();
         
         INT64 seqOneNext = regDb->getNextUpdateNumberForRegistrar("seqOne", 0LL);
         CPPUNIT_ASSERT_EQUAL(1LL, seqOneNext);

         seqOneNext = regDb->getNextUpdateNumberForRegistrar("seqOne", 1LL);
         CPPUNIT_ASSERT_EQUAL(2LL, seqOneNext);
         
         seqOneNext = regDb->getNextUpdateNumberForRegistrar("seqOne", 2LL);
         CPPUNIT_ASSERT_EQUAL(3LL, seqOneNext);
         
         seqOneNext = regDb->getNextUpdateNumberForRegistrar("seqOne", 3LL);
         CPPUNIT_ASSERT_EQUAL(5LL, seqOneNext);
         
         seqOneNext = regDb->getNextUpdateNumberForRegistrar("seqOne", 4LL);
         CPPUNIT_ASSERT_EQUAL(5LL, seqOneNext);
         
         seqOneNext = regDb->getNextUpdateNumberForRegistrar("seqOne", 5LL);
         CPPUNIT_ASSERT_EQUAL(0LL, seqOneNext);
         
         INT64 seqTwoNext = regDb->getNextUpdateNumberForRegistrar("seqTwo", 4);
         CPPUNIT_ASSERT_EQUAL(7LL, seqTwoNext);
        
         // Pass in unknown registrar
         INT64 seqThreeNext = regDb->getNextUpdateNumberForRegistrar("seqThree", 1);
         CPPUNIT_ASSERT_EQUAL(0LL, seqThreeNext);                  
      }
      
   void testGetNextUpdateForRegistrar()
      {
         UtlSList bindings;
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("getMaxUpdate.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();
         
         INT64 seqOneUpdates = regDb->getNextUpdateForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(1LL, seqOneUpdates);
         
         UtlSListIterator iterator(bindings);

         // Loop through all returned bindings and mark the ones we've seen.
         // Also check correctness of bindings
         RegistrationBinding *binding;
         while ((binding = (RegistrationBinding*)iterator()))
         {
             CPPUNIT_ASSERT_EQUAL(binding->getCallId()->compareTo("ID3"), 0);
             CPPUNIT_ASSERT_EQUAL(binding->getContact()->compareTo("sip:300@10.1.1.20"), 0);
             CPPUNIT_ASSERT_EQUAL(binding->getUri()->toString().compareTo("sip:300@testdomain.example.com"), 0);
             CPPUNIT_ASSERT_EQUAL(300, binding->getCseq());
         }

         bindings.destroyAll();
         // Pass in high update number value, expect nothing to be returned
         seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 8, bindings);
         CPPUNIT_ASSERT_EQUAL(0LL, seqOneUpdates);

         bindings.destroyAll();         
         // Test the other registrar, expect one binding
         INT64 seqTwoUpdates = regDb->getNewUpdatesForRegistrar("seqTwo", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(1LL, seqTwoUpdates);         
         
         binding = (RegistrationBinding*)bindings.first();
         CPPUNIT_ASSERT_EQUAL(binding->getContact()->compareTo("sip:800@10.1.1.20"), 0);    
         CPPUNIT_ASSERT_EQUAL(binding->getUri()->toString().compareTo("sip:800@testdomain.example.com"), 0);                            
         CPPUNIT_ASSERT_EQUAL(800, binding->getCseq());       
      } 
      
   void testGetNewUpdatesForRegistrar()
      {
         UtlSList bindings;
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("getMaxUpdate.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();
         
         INT64 seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(2LL, seqOneUpdates);
         
         UtlSListIterator iterator(bindings);

         // Loop through all returned bindings and mark the ones we've seen.
         // Also check correctness of bindings
         UtlBoolean bSeenId3 = FALSE;         
         UtlBoolean bSeenId5 = FALSE;
         UtlBoolean bSeenAnythingElse = FALSE;
         RegistrationBinding *binding;
         
         while ((binding = (RegistrationBinding*)iterator()))
         {
             const UtlString *s = binding->getCallId();      
             if (s->compareTo("ID3") == 0)
             {
                 bSeenId3 = TRUE;
                 CPPUNIT_ASSERT_EQUAL(binding->getContact()->compareTo("sip:300@10.1.1.20"), 0);
                 CPPUNIT_ASSERT_EQUAL(binding->getUri()->toString().compareTo("sip:300@testdomain.example.com"), 0);
                 CPPUNIT_ASSERT_EQUAL(300, binding->getCseq());
             }
             else if (s->compareTo("ID5") == 0)
             {
                 bSeenId5 = TRUE;
                 CPPUNIT_ASSERT_EQUAL(binding->getContact()->compareTo("sip:500@10.1.1.20"), 0);    
                 CPPUNIT_ASSERT_EQUAL(binding->getUri()->toString().compareTo("sip:500@testdomain.example.com"), 0);                            
                 CPPUNIT_ASSERT_EQUAL(500, binding->getCseq());
             }
             else
             {
                 bSeenAnythingElse = TRUE;
             }
         }
         CPPUNIT_ASSERT(!bSeenAnythingElse);         
         CPPUNIT_ASSERT(bSeenId3);
         CPPUNIT_ASSERT(bSeenId5);
         
         bindings.destroyAll();
         // Pass in high update number value, expect nothing to be returned
         seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 8, bindings);
         CPPUNIT_ASSERT_EQUAL(0LL, seqOneUpdates);

         bindings.destroyAll();         
         // Test the other registrar, expect one binding
         INT64 seqTwoUpdates = regDb->getNewUpdatesForRegistrar("seqTwo", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(1LL, seqTwoUpdates);         
         
         binding = (RegistrationBinding*)bindings.first();
         CPPUNIT_ASSERT_EQUAL(binding->getContact()->compareTo("sip:800@10.1.1.20"), 0);    
         CPPUNIT_ASSERT_EQUAL(binding->getUri()->toString().compareTo("sip:800@testdomain.example.com"), 0);                            
         CPPUNIT_ASSERT_EQUAL(800, binding->getCseq());       
      } 
      
   void testGetUnexpiredContacts()
      {
         Url uri;
         ResultSet results;
         UtlHashMap record1;
         UtlHashMap record2;
         int timeNow = OsDateTime::getSecsSinceEpoch();
         
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("getUnexpiredContacts.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();  
         
         uri.setUserId("900");
         uri.setHostAddress("testdomain.example.com");

      
         regDb->getUnexpiredContacts(uri, timeNow, results);
         int numResults = results.getSize();            
         CPPUNIT_ASSERT_EQUAL(2, numResults);         
         

         if (numResults == 2)
         {
             // Get first record
             results.getIndex(0, record1);
             UtlString* value = (UtlString*)record1.findValue(&RegistrationDB::gContactKey);
             CPPUNIT_ASSERT_EQUAL(value->compareTo("sip:900@10.1.1.20"), 0);
  
             value = (UtlString*)record1.findValue(&RegistrationDB::gUriKey);
             CPPUNIT_ASSERT_EQUAL(value->compareTo("sip:900@testdomain.example.com"), 0);
 
             // Get second record
             results.getIndex(1, record2);
             value = (UtlString*)record2.findValue(&RegistrationDB::gContactKey);
             CPPUNIT_ASSERT_EQUAL(value->compareTo("sip:900@10.1.1.21"), 0);
             
             value = (UtlString*)record2.findValue(&RegistrationDB::gUriKey);
             CPPUNIT_ASSERT_EQUAL(value->compareTo("sip:900@testdomain.example.com"), 0);
         }
      }           
      
   void testIsOutOfSequence()
      {
         Url uri;
         UtlString callId;
        
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("getMaxUpdate.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();  
         
         uri.setUserId("200");
         uri.setHostAddress("testdomain.example.com");
         callId = "ID2";
         
         UtlBoolean bOutOfSequence = regDb->isOutOfSequence(uri, callId, 900);
         CPPUNIT_ASSERT(!bOutOfSequence);
         
         bOutOfSequence = regDb->isOutOfSequence(uri, callId, 200);
         CPPUNIT_ASSERT(bOutOfSequence);         

         bOutOfSequence = regDb->isOutOfSequence(uri, callId, 0);
         CPPUNIT_ASSERT(bOutOfSequence);                         
      }
      
   void testUpdateExistingBinding()
      {
         UtlSList bindings;         
         RegistrationBinding* binding;
         
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("updateBindings.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();
         
         // Get an existing binding  
         INT64 seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(1LL, seqOneUpdates);
         
         binding = (RegistrationBinding*)bindings.first();
         
         // Increment the CSeq number
         int newCseq = binding->getCseq() + 1;
         binding->setCseq(newCseq);
         regDb->updateBinding(*binding);
         binding->setCseq(0);
         
         // Get the same binding
         bindings.destroyAll();
         seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(1LL, seqOneUpdates);
         
         // Test if the new CSeq number got updated
         binding = (RegistrationBinding*)bindings.first();
         CPPUNIT_ASSERT_EQUAL(newCseq, binding->getCseq());
      }
      
   void testUpdateNewBinding()
      {
         Url uri;
         UtlString contact;
         UtlSList bindings;         
         RegistrationBinding* binding;
         RegistrationBinding newBinding;
         
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("updateBindings.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();
         
         // Make sure an existing binding doesn't exist
         INT64 seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 3, bindings);
         CPPUNIT_ASSERT_EQUAL(0LL, seqOneUpdates);
         
         // Create new binding with update number 3
         uri.setUserId("300");
         uri.setHostAddress("testdomain.example.com");
         newBinding.setUri(uri);
         
         newBinding.setContact("sip:300@10.1.1.20");
         newBinding.setCallId("ID3");
         newBinding.setPrimary("seqOne");
         newBinding.setUpdateNumber(3);
         
         // Add new binding
         regDb->updateBinding(newBinding);
         
         bindings.destroyAll();
         
         // We should now have two bindings - the original with update number 2 and the new one
         seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(2LL, seqOneUpdates);
         
         // Test if the registration data corresponds to what we expect
         UtlBoolean bSeenId2 = FALSE;         
         UtlBoolean bSeenId3 = FALSE;
         UtlBoolean bSeenAnythingElse = FALSE;
         UtlSListIterator iterator(bindings);         
         
         while ((binding = (RegistrationBinding*)iterator()))
         {
             const UtlString *s = binding->getCallId();      
             if (s->compareTo("ID2") == 0)
             {
                 bSeenId2 = TRUE;
                 CPPUNIT_ASSERT_EQUAL(binding->getContact()->compareTo("sip:200@10.1.1.20"), 0);
                 CPPUNIT_ASSERT_EQUAL(binding->getUri()->toString().compareTo("sip:200@testdomain.example.com"), 0);
             }
             else if (s->compareTo("ID3") == 0)
             {
                 bSeenId3 = TRUE;
                 CPPUNIT_ASSERT_EQUAL(binding->getContact()->compareTo("sip:300@10.1.1.20"), 0);
                 CPPUNIT_ASSERT_EQUAL(binding->getUri()->toString().compareTo("sip:300@testdomain.example.com"), 0);
                 CPPUNIT_ASSERT_EQUAL(binding->getUpdateNumber(), 3LL);
             }
             else
             {
                 bSeenAnythingElse = TRUE;
             }
         }
         CPPUNIT_ASSERT(!bSeenAnythingElse);
         CPPUNIT_ASSERT(bSeenId2);
         CPPUNIT_ASSERT(bSeenId3);
      }
      
   void testUpdateSameBinding()
      {
         UtlSList bindings;         
         RegistrationBinding* binding;
         
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("updateBindings.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();
         
         // Get an existing binding  
         INT64 seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(1LL, seqOneUpdates);
         
         binding = (RegistrationBinding*)bindings.first();
         
         // Call updateBinding with the same binding
         regDb->updateBinding(*binding);
         
         // Get it back and make sure we still only have one
         bindings.destroyAll();
         seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(1LL, seqOneUpdates);
      }
      
   void testUpdateExistingUriNewContact()
      {
         UtlSList bindings;
         RegistrationBinding* binding;
         
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("updateBindings.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();
         
         // Get an existing binding  
         INT64 seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(1LL, seqOneUpdates);
         
         binding = (RegistrationBinding*)bindings.first();
         
         // Change the contact address
         const UtlString* oldContact = binding->getContact();
         UtlString newContact(*oldContact);
         newContact.replace('2', '3');
         binding->setContact(newContact);
         regDb->updateBinding(*binding);
         
         // We should have 2 bindings now
         bindings.destroyAll();
         seqOneUpdates = regDb->getNewUpdatesForRegistrar("seqOne", 2, bindings);
         CPPUNIT_ASSERT_EQUAL(2LL, seqOneUpdates);
      }
      
   void testExpireAllBindings()
      {
         Url uri;
         UtlString contact;
         UtlSList bindings; 
         ResultSet results;         
         int timeNow = OsDateTime::getSecsSinceEpoch();                        
        
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("getUnexpiredContacts.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();  
         
         uri.setUserId("900");
         uri.setHostAddress("testdomain.example.com");
      
         regDb->getUnexpiredContacts(uri, timeNow, results);
         int numResults = results.getSize();            
         CPPUNIT_ASSERT_EQUAL(2, numResults);
         
         results.destroyAll();
         
         regDb->expireAllBindings(uri, "ID9", 900, timeNow, "seqOne", 7);
         
         regDb->getUnexpiredContacts(uri, timeNow, results);
         numResults = results.getSize();            
         CPPUNIT_ASSERT_EQUAL(0, numResults);         
      }                         
      
   void testExpireOldBindings()
      {
         Url uri;
         //UtlString contact;
         //UtlSList bindings; 
         ResultSet results;         
         int timeNow = OsDateTime::getSecsSinceEpoch();                        
        
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("getUnexpiredContacts.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();  
         
         uri.setUserId("900");
         uri.setHostAddress("testdomain.example.com");
      
         regDb->getUnexpiredContacts(uri, timeNow, results);
         int numResults = results.getSize();            
         CPPUNIT_ASSERT_EQUAL(2, numResults);
         
         results.destroyAll();
         
         regDb->expireOldBindings(uri, "ID9", 900, timeNow, "seqOne", 7);
         
         regDb->getUnexpiredContacts(uri, timeNow, results);
         numResults = results.getSize();            
         CPPUNIT_ASSERT_EQUAL(1, numResults);         
      }  
      
   void testCleanAndPersist()
      {
         int timeNow = OsDateTime::getSecsSinceEpoch();                        
        
         RegistrationDbTestContext testDbContext(TEST_DATA_DIR "/regdbdata",
                                                 TEST_WORK_DIR "/regdbdata"
                                                 );

         testDbContext.inputFile("cleanAndPersist.xml");

         RegistrationDB* regDb = RegistrationDB::getInstance();  
         
         regDb->cleanAndPersist(timeNow - 50);

      }                                                                  
};

CPPUNIT_TEST_SUITE_REGISTRATION(RegistryDbTest);



syntax highlighted by Code2HTML, v. 0.9.1