testlockfile.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. #include "lockfilejobs.h"
  2. #include "account.h"
  3. #include "accountstate.h"
  4. #include "common/syncjournaldb.h"
  5. #include "common/syncjournalfilerecord.h"
  6. #include "syncenginetestutils.h"
  7. #include <QTest>
  8. #include <QSignalSpy>
  9. class TestLockFile : public QObject
  10. {
  11. Q_OBJECT
  12. public:
  13. TestLockFile() = default;
  14. private slots:
  15. void initTestCase()
  16. {
  17. }
  18. void testLockFile_lockFile_jobSuccess()
  19. {
  20. const auto testFileName = QStringLiteral("file.txt");
  21. FakeFolder fakeFolder{FileInfo{}};
  22. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  23. fakeFolder.localModifier().insert(testFileName);
  24. QVERIFY(fakeFolder.syncOnce());
  25. auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem);
  26. QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError);
  27. QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError);
  28. job->start();
  29. QVERIFY(jobSuccess.wait());
  30. QCOMPARE(jobFailure.count(), 0);
  31. auto fileRecord = OCC::SyncJournalFileRecord{};
  32. QVERIFY(fakeFolder.syncJournal().getFileRecord(testFileName, &fileRecord));
  33. QCOMPARE(fileRecord._lockstate._locked, true);
  34. QCOMPARE(fileRecord._lockstate._lockEditorApp, QString{});
  35. QCOMPARE(fileRecord._lockstate._lockOwnerDisplayName, QStringLiteral("John Doe"));
  36. QCOMPARE(fileRecord._lockstate._lockOwnerId, QStringLiteral("admin"));
  37. QCOMPARE(fileRecord._lockstate._lockOwnerType, static_cast<qint64>(OCC::SyncFileItem::LockOwnerType::UserLock));
  38. QCOMPARE(fileRecord._lockstate._lockTime, 1234560);
  39. QCOMPARE(fileRecord._lockstate._lockTimeout, 1800);
  40. QVERIFY(fakeFolder.syncOnce());
  41. }
  42. void testLockFile_lockFile_unlockFile_jobSuccess()
  43. {
  44. const auto testFileName = QStringLiteral("file.txt");
  45. FakeFolder fakeFolder{FileInfo{}};
  46. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  47. fakeFolder.localModifier().insert(testFileName);
  48. QVERIFY(fakeFolder.syncOnce());
  49. auto lockFileJob = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem);
  50. QSignalSpy lockFileJobSuccess(lockFileJob, &OCC::LockFileJob::finishedWithoutError);
  51. QSignalSpy lockFileJobFailure(lockFileJob, &OCC::LockFileJob::finishedWithError);
  52. lockFileJob->start();
  53. QVERIFY(lockFileJobSuccess.wait());
  54. QCOMPARE(lockFileJobFailure.count(), 0);
  55. QVERIFY(fakeFolder.syncOnce());
  56. auto unlockFileJob = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::UnlockedItem);
  57. QSignalSpy unlockFileJobSuccess(unlockFileJob, &OCC::LockFileJob::finishedWithoutError);
  58. QSignalSpy unlockFileJobFailure(unlockFileJob, &OCC::LockFileJob::finishedWithError);
  59. unlockFileJob->start();
  60. QVERIFY(unlockFileJobSuccess.wait());
  61. QCOMPARE(unlockFileJobFailure.count(), 0);
  62. auto fileRecord = OCC::SyncJournalFileRecord{};
  63. QVERIFY(fakeFolder.syncJournal().getFileRecord(testFileName, &fileRecord));
  64. QCOMPARE(fileRecord._lockstate._locked, false);
  65. QVERIFY(fakeFolder.syncOnce());
  66. }
  67. void testLockFile_lockFile_alreadyLockedByUser()
  68. {
  69. static constexpr auto LockedHttpErrorCode = 423;
  70. static constexpr auto PreconditionFailedHttpErrorCode = 412;
  71. const auto testFileName = QStringLiteral("file.txt");
  72. const auto replyData = QByteArray("<?xml version=\"1.0\"?>\n"
  73. "<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
  74. " <nc:lock>1</nc:lock>\n"
  75. " <nc:lock-owner-type>0</nc:lock-owner-type>\n"
  76. " <nc:lock-owner>john</nc:lock-owner>\n"
  77. " <nc:lock-owner-displayname>John Doe</nc:lock-owner-displayname>\n"
  78. " <nc:lock-owner-editor>john</nc:lock-owner-editor>\n"
  79. " <nc:lock-time>1650619678</nc:lock-time>\n"
  80. " <nc:lock-timeout>300</nc:lock-timeout>\n"
  81. " <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
  82. "</d:prop>\n");
  83. FakeFolder fakeFolder{FileInfo{}};
  84. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  85. fakeFolder.setServerOverride([replyData] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) {
  86. QNetworkReply *reply = nullptr;
  87. if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK")) {
  88. reply = new FakeErrorReply(op, request, nullptr, LockedHttpErrorCode, replyData);
  89. } else if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK")) {
  90. reply = new FakeErrorReply(op, request, nullptr, PreconditionFailedHttpErrorCode, replyData);
  91. }
  92. return reply;
  93. });
  94. fakeFolder.localModifier().insert(testFileName);
  95. QVERIFY(fakeFolder.syncOnce());
  96. auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem);
  97. QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError);
  98. QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError);
  99. job->start();
  100. QVERIFY(jobFailure.wait());
  101. QCOMPARE(jobSuccess.count(), 0);
  102. }
  103. void testLockFile_lockFile_alreadyLockedByApp()
  104. {
  105. static constexpr auto LockedHttpErrorCode = 423;
  106. static constexpr auto PreconditionFailedHttpErrorCode = 412;
  107. const auto testFileName = QStringLiteral("file.txt");
  108. const auto replyData = QByteArray("<?xml version=\"1.0\"?>\n"
  109. "<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
  110. " <nc:lock>1</nc:lock>\n"
  111. " <nc:lock-owner-type>1</nc:lock-owner-type>\n"
  112. " <nc:lock-owner>john</nc:lock-owner>\n"
  113. " <nc:lock-owner-displayname>John Doe</nc:lock-owner-displayname>\n"
  114. " <nc:lock-owner-editor>Text</nc:lock-owner-editor>\n"
  115. " <nc:lock-time>1650619678</nc:lock-time>\n"
  116. " <nc:lock-timeout>300</nc:lock-timeout>\n"
  117. " <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
  118. "</d:prop>\n");
  119. FakeFolder fakeFolder{FileInfo{}};
  120. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  121. fakeFolder.setServerOverride([replyData] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) {
  122. QNetworkReply *reply = nullptr;
  123. if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK")) {
  124. reply = new FakeErrorReply(op, request, nullptr, LockedHttpErrorCode, replyData);
  125. } else if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK")) {
  126. reply = new FakeErrorReply(op, request, nullptr, PreconditionFailedHttpErrorCode, replyData);
  127. }
  128. return reply;
  129. });
  130. fakeFolder.localModifier().insert(testFileName);
  131. QVERIFY(fakeFolder.syncOnce());
  132. auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem);
  133. QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError);
  134. QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError);
  135. job->start();
  136. QVERIFY(jobFailure.wait());
  137. QCOMPARE(jobSuccess.count(), 0);
  138. }
  139. void testLockFile_unlockFile_alreadyUnlocked()
  140. {
  141. static constexpr auto LockedHttpErrorCode = 423;
  142. static constexpr auto PreconditionFailedHttpErrorCode = 412;
  143. const auto testFileName = QStringLiteral("file.txt");
  144. const auto replyData = QByteArray("<?xml version=\"1.0\"?>\n"
  145. "<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
  146. " <nc:lock/>\n"
  147. " <nc:lock-owner-type>0</nc:lock-owner-type>\n"
  148. " <nc:lock-owner>john</nc:lock-owner>\n"
  149. " <nc:lock-owner-displayname>John Doe</nc:lock-owner-displayname>\n"
  150. " <nc:lock-owner-editor>john</nc:lock-owner-editor>\n"
  151. " <nc:lock-time>1650619678</nc:lock-time>\n"
  152. " <nc:lock-timeout>300</nc:lock-timeout>\n"
  153. " <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
  154. "</d:prop>\n");
  155. FakeFolder fakeFolder{FileInfo{}};
  156. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  157. fakeFolder.setServerOverride([replyData] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) {
  158. QNetworkReply *reply = nullptr;
  159. if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK")) {
  160. reply = new FakeErrorReply(op, request, nullptr, LockedHttpErrorCode, replyData);
  161. } else if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK")) {
  162. reply = new FakeErrorReply(op, request, nullptr, PreconditionFailedHttpErrorCode, replyData);
  163. }
  164. return reply;
  165. });
  166. fakeFolder.localModifier().insert(testFileName);
  167. QVERIFY(fakeFolder.syncOnce());
  168. auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::UnlockedItem);
  169. QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError);
  170. QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError);
  171. job->start();
  172. QVERIFY(jobSuccess.wait());
  173. QCOMPARE(jobFailure.count(), 0);
  174. }
  175. void testLockFile_unlockFile_lockedBySomeoneElse()
  176. {
  177. static constexpr auto LockedHttpErrorCode = 423;
  178. const auto testFileName = QStringLiteral("file.txt");
  179. const auto replyData = QByteArray("<?xml version=\"1.0\"?>\n"
  180. "<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
  181. " <nc:lock>1</nc:lock>\n"
  182. " <nc:lock-owner-type>0</nc:lock-owner-type>\n"
  183. " <nc:lock-owner>alice</nc:lock-owner>\n"
  184. " <nc:lock-owner-displayname>Alice Doe</nc:lock-owner-displayname>\n"
  185. " <nc:lock-owner-editor>Text</nc:lock-owner-editor>\n"
  186. " <nc:lock-time>1650619678</nc:lock-time>\n"
  187. " <nc:lock-timeout>300</nc:lock-timeout>\n"
  188. " <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
  189. "</d:prop>\n");
  190. FakeFolder fakeFolder{FileInfo{}};
  191. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  192. fakeFolder.setServerOverride([replyData] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) {
  193. QNetworkReply *reply = nullptr;
  194. if (op == QNetworkAccessManager::CustomOperation && (request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK") ||
  195. request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK"))) {
  196. reply = new FakeErrorReply(op, request, nullptr, LockedHttpErrorCode, replyData);
  197. }
  198. return reply;
  199. });
  200. fakeFolder.localModifier().insert(testFileName);
  201. QVERIFY(fakeFolder.syncOnce());
  202. auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::UnlockedItem);
  203. QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError);
  204. QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError);
  205. job->start();
  206. QVERIFY(jobFailure.wait());
  207. QCOMPARE(jobSuccess.count(), 0);
  208. }
  209. void testLockFile_lockFile_jobError()
  210. {
  211. const auto testFileName = QStringLiteral("file.txt");
  212. static constexpr auto InternalServerErrorHttpErrorCode = 500;
  213. FakeFolder fakeFolder{FileInfo{}};
  214. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  215. fakeFolder.setServerOverride([] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) {
  216. QNetworkReply *reply = nullptr;
  217. if (op == QNetworkAccessManager::CustomOperation && (request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK") ||
  218. request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK"))) {
  219. reply = new FakeErrorReply(op, request, nullptr, InternalServerErrorHttpErrorCode, {});
  220. }
  221. return reply;
  222. });
  223. fakeFolder.localModifier().insert(QStringLiteral("file.txt"));
  224. QVERIFY(fakeFolder.syncOnce());
  225. auto lockFileJob = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem);
  226. QSignalSpy lockFileJobSuccess(lockFileJob, &OCC::LockFileJob::finishedWithoutError);
  227. QSignalSpy lockFileJobFailure(lockFileJob, &OCC::LockFileJob::finishedWithError);
  228. lockFileJob->start();
  229. QVERIFY(lockFileJobFailure.wait());
  230. QCOMPARE(lockFileJobSuccess.count(), 0);
  231. QVERIFY(fakeFolder.syncOnce());
  232. auto unlockFileJob = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::UnlockedItem);
  233. QSignalSpy unlockFileJobSuccess(unlockFileJob, &OCC::LockFileJob::finishedWithoutError);
  234. QSignalSpy unlockFileJobFailure(unlockFileJob, &OCC::LockFileJob::finishedWithError);
  235. unlockFileJob->start();
  236. QVERIFY(unlockFileJobFailure.wait());
  237. QCOMPARE(unlockFileJobSuccess.count(), 0);
  238. QVERIFY(fakeFolder.syncOnce());
  239. }
  240. void testLockFile_lockFile_preconditionFailedError()
  241. {
  242. static constexpr auto PreconditionFailedHttpErrorCode = 412;
  243. const auto testFileName = QStringLiteral("file.txt");
  244. const auto replyData = QByteArray("<?xml version=\"1.0\"?>\n"
  245. "<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
  246. " <nc:lock>1</nc:lock>\n"
  247. " <nc:lock-owner-type>0</nc:lock-owner-type>\n"
  248. " <nc:lock-owner>alice</nc:lock-owner>\n"
  249. " <nc:lock-owner-displayname>Alice Doe</nc:lock-owner-displayname>\n"
  250. " <nc:lock-owner-editor>Text</nc:lock-owner-editor>\n"
  251. " <nc:lock-time>1650619678</nc:lock-time>\n"
  252. " <nc:lock-timeout>300</nc:lock-timeout>\n"
  253. " <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
  254. "</d:prop>\n");
  255. FakeFolder fakeFolder{FileInfo{}};
  256. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  257. fakeFolder.setServerOverride([replyData] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) {
  258. QNetworkReply *reply = nullptr;
  259. if (op == QNetworkAccessManager::CustomOperation && (request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK") ||
  260. request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK"))) {
  261. reply = new FakeErrorReply(op, request, nullptr, PreconditionFailedHttpErrorCode, replyData);
  262. }
  263. return reply;
  264. });
  265. fakeFolder.localModifier().insert(testFileName);
  266. QVERIFY(fakeFolder.syncOnce());
  267. auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::UnlockedItem);
  268. QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError);
  269. QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError);
  270. job->start();
  271. QVERIFY(jobFailure.wait());
  272. QCOMPARE(jobSuccess.count(), 0);
  273. }
  274. };
  275. QTEST_GUILESS_MAIN(TestLockFile)
  276. #include "testlockfile.moc"