LCOV - code coverage report
Current view: top level - test/unitTest/conversation - conversation.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 0 1598 0.0 %
Date: 2024-04-26 09:41:19 Functions: 0 266 0.0 %

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

Generated by: LCOV version 1.14