File indexing completed on 2024-11-10 04:40:20

0001 /*
0002     SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <QObject>
0008 
0009 #include "entities.h"
0010 #include "fakeakonadiserver.h"
0011 #include "shared/aktest.h"
0012 
0013 #include "private/imapset_p.h"
0014 #include "private/scope_p.h"
0015 
0016 #include <QTest>
0017 
0018 using namespace Akonadi;
0019 using namespace Akonadi::Server;
0020 
0021 class ItemLinkHandlerTest : public QObject
0022 {
0023     Q_OBJECT
0024 
0025     FakeAkonadiServer mAkonadi;
0026 
0027 public:
0028     ItemLinkHandlerTest()
0029     {
0030         qRegisterMetaType<Akonadi::Protocol::ChangeNotificationList>();
0031 
0032         mAkonadi.init();
0033     }
0034 
0035     Protocol::LinkItemsResponsePtr createError(const QString &error)
0036     {
0037         auto resp = Protocol::LinkItemsResponsePtr::create();
0038         resp->setError(1, error);
0039         return resp;
0040     }
0041 
0042     Protocol::FetchItemsResponse itemResponse(qint64 id, const QString &rid, const QString &rrev, const QString &mimeType)
0043     {
0044         Protocol::FetchItemsResponse item;
0045         item.setId(id);
0046         item.setRemoteId(rid);
0047         item.setRemoteRevision(rrev);
0048         item.setMimeType(mimeType);
0049         return item;
0050     }
0051 
0052 private Q_SLOTS:
0053     void testLink_data()
0054     {
0055         QTest::addColumn<TestScenario::List>("scenarios");
0056         QTest::addColumn<Akonadi::Protocol::ItemChangeNotificationPtr>("notification");
0057         QTest::addColumn<bool>("expectFail");
0058 
0059         TestScenario::List scenarios;
0060 
0061         scenarios << FakeAkonadiServer::loginScenario()
0062                   << TestScenario::create(5,
0063                                           TestScenario::ClientCmd,
0064                                           Protocol::LinkItemsCommandPtr::create(Protocol::LinkItemsCommand::Link, ImapInterval(1, 3), 3))
0065                   << TestScenario::create(5, TestScenario::ServerCmd, createError(QStringLiteral("Can't link items to non-virtual collections")));
0066         QTest::newRow("non-virtual collection") << scenarios << Protocol::ItemChangeNotificationPtr::create() << true;
0067 
0068         auto notification = Protocol::ItemChangeNotificationPtr::create();
0069         notification->setOperation(Protocol::ItemChangeNotification::Link);
0070         notification->setItems({itemResponse(1, QLatin1StringView("A"), QString(), QLatin1StringView("application/octet-stream")),
0071                                 itemResponse(2, QLatin1StringView("B"), QString(), QLatin1StringView("application/octet-stream")),
0072                                 itemResponse(3, QLatin1StringView("C"), QString(), QLatin1StringView("application/octet-stream"))});
0073         notification->setParentCollection(6);
0074         notification->setResource("akonadi_fake_resource_with_virtual_collections_0");
0075         notification->setSessionId(FakeAkonadiServer::instanceName().toLatin1());
0076 
0077         scenarios.clear();
0078         scenarios << FakeAkonadiServer::loginScenario()
0079                   << TestScenario::create(5,
0080                                           TestScenario::ClientCmd,
0081                                           Protocol::LinkItemsCommandPtr::create(Protocol::LinkItemsCommand::Link, ImapInterval(1, 3), 6))
0082                   << TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponsePtr::create());
0083         QTest::newRow("normal") << scenarios << notification << false;
0084 
0085         notification = Protocol::ItemChangeNotificationPtr::create(*notification);
0086         notification->setItems({itemResponse(4, QLatin1StringView("D"), QString(), QLatin1StringView("application/octet-stream"))});
0087         scenarios.clear();
0088         scenarios << FakeAkonadiServer::loginScenario()
0089                   << TestScenario::create(5,
0090                                           TestScenario::ClientCmd,
0091                                           Protocol::LinkItemsCommandPtr::create(Protocol::LinkItemsCommand::Link, QList<qint64>{4, 123456}, 6))
0092                   << TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponsePtr::create());
0093         QTest::newRow("existent and non-existent item") << scenarios << notification << false;
0094 
0095         scenarios.clear();
0096         scenarios << FakeAkonadiServer::loginScenario()
0097                   << TestScenario::create(5, TestScenario::ClientCmd, Protocol::LinkItemsCommandPtr::create(Protocol::LinkItemsCommand::Link, 4, 6))
0098                   << TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponsePtr::create());
0099         QTest::newRow("non-existent item only") << scenarios << Protocol::ItemChangeNotificationPtr::create() << false;
0100 
0101         // FIXME: All RID related operations are currently broken because we reset the collection context before every command,
0102         // and LINK still relies on SELECT to set the collection context.
0103 
0104         // scenario.clear();
0105         // scenario << FakeAkonadiServer::defaultScenario()
0106         //          << FakeAkonadiServer::selectCollectionScenario(QLatin1StringView("Collection B"))
0107         //          << "C: 3 UID LINK 6 RID (\"F\" \"G\")\n"
0108         //          << "S: 3 OK LINK complete";
0109         // notification.clearEntities();
0110         // notification.clearEntities();
0111         // notification.addEntity(6, QLatin1StringView("F"), QString(), QLatin1StringView("application/octet-stream"));
0112         // notification.addEntity(7, QLatin1StringView("G"), QString(), QLatin1StringView("application/octet-stream"));
0113         // QTest::newRow("RID items") << scenario << notification << false;
0114 
0115         // scenario.clear();
0116         // scenario << FakeAkonadiServer::defaultScenario()
0117         //          << FakeAkonadiServer::selectResourceScenario(QLatin1StringView("akonadi_fake_resource_with_virtual_collections_0"))
0118         //          << "C: 4 HRID LINK ((-1, \"virtual2\") (-1, \"virtual\") (-1, \"\")) UID 5"
0119         //          << "S: 4 OK LINK complete";
0120         // notification.setParentCollection(7);
0121         // notification.clearEntities();
0122         // notification.addEntity(5, QLatin1StringView("E"), QString(), QLatin1StringView("application/octet-stream"));
0123         // QTest::newRow("HRID collection") << scenario << notification << false;
0124 
0125         // scenario.clear();
0126         // scenario << FakeAkonadiServer::defaultScenario()
0127         //          << FakeAkonadiServer::selectResourceScenario(QLatin1StringView("akonadi_fake_resource_with_virtual_collections_0"))
0128         //          << FakeAkonadiServer::selectCollectionScenario(QLatin1StringView("Collection B"))
0129         //          << "C: 4 HRID LINK ((-1, \"virtual2\") (-1, \"virtual\") (-1, \"\")) RID \"H\""
0130         //          << "S: 4 OK LINK complete";
0131         // notification.clearEntities();
0132         // notification.addEntity(8, QLatin1StringView("H"), QString(), QLatin1StringView("application/octet-stream"));
0133         // QTest::newRow("HRID collection, RID items") << scenario << notification << false;
0134     }
0135 
0136     void testLink()
0137     {
0138         QFETCH(TestScenario::List, scenarios);
0139         QFETCH(Protocol::ItemChangeNotificationPtr, notification);
0140         QFETCH(bool, expectFail);
0141 
0142         mAkonadi.setScenarios(scenarios);
0143         mAkonadi.runTest();
0144 
0145         auto notificationSpy = mAkonadi.notificationSpy();
0146         if (notification->operation() != Protocol::ItemChangeNotification::InvalidOp) {
0147             QTRY_COMPARE(notificationSpy->count(), 1);
0148             const Protocol::ChangeNotificationList notifications = notificationSpy->takeFirst().first().value<Protocol::ChangeNotificationList>();
0149             QCOMPARE(notifications.count(), 1);
0150             QCOMPARE(*notifications.first().staticCast<Protocol::ItemChangeNotification>(), *notification);
0151         } else {
0152             QVERIFY(notificationSpy->isEmpty() || notificationSpy->takeFirst().first().value<Protocol::ChangeNotificationList>().isEmpty());
0153         }
0154 
0155         const auto entities = notification->items();
0156         for (const auto &entity : entities) {
0157             if (expectFail) {
0158                 QVERIFY(!Collection::relatesToPimItem(notification->parentCollection(), entity.id()));
0159             } else {
0160                 QVERIFY(Collection::relatesToPimItem(notification->parentCollection(), entity.id()));
0161             }
0162         }
0163     }
0164 
0165     void testUnlink_data()
0166     {
0167         QTest::addColumn<TestScenario::List>("scenarios");
0168         QTest::addColumn<Akonadi::Protocol::ItemChangeNotificationPtr>("notification");
0169         QTest::addColumn<bool>("expectFail");
0170 
0171         TestScenario::List scenarios;
0172 
0173         scenarios << FakeAkonadiServer::loginScenario()
0174                   << TestScenario::create(5,
0175                                           TestScenario::ClientCmd,
0176                                           Protocol::LinkItemsCommandPtr::create(Protocol::LinkItemsCommand::Unlink, ImapInterval(1, 3), 3))
0177                   << TestScenario::create(5, TestScenario::ServerCmd, createError(QStringLiteral("Can't link items to non-virtual collections")));
0178         QTest::newRow("non-virtual collection") << scenarios << Protocol::ItemChangeNotificationPtr::create() << true;
0179 
0180         auto notification = Protocol::ItemChangeNotificationPtr::create();
0181         notification->setOperation(Protocol::ItemChangeNotification::Unlink);
0182         notification->setItems({itemResponse(1, QLatin1StringView("A"), QString(), QLatin1StringView("application/octet-stream")),
0183                                 itemResponse(2, QLatin1StringView("B"), QString(), QLatin1StringView("application/octet-stream")),
0184                                 itemResponse(3, QLatin1StringView("C"), QString(), QLatin1StringView("application/octet-stream"))});
0185         notification->setParentCollection(6);
0186         notification->setResource("akonadi_fake_resource_with_virtual_collections_0");
0187         notification->setSessionId(FakeAkonadiServer::instanceName().toLatin1());
0188         scenarios.clear();
0189         scenarios << FakeAkonadiServer::loginScenario()
0190                   << TestScenario::create(5,
0191                                           TestScenario::ClientCmd,
0192                                           Protocol::LinkItemsCommandPtr::create(Protocol::LinkItemsCommand::Unlink, ImapInterval(1, 3), 6))
0193                   << TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponsePtr::create());
0194         QTest::newRow("normal") << scenarios << notification << false;
0195 
0196         notification = Protocol::ItemChangeNotificationPtr::create(*notification);
0197         notification->setItems({itemResponse(4, QLatin1StringView("D"), QString(), QLatin1StringView("application/octet-stream"))});
0198         scenarios.clear();
0199         scenarios << FakeAkonadiServer::loginScenario()
0200                   << TestScenario::create(5,
0201                                           TestScenario::ClientCmd,
0202                                           Protocol::LinkItemsCommandPtr::create(Protocol::LinkItemsCommand::Unlink, QList<qint64>{4, 2048}, 6))
0203                   << TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponsePtr::create());
0204         QTest::newRow("existent and non-existent item") << scenarios << notification << false;
0205 
0206         scenarios.clear();
0207         scenarios << FakeAkonadiServer::loginScenario()
0208                   << TestScenario::create(5, TestScenario::ClientCmd, Protocol::LinkItemsCommandPtr::create(Protocol::LinkItemsCommand::Unlink, 4096, 6))
0209                   << TestScenario::create(5, TestScenario::ServerCmd, Protocol::LinkItemsResponsePtr::create());
0210         QTest::newRow("non-existent item only") << scenarios << Protocol::ItemChangeNotificationPtr::create() << false;
0211 
0212         // FIXME: All RID related operations are currently broken because we reset the collection context before every command,
0213         // and LINK still relies on SELECT to set the collection context.
0214 
0215         // scenario.clear();
0216         // scenario << FakeAkonadiServer::defaultScenario()
0217         //          << FakeAkonadiServer::selectCollectionScenario(QLatin1StringView("Collection B"))
0218         //          << "C: 4 UID UNLINK 6 RID (\"F\" \"G\")"
0219         //          << "S: 4 OK LINK complete";
0220         // notification.clearEntities();
0221         // notification.clearEntities();
0222         // notification.addEntity(6, QLatin1StringView("F"), QString(), QLatin1StringView("application/octet-stream"));
0223         // notification.addEntity(7, QLatin1StringView("G"), QString(), QLatin1StringView("application/octet-stream"));
0224         // QTest::newRow("RID items") << scenario << notification << false;
0225 
0226         // scenario.clear();
0227         // scenario << FakeAkonadiServer::defaultScenario()
0228         //          << FakeAkonadiServer::selectResourceScenario(QLatin1StringView("akonadi_fake_resource_with_virtual_collections_0"))
0229         //          << "C: 4 HRID UNLINK ((-1, \"virtual2\") (-1, \"virtual\") (-1, \"\")) UID 5"
0230         //          << "S: 4 OK LINK complete";
0231         // notification.setParentCollection(7);
0232         // notification.clearEntities();
0233         // notification.addEntity(5, QLatin1StringView("E"), QString(), QLatin1StringView("application/octet-stream"));
0234         // QTest::newRow("HRID collection") << scenario << notification << false;
0235 
0236         // scenario.clear();
0237         // scenario << FakeAkonadiServer::defaultScenario()
0238         //          << FakeAkonadiServer::selectCollectionScenario(QLatin1StringView("Collection B"))
0239         //          << FakeAkonadiServer::selectResourceScenario(QLatin1StringView("akonadi_fake_resource_with_virtual_collections_0"))
0240         //          << "C: 4 HRID UNLINK ((-1, \"virtual2\") (-1, \"virtual\") (-1, \"\")) RID \"H\""
0241         //          << "S: 4 OK LINK complete";
0242         // notification.clearEntities();
0243         // notification.addEntity(8, QLatin1StringView("H"), QString(), QLatin1StringView("application/octet-stream"));
0244         // QTest::newRow("HRID collection, RID items") << scenario << notification << false;
0245     }
0246 
0247     void testUnlink()
0248     {
0249         QFETCH(TestScenario::List, scenarios);
0250         QFETCH(Protocol::ItemChangeNotificationPtr, notification);
0251         QFETCH(bool, expectFail);
0252 
0253         mAkonadi.setScenarios(scenarios);
0254         mAkonadi.runTest();
0255 
0256         auto notificationSpy = mAkonadi.notificationSpy();
0257         if (notification->operation() != Protocol::ItemChangeNotification::InvalidOp) {
0258             QTRY_COMPARE(notificationSpy->count(), 1);
0259             const auto notifications = notificationSpy->takeFirst().first().value<Protocol::ChangeNotificationList>();
0260             QCOMPARE(notifications.count(), 1);
0261             QCOMPARE(*notifications.first().staticCast<Protocol::ItemChangeNotification>(), *notification);
0262         } else {
0263             QVERIFY(notificationSpy->isEmpty() || notificationSpy->takeFirst().first().value<Protocol::ChangeNotificationList>().isEmpty());
0264         }
0265 
0266         const auto entities = notification->items();
0267         for (const auto &entity : entities) {
0268             if (expectFail) {
0269                 QVERIFY(Collection::relatesToPimItem(notification->parentCollection(), entity.id()));
0270             } else {
0271                 QVERIFY(!Collection::relatesToPimItem(notification->parentCollection(), entity.id()));
0272             }
0273         }
0274     }
0275 };
0276 
0277 AKTEST_FAKESERVER_MAIN(ItemLinkHandlerTest)
0278 
0279 #include "itemlinkhandlertest.moc"