LCOV - code coverage report
Current view: top level - test/unitTest/conversation - conversationRequest.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 761 779 97.7 %
Date: 2024-04-30 08:52:06 Functions: 127 128 99.2 %

          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 "manager.h"
      32             : #include "jamidht/conversation.h"
      33             : #include "jamidht/conversationrepository.h"
      34             : #include "jamidht/jamiaccount.h"
      35             : #include "../../test_runner.h"
      36             : #include "jami.h"
      37             : #include "base64.h"
      38             : #include "fileutils.h"
      39             : #include "account_const.h"
      40             : #include "common.h"
      41             : 
      42             : using namespace std::string_literals;
      43             : using namespace std::literals::chrono_literals;
      44             : using namespace libjami::Account;
      45             : 
      46             : namespace jami {
      47             : namespace test {
      48             : 
      49             : struct UserData {
      50             :     std::string conversationId;
      51             :     bool removed {false};
      52             :     bool requestReceived {false};
      53             :     bool requestRemoved {false};
      54             :     bool registered {false};
      55             :     bool stopped {false};
      56             :     bool deviceAnnounced {false};
      57             :     bool contactAdded {false};
      58             :     bool contactRemoved {false};
      59             :     std::string profilePath;
      60             :     std::string payloadTrustRequest;
      61             :     std::vector<libjami::SwarmMessage> messages;
      62             :     std::vector<libjami::SwarmMessage> messagesUpdated;
      63             : };
      64             : 
      65             : class ConversationRequestTest : public CppUnit::TestFixture
      66             : {
      67             : public:
      68          52 :     ~ConversationRequestTest() { libjami::fini(); }
      69           2 :     static std::string name() { return "ConversationRequest"; }
      70             :     void setUp();
      71             :     void tearDown();
      72             : 
      73             :     void testAcceptTrustRemoveConvReq();
      74             :     void acceptConvReqAlsoAddContact();
      75             :     void testGetRequests();
      76             :     void testDeclineRequest();
      77             :     void testAddContact();
      78             :     void testDeclineConversationRequestRemoveTrustRequest();
      79             :     void testMalformedTrustRequest();
      80             :     void testAddContactDeleteAndReAdd();
      81             :     void testRemoveContact();
      82             :     void testRemoveContactMultiDevice();
      83             :     void testRemoveSelfDoesntRemoveConversation();
      84             :     void testRemoveConversationUpdateContactDetails();
      85             :     void testBanContact();
      86             :     void testBanContactRestartAccount();
      87             :     void testBanContactRemoveTrustRequest();
      88             :     void testAddOfflineContactThenConnect();
      89             :     void testDeclineTrustRequestDoNotGenerateAnother();
      90             :     void testRemoveContactRemoveSyncing();
      91             :     void testRemoveConversationRemoveSyncing();
      92             :     void testCacheRequestFromClient();
      93             :     void testNeedsSyncingWithForCloning();
      94             :     void testRemoveContactRemoveTrustRequest();
      95             :     void testAddConversationNoPresenceThenConnects();
      96             :     void testRequestBigPayload();
      97             :     void testBothRemoveReadd();
      98             :     void doNotLooseMetadata();
      99             :     std::string aliceId;
     100             :     UserData aliceData;
     101             :     std::string bobId;
     102             :     UserData bobData;
     103             :     std::string bob2Id;
     104             :     UserData bob2Data;
     105             :     std::string carlaId;
     106             :     UserData carlaData;
     107             : 
     108             :     std::mutex mtx;
     109             :     std::unique_lock<std::mutex> lk {mtx};
     110             :     std::condition_variable cv;
     111             : 
     112             :     void connectSignals();
     113             : 
     114             : private:
     115           2 :     CPPUNIT_TEST_SUITE(ConversationRequestTest);
     116           1 :     CPPUNIT_TEST(testAcceptTrustRemoveConvReq);
     117           1 :     CPPUNIT_TEST(acceptConvReqAlsoAddContact);
     118           1 :     CPPUNIT_TEST(testGetRequests);
     119           1 :     CPPUNIT_TEST(testDeclineRequest);
     120           1 :     CPPUNIT_TEST(testAddContact);
     121           1 :     CPPUNIT_TEST(testDeclineConversationRequestRemoveTrustRequest);
     122           1 :     CPPUNIT_TEST(testMalformedTrustRequest);
     123           1 :     CPPUNIT_TEST(testAddContactDeleteAndReAdd);
     124           1 :     CPPUNIT_TEST(testRemoveContact);
     125           1 :     CPPUNIT_TEST(testRemoveContactMultiDevice);
     126           1 :     CPPUNIT_TEST(testRemoveSelfDoesntRemoveConversation);
     127           1 :     CPPUNIT_TEST(testRemoveConversationUpdateContactDetails);
     128           1 :     CPPUNIT_TEST(testBanContact);
     129           1 :     CPPUNIT_TEST(testBanContactRestartAccount);
     130           1 :     CPPUNIT_TEST(testBanContactRemoveTrustRequest);
     131           1 :     CPPUNIT_TEST(testAddOfflineContactThenConnect);
     132           1 :     CPPUNIT_TEST(testDeclineTrustRequestDoNotGenerateAnother);
     133           1 :     CPPUNIT_TEST(testRemoveContactRemoveSyncing);
     134           1 :     CPPUNIT_TEST(testRemoveConversationRemoveSyncing);
     135           1 :     CPPUNIT_TEST(testCacheRequestFromClient);
     136           1 :     CPPUNIT_TEST(testNeedsSyncingWithForCloning);
     137           1 :     CPPUNIT_TEST(testRemoveContactRemoveTrustRequest);
     138           1 :     CPPUNIT_TEST(testAddConversationNoPresenceThenConnects);
     139           1 :     CPPUNIT_TEST(testRequestBigPayload);
     140           1 :     CPPUNIT_TEST(testBothRemoveReadd);
     141           1 :     CPPUNIT_TEST(doNotLooseMetadata);
     142           4 :     CPPUNIT_TEST_SUITE_END();
     143             : };
     144             : 
     145             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ConversationRequestTest, ConversationRequestTest::name());
     146             : 
     147             : void
     148          26 : ConversationRequestTest::setUp()
     149             : {
     150             :     // Init daemon
     151          26 :     libjami::init(
     152             :         libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
     153          26 :     if (not Manager::instance().initialized)
     154           1 :         CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
     155             : 
     156          26 :     auto actors = load_actors("actors/alice-bob-carla.yml");
     157          26 :     aliceId = actors["alice"];
     158          26 :     bobId = actors["bob"];
     159          26 :     carlaId = actors["carla"];
     160          26 :     aliceData = {};
     161          26 :     bobData = {};
     162          26 :     bob2Data = {};
     163          26 :     carlaData = {};
     164             : 
     165          26 :     Manager::instance().sendRegister(carlaId, false);
     166          78 :     wait_for_announcement_of({aliceId, bobId});
     167          26 : }
     168             : void
     169          24 : ConversationRequestTest::connectSignals()
     170             : {
     171          24 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     172          24 :     confHandlers.insert(
     173          48 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
     174          21 :             [&](const std::string& accountId, const std::map<std::string, std::string>&) {
     175          21 :                 if (accountId == aliceId) {
     176           4 :                     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     177           4 :                     auto details = aliceAccount->getVolatileAccountDetails();
     178           8 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     179           4 :                     if (daemonStatus == "REGISTERED") {
     180           0 :                         aliceData.registered = true;
     181           4 :                     } else if (daemonStatus == "UNREGISTERED") {
     182           4 :                         aliceData.stopped = true;
     183             :                     }
     184           8 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     185           4 :                     aliceData.deviceAnnounced = deviceAnnounced == "true";
     186          21 :                 } else if (accountId == bobId) {
     187           4 :                     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     188           4 :                     auto details = bobAccount->getVolatileAccountDetails();
     189           8 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     190           4 :                     if (daemonStatus == "REGISTERED") {
     191           2 :                         bobData.registered = true;
     192           2 :                     } else if (daemonStatus == "UNREGISTERED") {
     193           1 :                         bobData.stopped = true;
     194             :                     }
     195           8 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     196           4 :                     bobData.deviceAnnounced = deviceAnnounced == "true";
     197          17 :                 } else if (accountId == bob2Id) {
     198           8 :                     auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id);
     199           8 :                     auto details = bob2Account->getVolatileAccountDetails();
     200          16 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     201           8 :                     if (daemonStatus == "REGISTERED") {
     202           4 :                         bob2Data.registered = true;
     203           4 :                     } else if (daemonStatus == "UNREGISTERED") {
     204           2 :                         bob2Data.stopped = true;
     205             :                     }
     206          16 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     207           8 :                     bob2Data.deviceAnnounced = deviceAnnounced == "true";
     208          13 :                 } else if (accountId == carlaId) {
     209           3 :                     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     210           3 :                     auto details = carlaAccount->getVolatileAccountDetails();
     211           6 :                     auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     212           3 :                     if (daemonStatus == "REGISTERED") {
     213           2 :                         carlaData.registered = true;
     214           1 :                     } else if (daemonStatus == "UNREGISTERED") {
     215           0 :                         carlaData.stopped = true;
     216             :                     }
     217           6 :                     auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     218           3 :                     carlaData.deviceAnnounced = deviceAnnounced == "true";
     219           3 :                 }
     220          21 :                 cv.notify_one();
     221          21 :             }));
     222          24 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     223          44 :         [&](const std::string& accountId, const std::string& conversationId) {
     224          44 :             if (accountId == aliceId) {
     225          28 :                 aliceData.conversationId = conversationId;
     226          16 :             } else if (accountId == bobId) {
     227          14 :                 bobData.conversationId = conversationId;
     228           2 :             } else if (accountId == bob2Id) {
     229           1 :                 bob2Data.conversationId = conversationId;
     230           1 :             } else if (accountId == carlaId) {
     231           1 :                 carlaData.conversationId = conversationId;
     232             :             }
     233          44 :             cv.notify_one();
     234          44 :         }));
     235          24 :     confHandlers.insert(
     236          48 :         libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>(
     237          24 :             [&](const std::string& account_id,
     238             :                 const std::string& /*from*/,
     239             :                 const std::string& /*conversationId*/,
     240             :                 const std::vector<uint8_t>& payload,
     241             :                 time_t /*received*/) {
     242          24 :                 auto payloadStr = std::string(payload.data(), payload.data() + payload.size());
     243          24 :                 if (account_id == aliceId)
     244           0 :                     aliceData.payloadTrustRequest = payloadStr;
     245          24 :                 else if (account_id == bobId)
     246          21 :                     bobData.payloadTrustRequest = payloadStr;
     247          24 :                 cv.notify_one();
     248          24 :             }));
     249          24 :     confHandlers.insert(
     250          48 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>(
     251          29 :             [&](const std::string& accountId,
     252             :                 const std::string& /* conversationId */,
     253             :                 std::map<std::string, std::string> /*metadatas*/) {
     254          29 :                 if (accountId == aliceId) {
     255           0 :                     aliceData.requestReceived = true;
     256          29 :                 } else if (accountId == bobId) {
     257          26 :                     bobData.requestReceived = true;
     258           3 :                 } else if (accountId == bob2Id) {
     259           2 :                     bob2Data.requestReceived = true;
     260           1 :                 } else if (accountId == carlaId) {
     261           1 :                     carlaData.requestReceived = true;
     262             :                 }
     263          29 :                 cv.notify_one();
     264          29 :             }));
     265          24 :     confHandlers.insert(
     266          48 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>(
     267           8 :             [&](const std::string& accountId, const std::string&) {
     268           8 :                 if (accountId == bobId) {
     269           7 :                     bobData.requestRemoved = true;
     270           1 :                 } else if (accountId == bob2Id) {
     271           1 :                     bob2Data.requestRemoved = true;
     272             :                 }
     273           8 :                 cv.notify_one();
     274           8 :             }));
     275          24 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>(
     276          15 :         [&](const std::string& accountId,
     277             :             const std::string& /* conversationId */,
     278             :             libjami::SwarmMessage message) {
     279          15 :             if (accountId == aliceId) {
     280          15 :                 aliceData.messages.emplace_back(message);
     281           0 :             } else if (accountId == bobId) {
     282           0 :                 bobData.messages.emplace_back(message);
     283           0 :             } else if (accountId == carlaId) {
     284           0 :                 carlaData.messages.emplace_back(message);
     285             :             }
     286          15 :             cv.notify_one();
     287          15 :         }));
     288          24 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>(
     289           0 :         [&](const std::string& accountId,
     290             :             const std::string& /* conversationId */,
     291             :             libjami::SwarmMessage message) {
     292           0 :             if (accountId == aliceId) {
     293           0 :                 aliceData.messagesUpdated.emplace_back(message);
     294           0 :             } else if (accountId == bobId) {
     295           0 :                 bobData.messagesUpdated.emplace_back(message);
     296           0 :             } else if (accountId == carlaId) {
     297           0 :                 carlaData.messagesUpdated.emplace_back(message);
     298             :             }
     299           0 :             cv.notify_one();
     300           0 :         }));
     301          24 :     confHandlers.insert(
     302          48 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>(
     303          12 :             [&](const std::string& accountId, const std::string&) {
     304          12 :                 if (accountId == aliceId)
     305           4 :                     aliceData.removed = true;
     306           8 :                 else if (accountId == bobId)
     307           6 :                     bobData.removed = true;
     308           2 :                 else if (accountId == bob2Id)
     309           2 :                     bob2Data.removed = true;
     310          12 :                 cv.notify_one();
     311          12 :             }));
     312          24 :     confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactAdded>(
     313          56 :         [&](const std::string& accountId, const std::string&, bool) {
     314          56 :             if (accountId == bobId) {
     315          16 :                 bobData.contactAdded = true;
     316             :             }
     317          56 :             cv.notify_one();
     318          56 :         }));
     319          24 :     confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>(
     320          12 :         [&](const std::string& accountId, const std::string&, bool) {
     321          12 :             if (accountId == bobId) {
     322           6 :                 bobData.contactRemoved = true;
     323           6 :             } else if (accountId == bob2Id) {
     324           2 :                 bob2Data.contactRemoved = true;
     325             :             }
     326          12 :             cv.notify_one();
     327          12 :         }));
     328             : 
     329          24 :     libjami::registerSignalHandlers(confHandlers);
     330          24 : }
     331             : 
     332             : 
     333             : void
     334          26 : ConversationRequestTest::tearDown()
     335             : {
     336          52 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
     337          26 :     std::remove(bobArchive.c_str());
     338             : 
     339          26 :     if (bob2Id.empty()) {
     340          96 :         wait_for_removal_of({aliceId, bobId, carlaId});
     341             :     } else {
     342          10 :         wait_for_removal_of({aliceId, bobId, carlaId, bob2Id});
     343             :     }
     344          26 : }
     345             : 
     346             : void
     347           1 : ConversationRequestTest::testAcceptTrustRemoveConvReq()
     348             : {
     349           1 :     connectSignals();
     350           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     351           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     352           1 :     auto bobUri = bobAccount->getUsername();
     353             : 
     354           1 :     aliceAccount->addContact(bobUri);
     355           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     356           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     357             : 
     358           1 :     CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 1);
     359           1 :     libjami::acceptConversationRequest(bobId, aliceData.conversationId);
     360           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
     361           1 :     CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 0);
     362           1 : }
     363             : 
     364             : void
     365           1 : ConversationRequestTest::acceptConvReqAlsoAddContact()
     366             : {
     367           1 :     connectSignals();
     368           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     369           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     370           1 :     auto bobUri = bobAccount->getUsername();
     371             : 
     372           1 :     aliceAccount->addContact(bobUri);
     373           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     374           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     375             : 
     376           1 :     bobData.requestReceived = false;
     377           1 :     auto convId2 = libjami::startConversation(aliceId);
     378           1 :     libjami::addConversationMember(aliceId, convId2, bobUri);
     379           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     380             : 
     381           1 :     libjami::acceptConversationRequest(bobId, convId2);
     382           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
     383           1 :     std::this_thread::sleep_for(5s);
     384           1 :     CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 0);
     385           1 : }
     386             : 
     387             : void
     388           1 : ConversationRequestTest::testGetRequests()
     389             : {
     390           1 :     connectSignals();
     391             : 
     392           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     393           1 :     auto bobUri = bobAccount->getUsername();
     394             : 
     395           1 :     auto convId = libjami::startConversation(aliceId);
     396             : 
     397           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     398           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     399             : 
     400           1 :     auto requests = libjami::getConversationRequests(bobId);
     401           1 :     CPPUNIT_ASSERT(requests.size() == 1);
     402           1 :     CPPUNIT_ASSERT(requests.front()["id"] == convId);
     403           1 : }
     404             : 
     405             : void
     406           1 : ConversationRequestTest::testDeclineRequest()
     407             : {
     408           1 :     connectSignals();
     409             : 
     410           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     411           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     412           1 :     auto bobUri = bobAccount->getUsername();
     413             : 
     414           1 :     auto convId = libjami::startConversation(aliceId);
     415             : 
     416           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     417           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     418             : 
     419           1 :     libjami::declineConversationRequest(bobId, convId);
     420             :     // Decline request
     421           1 :     auto requests = libjami::getConversationRequests(bobId);
     422           1 :     CPPUNIT_ASSERT(requests.size() == 0);
     423           1 : }
     424             : 
     425             : void
     426           1 : ConversationRequestTest::testAddContact()
     427             : {
     428           1 :     connectSignals();
     429             : 
     430           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     431           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     432           1 :     auto bobUri = bobAccount->getUsername();
     433           1 :     auto aliceUri = aliceAccount->getUsername();
     434             : 
     435           1 :     aliceAccount->addContact(bobUri);
     436           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     437           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return !aliceData.conversationId.empty(); }));
     438           1 :     ConversationRepository repo(aliceAccount, aliceData.conversationId);
     439             :     // Mode must be one to one
     440           1 :     CPPUNIT_ASSERT(repo.mode() == ConversationMode::ONE_TO_ONE);
     441             :     // Assert that repository exists
     442           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
     443           4 :                     / "conversations" / aliceData.conversationId;
     444           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     445           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     446           1 :     auto aliceMsgSize = aliceData.messages.size();
     447           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     448           6 :     CPPUNIT_ASSERT(
     449             :         cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); }));
     450           2 :     auto clonedPath = fileutils::get_data_dir() / bobId
     451           4 :                       / "conversations" / aliceData.conversationId;
     452           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(clonedPath));
     453           2 :     auto bobMember = clonedPath / "members" / (bobUri + ".crt");
     454           1 :     CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobMember));
     455           1 : }
     456             : 
     457             : void
     458           1 : ConversationRequestTest::testDeclineConversationRequestRemoveTrustRequest()
     459             : {
     460           1 :     connectSignals();
     461             : 
     462           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     463           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     464           1 :     auto bobUri = bobAccount->getUsername();
     465             : 
     466           1 :     aliceAccount->addContact(bobUri);
     467           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     468           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     469             : 
     470             :     // Decline request
     471           1 :     auto requests = libjami::getConversationRequests(bobId);
     472           1 :     CPPUNIT_ASSERT(requests.size() == 1);
     473           1 :     auto trustRequests = libjami::getTrustRequests(bobId);
     474           1 :     CPPUNIT_ASSERT(trustRequests.size() == 1);
     475           1 :     libjami::declineConversationRequest(bobId, aliceData.conversationId);
     476           1 :     requests = libjami::getConversationRequests(bobId);
     477           1 :     CPPUNIT_ASSERT(requests.size() == 0);
     478           1 :     trustRequests = libjami::getTrustRequests(bobId);
     479           1 :     CPPUNIT_ASSERT(trustRequests.size() == 0);
     480           1 : }
     481             : 
     482             : void
     483           1 : ConversationRequestTest::testMalformedTrustRequest()
     484             : {
     485           1 :     connectSignals();
     486             : 
     487           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     488           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     489           1 :     auto bobUri = bobAccount->getUsername();
     490             : 
     491           1 :     aliceAccount->addContact(bobUri);
     492           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     493           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     494             : 
     495             :     // Decline request
     496           1 :     auto requests = libjami::getConversationRequests(bobId);
     497           1 :     CPPUNIT_ASSERT(requests.size() == 1);
     498           1 :     auto trustRequests = libjami::getTrustRequests(bobId);
     499           1 :     CPPUNIT_ASSERT(trustRequests.size() == 1);
     500             :     // This will let the trust request (not libjami::declineConversationRequest)
     501           1 :     bobAccount->convModule()->declineConversationRequest(aliceData.conversationId);
     502           1 :     requests = libjami::getConversationRequests(bobId);
     503           1 :     CPPUNIT_ASSERT(requests.size() == 0);
     504           1 :     trustRequests = libjami::getTrustRequests(bobId);
     505           1 :     CPPUNIT_ASSERT(trustRequests.size() == 1);
     506             :     // Reload conversation will fix the state (the trustRequest is removed in another thread)
     507           1 :     bobAccount->convModule()->loadConversations();
     508           1 :     requests = libjami::getConversationRequests(bobId);
     509           1 :     CPPUNIT_ASSERT(requests.size() == 0);
     510             : 
     511           1 :     auto start = std::chrono::steady_clock::now();
     512             : 
     513           1 :     auto requestDeclined = false;
     514             :     do {
     515           2 :         trustRequests = libjami::getTrustRequests(bobId);
     516           2 :         requestDeclined = trustRequests.size() == 0;
     517           2 :         if (!requestDeclined)
     518           1 :             std::this_thread::sleep_for(1s);
     519           2 :     } while (not requestDeclined and std::chrono::steady_clock::now() - start < 2s);
     520             : 
     521           1 :     CPPUNIT_ASSERT(requestDeclined);
     522           1 : }
     523             : 
     524             : void
     525           1 : ConversationRequestTest::testAddContactDeleteAndReAdd()
     526             : {
     527           1 :     connectSignals();
     528             : 
     529           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     530           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     531           1 :     auto bobUri = bobAccount->getUsername();
     532           1 :     auto aliceUri = aliceAccount->getUsername();
     533             : 
     534           1 :     aliceAccount->addContact(bobUri);
     535           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     536           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     537           1 :     auto aliceMsgSize = aliceData.messages.size();
     538           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     539           6 :     CPPUNIT_ASSERT(
     540             :         cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); }));
     541             : 
     542             :     // removeContact
     543           1 :     aliceAccount->removeContact(bobUri, false);
     544           1 :     std::this_thread::sleep_for(5s); // wait a bit that connections are closed
     545             : 
     546             :     // re-add
     547           1 :     CPPUNIT_ASSERT(aliceData.conversationId != "");
     548           1 :     auto oldConvId = aliceData.conversationId;
     549           1 :     aliceAccount->addContact(bobUri);
     550           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     551             :     // Should retrieve previous conversation
     552           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return oldConvId == aliceData.conversationId; }));
     553           1 : }
     554             : 
     555             : void
     556           1 : ConversationRequestTest::testRemoveContact()
     557             : {
     558           1 :     connectSignals();
     559             : 
     560           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     561           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     562           1 :     auto bobUri = bobAccount->getUsername();
     563           1 :     auto aliceUri = aliceAccount->getUsername();
     564             : 
     565           1 :     aliceAccount->addContact(bobUri);
     566           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     567             :     // Check created files
     568           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     569           1 :     auto aliceMsgSize = aliceData.messages.size();
     570           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     571           6 :     CPPUNIT_ASSERT(
     572             :         cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     573             : 
     574           1 :     bobAccount->removeContact(aliceUri, false);
     575           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; }));
     576             : 
     577           1 :     auto details = bobAccount->getContactDetails(aliceUri);
     578           1 :     CPPUNIT_ASSERT(details.find("removed") != details.end() && details["removed"] != "0");
     579             : 
     580           1 :     aliceAccount->removeContact(bobUri, false);
     581           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]() { return aliceData.removed; }));
     582             : 
     583           1 :     std::this_thread::sleep_for(
     584           1 :         10s); // There is no signal, but daemon should then erase the repository
     585             : 
     586           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
     587           4 :                     / "conversations" / aliceData.conversationId;
     588           1 :     CPPUNIT_ASSERT(!std::filesystem::is_directory(repoPath));
     589             : 
     590           2 :     repoPath = fileutils::get_data_dir() / bobId
     591           3 :                / "conversations" / bobData.conversationId;
     592           1 :     CPPUNIT_ASSERT(!std::filesystem::is_directory(repoPath));
     593           1 : }
     594             : 
     595             : void
     596           1 : ConversationRequestTest::testRemoveContactMultiDevice()
     597             : {
     598           1 :     connectSignals();
     599             : 
     600           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     601           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     602           1 :     auto bobUri = bobAccount->getUsername();
     603           1 :     auto aliceUri = aliceAccount->getUsername();
     604             : 
     605             :     // Add second device for Bob
     606           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     607           2 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
     608           1 :     std::remove(bobArchive.c_str());
     609           1 :     bobAccount->exportArchive(bobArchive);
     610             : 
     611           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
     612           1 :     details[ConfProperties::TYPE] = "RING";
     613           1 :     details[ConfProperties::DISPLAYNAME] = "BOB2";
     614           1 :     details[ConfProperties::ALIAS] = "BOB2";
     615           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
     616           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
     617           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
     618           1 :     details[ConfProperties::ARCHIVE_PATH] = bobArchive;
     619             : 
     620           1 :     bob2Id = Manager::instance().addAccount(details);
     621             : 
     622           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     623             :         return bob2Data.deviceAnnounced;
     624             :     }));
     625             :     // First, Alice adds Bob
     626           1 :     aliceAccount->addContact(bobUri);
     627           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     628           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     629             :         return bobData.requestReceived && bob2Data.requestReceived;
     630             :     }));
     631             : 
     632             :     // Bob1 decline via removeContact, both device should remove the request
     633           1 :     bobAccount->removeContact(aliceUri, false);
     634           5 :     CPPUNIT_ASSERT(
     635             :         cv.wait_for(lk, 30s, [&]() { return bobData.requestRemoved && bob2Data.requestRemoved; }));
     636           1 : }
     637             : 
     638             : void
     639           1 : ConversationRequestTest::testRemoveSelfDoesntRemoveConversation()
     640             : {
     641           1 :     connectSignals();
     642             : 
     643           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     644           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     645           1 :     auto bobUri = bobAccount->getUsername();
     646           1 :     auto aliceUri = aliceAccount->getUsername();
     647             : 
     648           1 :     aliceAccount->addContact(bobUri);
     649           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     650             :     // Check created files
     651           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     652           1 :     auto aliceMsgSize = aliceData.messages.size();
     653           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     654           6 :     CPPUNIT_ASSERT(
     655             :         cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     656             : 
     657           1 :     aliceAccount->removeContact(aliceUri, false);
     658           4 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return aliceData.removed; }));
     659           2 :     auto repoPath = fileutils::get_data_dir() / aliceId
     660           4 :                     / "conversations" / aliceData.conversationId;
     661           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     662           1 : }
     663             : 
     664             : void
     665           1 : ConversationRequestTest::testRemoveConversationUpdateContactDetails()
     666             : {
     667           1 :     connectSignals();
     668             : 
     669           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     670           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     671           1 :     auto bobUri = bobAccount->getUsername();
     672           1 :     auto aliceUri = aliceAccount->getUsername();
     673             : 
     674           1 :     aliceAccount->addContact(bobUri);
     675           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     676             :     // Check created files
     677           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     678           1 :     auto aliceMsgSize = aliceData.messages.size();
     679           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     680           6 :     CPPUNIT_ASSERT(
     681             :         cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     682             : 
     683           1 :     libjami::removeConversation(bobId, bobData.conversationId);
     684           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; }));
     685             : 
     686           1 :     auto details = bobAccount->getContactDetails(aliceUri);
     687           1 :     CPPUNIT_ASSERT(details[libjami::Account::TrustRequest::CONVERSATIONID] == "");
     688           1 : }
     689             : 
     690             : void
     691           1 : ConversationRequestTest::testBanContact()
     692             : {
     693           1 :     connectSignals();
     694             : 
     695           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     696           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     697           1 :     auto bobUri = bobAccount->getUsername();
     698           1 :     auto aliceUri = aliceAccount->getUsername();
     699             : 
     700           1 :     aliceAccount->addContact(bobUri);
     701           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     702             :     // Check created files
     703           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     704           1 :     auto aliceMsgSize = aliceData.messages.size();
     705           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     706           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     707             : 
     708           1 :     bobAccount->removeContact(aliceUri, true);
     709           4 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }));
     710           2 :     auto repoPath = fileutils::get_data_dir() / bobId
     711           4 :                     / "conversations" / bobData.conversationId;
     712           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     713           1 : }
     714             : 
     715             : 
     716             : void
     717           1 : ConversationRequestTest::testBanContactRestartAccount()
     718             : {
     719           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     720           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     721           1 :     auto bobUri = bobAccount->getUsername();
     722           1 :     auto aliceUri = aliceAccount->getUsername();
     723           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     724           1 :     bool conversationReady = false, requestReceived = false, memberMessageGenerated = false;
     725           1 :     std::string convId = "";
     726           1 :     confHandlers.insert(
     727           2 :         libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>(
     728           1 :             [&](const std::string& account_id,
     729             :                 const std::string& /*from*/,
     730             :                 const std::string& /*conversationId*/,
     731             :                 const std::vector<uint8_t>& /*payload*/,
     732             :                 time_t /*received*/) {
     733           1 :                 if (account_id == bobId)
     734           1 :                     requestReceived = true;
     735           1 :                 cv.notify_one();
     736           1 :             }));
     737           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     738           2 :         [&](const std::string& accountId, const std::string& conversationId) {
     739           2 :             if (accountId == aliceId) {
     740           1 :                 convId = conversationId;
     741           1 :             } else if (accountId == bobId) {
     742           1 :                 conversationReady = true;
     743             :             }
     744           2 :             cv.notify_one();
     745           2 :         }));
     746           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>(
     747           1 :         [&](const std::string& accountId,
     748             :             const std::string& conversationId,
     749             :             std::map<std::string, std::string> message) {
     750           1 :             if (accountId == aliceId && conversationId == convId && message["type"] == "member") {
     751           1 :                 memberMessageGenerated = true;
     752             :             }
     753           1 :             cv.notify_one();
     754           1 :         }));
     755           1 :     bool contactRemoved = false;
     756           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>(
     757           1 :         [&](const std::string& accountId, const std::string& uri, bool) {
     758           1 :             if (accountId == bobId && uri == aliceUri) {
     759           1 :                 contactRemoved = true;
     760             :             }
     761           1 :             cv.notify_one();
     762           1 :         }));
     763           1 :     auto bobConnected = false;
     764           1 :     confHandlers.insert(
     765           2 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
     766           4 :             [&](const std::string&, const std::map<std::string, std::string>&) {
     767           4 :                 auto details = bobAccount->getVolatileAccountDetails();
     768           8 :                 auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
     769           4 :                 if (daemonStatus == "REGISTERED") {
     770           2 :                     bobConnected = true;
     771           2 :                 } else if (daemonStatus == "UNREGISTERED") {
     772           1 :                     bobConnected = false;
     773             :                 }
     774           4 :                 cv.notify_one();
     775           4 :             }));
     776           1 :     libjami::registerSignalHandlers(confHandlers);
     777             : 
     778           1 :     aliceAccount->addContact(bobUri);
     779           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     780           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return !convId.empty(); }));
     781             :     // Check created files
     782           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; }));
     783           1 :     memberMessageGenerated = false;
     784           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     785           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; }));
     786             : 
     787           1 :     memberMessageGenerated = false;
     788           1 :     bobAccount->removeContact(aliceUri, true);
     789           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return contactRemoved; }));
     790           1 :     std::this_thread::sleep_for(10s); // Wait a bit to ensure that everything is updated
     791           1 :     bobConnected = true;
     792           1 :     Manager::instance().sendRegister(bobId, false);
     793           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobConnected; }));
     794           1 :     std::this_thread::sleep_for(5s); // Wait a bit to ensure that everything is updated
     795             : 
     796             :     // Connect bob, it will trigger the bootstrap
     797           1 :     auto statusBootstrap = Conversation::BootstrapStatus::SUCCESS;
     798             :     // alice is banned so bootstrap MUST fail
     799           1 :     bobAccount->convModule()->onBootstrapStatus(
     800           1 :         [&](std::string /*convId*/, Conversation::BootstrapStatus status) {
     801           1 :             statusBootstrap = status;
     802           1 :             cv.notify_one();
     803           1 :         });
     804           1 :     Manager::instance().sendRegister(bobId, true);
     805           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return statusBootstrap == Conversation::BootstrapStatus::FAILED; }));
     806             : 
     807           1 : }
     808             : 
     809             : void
     810           1 : ConversationRequestTest::testBanContactRemoveTrustRequest()
     811             : {
     812           1 :     connectSignals();
     813             : 
     814           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     815           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     816           1 :     auto bobUri = bobAccount->getUsername();
     817           1 :     auto aliceUri = aliceAccount->getUsername();
     818             : 
     819           1 :     aliceAccount->addContact(bobUri);
     820           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     821             :     // Check created files
     822           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     823           1 :     bobAccount->removeContact(aliceUri, true);
     824           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return bobData.requestRemoved; }));
     825           1 :     auto requests = libjami::getConversationRequests(bobId);
     826           1 :     CPPUNIT_ASSERT(requests.size() == 0);
     827           1 : }
     828             : 
     829             : void
     830           1 : ConversationRequestTest::testAddOfflineContactThenConnect()
     831             : {
     832           1 :     connectSignals();
     833             : 
     834           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     835           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     836           1 :     auto carlaUri = carlaAccount->getUsername();
     837           1 :     auto aliceUri = aliceAccount->getUsername();
     838           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
     839             : 
     840           1 :     aliceAccount->addContact(carlaUri);
     841           1 :     aliceAccount->sendTrustRequest(carlaUri, {});
     842           1 :     cv.wait_for(lk, 5s); // Wait 5 secs for the put to happen
     843           1 :     Manager::instance().sendRegister(carlaId, true);
     844           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&]() { return carlaData.requestReceived; }));
     845           1 :     auto aliceMsgSize = aliceData.messages.size();
     846           1 :     CPPUNIT_ASSERT(carlaAccount->acceptTrustRequest(aliceUri));
     847           7 :     CPPUNIT_ASSERT(
     848             :         cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == aliceMsgSize + 1; }));
     849           1 : }
     850             : 
     851             : void
     852           1 : ConversationRequestTest::testDeclineTrustRequestDoNotGenerateAnother()
     853             : {
     854           1 :     connectSignals();
     855             : 
     856           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     857           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     858           1 :     auto bobUri = bobAccount->getUsername();
     859           1 :     auto aliceUri = aliceAccount->getUsername();
     860           1 :     aliceAccount->trackBuddyPresence(bobUri, true);
     861             : 
     862           1 :     aliceAccount->addContact(bobUri);
     863           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     864           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     865           1 :     CPPUNIT_ASSERT(bobAccount->discardTrustRequest(aliceUri));
     866           1 :     cv.wait_for(lk, 10s); // Wait a bit
     867           1 :     Manager::instance().sendRegister(bobId, false);
     868           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.stopped; }));
     869             :     // Trigger on peer online
     870           1 :     bobData.deviceAnnounced = false; bobData.requestReceived = false;
     871           1 :     Manager::instance().sendRegister(bobId, true);
     872           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.deviceAnnounced; }));
     873           3 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     874           1 : }
     875             : 
     876             : void
     877           1 : ConversationRequestTest::testRemoveContactRemoveSyncing()
     878             : {
     879           1 :     connectSignals();
     880             : 
     881           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     882           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     883           1 :     auto bobUri = bobAccount->getUsername();
     884           1 :     auto aliceUri = aliceAccount->getUsername();
     885             : 
     886           1 :     aliceAccount->addContact(bobUri);
     887           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     888           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     889             : 
     890           1 :     Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately
     891           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     892           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.contactAdded; }));
     893             : 
     894           1 :     CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 1);
     895           1 :     bobAccount->removeContact(aliceUri, false);
     896           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.contactRemoved; }));
     897             : 
     898           1 :     CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 0);
     899           1 : }
     900             : 
     901             : void
     902           1 : ConversationRequestTest::testRemoveConversationRemoveSyncing()
     903             : {
     904           1 :     connectSignals();
     905             : 
     906           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     907           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     908           1 :     auto bobUri = bobAccount->getUsername();
     909           1 :     auto aliceUri = aliceAccount->getUsername();
     910             : 
     911           1 :     aliceAccount->addContact(bobUri);
     912           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     913           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     914             : 
     915           1 :     Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately
     916           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     917           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.contactAdded; }));
     918             :     // At this point the conversation should be there and syncing.
     919             : 
     920           1 :     CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 1);
     921           1 :     libjami::removeConversation(bobId, aliceData.conversationId);
     922           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; }));
     923             : 
     924           1 :     CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 0);
     925           1 : }
     926             : 
     927             : void
     928           1 : ConversationRequestTest::testCacheRequestFromClient()
     929             : {
     930           1 :     connectSignals();
     931             : 
     932           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     933           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     934           1 :     auto bobUri = bobAccount->getUsername();
     935             : 
     936           1 :     aliceAccount->addContact(bobUri);
     937           1 :     std::vector<uint8_t> payload = {0x64, 0x64, 0x64};
     938           1 :     aliceAccount->sendTrustRequest(bobUri,
     939             :                                    payload); // Random payload, just care with the file
     940           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     941           2 :     auto cachedPath = fileutils::get_cache_dir() / aliceId
     942           4 :                       / "requests" / bobUri;
     943           1 :     CPPUNIT_ASSERT(std::filesystem::is_regular_file(cachedPath));
     944           1 :     CPPUNIT_ASSERT(fileutils::loadFile(cachedPath) == payload);
     945             : 
     946           1 :     CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 1);
     947           1 :     libjami::acceptConversationRequest(bobId, aliceData.conversationId);
     948           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
     949             :     // cachedPath is removed on confirmation (from the DHT), so this can take a few secs to come
     950           1 :     auto removed = false;
     951           1 :     for (int i = 0; i <= 10; ++i) {
     952           1 :         if (!std::filesystem::is_regular_file(cachedPath)) {
     953           1 :             removed = true;
     954           1 :             break;
     955             :         }
     956           0 :         std::this_thread::sleep_for(1s);
     957             :     }
     958           1 :     CPPUNIT_ASSERT(removed);
     959           1 : }
     960             : 
     961             : void
     962           1 : ConversationRequestTest::testNeedsSyncingWithForCloning()
     963             : {
     964           1 :     connectSignals();
     965             : 
     966           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     967           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     968           1 :     auto bobUri = bobAccount->getUsername();
     969           1 :     auto aliceUri = aliceAccount->getUsername();
     970           1 :     auto aliceDevice = std::string(aliceAccount->currentDeviceId());
     971             : 
     972           1 :     CPPUNIT_ASSERT(!bobAccount->convModule()->needsSyncingWith(aliceUri, aliceDevice));
     973           1 :     aliceAccount->addContact(bobUri);
     974           1 :     aliceAccount->sendTrustRequest(bobUri, {});
     975           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     976             : 
     977           1 :     Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately
     978           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
     979           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.contactAdded; }));
     980             :     // At this point the conversation should be there and syncing.
     981             : 
     982           1 :     CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 1);
     983           1 :     CPPUNIT_ASSERT(bobAccount->convModule()->needsSyncingWith(aliceUri, aliceDevice));
     984           1 : }
     985             : 
     986             : void
     987           1 : ConversationRequestTest::testRemoveContactRemoveTrustRequest()
     988             : {
     989           1 :     connectSignals();
     990             : 
     991           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     992           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     993           1 :     auto bobUri = bobAccount->getUsername();
     994           1 :     auto aliceUri = aliceAccount->getUsername();
     995             : 
     996             :     // Add second device for Bob
     997           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     998           2 :     auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
     999           1 :     std::remove(bobArchive.c_str());
    1000           1 :     bobAccount->exportArchive(bobArchive);
    1001             : 
    1002           2 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
    1003           1 :     details[ConfProperties::TYPE] = "RING";
    1004           1 :     details[ConfProperties::DISPLAYNAME] = "BOB2";
    1005           1 :     details[ConfProperties::ALIAS] = "BOB2";
    1006           1 :     details[ConfProperties::UPNP_ENABLED] = "true";
    1007           1 :     details[ConfProperties::ARCHIVE_PASSWORD] = "";
    1008           1 :     details[ConfProperties::ARCHIVE_PIN] = "";
    1009           1 :     details[ConfProperties::ARCHIVE_PATH] = bobArchive;
    1010             : 
    1011           1 :     bob2Id = Manager::instance().addAccount(details);
    1012             : 
    1013           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    1014             :         return bob2Data.deviceAnnounced;
    1015             :     }));
    1016           1 :     auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id);
    1017             : 
    1018             :     // First, Alice adds Bob
    1019           1 :     aliceAccount->addContact(bobUri);
    1020           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1021           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    1022             :         return bobData.requestReceived && bob2Data.requestReceived;
    1023             :     }));
    1024             : 
    1025             :     // Bob1 accepts, both device should get it
    1026           1 :     auto aliceMsgSize = aliceData.messages.size();
    1027           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
    1028           8 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    1029             :         return !bobData.conversationId.empty() && !bob2Data.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size();
    1030             :     }));
    1031             : 
    1032             :     // Bob2 remove Alice ; Bob1 should not have any trust requests
    1033           1 :     bob2Account->removeContact(aliceUri, false);
    1034           6 :     CPPUNIT_ASSERT(
    1035             :         cv.wait_for(lk, 30s, [&]() { return bobData.contactRemoved && bob2Data.contactRemoved; }));
    1036           1 :     std::this_thread::sleep_for(10s); // Wait a bit to ensure that everything is update (via synced)
    1037           1 :     CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 0);
    1038           1 :     CPPUNIT_ASSERT(bob2Account->getTrustRequests().size() == 0);
    1039           1 : }
    1040             : 
    1041             : void
    1042           1 : ConversationRequestTest::testAddConversationNoPresenceThenConnects()
    1043             : {
    1044           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1045           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
    1046           1 :     auto carlaUri = carlaAccount->getUsername();
    1047           1 :     auto aliceUri = aliceAccount->getUsername();
    1048           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
    1049           1 :     carlaAccount->publishPresence(false);
    1050             : 
    1051           1 :     std::mutex mtx;
    1052           1 :     std::unique_lock lk {mtx};
    1053           1 :     std::condition_variable cv;
    1054           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
    1055           1 :     std::string convId = "";
    1056           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
    1057           3 :         [&](const std::string& accountId, const std::string& conversationId) {
    1058           3 :             if (accountId == aliceId) {
    1059           2 :                 convId = conversationId;
    1060             :             }
    1061           3 :             cv.notify_one();
    1062           3 :         }));
    1063           1 :     auto carlaConnected = false;
    1064           1 :     confHandlers.insert(
    1065           2 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
    1066           7 :             [&](const std::string&, const std::map<std::string, std::string>&) {
    1067           7 :                 auto details = carlaAccount->getVolatileAccountDetails();
    1068          14 :                 auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS];
    1069           7 :                 if (daemonStatus == "REGISTERED") {
    1070           4 :                     carlaConnected = true;
    1071           3 :                 } else if (daemonStatus == "UNREGISTERED") {
    1072           1 :                     carlaConnected = false;
    1073             :                 }
    1074           7 :                 cv.notify_one();
    1075           7 :             }));
    1076           1 :     libjami::registerSignalHandlers(confHandlers);
    1077           1 :     aliceAccount->addContact(carlaUri);
    1078           1 :     cv.wait_for(lk, 5s); // Wait 5 secs for the put to happen
    1079           1 :     Manager::instance().sendRegister(carlaId, true);
    1080           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaConnected; }));
    1081           1 :     carlaAccount->addContact(aliceUri);
    1082           1 :     cv.wait_for(lk, 5s); // Wait 5 secs for the put to happen
    1083           1 :     carlaAccount->publishPresence(true);
    1084             : 
    1085           1 :     Manager::instance().sendRegister(carlaId, false);
    1086           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !carlaConnected; }));
    1087           1 :     convId.clear();
    1088           1 :     Manager::instance().sendRegister(carlaId, true);
    1089           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaConnected; }));
    1090           4 :     CPPUNIT_ASSERT(
    1091             :         cv.wait_for(lk, 30s, [&]() { return !convId.empty(); }));
    1092             : 
    1093           1 :     auto carlaDetails = carlaAccount->getContactDetails(aliceUri);
    1094           1 :     auto aliceDetails = aliceAccount->getContactDetails(carlaUri);
    1095           1 :     CPPUNIT_ASSERT(carlaDetails["conversationId"] == aliceDetails["conversationId"] && carlaDetails["conversationId"] == convId);
    1096           1 : }
    1097             : 
    1098             : void
    1099           1 : ConversationRequestTest::testRequestBigPayload()
    1100             : {
    1101           1 :     connectSignals();
    1102           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1103           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1104           1 :     auto bobUri = bobAccount->getUsername();
    1105             : 
    1106           1 :     aliceAccount->addContact(bobUri);
    1107             : 
    1108           1 :     auto data = std::string(64000, 'A');
    1109           1 :     std::vector<uint8_t> payload(data.begin(), data.end());
    1110           1 :     aliceAccount->sendTrustRequest(bobUri, payload);
    1111           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1112             : 
    1113           1 :     CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 1);
    1114           1 :     libjami::acceptConversationRequest(bobId, aliceData.conversationId);
    1115           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
    1116           1 :     CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 0);
    1117           1 : }
    1118             : 
    1119             : void
    1120           1 : ConversationRequestTest::testBothRemoveReadd()
    1121             : {
    1122           1 :     connectSignals();
    1123             : 
    1124           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1125           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1126           1 :     auto bobUri = bobAccount->getUsername();
    1127           1 :     auto aliceUri = aliceAccount->getUsername();
    1128             : 
    1129           1 :     aliceAccount->addContact(bobUri);
    1130           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1131           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1132           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
    1133           5 :     CPPUNIT_ASSERT(
    1134             :         cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); }));
    1135             : 
    1136             :     // removeContact
    1137           1 :     aliceAccount->removeContact(bobUri, false);
    1138           1 :     bobAccount->removeContact(aliceUri, false);
    1139           1 :     std::this_thread::sleep_for(5s); // wait a bit that connections are closed
    1140             : 
    1141             :     // re-add
    1142           1 :     aliceData.conversationId.clear();
    1143           1 :     bobData.requestReceived = false;
    1144           1 :     aliceAccount->addContact(bobUri);
    1145           1 :     aliceAccount->sendTrustRequest(bobUri, {});
    1146           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1147           1 :     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
    1148             :     // Should retrieve previous conversation
    1149           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    1150             :         return !aliceData.conversationId.empty() && bobData.conversationId == aliceData.conversationId; }));
    1151           1 : }
    1152             : 
    1153             : void
    1154           1 : ConversationRequestTest::doNotLooseMetadata()
    1155             : {
    1156           1 :     std::cout << "\nRunning test: " << __func__ << std::endl;
    1157           1 :     connectSignals();
    1158             : 
    1159           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
    1160           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
    1161           1 :     auto bobUri = bobAccount->getUsername();
    1162             : 
    1163           1 :     libjami::startConversation(aliceId);
    1164           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.conversationId != ""; }));
    1165             : 
    1166           1 :     auto aliceMsgSize = aliceData.messages.size();
    1167           2 :     aliceAccount->convModule()->updateConversationInfos(aliceData.conversationId, {{"title", "My awesome swarm"}});
    1168           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
    1169             :         return aliceMsgSize + 1 == aliceData.messages.size();
    1170             :     }));
    1171             : 
    1172           1 :     libjami::addConversationMember(aliceId, aliceData.conversationId, bobUri);
    1173           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
    1174             : 
    1175           1 :     Manager::instance().sendRegister(aliceId, false);
    1176           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.stopped; }));
    1177             : 
    1178           1 :     libjami::acceptConversationRequest(bobId, aliceData.conversationId);
    1179           3 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobData.conversationId != ""; }));
    1180             : 
    1181             :     // Force reset
    1182           1 :     bobAccount->convModule()->loadConversations();
    1183           1 :     auto infos = libjami::conversationInfos(bobId, aliceData.conversationId);
    1184           1 :     CPPUNIT_ASSERT(infos["syncing"] == "true");
    1185           1 :     CPPUNIT_ASSERT(infos["title"] == "My awesome swarm");
    1186           1 : }
    1187             : 
    1188             : } // namespace test
    1189             : } // namespace jami
    1190             : 
    1191           1 : RING_TEST_RUNNER(jami::test::ConversationRequestTest::name())

Generated by: LCOV version 1.14