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-12-21 08:56:24 Functions: 127 128 99.2 %

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

Generated by: LCOV version 1.14