add automated tests for LockFileJob to validate proper behavior

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
This commit is contained in:
Matthieu Gallien 2022-04-21 13:57:12 +02:00 коммит произвёл Matthieu Gallien
Родитель fcd07f26a3
Коммит 2ea68d75bd
7 изменённых файлов: 695 добавлений и 14 удалений

Просмотреть файл

@ -65,14 +65,14 @@ bool LockFileJob::finished()
const auto httpErrorCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (httpErrorCode == LOCKED_HTTP_ERROR_CODE) {
const auto record = handleReply();
if (static_cast<SyncFileItem::LockOwnerType>(record._lockOwnerType) == SyncFileItem::LockOwnerType::UserLock) {
Q_EMIT finishedWithError(httpErrorCode, {}, record._lockOwnerDisplayName);
if (static_cast<SyncFileItem::LockOwnerType>(record._lockstate._lockOwnerType) == SyncFileItem::LockOwnerType::UserLock) {
Q_EMIT finishedWithError(httpErrorCode, {}, record._lockstate._lockOwnerDisplayName);
} else {
Q_EMIT finishedWithError(httpErrorCode, {}, record._lockEditorApp);
Q_EMIT finishedWithError(httpErrorCode, {}, record._lockstate._lockEditorApp);
}
} else if (httpErrorCode == PRECONDITION_FAILED_ERROR_CODE) {
const auto record = handleReply();
if (_requestedLockState == SyncFileItem::LockStatus::UnlockedItem && !record._locked) {
if (_requestedLockState == SyncFileItem::LockStatus::UnlockedItem && !record._lockstate._locked) {
Q_EMIT finishedWithoutError();
} else {
Q_EMIT finishedWithError(httpErrorCode, reply()->errorString(), {});
@ -90,13 +90,13 @@ bool LockFileJob::finished()
void LockFileJob::setFileRecordLocked(SyncJournalFileRecord &record) const
{
record._locked = (_lockStatus == SyncFileItem::LockStatus::LockedItem);
record._lockOwnerType = static_cast<int>(_lockOwnerType);
record._lockOwnerDisplayName = _userDisplayName;
record._lockOwnerId = _userId;
record._lockEditorApp = _editorName;
record._lockTime = _lockTime;
record._lockTimeout = _lockTimeout;
record._lockstate._locked = (_lockStatus == SyncFileItem::LockStatus::LockedItem);
record._lockstate._lockOwnerType = static_cast<int>(_lockOwnerType);
record._lockstate._lockOwnerDisplayName = _userDisplayName;
record._lockstate._lockOwnerId = _userId;
record._lockstate._lockEditorApp = _editorName;
record._lockstate._lockTime = _lockTime;
record._lockstate._lockTimeout = _lockTimeout;
}
void LockFileJob::resetState()

Просмотреть файл

@ -64,6 +64,7 @@ nextcloud_add_test(UnifiedSearchListmodel)
nextcloud_add_test(ActivityListModel)
nextcloud_add_test(ActivityData)
nextcloud_add_test(TalkReply)
nextcloud_add_test(LockFile)
if( UNIX AND NOT APPLE )
nextcloud_add_test(InotifyWatcher)

Просмотреть файл

@ -1000,6 +1000,8 @@ QNetworkReply *FakeQNAM::createRequest(QNetworkAccessManager::Operation op, cons
if (contentType.startsWith(QStringLiteral("multipart/related; boundary="))) {
reply = new FakePutMultiFileReply { info, op, newRequest, contentType, outgoingData->readAll(), this };
}
} else if (verb == QLatin1String("LOCK") || verb == QLatin1String("UNLOCK")) {
reply = new FakeFileLockReply{info, op, newRequest, this};
} else {
qDebug() << verb << outgoingData;
Q_UNREACHABLE();
@ -1251,3 +1253,50 @@ FakeJsonErrorReply::FakeJsonErrorReply(QNetworkAccessManager::Operation op,
: FakeErrorReply{ op, request, parent, httpErrorCode, reply.toJson() }
{
}
FakeFileLockReply::FakeFileLockReply(FileInfo &remoteRootFileInfo,
QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
QObject *parent)
: FakePropfindReply(remoteRootFileInfo, op, request, parent)
{
const auto verb = request.attribute(QNetworkRequest::CustomVerbAttribute);
setRequest(request);
setUrl(request.url());
setOperation(op);
open(QIODevice::ReadOnly);
QString fileName = getFilePathFromUrl(request.url());
Q_ASSERT(!fileName.isNull()); // for root, it should be empty
FileInfo *fileInfo = remoteRootFileInfo.find(fileName);
if (!fileInfo) {
QMetaObject::invokeMethod(this, "respond404", Qt::QueuedConnection);
return;
}
const QString prefix = request.url().path().left(request.url().path().size() - fileName.size());
// Don't care about the request and just return a full propfind
const QString davUri { QStringLiteral("DAV:") };
const QString ocUri { QStringLiteral("http://owncloud.org/ns") };
const QString ncUri { QStringLiteral("http://nextcloud.org/ns") };
payload.clear();
QBuffer buffer { &payload };
buffer.open(QIODevice::WriteOnly);
QXmlStreamWriter xml(&buffer);
xml.writeNamespace(davUri, QStringLiteral("d"));
xml.writeNamespace(ocUri, QStringLiteral("oc"));
xml.writeNamespace(ncUri, QStringLiteral("nc"));
xml.writeStartDocument();
xml.writeStartElement(davUri, QStringLiteral("prop"));
xml.writeTextElement(ncUri, QStringLiteral("lock"), verb == QStringLiteral("LOCK") ? "1" : "0");
xml.writeTextElement(ncUri, QStringLiteral("lock-owner-type"), QString::number(0));
xml.writeTextElement(ncUri, QStringLiteral("lock-owner"), QStringLiteral("admin"));
xml.writeTextElement(ncUri, QStringLiteral("lock-owner-displayname"), QStringLiteral("John Doe"));
xml.writeTextElement(ncUri, QStringLiteral("lock-owner-editor"), {});
xml.writeTextElement(ncUri, QStringLiteral("lock-time"), QString::number(1234560));
xml.writeTextElement(ncUri, QStringLiteral("lock-timeout"), QString::number(1800));
xml.writeEndElement(); // prop
xml.writeEndDocument();
}

Просмотреть файл

@ -402,6 +402,16 @@ public:
qint64 readData(char *, qint64) override { return 0; }
};
class FakeFileLockReply : public FakePropfindReply
{
Q_OBJECT
public:
FakeFileLockReply(FileInfo &remoteRootFileInfo,
QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
QObject *parent);
};
// A delayed reply
template <class OriginalReply>
class DelayedReply : public OriginalReply

Просмотреть файл

@ -597,8 +597,6 @@ private slots:
void testDiscoverLockChanges()
{
// Logger::instance()->setLogDebug(true);
FakeFolder fakeFolder{FileInfo{}};
fakeFolder.syncEngine().account()->setCapabilities({{"activity", QVariantMap{{"apiv2", QVariantList{"filters", "filters-api", "previews", "rich-strings"}}}},
{"bruteforce", QVariantMap{{"delay", 0}}},
@ -626,7 +624,7 @@ private slots:
"<nc:lock-owner>user1</nc:lock-owner>"
"<nc:lock-owner-displayname>user1</nc:lock-owner-displayname>"
"<nc:lock-owner-editor>user1</nc:lock-owner-editor>"
"<nc:lock-time>1648046707</nc:lock-time><oc:size>20020</oc:size>";
"<nc:lock-time>1648046707</nc:lock-time>";
fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
fakeFolder.remoteModifier().insert(fooFileSubFolder);

381
test/testlockfile.cpp Normal file
Просмотреть файл

@ -0,0 +1,381 @@
#include "lockfilejobs.h"
#include "account.h"
#include "accountstate.h"
#include "common/syncjournaldb.h"
#include "common/syncjournalfilerecord.h"
#include "syncenginetestutils.h"
#include <QTest>
#include <QSignalSpy>
class TestLockFile : public QObject
{
Q_OBJECT
public:
TestLockFile() = default;
private slots:
void initTestCase()
{
}
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, 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._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<qint64>(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, 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._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("<?xml version=\"1.0\"?>\n"
"<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
" <nc:lock>1</nc:lock>\n"
" <nc:lock-owner-type>0</nc:lock-owner-type>\n"
" <nc:lock-owner>john</nc:lock-owner>\n"
" <nc:lock-owner-displayname>John Doe</nc:lock-owner-displayname>\n"
" <nc:lock-owner-editor>john</nc:lock-owner-editor>\n"
" <nc:lock-time>1650619678</nc:lock-time>\n"
" <nc:lock-timeout>300</nc:lock-timeout>\n"
" <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
"</d:prop>\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 testLockFile_lockFile_alreadyLockedByApp()
{
static constexpr auto LockedHttpErrorCode = 423;
static constexpr auto PreconditionFailedHttpErrorCode = 412;
const auto testFileName = QStringLiteral("file.txt");
const auto replyData = QByteArray("<?xml version=\"1.0\"?>\n"
"<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
" <nc:lock>1</nc:lock>\n"
" <nc:lock-owner-type>1</nc:lock-owner-type>\n"
" <nc:lock-owner>john</nc:lock-owner>\n"
" <nc:lock-owner-displayname>John Doe</nc:lock-owner-displayname>\n"
" <nc:lock-owner-editor>Text</nc:lock-owner-editor>\n"
" <nc:lock-time>1650619678</nc:lock-time>\n"
" <nc:lock-timeout>300</nc:lock-timeout>\n"
" <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
"</d:prop>\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 testLockFile_unlockFile_alreadyUnlocked()
{
static constexpr auto LockedHttpErrorCode = 423;
static constexpr auto PreconditionFailedHttpErrorCode = 412;
const auto testFileName = QStringLiteral("file.txt");
const auto replyData = QByteArray("<?xml version=\"1.0\"?>\n"
"<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
" <nc:lock/>\n"
" <nc:lock-owner-type>0</nc:lock-owner-type>\n"
" <nc:lock-owner>john</nc:lock-owner>\n"
" <nc:lock-owner-displayname>John Doe</nc:lock-owner-displayname>\n"
" <nc:lock-owner-editor>john</nc:lock-owner-editor>\n"
" <nc:lock-time>1650619678</nc:lock-time>\n"
" <nc:lock-timeout>300</nc:lock-timeout>\n"
" <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
"</d:prop>\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::UnlockedItem);
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("<?xml version=\"1.0\"?>\n"
"<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
" <nc:lock>1</nc:lock>\n"
" <nc:lock-owner-type>0</nc:lock-owner-type>\n"
" <nc:lock-owner>alice</nc:lock-owner>\n"
" <nc:lock-owner-displayname>Alice Doe</nc:lock-owner-displayname>\n"
" <nc:lock-owner-editor>Text</nc:lock-owner-editor>\n"
" <nc:lock-time>1650619678</nc:lock-time>\n"
" <nc:lock-timeout>300</nc:lock-timeout>\n"
" <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
"</d:prop>\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, OCC::SyncFileItem::LockStatus::UnlockedItem);
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, 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());
}
void testLockFile_lockFile_preconditionFailedError()
{
static constexpr auto PreconditionFailedHttpErrorCode = 412;
const auto testFileName = QStringLiteral("file.txt");
const auto replyData = QByteArray("<?xml version=\"1.0\"?>\n"
"<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
" <nc:lock>1</nc:lock>\n"
" <nc:lock-owner-type>0</nc:lock-owner-type>\n"
" <nc:lock-owner>alice</nc:lock-owner>\n"
" <nc:lock-owner-displayname>Alice Doe</nc:lock-owner-displayname>\n"
" <nc:lock-owner-editor>Text</nc:lock-owner-editor>\n"
" <nc:lock-time>1650619678</nc:lock-time>\n"
" <nc:lock-timeout>300</nc:lock-timeout>\n"
" <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
"</d:prop>\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, OCC::SyncFileItem::LockStatus::UnlockedItem);
QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError);
QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError);
job->start();
QVERIFY(jobFailure.wait());
QCOMPARE(jobSuccess.count(), 0);
}
};
QTEST_GUILESS_MAIN(TestLockFile)
#include "testlockfile.moc"

242
test/testlockfilejobs.cpp Normal file
Просмотреть файл

@ -0,0 +1,242 @@
#include "lockfilejobs.h"
#include "account.h"
#include "accountstate.h"
#include "common/syncjournaldb.h"
#include "common/syncjournalfilerecord.h"
#include "syncenginetestutils.h"
#include <QTest>
#include <QSignalSpy>
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<qint64>(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("<?xml version=\"1.0\"?>\n"
"<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
" <nc:lock>1</nc:lock>\n"
" <nc:lock-owner-type>0</nc:lock-owner-type>\n"
" <nc:lock-owner>john</nc:lock-owner>\n"
" <nc:lock-owner-displayname>John Doe</nc:lock-owner-displayname>\n"
" <nc:lock-owner-editor>john</nc:lock-owner-editor>\n"
" <nc:lock-time>1650619678</nc:lock-time>\n"
" <nc:lock-timeout>300</nc:lock-timeout>\n"
" <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
"</d:prop>\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("<?xml version=\"1.0\"?>\n"
"<d:prop xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n"
" <nc:lock/>\n"
" <nc:lock-owner-type>0</nc:lock-owner-type>\n"
" <nc:lock-owner>john</nc:lock-owner>\n"
" <nc:lock-owner-displayname>John Doe</nc:lock-owner-displayname>\n"
" <nc:lock-owner-editor>john</nc:lock-owner-editor>\n"
" <nc:lock-time>1650619678</nc:lock-time>\n"
" <nc:lock-timeout>300</nc:lock-timeout>\n"
" <nc:lock-token>files_lock/310997d7-0aae-4e48-97e1-eeb6be6e2202</nc:lock-token>\n"
"</d:prop>\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"