nextcloud-desktop/test/testblacklist.cpp
Olivier Goffart 09cacc4cd4
Blacklist: remember the X-Request-ID
Issue #6420
Store the X-Request-ID in the SyncFileItem and also in the blacklist.
Note that for consistency reason, the X-Request-ID is also in the
SyncFileItem if the request succeeds.

Currently there is no UI to access it, but it can be queried with sql
commands
2020-12-15 10:57:53 +01:00

193 lines
6.9 KiB
C++

/*
* This software is in the public domain, furnished "as is", without technical
* support, and with no warranty, express or implied, as to its usefulness for
* any purpose.
*
*/
#include <QtTest>
#include "syncenginetestutils.h"
#include <syncengine.h>
using namespace OCC;
SyncFileItemPtr findItem(const QSignalSpy &spy, const QString &path)
{
for (const QList<QVariant> &args : spy) {
auto item = args[0].value<SyncFileItemPtr>();
if (item->destination() == path)
return item;
}
return SyncFileItemPtr(new SyncFileItem);
}
SyncJournalFileRecord journalRecord(FakeFolder &folder, const QByteArray &path)
{
SyncJournalFileRecord rec;
folder.syncJournal().getFileRecord(path, &rec);
return rec;
}
class TestBlacklist : public QObject
{
Q_OBJECT
private slots:
void testBlacklistBasic_data()
{
QTest::addColumn<bool>("remote");
QTest::newRow("remote") << true;
QTest::newRow("local") << false;
}
void testBlacklistBasic()
{
QFETCH(bool, remote);
FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
auto &modifier = remote ? fakeFolder.remoteModifier() : fakeFolder.localModifier();
int counter = 0;
QByteArray reqId;
fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *) -> QNetworkReply * {
reqId = req.rawHeader("X-Request-ID");
if (!remote && op == QNetworkAccessManager::PutOperation)
++counter;
if (remote && op == QNetworkAccessManager::GetOperation)
++counter;
return nullptr;
});
auto cleanup = [&]() {
completeSpy.clear();
};
auto initialEtag = journalRecord(fakeFolder, "A")._etag;
QVERIFY(!initialEtag.isEmpty());
// The first sync and the download will fail - the item will be blacklisted
modifier.insert("A/new");
fakeFolder.serverErrorPaths().append("A/new", 500); // will be blacklisted
QVERIFY(!fakeFolder.syncOnce());
{
auto it = findItem(completeSpy, "A/new");
QVERIFY(it);
QCOMPARE(it->_status, SyncFileItem::NormalError); // initial error visible
QCOMPARE(it->_instruction, CSYNC_INSTRUCTION_NEW);
auto entry = fakeFolder.syncJournal().errorBlacklistEntry("A/new");
QVERIFY(entry.isValid());
QCOMPARE(entry._errorCategory, SyncJournalErrorBlacklistRecord::Normal);
QCOMPARE(entry._retryCount, 1);
QCOMPARE(counter, 1);
QVERIFY(entry._ignoreDuration > 0);
QCOMPARE(entry._requestId, reqId);
if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, initialEtag);
}
cleanup();
// Ignored during the second run - but soft errors are also errors
QVERIFY(!fakeFolder.syncOnce());
{
auto it = findItem(completeSpy, "A/new");
QVERIFY(it);
QCOMPARE(it->_status, SyncFileItem::BlacklistedError);
QCOMPARE(it->_instruction, CSYNC_INSTRUCTION_IGNORE); // no retry happened!
auto entry = fakeFolder.syncJournal().errorBlacklistEntry("A/new");
QVERIFY(entry.isValid());
QCOMPARE(entry._errorCategory, SyncJournalErrorBlacklistRecord::Normal);
QCOMPARE(entry._retryCount, 1);
QCOMPARE(counter, 1);
QVERIFY(entry._ignoreDuration > 0);
QCOMPARE(entry._requestId, reqId);
if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, initialEtag);
}
cleanup();
// Let's expire the blacklist entry to verify it gets retried
{
auto entry = fakeFolder.syncJournal().errorBlacklistEntry("A/new");
entry._ignoreDuration = 1;
entry._lastTryTime -= 1;
fakeFolder.syncJournal().setErrorBlacklistEntry(entry);
}
QVERIFY(!fakeFolder.syncOnce());
{
auto it = findItem(completeSpy, "A/new");
QVERIFY(it);
QCOMPARE(it->_status, SyncFileItem::BlacklistedError); // blacklisted as it's just a retry
QCOMPARE(it->_instruction, CSYNC_INSTRUCTION_NEW); // retry!
auto entry = fakeFolder.syncJournal().errorBlacklistEntry("A/new");
QVERIFY(entry.isValid());
QCOMPARE(entry._errorCategory, SyncJournalErrorBlacklistRecord::Normal);
QCOMPARE(entry._retryCount, 2);
QCOMPARE(counter, 2);
QVERIFY(entry._ignoreDuration > 0);
QCOMPARE(entry._requestId, reqId);
if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, initialEtag);
}
cleanup();
// When the file changes a retry happens immediately
modifier.appendByte("A/new");
QVERIFY(!fakeFolder.syncOnce());
{
auto it = findItem(completeSpy, "A/new");
QVERIFY(it);
QCOMPARE(it->_status, SyncFileItem::BlacklistedError);
QCOMPARE(it->_instruction, CSYNC_INSTRUCTION_NEW); // retry!
auto entry = fakeFolder.syncJournal().errorBlacklistEntry("A/new");
QVERIFY(entry.isValid());
QCOMPARE(entry._errorCategory, SyncJournalErrorBlacklistRecord::Normal);
QCOMPARE(entry._retryCount, 3);
QCOMPARE(counter, 3);
QVERIFY(entry._ignoreDuration > 0);
QCOMPARE(entry._requestId, reqId);
if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, initialEtag);
}
cleanup();
// When the error goes away and the item is retried, the sync succeeds
fakeFolder.serverErrorPaths().clear();
{
auto entry = fakeFolder.syncJournal().errorBlacklistEntry("A/new");
entry._ignoreDuration = 1;
entry._lastTryTime -= 1;
fakeFolder.syncJournal().setErrorBlacklistEntry(entry);
}
QVERIFY(fakeFolder.syncOnce());
{
auto it = findItem(completeSpy, "A/new");
QVERIFY(it);
QCOMPARE(it->_status, SyncFileItem::Success);
QCOMPARE(it->_instruction, CSYNC_INSTRUCTION_NEW);
auto entry = fakeFolder.syncJournal().errorBlacklistEntry("A/new");
QVERIFY(!entry.isValid());
QCOMPARE(counter, 4);
if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, fakeFolder.currentRemoteState().find("A")->etag.toUtf8());
}
cleanup();
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
}
};
QTEST_GUILESS_MAIN(TestBlacklist)
#include "testblacklist.moc"