diff options
author | Claudio Cambra <claudio.cambra@nextcloud.com> | 2022-10-17 11:37:14 +0300 |
---|---|---|
committer | Claudio Cambra <claudio.cambra@nextcloud.com> | 2022-10-31 20:06:09 +0300 |
commit | a0ff46a7fbb94ed4a815244f14a7ef1caa9f1d97 (patch) | |
tree | a562b57bfcc292d1f449a6b2781994f530d7ddc8 | |
parent | e021f256345564193ebde2857903eaa5462cf5d1 (diff) |
Add testing for ShareeModel
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
-rw-r--r-- | test/CMakeLists.txt | 1 | ||||
-rw-r--r-- | test/testshareemodel.cpp | 442 |
2 files changed, 443 insertions, 0 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7ede2c3f2..820984631 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -65,6 +65,7 @@ nextcloud_add_test(ActivityData) nextcloud_add_test(TalkReply) nextcloud_add_test(LockFile) nextcloud_add_test(ShareModel) +nextcloud_add_test(ShareeModel) if( UNIX AND NOT APPLE ) nextcloud_add_test(InotifyWatcher) diff --git a/test/testshareemodel.cpp b/test/testshareemodel.cpp new file mode 100644 index 000000000..c9560a75f --- /dev/null +++ b/test/testshareemodel.cpp @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "gui/filedetails/shareemodel.h" + +#include <QTest> +#include <QSignalSpy> + +#include "accountmanager.h" +#include "syncenginetestutils.h" +#include "testhelper.h" + +using namespace OCC; + +static QByteArray fake400Response = R"( +{"ocs":{"meta":{"status":"failure","statuscode":400,"message":"Parameter is incorrect.\n"},"data":[]}} +)"; + +constexpr auto searchResultsReplyDelay = 100; + +class TestShareeModel : public QObject +{ + Q_OBJECT + +public: + ~TestShareeModel() override + { + AccountManager::instance()->deleteAccount(_accountState.data()); + }; + + struct FakeShareeDefinition + { + QString label; + + QString shareWith; + Sharee::Type type; + QString shareWithAdditionalInfo; + }; + + void appendShareeToReply(const FakeShareeDefinition &definition) + { + QJsonObject newShareeJson; + newShareeJson.insert("label", definition.label); + + QJsonObject newShareeValueJson; + newShareeValueJson.insert("shareWith", definition.shareWith); + newShareeValueJson.insert("shareType", definition.type); + newShareeValueJson.insert("shareWithAdditionalInfo", definition.shareWithAdditionalInfo); + + newShareeJson.insert("value", newShareeValueJson); + + QString category; + switch(definition.type) { + case Sharee::Circle: + category = QStringLiteral("circles"); + break; + case Sharee::Email: + category = QStringLiteral("emails"); + break; + case Sharee::Federated: + category = QStringLiteral("remotes"); + break; + case Sharee::Group: + category = QStringLiteral("groups"); + break; + case Sharee::Room: + category = QStringLiteral("rooms"); + break; + case Sharee::User: + category = QStringLiteral("users"); + break; + } + + auto shareesInCategory = _shareesMap.value(category).toJsonArray(); + shareesInCategory.append(newShareeJson); + _shareesMap.insert(category, shareesInCategory); + } + + void standardReplyPopulate() + { + appendShareeToReply(_michaelUserDefinition); + appendShareeToReply(_liamUserDefinition); + appendShareeToReply(_iqbalUserDefinition); + appendShareeToReply(_universityGroupDefinition); + appendShareeToReply(_testEmailDefinition); + } + + QVariantMap filteredSharees(const QString &searchString) + { + if (searchString.isEmpty()) { + return _shareesMap; + } + + QVariantMap returnSharees; + QJsonArray exactMatches; + + for (auto it = _shareesMap.constKeyValueBegin(); it != _shareesMap.constKeyValueEnd(); ++it) { + const auto shareesCategory = it->first; + const auto shareesArray = it->second.toJsonArray(); + QJsonArray filteredShareesArray; + + std::copy_if(shareesArray.cbegin(), shareesArray.cend(), std::back_inserter(filteredShareesArray), [&searchString](const QJsonValue &shareeValue) { + const auto shareeObject = shareeValue.toObject().value("value").toObject(); + const auto shareeShareWith = shareeObject.value("shareWith").toString(); + return shareeShareWith.contains(searchString, Qt::CaseInsensitive); + }); + + std::copy_if(filteredShareesArray.cbegin(), filteredShareesArray.cend(), std::back_inserter(exactMatches), [&searchString](const QJsonValue &shareeValue) { + const auto shareeObject = shareeValue.toObject().value("value").toObject(); + const auto shareeShareWith = shareeObject.value("shareWith").toString(); + return shareeShareWith == searchString; + }); + + returnSharees.insert(shareesCategory, filteredShareesArray); + } + + returnSharees.insert(QStringLiteral("exact"), exactMatches); + + return returnSharees; + } + + QByteArray testShareesReply(const QString &searchString) + { + QJsonObject root; + QJsonObject ocs; + QJsonObject meta; + + meta.insert("statuscode", 200); + + const auto resultSharees = filteredSharees(searchString); + const auto shareesJsonObject = QJsonObject::fromVariantMap(resultSharees); + + ocs.insert(QStringLiteral("data"), shareesJsonObject); + ocs.insert(QStringLiteral("meta"), meta); + root.insert(QStringLiteral("ocs"), ocs); + + return QJsonDocument(root).toJson(); + } + + int shareesCount(const QString &searchString) + { + const auto sharees = filteredSharees(searchString); + + auto count = 0; + const auto shareesCategories = sharees.values(); + for (const auto &shareesArrayValue : shareesCategories) { + const auto shareesArray = shareesArrayValue.toJsonArray(); + count += shareesArray.count(); + } + + return count; + } + + void resetTestData() + { + _alwaysReturnErrors = false; + _shareesMap.clear(); + } + + +private: + AccountPtr _account; + AccountStatePtr _accountState; + QScopedPointer<FakeQNAM> _fakeQnam; + + QVariantMap _shareesMap; + + // Some fake sharees of different categories + // ALL OF THEM CONTAIN AN 'I' !! Important for testing + FakeShareeDefinition _michaelUserDefinition { + QStringLiteral("Michael"), + QStringLiteral("michael"), + Sharee::User, + {}, + }; + FakeShareeDefinition _liamUserDefinition { + QStringLiteral("Liam"), + QStringLiteral("liam"), + Sharee::User, + {}, + }; + FakeShareeDefinition _iqbalUserDefinition { + QStringLiteral("Iqbal"), + QStringLiteral("iqbal"), + Sharee::User, + {}, + }; + + FakeShareeDefinition _universityGroupDefinition { + QStringLiteral("University"), + QStringLiteral("university"), + Sharee::Group, + {}, + }; + + FakeShareeDefinition _testEmailDefinition { + QStringLiteral("test.email@nextcloud.com"), + QStringLiteral("test.email@nextcloud.com"), + Sharee::Email, + {}, + }; + + bool _alwaysReturnErrors = false; + +private slots: + void initTestCase() + { + _fakeQnam.reset(new FakeQNAM({})); + _fakeQnam->setOverride([this](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *device) { + Q_UNUSED(device); + + QNetworkReply *reply = nullptr; + + if (_alwaysReturnErrors) { + reply = new FakeErrorReply(op, req, this, 400, fake400Response); + return reply; + } + + const auto reqUrl = req.url(); + const auto reqRawPath = reqUrl.path(); + const auto reqPath = reqRawPath.startsWith("/owncloud/") ? reqRawPath.mid(10) : reqRawPath; + qDebug() << req.url() << reqPath << op; + + if(req.url().toString().startsWith(_accountState->account()->url().toString()) && + reqPath == QStringLiteral("ocs/v2.php/apps/files_sharing/api/v1/sharees") && + req.attribute(QNetworkRequest::CustomVerbAttribute) == "GET") { + + const auto urlQuery = QUrlQuery(req.url()); + const auto searchParam = urlQuery.queryItemValue(QStringLiteral("search")); + const auto itemTypeParam = urlQuery.queryItemValue(QStringLiteral("itemType")); + const auto pageParam = urlQuery.queryItemValue(QStringLiteral("page")); + const auto perPageParam = urlQuery.queryItemValue(QStringLiteral("perPage")); + const auto lookupParam = urlQuery.queryItemValue(QStringLiteral("lookup")); + const auto formatParam = urlQuery.queryItemValue(QStringLiteral("format")); + + if (formatParam != QStringLiteral("json")) { + reply = new FakeErrorReply(op, req, this, 400, fake400Response); + } else { + reply = new FakePayloadReply(op, req, testShareesReply(searchParam), searchResultsReplyDelay, _fakeQnam.data()); + } + } + + return reply; + }); + + _account = Account::create(); + _account->setCredentials(new FakeCredentials{_fakeQnam.data()}); + _account->setUrl(QUrl(("owncloud://somehost/owncloud"))); + _accountState = new AccountState(_account); + AccountManager::instance()->addAccount(_account); + + // Let's verify our test is working -- all sharees have an I in their "shareWith" + standardReplyPopulate(); + const auto searchString = QStringLiteral("i"); + QCOMPARE(shareesCount(searchString), 5); + + const auto emailSearchString = QStringLiteral("email"); + QCOMPARE(shareesCount(emailSearchString), 1); + } + + void testSetAccountAndPath() + { + resetTestData(); + + ShareeModel model; + QAbstractItemModelTester modelTester(&model); + QCOMPARE(model.rowCount(), 0); + + QSignalSpy accountStateChanged(&model, &ShareeModel::accountStateChanged); + QSignalSpy shareItemIsFolderChanged(&model, &ShareeModel::shareItemIsFolderChanged); + QSignalSpy searchStringChanged(&model, &ShareeModel::searchStringChanged); + QSignalSpy lookupModeChanged(&model, &ShareeModel::lookupModeChanged); + QSignalSpy shareeBlocklistChanged(&model, &ShareeModel::shareeBlocklistChanged); + + model.setAccountState(_accountState.data()); + QCOMPARE(accountStateChanged.count(), 1); + QCOMPARE(model.accountState(), _accountState.data()); + + const auto shareItemIsFolder = !model.shareItemIsFolder(); + model.setShareItemIsFolder(shareItemIsFolder); + QCOMPARE(shareItemIsFolderChanged.count(), 1); + QCOMPARE(model.shareItemIsFolder(), shareItemIsFolder); + + const auto searchString = QStringLiteral("search string"); + model.setSearchString(searchString); + QCOMPARE(searchStringChanged.count(), 1); + QCOMPARE(model.searchString(), searchString); + + const auto lookupMode = ShareeModel::LookupMode::GlobalSearch; + model.setLookupMode(lookupMode); + QCOMPARE(lookupModeChanged.count(), 1); + QCOMPARE(model.lookupMode(), lookupMode); + + const ShareePtr sharee(new Sharee(_testEmailDefinition.shareWith, _testEmailDefinition.label, _testEmailDefinition.type)); + const QVariantList shareeBlocklist {QVariant::fromValue(sharee)}; + model.setShareeBlocklist(shareeBlocklist); + QCOMPARE(shareeBlocklistChanged.count(), 1); + QCOMPARE(model.shareeBlocklist(), shareeBlocklist); + } + + void testShareesFetch() + { + resetTestData(); + standardReplyPopulate(); + + ShareeModel model; + QAbstractItemModelTester modelTester(&model); + QCOMPARE(model.rowCount(), 0); + + model.setAccountState(_accountState.data()); + + QSignalSpy shareesReady(&model, &ShareeModel::shareesReady); + const auto searchString = QStringLiteral("i"); + model.setSearchString(searchString); + QVERIFY(shareesReady.wait(3000)); + QCOMPARE(model.rowCount(), shareesCount(searchString)); + + const auto emailSearchString = QStringLiteral("email"); + model.setSearchString(emailSearchString); + QVERIFY(shareesReady.wait(3000)); + QCOMPARE(model.rowCount(), shareesCount(emailSearchString)); + } + + void testFetchSignalling() + { + resetTestData(); + standardReplyPopulate(); + + ShareeModel model; + QAbstractItemModelTester modelTester(&model); + QCOMPARE(model.rowCount(), 0); + + model.setAccountState(_accountState.data()); + QSignalSpy fetchOngoingChanged(&model, &ShareeModel::fetchOngoingChanged); + const auto searchString = QStringLiteral("i"); + model.setSearchString(searchString); + + QVERIFY(fetchOngoingChanged.wait(1000)); + QCOMPARE(model.fetchOngoing(), true); + QVERIFY(fetchOngoingChanged.wait(3000)); + QCOMPARE(model.fetchOngoing(), false); + } + + void testData() + { + resetTestData(); + appendShareeToReply(_testEmailDefinition); + + ShareeModel model; + QAbstractItemModelTester modelTester(&model); + QCOMPARE(model.rowCount(), 0); + + model.setAccountState(_accountState.data()); + const auto searchString = QStringLiteral("i"); + model.setSearchString(searchString); + + QSignalSpy shareesReady(&model, &ShareeModel::shareesReady); + QVERIFY(shareesReady.wait(3000)); + QCOMPARE(model.rowCount(), shareesCount(searchString)); + + const auto shareeIndex = model.index(0, 0, {}); + + const ShareePtr expectedSharee(new Sharee(_testEmailDefinition.shareWith, _testEmailDefinition.label, _testEmailDefinition.type)); + const auto sharee = shareeIndex.data(ShareeModel::ShareeRole).value<ShareePtr>(); + QCOMPARE(sharee->format(), expectedSharee->format()); + QCOMPARE(sharee->shareWith(), expectedSharee->shareWith()); + QCOMPARE(sharee->displayName(), expectedSharee->displayName()); + QCOMPARE(sharee->type(), expectedSharee->type()); + + const auto expectedShareeDisplay = QString(_testEmailDefinition.label + QStringLiteral(" (email)")); + const auto shareeDisplay = shareeIndex.data(Qt::DisplayRole).toString(); + QCOMPARE(shareeDisplay, expectedShareeDisplay); + + const auto expectedAutoCompleterStringMatch = QString(_testEmailDefinition.label + + QStringLiteral(" (") + + _testEmailDefinition.shareWith + + QStringLiteral(")")); + const auto autoCompleterStringMatch = shareeIndex.data(ShareeModel::AutoCompleterStringMatchRole).toString(); + QCOMPARE(autoCompleterStringMatch, expectedAutoCompleterStringMatch); + } + + void testBlocklist() + { + resetTestData(); + standardReplyPopulate(); + + ShareeModel model; + QAbstractItemModelTester modelTester(&model); + QCOMPARE(model.rowCount(), 0); + + model.setAccountState(_accountState.data()); + + const ShareePtr sharee(new Sharee(_testEmailDefinition.shareWith, _testEmailDefinition.label, _testEmailDefinition.type)); + const QVariantList shareeBlocklist {QVariant::fromValue(sharee)}; + model.setShareeBlocklist(shareeBlocklist); + + QSignalSpy shareesReady(&model, &ShareeModel::shareesReady); + const auto searchString = QStringLiteral("i"); + model.setSearchString(searchString); + QVERIFY(shareesReady.wait(3000)); + QCOMPARE(model.rowCount(), shareesCount(searchString) - 1); + + const ShareePtr shareeTwo(new Sharee(_michaelUserDefinition.shareWith, _michaelUserDefinition.label, _michaelUserDefinition.type)); + const QVariantList largerShareeBlocklist {QVariant::fromValue(sharee), QVariant::fromValue(shareeTwo)}; + model.setShareeBlocklist(largerShareeBlocklist); + QCOMPARE(model.rowCount(), shareesCount(searchString) - 2); + } + + void testServerError() + { + resetTestData(); + _alwaysReturnErrors = true; + + ShareeModel model; + QAbstractItemModelTester modelTester(&model); + QCOMPARE(model.rowCount(), 0); + + model.setAccountState(_accountState.data()); + + QSignalSpy displayErrorMessage(&model, &ShareeModel::displayErrorMessage); + QSignalSpy fetchOngoingChanged(&model, &ShareeModel::fetchOngoingChanged); + model.setSearchString(QStringLiteral("i")); + QVERIFY(displayErrorMessage.wait(3000)); + + QCOMPARE(fetchOngoingChanged.count(), 2); + QCOMPARE(model.fetchOngoing(), false); + } +}; + +QTEST_MAIN(TestShareeModel) +#include "testshareemodel.moc" |