| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- /*
- * 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
- int _numLookupSearchParamSet = 0;
- 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::Invalid:
- category = QStringLiteral("invalid");
- break;
- 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;
- case Sharee::LookupServerSearch:
- category = QStringLiteral("placeholder_lookupserversearch");
- break;
- case Sharee::LookupServerSearchResults:
- category = QStringLiteral("placeholder_lookupserversearchresults");
- 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 (!lookupParam.isEmpty() && lookupParam == QStringLiteral("true")) {
- ++_numLookupSearchParamSet;
- }
- 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) + 1);
- QVERIFY(model.rowCount() > 0);
- auto lastElementType = model.data(model.index(model.rowCount() - 1), ShareeModel::Roles::TypeRole).toInt();
- QVERIFY(lastElementType == Sharee::Type::LookupServerSearch);
- const auto emailSearchString = QStringLiteral("email");
- model.setSearchString(emailSearchString);
- QVERIFY(shareesReady.wait(3000));
- QCOMPARE(model.rowCount(), shareesCount(emailSearchString) + 1);
- QVERIFY(model.rowCount() > 0);
- lastElementType = model.data(model.index(model.rowCount() - 1), ShareeModel::Roles::TypeRole).toInt();
- QVERIFY(lastElementType == Sharee::Type::LookupServerSearch);
- }
- void testShareesFetchGlobally()
- {
- resetTestData();
- standardReplyPopulate();
- ShareeModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- model.setAccountState(_accountState.data());
- QSignalSpy shareesReady(&model, &ShareeModel::shareesReady);
- const auto emailSearchString = QStringLiteral("email");
- model.setSearchString(emailSearchString);
- QVERIFY(shareesReady.wait(3000));
- QCOMPARE(model.rowCount(), shareesCount(emailSearchString) + 1);
- QVERIFY(model.rowCount() > 0);
- auto lastElementType = model.data(model.index(model.rowCount() - 1), ShareeModel::Roles::TypeRole).toInt();
- QVERIFY(lastElementType == Sharee::Type::LookupServerSearch);
- QCOMPARE(_numLookupSearchParamSet, 0);
- QSignalSpy lookupModeChanged(&model, &ShareeModel::lookupModeChanged);
- model.searchGlobally();
- QVERIFY(shareesReady.wait(3000));
- QCOMPARE(lookupModeChanged.count(), 2);
- QVERIFY(model.lookupMode() == ShareeModel::LookupMode::LocalSearch);
- QCOMPARE(model.rowCount(), shareesCount(emailSearchString) + 1);
- QVERIFY(model.rowCount() > 0);
- lastElementType = model.data(model.index(model.rowCount() - 1), ShareeModel::Roles::TypeRole).toInt();
- QVERIFY(lastElementType == Sharee::Type::LookupServerSearchResults);
- QCOMPARE(_numLookupSearchParamSet, 1);
- }
- 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) + 1);
- auto lastElementType = model.data(model.index(model.rowCount() - 1), ShareeModel::Roles::TypeRole).toInt();
- QVERIFY(lastElementType == Sharee::Type::LookupServerSearch);
- 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 + 1);
- auto lastElementType = model.data(model.index(model.rowCount() - 1), ShareeModel::Roles::TypeRole).toInt();
- QVERIFY(lastElementType == Sharee::Type::LookupServerSearch);
- 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 + 1);
- lastElementType = model.data(model.index(model.rowCount() - 1), ShareeModel::Roles::TypeRole).toInt();
- QVERIFY(lastElementType == Sharee::Type::LookupServerSearch);
- }
- 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"
|