diff --git a/src/contactmodel.cpp b/src/contactmodel.cpp index 257c9a1b7c090cb5cf3b5d35b8299a81ad8b7b01..5d2824507bfc45dd96665d526554c2d5b5df7010 100644 --- a/src/contactmodel.cpp +++ b/src/contactmodel.cpp @@ -210,10 +210,12 @@ ContactModel::addContact(contact::Info contactInfo) switch (profile.type) { case profile::Type::TEMPORARY: // NOTE: do not set profile::Type::RING, this has to be done when the daemon has emited contactAdded - #ifndef ENABLE_TEST // The old LRC doesn't like mocks +#ifndef ENABLE_TEST // The old LRC doesn't like mocks if (auto* account = AccountModel::instance().getById(owner.id.c_str())) account->sendContactRequest(URI(profile.uri.c_str())); - #endif +#else + ConfigurationManager::instance().addContact(owner.id.c_str(), profile.uri.c_str()); +#endif break; case profile::Type::PENDING: daemon::addContactFromPending(owner, profile.uri); diff --git a/test/conversationmodeltester.cpp b/test/conversationmodeltester.cpp index 0b83b98c8da19e4db36093b120fc2fc68eabaa9a..10ca9051afcc3e981041e571cf89cbcf8fee8809 100644 --- a/test/conversationmodeltester.cpp +++ b/test/conversationmodeltester.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2017-2018 Savoir-faire Linux Inc. * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> + * Author: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,49 +56,145 @@ void ConversationModelTester::testAddValidConversation() { // Dummy should not be in contacts - auto contactId = accInfo_.contactModel->getContactProfileId("dummy"); - CPPUNIT_ASSERT(contactId.empty()); - // Search contact - accInfo_.conversationModel->setFilter("dummy"); - WaitForSignalHelper(*accInfo_.contactModel, - SIGNAL(modelUpdated())).wait(1000); - // So, add dummy to contacts - auto newContactUri = accInfo_.conversationModel->owner.contactModel->getContact("").profileInfo.uri; - accInfo_.conversationModel->makePermanent(newContactUri); - auto contactAdded = WaitForSignalHelper(ConfigurationManager::instance(), - SIGNAL(contactAdded(const QString&, const QString&, bool))).wait(1000); - CPPUNIT_ASSERT_EQUAL(contactAdded, true); + CPPUNIT_ASSERT(!isAContact("dummy")); + + auto newContactUri = addToContacts("dummy"); + // Dummy should be in contacts - contactId = accInfo_.contactModel->getContactProfileId("dummy"); - CPPUNIT_ASSERT(!contactId.empty()); + CPPUNIT_ASSERT(isAContact("dummy")); + // So, a conversation should exists. + CPPUNIT_ASSERT(hasConversationWithContact("dummy")); +} + +void +ConversationModelTester::testPlaceCallWithBannedContact() +{ + // bannedContact should not be in contacts + CPPUNIT_ASSERT(!isAContact("badguy0")); + + auto uri = addToContacts("badguy0"); + + // badguy0 should now be in contacts + CPPUNIT_ASSERT(isAContact("badguy0")); + + // Ban badguy0 + banContact(uri); + auto contactInfo = accInfo_.contactModel->getContact(uri); + CPPUNIT_ASSERT_EQUAL(contactInfo.isBanned, true); + + // So, now that badguy0 is banned, calling him should be forbidden auto conversations = accInfo_.conversationModel->allFilteredConversations(); - auto i = std::find_if(conversations.begin(), conversations.end(), - [](const lrc::api::conversation::Info& conversation) { - return std::find(conversation.participants.begin(), - conversation.participants.end(), - "dummy") != conversation.participants.end(); - }); - CPPUNIT_ASSERT(i != conversations.end()); + bool conversationExists = false; + for (const auto& conversation: conversations) { + if (std::find(conversation.participants.begin(), conversation.participants.end(), contactInfo.profileInfo.uri) != conversation.participants.end()) { + conversationExists = true; + // Try to call banned contact + auto baseInteractionsSize = conversation.interactions.size(); + accInfo_.conversationModel->placeCall(conversation.uid); + // Make sure call didn't succeed + CPPUNIT_ASSERT_EQUAL((int)baseInteractionsSize, (int)conversation.interactions.size()); + break; + } + } + + CPPUNIT_ASSERT(conversationExists); +} + +void +ConversationModelTester::testFilterBannedContact() +{ + // bannedContact should not be in contacts + CPPUNIT_ASSERT(!isAContact("bannedContact")); + CPPUNIT_ASSERT(!isAContact("bannedContacte")); + CPPUNIT_ASSERT(!isAContact("bannedContac")); + + auto newContactUri = addToContacts("bannedContact"); + + // bannedContact now should be in contacts + CPPUNIT_ASSERT(isAContact("bannedContact")); + + // Ban bannedContact + banContact(newContactUri); + auto contactInfo = accInfo_.contactModel->getContact(newContactUri); + CPPUNIT_ASSERT_EQUAL(contactInfo.isBanned, true); + + // Make sure bannedContact doesn't appear is non-perfect-match filter searches + // We expect 1 (temporary item) + accInfo_.conversationModel->setFilter("bannedContac"); + WaitForSignalHelper(*accInfo_.contactModel, + SIGNAL(modelUpdated())).wait(1000); + CPPUNIT_ASSERT_EQUAL(1, (int)accInfo_.conversationModel->allFilteredConversations().size()); + auto isTemporary = accInfo_.conversationModel->filteredConversation(0).participants.front() == ""; + CPPUNIT_ASSERT(isTemporary); + accInfo_.conversationModel->setFilter("bannedContacte"); + WaitForSignalHelper(*accInfo_.contactModel, + SIGNAL(modelUpdated())).wait(1000); + CPPUNIT_ASSERT_EQUAL(1, (int)accInfo_.conversationModel->allFilteredConversations().size()); + isTemporary = accInfo_.conversationModel->filteredConversation(0).participants.front() == ""; + CPPUNIT_ASSERT(isTemporary); + + // Make sure bannedContact appears in perfect-match filter searches + // We expect 1 (bannedContact) + accInfo_.conversationModel->setFilter("bannedContact"); + WaitForSignalHelper(*accInfo_.contactModel, + SIGNAL(modelUpdated())).wait(1000); + CPPUNIT_ASSERT_EQUAL(1, (int)accInfo_.conversationModel->allFilteredConversations().size()); + isTemporary = accInfo_.conversationModel->filteredConversation(0).participants.front() == ""; + CPPUNIT_ASSERT(!isTemporary); +} + +void +ConversationModelTester::testSendMessageToBannedContact() +{ + // bannedContact should not be in contacts + CPPUNIT_ASSERT(!isAContact("badguy1")); + + auto uri = addToContacts("badguy1"); + + // badguy1 should now be in contacts + CPPUNIT_ASSERT(isAContact("badguy1")); + + // Ban badguy1 + banContact(uri); + auto contactInfo = accInfo_.contactModel->getContact(uri); + CPPUNIT_ASSERT_EQUAL(contactInfo.isBanned, true); + + // So, now that badguy is banned, sending a message should be forbidden + auto conversations = accInfo_.conversationModel->allFilteredConversations(); + bool conversationExists = false; + for (const auto& conversation: conversations) { + if (std::find(conversation.participants.begin(), conversation.participants.end(), contactInfo.profileInfo.uri) != conversation.participants.end()) { + conversationExists = true; + // Try to send message to banned contact + auto baseInteractionsSize = conversation.interactions.size(); + accInfo_.conversationModel->sendMessage(conversation.uid, "Hello banned !"); + // Make sure message didn't arrive (but contact added is already here) + CPPUNIT_ASSERT_EQUAL((int)baseInteractionsSize, (int)conversation.interactions.size()); + break; + } + } + + CPPUNIT_ASSERT(conversationExists); } void ConversationModelTester::testAddInvalidConversation() { // notAContact should not be in contacts - auto contacts = accInfo_.contactModel->getAllContacts(); - CPPUNIT_ASSERT(contacts.find("notAContact") == contacts.end()); + CPPUNIT_ASSERT(!isAContact("notAContact")); + // Search contact accInfo_.conversationModel->setFilter("notAContact"); WaitForSignalHelper(*accInfo_.contactModel, SIGNAL(modelUpdated())).wait(1000); - // Temporary item should contains "Searching...notAContact" + + // Temporary item should contain "Searching...notAContact" // makePermanent should not do anything accInfo_.conversationModel->makePermanent(""); WaitForSignalHelper(ConfigurationManager::instance(), SIGNAL(contactAdded(const QString&, const QString&, bool))).wait(1000); - contacts = accInfo_.contactModel->getAllContacts(); - CPPUNIT_ASSERT(contacts.find("notAContact") == contacts.end()); + CPPUNIT_ASSERT(!isAContact("notAContact")); } void @@ -112,18 +209,13 @@ ConversationModelTester::testRmConversation() "contact0") != conversation.participants.end(); }); CPPUNIT_ASSERT(i != conversations.end()); + accInfo_.conversationModel->removeConversation((*i).uid); auto conversationRemoved = WaitForSignalHelper(*accInfo_.conversationModel, SIGNAL(conversationRemoved(const std::string& uid))).wait(1000); CPPUNIT_ASSERT(conversationRemoved); - conversations = accInfo_.conversationModel->allFilteredConversations(); - i = std::find_if(conversations.begin(), conversations.end(), - [](const lrc::api::conversation::Info& conversation) { - return std::find(conversation.participants.begin(), - conversation.participants.end(), - "contact0") != conversation.participants.end(); - }); - CPPUNIT_ASSERT(i == conversations.end()); + + CPPUNIT_ASSERT(!hasConversationWithContact("contact0")); } void @@ -135,6 +227,7 @@ ConversationModelTester::testFilterAndGetConversations() SIGNAL(modelUpdated())).wait(1000); auto conversations = accInfo_.conversationModel->allFilteredConversations(); CPPUNIT_ASSERT_EQUAL((int)conversations.size(), 1); + // Count when filter exact name auto contacts = accInfo_.contactModel->getAllContacts(); CPPUNIT_ASSERT(contacts.size() != 0); // the daemon should return some contacts @@ -146,6 +239,7 @@ ConversationModelTester::testFilterAndGetConversations() CPPUNIT_ASSERT_EQUAL((int)conversations.size(), 1); // We should see the contact auto firstConversation = accInfo_.conversationModel->filteredConversation(0); CPPUNIT_ASSERT_EQUAL(firstConversation.participants.front(), contactUri); + // Count all contacts auto nbContact = 0; for (const auto& contact: contacts) @@ -171,8 +265,8 @@ ConversationModelTester::testSendMessageAndClearHistory() for (const auto& conversation: conversations) { if (conversation.uid == firstConversation) { conversationExists = true; - // Should contains "Hello World!" - CPPUNIT_ASSERT_EQUAL((int)conversation.interactions.size(), 1); + // Should contains "Contact Added" + "Hello World!" + CPPUNIT_ASSERT_EQUAL((int)conversation.interactions.size(), 2); CPPUNIT_ASSERT_EQUAL((*conversation.interactions.rbegin()).second.body, std::string("Hello World!")); break; } @@ -209,6 +303,7 @@ ConversationModelTester::testReceiveMessageAndSetRead() auto unreadMessage = WaitForSignalHelper(*accInfo_.conversationModel, SIGNAL(newUnreadMessage(const std::string&, uint64_t, const interaction::Info&))).wait(1000); CPPUNIT_ASSERT_EQUAL(unreadMessage, true); + // This message should be unread conversations = accInfo_.conversationModel->allFilteredConversations(); CPPUNIT_ASSERT(conversations.size() != 0); @@ -216,6 +311,7 @@ ConversationModelTester::testReceiveMessageAndSetRead() auto lastInteraction = *firstConversation.interactions.rbegin(); CPPUNIT_ASSERT(lastInteraction.second.status == lrc::api::interaction::Status::UNREAD); accInfo_.conversationModel->setInteractionRead(firstConversation.uid, lastInteraction.first); + // Now, the interaction should be READ conversations = accInfo_.conversationModel->allFilteredConversations(); CPPUNIT_ASSERT(conversations.size() != 0); @@ -296,44 +392,91 @@ ConversationModelTester::testClearUnreadInteractions() auto conversations = accInfo_.conversationModel->allFilteredConversations(); CPPUNIT_ASSERT(conversations.size() != 0); auto firstConversation = accInfo_.conversationModel->filteredConversation(0); + auto sender = firstConversation.participants.front().c_str(); + QMap<QString, QString> payloads; - // First message + + // Send a first message payloads["text/plain"] = "This is not a message"; - ConfigurationManager::instance().emitIncomingAccountMessage(accInfo_.id.c_str(), - firstConversation.participants.front().c_str(), payloads); + ConfigurationManager::instance().emitIncomingAccountMessage(accInfo_.id.c_str(), sender, payloads); auto unreadMessage = WaitForSignalHelper(*accInfo_.conversationModel, SIGNAL(newUnreadMessage(const std::string&, uint64_t, const interaction::Info&))).wait(1000); CPPUNIT_ASSERT_EQUAL(unreadMessage, true); - // Second message - ConfigurationManager::instance().emitIncomingAccountMessage(accInfo_.id.c_str(), - firstConversation.participants.front().c_str(), payloads); + + // Send a second message + ConfigurationManager::instance().emitIncomingAccountMessage(accInfo_.id.c_str(), sender, payloads); unreadMessage = WaitForSignalHelper(*accInfo_.conversationModel, SIGNAL(newUnreadMessage(const std::string&, uint64_t, const interaction::Info&))).wait(1000); CPPUNIT_ASSERT_EQUAL(unreadMessage, true); - conversations = accInfo_.conversationModel->allFilteredConversations(); - CPPUNIT_ASSERT(conversations.size() != 0); + + // Make sure both messages are unread firstConversation = accInfo_.conversationModel->filteredConversation(0); - CPPUNIT_ASSERT(firstConversation.interactions.size() != 0); - for(auto&& interaction : firstConversation.interactions) { - CPPUNIT_ASSERT(interaction.second.status == lrc::api::interaction::Status::UNREAD); - } + auto interactions = firstConversation.interactions.rbegin(); + CPPUNIT_ASSERT(interactions->second.status == lrc::api::interaction::Status::UNREAD); + CPPUNIT_ASSERT((++interactions)->second.status == lrc::api::interaction::Status::UNREAD); + // Clear conversation of unread interactions accInfo_.conversationModel->clearUnreadInteractions(firstConversation.uid); auto conversationUpdated = WaitForSignalHelper(*accInfo_.conversationModel, SIGNAL(conversationUpdated(const std::string&))).wait(1000); CPPUNIT_ASSERT_EQUAL(conversationUpdated, true); - conversations = accInfo_.conversationModel->allFilteredConversations(); - CPPUNIT_ASSERT(conversations.size() != 0); + + // Now make sure both messages are read firstConversation = accInfo_.conversationModel->filteredConversation(0); - for(auto&& interaction : firstConversation.interactions) { - CPPUNIT_ASSERT(interaction.second.status == lrc::api::interaction::Status::READ); - } + interactions = firstConversation.interactions.rbegin(); + CPPUNIT_ASSERT(interactions->second.status == lrc::api::interaction::Status::READ); + CPPUNIT_ASSERT((++interactions)->second.status == lrc::api::interaction::Status::READ); } void ConversationModelTester::tearDown() { +} + +bool +ConversationModelTester::hasConversationWithContact(std::string uri) +{ + auto conversations = accInfo_.conversationModel->allFilteredConversations(); + auto i = std::find_if(conversations.begin(), conversations.end(), + [&uri](const lrc::api::conversation::Info& conversation) { + return std::find(conversation.participants.begin(), + conversation.participants.end(), + uri) != conversation.participants.end(); + }); + return i != conversations.end(); +} + +void +ConversationModelTester::banContact(std::string uri) +{ + accInfo_.contactModel->removeContact(uri, true); + auto contactBanned = WaitForSignalHelper(ConfigurationManager::instance(), + SIGNAL(lrc::api::ConversationModel::filterChanged())).wait(2000); + CPPUNIT_ASSERT_EQUAL(contactBanned, true); +} + +bool +ConversationModelTester::isAContact(std::string uri) +{ + return !accInfo_.contactModel->getContactProfileId(uri).empty(); +} + +std::string +ConversationModelTester::addToContacts(std::string username) +{ + // Search contact + accInfo_.conversationModel->setFilter(username); + WaitForSignalHelper(*accInfo_.contactModel, + SIGNAL(modelUpdated())).wait(1000); + + // Add to contacts + auto uri = accInfo_.conversationModel->owner.contactModel->getContact("").profileInfo.uri; + accInfo_.conversationModel->makePermanent(uri); + auto contactAdded = WaitForSignalHelper(ConfigurationManager::instance(), + SIGNAL(contactAdded(const QString&, const QString&, bool))).wait(1000); + CPPUNIT_ASSERT_EQUAL(contactAdded, true); + return uri; } } // namespace test diff --git a/test/conversationmodeltester.h b/test/conversationmodeltester.h index 5fe9501a4b4a13c347016632052211df66cde56d..b88dc6c004ffa0a24271747693f2d25424383a41 100644 --- a/test/conversationmodeltester.h +++ b/test/conversationmodeltester.h @@ -51,6 +51,9 @@ class ConversationModelTester : public CppUnit::TestFixture { CPPUNIT_TEST(testCreateConference); CPPUNIT_TEST(testPlaceAudioOnlyCall); CPPUNIT_TEST(testClearUnreadInteractions); + CPPUNIT_TEST(testSendMessageToBannedContact); + CPPUNIT_TEST(testFilterBannedContact); + CPPUNIT_TEST(testPlaceCallWithBannedContact); CPPUNIT_TEST_SUITE_END(); public: @@ -83,6 +86,14 @@ public: * Send "Hello World!" to the first conversation and clear the history */ void testSendMessageAndClearHistory(); + /** + * Make sure it is not possible to send a message to a banned contact + */ + void testSendMessageToBannedContact(); + /** + * Make sure banned contacts only appear in perfect-match filter searches. + */ + void testFilterBannedContact(); /** * Receives a message from a conversation and set this message READ */ @@ -91,6 +102,10 @@ public: * Call the first conversation */ void testPlaceCall(); + /** + * Make sure it is not possible to call a banned contact + */ + void testPlaceCallWithBannedContact(); /** * Start and audio-only call with the first conversation */ @@ -111,6 +126,25 @@ public: protected: std::unique_ptr<lrc::api::Lrc> lrc_; const lrc::api::account::Info& accInfo_; + + // Helpers + + /** + * Ban contact with passed uri + */ + void banContact(std::string uri); + /** + * Return whether passed uri already maps to a contact or not + */ + bool isAContact(std::string uri); + /** + * Add passed usename to contacts and return its uri + */ + std::string addToContacts(std::string username); + /** + * Return whether a converation with passed contact uri exists or not + */ + bool hasConversationWithContact(std::string uri); }; } // namespace test diff --git a/test/mocks/configurationmanager_mock.h b/test/mocks/configurationmanager_mock.h index e7c883e1bd3dca1555eb6cfb15465112d8988223..180f9dfb9f255ce7e6b3f966b6f519f45baf1072 100644 --- a/test/mocks/configurationmanager_mock.h +++ b/test/mocks/configurationmanager_mock.h @@ -59,12 +59,18 @@ public: availableContacts_ << "contact0"; availableContacts_ << "contact1"; availableContacts_ << "contact2"; + availableContacts_ << "badguy0"; + availableContacts_ << "badguy1"; + availableContacts_ << "bannedContact"; availableContacts_ << "dummy"; for (auto& account: getAccountList()) { auto contacts = VectorMapStringString(); if (account.indexOf("ring") != -1) { for (auto& contactUri: availableContacts_) { if (contactUri == "dummy") break; + if (contactUri == "bannedContact") break; + if (contactUri == "badguy0") break; + if (contactUri == "badguy1") break; auto contact = QMap<QString, QString>(); contact.insert("id", contactUri); contact.insert("added", "true"); @@ -615,7 +621,12 @@ public Q_SLOTS: // METHODS auto contacts = accountToContactsMap[accountId]; for (auto c = 0 ; c < contacts.size() ; ++c) { if (contacts.at(c)["id"] == uri) { - contacts.remove(c); + if (ban) { + contacts[c].insert("removed", "true"); + contacts[c].insert("banned", "true"); + } else { + contacts.remove(c); + } emit contactRemoved(accountId, uri, ban); return; }