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-05-08 08:55:44 Functions: 92 94 97.9 %

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

Generated by: LCOV version 1.14