File indexing completed on 2024-12-22 04:57:37

0001 /*  This file is part of the KDE project
0002     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
0003     SPDX-FileContributor: Kevin Krammer <krake@kdab.com>
0004 
0005     SPDX-FileCopyrightText: 2011 Kevin Krammer <kevin.krammer@gmx.at>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "mixedmaildirstore.h"
0011 
0012 #include "testdatautil.h"
0013 
0014 #include "filestore/itemfetchjob.h"
0015 #include "filestore/itemmodifyjob.h"
0016 #include "filestore/storecompactjob.h"
0017 
0018 #include "libmaildir/maildir.h"
0019 
0020 #include <Akonadi/MessageParts>
0021 
0022 #include <Akonadi/ItemFetchScope>
0023 
0024 #include <KMbox/MBox>
0025 
0026 #include <KMime/Message>
0027 
0028 #include <KRandom>
0029 
0030 #include <QCryptographicHash>
0031 #include <QDir>
0032 #include <QFileInfo>
0033 #include <QRandomGenerator>
0034 #include <QTemporaryDir>
0035 #include <QTest>
0036 
0037 using namespace Akonadi;
0038 using namespace KMBox;
0039 
0040 static bool fullEntryCompare(const MBoxEntry &a, const MBoxEntry &b)
0041 {
0042     return a.messageOffset() == b.messageOffset() && a.separatorSize() == b.separatorSize() && a.messageSize() == b.messageSize();
0043 }
0044 
0045 class ItemModifyTest : public QObject
0046 {
0047     Q_OBJECT
0048 
0049 public:
0050     ItemModifyTest()
0051         : QObject()
0052         , mStore(nullptr)
0053         , mDir(nullptr)
0054     {
0055     }
0056 
0057     ~ItemModifyTest() override
0058     {
0059         delete mStore;
0060         delete mDir;
0061     }
0062 
0063 private:
0064     MixedMaildirStore *mStore = nullptr;
0065     QTemporaryDir *mDir = nullptr;
0066 
0067 private Q_SLOTS:
0068     void init();
0069     void cleanup();
0070     void testExpectedFail();
0071     void testIgnorePayload();
0072     void testModifyPayload();
0073     void testModifyFlags();
0074     void testModifyFlagsAndPayload();
0075 };
0076 
0077 void ItemModifyTest::init()
0078 {
0079     mStore = new MixedMaildirStore;
0080 
0081     mDir = new QTemporaryDir;
0082     QVERIFY(mDir->isValid());
0083     QVERIFY(QDir(mDir->path()).exists());
0084 }
0085 
0086 void ItemModifyTest::cleanup()
0087 {
0088     delete mStore;
0089     mStore = nullptr;
0090     delete mDir;
0091     mDir = nullptr;
0092 }
0093 
0094 void ItemModifyTest::testExpectedFail()
0095 {
0096     QDir topDir(mDir->path());
0097 
0098     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("collection1")));
0099     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QStringLiteral("collection2")));
0100 
0101     KPIM::Maildir topLevelMd(topDir.path(), true);
0102 
0103     KPIM::Maildir md1 = topLevelMd.subFolder(QStringLiteral("collection1"));
0104     QStringList md1EntryList = md1.entryList();
0105     QSet<QString> entrySet1(md1EntryList.cbegin(), md1EntryList.cend());
0106     QCOMPARE((int)entrySet1.count(), 4);
0107 
0108     QFileInfo fileInfo2(topDir.path(), QStringLiteral("collection2"));
0109     MBox mbox2;
0110     QVERIFY(mbox2.load(fileInfo2.absoluteFilePath()));
0111     const MBoxEntry::List entryList2 = mbox2.entries();
0112     QCOMPARE((int)entryList2.count(), 4);
0113 
0114     QSet<qint64> entrySet2;
0115     for (const MBoxEntry &entry : entryList2) {
0116         entrySet2 << entry.messageOffset();
0117     }
0118 
0119     mStore->setPath(topDir.path());
0120 
0121     // common variables
0122     FileStore::ItemModifyJob *job = nullptr;
0123 
0124     // test failure of modifying a non-existent maildir entry
0125     Collection collection1;
0126     collection1.setName(QStringLiteral("collection1"));
0127     collection1.setRemoteId(QStringLiteral("collection1"));
0128     collection1.setParentCollection(mStore->topLevelCollection());
0129 
0130     QString remoteId1;
0131     do {
0132         remoteId1 = KRandom::randomString(20);
0133     } while (entrySet1.contains(remoteId1));
0134 
0135     KMime::Message::Ptr msgPtr(new KMime::Message);
0136 
0137     Item item1;
0138     item1.setMimeType(KMime::Message::mimeType());
0139     item1.setRemoteId(remoteId1);
0140     item1.setParentCollection(collection1);
0141     item1.setPayload<KMime::Message::Ptr>(msgPtr);
0142 
0143     job = mStore->modifyItem(item1);
0144 
0145     QVERIFY(!job->exec());
0146     QCOMPARE(job->error(), (int)FileStore::Job::InvalidJobContext);
0147 
0148     md1EntryList = md1.entryList();
0149     QSet<QString> entrySet(md1EntryList.cbegin(), md1EntryList.cend());
0150     QCOMPARE(entrySet, entrySet1);
0151 
0152     // test failure of modifying a non-existent mbox entry
0153     Collection collection2;
0154     collection2.setName(QStringLiteral("collection2"));
0155     collection2.setRemoteId(QStringLiteral("collection2"));
0156     collection2.setParentCollection(mStore->topLevelCollection());
0157 
0158     qint64 remoteId2;
0159     do {
0160         remoteId2 = QRandomGenerator::global()->generate();
0161     } while (entrySet2.contains(remoteId2));
0162 
0163     Item item2;
0164     item2.setMimeType(KMime::Message::mimeType());
0165     item2.setRemoteId(QString::number(remoteId2));
0166     item2.setParentCollection(collection2);
0167     item2.setPayload<KMime::Message::Ptr>(msgPtr);
0168 
0169     job = mStore->modifyItem(item2);
0170 
0171     QVERIFY(!job->exec());
0172     QCOMPARE(job->error(), (int)FileStore::Job::InvalidJobContext);
0173 
0174     QVERIFY(mbox2.load(mbox2.fileName()));
0175     MBoxEntry::List entryList = mbox2.entries();
0176     QVERIFY(std::equal(entryList.begin(), entryList.end(), entryList2.begin(), fullEntryCompare));
0177 }
0178 
0179 void ItemModifyTest::testIgnorePayload()
0180 {
0181     QDir topDir(mDir->path());
0182 
0183     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("collection1")));
0184     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QStringLiteral("collection2")));
0185 
0186     KPIM::Maildir topLevelMd(topDir.path(), true);
0187 
0188     KPIM::Maildir md1 = topLevelMd.subFolder(QStringLiteral("collection1"));
0189     QStringList entryList1 = md1.entryList();
0190     QCOMPARE((int)entryList1.count(), 4);
0191 
0192     QFileInfo fileInfo2(topDir.path(), QStringLiteral("collection2"));
0193     MBox mbox2;
0194     QVERIFY(mbox2.load(fileInfo2.absoluteFilePath()));
0195     MBoxEntry::List entryList2 = mbox2.entries();
0196     QCOMPARE((int)entryList2.count(), 4);
0197 
0198     mStore->setPath(topDir.path());
0199 
0200     // common variables
0201     FileStore::ItemModifyJob *job = nullptr;
0202 
0203     // test failure of modifying a non-existent maildir entry
0204     Collection collection1;
0205     collection1.setName(QStringLiteral("collection1"));
0206     collection1.setRemoteId(QStringLiteral("collection1"));
0207     collection1.setParentCollection(mStore->topLevelCollection());
0208 
0209     const QByteArray data1 = md1.readEntry(entryList1.first());
0210 
0211     KMime::Message::Ptr msgPtr(new KMime::Message);
0212     msgPtr->subject()->from7BitString("Modify Test");
0213     msgPtr->assemble();
0214 
0215     Item item1;
0216     item1.setMimeType(KMime::Message::mimeType());
0217     item1.setRemoteId(entryList1.first());
0218     item1.setParentCollection(collection1);
0219     item1.setPayload<KMime::Message::Ptr>(msgPtr);
0220 
0221     job = mStore->modifyItem(item1);
0222     job->setIgnorePayload(true);
0223 
0224     QVERIFY(job->exec());
0225     QCOMPARE(job->error(), 0);
0226 
0227     QCOMPARE(md1.entryList(), entryList1);
0228     QCOMPARE(md1.readEntry(entryList1.first()), data1);
0229 
0230     // test failure of modifying a non-existent mbox entry
0231     Collection collection2;
0232     collection2.setName(QStringLiteral("collection2"));
0233     collection2.setRemoteId(QStringLiteral("collection2"));
0234     collection2.setParentCollection(mStore->topLevelCollection());
0235 
0236     const QByteArray data2 = mbox2.readRawMessage(MBoxEntry(0));
0237 
0238     Item item2;
0239     item2.setMimeType(KMime::Message::mimeType());
0240     item2.setRemoteId(QStringLiteral("0"));
0241     item2.setParentCollection(collection2);
0242     item2.setPayload<KMime::Message::Ptr>(msgPtr);
0243 
0244     job = mStore->modifyItem(item2);
0245     job->setIgnorePayload(true);
0246 
0247     QVERIFY(job->exec());
0248     QCOMPARE(job->error(), 0);
0249 
0250     QVERIFY(mbox2.load(mbox2.fileName()));
0251     QCOMPARE(mbox2.entries(), entryList2);
0252     QCOMPARE(mbox2.readRawMessage(MBoxEntry(0)), data2);
0253 }
0254 
0255 void ItemModifyTest::testModifyPayload()
0256 {
0257     QDir topDir(mDir->path());
0258 
0259     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("collection1")));
0260     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QStringLiteral("collection2")));
0261 
0262     KPIM::Maildir topLevelMd(topDir.path(), true);
0263 
0264     KPIM::Maildir md1 = topLevelMd.subFolder(QStringLiteral("collection1"));
0265     QStringList entryList1 = md1.entryList();
0266     QCOMPARE((int)entryList1.count(), 4);
0267 
0268     QFileInfo fileInfo2(topDir.path(), QStringLiteral("collection2"));
0269     MBox mbox2;
0270     QVERIFY(mbox2.load(fileInfo2.absoluteFilePath()));
0271     MBoxEntry::List entryList2 = mbox2.entries();
0272     QCOMPARE((int)entryList2.count(), 4);
0273 
0274     mStore->setPath(topDir.path());
0275 
0276     // common variables
0277     FileStore::ItemModifyJob *job = nullptr;
0278 
0279     const QVariant colListVar = QVariant::fromValue<Collection::List>(Collection::List());
0280     QVariant var;
0281     Collection::List collections;
0282 
0283     // test modifying a maildir entry's header
0284     Collection collection1;
0285     collection1.setName(QStringLiteral("collection1"));
0286     collection1.setRemoteId(QStringLiteral("collection1"));
0287     collection1.setParentCollection(mStore->topLevelCollection());
0288 
0289     const QByteArray data1 = md1.readEntry(entryList1.first());
0290 
0291     KMime::Message::Ptr msgPtr(new KMime::Message);
0292     msgPtr->setContent(KMime::CRLFtoLF(data1));
0293     msgPtr->subject()->from7BitString("Modify Test");
0294     msgPtr->assemble();
0295 
0296     Item item1;
0297     item1.setMimeType(KMime::Message::mimeType());
0298     item1.setRemoteId(entryList1.first());
0299     item1.setParentCollection(collection1);
0300     item1.setPayload<KMime::Message::Ptr>(msgPtr);
0301 
0302     job = mStore->modifyItem(item1);
0303     // changing subject, so indicate a header change
0304     job->setParts(QSet<QByteArray>() << QByteArray("PLD:") + QByteArray(MessagePart::Header));
0305 
0306     QVERIFY(job->exec());
0307     QCOMPARE(job->error(), 0);
0308 
0309     QCOMPARE(md1.entryList(), entryList1);
0310 
0311     QCOMPARE(md1.readEntry(entryList1.first()).size(), msgPtr->encodedContent().size());
0312     QCOMPARE(md1.readEntry(entryList1.first()), msgPtr->encodedContent());
0313 
0314     // check for index preservation
0315     var = job->property("onDiskIndexInvalidated");
0316     QVERIFY(var.isValid());
0317     QCOMPARE(var.userType(), colListVar.userType());
0318 
0319     collections = var.value<Collection::List>();
0320     QCOMPARE((int)collections.count(), 1);
0321     QCOMPARE(collections.first(), collection1);
0322 
0323     // test modifying an mbox entry's header
0324     Collection collection2;
0325     collection2.setName(QStringLiteral("collection2"));
0326     collection2.setRemoteId(QStringLiteral("collection2"));
0327     collection2.setParentCollection(mStore->topLevelCollection());
0328 
0329     const QByteArray data2 = mbox2.readRawMessage(MBoxEntry(0));
0330 
0331     msgPtr->setContent(KMime::CRLFtoLF(data2));
0332     msgPtr->subject()->from7BitString("Modify Test");
0333     msgPtr->assemble();
0334 
0335     Item item2;
0336     item2.setMimeType(KMime::Message::mimeType());
0337     item2.setRemoteId(QStringLiteral("0"));
0338     item2.setParentCollection(collection2);
0339     item2.setPayload<KMime::Message::Ptr>(msgPtr);
0340 
0341     job = mStore->modifyItem(item2);
0342     // changing subject, so indicate a header change
0343     job->setParts(QSet<QByteArray>() << QByteArray("PLD:") + QByteArray(MessagePart::Header));
0344 
0345     QVERIFY(job->exec());
0346     QCOMPARE(job->error(), 0);
0347 
0348     Item item = job->item();
0349 
0350     QVERIFY(mbox2.load(mbox2.fileName()));
0351     MBoxEntry::List entryList = mbox2.entries();
0352     QCOMPARE((int)entryList.count(), 5); // mbox file not purged yet
0353     QCOMPARE(entryList.last().messageOffset(), item.remoteId().toULongLong());
0354 
0355     var = job->property("compactStore");
0356     QVERIFY(var.isValid());
0357     QCOMPARE(var.userType(), QMetaType::Bool);
0358     QCOMPARE(var.toBool(), true);
0359 
0360     // check for index preservation
0361     var = job->property("onDiskIndexInvalidated");
0362     QVERIFY(var.isValid());
0363     QCOMPARE(var.userType(), colListVar.userType());
0364 
0365     collections = var.value<Collection::List>();
0366     QCOMPARE((int)collections.count(), 1);
0367     QCOMPARE(collections.first(), collection2);
0368 
0369     FileStore::ItemFetchJob *itemFetch = mStore->fetchItem(item2);
0370     QVERIFY(!itemFetch->exec()); // item at old offset gone
0371 
0372     FileStore::StoreCompactJob *storeCompact = mStore->compactStore();
0373     QVERIFY(storeCompact->exec());
0374 
0375     QVERIFY(mbox2.load(mbox2.fileName()));
0376     entryList = mbox2.entries();
0377     QCOMPARE((int)entryList.count(), 4);
0378 
0379     QCOMPARE(mbox2.readRawMessage(entryList.last()), msgPtr->encodedContent());
0380 }
0381 
0382 void ItemModifyTest::testModifyFlags()
0383 {
0384     QDir topDir(mDir->path());
0385 
0386     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("collection1")));
0387     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QStringLiteral("collection2")));
0388 
0389     KPIM::Maildir topLevelMd(topDir.path(), true);
0390 
0391     KPIM::Maildir md1 = topLevelMd.subFolder(QStringLiteral("collection1"));
0392     const QStringList entryList1 = md1.entryList();
0393     QCOMPARE((int)entryList1.count(), 4);
0394 
0395     QFileInfo fileInfo2(topDir.path(), QStringLiteral("collection2"));
0396     MBox mbox2;
0397     QVERIFY(mbox2.load(fileInfo2.absoluteFilePath()));
0398     MBoxEntry::List entryList2 = mbox2.entries();
0399     QCOMPARE((int)entryList2.count(), 4);
0400 
0401     QCryptographicHash cryptoHash(QCryptographicHash::Sha1);
0402 
0403     QFile file2(fileInfo2.absoluteFilePath());
0404     QVERIFY(file2.open(QIODevice::ReadOnly));
0405     cryptoHash.addData(file2.readAll());
0406     const QByteArray mbox2Sha1 = cryptoHash.result();
0407 
0408     file2.close();
0409     cryptoHash.reset();
0410 
0411     mStore->setPath(topDir.path());
0412 
0413     // common variables
0414     FileStore::ItemModifyJob *job = nullptr;
0415 
0416     const QVariant colListVar = QVariant::fromValue<Collection::List>(Collection::List());
0417     QVariant var;
0418     Collection::List collections;
0419     KMime::Message::Ptr msgPtr(new KMime::Message);
0420 
0421     // test modifying a flag of a maildir items changes the entry name but not the
0422     // message contents
0423     Collection collection1;
0424     collection1.setName(QStringLiteral("collection1"));
0425     collection1.setRemoteId(QStringLiteral("collection1"));
0426     collection1.setParentCollection(mStore->topLevelCollection());
0427 
0428     // check that the \SEEN flag is not set yet
0429     QVERIFY(!md1.readEntryFlags(entryList1.first()).contains("\\SEEN"));
0430 
0431     const QByteArray data1 = md1.readEntry(entryList1.first());
0432 
0433     msgPtr->setContent(KMime::CRLFtoLF(data1));
0434     msgPtr->subject()->from7BitString("Modify Test");
0435     msgPtr->assemble();
0436 
0437     Item item1;
0438     item1.setMimeType(KMime::Message::mimeType());
0439     item1.setRemoteId(entryList1.first());
0440     item1.setParentCollection(collection1);
0441     item1.setPayload<KMime::Message::Ptr>(msgPtr);
0442     item1.setFlag("\\SEEN");
0443 
0444     job = mStore->modifyItem(item1);
0445     // setting \SEEN, so indicate a flags change
0446     job->setParts(QSet<QByteArray>() << "FLAGS");
0447 
0448     QVERIFY(job->exec());
0449     QCOMPARE(job->error(), 0);
0450 
0451     Item item = job->item();
0452 
0453     // returned item should contain the change
0454     QVERIFY(item.flags().contains("\\SEEN"));
0455 
0456     // remote ID has changed
0457     QVERIFY(item.remoteId() != entryList1.first());
0458     QVERIFY(!md1.entryList().contains(entryList1.first()));
0459 
0460     // no change in number of entries, one difference
0461     QStringList entryList3 = md1.entryList();
0462     QCOMPARE(entryList3.count(), entryList1.count());
0463     for (const QString &oldEntry : entryList1) {
0464         entryList3.removeAll(oldEntry);
0465     }
0466     QCOMPARE(entryList3.count(), 1);
0467 
0468     // no change to data
0469     QCOMPARE(md1.readEntry(entryList3.first()), data1);
0470 
0471     var = job->property("onDiskIndexInvalidated");
0472     QVERIFY(var.isValid());
0473     QCOMPARE(var.userType(), colListVar.userType());
0474 
0475     collections = var.value<Collection::List>();
0476     QCOMPARE((int)collections.count(), 1);
0477     QCOMPARE(collections.first(), collection1);
0478 
0479     // fetch new item, check flag
0480     item1 = Item();
0481     item1.setMimeType(KMime::Message::mimeType());
0482     item1.setRemoteId(entryList3.first());
0483     item1.setParentCollection(collection1);
0484 
0485     FileStore::ItemFetchJob *itemFetch = mStore->fetchItem(item1);
0486 
0487     QVERIFY(itemFetch->exec());
0488     QCOMPARE(itemFetch->error(), 0);
0489 
0490     QCOMPARE(itemFetch->items().count(), 1);
0491     QEXPECT_FAIL("", "ItemFetch handling needs to be fixed to also fetch flags", Continue);
0492     QVERIFY(itemFetch->items()[0].flags().contains("\\SEEN"));
0493 
0494     // test modifying flags of an mbox item "succeeds" (no error) but does not change
0495     // anything in store or on disk
0496     Collection collection2;
0497     collection2.setName(QStringLiteral("collection2"));
0498     collection2.setRemoteId(QStringLiteral("collection2"));
0499     collection2.setParentCollection(mStore->topLevelCollection());
0500 
0501     const QByteArray data2 = mbox2.readRawMessage(MBoxEntry(0));
0502 
0503     msgPtr->setContent(KMime::CRLFtoLF(data2));
0504     msgPtr->subject()->from7BitString("Modify Test");
0505     msgPtr->assemble();
0506 
0507     Item item2;
0508     item2.setMimeType(KMime::Message::mimeType());
0509     item2.setRemoteId(QStringLiteral("0"));
0510     item2.setParentCollection(collection2);
0511     item2.setPayload<KMime::Message::Ptr>(msgPtr);
0512     item2.setFlag("\\SEEN");
0513 
0514     job = mStore->modifyItem(item2);
0515     // setting \SEEN, so indicate a flags change
0516     job->setParts(QSet<QByteArray>() << "FLAGS");
0517 
0518     QVERIFY(job->exec());
0519     QCOMPARE(job->error(), 0);
0520 
0521     item = job->item();
0522 
0523     // returned item should contain the change
0524     QVERIFY(item.flags().contains("\\SEEN"));
0525 
0526     // mbox not changed
0527     QVERIFY(mbox2.load(mbox2.fileName()));
0528     MBoxEntry::List entryList = mbox2.entries();
0529     QCOMPARE((int)entryList.count(), 4);
0530 
0531     var = job->property("compactStore");
0532     QVERIFY(!var.isValid());
0533 
0534     // file not modified
0535     QVERIFY(file2.open(QIODevice::ReadOnly));
0536     cryptoHash.addData(file2.readAll());
0537     QCOMPARE(cryptoHash.result(), mbox2Sha1);
0538 
0539     file2.close();
0540     cryptoHash.reset();
0541 
0542     // check index preservation is not triggered
0543     var = job->property("onDiskIndexInvalidated");
0544     QVERIFY(!var.isValid());
0545 }
0546 
0547 void ItemModifyTest::testModifyFlagsAndPayload()
0548 {
0549     QDir topDir(mDir->path());
0550 
0551     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("collection1")));
0552     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QStringLiteral("collection2")));
0553 
0554     KPIM::Maildir topLevelMd(topDir.path(), true);
0555 
0556     KPIM::Maildir md1 = topLevelMd.subFolder(QStringLiteral("collection1"));
0557     const QStringList entryList1 = md1.entryList();
0558     QCOMPARE((int)entryList1.count(), 4);
0559 
0560     QFileInfo fileInfo2(topDir.path(), QStringLiteral("collection2"));
0561     MBox mbox2;
0562     QVERIFY(mbox2.load(fileInfo2.absoluteFilePath()));
0563     MBoxEntry::List entryList2 = mbox2.entries();
0564     QCOMPARE((int)entryList2.count(), 4);
0565 
0566     mStore->setPath(topDir.path());
0567 
0568     // common variables
0569     FileStore::ItemModifyJob *job = nullptr;
0570 
0571     const QVariant colListVar = QVariant::fromValue<Collection::List>(Collection::List());
0572     QVariant var;
0573     Collection::List collections;
0574     KMime::Message::Ptr msgPtr(new KMime::Message);
0575 
0576     // test modifying a flag of a maildir items changes the entry name but not the
0577     // message contents
0578     Collection collection1;
0579     collection1.setName(QStringLiteral("collection1"));
0580     collection1.setRemoteId(QStringLiteral("collection1"));
0581     collection1.setParentCollection(mStore->topLevelCollection());
0582 
0583     // check that the \SEEN flag is not set yet
0584     QVERIFY(!md1.readEntryFlags(entryList1.first()).contains("\\SEEN"));
0585 
0586     const QByteArray data1 = md1.readEntry(entryList1.first());
0587 
0588     msgPtr->setContent(KMime::CRLFtoLF(data1));
0589     msgPtr->subject()->from7BitString("Modify Test");
0590     msgPtr->assemble();
0591 
0592     Item item1;
0593     item1.setMimeType(KMime::Message::mimeType());
0594     item1.setRemoteId(entryList1.first());
0595     item1.setParentCollection(collection1);
0596     item1.setPayload<KMime::Message::Ptr>(msgPtr);
0597     item1.setFlag("\\SEEN");
0598 
0599     job = mStore->modifyItem(item1);
0600     // setting \SEEN so indicate a flags change and
0601     // setting new subject so indicate a payload change
0602     job->setParts(QSet<QByteArray>() << "FLAGS" << QByteArray("PLD:") + QByteArray(MessagePart::Header));
0603 
0604     QVERIFY(job->exec());
0605     QCOMPARE(job->error(), 0);
0606 
0607     Item item = job->item();
0608 
0609     // returned item should contain the change
0610     QVERIFY(item.flags().contains("\\SEEN"));
0611 
0612     // remote ID has changed
0613     QVERIFY(item.remoteId() != entryList1.first());
0614     QVERIFY(!md1.entryList().contains(entryList1.first()));
0615 
0616     // no change in number of entries, one difference
0617     QStringList entryList3 = md1.entryList();
0618     QCOMPARE(entryList3.count(), entryList1.count());
0619     for (const QString &oldEntry : entryList1) {
0620         entryList3.removeAll(oldEntry);
0621     }
0622     QCOMPARE(entryList3.count(), 1);
0623 
0624     // data changed
0625     QCOMPARE(md1.readEntry(entryList3.first()), msgPtr->encodedContent());
0626 
0627     var = job->property("onDiskIndexInvalidated");
0628     QVERIFY(var.isValid());
0629     QCOMPARE(var.userType(), colListVar.userType());
0630 
0631     collections = var.value<Collection::List>();
0632     QCOMPARE((int)collections.count(), 1);
0633     QCOMPARE(collections.first(), collection1);
0634 
0635     // fetch new item, check flag
0636     item1 = Item();
0637     item1.setMimeType(KMime::Message::mimeType());
0638     item1.setRemoteId(entryList3.first());
0639     item1.setParentCollection(collection1);
0640 
0641     FileStore::ItemFetchJob *itemFetch = mStore->fetchItem(item1);
0642     itemFetch->fetchScope().fetchFullPayload();
0643 
0644     QVERIFY(itemFetch->exec());
0645     QCOMPARE(itemFetch->error(), 0);
0646 
0647     QCOMPARE(itemFetch->items().count(), 1);
0648     Item fetchedItem = itemFetch->items().first();
0649     QEXPECT_FAIL("", "ItemFetch handling needs to be fixed to also fetch flags", Continue);
0650     QVERIFY(fetchedItem.flags().contains("\\SEEN"));
0651 
0652     QVERIFY(fetchedItem.hasPayload<KMime::Message::Ptr>());
0653     auto fetchedMsgPtr = fetchedItem.payload<KMime::Message::Ptr>();
0654     QCOMPARE(msgPtr->encodedContent(), fetchedMsgPtr->encodedContent());
0655 
0656     // TODO test for mbox.
0657 }
0658 
0659 QTEST_MAIN(ItemModifyTest)
0660 
0661 #include "itemmodifytest.moc"