LCOV - code coverage report
Current view: top level - test/unitTest/syncHistory - syncHistory.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 591 622 95.0 %
Date: 2024-12-21 08:56:24 Functions: 92 94 97.9 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "fileutils.h"
      19             : #include "manager.h"
      20             : #include "jamidht/jamiaccount.h"
      21             : #include "../../test_runner.h"
      22             : #include "jami.h"
      23             : #include "account_const.h"
      24             : #include "common.h"
      25             : 
      26             : #include <dhtnet/connectionmanager.h>
      27             : #include <dhtnet/multiplexed_socket.h>
      28             : 
      29             : #include <cppunit/TestAssert.h>
      30             : #include <cppunit/TestFixture.h>
      31             : #include <cppunit/extensions/HelperMacros.h>
      32             : 
      33             : #include <chrono>
      34             : #include <condition_variable>
      35             : #include <filesystem>
      36             : 
      37             : using namespace libjami::Account;
      38             : using namespace std::literals::chrono_literals;
      39             : 
      40             : namespace jami {
      41             : namespace test {
      42             : 
      43             : struct UserData {
      44             :     std::string conversationId;
      45             :     bool removed {false};
      46             :     bool requestReceived {false};
      47             :     bool requestRemoved {false};
      48             :     bool errorDetected {false};
      49             :     bool registered {false};
      50             :     bool stopped {false};
      51             :     bool deviceAnnounced {false};
      52             :     bool sending {false};
      53             :     bool sent {false};
      54             :     std::string profilePath;
      55             :     std::string payloadTrustRequest;
      56             :     std::vector<libjami::SwarmMessage> messages;
      57             :     std::vector<libjami::SwarmMessage> messagesLoaded;
      58             :     std::vector<libjami::SwarmMessage> messagesUpdated;
      59             :     std::map<std::string, int> members;
      60             : };
      61             : 
      62             : class SyncHistoryTest : public CppUnit::TestFixture
      63             : {
      64             : public:
      65          14 :     SyncHistoryTest()
      66          14 :     {
      67             :         // Init daemon
      68          14 :         libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
      69          14 :         if (not Manager::instance().initialized)
      70           1 :             CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
      71          14 :     }
      72          28 :     ~SyncHistoryTest() { libjami::fini(); }
      73           2 :     static std::string name() { return "SyncHistory"; }
      74             :     void setUp();
      75             :     void tearDown();
      76             : 
      77             :     std::string aliceId;
      78             :     UserData aliceData;
      79             :     std::string bobId;
      80             :     UserData bobData;
      81             :     std::string alice2Id;
      82             :     UserData alice2Data;
      83             : 
      84             :     std::mutex mtx;
      85             :     std::unique_lock<std::mutex> lk {mtx};
      86             :     std::condition_variable cv;
      87             :     void connectSignals();
      88             : 
      89             : private:
      90             :     void testCreateConversationThenSync();
      91             :     void testCreateConversationWithOnlineDevice();
      92             :     void testCreateConversationWithMessagesThenAddDevice();
      93             :     void testCreateMultipleConversationThenAddDevice();
      94             :     void testReceivesInviteThenAddDevice();
      95             :     void testRemoveConversationOnAllDevices();
      96             :     void testSyncCreateAccountExportDeleteReimportOldBackup();
      97             :     void testSyncCreateAccountExportDeleteReimportWithConvId();
      98             :     void testSyncCreateAccountExportDeleteReimportWithConvReq();
      99             :     void testSyncOneToOne();
     100             :     void testConversationRequestRemoved();
     101             :     void testProfileReceivedMultiDevice();
     102             :     void testLastInteractionAfterClone();
     103             :     void testLastInteractionAfterSomeMessages();
     104             : 
     105           2 :     CPPUNIT_TEST_SUITE(SyncHistoryTest);
     106           1 :     CPPUNIT_TEST(testCreateConversationThenSync);
     107           1 :     CPPUNIT_TEST(testCreateConversationWithOnlineDevice);
     108           1 :     CPPUNIT_TEST(testCreateConversationWithMessagesThenAddDevice);
     109           1 :     CPPUNIT_TEST(testCreateMultipleConversationThenAddDevice);
     110           1 :     CPPUNIT_TEST(testReceivesInviteThenAddDevice);
     111           1 :     CPPUNIT_TEST(testRemoveConversationOnAllDevices);
     112           1 :     CPPUNIT_TEST(testSyncCreateAccountExportDeleteReimportOldBackup);
     113           1 :     CPPUNIT_TEST(testSyncCreateAccountExportDeleteReimportWithConvId);
     114           1 :     CPPUNIT_TEST(testSyncCreateAccountExportDeleteReimportWithConvReq);
     115           1 :     CPPUNIT_TEST(testSyncOneToOne);
     116           1 :     CPPUNIT_TEST(testConversationRequestRemoved);
     117           1 :     CPPUNIT_TEST(testProfileReceivedMultiDevice);
     118           1 :     CPPUNIT_TEST(testLastInteractionAfterClone);
     119           1 :     CPPUNIT_TEST(testLastInteractionAfterSomeMessages);
     120           4 :     CPPUNIT_TEST_SUITE_END();
     121             : };
     122             : 
     123             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SyncHistoryTest, SyncHistoryTest::name());
     124             : 
     125             : void
     126          14 : SyncHistoryTest::setUp()
     127             : {
     128          28 :     auto actors = load_actors_and_wait_for_announcement("actors/alice-bob.yml");
     129          14 :     aliceId = actors["alice"];
     130          14 :     bobId = actors["bob"];
     131          14 :     alice2Id = "";
     132          14 :     aliceData = {};
     133          14 :     bobData = {};
     134          14 :     alice2Data = {};
     135          14 : }
     136             : 
     137             : void
     138          14 : SyncHistoryTest::tearDown()
     139             : {
     140          28 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     141          14 :     std::remove(aliceArchive.c_str());
     142          14 :     if (alice2Id.empty()) {
     143           0 :         wait_for_removal_of({aliceId, bobId});
     144             :     } else {
     145          56 :         wait_for_removal_of({aliceId, bobId, alice2Id});
     146             :     }
     147          14 : }
     148             : 
     149             : void
     150          13 : SyncHistoryTest::connectSignals()
     151             : {
     152          13 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     153          13 :     confHandlers.insert(
     154          26 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
     155          60 :             [&](const std::string& accountId, const std::map<std::string, std::string>&) {
     156          60 :                 if (accountId == aliceId) {
     157           3 :                     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     158           3 :                     auto details = aliceAccount->getVolatileAccountDetails();
     159           6 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     160           3 :                     if (daemonStatus == "REGISTERED") {
     161           0 :                         aliceData.registered = true;
     162           3 :                     } else if (daemonStatus == "UNREGISTERED") {
     163           3 :                         aliceData.stopped = true;
     164             :                     }
     165           6 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     166           3 :                     aliceData.deviceAnnounced = deviceAnnounced == "true";
     167          60 :                 } else if (accountId == bobId) {
     168           0 :                     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     169           0 :                     auto details = bobAccount->getVolatileAccountDetails();
     170           0 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     171           0 :                     if (daemonStatus == "REGISTERED") {
     172           0 :                         bobData.registered = true;
     173           0 :                     } else if (daemonStatus == "UNREGISTERED") {
     174           0 :                         bobData.stopped = true;
     175             :                     }
     176           0 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     177           0 :                     bobData.deviceAnnounced = deviceAnnounced == "true";
     178          57 :                 } else if (accountId == alice2Id) {
     179          44 :                     auto alice2Account = Manager::instance().getAccount<JamiAccount>(alice2Id);
     180          44 :                     auto details = alice2Account->getVolatileAccountDetails();
     181          88 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     182          44 :                     if (daemonStatus == "REGISTERED") {
     183          18 :                         alice2Data.registered = true;
     184          26 :                     } else if (daemonStatus == "UNREGISTERED") {
     185          13 :                         alice2Data.stopped = true;
     186             :                     }
     187          88 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     188          44 :                     alice2Data.deviceAnnounced = deviceAnnounced == "true";
     189          44 :                 }
     190          60 :                 cv.notify_one();
     191          60 :             }));
     192          13 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     193          29 :         [&](const std::string& accountId, const std::string& conversationId) {
     194          29 :             if (accountId == aliceId) {
     195          10 :                 aliceData.conversationId = conversationId;
     196          19 :             } else if (accountId == bobId) {
     197           8 :                 bobData.conversationId = conversationId;
     198          11 :             } else if (accountId == alice2Id) {
     199          11 :                 alice2Data.conversationId = conversationId;
     200             :             }
     201          29 :             cv.notify_one();
     202          29 :         }));
     203          13 :     confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ProfileReceived>(
     204           9 :         [&](const std::string& accountId, const std::string& peerId, const std::string& path) {
     205           9 :             if (accountId == bobId)
     206           5 :                 bobData.profilePath = path;
     207           4 :             else if (accountId == aliceId)
     208           1 :                 aliceData.profilePath = path;
     209           3 :             else if (accountId == alice2Id)
     210           3 :                 alice2Data.profilePath = path;
     211           9 :             cv.notify_one();
     212           9 :         }));
     213          13 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationMemberEvent>(
     214          12 :         [&](const std::string& accountId, const std::string& conversationId, const auto& member, auto status) {
     215          12 :             if (accountId == aliceId) {
     216           7 :                 aliceData.members[member] = status;
     217           5 :             } else if (accountId == bobId) {
     218           4 :                 bobData.members[member] = status;
     219           1 :             } else if (accountId == alice2Id) {
     220           1 :                 alice2Data.members[member] = status;
     221             :             }
     222          12 :             cv.notify_one();
     223          12 :         }));
     224          13 :     confHandlers.insert(
     225          26 :         libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>(
     226          86 :             [&](const std::string& accountId,
     227             :                 const std::string& /*conversationId*/,
     228             :                 const std::string& /*peer*/,
     229             :                 const std::string& /*msgId*/,
     230             :                 int status) {
     231          86 :                 if (accountId == aliceId) {
     232          36 :                     if (status == 2)
     233          11 :                         aliceData.sending = true;
     234          36 :                     if (status == 3)
     235          10 :                         aliceData.sent = true;
     236          50 :                 } else if (accountId == alice2Id) {
     237           7 :                     if (status == 2)
     238           4 :                         alice2Data.sending = true;
     239           7 :                     if (status == 3)
     240           1 :                         alice2Data.sent = true;
     241          43 :                 } else if (accountId == bobId) {
     242          43 :                     if (status == 2)
     243          17 :                         bobData.sending = true;
     244          43 :                     if (status == 3)
     245           7 :                         bobData.sent = true;
     246             :                 }
     247          86 :                 cv.notify_one();
     248          86 :             }));
     249          13 :     confHandlers.insert(
     250          26 :         libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>(
     251           3 :             [&](const std::string& account_id,
     252             :                 const std::string& /*from*/,
     253             :                 const std::string& /*conversationId*/,
     254             :                 const std::vector<uint8_t>& payload,
     255             :                 time_t /*received*/) {
     256           3 :                 auto payloadStr = std::string(payload.data(), payload.data() + payload.size());
     257           3 :                 if (account_id == aliceId)
     258           0 :                     aliceData.payloadTrustRequest = payloadStr;
     259           3 :                 else if (account_id == bobId)
     260           3 :                     bobData.payloadTrustRequest = payloadStr;
     261           3 :                 cv.notify_one();
     262           3 :             }));
     263          13 :     confHandlers.insert(
     264          26 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>(
     265          10 :             [&](const std::string& accountId,
     266             :                 const std::string& /* conversationId */,
     267             :                 std::map<std::string, std::string> /*metadatas*/) {
     268          10 :                 if (accountId == aliceId) {
     269           3 :                     aliceData.requestReceived = true;
     270           7 :                 } else if (accountId == bobId) {
     271           5 :                     bobData.requestReceived = true;
     272           2 :                 } else if (accountId == alice2Id) {
     273           2 :                     alice2Data.requestReceived = true;
     274             :                 }
     275          10 :                 cv.notify_one();
     276          10 :             }));
     277          13 :     confHandlers.insert(
     278          26 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>(
     279           2 :             [&](const std::string& accountId, const std::string&) {
     280           2 :                 if (accountId == bobId) {
     281           0 :                     bobData.requestRemoved = true;
     282           2 :                 } else if (accountId == aliceId) {
     283           1 :                     aliceData.requestRemoved = true;
     284           1 :                 } else if (accountId == alice2Id) {
     285           1 :                     alice2Data.requestRemoved = true;
     286             :                 }
     287           2 :                 cv.notify_one();
     288           2 :             }));
     289          13 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>(
     290          35 :         [&](const std::string& accountId,
     291             :             const std::string& /* conversationId */,
     292             :             libjami::SwarmMessage message) {
     293          35 :             if (accountId == aliceId) {
     294          16 :                 aliceData.messages.emplace_back(message);
     295          19 :             } else if (accountId == bobId) {
     296          13 :                 bobData.messages.emplace_back(message);
     297           6 :             } else if (accountId == alice2Id) {
     298           6 :                 alice2Data.messages.emplace_back(message);
     299             :             }
     300          35 :             cv.notify_one();
     301          35 :         }));
     302          13 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmLoaded>(
     303           1 :         [&](uint32_t, const std::string& accountId,
     304             :             const std::string& /* conversationId */,
     305             :             std::vector<libjami::SwarmMessage> messages) {
     306           1 :             if (accountId == aliceId) {
     307           0 :                 aliceData.messagesLoaded.insert(aliceData.messagesLoaded.end(), messages.begin(), messages.end());
     308           1 :             } else if (accountId == alice2Id) {
     309           1 :                 alice2Data.messagesLoaded.insert(alice2Data.messagesLoaded.end(), messages.begin(), messages.end());
     310           0 :             } else if (accountId == bobId) {
     311           0 :                 bobData.messagesLoaded.insert(bobData.messagesLoaded.end(), messages.begin(), messages.end());
     312             :             }
     313           1 :             cv.notify_one();
     314           1 :         }));
     315          13 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>(
     316           0 :         [&](const std::string& accountId,
     317             :             const std::string& /* conversationId */,
     318             :             libjami::SwarmMessage message) {
     319           0 :             if (accountId == aliceId) {
     320           0 :                 aliceData.messagesUpdated.emplace_back(message);
     321           0 :             } else if (accountId == bobId) {
     322           0 :                 bobData.messagesUpdated.emplace_back(message);
     323             :             }
     324           0 :             cv.notify_one();
     325           0 :         }));
     326          13 :     confHandlers.insert(
     327          26 :         libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>(
     328           0 :             [&](const std::string& accountId,
     329             :                 const std::string& /* conversationId */,
     330             :                 int /*code*/,
     331             :                 const std::string& /* what */) {
     332           0 :                 if (accountId == aliceId)
     333           0 :                     aliceData.errorDetected = true;
     334           0 :                 else if (accountId == bobId)
     335           0 :                     bobData.errorDetected = true;
     336           0 :                 cv.notify_one();
     337           0 :             }));
     338          13 :     confHandlers.insert(
     339          26 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>(
     340           2 :             [&](const std::string& accountId, const std::string&) {
     341           2 :                 if (accountId == aliceId)
     342           1 :                     aliceData.removed = true;
     343           1 :                 else if (accountId == bobId)
     344           0 :                     bobData.removed = true;
     345           1 :                 else if (accountId == alice2Id)
     346           1 :                     alice2Data.removed = true;
     347           2 :                 cv.notify_one();
     348           2 :             }));
     349          13 :     libjami::registerSignalHandlers(confHandlers);
     350          13 : }
     351             : 
     352             : void
     353           1 : SyncHistoryTest::testCreateConversationThenSync()
     354             : {
     355           1 :     connectSignals();
     356             : 
     357           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     358             :     // Start conversation
     359           1 :     auto convId = libjami::startConversation(aliceId);
     360             : 
     361             :     // Now create alice2
     362           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     363           1 :     aliceAccount->exportArchive(aliceArchive);
     364           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     365           1 :     details[ConfProperties::TYPE] = "RING";
     366           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     367           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     368           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     369           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     370           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     371           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     372             : 
     373           1 :     alice2Id = Manager::instance().addAccount(details);
     374           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); }));
     375           1 : }
     376             : 
     377             : void
     378           1 : SyncHistoryTest::testCreateConversationWithOnlineDevice()
     379             : {
     380           1 :     connectSignals();
     381           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     382             : 
     383             :     // Now create alice2
     384           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     385           1 :     aliceAccount->exportArchive(aliceArchive);
     386           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     387           1 :     details[ConfProperties::TYPE] = "RING";
     388           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     389           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     390           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     391           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     392           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     393           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     394           1 :     auto convId = libjami::startConversation(aliceId);
     395           1 :     alice2Id = Manager::instance().addAccount(details);
     396           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); }));
     397           1 : }
     398             : 
     399             : void
     400           1 : SyncHistoryTest::testCreateConversationWithMessagesThenAddDevice()
     401             : {
     402           1 :     connectSignals();
     403           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     404           1 :     auto convId = libjami::startConversation(aliceId);
     405             : 
     406             :     // Start conversation
     407           1 :     auto aliceMsgSize = aliceData.messages.size();
     408           1 :     libjami::sendMessage(aliceId, convId, std::string("Message 1"), "");
     409           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     410           1 :     libjami::sendMessage(aliceId, convId, std::string("Message 2"), "");
     411           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 2 == aliceData.messages.size(); }));
     412           1 :     libjami::sendMessage(aliceId, convId, std::string("Message 3"), "");
     413           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 3 == aliceData.messages.size(); }));
     414             : 
     415             :     // Now create alice2
     416           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     417           1 :     aliceAccount->exportArchive(aliceArchive);
     418           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     419           1 :     details[ConfProperties::TYPE] = "RING";
     420           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     421           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     422           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     423           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     424           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     425           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     426           1 :     alice2Id = Manager::instance().addAccount(details);
     427           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); }));
     428             : 
     429           1 :     libjami::loadConversation(alice2Id, convId, "", 0);
     430           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.messagesLoaded.size() == 4; }));
     431             : 
     432             :     // Check messages
     433           1 :     CPPUNIT_ASSERT(alice2Data.messagesLoaded[0].body["body"] == "Message 3");
     434           1 :     CPPUNIT_ASSERT(alice2Data.messagesLoaded[1].body["body"] == "Message 2");
     435           1 :     CPPUNIT_ASSERT(alice2Data.messagesLoaded[2].body["body"] == "Message 1");
     436           1 : }
     437             : 
     438             : void
     439           1 : SyncHistoryTest::testCreateMultipleConversationThenAddDevice()
     440             : {
     441           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     442             :     // Start conversation
     443           1 :     auto convId = libjami::startConversation(aliceId);
     444           1 :     libjami::sendMessage(aliceId, convId, std::string("Message 1"), "");
     445           1 :     libjami::sendMessage(aliceId, convId, std::string("Message 2"), "");
     446           1 :     libjami::sendMessage(aliceId, convId, std::string("Message 3"), "");
     447           1 :     std::this_thread::sleep_for(1s);
     448           1 :     auto convId2 = libjami::startConversation(aliceId);
     449           1 :     libjami::sendMessage(aliceId, convId2, std::string("Message 1"), "");
     450           1 :     libjami::sendMessage(aliceId, convId2, std::string("Message 2"), "");
     451           1 :     libjami::sendMessage(aliceId, convId2, std::string("Message 3"), "");
     452           1 :     std::this_thread::sleep_for(1s);
     453           1 :     auto convId3 = libjami::startConversation(aliceId);
     454           1 :     libjami::sendMessage(aliceId, convId3, std::string("Message 1"), "");
     455           1 :     libjami::sendMessage(aliceId, convId3, std::string("Message 2"), "");
     456           1 :     libjami::sendMessage(aliceId, convId3, std::string("Message 3"), "");
     457           1 :     std::this_thread::sleep_for(1s);
     458           1 :     auto convId4 = libjami::startConversation(aliceId);
     459           1 :     libjami::sendMessage(aliceId, convId4, std::string("Message 1"), "");
     460           1 :     libjami::sendMessage(aliceId, convId4, std::string("Message 2"), "");
     461           1 :     libjami::sendMessage(aliceId, convId4, std::string("Message 3"), "");
     462             : 
     463             :     // Now create alice2
     464           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     465           1 :     aliceAccount->exportArchive(aliceArchive);
     466           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     467           1 :     details[ConfProperties::TYPE] = "RING";
     468           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     469           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     470           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     471           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     472           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     473           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     474           1 :     alice2Id = Manager::instance().addAccount(details);
     475             : 
     476           1 :     std::mutex mtx;
     477           1 :     std::unique_lock lk {mtx};
     478           1 :     std::condition_variable cv;
     479           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     480           1 :     std::atomic_int conversationReady = 0;
     481           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     482           4 :         [&](const std::string& accountId, const std::string&) {
     483           4 :             if (accountId == alice2Id) {
     484           4 :                 conversationReady += 1;
     485           4 :                 cv.notify_one();
     486             :             }
     487           4 :         }));
     488           1 :     libjami::registerSignalHandlers(confHandlers);
     489           1 :     confHandlers.clear();
     490             : 
     491             :     // Check if conversation is ready
     492           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return conversationReady == 4; }));
     493           1 : }
     494             : 
     495             : void
     496           1 : SyncHistoryTest::testReceivesInviteThenAddDevice()
     497             : {
     498           1 :     connectSignals();
     499           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     500             : 
     501             :     // Export alice
     502           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     503           1 :     aliceAccount->exportArchive(aliceArchive);
     504           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     505           1 :     auto uri = aliceAccount->getUsername();
     506             : 
     507             :     // Start conversation for Alice
     508           1 :     auto convId = libjami::startConversation(bobId);
     509             : 
     510           1 :     libjami::addConversationMember(bobId, convId, uri);
     511          11 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.requestReceived; }));
     512             : 
     513             :     // Now create alice2
     514           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     515           1 :     details[ConfProperties::TYPE] = "RING";
     516           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     517           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     518           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     519           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     520           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     521           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     522           1 :     alice2Id = Manager::instance().addAccount(details);
     523             : 
     524           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.requestReceived; }));
     525           1 : }
     526             : 
     527             : void
     528           1 : SyncHistoryTest::testRemoveConversationOnAllDevices()
     529             : {
     530           1 :     connectSignals();
     531           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     532             : 
     533             :     // Now create alice2
     534           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     535           1 :     aliceAccount->exportArchive(aliceArchive);
     536           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     537           1 :     details[ConfProperties::TYPE] = "RING";
     538           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     539           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     540           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     541           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     542           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     543           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     544           1 :     auto convId = libjami::startConversation(aliceId);
     545             : 
     546           1 :     alice2Id = Manager::instance().addAccount(details);
     547           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return !alice2Data.conversationId.empty(); }));
     548           1 :     libjami::removeConversation(aliceId, aliceData.conversationId);
     549           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.removed; }));
     550           1 : }
     551             : 
     552             : void
     553           1 : SyncHistoryTest::testSyncCreateAccountExportDeleteReimportOldBackup()
     554             : {
     555           1 :     connectSignals();
     556           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     557           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     558           1 :     auto bobUri = bobAccount->getUsername();
     559             : 
     560             :     // Backup alice before start conversation, worst scenario for invites
     561           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     562           1 :     aliceAccount->exportArchive(aliceArchive);
     563             : 
     564             :     // Start conversation
     565           1 :     auto convId = libjami::startConversation(aliceId);
     566             : 
     567           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     568          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     569           1 :     auto aliceMsgSize = aliceData.messages.size();
     570           1 :     libjami::acceptConversationRequest(bobId, convId);
     571           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size() && !bobData.conversationId.empty(); }));
     572             : 
     573             :     // disable account (same as removed)
     574           1 :     Manager::instance().sendRegister(aliceId, false);
     575           1 :     std::this_thread::sleep_for(5s);
     576             : 
     577           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     578           1 :     details[ConfProperties::TYPE] = "RING";
     579           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     580           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     581           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     582           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     583           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     584           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     585           1 :     alice2Id = Manager::instance().addAccount(details);
     586           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.deviceAnnounced; }));
     587             : 
     588             :     // This will trigger a conversation request. Cause alice2 is unable to know of first conversation
     589           1 :     libjami::sendMessage(bobId, convId, std::string("hi"), "");
     590           8 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.requestReceived; }));
     591             : 
     592           1 :     libjami::acceptConversationRequest(alice2Id, convId);
     593           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); }));
     594             : 
     595           1 :     auto bobMsgSize = bobData.messages.size();
     596           1 :     libjami::sendMessage(alice2Id, convId, std::string("hi"), "");
     597           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
     598           1 : }
     599             : 
     600             : void
     601           1 : SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvId()
     602             : {
     603           1 :     connectSignals();
     604           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     605           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     606           1 :     auto aliceUri = aliceAccount->getUsername();
     607           1 :     auto bobUri = bobAccount->getUsername();
     608             : 
     609             :     // Start conversation
     610           1 :     auto convId = libjami::startConversation(aliceId);
     611             : 
     612           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     613          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     614             : 
     615           1 :     auto aliceMsgSize = aliceData.messages.size();
     616           1 :     libjami::acceptConversationRequest(bobId, convId);
     617           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     618             : 
     619             :     // We need to track presence to know when to sync
     620           1 :     bobAccount->trackBuddyPresence(aliceUri, true);
     621             : 
     622             :     // Backup alice after startConversation with member
     623           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     624           1 :     aliceAccount->exportArchive(aliceArchive);
     625             : 
     626             :     // disable account (same as removed)
     627           1 :     Manager::instance().sendRegister(aliceId, false);
     628           1 :     std::this_thread::sleep_for(5s);
     629             : 
     630           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     631           1 :     details[ConfProperties::TYPE] = "RING";
     632           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     633           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     634           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     635           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     636           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     637           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     638           1 :     alice2Id = Manager::instance().addAccount(details);
     639             :     // Should retrieve conversation, no need for action as the convInfos is in the archive
     640           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); }));
     641             : 
     642           1 :     auto bobMsgSize = bobData.messages.size();
     643           1 :     libjami::sendMessage(alice2Id, convId, std::string("hi"), "");
     644           5 :     cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); });
     645           1 : }
     646             : 
     647             : void
     648           1 : SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvReq()
     649             : {
     650           1 :     connectSignals();
     651           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     652           1 :     auto aliceUri = aliceAccount->getUsername();
     653             : 
     654             :     // Start conversation
     655           1 :     auto convId = libjami::startConversation(bobId);
     656             : 
     657           1 :     libjami::addConversationMember(bobId, convId, aliceUri);
     658          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.requestReceived; }));
     659             : 
     660             :     // Backup alice after startConversation with member
     661           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     662           1 :     aliceAccount->exportArchive(aliceArchive);
     663             : 
     664             :     // disable account (same as removed)
     665           1 :     Manager::instance().sendRegister(aliceId, false);
     666           1 :     std::this_thread::sleep_for(5s);
     667             : 
     668           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     669           1 :     details[ConfProperties::TYPE] = "RING";
     670           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     671           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     672           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     673           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     674           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     675           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     676           1 :     alice2Id = Manager::instance().addAccount(details);
     677           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.deviceAnnounced; }));
     678             : 
     679             :     // Should get the same request as before.
     680           1 :     auto bobMsgSize = bobData.messages.size();
     681           1 :     libjami::acceptConversationRequest(alice2Id, convId);
     682           5 :     CPPUNIT_ASSERT(
     683             :         cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
     684           1 : }
     685             : 
     686             : void
     687           1 : SyncHistoryTest::testSyncOneToOne()
     688             : {
     689           1 :     connectSignals();
     690           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     691           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     692             : 
     693           1 :     aliceAccount->addContact(bobAccount->getUsername());
     694             :     // Now create alice2
     695           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     696           1 :     aliceAccount->exportArchive(aliceArchive);
     697           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     698           1 :     details[ConfProperties::TYPE] = "RING";
     699           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     700           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     701           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     702           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     703           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     704           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     705             : 
     706           1 :     alice2Id = Manager::instance().addAccount(details);
     707           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); }));
     708           1 : }
     709             : 
     710             : void
     711           1 : SyncHistoryTest::testConversationRequestRemoved()
     712             : {
     713           1 :     connectSignals();
     714           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     715           1 :     auto uri = aliceAccount->getUsername();
     716             : 
     717             :     // Export alice
     718           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     719           1 :     aliceAccount->exportArchive(aliceArchive);
     720             : 
     721           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     722             : 
     723             :     // Start conversation for Alice
     724           1 :     auto convId = libjami::startConversation(bobId);
     725             : 
     726             :     // Check that alice receives the request
     727           1 :     libjami::addConversationMember(bobId, convId, uri);
     728          11 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.requestReceived; }));
     729             : 
     730             :     // Now create alice2
     731           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     732           1 :     details[ConfProperties::TYPE] = "RING";
     733           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     734           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     735           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     736           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     737           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     738           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     739           1 :     alice2Id = Manager::instance().addAccount(details);
     740             : 
     741           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.requestReceived; }));
     742             :     // Now decline trust request, this should trigger ConversationRequestDeclined both sides for Alice
     743           1 :     libjami::declineConversationRequest(aliceId, convId);
     744             : 
     745           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.requestRemoved && alice2Data.requestRemoved; }));
     746           1 : }
     747             : 
     748             : void
     749           1 : SyncHistoryTest::testProfileReceivedMultiDevice()
     750             : {
     751           1 :     connectSignals();
     752           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     753           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     754           1 :     auto aliceUri = aliceAccount->getUsername();
     755           1 :     auto bobUri = bobAccount->getUsername();
     756             : 
     757             :     // Export alice
     758           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     759           1 :     aliceAccount->exportArchive(aliceArchive);
     760             : 
     761             :     // Set VCards
     762           1 :     std::string vcard = "BEGIN:VCARD\n\
     763             : VERSION:2.1\n\
     764             : FN:TITLE\n\
     765             : DESCRIPTION:DESC\n\
     766             : END:VCARD";
     767           2 :     auto alicePath = fileutils::get_data_dir() / aliceId
     768           2 :                      / "profile.vcf";
     769           2 :     auto bobPath = fileutils::get_data_dir() / bobId
     770           2 :                    / "profile.vcf";
     771             :     // Save VCard
     772           1 :     auto p = std::filesystem::path(alicePath);
     773           1 :     dhtnet::fileutils::recursive_mkdir(p.parent_path());
     774           1 :     std::ofstream aliceFile(alicePath);
     775           1 :     if (aliceFile.is_open()) {
     776           1 :         aliceFile << vcard;
     777           1 :         aliceFile.close();
     778             :     }
     779           1 :     p = std::filesystem::path(bobPath);
     780           1 :     dhtnet::fileutils::recursive_mkdir(p.parent_path());
     781           1 :     std::ofstream bobFile(bobPath);
     782           1 :     if (bobFile.is_open()) {
     783           1 :         bobFile << vcard;
     784           1 :         bobFile.close();
     785             :     }
     786             : 
     787           1 :     aliceAccount->addContact(bobUri);
     788           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     789           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     790             : 
     791           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     792           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     793             :         return !bobData.profilePath.empty() && !aliceData.profilePath.empty() && !bobData.conversationId.empty();
     794             :     }));
     795           1 :     CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobData.profilePath));
     796             : 
     797             :     // Now create alice2
     798           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     799           1 :     details[ConfProperties::TYPE] = "RING";
     800           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     801           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     802           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     803           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     804           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     805           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     806           1 :     bobData.profilePath = {};
     807           1 :     alice2Data.profilePath = {};
     808           1 :     alice2Id = Manager::instance().addAccount(details);
     809             : 
     810          16 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
     811             :         return alice2Data.deviceAnnounced && !bobData.profilePath.empty() && !alice2Data.profilePath.empty(); }));
     812           1 : }
     813             : 
     814             : void
     815           1 : SyncHistoryTest::testLastInteractionAfterClone()
     816             : {
     817           1 :     connectSignals();
     818           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     819           1 :     auto aliceUri = aliceAccount->getUsername();
     820           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     821           1 :     auto bobUri = bobAccount->getUsername();
     822             : 
     823           1 :     aliceAccount->addContact(bobUri);
     824           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     825           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     826             : 
     827           1 :     auto aliceMsgSize = aliceData.messages.size();
     828           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     829           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     830             : 
     831             :     // Start conversation
     832           1 :     libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 1"), "");
     833           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 2 == aliceData.messages.size(); }));
     834           1 :     libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 2"), "");
     835           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 3 == aliceData.messages.size(); }));
     836           1 :     libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 3"), "");
     837           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 4 == aliceData.messages.size(); }));
     838             : 
     839           1 :     auto msgId = aliceData.messages.rbegin()->id;
     840           1 :     libjami::setMessageDisplayed(aliceId, "swarm:" + aliceData.conversationId, msgId, 3);
     841           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceData.sent; }));
     842             : 
     843             :     // Now create alice2
     844           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     845           1 :     aliceAccount->exportArchive(aliceArchive);
     846           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     847           1 :     details[ConfProperties::TYPE] = "RING";
     848           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     849           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     850           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     851           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     852           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     853           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     854           1 :     alice2Id = Manager::instance().addAccount(details);
     855             : 
     856             :     // Check if conversation is ready
     857           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return !alice2Data.conversationId.empty(); }));
     858             :     // Check that last displayed is synched
     859           1 :     auto membersInfos = libjami::getConversationMembers(alice2Id, alice2Data.conversationId);
     860           2 :     CPPUNIT_ASSERT(std::find_if(membersInfos.begin(),
     861             :                                 membersInfos.end(),
     862             :                                 [&](auto infos) {
     863             :                                     return infos["uri"] == aliceUri
     864             :                                            && infos["lastDisplayed"] == msgId;
     865             :                                 })
     866             :                    != membersInfos.end());
     867           1 : }
     868             : 
     869             : void
     870           1 : SyncHistoryTest::testLastInteractionAfterSomeMessages()
     871             : {
     872           1 :     connectSignals();
     873           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     874           1 :     auto aliceUri = aliceAccount->getUsername();
     875           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     876           1 :     auto bobUri = bobAccount->getUsername();
     877             : 
     878             :     // Creates alice2
     879           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     880           1 :     aliceAccount->exportArchive(aliceArchive);
     881           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     882           1 :     details[ConfProperties::TYPE] = "RING";
     883           1 :     details[ConfProperties::DISPLAYNAME] = "ALICE2";
     884           1 :     details[ConfProperties::ALIAS] = "ALICE2";
     885           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     886           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     887           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     888           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     889           1 :     alice2Id = Manager::instance().addAccount(details);
     890           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {return alice2Data.deviceAnnounced; }));
     891           9 :     auto getMessageFromBody = [](const auto& data, const auto& body) -> std::string {
     892           9 :         auto it = std::find_if(data.messages.begin(), data.messages.end(), [&](auto& msg) {
     893           6 :             return msg.body.find("body") != msg.body.end() && msg.body.at("body") == body; });
     894           9 :         if (it != data.messages.end()) {
     895           6 :             return it->id;
     896             :         }
     897           3 :         return {};
     898             :     };
     899          18 :     auto getMessage = [](const auto& data, const auto& mid) -> bool {
     900          63 :         return std::find_if(data.messages.begin(), data.messages.end(), [&](auto& msg) { return msg.id == mid; }) != data.messages.end();
     901             :     };
     902             : 
     903           1 :     aliceAccount->addContact(bobUri);
     904           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     905           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && !alice2Data.conversationId.empty(); }));
     906             : 
     907           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     908           6 :     CPPUNIT_ASSERT(
     909             :         cv.wait_for(lk, 30s, [&]() { return aliceData.members[bobUri] == 1 && alice2Data.members[bobUri] == 1; }));
     910             : 
     911             :     // Start conversation
     912           1 :     bobData.messages.clear();
     913           1 :     libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 1"), "");
     914           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !getMessageFromBody(bobData, "Message 1").empty(); }));
     915           1 :     auto msgId = getMessageFromBody(bobData, "Message 1");
     916           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return getMessage(aliceData, msgId) && getMessage(alice2Data, msgId); }));
     917             : 
     918           1 :     bobData.messages.clear();
     919           1 :     libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 2"), "");
     920           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !getMessageFromBody(bobData, "Message 2").empty(); }));
     921           1 :     msgId = getMessageFromBody(bobData, "Message 2");
     922           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return getMessage(aliceData, msgId) && getMessage(alice2Data, msgId); }));
     923             : 
     924           1 :     bobData.messages.clear();
     925           1 :     libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 3"), "");
     926           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !getMessageFromBody(bobData, "Message 3").empty(); }));
     927           1 :     msgId = getMessageFromBody(bobData, "Message 3");
     928           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return getMessage(aliceData, msgId) && getMessage(alice2Data, msgId); }));
     929             : 
     930           1 :     libjami::setMessageDisplayed(aliceId, "swarm:" + aliceData.conversationId, msgId, 3);
     931           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
     932             :         return aliceData.sent && alice2Data.sent; }));
     933             : 
     934           1 :     auto membersInfos = libjami::getConversationMembers(alice2Id, alice2Data.conversationId);
     935           2 :     CPPUNIT_ASSERT(std::find_if(membersInfos.begin(),
     936             :                                 membersInfos.end(),
     937             :                                 [&](auto infos) {
     938             :                                     return infos["uri"] == aliceUri
     939             :                                            && infos["lastDisplayed"] == msgId;
     940             :                                 })
     941             :                    != membersInfos.end());
     942           1 : }
     943             : 
     944             : } // namespace test
     945             : } // namespace jami
     946             : 
     947           1 : RING_TEST_RUNNER(jami::test::SyncHistoryTest::name())

Generated by: LCOV version 1.14