| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981 |
- /*
- * Copyright (C) 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/sharemodel.h"
- #include <QTest>
- #include <QAbstractItemModelTester>
- #include <QSignalSpy>
- #include <QFileInfo>
- #include <QFlags>
- #include <QDateTime>
- #include <QTimeZone>
- #include "sharetestutils.h"
- #include "libsync/theme.h"
- using namespace OCC;
- class TestShareModel : public QObject
- {
- Q_OBJECT
- private:
- ShareTestHelper helper;
- FakeShareDefinition _testLinkShareDefinition;
- FakeShareDefinition _testEmailShareDefinition;
- FakeShareDefinition _testUserShareDefinition;
- FakeShareDefinition _testRemoteShareDefinition;
- private slots:
- void initTestCase()
- {
- QSignalSpy helperSetupSucceeded(&helper, &ShareTestHelper::setupSucceeded);
- helper.setup();
- QCOMPARE(helperSetupSucceeded.count(), 1);
- const auto testSharePassword = "3|$argon2id$v=19$m=65536,"
- "t=4,"
- "p=1$M2FoLnliWkhIZkwzWjFBQg$BPraP+JUqP1sV89rkymXpCGxHBlCct6bZ39xUGaYQ5w";
- const auto testShareNote = QStringLiteral("This is a note!");
- const auto testShareExpiration = QDate::currentDate().addDays(1).toString(helper.expectedDtFormat);
- const auto linkShareLabel = QStringLiteral("Link share label");
- _testLinkShareDefinition = FakeShareDefinition(&helper,
- Share::TypeLink,
- {},
- linkShareLabel,
- testSharePassword,
- testShareNote,
- testShareExpiration);
- const auto emailShareShareWith = QStringLiteral("test-email@nextcloud.com");
- const auto emailShareShareWithDisplayName = QStringLiteral("Test email");
- _testEmailShareDefinition = FakeShareDefinition(&helper,
- Share::TypeEmail,
- emailShareShareWith,
- emailShareShareWithDisplayName,
- testSharePassword,
- testShareNote,
- testShareExpiration);
- const auto userShareShareWith = QStringLiteral("user");
- const auto userShareShareWithDisplayName("A Nextcloud user");
- _testUserShareDefinition = FakeShareDefinition(&helper,
- Share::TypeUser,
- userShareShareWith,
- userShareShareWithDisplayName);
- const auto remoteShareShareWith = QStringLiteral("remote_share");
- const auto remoteShareShareWithDisplayName("A remote share");
- _testRemoteShareDefinition = FakeShareDefinition(&helper,
- Share::TypeRemote,
- remoteShareShareWith,
- remoteShareShareWithDisplayName);
- qRegisterMetaType<ShareePtr>("ShareePtr");
- }
- void testSetAccountAndPath()
- {
- helper.resetTestData();
- // Test with a link share
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy accountStateChanged(&model, &ShareModel::accountStateChanged);
- QSignalSpy localPathChanged(&model, &ShareModel::localPathChanged);
- QSignalSpy accountConnectedChanged(&model, &ShareModel::accountConnectedChanged);
- QSignalSpy sharingEnabledChanged(&model, &ShareModel::sharingEnabledChanged);
- QSignalSpy publicLinkSharesEnabledChanged(&model, &ShareModel::publicLinkSharesEnabledChanged);
- model.setAccountState(helper.accountState.data());
- QCOMPARE(accountStateChanged.count(), 1);
- // Check all the account-related properties of the model
- QCOMPARE(model.accountConnected(), helper.accountState->isConnected());
- QCOMPARE(model.sharingEnabled(), helper.account->capabilities().shareAPI());
- QCOMPARE(model.publicLinkSharesEnabled() && Theme::instance()->linkSharing(), helper.account->capabilities().sharePublicLink());
- QCOMPARE(Theme::instance()->userGroupSharing(), model.userGroupSharingEnabled());
- const QString localPath(helper.fakeFolder.localPath() + helper.testFileName);
- model.setLocalPath(localPath);
- QCOMPARE(localPathChanged.count(), 1);
- QCOMPARE(model.localPath(), localPath);
- }
- void testSuccessfulFetchShares()
- {
- helper.resetTestData();
- // Test with a link share and a user/group email share "from the server"
- helper.appendShareReplyData(_testLinkShareDefinition);
- helper.appendShareReplyData(_testEmailShareDefinition);
- helper.appendShareReplyData(_testUserShareDefinition);
- QCOMPARE(helper.shareCount(), 3);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share
- }
- void testFetchSharesFailedError()
- {
- helper.resetTestData();
- // Test with a link share "from the server"
- helper.appendShareReplyData(_testLinkShareDefinition);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy serverError(&model, &ShareModel::serverError);
- // Test fetching the shares of a file that does not exist
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + "wrong-filename-oops.md");
- QVERIFY(serverError.wait(3000));
- QCOMPARE(model.hasInitialShareFetchCompleted(), true);
- QCOMPARE(model.rowCount(), 0); // Make sure no placeholder nor internal link share
- }
- void testCorrectFetchOngoingSignalling()
- {
- helper.resetTestData();
- // Test with a link share "from the server"
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy fetchOngoingChanged(&model, &ShareModel::fetchOngoingChanged);
- // Make sure we are correctly signalling the loading state of the fetch
- // Model resets twice when we set account and local path, resetting all model state.
- model.setAccountState(helper.accountState.data());
- QCOMPARE(fetchOngoingChanged.count(), 1);
- QCOMPARE(model.fetchOngoing(), false);
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- // If we can grab shares it then indicates fetch ongoing...
- QCOMPARE(fetchOngoingChanged.count(), 3);
- QCOMPARE(model.fetchOngoing(), true);
- // Then indicates fetch finished when done.
- QVERIFY(fetchOngoingChanged.wait(3000));
- QCOMPARE(model.fetchOngoing(), false);
- }
- void testCorrectInitialFetchCompleteSignalling()
- {
- helper.resetTestData();
- // Test with a link share "from the server"
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy accountStateChanged(&model, &ShareModel::accountStateChanged);
- QSignalSpy localPathChanged(&model, &ShareModel::localPathChanged);
- QSignalSpy hasInitialShareFetchCompletedChanged(&model, &ShareModel::hasInitialShareFetchCompletedChanged);
- // Make sure we are correctly signalling the loading state of the fetch
- // Model resets twice when we set account and local path, resetting all model state.
- model.setAccountState(helper.accountState.data());
- QCOMPARE(accountStateChanged.count(), 1);
- QCOMPARE(hasInitialShareFetchCompletedChanged.count(), 1);
- QCOMPARE(model.hasInitialShareFetchCompleted(), false);
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QCOMPARE(localPathChanged.count(), 1);
- QCOMPARE(hasInitialShareFetchCompletedChanged.count(), 2);
- QCOMPARE(model.hasInitialShareFetchCompleted(), false);
- // Once we have acquired shares from the server the initial share fetch is completed
- QVERIFY(hasInitialShareFetchCompletedChanged.wait(3000));
- QCOMPARE(hasInitialShareFetchCompletedChanged.count(), 3);
- QCOMPARE(model.hasInitialShareFetchCompleted(), true);
- }
- // Link shares and user group shares have slightly different behaviour in model.data()
- void testModelLinkShareData()
- {
- helper.resetTestData();
- // Test with a link share "from the server"
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Remember internal link share!
- // Placeholder link share gets added after we are done parsing fetched shares, and the
- // internal link share is added after we receive a reply from the PROPFIND, which we
- // send before fetching the shares, so it will be added first.
- //
- // Hence we grab the remote share in between.
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- QVERIFY(!shareIndex.data(Qt::DisplayRole).toString().isEmpty());
- QCOMPARE(shareIndex.data(ShareModel::ShareTypeRole).toInt(), _testLinkShareDefinition.shareType);
- QCOMPARE(shareIndex.data(ShareModel::ShareIdRole).toString(), _testLinkShareDefinition.shareId);
- QCOMPARE(shareIndex.data(ShareModel::LinkRole).toString(), _testLinkShareDefinition.linkShareUrl);
- QCOMPARE(shareIndex.data(ShareModel::LinkShareNameRole).toString(), _testLinkShareDefinition.linkShareName);
- QCOMPARE(shareIndex.data(ShareModel::LinkShareLabelRole).toString(), _testLinkShareDefinition.linkShareLabel);
- QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), !_testLinkShareDefinition.shareNote.isEmpty());
- QCOMPARE(shareIndex.data(ShareModel::NoteRole).toString(), _testLinkShareDefinition.shareNote);
- QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), !_testLinkShareDefinition.sharePassword.isEmpty());
- // We don't expose the fetched password to the user as it's useless to them
- QCOMPARE(shareIndex.data(ShareModel::PasswordRole).toString(), QString());
- QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), SharePermissions(_testLinkShareDefinition.sharePermissions).testFlag(SharePermissionUpdate));
- const auto expectedLinkShareExpireDate = QDate::fromString(_testLinkShareDefinition.shareExpiration, helper.expectedDtFormat);
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), expectedLinkShareExpireDate.isValid());
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), expectedLinkShareExpireDate.startOfDay(Qt::UTC).toMSecsSinceEpoch());
- const auto iconUrl = shareIndex.data(ShareModel::IconUrlRole).toString();
- QVERIFY(iconUrl.contains("public.svg"));
- }
- void testModelEmailShareData()
- {
- helper.resetTestData();
- // Test with a user/group email share "from the server"
- helper.appendShareReplyData(_testEmailShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(model.rowCount(), 3); // Remember about placeholder and internal link share
- // Placeholder link share gets added after we are done parsing fetched shares, and the
- // internal link share is added after we receive a reply from the PROPFIND, which we
- // send before fetching the shares, so it will be added first.
- //
- // Hence we grab the remote share in between.
- const auto shareIndex = model.index(1, 0, {});
- QVERIFY(!shareIndex.data(Qt::DisplayRole).toString().isEmpty());
- QCOMPARE(shareIndex.data(ShareModel::ShareTypeRole).toInt(), _testEmailShareDefinition.shareType);
- QCOMPARE(shareIndex.data(ShareModel::ShareIdRole).toString(), _testEmailShareDefinition.shareId);
- QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), !_testEmailShareDefinition.shareNote.isEmpty());
- QCOMPARE(shareIndex.data(ShareModel::NoteRole).toString(), _testEmailShareDefinition.shareNote);
- QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), !_testEmailShareDefinition.sharePassword.isEmpty());
- // We don't expose the fetched password to the user as it's useless to them
- QCOMPARE(shareIndex.data(ShareModel::PasswordRole).toString(), QString());
- QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), SharePermissions(_testEmailShareDefinition.sharePermissions).testFlag(SharePermissionUpdate));
- const auto expectedShareExpireDate = QDate::fromString(_testEmailShareDefinition.shareExpiration, helper.expectedDtFormat);
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), expectedShareExpireDate.isValid());
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), expectedShareExpireDate.startOfDay(Qt::UTC).toMSecsSinceEpoch());
- const auto iconUrl = shareIndex.data(ShareModel::IconUrlRole).toString();
- QVERIFY(iconUrl.contains("email.svg"));
- }
- void testModelUserShareData()
- {
- helper.resetTestData();
- // Test with a user/group user share "from the server"
- helper.appendShareReplyData(_testUserShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(model.rowCount(), 3); // Remember about placeholder and internal link share
- // Placeholder link share gets added after we are done parsing fetched shares, and the
- // internal link share is added after we receive a reply from the PROPFIND, which we
- // send before fetching the shares, so it will be added first.
- //
- // Hence we grab the remote share in between.
- const auto shareIndex = model.index(1, 0, {});
- QVERIFY(!shareIndex.data(Qt::DisplayRole).toString().isEmpty());
- QCOMPARE(shareIndex.data(ShareModel::ShareTypeRole).toInt(), _testUserShareDefinition.shareType);
- QCOMPARE(shareIndex.data(ShareModel::ShareIdRole).toString(), _testUserShareDefinition.shareId);
- QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), !_testUserShareDefinition.shareNote.isEmpty());
- QCOMPARE(shareIndex.data(ShareModel::NoteRole).toString(), _testUserShareDefinition.shareNote);
- QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), !_testUserShareDefinition.sharePassword.isEmpty());
- // We don't expose the fetched password to the user as it's useless to them
- QCOMPARE(shareIndex.data(ShareModel::PasswordRole).toString(), QString());
- QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), SharePermissions(_testUserShareDefinition.sharePermissions).testFlag(SharePermissionUpdate));
- const auto expectedShareExpireDate = QDate::fromString(_testUserShareDefinition.shareExpiration, helper.expectedDtFormat);
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), expectedShareExpireDate.isValid());
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), expectedShareExpireDate.startOfDay(Qt::UTC).toMSecsSinceEpoch());
- const auto iconUrl = shareIndex.data(ShareModel::IconUrlRole).toString();
- QVERIFY(iconUrl.contains("user.svg"));
- // Check correct user avatar
- const auto avatarUrl = shareIndex.data(ShareModel::AvatarUrlRole).toString();
- const auto relativeAvatarPath = QString("remote.php/dav/avatars/%1/%2.png").arg(_testUserShareDefinition.shareShareWith, QString::number(64));
- const auto expectedAvatarPath = Utility::concatUrlPath(helper.account->url(), relativeAvatarPath).toString();
- const QString expectedUrl(QStringLiteral("image://tray-image-provider/") + expectedAvatarPath);
- QCOMPARE(avatarUrl, expectedUrl);
- }
- void testSuccessfulCreateShares()
- {
- helper.resetTestData();
- // Test with an existing link share
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Test if it gets added
- model.createNewLinkShare();
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 2); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Test if it's the type we wanted
- const auto newLinkShareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(newLinkShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypeLink);
- // Do it again with a different type
- const ShareePtr sharee(new Sharee("testsharee@nextcloud.com", "Test sharee", Sharee::Type::Email));
- model.createNewUserGroupShare(sharee);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 3); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Test if it's the type we wanted
- const auto newUserGroupShareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(newUserGroupShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypeEmail);
- // Confirm correct addition of share with password
- const auto password = QStringLiteral("a pretty bad password but good thing it doesn't matter!");
- model.createNewLinkShareWithPassword(password);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 4); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- model.createNewUserGroupShareWithPassword(sharee, password);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 5); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- helper.resetTestData();
- }
- void testEnforcePasswordShares()
- {
- helper.resetTestData();
- // Enforce passwords for shares in capabilities
- const QVariantMap enforcePasswordsCapabilities {
- {QStringLiteral("files_sharing"), QVariantMap {
- {QStringLiteral("api_enabled"), true},
- {QStringLiteral("default_permissions"), 19},
- {QStringLiteral("public"), QVariantMap {
- {QStringLiteral("enabled"), true},
- {QStringLiteral("expire_date"), QVariantMap {
- {QStringLiteral("days"), 30},
- {QStringLiteral("enforced"), false},
- }},
- {QStringLiteral("expire_date_internal"), QVariantMap {
- {QStringLiteral("days"), 30},
- {QStringLiteral("enforced"), false},
- }},
- {QStringLiteral("expire_date_remote"), QVariantMap {
- {QStringLiteral("days"), 30},
- {QStringLiteral("enforced"), false},
- }},
- {QStringLiteral("password"), QVariantMap {
- {QStringLiteral("enforced"), true},
- }},
- }},
- {QStringLiteral("sharebymail"), QVariantMap {
- {QStringLiteral("enabled"), true},
- {QStringLiteral("password"), QVariantMap {
- {QStringLiteral("enforced"), true},
- }},
- }},
- }},
- };
- helper.account->setCapabilities(enforcePasswordsCapabilities);
- QVERIFY(helper.account->capabilities().sharePublicLinkEnforcePassword());
- QVERIFY(helper.account->capabilities().shareEmailPasswordEnforced());
- // Test with a link share "from the server"
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Confirm that the model requests a password
- QSignalSpy requestPasswordForLinkShare(&model, &ShareModel::requestPasswordForLinkShare);
- model.createNewLinkShare();
- QVERIFY(requestPasswordForLinkShare.wait(3000));
- QSignalSpy requestPasswordForEmailShare(&model, &ShareModel::requestPasswordForEmailSharee);
- const ShareePtr sharee(new Sharee("testsharee@nextcloud.com", "Test sharee", Sharee::Type::Email));
- model.createNewUserGroupShare(sharee);
- QCOMPARE(requestPasswordForEmailShare.count(), 1);
- // Test that the model data is correctly reporting that passwords are enforced
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(shareIndex.data(ShareModel::PasswordEnforcedRole).toBool(), true);
- QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), true);
- }
- void testEnforceExpireDate()
- {
- helper.resetTestData();
- const auto internalExpireDays = 45;
- const auto publicExpireDays = 30;
- const auto remoteExpireDays = 25;
- // Enforce expire dates for shares in capabilities
- const QVariantMap enforcePasswordsCapabilities {
- {QStringLiteral("files_sharing"), QVariantMap {
- {QStringLiteral("api_enabled"), true},
- {QStringLiteral("default_permissions"), 19},
- {QStringLiteral("public"), QVariantMap {
- {QStringLiteral("enabled"), true},
- {QStringLiteral("expire_date"), QVariantMap {
- {QStringLiteral("days"), publicExpireDays},
- {QStringLiteral("enforced"), true},
- }},
- {QStringLiteral("expire_date_internal"), QVariantMap {
- {QStringLiteral("days"), internalExpireDays},
- {QStringLiteral("enforced"), true},
- }},
- {QStringLiteral("expire_date_remote"), QVariantMap {
- {QStringLiteral("days"), remoteExpireDays},
- {QStringLiteral("enforced"), true},
- }},
- {QStringLiteral("password"), QVariantMap {
- {QStringLiteral("enforced"), false},
- }},
- }},
- {QStringLiteral("sharebymail"), QVariantMap {
- {QStringLiteral("enabled"), true},
- {QStringLiteral("password"), QVariantMap {
- {QStringLiteral("enforced"), true},
- }},
- }},
- }},
- };
- helper.account->setCapabilities(enforcePasswordsCapabilities);
- QVERIFY(helper.account->capabilities().sharePublicLinkEnforceExpireDate());
- QVERIFY(helper.account->capabilities().shareInternalEnforceExpireDate());
- QVERIFY(helper.account->capabilities().shareRemoteEnforceExpireDate());
- // Test with shares "from the server"
- helper.appendShareReplyData(_testLinkShareDefinition);
- helper.appendShareReplyData(_testEmailShareDefinition);
- helper.appendShareReplyData(_testRemoteShareDefinition);
- QCOMPARE(helper.shareCount(), 3);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Test that the model data is correctly reporting that expire dates are enforced for all share types
- for(auto i = 0; i < model.rowCount(); ++i) {
- const auto shareIndex = model.index(i, 0, {});
- const auto shareType = shareIndex.data(ShareModel::ShareTypeRole).toInt();
- const auto expectTrue = shareType != ShareModel::ShareTypePlaceholderLink &&
- shareType != ShareModel::ShareTypeInternalLink;
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnforcedRole).toBool(), expectTrue);
- QDateTime expectedExpireDateTime;
- switch(shareType) {
- case Share::TypeInternalLink:
- case Share::TypePlaceholderLink:
- return;
- case Share::TypeUser:
- case Share::TypeGroup:
- case Share::TypeCircle:
- case Share::TypeRoom:
- expectedExpireDateTime = QDate::currentDate().addDays(internalExpireDays).startOfDay(QTimeZone::utc());
- break;
- case Share::TypeLink:
- case Share::TypeEmail:
- expectedExpireDateTime = QDate::currentDate().addDays(publicExpireDays).startOfDay(QTimeZone::utc());
- break;
- case Share::TypeRemote:
- expectedExpireDateTime = QDate::currentDate().addDays(remoteExpireDays).startOfDay(QTimeZone::utc());
- break;
- }
- QCOMPARE(shareIndex.data(ShareModel::EnforcedMaximumExpireDateRole).toLongLong(), expectedExpireDateTime.toMSecsSinceEpoch());
- }
- }
- void testSuccessfulDeleteShares()
- {
- helper.resetTestData();
- // Test with an existing link share
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Create share
- model.createNewLinkShare();
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 2); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Test if it gets deleted properly
- const auto latestLinkShare = model.index(model.rowCount() - 1, 0, {}).data(ShareModel::ShareRole).value<SharePtr>();
- QSignalSpy shareDeleted(latestLinkShare.data(), &LinkShare::shareDeleted);
- model.deleteShare(latestLinkShare);
- QVERIFY(shareDeleted.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- helper.resetTestData();
- }
- void testPlaceholderLinkShare()
- {
- helper.resetTestData();
- // Start with no shares; should show the placeholder link share
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0); // There should be no placeholder yet
- QSignalSpy hasInitialShareFetchCompletedChanged(&model, &ShareModel::hasInitialShareFetchCompletedChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(hasInitialShareFetchCompletedChanged.wait(5000));
- QVERIFY(model.hasInitialShareFetchCompleted());
- QCOMPARE(model.rowCount(), 2); // There should be a placeholder and internal link share now
- const QPersistentModelIndex placeholderLinkShareIndex(model.index(model.rowCount() - 1, 0, {}));
- QCOMPARE(placeholderLinkShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypePlaceholderLink);
- // Test adding a user group share -- we should still be showing a placeholder link share
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- const ShareePtr sharee(new Sharee("testsharee@nextcloud.com", "Test sharee", Sharee::Type::Email));
- model.createNewUserGroupShare(sharee);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 2); // Internal link share too!
- QVERIFY(placeholderLinkShareIndex.isValid());
- QCOMPARE(placeholderLinkShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypePlaceholderLink);
- // Now try adding a link share, which should remove the placeholder
- model.createNewLinkShare();
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 2); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- QVERIFY(!placeholderLinkShareIndex.isValid());
- // Now delete the only link share, which should bring back the placeholder link share
- const auto latestLinkShare = model.index(model.rowCount() - 1, 0, {}).data(ShareModel::ShareRole).value<SharePtr>();
- QSignalSpy shareDeleted(latestLinkShare.data(), &LinkShare::shareDeleted);
- model.deleteShare(latestLinkShare);
- QVERIFY(shareDeleted.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 2); // Internal link share too!
- const auto newPlaceholderLinkShareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(newPlaceholderLinkShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypePlaceholderLink);
- helper.resetTestData();
- }
- void testSuccessfulToggleAllowEditing()
- {
- helper.resetTestData();
- // Test with an existing link share
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), SharePermissions(_testLinkShareDefinition.sharePermissions).testFlag(SharePermissionUpdate));
- const auto share = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
- QSignalSpy permissionsSet(share.data(), &Share::permissionsSet);
- model.toggleShareAllowEditing(share, false);
- QVERIFY(permissionsSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), false);
- }
- void testSuccessfulPasswordSet()
- {
- helper.resetTestData();
- // Test with an existing link share.
- // This one has a pre-existing password
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), true);
- const auto share = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
- QSignalSpy passwordSet(share.data(), &Share::passwordSet);
- model.toggleSharePasswordProtect(share, false);
- QVERIFY(passwordSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), false);
- const auto password = QStringLiteral("a pretty bad password but good thing it doesn't matter!");
- model.setSharePassword(share, password);
- QVERIFY(passwordSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), true);
- // The model stores the recently set password.
- // We want to present the user with it in the UI while the model is alive
- QCOMPARE(shareIndex.data(ShareModel::PasswordRole).toString(), password);
- }
- void testSuccessfulExpireDateSet()
- {
- helper.resetTestData();
- // Test with an existing link share.
- // This one has a pre-existing expire date
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Check what we know
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), true);
- // Disable expire date
- const auto sharePtr = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
- const auto linkSharePtr = sharePtr.dynamicCast<LinkShare>(); // Need to connect to signal
- QSignalSpy expireDateSet(linkSharePtr.data(), &LinkShare::expireDateSet);
- model.toggleShareExpirationDate(sharePtr, false);
- QVERIFY(expireDateSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), false);
- // Set a new expire date
- const auto expireDateMsecs = QDate::currentDate().addDays(10).startOfDay(Qt::UTC).toMSecsSinceEpoch();
- model.setShareExpireDate(linkSharePtr, expireDateMsecs);
- QVERIFY(expireDateSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), expireDateMsecs);
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), true);
- // Test the QML-specific slot
- const QVariant newExpireDateMsecs = QDate::currentDate().addDays(20).startOfDay(Qt::UTC).toMSecsSinceEpoch();
- model.setShareExpireDateFromQml(QVariant::fromValue(sharePtr), newExpireDateMsecs);
- QVERIFY(expireDateSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), newExpireDateMsecs);
- QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), true);
- }
- void testSuccessfulNoteSet()
- {
- helper.resetTestData();
- // Test with an existing link share.
- // This one has a pre-existing password
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), true);
- const auto sharePtr = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
- const auto linkSharePtr = sharePtr.dynamicCast<LinkShare>(); // Need to connect to signal
- QSignalSpy noteSet(linkSharePtr.data(), &LinkShare::noteSet);
- model.toggleShareNoteToRecipient(sharePtr, false);
- QVERIFY(noteSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), false);
- const auto note = QStringLiteral("Don't forget to test everything!");
- model.setShareNote(sharePtr, note);
- QVERIFY(noteSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), true);
- // The model stores the recently set password.
- // We want to present the user with it in the UI while the model is alive
- QCOMPARE(shareIndex.data(ShareModel::NoteRole).toString(), note);
- }
- void testSuccessfulLinkShareLabelSet()
- {
- helper.resetTestData();
- // Test with an existing link share.
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- QCOMPARE(shareIndex.data(ShareModel::LinkShareLabelRole).toBool(), true);
- const auto sharePtr = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
- const auto linkSharePtr = sharePtr.dynamicCast<LinkShare>(); // Need to connect to signal
- QSignalSpy labelSet(linkSharePtr.data(), &LinkShare::labelSet);
- const auto label = QStringLiteral("New link share label!");
- model.setLinkShareLabel(linkSharePtr, label);
- QVERIFY(labelSet.wait(3000));
- QCOMPARE(shareIndex.data(ShareModel::LinkShareLabelRole).toString(), label);
- }
- void testSharees()
- {
- helper.resetTestData();
- helper.appendShareReplyData(_testLinkShareDefinition);
- helper.appendShareReplyData(_testEmailShareDefinition);
- helper.appendShareReplyData(_testUserShareDefinition);
- QCOMPARE(helper.shareCount(), 3);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- QCOMPARE(model.sharees().count(), 2); // Link shares don't have sharees
- // Test adding a user group share -- we should still be showing a placeholder link share
- const ShareePtr sharee(new Sharee("testsharee@nextcloud.com", "Test sharee", Sharee::Type::Email));
- model.createNewUserGroupShare(sharee);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 4); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- const auto sharees = model.sharees();
- QCOMPARE(sharees.count(), 3); // Link shares don't have sharees
- const auto lastSharee = sharees.last().value<ShareePtr>();
- QVERIFY(lastSharee);
- // Remove the user group share we just added
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- const auto sharePtr = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
- model.deleteShare(sharePtr);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Now check the sharee is gone
- QCOMPARE(model.sharees().count(), 2);
- }
- void testSharePropertySetError()
- {
- helper.resetTestData();
- // Serve a broken share definition from the server to force an error
- auto brokenLinkShareDefinition = _testLinkShareDefinition;
- brokenLinkShareDefinition.shareId = QString();
- helper.appendShareReplyData(brokenLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- ShareModel model;
- QAbstractItemModelTester modelTester(&model);
- QCOMPARE(model.rowCount(), 0);
- QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
- model.setAccountState(helper.accountState.data());
- model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
- QVERIFY(sharesChanged.wait(5000));
- QCOMPARE(helper.shareCount(), 1); // Check our test is working!
- QCOMPARE(model.rowCount(), helper.shareCount() + 1); // Internal link share!
- // Reset the fake server to pretend like nothing is wrong there
- helper.resetTestShares();
- helper.appendShareReplyData(_testLinkShareDefinition);
- QCOMPARE(helper.shareCount(), 1);
- // Now try changing a property of the share
- const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
- const auto share = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
- QSignalSpy serverError(&model, &ShareModel::serverError);
- model.toggleShareAllowEditing(share, false);
- QVERIFY(serverError.wait(3000));
- // Specific signal for password set error
- QSignalSpy passwordSetError(&model, &ShareModel::passwordSetError);
- const auto password = QStringLiteral("a pretty bad password but good thing it doesn't matter!");
- model.setSharePassword(share, password);
- QVERIFY(passwordSetError.wait(3000));
- }
- };
- QTEST_MAIN(TestShareModel)
- #include "testsharemodel.moc"
|