LCOV - code coverage report
Current view: top level - test/unitTest/conversation - conversation.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 1557 1598 97.4 %
Date: 2024-12-21 08:56:24 Functions: 262 266 98.5 %

          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 <cppunit/TestAssert.h>
      19             : #include <cppunit/TestFixture.h>
      20             : #include <cppunit/extensions/HelperMacros.h>
      21             : 
      22             : #include <condition_variable>
      23             : #include <string>
      24             : #include <fstream>
      25             : #include <streambuf>
      26             : #include <git2.h>
      27             : #include <filesystem>
      28             : #include <msgpack.hpp>
      29             : 
      30             : #include "../../test_runner.h"
      31             : #include "account_const.h"
      32             : #include "archiver.h"
      33             : #include "base64.h"
      34             : #include "common.h"
      35             : #include "conversation/conversationcommon.h"
      36             : #include "fileutils.h"
      37             : #include "jami.h"
      38             : #include "manager.h"
      39             : #include <dhtnet/certstore.h>
      40             : 
      41             : using namespace std::string_literals;
      42             : using namespace std::literals::chrono_literals;
      43             : using namespace libjami::Account;
      44             : 
      45             : struct ConvInfoTest
      46             : {
      47             :     std::string id {};
      48             :     time_t created {0};
      49             :     time_t removed {0};
      50             :     time_t erased {0};
      51             : 
      52           2 :     MSGPACK_DEFINE_MAP(id, created, removed, erased)
      53             : };
      54             : 
      55             : namespace jami {
      56             : namespace test {
      57             : 
      58             : struct UserData {
      59             :     std::string conversationId;
      60             :     bool removed {false};
      61             :     bool requestReceived {false};
      62             :     bool errorDetected {false};
      63             :     bool registered {false};
      64             :     bool stopped {false};
      65             :     bool deviceAnnounced {false};
      66             :     bool sending {false};
      67             :     bool sent {false};
      68             :     bool searchFinished {false};
      69             :     std::string profilePath;
      70             :     std::string payloadTrustRequest;
      71             :     std::map<std::string, std::string> profile;
      72             :     std::vector<libjami::SwarmMessage> messages;
      73             :     std::vector<libjami::SwarmMessage> messagesUpdated;
      74             :     std::vector<std::map<std::string, std::string>> reactions;
      75             :     std::vector<std::map<std::string, std::string>> messagesFound;
      76             :     std::vector<std::string> reactionRemoved;
      77             :     std::map<std::string, std::string> preferences;
      78             : };
      79             : 
      80             : class ConversationTest : public CppUnit::TestFixture
      81             : {
      82             : public:
      83         102 :     ~ConversationTest() { libjami::fini(); }
      84           2 :     static std::string name() { return "Conversation"; }
      85             :     void setUp();
      86             :     void tearDown();
      87             :     std::string createFakeConversation(std::shared_ptr<JamiAccount> account,
      88             :                                        const std::string& fakeCert = "");
      89             : 
      90             :     std::string aliceId;
      91             :     UserData aliceData;
      92             :     std::string alice2Id;
      93             :     UserData alice2Data;
      94             :     std::string bobId;
      95             :     UserData bobData;
      96             :     std::string bob2Id;
      97             :     UserData bob2Data;
      98             :     std::string carlaId;
      99             :     UserData carlaData;
     100             : 
     101             :     std::mutex mtx;
     102             :     std::unique_lock<std::mutex> lk {mtx};
     103             :     std::condition_variable cv;
     104             : 
     105             :     void connectSignals();
     106             : 
     107             : private:
     108             :     void testCreateConversation();
     109             :     void testOfflineConvModule();
     110             :     void testCreateConversationInvalidDisplayName();
     111             :     void testGetConversation();
     112             :     void testGetConversationsAfterRm();
     113             :     void testRemoveInvalidConversation();
     114             :     void testSendMessage();
     115             :     void testSendMessageWithBadDisplayName();
     116             :     void testReplaceWithBadCertificate();
     117             :     void testSendMessageTriggerMessageReceived();
     118             :     void testMergeTwoDifferentHeads();
     119             :     void testSendMessageToMultipleParticipants();
     120             :     void testPingPongMessages();
     121             :     void testSetMessageDisplayedTwice();
     122             :     void testSetMessageDisplayedPreference();
     123             :     void testSetMessageDisplayedAfterClone();
     124             :     void testSendMessageWithLotOfKnownDevices();
     125             :     void testVoteNonEmpty();
     126             :     void testNoBadFileInInitialCommit();
     127             :     void testNoBadCertInInitialCommit();
     128             :     void testPlainTextNoBadFile();
     129             :     void testVoteNoBadFile();
     130             :     void testETooBigClone();
     131             :     void testETooBigFetch();
     132             :     void testUnknownModeDetected();
     133             :     void testUpdateProfile();
     134             :     void testGetProfileRequest();
     135             :     void testCheckProfileInConversationRequest();
     136             :     void testCheckProfileInTrustRequest();
     137             :     void testMemberCannotUpdateProfile();
     138             :     void testUpdateProfileWithBadFile();
     139             :     void testFetchProfileUnauthorized();
     140             :     void testSyncingWhileAccepting();
     141             :     void testCountInteractions();
     142             :     void testReplayConversation();
     143             :     void testSyncWithoutPinnedCert();
     144             :     void testImportMalformedContacts();
     145             :     void testCloneFromMultipleDevice();
     146             :     void testSendReply();
     147             :     void testSearchInConv();
     148             :     void testConversationPreferences();
     149             :     void testConversationPreferencesBeforeClone();
     150             :     void testConversationPreferencesMultiDevices();
     151             :     void testFixContactDetails();
     152             :     void testRemoveOneToOneNotInDetails();
     153             :     void testMessageEdition();
     154             :     void testMessageReaction();
     155             :     void testMessageEditionWithReaction();
     156             :     void testLoadPartiallyRemovedConversation();
     157             :     void testReactionsOnEditedMessage();
     158             :     void testUpdateProfileMultiDevice();
     159             : 
     160           2 :     CPPUNIT_TEST_SUITE(ConversationTest);
     161           1 :     CPPUNIT_TEST(testCreateConversation);
     162           1 :     CPPUNIT_TEST(testOfflineConvModule);
     163           1 :     CPPUNIT_TEST(testCreateConversationInvalidDisplayName);
     164           1 :     CPPUNIT_TEST(testGetConversation);
     165           1 :     CPPUNIT_TEST(testGetConversationsAfterRm);
     166           1 :     CPPUNIT_TEST(testRemoveInvalidConversation);
     167           1 :     CPPUNIT_TEST(testSendMessage);
     168           1 :     CPPUNIT_TEST(testSendMessageWithBadDisplayName);
     169           1 :     CPPUNIT_TEST(testReplaceWithBadCertificate);
     170           1 :     CPPUNIT_TEST(testSendMessageTriggerMessageReceived);
     171           1 :     CPPUNIT_TEST(testMergeTwoDifferentHeads);
     172           1 :     CPPUNIT_TEST(testSendMessageToMultipleParticipants);
     173           1 :     CPPUNIT_TEST(testPingPongMessages);
     174           1 :     CPPUNIT_TEST(testSetMessageDisplayedTwice);
     175           1 :     CPPUNIT_TEST(testSetMessageDisplayedPreference);
     176           1 :     CPPUNIT_TEST(testSetMessageDisplayedAfterClone);
     177           1 :     CPPUNIT_TEST(testSendMessageWithLotOfKnownDevices);
     178           1 :     CPPUNIT_TEST(testVoteNonEmpty);
     179           1 :     CPPUNIT_TEST(testNoBadFileInInitialCommit);
     180           1 :     CPPUNIT_TEST(testNoBadCertInInitialCommit);
     181           1 :     CPPUNIT_TEST(testPlainTextNoBadFile);
     182           1 :     CPPUNIT_TEST(testVoteNoBadFile);
     183           1 :     CPPUNIT_TEST(testETooBigClone);
     184           1 :     CPPUNIT_TEST(testETooBigFetch);
     185           1 :     CPPUNIT_TEST(testUnknownModeDetected);
     186           1 :     CPPUNIT_TEST(testUpdateProfile);
     187           1 :     CPPUNIT_TEST(testGetProfileRequest);
     188           1 :     CPPUNIT_TEST(testCheckProfileInConversationRequest);
     189           1 :     CPPUNIT_TEST(testCheckProfileInTrustRequest);
     190           1 :     CPPUNIT_TEST(testMemberCannotUpdateProfile);
     191           1 :     CPPUNIT_TEST(testUpdateProfileWithBadFile);
     192           1 :     CPPUNIT_TEST(testFetchProfileUnauthorized);
     193           1 :     CPPUNIT_TEST(testSyncingWhileAccepting);
     194           1 :     CPPUNIT_TEST(testCountInteractions);
     195           1 :     CPPUNIT_TEST(testReplayConversation);
     196           1 :     CPPUNIT_TEST(testSyncWithoutPinnedCert);
     197           1 :     CPPUNIT_TEST(testImportMalformedContacts);
     198           1 :     CPPUNIT_TEST(testCloneFromMultipleDevice);
     199           1 :     CPPUNIT_TEST(testSendReply);
     200           1 :     CPPUNIT_TEST(testSearchInConv);
     201           1 :     CPPUNIT_TEST(testConversationPreferences);
     202           1 :     CPPUNIT_TEST(testConversationPreferencesBeforeClone);
     203           1 :     CPPUNIT_TEST(testConversationPreferencesMultiDevices);
     204           1 :     CPPUNIT_TEST(testFixContactDetails);
     205           1 :     CPPUNIT_TEST(testRemoveOneToOneNotInDetails);
     206           1 :     CPPUNIT_TEST(testMessageEdition);
     207           1 :     CPPUNIT_TEST(testMessageReaction);
     208           1 :     CPPUNIT_TEST(testMessageEditionWithReaction);
     209           1 :     CPPUNIT_TEST(testLoadPartiallyRemovedConversation);
     210           1 :     CPPUNIT_TEST(testReactionsOnEditedMessage);
     211           1 :     CPPUNIT_TEST(testUpdateProfileMultiDevice);
     212           4 :     CPPUNIT_TEST_SUITE_END();
     213             : };
     214             : 
     215             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ConversationTest, ConversationTest::name());
     216             : 
     217             : void
     218          51 : ConversationTest::setUp()
     219             : {
     220             :     // Init daemon
     221          51 :     libjami::init(
     222             :         libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
     223          51 :     if (not Manager::instance().initialized)
     224           1 :         CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
     225             : 
     226          51 :     auto actors = load_actors("actors/alice-bob-carla.yml");
     227          51 :     aliceId = actors["alice"];
     228          51 :     bobId = actors["bob"];
     229          51 :     carlaId = actors["carla"];
     230             : 
     231          51 :     aliceData = {};
     232          51 :     alice2Data = {};
     233          51 :     bobData = {};
     234          51 :     bob2Data = {};
     235          51 :     carlaData = {};
     236             : 
     237          51 :     Manager::instance().sendRegister(carlaId, false);
     238         153 :     wait_for_announcement_of({aliceId, bobId});
     239          51 : }
     240             : 
     241             : void
     242          44 : ConversationTest::connectSignals()
     243             : {
     244          44 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     245          44 :     confHandlers.insert(
     246          88 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
     247          56 :             [&](const std::string& accountId, const std::map<std::string, std::string>&) {
     248          56 :                 if (accountId == aliceId) {
     249          12 :                     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     250          12 :                     auto details = aliceAccount->getVolatileAccountDetails();
     251          24 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     252          12 :                     if (daemonStatus == "REGISTERED") {
     253           4 :                         aliceData.registered = true;
     254           8 :                     } else if (daemonStatus == "UNREGISTERED") {
     255           5 :                         aliceData.stopped = true;
     256             :                     }
     257          24 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     258          12 :                     aliceData.deviceAnnounced = deviceAnnounced == "true";
     259          56 :                 } else if (accountId == bobId) {
     260           0 :                     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     261           0 :                     auto details = bobAccount->getVolatileAccountDetails();
     262           0 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     263           0 :                     if (daemonStatus == "REGISTERED") {
     264           0 :                         bobData.registered = true;
     265           0 :                     } else if (daemonStatus == "UNREGISTERED") {
     266           0 :                         bobData.stopped = true;
     267             :                     }
     268           0 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     269           0 :                     bobData.deviceAnnounced = deviceAnnounced == "true";
     270          44 :                 } else if (accountId == bob2Id) {
     271          21 :                     auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id);
     272          21 :                     auto details = bob2Account->getVolatileAccountDetails();
     273          42 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     274          21 :                     if (daemonStatus == "REGISTERED") {
     275           9 :                         bob2Data.registered = true;
     276          12 :                     } else if (daemonStatus == "UNREGISTERED") {
     277           6 :                         bob2Data.stopped = true;
     278             :                     }
     279          42 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     280          21 :                     bob2Data.deviceAnnounced = deviceAnnounced == "true";
     281          44 :                 } else if (accountId == carlaId) {
     282          14 :                     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     283          14 :                     auto details = carlaAccount->getVolatileAccountDetails();
     284          28 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     285          14 :                     if (daemonStatus == "REGISTERED") {
     286           9 :                         carlaData.registered = true;
     287           5 :                     } else if (daemonStatus == "UNREGISTERED") {
     288           0 :                         carlaData.stopped = true;
     289             :                     }
     290          28 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     291          14 :                     carlaData.deviceAnnounced = deviceAnnounced == "true";
     292          14 :                 }
     293          56 :                 cv.notify_one();
     294          56 :             }));
     295          44 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     296          79 :         [&](const std::string& accountId, const std::string& conversationId) {
     297          79 :             if (accountId == aliceId) {
     298          46 :                 aliceData.conversationId = conversationId;
     299          33 :             } else if (accountId == alice2Id) {
     300           1 :                 alice2Data.conversationId = conversationId;
     301          32 :             } else if (accountId == bobId) {
     302          25 :                 bobData.conversationId = conversationId;
     303           7 :             } else if (accountId == bob2Id) {
     304           5 :                 bob2Data.conversationId = conversationId;
     305           2 :             } else if (accountId == carlaId) {
     306           2 :                 carlaData.conversationId = conversationId;
     307             :             }
     308          79 :             cv.notify_one();
     309          79 :         }));
     310          44 :     confHandlers.insert(
     311          88 :         libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>(
     312          10 :             [&](const std::string& account_id,
     313             :                 const std::string& /*from*/,
     314             :                 const std::string& /*conversationId*/,
     315             :                 const std::vector<uint8_t>& payload,
     316             :                 time_t /*received*/) {
     317          10 :                 auto payloadStr = std::string(payload.data(), payload.data() + payload.size());
     318          10 :                 if (account_id == aliceId)
     319           0 :                     aliceData.payloadTrustRequest = payloadStr;
     320          10 :                 else if (account_id == bobId)
     321           9 :                     bobData.payloadTrustRequest = payloadStr;
     322          10 :                 cv.notify_one();
     323          10 :             }));
     324          44 :     confHandlers.insert(
     325          88 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>(
     326          37 :             [&](const std::string& accountId,
     327             :                 const std::string& /* conversationId */,
     328             :                 std::map<std::string, std::string> /*metadatas*/) {
     329          37 :                 if (accountId == aliceId) {
     330           2 :                     aliceData.requestReceived = true;
     331          35 :                 } else if (accountId == bobId) {
     332          31 :                     bobData.requestReceived = true;
     333           4 :                 } else if (accountId == bob2Id) {
     334           2 :                     bob2Data.requestReceived = true;
     335           2 :                 } else if (accountId == carlaId) {
     336           2 :                     carlaData.requestReceived = true;
     337             :                 }
     338          37 :                 cv.notify_one();
     339          37 :             }));
     340          44 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>(
     341         114 :         [&](const std::string& accountId,
     342             :             const std::string& /* conversationId */,
     343             :             libjami::SwarmMessage message) {
     344         114 :             if (accountId == aliceId) {
     345          83 :                 aliceData.messages.emplace_back(message);
     346          31 :             } else if (accountId == bobId) {
     347          23 :                 bobData.messages.emplace_back(message);
     348           8 :             } else if (accountId == carlaId) {
     349           7 :                 carlaData.messages.emplace_back(message);
     350             :             }
     351         114 :             cv.notify_one();
     352         114 :         }));
     353          44 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>(
     354           4 :         [&](const std::string& accountId,
     355             :             const std::string& /* conversationId */,
     356             :             libjami::SwarmMessage message) {
     357           4 :             if (accountId == aliceId) {
     358           3 :                 aliceData.messagesUpdated.emplace_back(message);
     359           1 :             } else if (accountId == bobId) {
     360           1 :                 bobData.messagesUpdated.emplace_back(message);
     361           0 :             } else if (accountId == carlaId) {
     362           0 :                 carlaData.messagesUpdated.emplace_back(message);
     363             :             }
     364           4 :             cv.notify_one();
     365           4 :         }));
     366          44 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ReactionAdded>(
     367           3 :         [&](const std::string& accountId,
     368             :             const std::string& /* conversationId */,
     369             :             const std::string& /* messageId */,
     370             :             std::map<std::string, std::string> reaction) {
     371           3 :             if (accountId == aliceId) {
     372           3 :                 aliceData.reactions.emplace_back(reaction);
     373           0 :             } else if (accountId == bobId) {
     374           0 :                 bobData.reactions.emplace_back(reaction);
     375           0 :             } else if (accountId == carlaId) {
     376           0 :                 carlaData.reactions.emplace_back(reaction);
     377             :             }
     378           3 :             cv.notify_one();
     379           3 :         }));
     380          44 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ReactionRemoved>(
     381           1 :         [&](const std::string& accountId,
     382             :             const std::string& /* conversationId */,
     383             :             const std::string& /* messageId */,
     384             :             const std::string& reactionId) {
     385           1 :             if (accountId == aliceId) {
     386           1 :                 aliceData.reactionRemoved.emplace_back(reactionId);
     387           0 :             } else if (accountId == bobId) {
     388           0 :                 bobData.reactionRemoved.emplace_back(reactionId);
     389           0 :             } else if (accountId == carlaId) {
     390           0 :                 carlaData.reactionRemoved.emplace_back(reactionId);
     391             :             }
     392           1 :             cv.notify_one();
     393           1 :         }));
     394          44 :     confHandlers.insert(
     395          88 :         libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>(
     396          14 :             [&](const std::string& accountId,
     397             :                 const std::string& /* conversationId */,
     398             :                 int /*code*/,
     399             :                 const std::string& /* what */) {
     400          14 :                 if (accountId == aliceId)
     401           3 :                     aliceData.errorDetected = true;
     402          11 :                 else if (accountId == bobId)
     403           9 :                     bobData.errorDetected = true;
     404           2 :                 else if (accountId == carlaId)
     405           2 :                     carlaData.errorDetected = true;
     406          14 :                 cv.notify_one();
     407          14 :             }));
     408          44 :     confHandlers.insert(
     409          88 :         libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>(
     410         325 :             [&](const std::string& accountId,
     411             :                 const std::string& /*conversationId*/,
     412             :                 const std::string& /*peer*/,
     413             :                 const std::string& /*msgId*/,
     414             :                 int status) {
     415         325 :                 if (accountId == aliceId) {
     416         242 :                     if (status == 2)
     417         103 :                         aliceData.sending = true;
     418         242 :                     if (status == 3)
     419          21 :                         aliceData.sent = true;
     420          83 :                 } else if (accountId == bobId) {
     421          59 :                     if (status == 2)
     422          28 :                         bobData.sending = true;
     423          59 :                     if (status == 3)
     424           5 :                         bobData.sent = true;
     425             :                 }
     426         325 :                 cv.notify_one();
     427         325 :             }));
     428          44 :     confHandlers.insert(
     429          88 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationProfileUpdated>(
     430           5 :             [&](const auto& accountId, const auto& /* conversationId */, const auto& profile) {
     431           5 :                 if (accountId == aliceId) {
     432           3 :                     aliceData.profile = profile;
     433           2 :                 } else if (accountId == bobId) {
     434           1 :                     bobData.profile = profile;
     435             :                 }
     436           5 :                 cv.notify_one();
     437           5 :             }));
     438          44 :     confHandlers.insert(
     439          88 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>(
     440           9 :             [&](const std::string& accountId, const std::string&) {
     441           9 :                 if (accountId == aliceId)
     442           9 :                     aliceData.removed = true;
     443           0 :                 else if (accountId == bobId)
     444           0 :                     bobData.removed = true;
     445           0 :                 else if (accountId == bob2Id)
     446           0 :                     bob2Data.removed = true;
     447           9 :                 cv.notify_one();
     448           9 :             }));
     449          44 :     confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ProfileReceived>(
     450           9 :         [&](const std::string& accountId, const std::string& peerId, const std::string& path) {
     451           9 :             if (accountId == bobId)
     452           5 :                 bobData.profilePath = path;
     453           9 :             cv.notify_one();
     454           9 :         }));
     455          44 :     confHandlers.insert(
     456          88 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationPreferencesUpdated>(
     457           7 :             [&](const std::string& accountId,
     458             :                 const std::string& conversationId,
     459             :                 std::map<std::string, std::string> preferences) {
     460           7 :                 if (accountId == bobId)
     461           3 :                     bobData.preferences = preferences;
     462           4 :                 else if (accountId == bob2Id)
     463           2 :                     bob2Data.preferences = preferences;
     464           7 :                 cv.notify_one();
     465           7 :             }));
     466          44 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessagesFound>(
     467           7 :         [&](uint32_t,
     468             :             const std::string& accountId,
     469             :             const std::string& conversationId,
     470             :             std::vector<std::map<std::string, std::string>> msg) {
     471           7 :             if (accountId == aliceId) {
     472           7 :                 aliceData.messagesFound.insert(aliceData.messagesFound.end(), msg.begin(), msg.end());
     473           7 :                 aliceData.searchFinished = conversationId.empty();
     474             :             }
     475           7 :             cv.notify_one();
     476           7 :         }));
     477          44 :     libjami::registerSignalHandlers(confHandlers);
     478          44 : }
     479             : 
     480             : void
     481          51 : ConversationTest::tearDown()
     482             : {
     483         102 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
     484          51 :     std::remove(bobArchive.c_str());
     485         102 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
     486          51 :     std::remove(aliceArchive.c_str());
     487          51 :     if (!alice2Id.empty()) {
     488           2 :         wait_for_removal_of(alice2Id);
     489             :     }
     490             : 
     491          51 :     if (bob2Id.empty()) {
     492         180 :         wait_for_removal_of({aliceId, bobId, carlaId});
     493             :     } else {
     494          30 :         wait_for_removal_of({aliceId, bobId, carlaId, bob2Id});
     495             :     }
     496          51 : }
     497             : 
     498             : void
     499           1 : ConversationTest::testCreateConversation()
     500             : {
     501           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     502           1 :     connectSignals();
     503             : 
     504           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     505           1 :     auto aliceDeviceId = aliceAccount->currentDeviceId();
     506           1 :     auto uri = aliceAccount->getUsername();
     507             : 
     508             :     // Start conversation
     509           1 :     auto convId = libjami::startConversation(aliceId);
     510           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }));
     511           1 :     ConversationRepository repo(aliceAccount, convId);
     512           1 :     CPPUNIT_ASSERT(repo.mode() == ConversationMode::INVITES_ONLY);
     513             : 
     514             :     // Assert that repository exists
     515           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
     516           4 :                     / "conversations" / convId;
     517           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     518             :     // Check created files
     519           2 :     auto adminCrt = repoPath / "admins" / (uri + ".crt");
     520           1 :     CPPUNIT_ASSERT(std::filesystem::is_regular_file(adminCrt));
     521           1 :     auto crt = std::ifstream(adminCrt);
     522           1 :     std::string adminCrtStr((std::istreambuf_iterator<char>(crt)), std::istreambuf_iterator<char>());
     523           1 :     auto cert = aliceAccount->identity().second;
     524           1 :     auto deviceCert = cert->toString(false);
     525           1 :     auto parentCert = cert->issuer->toString(true);
     526           1 :     CPPUNIT_ASSERT(adminCrtStr == parentCert);
     527           2 :     auto deviceCrt = repoPath / "devices" / (aliceDeviceId + ".crt");
     528           1 :     CPPUNIT_ASSERT(std::filesystem::is_regular_file(deviceCrt));
     529           1 :     crt = std::ifstream(deviceCrt);
     530             :     std::string deviceCrtStr((std::istreambuf_iterator<char>(crt)),
     531           1 :                              std::istreambuf_iterator<char>());
     532           1 :     CPPUNIT_ASSERT(deviceCrtStr == deviceCert);
     533           1 : }
     534             : 
     535             : void
     536           1 : ConversationTest::testOfflineConvModule()
     537             : {
     538           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     539           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     540           1 :     CPPUNIT_ASSERT(carlaAccount->convModule() != nullptr);
     541           1 : }
     542             : 
     543             : void
     544           1 : ConversationTest::testCreateConversationInvalidDisplayName()
     545             : {
     546           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     547             : 
     548           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     549             : 
     550           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     551           1 :     bool conversationReady = false;
     552           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     553           1 :         [&](const std::string& accountId, const std::string& /* conversationId */) {
     554           1 :             if (accountId == aliceId) {
     555           1 :                 conversationReady = true;
     556           1 :                 cv.notify_one();
     557             :             }
     558           1 :         }));
     559           1 :     bool aliceRegistered = false;
     560           1 :     confHandlers.insert(
     561           2 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
     562           3 :             [&](const std::string&, const std::map<std::string, std::string>&) {
     563           3 :                 auto details = aliceAccount->getVolatileAccountDetails();
     564           6 :                 auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     565           3 :                 if (daemonStatus == "REGISTERED") {
     566           1 :                     aliceRegistered = true;
     567           1 :                     cv.notify_one();
     568             :                 }
     569           3 :             }));
     570           1 :     auto messageAliceReceived = 0;
     571           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>(
     572           1 :         [&](const std::string& accountId,
     573             :             const std::string& /* conversationId */,
     574             :             std::map<std::string, std::string> /*message*/) {
     575           1 :             if (accountId == aliceId) {
     576           1 :                 messageAliceReceived += 1;
     577             :             }
     578           1 :             cv.notify_one();
     579           1 :         }));
     580           1 :     libjami::registerSignalHandlers(confHandlers);
     581             : 
     582             : 
     583           1 :     std::map<std::string, std::string> details;
     584           1 :     details[ConfProperties::DISPLAYNAME] = " ";
     585           1 :     libjami::setAccountDetails(aliceId, details);
     586           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceRegistered; }));
     587             : 
     588             :     // Start conversation
     589           1 :     auto convId = libjami::startConversation(aliceId);
     590           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; }));
     591           1 :     messageAliceReceived = 0;
     592           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
     593           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 1; }));
     594           1 : }
     595             : 
     596             : void
     597           1 : ConversationTest::testGetConversation()
     598             : {
     599           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     600             : 
     601           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     602           1 :     auto uri = aliceAccount->getUsername();
     603           1 :     auto convId = libjami::startConversation(aliceId);
     604             : 
     605           1 :     auto conversations = libjami::getConversations(aliceId);
     606           1 :     CPPUNIT_ASSERT(conversations.size() == 1);
     607           1 :     CPPUNIT_ASSERT(conversations.front() == convId);
     608           1 : }
     609             : 
     610             : void
     611           1 : ConversationTest::testGetConversationsAfterRm()
     612             : {
     613           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     614           1 :     connectSignals();
     615             : 
     616           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     617           1 :     auto uri = aliceAccount->getUsername();
     618             : 
     619             :     // Start conversation
     620           1 :     auto convId = libjami::startConversation(aliceId);
     621           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }));
     622             : 
     623           1 :     auto conversations = libjami::getConversations(aliceId);
     624           1 :     CPPUNIT_ASSERT(conversations.size() == 1);
     625           1 :     CPPUNIT_ASSERT(libjami::removeConversation(aliceId, convId));
     626           1 :     conversations = libjami::getConversations(aliceId);
     627           1 :     CPPUNIT_ASSERT(conversations.size() == 0);
     628           1 : }
     629             : 
     630             : void
     631           1 : ConversationTest::testRemoveInvalidConversation()
     632             : {
     633           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     634           1 :     connectSignals();
     635             : 
     636             :     // Start conversation
     637           1 :     auto convId = libjami::startConversation(aliceId);
     638           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }));
     639             : 
     640           1 :     auto conversations = libjami::getConversations(aliceId);
     641           1 :     CPPUNIT_ASSERT(conversations.size() == 1);
     642           1 :     CPPUNIT_ASSERT(!libjami::removeConversation(aliceId, "foo"));
     643           1 :     conversations = libjami::getConversations(aliceId);
     644           1 :     CPPUNIT_ASSERT(conversations.size() == 1);
     645           1 : }
     646             : 
     647             : void
     648           1 : ConversationTest::testSendMessage()
     649             : {
     650           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     651           1 :     connectSignals();
     652             : 
     653           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     654           1 :     auto bobUri = bobAccount->getUsername();
     655             : 
     656           1 :     auto convId = libjami::startConversation(aliceId);
     657           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     658           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     659             : 
     660           1 :     libjami::acceptConversationRequest(bobId, convId);
     661           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
     662             : 
     663             :     // Assert that repository exists
     664           2 :     auto repoPath = fileutils::get_data_dir() / bobId
     665           4 :                     / "conversations" / convId;
     666           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     667             :     // Wait that alice sees Bob
     668           3 :     cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == 2; });
     669             : 
     670           1 :     auto bobMsgSize = bobData.messages.size();
     671           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
     672           5 :     cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 1; });
     673           1 : }
     674             : 
     675             : void
     676           1 : ConversationTest::testSendMessageWithBadDisplayName()
     677             : {
     678           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     679           1 :     connectSignals();
     680             : 
     681           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     682           1 :     auto bobUri = bobAccount->getUsername();
     683             : 
     684           1 :     std::map<std::string, std::string> details;
     685           1 :     details[ConfProperties::DISPLAYNAME] = "<o>";
     686           1 :     libjami::setAccountDetails(aliceId, details);
     687           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.registered; }));
     688             : 
     689           1 :     auto convId = libjami::startConversation(aliceId);
     690           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     691           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     692             : 
     693           1 :     libjami::acceptConversationRequest(bobId, convId);
     694           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
     695             : 
     696             :     // Assert that repository exists
     697           2 :     auto repoPath = fileutils::get_data_dir() / bobId
     698           4 :                     / "conversations" / convId;
     699           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     700             :     // Wait that alice sees Bob
     701           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == 2; }));
     702             : 
     703           1 :     auto bobMsgSize = bobData.messages.size();
     704           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
     705           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 1; }));
     706           1 : }
     707             : 
     708             : void
     709           1 : ConversationTest::testReplaceWithBadCertificate()
     710             : {
     711           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     712           1 :     connectSignals();
     713             : 
     714           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     715           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     716           1 :     auto bobUri = bobAccount->getUsername();
     717             : 
     718           1 :     auto convId = libjami::startConversation(aliceId);
     719             : 
     720           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     721          11 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     722             : 
     723           1 :     libjami::acceptConversationRequest(bobId, convId);
     724           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
     725             : 
     726             :     // Wait that alice sees Bob
     727           3 :     cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == 2; });
     728             : 
     729             :     // Replace alice's certificate with a bad one.
     730           2 :     auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId;
     731           3 :     auto aliceDevicePath = repoPath / "devices" / fmt::format("{}.crt", aliceAccount->currentDeviceId());
     732           3 :     auto bobDevicePath = repoPath / "devices" / fmt::format("{}.crt", bobAccount->currentDeviceId());
     733           1 :     std::filesystem::copy(bobDevicePath,
     734             :                           aliceDevicePath,
     735             :                           std::filesystem::copy_options::overwrite_existing);
     736           1 :     addAll(aliceAccount, convId);
     737             : 
     738             :     // Note: Do not use libjami::sendMessage as it will replace the invalid certificate by a valid one
     739           1 :     Json::Value root;
     740           1 :     root["type"] = "text/plain";
     741           1 :     root["body"] = "hi";
     742           1 :     Json::StreamWriterBuilder wbuilder;
     743           1 :     wbuilder["commentStyle"] = "None";
     744           1 :     wbuilder["indentation"] = "";
     745           1 :     auto message = Json::writeString(wbuilder, root);
     746           1 :     commitInRepo(repoPath, aliceAccount, message);
     747             :     // now we need to sync!
     748           1 :     bobData.errorDetected = false;
     749           1 :     libjami::sendMessage(aliceId, convId, "trigger sync!"s, "");
     750             :     // We should detect the incorrect commit!
     751           5 :     cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; });
     752           1 : }
     753             : 
     754             : void
     755           1 : ConversationTest::testSendMessageTriggerMessageReceived()
     756             : {
     757           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     758           1 :     connectSignals();
     759             : 
     760           1 :     auto convId = libjami::startConversation(aliceId);
     761           2 :     cv.wait_for(lk, 30s, [&] { return !aliceData.conversationId.empty(); });
     762             : 
     763           1 :     auto msgSize = aliceData.messages.size();
     764           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
     765           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.messages.size() == msgSize + 1; }));
     766           1 : }
     767             : 
     768             : void
     769           1 : ConversationTest::testMergeTwoDifferentHeads()
     770             : {
     771           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     772           1 :     connectSignals();
     773             : 
     774           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     775           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     776           1 :     auto aliceUri = aliceAccount->getUsername();
     777           1 :     auto carlaUri = carlaAccount->getUsername();
     778           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
     779           1 :     carlaAccount->trackBuddyPresence(aliceUri, true);
     780           1 :     auto convId = libjami::startConversation(aliceId);
     781             : 
     782           1 :     auto msgSize = aliceData.messages.size();
     783           1 :     aliceAccount->convModule()->addConversationMember(convId, dht::InfoHash(carlaUri), false);
     784           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == msgSize + 1; }));
     785             : 
     786             :     // Cp conversations & convInfo
     787           2 :     auto repoPathAlice = fileutils::get_data_dir() / aliceId / "conversations";
     788           2 :     auto repoPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() / "conversations";
     789           1 :     std::filesystem::copy(repoPathAlice, repoPathCarla, std::filesystem::copy_options::recursive);
     790           2 :     auto ciPathAlice = fileutils::get_data_dir() / aliceId
     791           2 :                        / "convInfo";
     792           2 :     auto ciPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID()
     793           2 :                        / "convInfo";
     794           1 :     std::filesystem::remove_all(ciPathCarla);
     795           1 :     std::filesystem::copy(ciPathAlice, ciPathCarla);
     796           1 :     carlaAccount->convModule()->loadConversations(); // necessary to load conversation
     797             : 
     798             :     // Accept for alice and makes different heads
     799           1 :     ConversationRepository repo(carlaAccount, convId);
     800           1 :     repo.join();
     801             : 
     802           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
     803           1 :     libjami::sendMessage(aliceId, convId, "sup"s, "");
     804           1 :     libjami::sendMessage(aliceId, convId, "jami"s, "");
     805             : 
     806             :     // Start Carla, should merge and all messages should be there
     807           1 :     Manager::instance().sendRegister(carlaId, true);
     808           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return !carlaData.messages.empty(); }));
     809           1 : }
     810             : 
     811             : void
     812           1 : ConversationTest::testSendMessageToMultipleParticipants()
     813             : {
     814           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     815             : 
     816           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     817           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     818           1 :     auto bobUri = bobAccount->getUsername();
     819           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     820           1 :     auto carlaUri = carlaAccount->getUsername();
     821           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
     822             : 
     823             :     // Enable carla
     824           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     825           1 :     bool carlaConnected = false;
     826           1 :     confHandlers.insert(
     827           2 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
     828           3 :             [&](const std::string&, const std::map<std::string, std::string>&) {
     829           3 :                 auto details = carlaAccount->getVolatileAccountDetails();
     830             :                 auto deviceAnnounced
     831           6 :                     = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     832           3 :                 if (deviceAnnounced == "true") {
     833           1 :                     carlaConnected = true;
     834           1 :                     cv.notify_one();
     835             :                 }
     836           3 :             }));
     837           1 :     libjami::registerSignalHandlers(confHandlers);
     838             : 
     839           1 :     Manager::instance().sendRegister(carlaId, true);
     840           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaConnected; }));
     841           1 :     confHandlers.clear();
     842           1 :     libjami::unregisterSignalHandlers();
     843             : 
     844           1 :     auto messageReceivedAlice = 0;
     845           1 :     auto messageReceivedBob = 0;
     846           1 :     auto messageReceivedCarla = 0;
     847           1 :     auto requestReceived = 0;
     848           1 :     auto conversationReady = 0;
     849           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>(
     850          13 :         [&](const std::string& accountId,
     851             :             const std::string& /* conversationId */,
     852             :             std::map<std::string, std::string> /*message*/) {
     853          13 :             if (accountId == aliceId)
     854           7 :                 messageReceivedAlice += 1;
     855          13 :             if (accountId == bobId)
     856           3 :                 messageReceivedBob += 1;
     857          13 :             if (accountId == carlaId)
     858           3 :                 messageReceivedCarla += 1;
     859          13 :             cv.notify_one();
     860          13 :         }));
     861             : 
     862           1 :     confHandlers.insert(
     863           2 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>(
     864           2 :             [&](const std::string& /*accountId*/,
     865             :                 const std::string& /* conversationId */,
     866             :                 std::map<std::string, std::string> /*metadatas*/) {
     867           2 :                 requestReceived += 1;
     868           2 :                 cv.notify_one();
     869           2 :             }));
     870             : 
     871           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     872           3 :         [&](const std::string& /*accountId*/, const std::string& /* conversationId */) {
     873           3 :             conversationReady += 1;
     874           3 :             cv.notify_one();
     875           3 :         }));
     876           1 :     libjami::registerSignalHandlers(confHandlers);
     877             : 
     878           1 :     auto convId = libjami::startConversation(aliceId);
     879             : 
     880           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     881           1 :     libjami::addConversationMember(aliceId, convId, carlaUri);
     882           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return requestReceived == 2; }));
     883             : 
     884           1 :     messageReceivedAlice = 0;
     885           1 :     libjami::acceptConversationRequest(bobId, convId);
     886           1 :     libjami::acceptConversationRequest(carlaId, convId);
     887             :     // >= because we can have merges cause the accept commits
     888           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() {
     889             :         return conversationReady == 3 && messageReceivedAlice >= 2;
     890             :     }));
     891             : 
     892             :     // Assert that repository exists
     893           2 :     auto repoPath = fileutils::get_data_dir() / bobId
     894           4 :                     / "conversations" / convId;
     895           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     896           2 :     repoPath = fileutils::get_data_dir() / carlaAccount->getAccountID()
     897           3 :                / "conversations" / convId;
     898           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     899             : 
     900           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
     901           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() {
     902             :         return messageReceivedBob >= 1 && messageReceivedCarla >= 1;
     903             :     }));
     904           1 :     libjami::unregisterSignalHandlers();
     905           1 : }
     906             : 
     907             : void
     908           1 : ConversationTest::testPingPongMessages()
     909             : {
     910           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     911           1 :     connectSignals();
     912             : 
     913           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     914           1 :     auto bobUri = bobAccount->getUsername();
     915           1 :     auto convId = libjami::startConversation(aliceId);
     916           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     917           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     918           1 :     auto aliceMsgSize = aliceData.messages.size();
     919           1 :     libjami::acceptConversationRequest(bobId, convId);
     920           7 :     CPPUNIT_ASSERT(
     921             :         cv.wait_for(lk, 60s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); }));
     922             :     // Assert that repository exists
     923           2 :     auto repoPath = fileutils::get_data_dir() / bobId
     924           4 :                     / "conversations" / convId;
     925           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     926           1 :     aliceMsgSize = aliceData.messages.size();
     927           1 :     auto bobMsgSize = bobData.messages.size();
     928           1 :     libjami::sendMessage(aliceId, convId, "ping"s, "");
     929           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     930             :         return bobMsgSize + 1 == bobData.messages.size() && aliceMsgSize + 1 == aliceData.messages.size();
     931             :     }));
     932           1 :     libjami::sendMessage(bobId, convId, "pong"s, "");
     933           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     934             :         return bobMsgSize + 2 == bobData.messages.size() && aliceMsgSize + 2 == aliceData.messages.size();
     935             :     }));
     936           1 :     libjami::sendMessage(bobId, convId, "ping"s, "");
     937           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     938             :         return bobMsgSize + 3 == bobData.messages.size() && aliceMsgSize + 3 == aliceData.messages.size();
     939             :     }));
     940           1 :     libjami::sendMessage(aliceId, convId, "pong"s, "");
     941           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     942             :         return bobMsgSize + 4 == bobData.messages.size() && aliceMsgSize + 4 == aliceData.messages.size();
     943             :     }));
     944           1 : }
     945             : 
     946             : void
     947           1 : ConversationTest::testSetMessageDisplayedTwice()
     948             : {
     949           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     950           1 :     connectSignals();
     951             : 
     952           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     953           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     954           1 :     auto bobUri = bobAccount->getUsername();
     955           1 :     auto convId = libjami::startConversation(aliceId);
     956           1 :     auto aliceMsgSize = aliceData.messages.size();
     957           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     958           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     959             :     // Assert that repository exists
     960           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
     961           4 :                     / "conversations" / convId;
     962           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     963             :     // Check created files
     964           2 :     auto bobInvited = repoPath / "invited" / bobUri;
     965           1 :     CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobInvited));
     966           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     967           1 :     libjami::acceptConversationRequest(bobId, convId);
     968           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
     969             : 
     970           1 :     bobData.sent = false;
     971           1 :     aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3);
     972           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.sent; }));
     973             : 
     974           1 :     bobData.sent = false;
     975           1 :     aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3);
     976           5 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobData.sent; }));
     977           1 : }
     978             : 
     979             : void
     980           1 : ConversationTest::testSetMessageDisplayedPreference()
     981             : {
     982           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
     983           1 :     connectSignals();
     984             : 
     985           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     986           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     987           1 :     auto aliceUri = aliceAccount->getUsername();
     988           1 :     auto bobUri = bobAccount->getUsername();
     989           1 :     auto convId = libjami::startConversation(aliceId);
     990             : 
     991           1 :     auto details = aliceAccount->getAccountDetails();
     992           1 :     CPPUNIT_ASSERT(details[ConfProperties::SENDREADRECEIPT] == "true");
     993           1 :     details[ConfProperties::SENDREADRECEIPT] = "false";
     994           1 :     libjami::setAccountDetails(aliceId, details);
     995           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.registered; }));
     996             : 
     997           1 :     auto aliceMsgSize = aliceData.messages.size();
     998           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     999          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); }));
    1000             : 
    1001           1 :     libjami::acceptConversationRequest(bobId, convId);
    1002           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
    1003             : 
    1004           1 :     aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3);
    1005             :     // Bob should not receive anything here, as sendMessageDisplayed is disabled for Alice
    1006           4 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobData.sent; }));
    1007           1 : }
    1008             : 
    1009             : void
    1010           1 : ConversationTest::testSetMessageDisplayedAfterClone()
    1011             : {
    1012           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1013           1 :     connectSignals();
    1014             : 
    1015           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1016           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1017           1 :     auto aliceUri = aliceAccount->getUsername();
    1018           1 :     auto bobUri = bobAccount->getUsername();
    1019           1 :     auto convId = libjami::startConversation(aliceId);
    1020             : 
    1021           1 :     auto aliceMsgSize = aliceData.messages.size();
    1022           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1023           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); }));
    1024           1 :     libjami::acceptConversationRequest(bobId, convId);
    1025           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
    1026             : 
    1027           1 :     aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3);
    1028             : 
    1029             :     // Alice creates a second device
    1030           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
    1031           1 :     std::remove(aliceArchive.c_str());
    1032           1 :     aliceAccount->exportArchive(aliceArchive);
    1033           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    1034           1 :     details[ConfProperties::TYPE] = "RING";
    1035           1 :     details[ConfProperties::DISPLAYNAME] = "alice2";
    1036           1 :     details[ConfProperties::ALIAS] = "alice2";
    1037           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    1038           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    1039           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    1040           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
    1041           1 :     alice2Id = Manager::instance().addAccount(details);
    1042             : 
    1043             :     // Disconnect alice2, to create a valid conv betwen Alice and alice1
    1044           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !alice2Data.conversationId.empty(); }));
    1045             : 
    1046             :     // Assert that message is set as displayed for self (for the read status)
    1047           1 :     auto membersInfos = libjami::getConversationMembers(aliceId, convId);
    1048           2 :     CPPUNIT_ASSERT(std::find_if(membersInfos.begin(),
    1049             :                                 membersInfos.end(),
    1050             :                                 [&](auto infos) {
    1051             :                                     return infos["uri"] == aliceUri
    1052             :                                            && infos["lastDisplayed"] == convId;
    1053             :                                 })
    1054             :                    != membersInfos.end());
    1055           1 : }
    1056             : 
    1057             : void
    1058           1 : ConversationTest::testSendMessageWithLotOfKnownDevices()
    1059             : {
    1060           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1061             : 
    1062           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1063             : 
    1064             :     // Alice creates a second device
    1065           2 :     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
    1066           1 :     std::remove(aliceArchive.c_str());
    1067           1 :     aliceAccount->exportArchive(aliceArchive);
    1068           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    1069           1 :     details[ConfProperties::TYPE] = "RING";
    1070           1 :     details[ConfProperties::DISPLAYNAME] = "alice2";
    1071           1 :     details[ConfProperties::ALIAS] = "alice2";
    1072           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    1073           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    1074           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    1075           1 :     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
    1076           1 :     alice2Id = Manager::instance().addAccount(details);
    1077           1 :     auto alice2Account = Manager::instance().getAccount<JamiAccount>(alice2Id);
    1078             : 
    1079           1 :     bool conversationAlice2Ready = false;
    1080           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
    1081           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
    1082           2 :         [&](const std::string& accountId, const std::string& conversationId) {
    1083           2 :             if (accountId == alice2Id) {
    1084           1 :                 conversationAlice2Ready = true;
    1085             :             }
    1086           2 :             cv.notify_one();
    1087           2 :         }));
    1088           1 :     bool alice2Registered = false;
    1089           1 :     confHandlers.insert(
    1090           2 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
    1091           3 :             [&](const std::string&, const std::map<std::string, std::string>&) {
    1092           3 :                 auto details = alice2Account->getVolatileAccountDetails();
    1093           6 :                 auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
    1094           3 :                 if (daemonStatus == "REGISTERED") {
    1095           1 :                     alice2Registered = true;
    1096           1 :                     cv.notify_one();
    1097             :                 }
    1098           3 :             }));
    1099           1 :     libjami::registerSignalHandlers(confHandlers);
    1100             : 
    1101           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return alice2Registered; }));
    1102             : 
    1103             :     // Add a lot of known devices
    1104        1001 :     for (auto i = 0; i < 1000; ++i) {
    1105        1000 :         dht::Hash<32> h = dht::Hash<32>::get(std::to_string(i));
    1106        1000 :         aliceAccount->accountManager()->getInfo()->contacts->foundAccountDevice(h);
    1107        1000 :         alice2Account->accountManager()->getInfo()->contacts->foundAccountDevice(h);
    1108             :     }
    1109             : 
    1110           1 :     auto bootstraped = std::make_shared<bool>(false);
    1111           1 :     alice2Account->convModule()->onBootstrapStatus(
    1112           2 :         [=](std::string /*convId*/, Conversation::BootstrapStatus status) {
    1113           2 :             *bootstraped = status == Conversation::BootstrapStatus::SUCCESS;
    1114           2 :             cv.notify_one();
    1115           2 :         });
    1116             : 
    1117           1 :     auto convId = libjami::startConversation(aliceId);
    1118           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationAlice2Ready; }));
    1119             : 
    1120             :     // Should bootstrap successfully
    1121           1 :     *bootstraped = false;
    1122           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return *bootstraped; }));
    1123           1 :     libjami::unregisterSignalHandlers();
    1124           1 : }
    1125             : 
    1126             : std::string
    1127           2 : ConversationTest::createFakeConversation(std::shared_ptr<JamiAccount> account,
    1128             :                                          const std::string& fakeCert)
    1129             : {
    1130           4 :     auto repoPath = fileutils::get_data_dir() / account->getAccountID()
    1131           8 :                     / "conversations" / "tmp";
    1132             : 
    1133           2 :     git_repository* repo_ptr = nullptr;
    1134             :     git_repository_init_options opts;
    1135           2 :     git_repository_init_options_init(&opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION);
    1136           2 :     opts.flags |= GIT_REPOSITORY_INIT_MKPATH;
    1137           2 :     opts.initial_head = "main";
    1138           2 :     if (git_repository_init_ext(&repo_ptr, repoPath.c_str(), &opts) < 0) {
    1139           0 :         JAMI_ERR("Unable to create a git repository in %s", repoPath.c_str());
    1140             :     }
    1141           2 :     GitRepository repo {std::move(repo_ptr), git_repository_free};
    1142             : 
    1143             :     // Add files
    1144           2 :     auto deviceId = std::string(account->currentDeviceId());
    1145             : 
    1146           2 :     repoPath = git_repository_workdir(repo.get());
    1147           2 :     auto adminsPath = repoPath / "admins";
    1148           2 :     auto devicesPath = repoPath / "devices";
    1149           4 :     auto crlsPath = repoPath / "CRLs" / deviceId;
    1150             : 
    1151           2 :     if (!dhtnet::fileutils::recursive_mkdir(adminsPath, 0700)) {
    1152           0 :         JAMI_ERROR("Error when creating %s. Abort create conversations", adminsPath.c_str());
    1153             :     }
    1154             : 
    1155           2 :     auto cert = account->identity().second;
    1156           2 :     auto deviceCert = cert->toString(false);
    1157           2 :     auto parentCert = cert->issuer;
    1158           2 :     if (!parentCert) {
    1159           0 :         JAMI_ERR("Parent cert is null!");
    1160             :     }
    1161             : 
    1162             :     // /admins
    1163           6 :     auto adminPath = adminsPath / fmt::format("{}.crt", parentCert->getId());
    1164           2 :     std::ofstream file(adminPath, std::ios::trunc | std::ios::binary);
    1165           2 :     if (!file.is_open()) {
    1166           0 :         JAMI_ERROR("Unable to write data to %s", adminPath.c_str());
    1167             :     }
    1168           2 :     file << parentCert->toString(true);
    1169           2 :     file.close();
    1170             : 
    1171           2 :     if (!dhtnet::fileutils::recursive_mkdir(devicesPath, 0700)) {
    1172           0 :         JAMI_ERR("Error when creating %s. Abort create conversations", devicesPath.c_str());
    1173             :     }
    1174             : 
    1175             :     // /devices
    1176           6 :     auto devicePath = devicesPath / fmt::format("{}.crt", cert->getLongId());
    1177           2 :     file = std::ofstream(devicePath, std::ios::trunc | std::ios::binary);
    1178           2 :     if (!file.is_open()) {
    1179           0 :         JAMI_ERR("Unable to write data to %s", devicePath.c_str());
    1180             :     }
    1181           2 :     file << (fakeCert.empty() ? deviceCert : fakeCert);
    1182           2 :     file.close();
    1183             : 
    1184           2 :     if (!dhtnet::fileutils::recursive_mkdir(crlsPath, 0700)) {
    1185           0 :         JAMI_ERR("Error when creating %s. Abort create conversations", crlsPath.c_str());
    1186             :     }
    1187             : 
    1188           2 :     if (fakeCert.empty()) {
    1189             :         // Add a unwanted file
    1190           1 :         auto badFile = repoPath / "BAD";
    1191           1 :         file = std::ofstream(badFile, std::ios::trunc | std::ios::binary);
    1192           1 :     }
    1193             : 
    1194           2 :     addAll(account, "tmp");
    1195             : 
    1196           2 :     JAMI_INFO("Initial files added in %s", repoPath.c_str());
    1197             : 
    1198           2 :     std::string name = account->getDisplayName();
    1199           2 :     if (name.empty())
    1200           0 :         name = deviceId;
    1201             : 
    1202           2 :     git_signature* sig_ptr = nullptr;
    1203           2 :     git_index* index_ptr = nullptr;
    1204             :     git_oid tree_id, commit_id;
    1205           2 :     git_tree* tree_ptr = nullptr;
    1206           2 :     git_buf to_sign = {};
    1207             : 
    1208             :     // Sign commit's buffer
    1209           2 :     if (git_signature_new(&sig_ptr, name.c_str(), deviceId.c_str(), std::time(nullptr), 0) < 0) {
    1210           0 :         JAMI_ERR("Unable to create a commit signature.");
    1211             :     }
    1212           2 :     GitSignature sig {sig_ptr, git_signature_free};
    1213             : 
    1214           2 :     if (git_repository_index(&index_ptr, repo.get()) < 0) {
    1215           0 :         JAMI_ERR("Unable to open repository index");
    1216             :     }
    1217           2 :     GitIndex index {index_ptr, git_index_free};
    1218             : 
    1219           2 :     if (git_index_write_tree(&tree_id, index.get()) < 0) {
    1220           0 :         JAMI_ERR("Unable to write initial tree from index");
    1221             :     }
    1222             : 
    1223           2 :     if (git_tree_lookup(&tree_ptr, repo.get(), &tree_id) < 0) {
    1224           0 :         JAMI_ERR("Unable to look up initial tree");
    1225             :     }
    1226           2 :     GitTree tree = {tree_ptr, git_tree_free};
    1227             : 
    1228           2 :     Json::Value json;
    1229           2 :     json["mode"] = 1;
    1230           2 :     json["type"] = "initial";
    1231           2 :     Json::StreamWriterBuilder wbuilder;
    1232           2 :     wbuilder["commentStyle"] = "None";
    1233           2 :     wbuilder["indentation"] = "";
    1234             : 
    1235           4 :     if (git_commit_create_buffer(&to_sign,
    1236             :                                  repo.get(),
    1237           2 :                                  sig.get(),
    1238           2 :                                  sig.get(),
    1239             :                                  nullptr,
    1240           4 :                                  Json::writeString(wbuilder, json).c_str(),
    1241           2 :                                  tree.get(),
    1242             :                                  0,
    1243             :                                  nullptr)
    1244           2 :         < 0) {
    1245           0 :         JAMI_ERR("Unable to create initial buffer");
    1246           0 :         return {};
    1247             :     }
    1248             : 
    1249           2 :     auto to_sign_vec = std::vector<uint8_t>(to_sign.ptr, to_sign.ptr + to_sign.size);
    1250           2 :     auto signed_buf = account->identity().first->sign(to_sign_vec);
    1251           2 :     std::string signed_str = base64::encode(signed_buf);
    1252             : 
    1253             :     // git commit -S
    1254           4 :     if (git_commit_create_with_signature(&commit_id,
    1255             :                                          repo.get(),
    1256           2 :                                          to_sign.ptr,
    1257             :                                          signed_str.c_str(),
    1258             :                                          "signature")
    1259           2 :         < 0) {
    1260           0 :         JAMI_ERR("Unable to sign initial commit");
    1261           0 :         return {};
    1262             :     }
    1263             : 
    1264             :     // Move commit to main branch
    1265           2 :     git_commit* commit = nullptr;
    1266           2 :     if (git_commit_lookup(&commit, repo.get(), &commit_id) == 0) {
    1267           2 :         git_reference* ref = nullptr;
    1268           2 :         git_branch_create(&ref, repo.get(), "main", commit, true);
    1269           2 :         git_commit_free(commit);
    1270           2 :         git_reference_free(ref);
    1271             :     }
    1272             : 
    1273           2 :     auto commit_str = git_oid_tostr_s(&commit_id);
    1274             : 
    1275           4 :     auto finalRepo = fileutils::get_data_dir() / account->getAccountID()
    1276           8 :                      / "conversations" / commit_str;
    1277           2 :     std::rename(repoPath.c_str(), finalRepo.c_str());
    1278             : 
    1279          10 :     file = std::ofstream(fileutils::get_data_dir() / account->getAccountID()
    1280           8 :                              / "convInfo",
    1281           2 :                          std::ios::trunc | std::ios::binary);
    1282             : 
    1283           2 :     std::vector<ConvInfoTest> test;
    1284           2 :     test.emplace_back(ConvInfoTest {commit_str, std::time(nullptr), 0, 0});
    1285           2 :     msgpack::pack(file, test);
    1286             : 
    1287           2 :     account->convModule()->loadConversations(); // necessary to load fake conv
    1288             : 
    1289           2 :     return commit_str;
    1290           2 : }
    1291             : 
    1292             : void
    1293           1 : ConversationTest::testVoteNonEmpty()
    1294             : {
    1295           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1296           1 :     connectSignals();
    1297             : 
    1298           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1299           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1300           1 :     auto aliceUri = aliceAccount->getUsername();
    1301           1 :     auto bobUri = bobAccount->getUsername();
    1302           1 :     auto convId = libjami::startConversation(aliceId);
    1303           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
    1304           1 :     auto carlaUri = carlaAccount->getUsername();
    1305           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
    1306             : 
    1307           1 :     Manager::instance().sendRegister(carlaId, true);
    1308           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaData.deviceAnnounced; }));
    1309             : 
    1310           1 :     auto aliceMsgSize = aliceData.messages.size();
    1311           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1312          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); }));
    1313           1 :     libjami::acceptConversationRequest(bobId, convId);
    1314           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
    1315             : 
    1316           1 :     aliceMsgSize = aliceData.messages.size();
    1317           1 :     auto bobMsgSize = bobData.messages.size();
    1318           1 :     libjami::addConversationMember(aliceId, convId, carlaUri);
    1319          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); }));
    1320           1 :     libjami::acceptConversationRequest(carlaId, convId);
    1321           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && bobMsgSize + 2 == bobData.messages.size(); }));
    1322             : 
    1323             :     // Now Alice removes Carla with a non empty file
    1324           1 :     addVote(aliceAccount, convId, carlaUri, "CONTENT");
    1325           1 :     simulateRemoval(aliceAccount, convId, carlaUri);
    1326           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.errorDetected; }));
    1327           1 : }
    1328             : 
    1329             : void
    1330           1 : ConversationTest::testNoBadFileInInitialCommit()
    1331             : {
    1332           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1333           1 :     connectSignals();
    1334             : 
    1335           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1336           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
    1337           1 :     auto aliceUri = aliceAccount->getUsername();
    1338             : 
    1339           2 :     auto convId = createFakeConversation(carlaAccount);
    1340           1 :     Manager::instance().sendRegister(carlaId, true);
    1341           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaData.deviceAnnounced; }));
    1342           1 :     libjami::addConversationMember(carlaId, convId, aliceUri);
    1343           8 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.requestReceived; }));
    1344             : 
    1345           1 :     libjami::acceptConversationRequest(aliceId, convId);
    1346           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.errorDetected; }));
    1347           1 : }
    1348             : 
    1349             : void
    1350           1 : ConversationTest::testNoBadCertInInitialCommit()
    1351             : {
    1352           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1353           1 :     connectSignals();
    1354             : 
    1355           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1356           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
    1357           1 :     auto carlaUri = carlaAccount->getUsername();
    1358           1 :     auto aliceUri = aliceAccount->getUsername();
    1359           1 :     auto fakeCert = aliceAccount->certStore().getCertificate(
    1360           2 :         std::string(aliceAccount->currentDeviceId()));
    1361           1 :     auto carlaCert = carlaAccount->certStore().getCertificate(
    1362           2 :         std::string(carlaAccount->currentDeviceId()));
    1363             : 
    1364           1 :     CPPUNIT_ASSERT(fakeCert);
    1365             :     // Create a conversation from Carla with Alice's device
    1366           2 :     auto convId = createFakeConversation(carlaAccount, fakeCert->toString(false));
    1367             : 
    1368           1 :     Manager::instance().sendRegister(carlaId, true);
    1369           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaData.deviceAnnounced; }));
    1370           1 :     libjami::addConversationMember(carlaId, convId, aliceUri);
    1371           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.requestReceived; }));
    1372             : 
    1373           1 :     libjami::acceptConversationRequest(aliceId, convId);
    1374           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.errorDetected; }));
    1375           1 : }
    1376             : 
    1377             : void
    1378           1 : ConversationTest::testPlainTextNoBadFile()
    1379             : {
    1380           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1381           1 :     connectSignals();
    1382             : 
    1383           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1384           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1385           1 :     auto bobUri = bobAccount->getUsername();
    1386             : 
    1387           1 :     std::string convId = libjami::startConversation(aliceId);
    1388           1 :     auto aliceMsgSize = aliceData.messages.size();
    1389           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1390          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); }));
    1391           1 :     libjami::acceptConversationRequest(bobId, convId);
    1392           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
    1393             : 
    1394           1 :     addFile(aliceAccount, convId, "BADFILE");
    1395           1 :     Json::Value root;
    1396           1 :     root["type"] = "text/plain";
    1397           1 :     root["body"] = "hi";
    1398           1 :     commit(aliceAccount, convId, root);
    1399           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
    1400             :     // Check not received due to the unwanted file
    1401           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; }));
    1402           1 : }
    1403             : 
    1404             : void
    1405           1 : ConversationTest::testVoteNoBadFile()
    1406             : {
    1407           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1408           1 :     connectSignals();
    1409             : 
    1410           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1411           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1412           1 :     auto aliceUri = aliceAccount->getUsername();
    1413           1 :     auto bobUri = bobAccount->getUsername();
    1414           1 :     auto convId = libjami::startConversation(aliceId);
    1415           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
    1416           1 :     auto carlaUri = carlaAccount->getUsername();
    1417           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
    1418             : 
    1419           1 :     Manager::instance().sendRegister(carlaId, true);
    1420           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.deviceAnnounced; }));
    1421             : 
    1422           1 :     auto aliceMsgSize = aliceData.messages.size();
    1423           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1424           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); }));
    1425           1 :     libjami::acceptConversationRequest(bobId, convId);
    1426           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
    1427             : 
    1428           1 :     aliceMsgSize = aliceData.messages.size();
    1429           1 :     auto bobMsgSize = bobData.messages.size();
    1430           1 :     libjami::addConversationMember(aliceId, convId, carlaUri);
    1431          11 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); }));
    1432           1 :     libjami::acceptConversationRequest(carlaId, convId);
    1433           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && bobMsgSize + 2 == bobData.messages.size(); }));
    1434             : 
    1435             :     // Now Alice remove Carla without a vote. Bob will not receive the message
    1436           1 :     addFile(aliceAccount, convId, "BADFILE");
    1437           1 :     aliceMsgSize = aliceData.messages.size();
    1438           1 :     libjami::removeConversationMember(aliceId, convId, bobUri);
    1439           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
    1440             : 
    1441           1 :     auto carlaMsgSize = carlaData.messages.size();
    1442           1 :     libjami::sendMessage(bobId, convId, "final"s, "");
    1443           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaMsgSize + 1 == carlaData.messages.size(); }));
    1444           1 : }
    1445             : 
    1446             : void
    1447           1 : ConversationTest::testETooBigClone()
    1448             : {
    1449           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1450           1 :     connectSignals();
    1451             : 
    1452           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1453           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1454           1 :     auto bobUri = bobAccount->getUsername();
    1455             : 
    1456           1 :     auto convId = libjami::startConversation(aliceId);
    1457             : 
    1458             :     // Assert that repository exists
    1459           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
    1460           4 :                     / "conversations" / convId;
    1461           2 :     std::ofstream bad(repoPath / "BADFILE");
    1462           1 :     CPPUNIT_ASSERT(bad.is_open());
    1463   314572801 :     for (int i = 0; i < 300 * 1024 * 1024; ++i)
    1464   314572800 :         bad << "A";
    1465           1 :     bad.close();
    1466             : 
    1467           1 :     addAll(aliceAccount, convId);
    1468           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1469           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1470           1 :     libjami::acceptConversationRequest(bobId, convId);
    1471           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; }));
    1472           1 : }
    1473             : 
    1474             : void
    1475           1 : ConversationTest::testETooBigFetch()
    1476             : {
    1477           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1478           1 :     connectSignals();
    1479             : 
    1480           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1481           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1482           1 :     auto bobUri = bobAccount->getUsername();
    1483             : 
    1484           1 :     auto convId = libjami::startConversation(aliceId);
    1485             : 
    1486           1 :     auto aliceMsgSize = aliceData.messages.size();
    1487           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1488           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1489             : 
    1490           1 :     libjami::acceptConversationRequest(bobId, convId);
    1491           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
    1492             : 
    1493             :     // Wait that alice sees Bob
    1494           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
    1495             : 
    1496           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
    1497           4 :                     / "conversations" / convId;
    1498           2 :     std::ofstream bad(repoPath / "BADFILE");
    1499           1 :     CPPUNIT_ASSERT(bad.is_open());
    1500   314572801 :     for (int i = 0; i < 300 * 1024 * 1024; ++i)
    1501   314572800 :         bad << "A";
    1502           1 :     bad.close();
    1503             : 
    1504           1 :     addAll(aliceAccount, convId);
    1505           1 :     Json::Value json;
    1506           1 :     json["body"] = "o/";
    1507           1 :     json["type"] = "text/plain";
    1508           1 :     commit(aliceAccount, convId, json);
    1509             : 
    1510           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
    1511           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; }));
    1512           1 : }
    1513             : 
    1514             : void
    1515           1 : ConversationTest::testUnknownModeDetected()
    1516             : {
    1517           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1518           1 :     connectSignals();
    1519             : 
    1520           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1521           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1522           1 :     auto bobUri = bobAccount->getUsername();
    1523           1 :     auto convId = libjami::startConversation(aliceId);
    1524           1 :     ConversationRepository repo(aliceAccount, convId);
    1525           1 :     Json::Value json;
    1526           1 :     json["mode"] = 1412;
    1527           1 :     json["type"] = "initial";
    1528           1 :     Json::StreamWriterBuilder wbuilder;
    1529           1 :     wbuilder["commentStyle"] = "None";
    1530           1 :     wbuilder["indentation"] = "";
    1531           1 :     repo.amend(convId, Json::writeString(wbuilder, json));
    1532           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1533           8 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1534           1 :     libjami::acceptConversationRequest(bobId, convId);
    1535           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; }));
    1536           1 : }
    1537             : 
    1538             : void
    1539           1 : ConversationTest::testUpdateProfile()
    1540             : {
    1541           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1542           1 :     connectSignals();
    1543             : 
    1544           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1545           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1546           1 :     auto bobUri = bobAccount->getUsername();
    1547             : 
    1548           1 :     auto convId = libjami::startConversation(aliceId);
    1549           1 :     auto aliceMsgSize = aliceData.messages.size();
    1550           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1551           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1552           1 :     libjami::acceptConversationRequest(bobId, convId);
    1553           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && !bobData.conversationId.empty(); }));
    1554             : 
    1555           1 :     auto bobMsgSize = bobData.messages.size();
    1556           2 :     aliceAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}});
    1557           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    1558             :         return bobMsgSize + 1 == bobData.messages.size() && !aliceData.profile.empty() && !bobData.profile.empty();
    1559             :     }));
    1560             : 
    1561           1 :     auto infos = libjami::conversationInfos(bobId, convId);
    1562             :     // Verify that we have the same profile everywhere
    1563           1 :     CPPUNIT_ASSERT(infos["title"] == "My awesome swarm");
    1564           1 :     CPPUNIT_ASSERT(aliceData.profile["title"] == "My awesome swarm");
    1565           1 :     CPPUNIT_ASSERT(bobData.profile["title"] == "My awesome swarm");
    1566           1 :     CPPUNIT_ASSERT(infos["description"].empty());
    1567           1 :     CPPUNIT_ASSERT(aliceData.profile["description"].empty());
    1568           1 :     CPPUNIT_ASSERT(bobData.profile["description"].empty());
    1569           1 : }
    1570             : 
    1571             : void
    1572           1 : ConversationTest::testGetProfileRequest()
    1573             : {
    1574           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1575           1 :     connectSignals();
    1576             : 
    1577           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1578           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1579           1 :     auto bobUri = bobAccount->getUsername();
    1580             : 
    1581           1 :     auto convId = libjami::startConversation(aliceId);
    1582           1 :     auto aliceMsgSize = aliceData.messages.size();
    1583           2 :     aliceAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}});
    1584           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
    1585             : 
    1586           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1587          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1588             : 
    1589           1 :     auto infos = libjami::conversationInfos(bobId, convId);
    1590           1 :     CPPUNIT_ASSERT(infos["title"] == "My awesome swarm");
    1591           1 :     CPPUNIT_ASSERT(infos["description"].empty());
    1592           1 : }
    1593             : 
    1594             : void
    1595           1 : ConversationTest::testCheckProfileInConversationRequest()
    1596             : {
    1597           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1598           1 :     connectSignals();
    1599             : 
    1600           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1601           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1602           1 :     auto bobUri = bobAccount->getUsername();
    1603             : 
    1604           1 :     auto convId = libjami::startConversation(aliceId);
    1605           2 :     aliceAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}});
    1606             : 
    1607           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1608          12 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1609           1 :     auto requests = libjami::getConversationRequests(bobId);
    1610           1 :     CPPUNIT_ASSERT(requests.size() == 1);
    1611           1 :     CPPUNIT_ASSERT(requests.front()["title"] == "My awesome swarm");
    1612           1 : }
    1613             : 
    1614             : void
    1615           1 : ConversationTest::testCheckProfileInTrustRequest()
    1616             : {
    1617           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1618           1 :     connectSignals();
    1619             : 
    1620           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1621           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1622           1 :     auto bobUri = bobAccount->getUsername();
    1623           1 :     std::string vcard = "BEGIN:VCARD\n\
    1624             : VERSION:2.1\n\
    1625             : FN:TITLE\n\
    1626             : DESCRIPTION:DESC\n\
    1627             : END:VCARD";
    1628           1 :     aliceAccount->addContact(bobUri);
    1629           1 :     std::vector<uint8_t> payload(vcard.begin(), vcard.end());
    1630           1 :     aliceAccount->sendTrustRequest(bobUri, payload);
    1631           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return bobData.payloadTrustRequest == vcard; }));
    1632           1 : }
    1633             : 
    1634             : void
    1635           1 : ConversationTest::testMemberCannotUpdateProfile()
    1636             : {
    1637           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1638           1 :     connectSignals();
    1639             : 
    1640           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1641           1 :     auto bobUri = bobAccount->getUsername();
    1642             : 
    1643           1 :     auto convId = libjami::startConversation(aliceId);
    1644           1 :     auto aliceMsgSize = aliceData.messages.size();
    1645           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1646           8 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1647           1 :     libjami::acceptConversationRequest(bobId, convId);
    1648           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && !bobData.conversationId.empty(); }));
    1649             : 
    1650           2 :     bobAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}});
    1651           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return bobData.errorDetected; }));
    1652           1 : }
    1653             : 
    1654             : void
    1655           1 : ConversationTest::testUpdateProfileWithBadFile()
    1656             : {
    1657           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1658           1 :     connectSignals();
    1659             : 
    1660           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1661           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1662           1 :     auto bobUri = bobAccount->getUsername();
    1663             : 
    1664           1 :     auto convId = libjami::startConversation(aliceId);
    1665           1 :     auto aliceMsgSize = aliceData.messages.size();
    1666           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1667          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1668           1 :     libjami::acceptConversationRequest(bobId, convId);
    1669           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && !bobData.conversationId.empty(); }));
    1670             : 
    1671             :     // Update profile but with bad file
    1672           1 :     addFile(aliceAccount, convId, "BADFILE");
    1673           1 :     std::string vcard = "BEGIN:VCARD\n\
    1674             : VERSION:2.1\n\
    1675             : FN:TITLE\n\
    1676             : DESCRIPTION:DESC\n\
    1677             : END:VCARD";
    1678           1 :     addFile(aliceAccount, convId, "profile.vcf", vcard);
    1679           1 :     Json::Value root;
    1680           1 :     root["type"] = "application/update-profile";
    1681           1 :     commit(aliceAccount, convId, root);
    1682           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
    1683           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; }));
    1684           1 : }
    1685             : 
    1686             : void
    1687           1 : ConversationTest::testFetchProfileUnauthorized()
    1688             : {
    1689           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1690           1 :     connectSignals();
    1691             : 
    1692           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1693           1 :     auto bobUri = bobAccount->getUsername();
    1694             : 
    1695           1 :     auto convId = libjami::startConversation(aliceId);
    1696           1 :     auto aliceMsgSize = aliceData.messages.size();
    1697           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1698          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1699           1 :     libjami::acceptConversationRequest(bobId, convId);
    1700           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && !bobData.conversationId.empty(); }));
    1701             : 
    1702             :     // Fake realist profile update
    1703           1 :     std::string vcard = "BEGIN:VCARD\n\
    1704             : VERSION:2.1\n\
    1705             : FN:TITLE\n\
    1706             : DESCRIPTION:DESC\n\
    1707             : END:VCARD";
    1708           1 :     addFile(bobAccount, convId, "profile.vcf", vcard);
    1709           1 :     Json::Value root;
    1710           1 :     root["type"] = "application/update-profile";
    1711           1 :     commit(bobAccount, convId, root);
    1712           1 :     libjami::sendMessage(bobId, convId, "hi"s, "");
    1713           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.errorDetected; }));
    1714           1 : }
    1715             : 
    1716             : void
    1717           1 : ConversationTest::testSyncingWhileAccepting()
    1718             : {
    1719           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1720           1 :     connectSignals();
    1721             : 
    1722           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1723           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1724           1 :     auto bobUri = bobAccount->getUsername();
    1725           1 :     auto aliceUri = aliceAccount->getUsername();
    1726             : 
    1727           1 :     aliceAccount->addContact(bobUri);
    1728           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1729           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1730             : 
    1731           1 :     Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately
    1732           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
    1733             : 
    1734           1 :     auto convInfos = libjami::conversationInfos(bobId, aliceData.conversationId);
    1735           1 :     CPPUNIT_ASSERT(convInfos["syncing"] == "true");
    1736           1 :     CPPUNIT_ASSERT(convInfos.find("created") != convInfos.end());
    1737             : 
    1738           1 :     Manager::instance().sendRegister(aliceId, true); // This avoid to sync immediately
    1739           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
    1740             : 
    1741           1 :     convInfos = libjami::conversationInfos(bobId, bobData.conversationId);
    1742           1 :     CPPUNIT_ASSERT(convInfos.find("syncing") == convInfos.end());
    1743           1 : }
    1744             : 
    1745             : void
    1746           1 : ConversationTest::testCountInteractions()
    1747             : {
    1748           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1749             : 
    1750           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1751           1 :     auto convId = libjami::startConversation(aliceId);
    1752             : 
    1753           3 :     std::string msgId1 = "", msgId2 = "", msgId3 = "";
    1754             :     aliceAccount->convModule()
    1755           1 :         ->sendMessage(convId, "1"s, "", "text/plain", true, {}, [&](bool, std::string commitId) {
    1756           1 :             msgId1 = commitId;
    1757           1 :             cv.notify_one();
    1758           1 :         });
    1759           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !msgId1.empty(); }));
    1760             :     aliceAccount->convModule()
    1761           1 :         ->sendMessage(convId, "2"s, "", "text/plain", true, {}, [&](bool, std::string commitId) {
    1762           1 :             msgId2 = commitId;
    1763           1 :             cv.notify_one();
    1764           1 :         });
    1765           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !msgId2.empty(); }));
    1766             :     aliceAccount->convModule()
    1767           1 :         ->sendMessage(convId, "3"s, "", "text/plain", true, {}, [&](bool, std::string commitId) {
    1768           1 :             msgId3 = commitId;
    1769           1 :             cv.notify_one();
    1770           1 :         });
    1771           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !msgId3.empty(); }));
    1772             : 
    1773           1 :     CPPUNIT_ASSERT(libjami::countInteractions(aliceId, convId, "", "", "") == 4 /* 3 + initial */);
    1774           1 :     CPPUNIT_ASSERT(libjami::countInteractions(aliceId, convId, "", "", aliceAccount->getUsername())
    1775             :                    == 0);
    1776           1 :     CPPUNIT_ASSERT(libjami::countInteractions(aliceId, convId, msgId3, "", "") == 0);
    1777           1 :     CPPUNIT_ASSERT(libjami::countInteractions(aliceId, convId, msgId2, "", "") == 1);
    1778           1 : }
    1779             : 
    1780             : void
    1781           1 : ConversationTest::testReplayConversation()
    1782             : {
    1783           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1784           1 :     connectSignals();
    1785             : 
    1786           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1787           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1788           1 :     auto bobUri = bobAccount->getUsername();
    1789           1 :     auto aliceUri = aliceAccount->getUsername();
    1790             : 
    1791           1 :     aliceAccount->addContact(bobUri);
    1792           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1793           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1794           1 :     auto aliceMsgSize = aliceData.messages.size();
    1795           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
    1796           5 :     CPPUNIT_ASSERT(
    1797             :         cv.wait_for(lk, 30s, [&]() {
    1798             :             return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); }));
    1799             :     // removeContact
    1800           1 :     aliceAccount->removeContact(bobUri, false);
    1801           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; }));
    1802           1 :     std::this_thread::sleep_for(5s);
    1803             :     // re-add
    1804           1 :     CPPUNIT_ASSERT(bobData.conversationId != "");
    1805           1 :     auto oldConvId = bobData.conversationId;
    1806           1 :     aliceData.conversationId = "";
    1807           1 :     aliceAccount->addContact(bobUri);
    1808           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }));
    1809           1 :     aliceMsgSize = aliceData.messages.size();
    1810           1 :     libjami::sendMessage(aliceId, aliceData.conversationId, "foo"s, "");
    1811           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
    1812           1 :     libjami::sendMessage(aliceId, aliceData.conversationId, "bar"s, "");
    1813           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
    1814           1 :     bobData.messages.clear();
    1815           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1816             :     // Should retrieve previous conversation
    1817          14 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    1818             :         return bobData.messages.size() == 2 && bobData.messages[0].body["body"] == "foo" && bobData.messages[1].body["body"] == "bar";
    1819             :     }));
    1820           1 : }
    1821             : 
    1822             : void
    1823           1 : ConversationTest::testSyncWithoutPinnedCert()
    1824             : {
    1825           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1826           1 :     connectSignals();
    1827             : 
    1828           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1829           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1830           1 :     auto bobUri = bobAccount->getUsername();
    1831           1 :     auto aliceUri = aliceAccount->getUsername();
    1832             : 
    1833             :     // Bob creates a second device
    1834           2 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
    1835           1 :     std::remove(bobArchive.c_str());
    1836           1 :     bobAccount->exportArchive(bobArchive);
    1837           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    1838           1 :     details[ConfProperties::TYPE] = "RING";
    1839           1 :     details[ConfProperties::DISPLAYNAME] = "BOB2";
    1840           1 :     details[ConfProperties::ALIAS] = "BOB2";
    1841           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    1842           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    1843           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    1844           1 :     details[ConfProperties::ARCHIVE_PATH] = bobArchive;
    1845           1 :     bob2Id = Manager::instance().addAccount(details);
    1846             : 
    1847             :     // Disconnect bob2, to create a valid conv betwen Alice and Bob1
    1848           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.registered; }));
    1849           1 :     Manager::instance().sendRegister(bob2Id, false);
    1850           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.stopped; }));
    1851             : 
    1852             :     // Alice adds bob
    1853           1 :     aliceAccount->addContact(bobUri);
    1854           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1855           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1856           1 :     auto aliceMsgSize = aliceData.messages.size();
    1857           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
    1858           5 :     CPPUNIT_ASSERT(
    1859             :         cv.wait_for(lk, 30s, [&]() {
    1860             :             return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); }));
    1861             : 
    1862             :     // Bob send a message
    1863           1 :     libjami::sendMessage(bobId, bobData.conversationId, "hi"s, "");
    1864           5 :     cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); });
    1865             : 
    1866             :     // Alice off, bob2 On
    1867           1 :     Manager::instance().sendRegister(aliceId, false);
    1868           3 :     cv.wait_for(lk, 10s, [&]() { return aliceData.stopped; });
    1869           1 :     Manager::instance().sendRegister(bob2Id, true);
    1870             : 
    1871             :     // Sync + validate
    1872          12 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bob2Data.conversationId.empty(); }));
    1873           1 : }
    1874             : 
    1875             : void
    1876           1 : ConversationTest::testImportMalformedContacts()
    1877             : {
    1878           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1879             : 
    1880           2 :     auto malformedContacts = fileutils::loadFile(std::filesystem::current_path().string()
    1881           3 :                                                  + "/conversation/rsc/incorrectContacts");
    1882           2 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
    1883           1 :     std::remove(bobArchive.c_str());
    1884           1 :     archiver::compressGzip(malformedContacts, bobArchive);
    1885           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    1886           1 :     details[ConfProperties::TYPE] = "RING";
    1887           1 :     details[ConfProperties::DISPLAYNAME] = "BOB2";
    1888           1 :     details[ConfProperties::ALIAS] = "BOB2";
    1889           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    1890           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    1891           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    1892           1 :     details[ConfProperties::ARCHIVE_PATH] = bobArchive;
    1893           1 :     bob2Id = Manager::instance().addAccount(details);
    1894           1 :     wait_for_announcement_of({bob2Id});
    1895           1 :     auto contacts = libjami::getContacts(bob2Id);
    1896           1 :     CPPUNIT_ASSERT(contacts.size() == 1);
    1897           1 :     CPPUNIT_ASSERT(contacts[0][libjami::Account::TrustRequest::CONVERSATIONID] == "");
    1898           1 : }
    1899             : 
    1900             : void
    1901           1 : ConversationTest::testCloneFromMultipleDevice()
    1902             : {
    1903           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1904           1 :     connectSignals();
    1905             : 
    1906           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1907           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1908           1 :     auto bobUri = bobAccount->getUsername();
    1909             : 
    1910             :     // Bob creates a second device
    1911           2 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
    1912           1 :     std::remove(bobArchive.c_str());
    1913           1 :     bobAccount->exportArchive(bobArchive);
    1914           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    1915           1 :     details[ConfProperties::TYPE] = "RING";
    1916           1 :     details[ConfProperties::DISPLAYNAME] = "BOB2";
    1917           1 :     details[ConfProperties::ALIAS] = "BOB2";
    1918           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    1919           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    1920           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    1921           1 :     details[ConfProperties::ARCHIVE_PATH] = bobArchive;
    1922           1 :     bob2Id = Manager::instance().addAccount(details);
    1923             : 
    1924           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; }));
    1925             : 
    1926             :     // Alice adds bob
    1927           1 :     aliceAccount->addContact(bobUri);
    1928           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1929           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; }));
    1930           1 :     auto aliceMsgSize = aliceData.messages.size();
    1931           1 :     libjami::acceptConversationRequest(bobId, aliceData.conversationId);
    1932           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    1933             :         return !bobData.conversationId.empty() && !bob2Data.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size();
    1934             :     }));
    1935             : 
    1936             :     // Remove contact
    1937           1 :     aliceAccount->removeContact(bobUri, false);
    1938           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; }));
    1939             : 
    1940             :     // wait that connections are closed.
    1941           1 :     std::this_thread::sleep_for(10s);
    1942             : 
    1943             :     // Alice re-adds Bob
    1944           1 :     auto oldConv = bobData.conversationId;
    1945           1 :     aliceAccount->addContact(bobUri);
    1946           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1947             :     // This should retrieve the conversation from Bob and don't show any error
    1948           6 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return aliceData.errorDetected; }));
    1949           1 :     CPPUNIT_ASSERT(oldConv == aliceData.conversationId); // Check that convId didn't change and conversation is ready.
    1950           1 : }
    1951             : 
    1952             : void
    1953           1 : ConversationTest::testSendReply()
    1954             : {
    1955           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1956           1 :     connectSignals();
    1957             : 
    1958           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1959           1 :     auto bobUri = bobAccount->getUsername();
    1960           1 :     auto convId = libjami::startConversation(aliceId);
    1961             : 
    1962           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    1963          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1964           1 :     auto aliceMsgSize = aliceData.messages.size();
    1965           1 :     libjami::acceptConversationRequest(bobId, convId);
    1966           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); }));
    1967             : 
    1968           1 :     auto bobMsgSize = bobData.messages.size();
    1969           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
    1970           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 1; }));
    1971             : 
    1972           1 :     auto validId = bobData.messages.at(0).id;
    1973           1 :     libjami::sendMessage(aliceId, convId, "foo"s, validId);
    1974           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return bobData.messages.size() == bobMsgSize + 2; }));
    1975           1 :     CPPUNIT_ASSERT(bobData.messages.rbegin()->body.at("reply-to") == validId);
    1976             : 
    1977             :     // Check if parent doesn't exists, no message is generated
    1978           1 :     libjami::sendMessage(aliceId, convId, "foo"s, "invalid");
    1979           3 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobData.messages.size() == bobMsgSize + 3; }));
    1980           1 : }
    1981             : 
    1982             : void
    1983           1 : ConversationTest::testSearchInConv()
    1984             : {
    1985           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1986           1 :     connectSignals();
    1987             : 
    1988           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1989           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1990           1 :     auto bobUri = bobAccount->getUsername();
    1991           1 :     auto aliceUri = aliceAccount->getUsername();
    1992             : 
    1993           1 :     auto aliceMsgSize = aliceData.messages.size();
    1994           1 :     aliceAccount->addContact(bobUri);
    1995           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1996           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1997           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
    1998           5 :     CPPUNIT_ASSERT(
    1999             :         cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); }));
    2000             :     // Add some messages
    2001           1 :     auto bobMsgSize = bobData.messages.size();
    2002           1 :     libjami::sendMessage(aliceId, aliceData.conversationId, "message 1"s, "");
    2003           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
    2004           1 :     libjami::sendMessage(aliceId, aliceData.conversationId, "message 2"s, "");
    2005           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 2 == bobData.messages.size(); }));
    2006           1 :     libjami::sendMessage(aliceId, aliceData.conversationId, "Message 3"s, "");
    2007           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 3 == bobData.messages.size(); }));
    2008             : 
    2009           1 :     libjami::searchConversation(aliceId, aliceData.conversationId, "", "", "message", "", 0, 0, 0, 0);
    2010           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messagesFound.size() == 3 && aliceData.searchFinished; }));
    2011           1 :     aliceData.messagesFound.clear();
    2012           1 :     aliceData.searchFinished = false;
    2013           1 :     libjami::searchConversation(aliceId, aliceData.conversationId, "", "", "Message", "", 0, 0, 0, 1);
    2014           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messagesFound.size() == 1 && aliceData.searchFinished; }));
    2015           1 :     aliceData.messagesFound.clear();
    2016           1 :     aliceData.searchFinished = false;
    2017           1 :     libjami::searchConversation(aliceId, aliceData.conversationId, "", "", "message 2", "", 0, 0, 0, 0);
    2018           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messagesFound.size() == 1 && aliceData.searchFinished; }));
    2019           1 :     aliceData.messagesFound.clear();
    2020           1 :     aliceData.searchFinished = false;
    2021           1 :     libjami::searchConversation(aliceId, aliceData.conversationId, "", "", "foo", "", 0, 0, 0, 0);
    2022           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messagesFound.size() == 0 && aliceData.searchFinished; }));
    2023           1 : }
    2024             : 
    2025             : void
    2026           1 : ConversationTest::testConversationPreferences()
    2027             : {
    2028           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2029           1 :     connectSignals();
    2030             : 
    2031             :     // Start conversation and set preferences
    2032           1 :     auto convId = libjami::startConversation(aliceId);
    2033           2 :     cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); });
    2034           1 :     CPPUNIT_ASSERT(libjami::getConversationPreferences(aliceId, convId).size() == 0);
    2035           2 :     libjami::setConversationPreferences(aliceId, convId, {{"foo", "bar"}});
    2036           1 :     auto preferences = libjami::getConversationPreferences(aliceId, convId);
    2037           1 :     CPPUNIT_ASSERT(preferences.size() == 1);
    2038           1 :     CPPUNIT_ASSERT(preferences["foo"] == "bar");
    2039             :     // Update
    2040           3 :     libjami::setConversationPreferences(aliceId, convId, {{"foo", "bar2"}, {"bar", "foo"}});
    2041           1 :     preferences = libjami::getConversationPreferences(aliceId, convId);
    2042           1 :     CPPUNIT_ASSERT(preferences.size() == 2);
    2043           1 :     CPPUNIT_ASSERT(preferences["foo"] == "bar2");
    2044           1 :     CPPUNIT_ASSERT(preferences["bar"] == "foo");
    2045             :     // Remove conversations removes its preferences.
    2046           1 :     CPPUNIT_ASSERT(libjami::removeConversation(aliceId, convId));
    2047           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; }));
    2048           1 :     CPPUNIT_ASSERT(libjami::getConversationPreferences(aliceId, convId).size() == 0);
    2049           1 : }
    2050             : 
    2051             : void
    2052           1 : ConversationTest::testConversationPreferencesBeforeClone()
    2053             : {
    2054           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2055           1 :     connectSignals();
    2056             : 
    2057           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    2058           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    2059           1 :     auto bobUri = bobAccount->getUsername();
    2060             :     // Bob creates a second device
    2061           2 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
    2062           1 :     std::remove(bobArchive.c_str());
    2063           1 :     bobAccount->exportArchive(bobArchive);
    2064             :     // Alice adds bob
    2065           1 :     aliceAccount->addContact(bobUri);
    2066           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    2067           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    2068           1 :     libjami::acceptConversationRequest(bobId, aliceData.conversationId);
    2069           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
    2070             : 
    2071             :     // Set preferences
    2072           1 :     Manager::instance().sendRegister(aliceId, false);
    2073           3 :     libjami::setConversationPreferences(bobId, bobData.conversationId, {{"foo", "bar"}, {"bar", "foo"}});
    2074           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.preferences.size() == 2; }));
    2075           1 :     CPPUNIT_ASSERT(bobData.preferences["foo"] == "bar" && bobData.preferences["bar"] == "foo");
    2076             : 
    2077             :     // Bob2 should sync preferences
    2078           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    2079           1 :     details[ConfProperties::TYPE] = "RING";
    2080           1 :     details[ConfProperties::DISPLAYNAME] = "BOB2";
    2081           1 :     details[ConfProperties::ALIAS] = "BOB2";
    2082           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    2083           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    2084           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    2085           1 :     details[ConfProperties::ARCHIVE_PATH] = bobArchive;
    2086           1 :     bob2Id = Manager::instance().addAccount(details);
    2087          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    2088             :         return bob2Data.deviceAnnounced && !bob2Data.conversationId.empty() && !bob2Data.preferences.empty();
    2089             :     }));
    2090           1 :     CPPUNIT_ASSERT(bob2Data.preferences["foo"] == "bar" && bob2Data.preferences["bar"] == "foo");
    2091           1 : }
    2092             : 
    2093             : void
    2094           1 : ConversationTest::testConversationPreferencesMultiDevices()
    2095             : {
    2096           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2097           1 :     connectSignals();
    2098             : 
    2099           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    2100           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    2101           1 :     auto bobUri = bobAccount->getUsername();
    2102             :     // Bob creates a second device
    2103           2 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
    2104           1 :     std::remove(bobArchive.c_str());
    2105           1 :     bobAccount->exportArchive(bobArchive);
    2106           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    2107           1 :     details[ConfProperties::TYPE] = "RING";
    2108           1 :     details[ConfProperties::DISPLAYNAME] = "BOB2";
    2109           1 :     details[ConfProperties::ALIAS] = "BOB2";
    2110           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    2111           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    2112           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    2113           1 :     details[ConfProperties::ARCHIVE_PATH] = bobArchive;
    2114           1 :     bob2Id = Manager::instance().addAccount(details);
    2115           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; }));
    2116             :     // Alice adds bob
    2117           1 :     aliceAccount->addContact(bobUri);
    2118           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    2119           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; }));
    2120           1 :     libjami::acceptConversationRequest(bobId, aliceData.conversationId);
    2121           6 :     CPPUNIT_ASSERT(
    2122             :         cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && !bob2Data.conversationId.empty(); }));
    2123           3 :     libjami::setConversationPreferences(bobId, bobData.conversationId, {{"foo", "bar"}, {"bar", "foo"}});
    2124           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    2125             :         return bobData.preferences.size() == 2 && bob2Data.preferences.size() == 2;
    2126             :     }));
    2127           1 :     CPPUNIT_ASSERT(bobData.preferences["foo"] == "bar" && bobData.preferences["bar"] == "foo");
    2128           1 :     CPPUNIT_ASSERT(bob2Data.preferences["foo"] == "bar" && bob2Data.preferences["bar"] == "foo");
    2129           1 : }
    2130             : 
    2131             : void
    2132           1 : ConversationTest::testFixContactDetails()
    2133             : {
    2134           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2135           1 :     connectSignals();
    2136             : 
    2137           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    2138           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    2139           1 :     auto bobUri = bobAccount->getUsername();
    2140             : 
    2141           1 :     aliceAccount->addContact(bobUri);
    2142           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return !aliceData.conversationId.empty(); }));
    2143             : 
    2144           1 :     auto details = aliceAccount->getContactDetails(bobUri);
    2145           1 :     CPPUNIT_ASSERT(details["conversationId"] == aliceData.conversationId);
    2146             :     // Erase convId from contact details, this should be fixed by next reload.
    2147           1 :     CPPUNIT_ASSERT(aliceAccount->convModule()->updateConvForContact(bobUri, aliceData.conversationId, ""));
    2148           1 :     details = aliceAccount->getContactDetails(bobUri);
    2149           1 :     CPPUNIT_ASSERT(details["conversationId"].empty());
    2150             : 
    2151           1 :     aliceAccount->convModule()->loadConversations();
    2152             : 
    2153           1 :     std::this_thread::sleep_for(5s); // Let the daemon fix the structures
    2154             : 
    2155           1 :     details = aliceAccount->getContactDetails(bobUri);
    2156           1 :     CPPUNIT_ASSERT(details["conversationId"] == aliceData.conversationId);
    2157           1 : }
    2158             : 
    2159             : void
    2160           1 : ConversationTest::testRemoveOneToOneNotInDetails()
    2161             : {
    2162           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2163           1 :     connectSignals();
    2164             : 
    2165           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    2166           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    2167           1 :     auto bobUri = bobAccount->getUsername();
    2168             : 
    2169           1 :     aliceAccount->addContact(bobUri);
    2170           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return !aliceData.conversationId.empty(); }));
    2171             : 
    2172           1 :     auto details = aliceAccount->getContactDetails(bobUri);
    2173           1 :     CPPUNIT_ASSERT(details["conversationId"] == aliceData.conversationId);
    2174           1 :     auto firstConv = aliceData.conversationId;
    2175             :     // Create a duplicate
    2176           1 :     std::this_thread::sleep_for(2s); // Avoid to get same id
    2177           1 :     aliceAccount->convModule()->startConversation(ConversationMode::ONE_TO_ONE, dht::InfoHash(bobUri));
    2178           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return firstConv != aliceData.conversationId; }));
    2179             : 
    2180             :     // Assert that repository exists
    2181           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
    2182           4 :                     / "conversations" / aliceData.conversationId;
    2183           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
    2184             : 
    2185           1 :     aliceAccount->convModule()->loadConversations();
    2186             : 
    2187             :     // Check that conv is removed
    2188           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; }));
    2189           1 : }
    2190             : 
    2191             : void
    2192           1 : ConversationTest::testMessageEdition()
    2193             : {
    2194           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2195           1 :     connectSignals();
    2196             : 
    2197           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    2198           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    2199           1 :     auto bobUri = bobAccount->getUsername();
    2200           1 :     auto convId = libjami::startConversation(aliceId);
    2201           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
    2202           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    2203             : 
    2204           1 :     auto aliceMsgSize = aliceData.messages.size();
    2205           1 :     libjami::acceptConversationRequest(bobId, convId);
    2206           5 :     CPPUNIT_ASSERT(
    2207             :         cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceData.messages.size() == aliceMsgSize + 1; }));
    2208             : 
    2209           1 :     auto bobMsgSize = bobData.messages.size();
    2210           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
    2211           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 1; }));
    2212           1 :     auto editedId = bobData.messages.rbegin()->id;
    2213             :     // Should trigger MessageUpdated
    2214           1 :     bobMsgSize = bobData.messagesUpdated.size();
    2215           1 :     libjami::sendMessage(aliceId, convId, "New body"s, editedId, 1);
    2216           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return bobData.messagesUpdated.size() == bobMsgSize + 1; }));
    2217           1 :     CPPUNIT_ASSERT(bobData.messagesUpdated.rbegin()->body.at("body") == "New body");
    2218             :     // Not an existing message
    2219           1 :     bobMsgSize = bobData.messagesUpdated.size();
    2220           1 :     libjami::sendMessage(aliceId, convId, "New body"s, "invalidId", 1);
    2221           3 :     CPPUNIT_ASSERT(
    2222             :         !cv.wait_for(lk, 10s, [&]() { return bobData.messagesUpdated.size() == bobMsgSize + 1; }));
    2223             :     // Invalid author
    2224           1 :     libjami::sendMessage(aliceId, convId, "New body"s, convId, 1);
    2225           3 :     CPPUNIT_ASSERT(
    2226             :         !cv.wait_for(lk, 10s, [&]() { return bobData.messagesUpdated.size() == bobMsgSize + 1; }));
    2227             :     // Add invalid edition
    2228           1 :     Json::Value root;
    2229           1 :     root["type"] = "text/plain";
    2230           1 :     root["edit"] = convId;
    2231           1 :     root["body"] = "new";
    2232           1 :     Json::StreamWriterBuilder wbuilder;
    2233           1 :     wbuilder["commentStyle"] = "None";
    2234           1 :     wbuilder["indentation"] = "";
    2235           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
    2236           4 :                     / "conversations" / convId;
    2237           1 :     auto message = Json::writeString(wbuilder, root);
    2238           1 :     commitInRepo(repoPath, aliceAccount, message);
    2239           1 :     bobData.errorDetected = false;
    2240           1 :     libjami::sendMessage(aliceId, convId, "trigger"s, "");
    2241           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; }));
    2242             : 
    2243           1 : }
    2244             : 
    2245             : void
    2246           1 : ConversationTest::testMessageReaction()
    2247             : {
    2248           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2249           1 :     connectSignals();
    2250           1 :     auto convId = libjami::startConversation(aliceId);
    2251           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }));
    2252           1 :     auto msgSize = aliceData.messages.size();
    2253           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
    2254           3 :     CPPUNIT_ASSERT(
    2255             :         cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == msgSize + 1; }));
    2256           1 :     msgSize = aliceData.messages.size();
    2257             : 
    2258             :     // Add reaction
    2259           1 :     auto reactId = aliceData.messages.rbegin()->id;
    2260           1 :     libjami::sendMessage(aliceId, convId, "👋"s, reactId, 2);
    2261           3 :     CPPUNIT_ASSERT(
    2262             :         cv.wait_for(lk, 10s, [&]() { return aliceData.reactions.size() == 1; }));
    2263           1 :     CPPUNIT_ASSERT(aliceData.reactions.rbegin()->at("react-to") == reactId);
    2264           1 :     CPPUNIT_ASSERT(aliceData.reactions.rbegin()->at("body") == "👋");
    2265           2 :     auto emojiId = aliceData.reactions.rbegin()->at("id");
    2266             : 
    2267             :     // Remove reaction
    2268           1 :     libjami::sendMessage(aliceId, convId, ""s, emojiId, 1);
    2269           3 :     CPPUNIT_ASSERT(
    2270             :         cv.wait_for(lk, 10s, [&]() { return aliceData.reactionRemoved.size() == 1; }));
    2271           1 :     CPPUNIT_ASSERT(emojiId == aliceData.reactionRemoved[0]);
    2272           1 : }
    2273             : 
    2274             : void
    2275           1 : ConversationTest::testMessageEditionWithReaction()
    2276             : {
    2277           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2278           1 :     connectSignals();
    2279           1 :     auto convId = libjami::startConversation(aliceId);
    2280           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }));
    2281           1 :     auto msgSize = aliceData.messages.size();
    2282           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
    2283           3 :     CPPUNIT_ASSERT(
    2284             :         cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == msgSize + 1; }));
    2285           1 :     msgSize = aliceData.messages.size();
    2286             : 
    2287             :     // Add reaction
    2288           1 :     auto reactId = aliceData.messages.rbegin()->id;
    2289           1 :     libjami::sendMessage(aliceId, convId, "👋"s, reactId, 2);
    2290           3 :     CPPUNIT_ASSERT(
    2291             :         cv.wait_for(lk, 10s, [&]() { return aliceData.reactions.size() == 1; }));
    2292           1 :     CPPUNIT_ASSERT(aliceData.reactions.rbegin()->at("react-to") == reactId);
    2293           1 :     CPPUNIT_ASSERT(aliceData.reactions.rbegin()->at("body") == "👋");
    2294           2 :     auto emojiId = aliceData.reactions.rbegin()->at("id");
    2295             : 
    2296             :     // Remove base message should remove reaction
    2297           1 :     libjami::sendMessage(aliceId, convId, ""s, reactId, 1);
    2298           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return aliceData.messagesUpdated.size() == 1; }));
    2299             :     // Reaction is deleted
    2300           1 :     CPPUNIT_ASSERT(aliceData.messagesUpdated[0].reactions.empty());
    2301           1 : }
    2302             : 
    2303             : void
    2304           1 : ConversationTest::testLoadPartiallyRemovedConversation()
    2305             : {
    2306           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2307           1 :     connectSignals();
    2308             : 
    2309           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    2310           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    2311           1 :     auto bobUri = bobAccount->getUsername();
    2312             : 
    2313           1 :     aliceAccount->addContact(bobUri);
    2314           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    2315           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    2316             : 
    2317             :     // Copy alice's conversation temporary
    2318           2 :     auto repoPathAlice = fileutils::get_data_dir() / aliceId / "conversations" / aliceData.conversationId;
    2319           2 :     std::filesystem::copy(repoPathAlice, fmt::format("./{}", aliceData.conversationId), std::filesystem::copy_options::recursive);
    2320             : 
    2321             :     // removeContact
    2322           1 :     aliceAccount->removeContact(bobUri, false);
    2323           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; }));
    2324           1 :     std::this_thread::sleep_for(10s); // Wait for connection to close and async tasks to finish
    2325             : 
    2326             :     // Copy back alice's conversation
    2327           2 :     std::filesystem::copy(fmt::format("./{}", aliceData.conversationId), repoPathAlice, std::filesystem::copy_options::recursive);
    2328           2 :     std::filesystem::remove_all(fmt::format("./{}", aliceData.conversationId));
    2329             : 
    2330             :     // Reloading conversation should remove directory
    2331           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPathAlice));
    2332           1 :     aliceAccount->convModule()->loadConversations();
    2333           1 :     std::this_thread::sleep_for(5s); // Let the daemon the time to fix structures
    2334           1 :     CPPUNIT_ASSERT(!std::filesystem::is_directory(repoPathAlice));
    2335           1 : }
    2336             : 
    2337             : void
    2338           1 : ConversationTest::testReactionsOnEditedMessage()
    2339             : {
    2340           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2341           1 :     connectSignals();
    2342           1 :     auto convId = libjami::startConversation(aliceId);
    2343           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }));
    2344           1 :     auto msgSize = aliceData.messages.size();
    2345           1 :     libjami::sendMessage(aliceId, convId, "hi"s, "");
    2346           3 :     CPPUNIT_ASSERT(
    2347             :         cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == msgSize + 1; }));
    2348           1 :     msgSize = aliceData.messages.size();
    2349             : 
    2350             :     // Add reaction
    2351           1 :     auto reactId = aliceData.messages.rbegin()->id;
    2352           1 :     libjami::sendMessage(aliceId, convId, "👋"s, reactId, 2);
    2353           3 :     CPPUNIT_ASSERT(
    2354             :         cv.wait_for(lk, 10s, [&]() { return aliceData.reactions.size() == 1; }));
    2355           1 :     CPPUNIT_ASSERT(aliceData.reactions.rbegin()->at("react-to") == reactId);
    2356           1 :     CPPUNIT_ASSERT(aliceData.reactions.rbegin()->at("body") == "👋");
    2357           2 :     auto emojiId = aliceData.reactions.rbegin()->at("id");
    2358             : 
    2359             :     // Edit message
    2360           1 :     aliceData.messagesUpdated.clear();
    2361           1 :     libjami::sendMessage(aliceId, convId, "EDITED"s, reactId, 1);
    2362             : 
    2363           3 :     CPPUNIT_ASSERT(
    2364             :         cv.wait_for(lk, 10s, [&]() { return aliceData.messagesUpdated.size() == 1; }));
    2365             : 
    2366             :     // Reaction is kept
    2367           1 :     CPPUNIT_ASSERT(emojiId == aliceData.messagesUpdated[0].reactions[0]["id"]);
    2368           1 : }
    2369             : 
    2370             : void
    2371           1 : ConversationTest::testUpdateProfileMultiDevice()
    2372             : {
    2373           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    2374           1 :     connectSignals();
    2375             : 
    2376           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    2377             : 
    2378             :     // Bob creates a second device
    2379           2 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
    2380           1 :     std::remove(bobArchive.c_str());
    2381           1 :     bobAccount->exportArchive(bobArchive);
    2382           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    2383           1 :     details[ConfProperties::TYPE] = "RING";
    2384           1 :     details[ConfProperties::DISPLAYNAME] = "BOB2";
    2385           1 :     details[ConfProperties::ALIAS] = "BOB2";
    2386           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    2387           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    2388           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    2389           1 :     details[ConfProperties::ARCHIVE_PATH] = bobArchive;
    2390           1 :     bob2Id = Manager::instance().addAccount(details);
    2391           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.registered; }));
    2392             : 
    2393             :     // Bob creates a conversation
    2394           1 :     auto convId = libjami::startConversation(bobId);
    2395           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bob2Data.conversationId.empty(); }));
    2396             : 
    2397             : 
    2398           1 :     auto bobMsgSize = bobData.messages.size();
    2399           1 :     auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id);
    2400           2 :     bob2Account->convModule()->updateConversationInfos(bob2Data.conversationId, {{"title", "My awesome swarm"}});
    2401           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
    2402             : 
    2403           1 : }
    2404             : 
    2405             : } // namespace test
    2406             : } // namespace jami
    2407             : 
    2408           1 : RING_TEST_RUNNER(jami::test::ConversationTest::name())

Generated by: LCOV version 1.14