#include "lockfilejobs.h" #include "account.h" #include "accountstate.h" #include "common/syncjournaldb.h" #include "common/syncjournalfilerecord.h" #include "syncenginetestutils.h" #include "localdiscoverytracker.h" #include #include class TestLockFile : public QObject { Q_OBJECT public: TestLockFile() = default; private slots: void initTestCase() { OCC::Logger::instance()->setLogFlush(true); OCC::Logger::instance()->setLogDebug(true); QStandardPaths::setTestModeEnabled(true); } 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, QStringLiteral("/"), fakeFolder.localPath(), &fakeFolder.syncJournal(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); 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, QStringLiteral("/"), fakeFolder.localPath(), &fakeFolder.syncJournal(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); 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, QStringLiteral("/"), fakeFolder.localPath(), &fakeFolder.syncJournal(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); 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, QStringLiteral("/"), fakeFolder.localPath(), &fakeFolder.syncJournal(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); 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"); 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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); 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._lockstate._locked, true); QCOMPARE(fileRecord._lockstate._lockEditorApp, QString{}); QCOMPARE(fileRecord._lockstate._lockOwnerDisplayName, QStringLiteral("John Doe")); QCOMPARE(fileRecord._lockstate._lockOwnerId, QStringLiteral("admin")); QCOMPARE(fileRecord._lockstate._lockOwnerType, static_cast(OCC::SyncFileItem::LockOwnerType::UserLock)); QCOMPARE(fileRecord._lockstate._lockTime, 1234560); QCOMPARE(fileRecord._lockstate._lockTimeout, 1800); QVERIFY(fakeFolder.syncOnce()); } void testLockFile_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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); 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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::UnlockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); 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._lockstate._locked, false); QVERIFY(fakeFolder.syncOnce()); } void testLockFile_lockFile_alreadyLockedByUser() { 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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError); QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError); job->start(); QVERIFY(jobFailure.wait()); QCOMPARE(jobSuccess.count(), 0); } void testLockFile_lockFile_alreadyLockedByApp() { static constexpr auto LockedHttpErrorCode = 423; static constexpr auto PreconditionFailedHttpErrorCode = 412; const auto testFileName = QStringLiteral("file.txt"); const auto replyData = QByteArray("\n" "\n" " 1\n" " 1\n" " john\n" " John Doe\n" " Text\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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError); QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError); job->start(); QVERIFY(jobFailure.wait()); QCOMPARE(jobSuccess.count(), 0); } void testLockFile_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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::UnlockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError); QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError); job->start(); QVERIFY(jobSuccess.wait()); QCOMPARE(jobFailure.count(), 0); } void testLockFile_unlockFile_lockedBySomeoneElse() { static constexpr auto LockedHttpErrorCode = 423; const auto testFileName = QStringLiteral("file.txt"); const auto replyData = QByteArray("\n" "\n" " 1\n" " 0\n" " alice\n" " Alice Doe\n" " Text\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") || request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("UNLOCK"))) { reply = new FakeErrorReply(op, request, nullptr, LockedHttpErrorCode, replyData); } return reply; }); fakeFolder.localModifier().insert(testFileName); QVERIFY(fakeFolder.syncOnce()); auto job = new OCC::LockFileJob(fakeFolder.account(), &fakeFolder.syncJournal(), QStringLiteral("/") + testFileName, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::UnlockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError); QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError); job->start(); QVERIFY(jobFailure.wait()); QCOMPARE(jobSuccess.count(), 0); } void testLockFile_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") || 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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::LockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); 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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::UnlockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); QSignalSpy unlockFileJobSuccess(unlockFileJob, &OCC::LockFileJob::finishedWithoutError); QSignalSpy unlockFileJobFailure(unlockFileJob, &OCC::LockFileJob::finishedWithError); unlockFileJob->start(); QVERIFY(unlockFileJobFailure.wait()); QCOMPARE(unlockFileJobSuccess.count(), 0); QVERIFY(fakeFolder.syncOnce()); } void testLockFile_lockFile_preconditionFailedError() { static constexpr auto PreconditionFailedHttpErrorCode = 412; const auto testFileName = QStringLiteral("file.txt"); const auto replyData = QByteArray("\n" "\n" " 1\n" " 0\n" " alice\n" " Alice Doe\n" " Text\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") || 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, QStringLiteral("/"), fakeFolder.localPath(), OCC::SyncFileItem::LockStatus::UnlockedItem, OCC::SyncFileItem::LockOwnerType::UserLock); QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError); QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError); job->start(); QVERIFY(jobFailure.wait()); QCOMPARE(jobSuccess.count(), 0); } void testSyncLockedFilesAlmostExpired() { FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); QSignalSpy spySyncCompleted(&fakeFolder.syncEngine(), &OCC::SyncEngine::finished); ItemCompletedSpy completeSpy(fakeFolder); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem); OCC::SyncJournalFileRecord fileRecordBefore; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordBefore)); QVERIFY(!fileRecordBefore._lockstate._locked); fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileLocked, 1, QStringLiteral("Nextcloud Office"), {}, QStringLiteral("richdocuments"), QDateTime::currentDateTime().toSecsSinceEpoch() - 1220, 1226); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::LockedItem); OCC::SyncJournalFileRecord fileRecordLocked; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordLocked)); QVERIFY(fileRecordLocked._lockstate._locked); spySyncCompleted.clear(); QTest::qWait(5000); fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileUnlocked, {}, {}, {}, {}, {}, {}); QCOMPARE(spySyncCompleted.count(), 0); QVERIFY(spySyncCompleted.wait(3000)); QCOMPARE(spySyncCompleted.count(), 1); OCC::SyncJournalFileRecord fileRecordUnlocked; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordUnlocked)); QVERIFY(!fileRecordUnlocked._lockstate._locked); } void testSyncLockedFilesNoExpiredLockedFiles() { FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); QSignalSpy spySyncCompleted(&fakeFolder.syncEngine(), &OCC::SyncEngine::finished); ItemCompletedSpy completeSpy(fakeFolder); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem); OCC::SyncJournalFileRecord fileRecordBefore; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordBefore)); QVERIFY(!fileRecordBefore._lockstate._locked); fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileLocked, 1, QStringLiteral("Nextcloud Office"), {}, QStringLiteral("richdocuments"), QDateTime::currentDateTime().toSecsSinceEpoch() - 1220, 1226); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::LockedItem); OCC::SyncJournalFileRecord fileRecordLocked; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordLocked)); QVERIFY(fileRecordLocked._lockstate._locked); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); spySyncCompleted.clear(); QTest::qWait(1000); fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileUnlocked, {}, {}, {}, {}, {}, {}); QCOMPARE(spySyncCompleted.count(), 0); QVERIFY(!spySyncCompleted.wait(3000)); QCOMPARE(spySyncCompleted.count(), 0); OCC::SyncJournalFileRecord fileRecordUnlocked; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordUnlocked)); QVERIFY(fileRecordUnlocked._lockstate._locked); } void testSyncLockedFiles() { FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); int nGET = 0, nPUT = 0; QObject parent; fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) -> QNetworkReply * { Q_UNUSED(outgoingData) Q_UNUSED(request) if (op == QNetworkAccessManager::PutOperation) { ++nPUT; } else if (op == QNetworkAccessManager::GetOperation) { ++nGET; } return nullptr; }); ItemCompletedSpy completeSpy(fakeFolder); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(nGET, 0); QCOMPARE(nPUT, 0); QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem); OCC::SyncJournalFileRecord fileRecordBefore; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordBefore)); QVERIFY(!fileRecordBefore._lockstate._locked); fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileLocked, 1, QStringLiteral("Nextcloud Office"), {}, QStringLiteral("richdocuments"), QDateTime::currentDateTime().toSecsSinceEpoch(), 1226); fakeFolder.remoteModifier().setModTimeKeepEtag(QStringLiteral("A/a1"), QDateTime::currentDateTime()); fakeFolder.remoteModifier().appendByte(QStringLiteral("A/a1")); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(nGET, 1); QCOMPARE(nPUT, 0); QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::LockedItem); OCC::SyncJournalFileRecord fileRecordLocked; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordLocked)); QVERIFY(fileRecordLocked._lockstate._locked); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(nGET, 1); QCOMPARE(nPUT, 0); OCC::SyncJournalFileRecord fileRecordAfter; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordAfter)); QVERIFY(fileRecordAfter._lockstate._locked); auto expectedState = fakeFolder.currentLocalState(); QCOMPARE(fakeFolder.currentRemoteState(), expectedState); } void testLockFile_lockedFileReadOnly_afterSync() { FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); ItemCompletedSpy completeSpy(fakeFolder); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem); OCC::SyncJournalFileRecord fileRecordBefore; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordBefore)); QVERIFY(!fileRecordBefore._lockstate._locked); QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); const auto localFileNotLocked = QFileInfo{fakeFolder.localPath() + u"A/a1"}; QVERIFY(localFileNotLocked.isWritable()); fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileLocked, 1, QStringLiteral("Nextcloud Office"), {}, QStringLiteral("richdocuments"), QDateTime::currentDateTime().toSecsSinceEpoch(), 1226); fakeFolder.remoteModifier().setModTimeKeepEtag(QStringLiteral("A/a1"), QDateTime::currentDateTime()); fakeFolder.remoteModifier().appendByte(QStringLiteral("A/a1")); completeSpy.clear(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::LockedItem); OCC::SyncJournalFileRecord fileRecordLocked; QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordLocked)); QVERIFY(fileRecordLocked._lockstate._locked); auto expectedState = fakeFolder.currentLocalState(); QCOMPARE(fakeFolder.currentRemoteState(), expectedState); const auto localFileLocked = QFileInfo{fakeFolder.localPath() + u"A/a1"}; QVERIFY(!localFileLocked.isWritable()); } void testLockFile_lockFile_detect_newly_uploaded() { const auto testFileName = QStringLiteral("document.docx"); const auto testLockFileName = QStringLiteral(".~lock.document.docx#"); const auto testDocumentsDirName = "documents"; FakeFolder fakeFolder{FileInfo{}}; fakeFolder.localModifier().mkdir(testDocumentsDirName); fakeFolder.syncEngine().account()->setCapabilities({{"files", QVariantMap{{"locking", QByteArray{"1.0"}}}}}); QSignalSpy lockFileDetectedNewlyUploadedSpy(&fakeFolder.syncEngine(), &OCC::SyncEngine::lockFileDetected); fakeFolder.localModifier().insert(testDocumentsDirName + QString("/") + testLockFileName); fakeFolder.localModifier().insert(testDocumentsDirName + QString("/") + testFileName); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(lockFileDetectedNewlyUploadedSpy.count(), 1); } }; QTEST_GUILESS_MAIN(TestLockFile) #include "testlockfile.moc"