testsharemodel.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  1. /*
  2. * Copyright (C) by Claudio Cambra <claudio.cambra@nextcloud.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. * for more details.
  13. */
  14. #include "gui/filedetails/sharemodel.h"
  15. #include <QTest>
  16. #include <QAbstractItemModelTester>
  17. #include <QSignalSpy>
  18. #include <QFileInfo>
  19. #include <QFlags>
  20. #include <QDateTime>
  21. #include <QTimeZone>
  22. #include "sharetestutils.h"
  23. #include "libsync/theme.h"
  24. using namespace OCC;
  25. class TestShareModel : public QObject
  26. {
  27. Q_OBJECT
  28. private:
  29. ShareTestHelper helper;
  30. FakeShareDefinition _testLinkShareDefinition;
  31. FakeShareDefinition _testEmailShareDefinition;
  32. FakeShareDefinition _testUserShareDefinition;
  33. FakeShareDefinition _testRemoteShareDefinition;
  34. private slots:
  35. void initTestCase()
  36. {
  37. QSignalSpy helperSetupSucceeded(&helper, &ShareTestHelper::setupSucceeded);
  38. helper.setup();
  39. QCOMPARE(helperSetupSucceeded.count(), 1);
  40. const auto testSharePassword = "3|$argon2id$v=19$m=65536,"
  41. "t=4,"
  42. "p=1$M2FoLnliWkhIZkwzWjFBQg$BPraP+JUqP1sV89rkymXpCGxHBlCct6bZ39xUGaYQ5w";
  43. const auto testShareNote = QStringLiteral("This is a note!");
  44. const auto testShareExpiration = QDate::currentDate().addDays(1).toString(helper.expectedDtFormat);
  45. const auto linkShareLabel = QStringLiteral("Link share label");
  46. _testLinkShareDefinition = FakeShareDefinition(&helper,
  47. Share::TypeLink,
  48. {},
  49. linkShareLabel,
  50. testSharePassword,
  51. testShareNote,
  52. testShareExpiration);
  53. const auto emailShareShareWith = QStringLiteral("test-email@nextcloud.com");
  54. const auto emailShareShareWithDisplayName = QStringLiteral("Test email");
  55. _testEmailShareDefinition = FakeShareDefinition(&helper,
  56. Share::TypeEmail,
  57. emailShareShareWith,
  58. emailShareShareWithDisplayName,
  59. testSharePassword,
  60. testShareNote,
  61. testShareExpiration);
  62. const auto userShareShareWith = QStringLiteral("user");
  63. const auto userShareShareWithDisplayName("A Nextcloud user");
  64. _testUserShareDefinition = FakeShareDefinition(&helper,
  65. Share::TypeUser,
  66. userShareShareWith,
  67. userShareShareWithDisplayName);
  68. const auto remoteShareShareWith = QStringLiteral("remote_share");
  69. const auto remoteShareShareWithDisplayName("A remote share");
  70. _testRemoteShareDefinition = FakeShareDefinition(&helper,
  71. Share::TypeRemote,
  72. remoteShareShareWith,
  73. remoteShareShareWithDisplayName);
  74. qRegisterMetaType<ShareePtr>("ShareePtr");
  75. }
  76. void testSetAccountAndPath()
  77. {
  78. helper.resetTestData();
  79. // Test with a link share
  80. helper.appendShareReplyData(_testLinkShareDefinition);
  81. QCOMPARE(helper.shareCount(), 1);
  82. ShareModel model;
  83. QAbstractItemModelTester modelTester(&model);
  84. QCOMPARE(model.rowCount(), 0);
  85. QSignalSpy accountStateChanged(&model, &ShareModel::accountStateChanged);
  86. QSignalSpy localPathChanged(&model, &ShareModel::localPathChanged);
  87. QSignalSpy accountConnectedChanged(&model, &ShareModel::accountConnectedChanged);
  88. QSignalSpy sharingEnabledChanged(&model, &ShareModel::sharingEnabledChanged);
  89. QSignalSpy publicLinkSharesEnabledChanged(&model, &ShareModel::publicLinkSharesEnabledChanged);
  90. model.setAccountState(helper.accountState.data());
  91. QCOMPARE(accountStateChanged.count(), 1);
  92. // Check all the account-related properties of the model
  93. QCOMPARE(model.accountConnected(), helper.accountState->isConnected());
  94. QCOMPARE(model.sharingEnabled(), helper.account->capabilities().shareAPI());
  95. QCOMPARE(model.publicLinkSharesEnabled() && Theme::instance()->linkSharing(), helper.account->capabilities().sharePublicLink());
  96. QCOMPARE(Theme::instance()->userGroupSharing(), model.userGroupSharingEnabled());
  97. const QString localPath(helper.fakeFolder.localPath() + helper.testFileName);
  98. model.setLocalPath(localPath);
  99. QCOMPARE(localPathChanged.count(), 1);
  100. QCOMPARE(model.localPath(), localPath);
  101. }
  102. void testSuccessfulFetchShares()
  103. {
  104. helper.resetTestData();
  105. // Test with a link share and a user/group email share "from the server"
  106. helper.appendShareReplyData(_testLinkShareDefinition);
  107. helper.appendShareReplyData(_testEmailShareDefinition);
  108. helper.appendShareReplyData(_testUserShareDefinition);
  109. QCOMPARE(helper.shareCount(), 3);
  110. ShareModel model;
  111. QAbstractItemModelTester modelTester(&model);
  112. QCOMPARE(model.rowCount(), 0);
  113. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  114. model.setAccountState(helper.accountState.data());
  115. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  116. QVERIFY(sharesChanged.wait(5000));
  117. QCOMPARE(model.rowCount(), helper.shareCount());
  118. }
  119. void testFetchSharesFailedError()
  120. {
  121. helper.resetTestData();
  122. // Test with a link share "from the server"
  123. helper.appendShareReplyData(_testLinkShareDefinition);
  124. ShareModel model;
  125. QAbstractItemModelTester modelTester(&model);
  126. QCOMPARE(model.rowCount(), 0);
  127. QSignalSpy serverError(&model, &ShareModel::serverError);
  128. // Test fetching the shares of a file that does not exist
  129. model.setAccountState(helper.accountState.data());
  130. model.setLocalPath(helper.fakeFolder.localPath() + "wrong-filename-oops.md");
  131. QVERIFY(serverError.wait(3000));
  132. QCOMPARE(model.hasInitialShareFetchCompleted(), true);
  133. QCOMPARE(model.rowCount(), 0); // Make sure no placeholder
  134. }
  135. void testCorrectFetchOngoingSignalling()
  136. {
  137. helper.resetTestData();
  138. // Test with a link share "from the server"
  139. helper.appendShareReplyData(_testLinkShareDefinition);
  140. QCOMPARE(helper.shareCount(), 1);
  141. ShareModel model;
  142. QAbstractItemModelTester modelTester(&model);
  143. QCOMPARE(model.rowCount(), 0);
  144. QSignalSpy fetchOngoingChanged(&model, &ShareModel::fetchOngoingChanged);
  145. // Make sure we are correctly signalling the loading state of the fetch
  146. // Model resets twice when we set account and local path, resetting all model state.
  147. model.setAccountState(helper.accountState.data());
  148. QCOMPARE(fetchOngoingChanged.count(), 1);
  149. QCOMPARE(model.fetchOngoing(), false);
  150. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  151. // If we can grab shares it then indicates fetch ongoing...
  152. QCOMPARE(fetchOngoingChanged.count(), 3);
  153. QCOMPARE(model.fetchOngoing(), true);
  154. // Then indicates fetch finished when done.
  155. QVERIFY(fetchOngoingChanged.wait(3000));
  156. QCOMPARE(model.fetchOngoing(), false);
  157. }
  158. void testCorrectInitialFetchCompleteSignalling()
  159. {
  160. helper.resetTestData();
  161. // Test with a link share "from the server"
  162. helper.appendShareReplyData(_testLinkShareDefinition);
  163. QCOMPARE(helper.shareCount(), 1);
  164. ShareModel model;
  165. QAbstractItemModelTester modelTester(&model);
  166. QCOMPARE(model.rowCount(), 0);
  167. QSignalSpy accountStateChanged(&model, &ShareModel::accountStateChanged);
  168. QSignalSpy localPathChanged(&model, &ShareModel::localPathChanged);
  169. QSignalSpy hasInitialShareFetchCompletedChanged(&model, &ShareModel::hasInitialShareFetchCompletedChanged);
  170. // Make sure we are correctly signalling the loading state of the fetch
  171. // Model resets twice when we set account and local path, resetting all model state.
  172. model.setAccountState(helper.accountState.data());
  173. QCOMPARE(accountStateChanged.count(), 1);
  174. QCOMPARE(hasInitialShareFetchCompletedChanged.count(), 1);
  175. QCOMPARE(model.hasInitialShareFetchCompleted(), false);
  176. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  177. QCOMPARE(localPathChanged.count(), 1);
  178. QCOMPARE(hasInitialShareFetchCompletedChanged.count(), 2);
  179. QCOMPARE(model.hasInitialShareFetchCompleted(), false);
  180. // Once we have acquired shares from the server the initial share fetch is completed
  181. QVERIFY(hasInitialShareFetchCompletedChanged.wait(3000));
  182. QCOMPARE(hasInitialShareFetchCompletedChanged.count(), 3);
  183. QCOMPARE(model.hasInitialShareFetchCompleted(), true);
  184. }
  185. // Link shares and user group shares have slightly different behaviour in model.data()
  186. void testModelLinkShareData()
  187. {
  188. helper.resetTestData();
  189. // Test with a link share "from the server"
  190. helper.appendShareReplyData(_testLinkShareDefinition);
  191. QCOMPARE(helper.shareCount(), 1);
  192. ShareModel model;
  193. QAbstractItemModelTester modelTester(&model);
  194. QCOMPARE(model.rowCount(), 0);
  195. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  196. model.setAccountState(helper.accountState.data());
  197. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  198. QVERIFY(sharesChanged.wait(5000));
  199. QCOMPARE(model.rowCount(), helper.shareCount());
  200. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  201. QVERIFY(!shareIndex.data(Qt::DisplayRole).toString().isEmpty());
  202. QCOMPARE(shareIndex.data(ShareModel::ShareTypeRole).toInt(), _testLinkShareDefinition.shareType);
  203. QCOMPARE(shareIndex.data(ShareModel::ShareIdRole).toString(), _testLinkShareDefinition.shareId);
  204. QCOMPARE(shareIndex.data(ShareModel::LinkRole).toString(), _testLinkShareDefinition.linkShareUrl);
  205. QCOMPARE(shareIndex.data(ShareModel::LinkShareNameRole).toString(), _testLinkShareDefinition.linkShareName);
  206. QCOMPARE(shareIndex.data(ShareModel::LinkShareLabelRole).toString(), _testLinkShareDefinition.linkShareLabel);
  207. QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), !_testLinkShareDefinition.shareNote.isEmpty());
  208. QCOMPARE(shareIndex.data(ShareModel::NoteRole).toString(), _testLinkShareDefinition.shareNote);
  209. QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), !_testLinkShareDefinition.sharePassword.isEmpty());
  210. // We don't expose the fetched password to the user as it's useless to them
  211. QCOMPARE(shareIndex.data(ShareModel::PasswordRole).toString(), QString());
  212. QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), SharePermissions(_testLinkShareDefinition.sharePermissions).testFlag(SharePermissionUpdate));
  213. const auto expectedLinkShareExpireDate = QDate::fromString(_testLinkShareDefinition.shareExpiration, helper.expectedDtFormat);
  214. QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), expectedLinkShareExpireDate.isValid());
  215. QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), expectedLinkShareExpireDate.startOfDay(Qt::UTC).toMSecsSinceEpoch());
  216. const auto iconUrl = shareIndex.data(ShareModel::IconUrlRole).toString();
  217. QVERIFY(iconUrl.contains("public.svg"));
  218. }
  219. void testModelEmailShareData()
  220. {
  221. helper.resetTestData();
  222. // Test with a user/group email share "from the server"
  223. helper.appendShareReplyData(_testEmailShareDefinition);
  224. QCOMPARE(helper.shareCount(), 1);
  225. ShareModel model;
  226. QAbstractItemModelTester modelTester(&model);
  227. QCOMPARE(model.rowCount(), 0);
  228. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  229. model.setAccountState(helper.accountState.data());
  230. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  231. QVERIFY(sharesChanged.wait(5000));
  232. QCOMPARE(model.rowCount(), 2); // Remember about placeholder link share
  233. const auto shareIndex = model.index(0, 0, {}); // Placeholder link share gets added after we are done parsing fetched shares
  234. QVERIFY(!shareIndex.data(Qt::DisplayRole).toString().isEmpty());
  235. QCOMPARE(shareIndex.data(ShareModel::ShareTypeRole).toInt(), _testEmailShareDefinition.shareType);
  236. QCOMPARE(shareIndex.data(ShareModel::ShareIdRole).toString(), _testEmailShareDefinition.shareId);
  237. QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), !_testEmailShareDefinition.shareNote.isEmpty());
  238. QCOMPARE(shareIndex.data(ShareModel::NoteRole).toString(), _testEmailShareDefinition.shareNote);
  239. QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), !_testEmailShareDefinition.sharePassword.isEmpty());
  240. // We don't expose the fetched password to the user as it's useless to them
  241. QCOMPARE(shareIndex.data(ShareModel::PasswordRole).toString(), QString());
  242. QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), SharePermissions(_testEmailShareDefinition.sharePermissions).testFlag(SharePermissionUpdate));
  243. const auto expectedShareExpireDate = QDate::fromString(_testEmailShareDefinition.shareExpiration, helper.expectedDtFormat);
  244. QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), expectedShareExpireDate.isValid());
  245. QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), expectedShareExpireDate.startOfDay(Qt::UTC).toMSecsSinceEpoch());
  246. const auto iconUrl = shareIndex.data(ShareModel::IconUrlRole).toString();
  247. QVERIFY(iconUrl.contains("email.svg"));
  248. }
  249. void testModelUserShareData()
  250. {
  251. helper.resetTestData();
  252. // Test with a user/group user share "from the server"
  253. helper.appendShareReplyData(_testUserShareDefinition);
  254. QCOMPARE(helper.shareCount(), 1);
  255. ShareModel model;
  256. QAbstractItemModelTester modelTester(&model);
  257. QCOMPARE(model.rowCount(), 0);
  258. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  259. model.setAccountState(helper.accountState.data());
  260. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  261. QVERIFY(sharesChanged.wait(5000));
  262. QCOMPARE(model.rowCount(), 2); // Remember about placeholder link share
  263. const auto shareIndex = model.index(0, 0, {}); // Placeholder link share gets added after we are done parsing fetched shares
  264. QVERIFY(!shareIndex.data(Qt::DisplayRole).toString().isEmpty());
  265. QCOMPARE(shareIndex.data(ShareModel::ShareTypeRole).toInt(), _testUserShareDefinition.shareType);
  266. QCOMPARE(shareIndex.data(ShareModel::ShareIdRole).toString(), _testUserShareDefinition.shareId);
  267. QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), !_testUserShareDefinition.shareNote.isEmpty());
  268. QCOMPARE(shareIndex.data(ShareModel::NoteRole).toString(), _testUserShareDefinition.shareNote);
  269. QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), !_testUserShareDefinition.sharePassword.isEmpty());
  270. // We don't expose the fetched password to the user as it's useless to them
  271. QCOMPARE(shareIndex.data(ShareModel::PasswordRole).toString(), QString());
  272. QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), SharePermissions(_testUserShareDefinition.sharePermissions).testFlag(SharePermissionUpdate));
  273. const auto expectedShareExpireDate = QDate::fromString(_testUserShareDefinition.shareExpiration, helper.expectedDtFormat);
  274. QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), expectedShareExpireDate.isValid());
  275. QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), expectedShareExpireDate.startOfDay(Qt::UTC).toMSecsSinceEpoch());
  276. const auto iconUrl = shareIndex.data(ShareModel::IconUrlRole).toString();
  277. QVERIFY(iconUrl.contains("user.svg"));
  278. // Check correct user avatar
  279. const auto avatarUrl = shareIndex.data(ShareModel::AvatarUrlRole).toString();
  280. const auto relativeAvatarPath = QString("remote.php/dav/avatars/%1/%2.png").arg(_testUserShareDefinition.shareShareWith, QString::number(64));
  281. const auto expectedAvatarPath = Utility::concatUrlPath(helper.account->url(), relativeAvatarPath).toString();
  282. const QString expectedUrl(QStringLiteral("image://tray-image-provider/") + expectedAvatarPath);
  283. QCOMPARE(avatarUrl, expectedUrl);
  284. }
  285. void testSuccessfulCreateShares()
  286. {
  287. helper.resetTestData();
  288. // Test with an existing link share
  289. helper.appendShareReplyData(_testLinkShareDefinition);
  290. QCOMPARE(helper.shareCount(), 1);
  291. ShareModel model;
  292. QAbstractItemModelTester modelTester(&model);
  293. QCOMPARE(model.rowCount(), 0);
  294. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  295. model.setAccountState(helper.accountState.data());
  296. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  297. QVERIFY(sharesChanged.wait(5000));
  298. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  299. QCOMPARE(model.rowCount(), helper.shareCount());
  300. // Test if it gets added
  301. model.createNewLinkShare();
  302. QVERIFY(sharesChanged.wait(5000));
  303. QCOMPARE(helper.shareCount(), 2); // Check our test is working!
  304. QCOMPARE(model.rowCount(), helper.shareCount());
  305. // Test if it's the type we wanted
  306. const auto newLinkShareIndex = model.index(model.rowCount() - 1, 0, {});
  307. QCOMPARE(newLinkShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypeLink);
  308. // Do it again with a different type
  309. const ShareePtr sharee(new Sharee("testsharee@nextcloud.com", "Test sharee", Sharee::Type::Email));
  310. model.createNewUserGroupShare(sharee);
  311. QVERIFY(sharesChanged.wait(5000));
  312. QCOMPARE(helper.shareCount(), 3); // Check our test is working!
  313. QCOMPARE(model.rowCount(), helper.shareCount());
  314. // Test if it's the type we wanted
  315. const auto newUserGroupShareIndex = model.index(model.rowCount() - 1, 0, {});
  316. QCOMPARE(newUserGroupShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypeEmail);
  317. // Confirm correct addition of share with password
  318. const auto password = QStringLiteral("a pretty bad password but good thing it doesn't matter!");
  319. model.createNewLinkShareWithPassword(password);
  320. QVERIFY(sharesChanged.wait(5000));
  321. QCOMPARE(helper.shareCount(), 4); // Check our test is working!
  322. QCOMPARE(model.rowCount(), helper.shareCount());
  323. model.createNewUserGroupShareWithPassword(sharee, password);
  324. QVERIFY(sharesChanged.wait(5000));
  325. QCOMPARE(helper.shareCount(), 5); // Check our test is working!
  326. QCOMPARE(model.rowCount(), helper.shareCount());
  327. helper.resetTestData();
  328. }
  329. void testEnforcePasswordShares()
  330. {
  331. helper.resetTestData();
  332. // Enforce passwords for shares in capabilities
  333. const QVariantMap enforcePasswordsCapabilities {
  334. {QStringLiteral("files_sharing"), QVariantMap {
  335. {QStringLiteral("api_enabled"), true},
  336. {QStringLiteral("default_permissions"), 19},
  337. {QStringLiteral("public"), QVariantMap {
  338. {QStringLiteral("enabled"), true},
  339. {QStringLiteral("expire_date"), QVariantMap {
  340. {QStringLiteral("days"), 30},
  341. {QStringLiteral("enforced"), false},
  342. }},
  343. {QStringLiteral("expire_date_internal"), QVariantMap {
  344. {QStringLiteral("days"), 30},
  345. {QStringLiteral("enforced"), false},
  346. }},
  347. {QStringLiteral("expire_date_remote"), QVariantMap {
  348. {QStringLiteral("days"), 30},
  349. {QStringLiteral("enforced"), false},
  350. }},
  351. {QStringLiteral("password"), QVariantMap {
  352. {QStringLiteral("enforced"), true},
  353. }},
  354. }},
  355. {QStringLiteral("sharebymail"), QVariantMap {
  356. {QStringLiteral("enabled"), true},
  357. {QStringLiteral("password"), QVariantMap {
  358. {QStringLiteral("enforced"), true},
  359. }},
  360. }},
  361. }},
  362. };
  363. helper.account->setCapabilities(enforcePasswordsCapabilities);
  364. QVERIFY(helper.account->capabilities().sharePublicLinkEnforcePassword());
  365. QVERIFY(helper.account->capabilities().shareEmailPasswordEnforced());
  366. // Test with a link share "from the server"
  367. helper.appendShareReplyData(_testLinkShareDefinition);
  368. QCOMPARE(helper.shareCount(), 1);
  369. ShareModel model;
  370. QAbstractItemModelTester modelTester(&model);
  371. QCOMPARE(model.rowCount(), 0);
  372. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  373. model.setAccountState(helper.accountState.data());
  374. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  375. QVERIFY(sharesChanged.wait(5000));
  376. QCOMPARE(model.rowCount(), helper.shareCount());
  377. // Confirm that the model requests a password
  378. QSignalSpy requestPasswordForLinkShare(&model, &ShareModel::requestPasswordForLinkShare);
  379. model.createNewLinkShare();
  380. QVERIFY(requestPasswordForLinkShare.wait(3000));
  381. QSignalSpy requestPasswordForEmailShare(&model, &ShareModel::requestPasswordForEmailSharee);
  382. const ShareePtr sharee(new Sharee("testsharee@nextcloud.com", "Test sharee", Sharee::Type::Email));
  383. model.createNewUserGroupShare(sharee);
  384. QCOMPARE(requestPasswordForEmailShare.count(), 1);
  385. // Test that the model data is correctly reporting that passwords are enforced
  386. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  387. QCOMPARE(shareIndex.data(ShareModel::PasswordEnforcedRole).toBool(), true);
  388. QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), true);
  389. }
  390. void testEnforceExpireDate()
  391. {
  392. helper.resetTestData();
  393. const auto internalExpireDays = 45;
  394. const auto publicExpireDays = 30;
  395. const auto remoteExpireDays = 25;
  396. // Enforce expire dates for shares in capabilities
  397. const QVariantMap enforcePasswordsCapabilities {
  398. {QStringLiteral("files_sharing"), QVariantMap {
  399. {QStringLiteral("api_enabled"), true},
  400. {QStringLiteral("default_permissions"), 19},
  401. {QStringLiteral("public"), QVariantMap {
  402. {QStringLiteral("enabled"), true},
  403. {QStringLiteral("expire_date"), QVariantMap {
  404. {QStringLiteral("days"), publicExpireDays},
  405. {QStringLiteral("enforced"), true},
  406. }},
  407. {QStringLiteral("expire_date_internal"), QVariantMap {
  408. {QStringLiteral("days"), internalExpireDays},
  409. {QStringLiteral("enforced"), true},
  410. }},
  411. {QStringLiteral("expire_date_remote"), QVariantMap {
  412. {QStringLiteral("days"), remoteExpireDays},
  413. {QStringLiteral("enforced"), true},
  414. }},
  415. {QStringLiteral("password"), QVariantMap {
  416. {QStringLiteral("enforced"), false},
  417. }},
  418. }},
  419. {QStringLiteral("sharebymail"), QVariantMap {
  420. {QStringLiteral("enabled"), true},
  421. {QStringLiteral("password"), QVariantMap {
  422. {QStringLiteral("enforced"), true},
  423. }},
  424. }},
  425. }},
  426. };
  427. helper.account->setCapabilities(enforcePasswordsCapabilities);
  428. QVERIFY(helper.account->capabilities().sharePublicLinkEnforceExpireDate());
  429. QVERIFY(helper.account->capabilities().shareInternalEnforceExpireDate());
  430. QVERIFY(helper.account->capabilities().shareRemoteEnforceExpireDate());
  431. // Test with shares "from the server"
  432. helper.appendShareReplyData(_testLinkShareDefinition);
  433. helper.appendShareReplyData(_testEmailShareDefinition);
  434. helper.appendShareReplyData(_testRemoteShareDefinition);
  435. QCOMPARE(helper.shareCount(), 3);
  436. ShareModel model;
  437. QAbstractItemModelTester modelTester(&model);
  438. QCOMPARE(model.rowCount(), 0);
  439. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  440. model.setAccountState(helper.accountState.data());
  441. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  442. QVERIFY(sharesChanged.wait(5000));
  443. QCOMPARE(model.rowCount(), helper.shareCount());
  444. // Test that the model data is correctly reporting that expire dates are enforced for all share types
  445. for(auto i = 0; i < model.rowCount(); ++i) {
  446. const auto shareIndex = model.index(i, 0, {});
  447. QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnforcedRole).toBool(), true);
  448. QDateTime expectedExpireDateTime;
  449. switch(shareIndex.data(ShareModel::ShareTypeRole).toInt()) {
  450. case Share::TypePlaceholderLink:
  451. break;
  452. case Share::TypeUser:
  453. case Share::TypeGroup:
  454. case Share::TypeCircle:
  455. case Share::TypeRoom:
  456. expectedExpireDateTime = QDate::currentDate().addDays(internalExpireDays).startOfDay(QTimeZone::utc());
  457. break;
  458. case Share::TypeLink:
  459. case Share::TypeEmail:
  460. expectedExpireDateTime = QDate::currentDate().addDays(publicExpireDays).startOfDay(QTimeZone::utc());
  461. break;
  462. case Share::TypeRemote:
  463. expectedExpireDateTime = QDate::currentDate().addDays(remoteExpireDays).startOfDay(QTimeZone::utc());
  464. break;
  465. }
  466. QCOMPARE(shareIndex.data(ShareModel::EnforcedMaximumExpireDateRole).toLongLong(), expectedExpireDateTime.toMSecsSinceEpoch());
  467. }
  468. }
  469. void testSuccessfulDeleteShares()
  470. {
  471. helper.resetTestData();
  472. // Test with an existing link share
  473. helper.appendShareReplyData(_testLinkShareDefinition);
  474. QCOMPARE(helper.shareCount(), 1);
  475. ShareModel model;
  476. QAbstractItemModelTester modelTester(&model);
  477. QCOMPARE(model.rowCount(), 0);
  478. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  479. model.setAccountState(helper.accountState.data());
  480. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  481. QVERIFY(sharesChanged.wait(5000));
  482. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  483. QCOMPARE(model.rowCount(), helper.shareCount());
  484. // Create share
  485. model.createNewLinkShare();
  486. QVERIFY(sharesChanged.wait(5000));
  487. QCOMPARE(helper.shareCount(), 2); // Check our test is working!
  488. QCOMPARE(model.rowCount(), helper.shareCount());
  489. // Test if it gets deleted properly
  490. const auto latestLinkShare = model.index(model.rowCount() - 1, 0, {}).data(ShareModel::ShareRole).value<SharePtr>();
  491. QSignalSpy shareDeleted(latestLinkShare.data(), &LinkShare::shareDeleted);
  492. model.deleteShare(latestLinkShare);
  493. QVERIFY(shareDeleted.wait(5000));
  494. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  495. QCOMPARE(model.rowCount(), helper.shareCount());
  496. helper.resetTestData();
  497. }
  498. void testPlaceholderLinkShare()
  499. {
  500. helper.resetTestData();
  501. // Start with no shares; should show the placeholder link share
  502. ShareModel model;
  503. QAbstractItemModelTester modelTester(&model);
  504. QCOMPARE(model.rowCount(), 0); // There should be no placeholder yet
  505. QSignalSpy hasInitialShareFetchCompletedChanged(&model, &ShareModel::hasInitialShareFetchCompletedChanged);
  506. model.setAccountState(helper.accountState.data());
  507. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  508. QVERIFY(hasInitialShareFetchCompletedChanged.wait(5000));
  509. QVERIFY(model.hasInitialShareFetchCompleted());
  510. QCOMPARE(model.rowCount(), 1); // There should be a placeholder now
  511. const QPersistentModelIndex placeholderLinkShareIndex(model.index(model.rowCount() - 1, 0, {}));
  512. QCOMPARE(placeholderLinkShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypePlaceholderLink);
  513. // Test adding a user group share -- we should still be showing a placeholder link share
  514. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  515. const ShareePtr sharee(new Sharee("testsharee@nextcloud.com", "Test sharee", Sharee::Type::Email));
  516. model.createNewUserGroupShare(sharee);
  517. QVERIFY(sharesChanged.wait(5000));
  518. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  519. QCOMPARE(model.rowCount(), helper.shareCount() + 1);
  520. QVERIFY(placeholderLinkShareIndex.isValid());
  521. QCOMPARE(placeholderLinkShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypePlaceholderLink);
  522. // Now try adding a link share, which should remove the placeholder
  523. model.createNewLinkShare();
  524. QVERIFY(sharesChanged.wait(5000));
  525. QCOMPARE(helper.shareCount(), 2); // Check our test is working!
  526. QCOMPARE(model.rowCount(), helper.shareCount());
  527. QVERIFY(!placeholderLinkShareIndex.isValid());
  528. // Now delete the only link share, which should bring back the placeholder link share
  529. const auto latestLinkShare = model.index(model.rowCount() - 1, 0, {}).data(ShareModel::ShareRole).value<SharePtr>();
  530. QSignalSpy shareDeleted(latestLinkShare.data(), &LinkShare::shareDeleted);
  531. model.deleteShare(latestLinkShare);
  532. QVERIFY(shareDeleted.wait(5000));
  533. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  534. QCOMPARE(model.rowCount(), helper.shareCount() + 1);
  535. const auto newPlaceholderLinkShareIndex = model.index(model.rowCount() - 1, 0, {});
  536. QCOMPARE(newPlaceholderLinkShareIndex.data(ShareModel::ShareTypeRole).toInt(), Share::TypePlaceholderLink);
  537. helper.resetTestData();
  538. }
  539. void testSuccessfulToggleAllowEditing()
  540. {
  541. helper.resetTestData();
  542. // Test with an existing link share
  543. helper.appendShareReplyData(_testLinkShareDefinition);
  544. QCOMPARE(helper.shareCount(), 1);
  545. ShareModel model;
  546. QAbstractItemModelTester modelTester(&model);
  547. QCOMPARE(model.rowCount(), 0);
  548. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  549. model.setAccountState(helper.accountState.data());
  550. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  551. QVERIFY(sharesChanged.wait(5000));
  552. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  553. QCOMPARE(model.rowCount(), helper.shareCount());
  554. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  555. QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), SharePermissions(_testLinkShareDefinition.sharePermissions).testFlag(SharePermissionUpdate));
  556. const auto share = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
  557. QSignalSpy permissionsSet(share.data(), &Share::permissionsSet);
  558. model.toggleShareAllowEditing(share, false);
  559. QVERIFY(permissionsSet.wait(3000));
  560. QCOMPARE(shareIndex.data(ShareModel::EditingAllowedRole).toBool(), false);
  561. }
  562. void testSuccessfulPasswordSet()
  563. {
  564. helper.resetTestData();
  565. // Test with an existing link share.
  566. // This one has a pre-existing password
  567. helper.appendShareReplyData(_testLinkShareDefinition);
  568. QCOMPARE(helper.shareCount(), 1);
  569. ShareModel model;
  570. QAbstractItemModelTester modelTester(&model);
  571. QCOMPARE(model.rowCount(), 0);
  572. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  573. model.setAccountState(helper.accountState.data());
  574. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  575. QVERIFY(sharesChanged.wait(5000));
  576. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  577. QCOMPARE(model.rowCount(), helper.shareCount());
  578. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  579. QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), true);
  580. const auto share = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
  581. QSignalSpy passwordSet(share.data(), &Share::passwordSet);
  582. model.toggleSharePasswordProtect(share, false);
  583. QVERIFY(passwordSet.wait(3000));
  584. QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), false);
  585. const auto password = QStringLiteral("a pretty bad password but good thing it doesn't matter!");
  586. model.setSharePassword(share, password);
  587. QVERIFY(passwordSet.wait(3000));
  588. QCOMPARE(shareIndex.data(ShareModel::PasswordProtectEnabledRole).toBool(), true);
  589. // The model stores the recently set password.
  590. // We want to present the user with it in the UI while the model is alive
  591. QCOMPARE(shareIndex.data(ShareModel::PasswordRole).toString(), password);
  592. }
  593. void testSuccessfulExpireDateSet()
  594. {
  595. helper.resetTestData();
  596. // Test with an existing link share.
  597. // This one has a pre-existing expire date
  598. helper.appendShareReplyData(_testLinkShareDefinition);
  599. QCOMPARE(helper.shareCount(), 1);
  600. ShareModel model;
  601. QAbstractItemModelTester modelTester(&model);
  602. QCOMPARE(model.rowCount(), 0);
  603. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  604. model.setAccountState(helper.accountState.data());
  605. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  606. QVERIFY(sharesChanged.wait(5000));
  607. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  608. QCOMPARE(model.rowCount(), helper.shareCount());
  609. // Check what we know
  610. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  611. QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), true);
  612. // Disable expire date
  613. const auto sharePtr = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
  614. const auto linkSharePtr = sharePtr.dynamicCast<LinkShare>(); // Need to connect to signal
  615. QSignalSpy expireDateSet(linkSharePtr.data(), &LinkShare::expireDateSet);
  616. model.toggleShareExpirationDate(sharePtr, false);
  617. QVERIFY(expireDateSet.wait(3000));
  618. QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), false);
  619. // Set a new expire date
  620. const auto expireDateMsecs = QDate::currentDate().addDays(10).startOfDay(Qt::UTC).toMSecsSinceEpoch();
  621. model.setShareExpireDate(linkSharePtr, expireDateMsecs);
  622. QVERIFY(expireDateSet.wait(3000));
  623. QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), expireDateMsecs);
  624. QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), true);
  625. // Test the QML-specific slot
  626. const QVariant newExpireDateMsecs = QDate::currentDate().addDays(20).startOfDay(Qt::UTC).toMSecsSinceEpoch();
  627. model.setShareExpireDateFromQml(QVariant::fromValue(sharePtr), newExpireDateMsecs);
  628. QVERIFY(expireDateSet.wait(3000));
  629. QCOMPARE(shareIndex.data(ShareModel::ExpireDateRole).toLongLong(), newExpireDateMsecs);
  630. QCOMPARE(shareIndex.data(ShareModel::ExpireDateEnabledRole).toBool(), true);
  631. }
  632. void testSuccessfulNoteSet()
  633. {
  634. helper.resetTestData();
  635. // Test with an existing link share.
  636. // This one has a pre-existing password
  637. helper.appendShareReplyData(_testLinkShareDefinition);
  638. QCOMPARE(helper.shareCount(), 1);
  639. ShareModel model;
  640. QAbstractItemModelTester modelTester(&model);
  641. QCOMPARE(model.rowCount(), 0);
  642. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  643. model.setAccountState(helper.accountState.data());
  644. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  645. QVERIFY(sharesChanged.wait(5000));
  646. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  647. QCOMPARE(model.rowCount(), helper.shareCount());
  648. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  649. QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), true);
  650. const auto sharePtr = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
  651. const auto linkSharePtr = sharePtr.dynamicCast<LinkShare>(); // Need to connect to signal
  652. QSignalSpy noteSet(linkSharePtr.data(), &LinkShare::noteSet);
  653. model.toggleShareNoteToRecipient(sharePtr, false);
  654. QVERIFY(noteSet.wait(3000));
  655. QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), false);
  656. const auto note = QStringLiteral("Don't forget to test everything!");
  657. model.setShareNote(sharePtr, note);
  658. QVERIFY(noteSet.wait(3000));
  659. QCOMPARE(shareIndex.data(ShareModel::NoteEnabledRole).toBool(), true);
  660. // The model stores the recently set password.
  661. // We want to present the user with it in the UI while the model is alive
  662. QCOMPARE(shareIndex.data(ShareModel::NoteRole).toString(), note);
  663. }
  664. void testSuccessfulLinkShareLabelSet()
  665. {
  666. helper.resetTestData();
  667. // Test with an existing link share.
  668. helper.appendShareReplyData(_testLinkShareDefinition);
  669. QCOMPARE(helper.shareCount(), 1);
  670. ShareModel model;
  671. QAbstractItemModelTester modelTester(&model);
  672. QCOMPARE(model.rowCount(), 0);
  673. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  674. model.setAccountState(helper.accountState.data());
  675. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  676. QVERIFY(sharesChanged.wait(5000));
  677. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  678. QCOMPARE(model.rowCount(), helper.shareCount());
  679. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  680. QCOMPARE(shareIndex.data(ShareModel::LinkShareLabelRole).toBool(), true);
  681. const auto sharePtr = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
  682. const auto linkSharePtr = sharePtr.dynamicCast<LinkShare>(); // Need to connect to signal
  683. QSignalSpy labelSet(linkSharePtr.data(), &LinkShare::labelSet);
  684. const auto label = QStringLiteral("New link share label!");
  685. model.setLinkShareLabel(linkSharePtr, label);
  686. QVERIFY(labelSet.wait(3000));
  687. QCOMPARE(shareIndex.data(ShareModel::LinkShareLabelRole).toString(), label);
  688. }
  689. void testSharees()
  690. {
  691. helper.resetTestData();
  692. helper.appendShareReplyData(_testLinkShareDefinition);
  693. helper.appendShareReplyData(_testEmailShareDefinition);
  694. helper.appendShareReplyData(_testUserShareDefinition);
  695. QCOMPARE(helper.shareCount(), 3);
  696. ShareModel model;
  697. QAbstractItemModelTester modelTester(&model);
  698. QCOMPARE(model.rowCount(), 0);
  699. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  700. model.setAccountState(helper.accountState.data());
  701. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  702. QVERIFY(sharesChanged.wait(5000));
  703. QCOMPARE(model.rowCount(), helper.shareCount());
  704. QCOMPARE(model.sharees().count(), 2); // Link shares don't have sharees
  705. // Test adding a user group share -- we should still be showing a placeholder link share
  706. const ShareePtr sharee(new Sharee("testsharee@nextcloud.com", "Test sharee", Sharee::Type::Email));
  707. model.createNewUserGroupShare(sharee);
  708. QVERIFY(sharesChanged.wait(5000));
  709. QCOMPARE(helper.shareCount(), 4); // Check our test is working!
  710. QCOMPARE(model.rowCount(), helper.shareCount());
  711. const auto sharees = model.sharees();
  712. QCOMPARE(sharees.count(), 3); // Link shares don't have sharees
  713. const auto lastSharee = sharees.last().value<ShareePtr>();
  714. QVERIFY(lastSharee);
  715. // Remove the user group share we just added
  716. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  717. const auto sharePtr = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
  718. model.deleteShare(sharePtr);
  719. QVERIFY(sharesChanged.wait(5000));
  720. QCOMPARE(model.rowCount(), helper.shareCount());
  721. // Now check the sharee is gone
  722. QCOMPARE(model.sharees().count(), 2);
  723. }
  724. void testSharePropertySetError()
  725. {
  726. helper.resetTestData();
  727. // Serve a broken share definition from the server to force an error
  728. auto brokenLinkShareDefinition = _testLinkShareDefinition;
  729. brokenLinkShareDefinition.shareId = QString();
  730. helper.appendShareReplyData(brokenLinkShareDefinition);
  731. QCOMPARE(helper.shareCount(), 1);
  732. ShareModel model;
  733. QAbstractItemModelTester modelTester(&model);
  734. QCOMPARE(model.rowCount(), 0);
  735. QSignalSpy sharesChanged(&model, &ShareModel::sharesChanged);
  736. model.setAccountState(helper.accountState.data());
  737. model.setLocalPath(helper.fakeFolder.localPath() + helper.testFileName);
  738. QVERIFY(sharesChanged.wait(5000));
  739. QCOMPARE(helper.shareCount(), 1); // Check our test is working!
  740. QCOMPARE(model.rowCount(), helper.shareCount());
  741. // Reset the fake server to pretend like nothing is wrong there
  742. helper.resetTestShares();
  743. helper.appendShareReplyData(_testLinkShareDefinition);
  744. QCOMPARE(helper.shareCount(), 1);
  745. // Now try changing a property of the share
  746. const auto shareIndex = model.index(model.rowCount() - 1, 0, {});
  747. const auto share = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
  748. QSignalSpy serverError(&model, &ShareModel::serverError);
  749. model.toggleShareAllowEditing(share, false);
  750. QVERIFY(serverError.wait(3000));
  751. // Specific signal for password set error
  752. QSignalSpy passwordSetError(&model, &ShareModel::passwordSetError);
  753. const auto password = QStringLiteral("a pretty bad password but good thing it doesn't matter!");
  754. model.setSharePassword(share, password);
  755. QVERIFY(passwordSetError.wait(3000));
  756. }
  757. };
  758. QTEST_MAIN(TestShareModel)
  759. #include "testsharemodel.moc"