fix regressions in automated tests for bulk upload
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Wed, 27 Nov 2024 15:26:00 +0000 (16:26 +0100)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Fri, 29 Nov 2024 10:09:53 +0000 (10:09 +0000)
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
src/libsync/bulkpropagatorjob.cpp
src/libsync/propagateupload.cpp
test/syncenginetestutils.cpp
test/syncenginetestutils.h
test/testsyncengine.cpp

index 58e1ab82f0c958433d0b0160ffbaf2cf09c9e669..2d06298fe10880c5644ca08372876dbaf672d5a7 100644 (file)
@@ -520,6 +520,7 @@ void BulkPropagatorJob::finalize(const QJsonObject &fullReply)
         }
         if (!singleFile._item->hasErrorStatus()) {
             finalizeOneFile(singleFile);
+            singleFile._item->_status = OCC::SyncFileItem::Success;
         }
 
         done(singleFile._item, singleFile._item->_status, {}, ErrorCategory::GenericError);
index d486f861da03f3d28f325365a9c431929065b88c..16e68c96b5ac60a6ddb611597cab81c675f75d56 100644 (file)
@@ -532,7 +532,10 @@ qint64 UploadDevice::readData(char *data, qint64 maxlen)
     }
 
     auto c = _file.read(data, maxlen);
-    if (c < 0) {
+    if (c == 0) {
+        setErrorString({});
+        return c;
+    } else if (c < 0) {
         setErrorString(_file.errorString());
         return -1;
     }
index 39edfc6537abd8e437be20aad9ebe0f0e8f41270..d3f351f1c725d60b190afcd429312f740f50adb0 100644 (file)
@@ -552,12 +552,12 @@ QVector<FileInfo *> FakePutMultiFileReply::performMultiPart(FileInfo &remoteRoot
         auto onePartBody = onePart.mid(headerEndPosition + 4, onePart.size() - headerEndPosition - 6);
         auto onePartHeaders = onePartHeaderPart.split(QStringLiteral("\r\n"));
         QMap<QString, QString> allHeaders;
-        for(auto oneHeader : onePartHeaders) {
+        for(const auto &oneHeader : onePartHeaders) {
             auto headerParts = oneHeader.split(QStringLiteral(": "));
-            allHeaders[headerParts.at(0)] = headerParts.at(1);
+            allHeaders[headerParts.at(0).toLower()] = headerParts.at(1);
         }
-        const auto fileName = allHeaders[QStringLiteral("X-File-Path")];
-        const auto modtime = allHeaders[QByteArrayLiteral("X-File-Mtime")].toLongLong();
+        const auto fileName = allHeaders[QStringLiteral("x-file-path")];
+        const auto modtime = allHeaders[QByteArrayLiteral("x-file-mtime")].toLongLong();
         Q_ASSERT(!fileName.isEmpty());
         Q_ASSERT(modtime > 0);
         FileInfo *fileInfo = remoteRootFileInfo.find(fileName);
@@ -568,7 +568,7 @@ QVector<FileInfo *> FakePutMultiFileReply::performMultiPart(FileInfo &remoteRoot
             // Assume that the file is filled with the same character
             fileInfo = remoteRootFileInfo.create(fileName, onePartBody.size(), onePartBody.at(0).toLatin1());
         }
-        fileInfo->lastModified = OCC::Utility::qDateTimeFromTime_t(request.rawHeader("X-OC-Mtime").toLongLong());
+        fileInfo->lastModified = OCC::Utility::qDateTimeFromTime_t(request.rawHeader("x-oc-mtime").toLongLong());
         remoteRootFileInfo.find(fileName, /*invalidateEtags=*/true);
         result.push_back(fileInfo);
     }
@@ -1052,13 +1052,13 @@ QJsonObject FakeQNAM::forEachReplyPart(QIODevice *outgoingData,
         QMap<QString, QByteArray> allHeaders;
         for(const auto &oneHeader : qAsConst(onePartHeaders)) {
             auto headerParts = oneHeader.split(QStringLiteral(": "));
-            allHeaders[headerParts.at(0)] = headerParts.at(1).toLatin1();
+            allHeaders[headerParts.at(0).toLower()] = headerParts.at(1).toLatin1();
         }
 
         auto reply = replyFunction(allHeaders);
         if (reply.contains(QStringLiteral("error")) &&
                 reply.contains(QStringLiteral("etag"))) {
-            fullReply.insert(allHeaders[QStringLiteral("X-File-Path")], reply);
+            fullReply.insert(allHeaders[QStringLiteral("x-file-path")], reply);
         }
     }
 
@@ -1413,3 +1413,46 @@ FakeFileLockReply::FakeFileLockReply(FileInfo &remoteRootFileInfo,
     xml.writeEndElement(); // prop
     xml.writeEndDocument();
 }
+
+FakeJsonReply::FakeJsonReply(QNetworkAccessManager::Operation op,
+                             const QNetworkRequest &request,
+                             QObject *parent,
+                             int httpReturnCode,
+                             const QJsonDocument &reply)
+    : FakeReply{parent}
+    , _body{reply.toJson()}
+{
+    setRequest(request);
+    setUrl(request.url());
+    setOperation(op);
+    open(QIODevice::ReadOnly);
+    setAttribute(QNetworkRequest::HttpStatusCodeAttribute, httpReturnCode);
+    QMetaObject::invokeMethod(this, &FakeJsonReply::respond, Qt::QueuedConnection);
+}
+
+void FakeJsonReply::respond()
+{
+    emit metaDataChanged();
+    emit readyRead();
+    // finishing can come strictly after readyRead was called
+    QTimer::singleShot(5, this, &FakeJsonReply::slotSetFinished);
+}
+
+void FakeJsonReply::slotSetFinished()
+{
+    setFinished(true);
+    emit finished();
+}
+
+qint64 FakeJsonReply::readData(char *buf, qint64 max)
+{
+    max = qMin<qint64>(max, _body.size());
+    memcpy(buf, _body.constData(), max);
+    _body = _body.mid(max);
+    return max;
+}
+
+qint64 FakeJsonReply::bytesAvailable() const
+{
+    return _body.size();
+}
index dca0eb02b92a4dcc035fc43fbcef0d5fd8fd034c..fee77f1494c89ed6f0206fc3089e8255054e7fc2 100644 (file)
@@ -214,6 +214,29 @@ public:
     using QNetworkReply::setRawHeader;
 };
 
+class FakeJsonReply : public FakeReply
+{
+    Q_OBJECT
+public:
+    FakeJsonReply(QNetworkAccessManager::Operation op,
+                  const QNetworkRequest &request,
+                  QObject *parent,
+                  int httpReturnCode,
+                  const QJsonDocument &reply = QJsonDocument());
+
+    Q_INVOKABLE virtual void respond();
+
+public slots:
+    void slotSetFinished();
+
+public:
+    void abort() override { }
+    qint64 readData(char *buf, qint64 max) override;
+    [[nodiscard]] qint64 bytesAvailable() const override;
+
+    QByteArray _body;
+};
+
 class FakePropfindReply : public FakeReply
 {
     Q_OBJECT
index c46a9f7ab0143d4a179bc634d947dfef19367c85..c745ddcad4ffed1c3d43397584513d27a4d81aa3 100644 (file)
@@ -984,15 +984,17 @@ private slots:
             if (op == QNetworkAccessManager::PostOperation) {
                 ++nPOST;
                 if (contentType.startsWith(QStringLiteral("multipart/related; boundary="))) {
-                    auto jsonReplyObject = fakeFolder.forEachReplyPart(outgoingData, contentType, [] (const QMap<QString, QByteArray> &allHeaders) -> QJsonObject {
+                    auto hasAnError = false;
+                    auto jsonReplyObject = fakeFolder.forEachReplyPart(outgoingData, contentType, [&hasAnError] (const QMap<QString, QByteArray> &allHeaders) -> QJsonObject {
                         auto reply = QJsonObject{};
-                        const auto fileName = allHeaders[QStringLiteral("X-File-Path")];
+                        const auto fileName = allHeaders[QStringLiteral("x-file-path")];
                         if (fileName.endsWith("A/big2") ||
                                 fileName.endsWith("A/big3") ||
                                 fileName.endsWith("A/big4") ||
                                 fileName.endsWith("A/big5") ||
                                 fileName.endsWith("A/big7") ||
                                 fileName.endsWith("B/big8")) {
+                            hasAnError = true;
                             reply.insert(QStringLiteral("error"), true);
                             reply.insert(QStringLiteral("etag"), {});
                             return reply;
@@ -1005,7 +1007,11 @@ private slots:
                     if (jsonReplyObject.size()) {
                         auto jsonReply = QJsonDocument{};
                         jsonReply.setObject(jsonReplyObject);
-                        return new FakeJsonErrorReply{op, request, this, 200, jsonReply};
+                        if (hasAnError) {
+                            return new FakeJsonErrorReply{op, request, this, 200, jsonReply};
+                        } else {
+                            return new FakeJsonReply{op, request, this, 200, jsonReply};
+                        }
                     }
                     return  nullptr;
                 }