// SPDX-FileCopyrightText: 2020 Linus Jahn // SPDX-FileCopyrightText: 2020 Germán Márquez Mejía // SPDX-FileCopyrightText: 2022 Melvin Keskin // // SPDX-License-Identifier: LGPL-2.1-or-later #include "QXmppClient.h" #include "QXmppMessage.h" #include "QXmppPubSubAffiliation.h" #include "QXmppPubSubBaseItem.h" #include "QXmppPubSubEventHandler.h" #include "QXmppPubSubManager.h" #include "QXmppPubSubPublishOptions.h" #include "QXmppPubSubSubscribeOptions.h" #include "QXmppUserTuneItem.h" #include "TestClient.h" #include "util.h" #include Q_DECLARE_METATYPE(QXmpp::Private::PubSubIq<>); Q_DECLARE_METATYPE(std::optional); using PSManager = QXmppPubSubManager; using Affiliation = QXmppPubSubAffiliation; using AffiliationType = QXmppPubSubAffiliation::Affiliation; const char *ns_pubsub = "http://jabber.org/protocol/pubsub"; const char *ns_pubsub_auto_create = "http://jabber.org/protocol/pubsub#auto-create"; class TestEventManager : public QXmppClientExtension, public QXmppPubSubEventHandler { public: QXmppPubSubManager *pubSub() { return client()->findExtension(); } bool handlePubSubEvent(const QDomElement &, const QString &pubSubService, const QString &nodeName) override { m_events++; #define return return false; QCOMPARE(pubSubService, m_serviceJid); QCOMPARE(nodeName, m_node); #undef return return true; } QString m_serviceJid; QString m_node; uint m_events = 0; }; struct Client { Client() { } Client(const QString &jid) { test.configuration().setJid(jid); } TestClient test; PSManager *psManager = test.addNewExtension(); }; class tst_QXmppPubSubManager : public QObject { Q_OBJECT private: Q_SLOT void testDiscoFeatures(); Q_SLOT void testRequestFeatures(); Q_SLOT void testRequestPepFeatures(); Q_SLOT void testFetchNodes(); Q_SLOT void testFetchPepNodes(); Q_SLOT void testCreateNodes_data(); Q_SLOT void testCreateNodes(); Q_SLOT void testCreateNodeWithConfig(); Q_SLOT void testCreateInstantNode(); Q_SLOT void testCreateInstantNodeWithConfig(); Q_SLOT void testDeleteNodes_data(); Q_SLOT void testDeleteNodes(); Q_SLOT void testPublishItems_data(); Q_SLOT void testPublishItems(); Q_SLOT void testRetractCurrentItem(); Q_SLOT void testRetractItem_data(); Q_SLOT void testRetractItem(); Q_SLOT void testRetractCurrentPepItem(); Q_SLOT void testPurgeItems(); Q_SLOT void testPurgePepItems(); Q_SLOT void testRequestItemIds(); Q_SLOT void testRequestPepItemIds(); Q_SLOT void testRequestCurrentItem(); Q_SLOT void testRequestItems_data(); Q_SLOT void testRequestItems(); Q_SLOT void testRequestCurrentPepItem(); Q_SLOT void testRequestPepItem(); Q_SLOT void testRequestPepItems(); Q_SLOT void testRequestItemNotFound(); Q_SLOT void testRequestNodeAffiliations(); Q_SLOT void testRequestAffiliations(); Q_SLOT void testRequestAffiliationsNode(); Q_SLOT void testRequestOptions(); Q_SLOT void testRequestOptionsError(); Q_SLOT void testSetOptions(); Q_SLOT void testRequestNodeConfig(); Q_SLOT void testConfigureNode(); Q_SLOT void testCancelConfig(); Q_SLOT void testSubscribeToNode(); Q_SLOT void testUnsubscribeFromNode(); Q_SLOT void testEventNotifications_data(); Q_SLOT void testEventNotifications(); Q_SLOT void testStandardItemToString(); }; void tst_QXmppPubSubManager::testDiscoFeatures() { // so the coverage report is happy: PSManager manager; QCOMPARE(manager.discoveryFeatures(), QStringList { "http://jabber.org/protocol/pubsub#rsm" }); } void tst_QXmppPubSubManager::testRequestFeatures() { auto [test, psManager] = Client(); auto future = psManager->requestFeatures("pubsub.shakespeare.lit"); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "")); expectFutureVariant(future); future = psManager->requestFeatures("pubsub.shakespeare.lit"); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); auto features = expectFutureVariant>(future); QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); future = psManager->requestFeatures("juliet@capulet.lit"); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); features = expectFutureVariant>(future); QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); future = psManager->requestFeatures("juliet@capulet.lit", QXmppPubSubManager::PubSub); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); expectFutureVariant(future); future = psManager->requestFeatures("pubsub.shakespeare.lit", QXmppPubSubManager::PubSub); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); features = expectFutureVariant>(future); QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); future = psManager->requestFeatures("pubsub.shakespeare.lit", QXmppPubSubManager::Pep); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); expectFutureVariant(future); future = psManager->requestFeatures("juliet@capulet.lit", QXmppPubSubManager::Pep); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); features = expectFutureVariant>(future); QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); } void tst_QXmppPubSubManager::testRequestPepFeatures() { auto [test, psManager] = Client(); test.configuration().setJid("juliet@capulet.lit"); auto future = psManager->requestOwnPepFeatures(); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); auto features = expectFutureVariant>(future); QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); } void tst_QXmppPubSubManager::testFetchNodes() { auto [test, psManager] = Client(); auto future = psManager->requestNodes("pubsub.shakespeare.lit"); test.expect(""); test.inject(QStringLiteral("" "" "" "" "")); const auto nodes = expectFutureVariant>(future); QCOMPARE(nodes, (QVector { "blogs", "news" })); } void tst_QXmppPubSubManager::testFetchPepNodes() { auto [test, psManager] = Client(); test.configuration().setJid("juliet@capulet.lit"); auto future = psManager->requestOwnPepNodes(); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "")); const auto nodes = expectFutureVariant>(future); QCOMPARE(nodes, (QVector { "blogs", "news" })); } void tst_QXmppPubSubManager::testCreateNodes_data() { QTest::addColumn("isPep"); QTest::addColumn("jid"); QTest::addColumn("node"); QTest::addRow("createNode") << false << "pubsub.shakespeare.lit" << "princely_musings"; QTest::addRow("createPepNode") << true << "juliet@capulet.lit" << "urn:xmpp:omemo:1:bundles"; } void tst_QXmppPubSubManager::testCreateNodes() { QFETCH(bool, isPep); QFETCH(QString, jid); QFETCH(QString, node); auto [test, psManager] = Client(); QXmppTask future = [=, &t = test, psM = psManager]() { if (isPep) { t.configuration().setJid(jid); return psM->createOwnPepNode(node); } else { return psM->createNode(jid, node); } }(); test.expect(QStringLiteral("").arg(jid, node)); test.inject(QStringLiteral("").arg(jid)); expectFutureVariant(future); } void tst_QXmppPubSubManager::testCreateNodeWithConfig() { auto [test, psManager] = Client(); QXmppPubSubNodeConfig config; config.setTitle("Princely Musings (Atom)"); auto future = psManager->createNode("pubsub.qxmpp.org", "princely_musings", config); test.expect("" "" "" "" "" "http://jabber.org/protocol/pubsub#node_config" "Princely Musings (Atom)" "" "" "" ""); test.inject(""); expectFutureVariant(future); } void tst_QXmppPubSubManager::testCreateInstantNode() { auto [test, psManager] = Client(); auto future = psManager->createInstantNode("pubsub.qxmpp.org"); test.expect("" ""); test.inject(QStringLiteral("" "" "" "")); const auto nodeId = expectFutureVariant(future); QCOMPARE(nodeId, QString("25e3d37dabbab9541f7523321421edc5bfeb2dae")); } void tst_QXmppPubSubManager::testCreateInstantNodeWithConfig() { auto [test, psManager] = Client(); QXmppPubSubNodeConfig config; config.setTitle("Princely Musings (Atom)"); auto future = psManager->createInstantNode("pubsub.qxmpp.org", config); test.expect("" "" "" "" "" "http://jabber.org/protocol/pubsub#node_config" "Princely Musings (Atom)" "" "" "" ""); test.inject("" "" "" ""); QCOMPARE(expectFutureVariant(future), QStringLiteral("25e3d37dabbab9541f7523321421edc5bfeb2dae")); } void tst_QXmppPubSubManager::testDeleteNodes_data() { QTest::addColumn("isPep"); QTest::addColumn("jid"); QTest::addColumn("node"); QTest::addRow("deleteNode") << false << "pubsub.shakespeare.lit" << "princely_musings"; QTest::addRow("deletePepNode") << true << "juliet@capulet.lit" << "urn:xmpp:omemo:1:bundles"; } void tst_QXmppPubSubManager::testDeleteNodes() { QFETCH(bool, isPep); QFETCH(QString, jid); QFETCH(QString, node); auto [test, psManager] = Client(); if (isPep) { test.configuration().setJid(jid); } QXmppTask future = [=, psM = psManager]() { if (isPep) { return psM->deleteOwnPepNode(node); } else { return psM->deleteNode(jid, node); } }(); // FIXME: pubsub#owner here, but not for ? test.expect(QStringLiteral("").arg(jid, node)); test.inject(QStringLiteral("").arg(jid)); expectFutureVariant(future); } void tst_QXmppPubSubManager::testPublishItems_data() { using OptionsOpt = std::optional; QTest::addColumn("isPep"); QTest::addColumn("jid"); QTest::addColumn("node"); QTest::addColumn>("items"); QTest::addColumn("publishOptions"); QTest::addColumn("returnIds"); QXmppTuneItem item1; item1.setId("1234"); item1.setTitle("Hello Goodbye"); QXmppTuneItem item2; item2.setId("5678"); item2.setArtist("Rick Astley"); item2.setTitle("Never gonna give you up"); QVector items1 { item1 }; QVector items2 { item1, item2 }; QXmppPubSubPublishOptions publishOptions; publishOptions.setAccessModel(QXmppPubSubPublishOptions::Presence); auto addRow = [&](const char *name, bool isPep, QString &&jid, QString &&node, const QVector &items) { QTest::addRow("%s", name) << isPep << jid << node << items << OptionsOpt() << false; QTest::addRow("%s%s", name, "ReturnIds") << isPep << jid << node << items << OptionsOpt() << true; QTest::addRow("%s%s", name, "WithOptions") << isPep << jid << node << items << std::make_optional(publishOptions) << false; QTest::addRow("%s%s%s", name, "WithOptions", "ReturnIds") << isPep << jid << node << items << std::make_optional(publishOptions) << true; }; addRow("publishItem", false, "pubsub.shakespeare.lit", "princely_musings", items1); addRow("publishItems", false, "pubsub.shakespeare.lit", "princely_musings", items2); addRow("publishPepItem", true, "juliet@capulet.lit", "urn:xmpp:omemo:1:bundles", items1); addRow("publishPepItems", true, "juliet@capulet.lit", "urn:xmpp:omemo:1:bundles", items2); } void tst_QXmppPubSubManager::testPublishItems() { QFETCH(bool, isPep); QFETCH(QString, jid); QFETCH(QString, node); QFETCH(QVector, items); QFETCH(std::optional, publishOptions); QFETCH(bool, returnIds); const auto itemsXml = [=]() { QBuffer buffer; buffer.open(QIODevice::ReadWrite); QXmlStreamWriter writer(&buffer); for (const auto &item : items) { item.toXml(&writer); } return buffer.data(); }(); const auto publishOptionsXml = [&]() -> QString { if (publishOptions) { auto form = publishOptions->toDataForm(); form.setType(QXmppDataForm::Submit); return "" + QString::fromUtf8(packetToXml(form)) + ""; } return {}; }(); const auto itemIdsXml = [&]() { QString result; for (const auto &item : std::as_const(items)) { result += ""; } return result; }(); const auto itemIds = [&]() { QVector ids; for (const auto &item : std::as_const(items)) { ids << item.id(); } return ids; }(); TestClient test; if (isPep) { test.configuration().setJid(jid); } PSManager *psManager = test.addNewExtension(); auto injectXml = [&]() { test.expect(QStringLiteral("%3%4") .arg(jid, node, itemsXml, publishOptionsXml)); if (returnIds) { test.inject(QStringLiteral(R"( %3 )") .arg(jid, node, itemIdsXml)); } else { test.inject(QStringLiteral("").arg(jid)); } }; if (items.size() == 1) { QXmppTask future = [=, psM = psManager]() { if (isPep) { if (publishOptions) { return psManager->publishOwnPepItem(node, items.constFirst(), *publishOptions); } else { return psManager->publishOwnPepItem(node, items.constFirst()); } } else { if (publishOptions) { return psManager->publishItem(jid, node, items.constFirst(), *publishOptions); } else { return psManager->publishItem(jid, node, items.constFirst()); } } }(); injectXml(); const auto id = expectFutureVariant(future); if (returnIds) { QCOMPARE(id, items.constFirst().id()); } else { QVERIFY(id.isNull()); } } else { QXmppTask future = [=, psM = psManager]() { if (isPep) { if (publishOptions) { return psManager->publishOwnPepItems(node, items, *publishOptions); } else { return psManager->publishOwnPepItems(node, items); } } else { if (publishOptions) { return psManager->publishItems(jid, node, items, *publishOptions); } else { return psManager->publishItems(jid, node, items); } } }(); injectXml(); const auto ids = expectFutureVariant>(future); if (returnIds) { QCOMPARE(ids, itemIds); } else { QVERIFY(ids.empty()); } } } void tst_QXmppPubSubManager::testRetractCurrentItem() { auto [test, psManager] = Client(); auto future = psManager->retractItem(QStringLiteral("pubsub.shakespeare.lit"), QStringLiteral("princely_musings"), PSManager::Current); test.expect(QStringLiteral("" "" "" "" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); expectFutureVariant(future); } void tst_QXmppPubSubManager::testRetractItem_data() { QTest::addColumn("isPep"); QTest::addColumn("jid"); QTest::addColumn("node"); QTest::addColumn("itemId"); QTest::addRow("retractItem") << false << "pubsub.shakespeare.lit" << "princely_musings" << "ae890ac52d0df67ed7cfdf51b644e901"; QTest::addRow("retractPepItem") << true << "juliet@capulet.lit" << "urn:xmpp:omemo:1:bundles" << "31415"; } void tst_QXmppPubSubManager::testRetractItem() { QFETCH(bool, isPep); QFETCH(QString, jid); QFETCH(QString, node); QFETCH(QString, itemId); auto [test, psManager] = Client(); QXmppTask future = [=, psM = psManager, &t = test]() { if (isPep) { t.configuration().setJid(jid); return psM->retractOwnPepItem(node, itemId); } else { return psM->retractItem(jid, node, itemId); } }(); test.expect(QStringLiteral("") .arg(jid, node, itemId)); test.inject(QStringLiteral("") .arg(jid)); expectFutureVariant(future); } void tst_QXmppPubSubManager::testRetractCurrentPepItem() { auto [test, psManager] = Client(); test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->retractOwnPepItem(QStringLiteral("princely_musings"), PSManager::Current); test.expect(QStringLiteral("" "" "" "" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); expectFutureVariant(future); } void tst_QXmppPubSubManager::testPurgeItems() { auto [test, psManager] = Client(); auto future = psManager->purgeItems("pubsub.qxmpp.org", "news"); test.expect("" "" "" ""); test.inject(QStringLiteral("")); expectFutureVariant(future); } void tst_QXmppPubSubManager::testPurgePepItems() { auto [test, psManager] = Client("user@qxmpp.org"); auto future = psManager->purgeOwnPepItems("urn:xmpp:x-avatar:0"); test.expect("" "" "" ""); test.inject(QStringLiteral("")); expectFutureVariant(future); } void tst_QXmppPubSubManager::testRequestItemIds() { auto [test, psManager] = Client(); auto future = psManager->requestItemIds(QStringLiteral("pubsub.shakespeare.lit"), QStringLiteral("princely_musings")); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "")); auto itemIds = expectFutureVariant>(future); QCOMPARE(itemIds, (QVector { QStringLiteral("368866411b877c30064a5f62b917cffe"), QStringLiteral("3300659945416e274474e469a1f0154c") })); } void tst_QXmppPubSubManager::testRequestPepItemIds() { auto [test, psManager] = Client(); test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->requestOwnPepItemIds(QStringLiteral("princely_musings")); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "" "")); auto itemIds = expectFutureVariant>(future); QCOMPARE(itemIds, (QVector { QStringLiteral("368866411b877c30064a5f62b917cffe"), QStringLiteral("3300659945416e274474e469a1f0154c") })); } void tst_QXmppPubSubManager::testRequestCurrentItem() { auto [test, psManager] = Client(); auto future = psManager->requestItem(QStringLiteral("pubsub.shakespeare.lit"), QStringLiteral("princely_musings"), PSManager::Current); test.expect(QStringLiteral("" "" "" "" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); const auto item = expectFutureVariant(future); QCOMPARE(item.id(), QStringLiteral("current")); } void tst_QXmppPubSubManager::testRequestItems_data() { QTest::addColumn("jid"); QTest::addColumn("node"); QTest::addColumn("requestIds"); QTest::addColumn("itemIds"); QTest::addRow("allItems-0") << "pubsub.shakespeare.lit" << "princely_musings" << false << QStringList(); QTest::addRow("allItems-1") << "pubsub.shakespeare.lit" << "princely_musings" << false << QStringList { "ae890ac52d0df67ed7cfdf51b644e901" }; QTest::addRow("allItems-2") << "pubsub.shakespeare.lit" << "princely_musings" << false << QStringList { "ae890ac52d0df67ed7cfdf51b644e901", "3300659945416e274474e469a1f0154c" }; QTest::addRow("oneItemById") << "pubsub.shakespeare.lit" << "princely_musings" << true << QStringList { "ae890ac52d0df67ed7cfdf51b644e901" }; QTest::addRow("twoItemsByIds") << "pubsub.shakespeare.lit" << "princely_musings" << true << QStringList { "ae890ac52d0df67ed7cfdf51b644e901", "3300659945416e274474e469a1f0154c" }; } void tst_QXmppPubSubManager::testRequestItems() { QFETCH(QString, jid); QFETCH(QString, node); QFETCH(bool, requestIds); QFETCH(QStringList, itemIds); QString itemsReplyXml; for (const auto &id : std::as_const(itemIds)) { itemsReplyXml += QStringLiteral(R"( Yes 686 8 Yessongs Heart of the Sunrise 3 http://www.yesworld.com/lyrics/Fragile.html#9 )") .arg(id); } auto [test, psManager] = Client(); QVector returnedItems; if (requestIds) { QString itemsXml; for (const auto &id : std::as_const(itemIds)) { itemsXml += ""; } if (itemIds.size() == 1) { auto future = psManager->requestItem(jid, node, itemIds.constFirst()); test.expect(QStringLiteral("" "" "%3" "") .arg(jid, node, itemsXml)); test.inject(QStringLiteral("" "" "%3" "") .arg(jid, node, itemsReplyXml)); returnedItems = { expectFutureVariant(future) }; } else { auto future = psManager->requestItems(jid, node, itemIds); test.expect(QStringLiteral("" "" "%3" "") .arg(jid, node, itemsXml)); test.inject(QStringLiteral("" "" "%3" "") .arg(jid, node, itemsReplyXml)); returnedItems = expectFutureVariant>(future).items; } } else { auto future = psManager->requestItems(jid, node); test.expect(QStringLiteral("" "" "") .arg(jid, node)); test.inject(QStringLiteral("" "" "%3" "") .arg(jid, node, itemsReplyXml)); returnedItems = expectFutureVariant>(future).items; } for (const auto &item : std::as_const(returnedItems)) { QCOMPARE(item.artist(), QStringLiteral("Yes")); QCOMPARE(*item.length(), uint16_t(686)); QCOMPARE(*item.rating(), uint8_t(8)); QCOMPARE(item.source(), QStringLiteral("Yessongs")); QCOMPARE(item.title(), QStringLiteral("Heart of the Sunrise")); QCOMPARE(item.track(), QLatin1String("3")); QCOMPARE(item.uri(), QUrl("http://www.yesworld.com/lyrics/Fragile.html#9")); } const auto itemsEqual = std::equal(returnedItems.cbegin(), returnedItems.cend(), itemIds.cbegin(), itemIds.cend(), [](const QXmppTuneItem &item, const QString &itemId) { return item.id() == itemId; }); QVERIFY2(itemsEqual, "The items returned from the manager don't match the item IDs from the XML response"); } void tst_QXmppPubSubManager::testRequestCurrentPepItem() { auto [test, psManager] = Client(); test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->requestOwnPepItem(QStringLiteral("princely_musings"), PSManager::Current); test.expect(QStringLiteral("" "" "" "" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); const auto item = expectFutureVariant(future); QCOMPARE(item.id(), QStringLiteral("current")); } void tst_QXmppPubSubManager::testRequestPepItem() { auto [test, psManager] = Client(); test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->requestOwnPepItem(QStringLiteral("princely_musings"), QStringLiteral("ae890ac52d0df67ed7cfdf51b644e901")); test.expect(QStringLiteral("" "" "" "" "" "")); test.inject(QStringLiteral("" "" "" "" "" "")); const auto item = expectFutureVariant(future); QCOMPARE(item.id(), QStringLiteral("ae890ac52d0df67ed7cfdf51b644e901")); } void tst_QXmppPubSubManager::testRequestPepItems() { auto [test, psManager] = Client(); test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->requestOwnPepItems(QStringLiteral("princely_musings")); test.expect(QStringLiteral("" "" "" "")); test.inject(QStringLiteral("" "" "" "" "" "" "")); const auto items = expectFutureVariant>(future); QCOMPARE(items.items.first().id(), QStringLiteral("368866411b877c30064a5f62b917cffe")); QCOMPARE(items.items.last().id(), QStringLiteral("3300659945416e274474e469a1f0154c")); } void tst_QXmppPubSubManager::testRequestItemNotFound() { auto [test, psManager] = Client(); auto future = psManager->requestItem("pubsub.qxmpp.org", "features", "item1"); test.expect(QStringLiteral("" "" "")); test.inject(QStringLiteral("" "" "" "")); auto error = expectFutureVariant(future); } void tst_QXmppPubSubManager::testRequestNodeAffiliations() { auto [test, psManager] = Client(); auto future = psManager->requestNodeAffiliations("pubsub.qxmpp.org", "news"); test.expect("" "" "" ""); test.inject(QStringLiteral("" "" "" "" "" "")); const auto affiliations = expectFutureVariant>(future); QCOMPARE(affiliations.size(), 2); QCOMPARE(affiliations[0].node(), QString()); QCOMPARE(affiliations[0].jid(), QString("hamlet@denmark.lit")); QCOMPARE(affiliations[0].type(), AffiliationType::Owner); QCOMPARE(affiliations[1].node(), QString()); QCOMPARE(affiliations[1].jid(), QString("polonius@denmark.lit")); QCOMPARE(affiliations[1].type(), AffiliationType::Outcast); } void tst_QXmppPubSubManager::testRequestAffiliations() { auto [test, psManager] = Client(); auto future = psManager->requestAffiliations("pubsub.qxmpp.org"); test.expect("" ""); test.inject(QStringLiteral("" "" "" "" "" "" "")); const auto affiliations = expectFutureVariant>(future); QCOMPARE(affiliations.size(), 4); QCOMPARE(affiliations[3].node(), QString("node6")); QCOMPARE(affiliations[3].jid(), QString()); QCOMPARE(affiliations[3].type(), AffiliationType::Owner); } void tst_QXmppPubSubManager::testRequestAffiliationsNode() { auto [test, psManager] = Client(); auto future = psManager->requestAffiliations("pubsub.qxmpp.org", "node6"); test.expect("" ""); test.inject(QStringLiteral("" "" "" "")); const auto affiliations = expectFutureVariant>(future); QCOMPARE(affiliations.size(), 1); QCOMPARE(affiliations[0].node(), QString("node6")); QCOMPARE(affiliations[0].jid(), QString()); QCOMPARE(affiliations[0].type(), AffiliationType::Owner); } void tst_QXmppPubSubManager::testRequestOptions() { using PresenceStates = QXmppPubSubSubscribeOptions::PresenceState; TestClient test; auto *psManager = test.addNewExtension(); auto testOpts = [&](QXmppTask &&future) { test.expect("" "" ""); test.inject("" "" "" "" "http://jabber.org/protocol/pubsub#subscribe_options" "1" "0" "false" "" "" "" "" "" "" "chat" "online" ""); const auto form = expectFutureVariant(future); QCOMPARE(form.notificationsEnabled().value(), true); QCOMPARE(form.digestsEnabled().value(), false); QCOMPARE(form.bodyIncluded().value(), false); QCOMPARE(form.notificationRules(), PresenceStates::Chat | PresenceStates::Online); QCOMPARE(form.unknownFields().size(), 0); }; testOpts(psManager->requestSubscribeOptions("pubsub.shakespeare.lit", "node1", "me@qxmpp.org")); test.configuration().setJid("me@qxmpp.org"); testOpts(psManager->requestSubscribeOptions("pubsub.shakespeare.lit", "node1")); } void tst_QXmppPubSubManager::testRequestOptionsError() { auto [test, psManager] = Client(); auto future = psManager->requestSubscribeOptions("pubsub.shakespeare.lit", "node1", "me@qxmpp.org"); test.expect("" "" ""); test.inject("" "" "" "" "urn:xmpp:invlid:pubsub#subscribe_options" "1" "0" "false" "" "" "" "" "" "" "chat" "online" ""); auto err = expectFutureVariant(future); QVERIFY(!err.description.isEmpty()); } void tst_QXmppPubSubManager::testSetOptions() { using PresenceStates = QXmppPubSubSubscribeOptions::PresenceState; auto [test, psManager] = Client("francisco@denmark.lit"); QXmppPubSubSubscribeOptions opts; opts.setNotificationsEnabled(true); opts.setDigestsEnabled(false); opts.setBodyIncluded(false); opts.setNotificationRules(PresenceStates::Chat | PresenceStates::Online | PresenceStates::Away); auto future = psManager->setSubscribeOptions("pubsub.shakespeare.lit", "princely_musings", opts); test.expect("" "" "" "http://jabber.org/protocol/pubsub#subscribe_options" "1" "0" "0" "awaychatonline" ""); test.inject(""); expectFutureVariant(future); } void tst_QXmppPubSubManager::testRequestNodeConfig() { auto [test, psManager] = Client(); auto future = psManager->requestNodeConfiguration("pubsub.qxmpp.org", "tune"); test.expect("" "" "" "" ""); test.inject("" "" "" "" "http://jabber.org/protocol/pubsub#node_config" "" "true" "true" "" "" "" "" ""); const auto form = expectFutureVariant(future); QCOMPARE(form.title(), QString()); QVERIFY(form.notificationsEnabled().has_value()); QCOMPARE(form.notificationsEnabled().value_or(false), true); QVERIFY(form.dataFormXslt().isNull()); } void tst_QXmppPubSubManager::testConfigureNode() { auto [test, psManager] = Client(); QXmppPubSubNodeConfig config; config.setTitle("Princely Musings (Atom)"); config.setNotificationsEnabled(true); config.setIncludePayloads(true); config.setPersistItems(true); config.setMaxItems(10ULL); config.setItemExpiry(604800); config.setAccessModel(QXmppPubSubNodeConfig::Roster); config.setAllowedRosterGroups({ "friends", "servants", "courtiers" }); config.setPublishModel(QXmppPubSubNodeConfig::Publishers); config.setPurgeWhenOffline(false); config.setSendLastItem(QXmppPubSubNodeConfig::Never); config.setPresenceBasedNotifications(false); config.setNotificationType(QXmppPubSubNodeConfig::Headline); config.setConfigNotificationsEnabled(false); config.setDeleteNotificationsEnabled(false); config.setRetractNotificationsEnabled(false); config.setSubNotificationsEnabled(false); config.setMaxPayloadSize(1028); config.setPayloadType("http://www.w3.org/2005/Atom"); config.setBodyXslt("http://jabxslt.jabberstudio.org/atom_body.xslt"); auto future = psManager->configureNode("pubsub.shakespeare.lit", "princely_musings", config); test.expect("" "" "" "" "http://jabber.org/protocol/pubsub#node_config" "roster" "http://jabxslt.jabberstudio.org/atom_body.xslt" "1" "1" "604800" "10" "1028" "headline" "0" "0" "0" "0" "1" "0" "publishers" "0" "friendsservantscourtiers" "never" "Princely Musings (Atom)" "http://www.w3.org/2005/Atom" "" "" "" ""); test.inject(""); expectFutureVariant(future); } void tst_QXmppPubSubManager::testCancelConfig() { auto [test, psManager] = Client(); auto future = psManager->cancelNodeConfiguration("pubsub.qxmpp.org", "nodeee"); test.expect("" "" "" "" "" "" ""); test.inject(""); expectFutureVariant(future); } void tst_QXmppPubSubManager::testSubscribeToNode() { auto [test, psManager] = Client(); auto future = psManager->subscribeToNode(QStringLiteral("pubsub.shakespeare.lit"), QStringLiteral("princely_musings"), QStringLiteral("francisco@denmark.lit")); test.expect(QStringLiteral("" "" "" "")); test.inject(QStringLiteral("" "" "" "")); expectFutureVariant(future); } void tst_QXmppPubSubManager::testUnsubscribeFromNode() { auto [test, psManager] = Client(); auto future = psManager->unsubscribeFromNode(QStringLiteral("pubsub.shakespeare.lit"), QStringLiteral("princely_musings"), QStringLiteral("francisco@denmark.lit")); test.expect(QStringLiteral("" "" "" "")); test.inject(QStringLiteral("" "" "" "")); expectFutureVariant(future); } void tst_QXmppPubSubManager::testEventNotifications_data() { QTest::addColumn("xml"); QTest::addColumn("accepted"); QTest::addRow("default") << QStringLiteral( "" "" "" "" "" "Soliloquy" "" "To be, or not to be: that is the question:" "Whether 'tis nobler in the mind to suffer" "The slings and arrows of outrageous fortune," "Or to take arms against a sea of troubles," "And by opposing end them?" "" "" "tag:denmark.lit,2003:entry-32397" "2003-12-13T18:30:02Z" "2003-12-13T18:30:02Z" "" "" "" "" "") << true; QTest::addRow("additional-subelement") << QStringLiteral( "" "" "" "" "" "" "Soliloquy" "" "To be, or not to be: that is the question:" "Whether 'tis nobler in the mind to suffer" "The slings and arrows of outrageous fortune," "Or to take arms against a sea of troubles," "And by opposing end them?" "" "" "tag:denmark.lit,2003:entry-32397" "2003-12-13T18:30:02Z" "2003-12-13T18:30:02Z" "" "" "" "" "") << true; QTest::addRow("wrong-event-namespace") << QStringLiteral( "" "" "" "" "" "" "Soliloquy" "" "" "" "" "") << false; } void tst_QXmppPubSubManager::testEventNotifications() { QFETCH(QString, xml); QFETCH(bool, accepted); const auto event = xmlToDom(xml); auto [client, psManager] = Client(); auto *eventManager = client.addNewExtension(); eventManager->m_node = "princely_musings"; eventManager->m_serviceJid = "pubsub.shakespeare.lit"; QCOMPARE(psManager->handleStanza(event), accepted); if (accepted) { QCOMPARE(eventManager->m_events, 1u); } else { QCOMPARE(eventManager->m_events, 0u); } QCOMPARE(eventManager->pubSub(), psManager); } void tst_QXmppPubSubManager::testStandardItemToString() { auto standardItemString = PSManager::standardItemIdToString(PSManager::Current); QCOMPARE(standardItemString, QStringLiteral("current")); } QTEST_MAIN(tst_QXmppPubSubManager) #include "tst_qxmpppubsubmanager.moc"