From: Matthieu Gallien Date: Wed, 13 Apr 2022 08:47:23 +0000 (+0200) Subject: allow lock/unlock of files from files explorer integration X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~12^2~17^2~42^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=b55a099b617801ccf7e632be485340db752d0364;p=nextcloud-desktop.git allow lock/unlock of files from files explorer integration add new commands to the contextual menu provided by our files explorer plugins to allow locking/unlocking a file Signed-off-by: Matthieu Gallien --- diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp index d05c75006..44f9b4e60 100644 --- a/src/gui/socketapi/socketapi.cpp +++ b/src/gui/socketapi/socketapi.cpp @@ -958,6 +958,32 @@ void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *) solver.setRemoteVersionFilename(target); } +void SocketApi::command_LOCK_FILE(const QString &localFile, SocketListener *listener) +{ + Q_UNUSED(listener) + + setFileLock(localFile, SyncFileItem::LockStatus::LockedItem); +} + +void SocketApi::command_UNLOCK_FILE(const QString &localFile, SocketListener *listener) +{ + Q_UNUSED(listener) + + setFileLock(localFile, SyncFileItem::LockStatus::UnlockedItem); +} + +void SocketApi::setFileLock(const QString &localFile, const SyncFileItem::LockStatus lockState) const +{ + const auto fileData = FileData::get(localFile); + + const auto shareFolder = fileData.folder; + if (!shareFolder || !shareFolder->accountState()->isConnected()) { + return; + } + + shareFolder->accountState()->account()->setLockFileState(fileData.serverRelativePath, shareFolder->journalDb(), lockState); +} + void SocketApi::command_V2_LIST_ACCOUNTS(const QSharedPointer &job) const { QJsonArray out; @@ -1047,6 +1073,39 @@ void SocketApi::sendSharingContextMenuOptions(const FileData &fileData, SocketLi //listener->sendMessage(QLatin1String("MENU_ITEM:EMAIL_PRIVATE_LINK") + flagString + tr("Send private link by email …")); } +void SocketApi::sendLockFileCommandMenuEntries(const QFileInfo &fileInfo, + Folder* const syncFolder, + const FileData &fileData, + const OCC::SocketListener* const listener) const +{ + if (!fileInfo.isDir() && syncFolder->accountState()->account()->capabilities().filesLockAvailable()) { + if (syncFolder->accountState()->account()->fileLockStatus(syncFolder->journalDb(), fileData.folderRelativePath) == SyncFileItem::LockStatus::UnlockedItem) { + listener->sendMessage(QLatin1String("MENU_ITEM:LOCK_FILE::") + tr("Lock file")); + } else { + if (syncFolder->accountState()->account()->fileCanBeUnlocked(syncFolder->journalDb(), fileData.folderRelativePath)) { + listener->sendMessage(QLatin1String("MENU_ITEM:UNLOCK_FILE::") + tr("Unlock file")); + } + } + } +} + +void SocketApi::sendLockFileInfoMenuEntries(const QFileInfo &fileInfo, + Folder * const syncFolder, + const FileData &fileData, + const SocketListener * const listener, + const SyncJournalFileRecord &record) const +{ + static constexpr auto SECONDS_PER_MINUTE = 60; + if (!fileInfo.isDir() && syncFolder->accountState()->account()->capabilities().filesLockAvailable() && + syncFolder->accountState()->account()->fileLockStatus(syncFolder->journalDb(), fileData.folderRelativePath) == SyncFileItem::LockStatus::LockedItem) { + listener->sendMessage(QLatin1String("MENU_ITEM:LOCKED_FILE_OWNER:d:") + tr("Locked by %1").arg(record._lockstate._lockOwnerDisplayName)); + const auto lockExpirationTime = record._lockstate._lockTime + record._lockstate._lockTimeout; + const auto remainingTime = QDateTime::currentDateTime().secsTo(QDateTime::fromSecsSinceEpoch(lockExpirationTime)); + const auto remainingTimeInMinute = static_cast(remainingTime > 0 ? remainingTime / SECONDS_PER_MINUTE : 0); + listener->sendMessage(QLatin1String("MENU_ITEM:LOCKED_FILE_DATE:d:") + tr("Expire in %1 minutes", "remaining time before lock expire", remainingTimeInMinute).arg(remainingTimeInMinute)); + } +} + SocketApi::FileData SocketApi::FileData::get(const QString &localFile) { FileData data; @@ -1133,6 +1192,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe auto flagString = isOnTheServer && !isE2eEncryptedPath ? QLatin1String("::") : QLatin1String(":d:"); const QFileInfo fileInfo(fileData.localPath); + sendLockFileInfoMenuEntries(fileInfo, syncFolder, fileData, listener, record); if (!fileInfo.isDir()) { listener->sendMessage(QLatin1String("MENU_ITEM:ACTIVITY") + flagString + tr("Activity")); } @@ -1145,6 +1205,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe listener->sendMessage(QLatin1String("MENU_ITEM:OPEN_PRIVATE_LINK") + flagString + tr("Open in browser")); } + sendLockFileCommandMenuEntries(fileInfo, syncFolder, fileData, listener); sendSharingContextMenuOptions(fileData, listener, !isE2eEncryptedPath); // Conflict files get conflict resolution actions diff --git a/src/gui/socketapi/socketapi.h b/src/gui/socketapi/socketapi.h index 11f8836f4..1f36d6cef 100644 --- a/src/gui/socketapi/socketapi.h +++ b/src/gui/socketapi/socketapi.h @@ -27,6 +27,7 @@ class QUrl; class QLocalSocket; class QStringList; +class QFileInfo; namespace OCC { @@ -124,6 +125,10 @@ private: Q_INVOKABLE void command_RESOLVE_CONFLICT(const QString &localFile, SocketListener *listener); Q_INVOKABLE void command_DELETE_ITEM(const QString &localFile, SocketListener *listener); Q_INVOKABLE void command_MOVE_ITEM(const QString &localFile, SocketListener *listener); + Q_INVOKABLE void command_LOCK_FILE(const QString &localFile, SocketListener *listener); + Q_INVOKABLE void command_UNLOCK_FILE(const QString &localFile, SocketListener *listener); + + void setFileLock(const QString &localFile, const SyncFileItem::LockStatus lockState) const; // Windows Shell / Explorer pinning fallbacks, see issue: https://github.com/nextcloud/desktop/issues/1599 #ifdef Q_OS_WIN @@ -145,6 +150,17 @@ private: // Sends the context menu options relating to sharing to listener void sendSharingContextMenuOptions(const FileData &fileData, SocketListener *listener, bool enabled); + void sendLockFileCommandMenuEntries(const QFileInfo &fileInfo, + Folder * const syncFolder, + const FileData &fileData, + const SocketListener * const listener) const; + + void sendLockFileInfoMenuEntries(const QFileInfo &fileInfo, + Folder * const syncFolder, + const FileData &fileData, + const SocketListener * const listener, + const SyncJournalFileRecord &record) const; + /** Send the list of menu item. (added in version 1.1) * argument is a list of files for which the menu should be shown, separated by '\x1e' * Reply with GET_MENU_ITEMS:BEGIN diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 27e4d997d..bdc08c9ba 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -25,8 +25,10 @@ #include "pushnotifications.h" #include "version.h" -#include +#include "deletejob.h" +#include "lockfilejobs.h" +#include "common/syncjournaldb.h" #include "common/asserts.h" #include "clientsideencryption.h" #include "ocsuserstatusconnector.h" @@ -113,6 +115,11 @@ AccountPtr Account::sharedFromThis() return _sharedThis.toStrongRef(); } +AccountPtr Account::sharedFromThis() const +{ + return _sharedThis.toStrongRef(); +} + QString Account::davUser() const { return _davUser.isEmpty() && _credentials ? _credentials->user() : _davUser; @@ -850,4 +857,58 @@ std::shared_ptr Account::userStatusConnector() const return _userStatusConnector; } +void Account::setLockFileState(const QString &serverRelativePath, + SyncJournalDb * const journal, + const SyncFileItem::LockStatus lockStatus) +{ + auto job = std::make_unique(sharedFromThis(), journal, serverRelativePath, lockStatus); + connect(job.get(), &LockFileJob::finishedWithoutError, this, [this]() { + Q_EMIT lockFileSuccess(); + }); + connect(job.get(), &LockFileJob::finishedWithError, this, [lockStatus, serverRelativePath, this](const int httpErrorCode, const QString &errorString, const QString &lockOwnerName) { + auto errorMessage = QString{}; + const auto filePath = serverRelativePath.mid(1); + + if (httpErrorCode == LockFileJob::LOCKED_HTTP_ERROR_CODE) { + errorMessage = tr("File %1 is already locked by %2.").arg(filePath, lockOwnerName); + } else if (lockStatus == SyncFileItem::LockStatus::LockedItem) { + errorMessage = tr("Lock operation on %1 failed with error %2").arg(filePath, errorString); + } else if (lockStatus == SyncFileItem::LockStatus::UnlockedItem) { + errorMessage = tr("Unlock operation on %1 failed with error %2").arg(filePath, errorString); + } + Q_EMIT lockFileError(errorMessage); + }); + job->start(); + static_cast(job.release()); +} + +SyncFileItem::LockStatus Account::fileLockStatus(SyncJournalDb * const journal, + const QString &folderRelativePath) const +{ + SyncJournalFileRecord record; + if (journal->getFileRecord(folderRelativePath, &record)) { + return record._lockstate._locked ? SyncFileItem::LockStatus::LockedItem : SyncFileItem::LockStatus::UnlockedItem; + } + + return SyncFileItem::LockStatus::UnlockedItem; +} + +bool Account::fileCanBeUnlocked(SyncJournalDb * const journal, + const QString &folderRelativePath) const +{ + SyncJournalFileRecord record; + if (journal->getFileRecord(folderRelativePath, &record)) { + if (record._lockstate._lockOwnerType != static_cast(SyncFileItem::LockOwnerType::UserLock)) { + return false; + } + + if (record._lockstate._lockOwnerId != sharedFromThis()->davUser()) { + return false; + } + + return true; + } + return false; +} + } // namespace OCC diff --git a/src/libsync/account.h b/src/libsync/account.h index c9d1ba551..d211d77d0 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -35,6 +35,7 @@ #include #include "capabilities.h" #include "clientsideencryption.h" +#include "syncfileitem.h" class QSettings; class QNetworkReply; @@ -56,6 +57,7 @@ class AccessManager; class SimpleNetworkJob; class PushNotifications; class UserStatusConnector; +class SyncJournalDb; /** * @brief Reimplement this to handle SSL errors from libsync @@ -89,6 +91,8 @@ public: AccountPtr sharedFromThis(); + AccountPtr sharedFromThis() const; + /** * The user that can be used in dav url. * @@ -275,6 +279,15 @@ public: std::shared_ptr userStatusConnector() const; + void setLockFileState(const QString &serverRelativePath, + SyncJournalDb * const journal, + const SyncFileItem::LockStatus lockStatus); + + SyncFileItem::LockStatus fileLockStatus(SyncJournalDb * const journal, + const QString &folderRelativePath) const; + + bool fileCanBeUnlocked(SyncJournalDb * const journal, const QString &folderRelativePath) const; + public slots: /// Used when forgetting credentials void clearQNAMCache(); @@ -311,6 +324,9 @@ signals: void capabilitiesChanged(); + void lockFileSuccess(); + void lockFileError(const QString&); + protected Q_SLOTS: void slotCredentialsFetched(); void slotCredentialsAsked(); diff --git a/test/testlockfile.cpp b/test/testlockfile.cpp index 78e76e6a8..97538295e 100644 --- a/test/testlockfile.cpp +++ b/test/testlockfile.cpp @@ -21,6 +21,113 @@ private slots: { } + void testLockFile_lockFile_lockSuccess() + { + const auto testFileName = QStringLiteral("file.txt"); + + FakeFolder fakeFolder{FileInfo{}}; + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + + QSignalSpy lockFileSuccessSpy(fakeFolder.account().data(), &OCC::Account::lockFileSuccess); + QSignalSpy lockFileErrorSpy(fakeFolder.account().data(), &OCC::Account::lockFileError); + + fakeFolder.localModifier().insert(testFileName); + + QVERIFY(fakeFolder.syncOnce()); + + fakeFolder.account()->setLockFileState(QStringLiteral("/") + testFileName, &fakeFolder.syncJournal(), OCC::SyncFileItem::LockStatus::LockedItem); + + QVERIFY(lockFileSuccessSpy.wait()); + QCOMPARE(lockFileErrorSpy.count(), 0); + } + + void testLockFile_lockFile_lockError() + { + const auto testFileName = QStringLiteral("file.txt"); + static constexpr auto LockedHttpErrorCode = 423; + const auto replyData = QByteArray("\n" + "\n" + " \n" + " 0\n" + " john\n" + " John Doe\n" + " john\n" + " 1650619678\n" + " 300\n" + " files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202\n" + "\n"); + + FakeFolder fakeFolder{FileInfo{}}; + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + + fakeFolder.setServerOverride([replyData] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) { + QNetworkReply *reply = nullptr; + if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK")) { + reply = new FakeErrorReply(op, request, nullptr, LockedHttpErrorCode, replyData); + } + + return reply; + }); + + QSignalSpy lockFileSuccessSpy(fakeFolder.account().data(), &OCC::Account::lockFileSuccess); + QSignalSpy lockFileErrorSpy(fakeFolder.account().data(), &OCC::Account::lockFileError); + + fakeFolder.localModifier().insert(testFileName); + + QVERIFY(fakeFolder.syncOnce()); + + fakeFolder.account()->setLockFileState(QStringLiteral("/") + testFileName, &fakeFolder.syncJournal(), OCC::SyncFileItem::LockStatus::LockedItem); + + QVERIFY(lockFileErrorSpy.wait()); + QCOMPARE(lockFileSuccessSpy.count(), 0); + } + + void testLockFile_fileLockStatus_queryLockStatus() + { + const auto testFileName = QStringLiteral("file.txt"); + + FakeFolder fakeFolder{FileInfo{}}; + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + + QSignalSpy lockFileSuccessSpy(fakeFolder.account().data(), &OCC::Account::lockFileSuccess); + QSignalSpy lockFileErrorSpy(fakeFolder.account().data(), &OCC::Account::lockFileError); + + fakeFolder.localModifier().insert(testFileName); + + QVERIFY(fakeFolder.syncOnce()); + + fakeFolder.account()->setLockFileState(QStringLiteral("/") + testFileName, &fakeFolder.syncJournal(), OCC::SyncFileItem::LockStatus::LockedItem); + + QVERIFY(lockFileSuccessSpy.wait()); + QCOMPARE(lockFileErrorSpy.count(), 0); + + auto lockStatus = fakeFolder.account()->fileLockStatus(&fakeFolder.syncJournal(), testFileName); + QCOMPARE(lockStatus, OCC::SyncFileItem::LockStatus::LockedItem); + } + + void testLockFile_fileCanBeUnlocked_canUnlock() + { + const auto testFileName = QStringLiteral("file.txt"); + + FakeFolder fakeFolder{FileInfo{}}; + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + + QSignalSpy lockFileSuccessSpy(fakeFolder.account().data(), &OCC::Account::lockFileSuccess); + QSignalSpy lockFileErrorSpy(fakeFolder.account().data(), &OCC::Account::lockFileError); + + fakeFolder.localModifier().insert(testFileName); + + QVERIFY(fakeFolder.syncOnce()); + + fakeFolder.account()->setLockFileState(QStringLiteral("/") + testFileName, &fakeFolder.syncJournal(), OCC::SyncFileItem::LockStatus::LockedItem); + + QVERIFY(lockFileSuccessSpy.wait()); + QCOMPARE(lockFileErrorSpy.count(), 0); + + auto lockStatus = fakeFolder.account()->fileCanBeUnlocked(&fakeFolder.syncJournal(), testFileName); + QCOMPARE(lockStatus, true); + } + void testLockFile_lockFile_jobSuccess() { const auto testFileName = QStringLiteral("file.txt"); diff --git a/test/testlockfilejobs.cpp b/test/testlockfilejobs.cpp deleted file mode 100644 index 6e0b5f7dc..000000000 --- a/test/testlockfilejobs.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include "lockfilejobs.h" - -#include "account.h" -#include "accountstate.h" -#include "common/syncjournaldb.h" -#include "common/syncjournalfilerecord.h" -#include "syncenginetestutils.h" - -#include -#include - -class TestLockFileJobs : public QObject -{ - Q_OBJECT - -public: - TestLockFileJobs() = default; - -private slots: - void initTestCase() - { - } - - void testLockFileJob_lockFile_jobSuccess() - { - const auto testFileName = QStringLiteral("file.txt"); - FakeFolder fakeFolder{FileInfo{}}; - QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - - fakeFolder.localModifier().insert(testFileName); - - QVERIFY(fakeFolder.syncOnce()); - - auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem); - - QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError); - QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError); - - job->start(); - - QVERIFY(jobSuccess.wait()); - QCOMPARE(jobFailure.count(), 0); - - auto fileRecord = OCC::SyncJournalFileRecord{}; - QVERIFY(fakeFolder.syncJournal().getFileRecord(testFileName, &fileRecord)); - QCOMPARE(fileRecord._locked, true); - QCOMPARE(fileRecord._lockEditorApp, QString{}); - QCOMPARE(fileRecord._lockOwnerDisplayName, QStringLiteral("John Doe")); - QCOMPARE(fileRecord._lockOwnerId, QStringLiteral("john")); - QCOMPARE(fileRecord._lockOwnerType, static_cast(OCC::SyncFileItem::LockOwnerType::UserLock)); - QCOMPARE(fileRecord._lockTime, 1234560); - QCOMPARE(fileRecord._lockTimeout, 1800); - - QVERIFY(fakeFolder.syncOnce()); - } - - void testLockFileJob_lockFile_unlockFile_jobSuccess() - { - const auto testFileName = QStringLiteral("file.txt"); - FakeFolder fakeFolder{FileInfo{}}; - QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - - fakeFolder.localModifier().insert(testFileName); - - QVERIFY(fakeFolder.syncOnce()); - - auto lockFileJob = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem); - - QSignalSpy lockFileJobSuccess(lockFileJob, &OCC::LockFileJob::finishedWithoutError); - QSignalSpy lockFileJobFailure(lockFileJob, &OCC::LockFileJob::finishedWithError); - - lockFileJob->start(); - - QVERIFY(lockFileJobSuccess.wait()); - QCOMPARE(lockFileJobFailure.count(), 0); - - QVERIFY(fakeFolder.syncOnce()); - - auto unlockFileJob = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::UnlockedItem); - - QSignalSpy unlockFileJobSuccess(unlockFileJob, &OCC::LockFileJob::finishedWithoutError); - QSignalSpy unlockFileJobFailure(unlockFileJob, &OCC::LockFileJob::finishedWithError); - - unlockFileJob->start(); - - QVERIFY(unlockFileJobSuccess.wait()); - QCOMPARE(unlockFileJobFailure.count(), 0); - - auto fileRecord = OCC::SyncJournalFileRecord{}; - QVERIFY(fakeFolder.syncJournal().getFileRecord(testFileName, &fileRecord)); - QCOMPARE(fileRecord._locked, false); - - QVERIFY(fakeFolder.syncOnce()); - } - - void testLockFileJob_lockFile_alreadyLocked() - { - static constexpr auto LockedHttpErrorCode = 423; - static constexpr auto PreconditionFailedHttpErrorCode = 412; - - const auto testFileName = QStringLiteral("file.txt"); - - const auto replyData = QByteArray("\n" - "\n" - " 1\n" - " 0\n" - " john\n" - " John Doe\n" - " john\n" - " 1650619678\n" - " 300\n" - " files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202\n" - "\n"); - - FakeFolder fakeFolder{FileInfo{}}; - QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - - fakeFolder.setServerOverride([replyData] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) { - QNetworkReply *reply = nullptr; - if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK")) { - reply = new FakeErrorReply(op, request, nullptr, LockedHttpErrorCode, replyData); - } else if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK")) { - reply = new FakeErrorReply(op, request, nullptr, PreconditionFailedHttpErrorCode, replyData); - } - - return reply; - }); - - fakeFolder.localModifier().insert(testFileName); - - QVERIFY(fakeFolder.syncOnce()); - - auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem); - - QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError); - QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError); - - job->start(); - - QVERIFY(jobFailure.wait()); - QCOMPARE(jobSuccess.count(), 0); - } - - void testLockFileJob_unlockFile_alreadyUnlocked() - { - static constexpr auto LockedHttpErrorCode = 423; - static constexpr auto PreconditionFailedHttpErrorCode = 412; - - const auto testFileName = QStringLiteral("file.txt"); - - const auto replyData = QByteArray("\n" - "\n" - " \n" - " 0\n" - " john\n" - " John Doe\n" - " john\n" - " 1650619678\n" - " 300\n" - " files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202\n" - "\n"); - - FakeFolder fakeFolder{FileInfo{}}; - QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - - fakeFolder.setServerOverride([replyData] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) { - QNetworkReply *reply = nullptr; - if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK")) { - reply = new FakeErrorReply(op, request, nullptr, LockedHttpErrorCode, replyData); - } else if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK")) { - reply = new FakeErrorReply(op, request, nullptr, PreconditionFailedHttpErrorCode, replyData); - } - - return reply; - }); - - fakeFolder.localModifier().insert(testFileName); - - QVERIFY(fakeFolder.syncOnce()); - - auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem); - - QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError); - QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError); - - job->start(); - - QVERIFY(jobFailure.wait()); - QCOMPARE(jobSuccess.count(), 0); - } - - void testLockFileJob_lockFile_jobError() - { - const auto testFileName = QStringLiteral("file.txt"); - static constexpr auto InternalServerErrorHttpErrorCode = 500; - - FakeFolder fakeFolder{FileInfo{}}; - QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - - fakeFolder.setServerOverride([] (FakeQNAM::Operation op, const QNetworkRequest &request, QIODevice *) { - QNetworkReply *reply = nullptr; - if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("LOCK")) { - reply = new FakeErrorReply(op, request, nullptr, InternalServerErrorHttpErrorCode, {}); - } else if (op == QNetworkAccessManager::CustomOperation && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK")) { - reply = new FakeErrorReply(op, request, nullptr, InternalServerErrorHttpErrorCode, {}); - } - - return reply; - }); - - fakeFolder.localModifier().insert(QStringLiteral("file.txt")); - - QVERIFY(fakeFolder.syncOnce()); - - auto lockFileJob = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::LockedItem); - - QSignalSpy lockFileJobSuccess(lockFileJob, &OCC::LockFileJob::finishedWithoutError); - QSignalSpy lockFileJobFailure(lockFileJob, &OCC::LockFileJob::finishedWithError); - - lockFileJob->start(); - - QVERIFY(lockFileJobFailure.wait()); - QCOMPARE(lockFileJobSuccess.count(), 0); - - QVERIFY(fakeFolder.syncOnce()); - - auto unlockFileJob = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, OCC::SyncFileItem::LockStatus::UnlockedItem); - - QSignalSpy unlockFileJobSuccess(unlockFileJob, &OCC::LockFileJob::finishedWithoutError); - QSignalSpy unlockFileJobFailure(unlockFileJob, &OCC::LockFileJob::finishedWithError); - - unlockFileJob->start(); - - QVERIFY(unlockFileJobFailure.wait()); - QCOMPARE(unlockFileJobSuccess.count(), 0); - - QVERIFY(fakeFolder.syncOnce()); - } -}; - -QTEST_MAIN(TestLockFileJobs) -#include "testlockfilejobs.moc"