File indexing completed on 2024-05-12 05:10:39
0001 /* 0002 SPDX-FileCopyrightText: 2013 Sérgio Martins <iamsergio@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "itiphandlertest.h" 0008 #include "fetchjobcalendar.h" 0009 #include "helper.h" 0010 #include "mailclient_p.h" 0011 #include "utils_p.h" 0012 0013 #include <Akonadi/CollectionFetchJob> 0014 #include <Akonadi/CollectionFetchScope> 0015 #include <Akonadi/ItemDeleteJob> 0016 #include <Akonadi/ItemFetchScope> 0017 #include <Akonadi/MessageQueueJob> 0018 #include <KCalendarCore/Attendee> 0019 #include <KCalendarCore/ICalFormat> 0020 #include <akonadi/qtest_akonadi.h> 0021 0022 #include <QString> 0023 #include <QTimeZone> 0024 0025 using namespace Akonadi; 0026 using namespace KCalendarCore; 0027 0028 Q_DECLARE_METATYPE(Akonadi::IncidenceChanger::InvitationPolicy) 0029 Q_DECLARE_METATYPE(QList<Akonadi::IncidenceChanger::ChangeType>) 0030 Q_DECLARE_METATYPE(Akonadi::ITIPHandler::Result) 0031 Q_DECLARE_METATYPE(KCalendarCore::Attendee::PartStat) 0032 Q_DECLARE_METATYPE(QList<int>) 0033 0034 static const char *s_ourEmail = "unittests@dev.nul"; // change also in kdepimlibs/akonadi/calendar/tests/unittestenv/kdehome/share/config 0035 static const char *s_outEmail2 = "identity2@kde.org"; 0036 0037 class FakeMessageQueueJob : public Akonadi::MessageQueueJob 0038 { 0039 public: 0040 explicit FakeMessageQueueJob(QObject *parent = nullptr) 0041 : Akonadi::MessageQueueJob(parent) 0042 { 0043 } 0044 0045 void start() override 0046 { 0047 UnitTestResult unitTestResult; 0048 unitTestResult.message = message(); 0049 unitTestResult.from = addressAttribute().from(); 0050 unitTestResult.to = addressAttribute().to(); 0051 unitTestResult.cc = addressAttribute().cc(); 0052 unitTestResult.bcc = addressAttribute().bcc(); 0053 unitTestResult.transportId = transportAttribute().transportId(); 0054 FakeMessageQueueJob::sUnitTestResults << unitTestResult; 0055 0056 setError(Akonadi::MailClient::ResultSuccess); 0057 setErrorText(QString()); 0058 0059 emitResult(); 0060 } 0061 0062 static UnitTestResult::List sUnitTestResults; 0063 }; 0064 0065 UnitTestResult::List FakeMessageQueueJob::sUnitTestResults; 0066 0067 class FakeITIPHandlerComponentFactory : public ITIPHandlerComponentFactory 0068 { 0069 public: 0070 FakeITIPHandlerComponentFactory(QObject *parent = nullptr) 0071 : ITIPHandlerComponentFactory(parent) 0072 { 0073 } 0074 0075 Akonadi::MessageQueueJob *createMessageQueueJob(const KCalendarCore::IncidenceBase::Ptr &incidence, 0076 const KIdentityManagementCore::Identity &identity, 0077 QObject *parent = nullptr) override 0078 { 0079 Q_UNUSED(incidence) 0080 Q_UNUSED(identity) 0081 return new FakeMessageQueueJob(parent); 0082 } 0083 }; 0084 0085 void ITIPHandlerTest::initTestCase() 0086 { 0087 AkonadiTest::checkTestIsIsolated(); 0088 extern AKONADI_CALENDAR_TESTS_EXPORT bool akonadi_calendar_running_unittests; 0089 akonadi_calendar_running_unittests = true; 0090 m_pendingItipMessageSignal = 0; 0091 m_pendingIncidenceChangerSignal = 0; 0092 m_itipHandler = nullptr; 0093 m_cancelExpected = false; 0094 m_changer = new IncidenceChanger(new FakeITIPHandlerComponentFactory(this), this); 0095 m_changer->setHistoryEnabled(false); 0096 m_changer->setGroupwareCommunication(true); 0097 m_changer->setInvitationPolicy(IncidenceChanger::InvitationPolicySend); // don't show dialogs 0098 0099 connect(m_changer, &IncidenceChanger::createFinished, this, &ITIPHandlerTest::onCreateFinished); 0100 0101 connect(m_changer, &IncidenceChanger::deleteFinished, this, &ITIPHandlerTest::onDeleteFinished); 0102 0103 connect(m_changer, &IncidenceChanger::modifyFinished, this, &ITIPHandlerTest::onModifyFinished); 0104 } 0105 0106 void ITIPHandlerTest::testProcessITIPMessage_data() 0107 { 0108 QTest::addColumn<QString>("data_filename"); 0109 QTest::addColumn<QString>("action"); 0110 QTest::addColumn<QString>("receiver"); 0111 QTest::addColumn<QString>("incidenceUid"); // uid of incidence in invitation 0112 QTest::addColumn<Akonadi::ITIPHandler::Result>("expectedResult"); 0113 QTest::addColumn<int>("expectedNumIncidences"); 0114 QTest::addColumn<KCalendarCore::Attendee::PartStat>("expectedPartStat"); 0115 0116 QString data_filename; 0117 QString action = QStringLiteral("accepted"); 0118 QString incidenceUid = QStringLiteral("uosj936i6arrtl9c2i5r2mfuvg"); 0119 QString receiver = QLatin1StringView(s_ourEmail); 0120 Akonadi::ITIPHandler::Result expectedResult; 0121 int expectedNumIncidences = 0; 0122 KCalendarCore::Attendee::PartStat expectedPartStat; 0123 0124 //---------------------------------------------------------------------------------------------- 0125 // Someone invited us to an event, and we accept 0126 expectedResult = ITIPHandler::ResultSuccess; 0127 data_filename = QStringLiteral("invited_us"); 0128 expectedNumIncidences = 1; 0129 expectedPartStat = KCalendarCore::Attendee::Accepted; 0130 action = QStringLiteral("accepted"); 0131 QTest::newRow("invited us1") << data_filename << action << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0132 //---------------------------------------------------------------------------------------------- 0133 // Someone invited us to an event, and we accept conditionally 0134 expectedResult = ITIPHandler::ResultSuccess; 0135 data_filename = QStringLiteral("invited_us"); 0136 expectedNumIncidences = 1; 0137 expectedPartStat = KCalendarCore::Attendee::Tentative; 0138 action = QStringLiteral("tentative"); 0139 QTest::newRow("invited us2") << data_filename << action << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0140 //---------------------------------------------------------------------------------------------- 0141 // Someone invited us to an event, we delegate it 0142 expectedResult = ITIPHandler::ResultSuccess; 0143 data_filename = QStringLiteral("invited_us"); 0144 0145 // The e-mail to the delegate is sent by kmail's text_calendar.cpp 0146 expectedNumIncidences = 1; 0147 expectedPartStat = KCalendarCore::Attendee::Delegated; 0148 action = QStringLiteral("delegated"); 0149 QTest::newRow("invited us3") << data_filename << action << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0150 //---------------------------------------------------------------------------------------------- 0151 // Process a CANCEL without having the incidence in our calendar. 0152 // itiphandler should return success and not error 0153 expectedResult = ITIPHandler::ResultSuccess; 0154 data_filename = QStringLiteral("invited_us"); 0155 expectedNumIncidences = 0; 0156 action = QStringLiteral("cancel"); 0157 QTest::newRow("invited us4") << data_filename << action << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0158 //---------------------------------------------------------------------------------------------- 0159 // Process a REQUEST without having the incidence in our calendar. 0160 // itiphandler should return success and add the request to a calendar 0161 expectedResult = ITIPHandler::ResultSuccess; 0162 data_filename = QStringLiteral("invited_us"); 0163 expectedNumIncidences = 1; 0164 expectedPartStat = KCalendarCore::Attendee::NeedsAction; 0165 action = QStringLiteral("request"); 0166 QTest::newRow("invited us5") << data_filename << action << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0167 //---------------------------------------------------------------------------------------------- 0168 // Here we're testing an error case, where data is null. 0169 expectedResult = ITIPHandler::ResultError; 0170 expectedNumIncidences = 0; 0171 action = QStringLiteral("accepted"); 0172 QTest::newRow("invalid data") << QString() << action << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0173 //---------------------------------------------------------------------------------------------- 0174 // Testing invalid action 0175 expectedResult = ITIPHandler::ResultError; 0176 data_filename = QStringLiteral("invitation_us"); 0177 expectedNumIncidences = 0; 0178 action = QStringLiteral("accepted"); 0179 QTest::newRow("invalid action") << data_filename << QString() << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0180 //---------------------------------------------------------------------------------------------- 0181 // Test bug 235749 0182 expectedResult = ITIPHandler::ResultSuccess; 0183 data_filename = QStringLiteral("bug235749"); 0184 expectedNumIncidences = 1; 0185 expectedPartStat = KCalendarCore::Attendee::Accepted; 0186 action = QStringLiteral("accepted"); 0187 incidenceUid = QStringLiteral("b6f0466a-8877-49d0-a4fc-8ee18ffd8e07"); // Don't change, hardcoded in data file 0188 QTest::newRow("bug 235749") << data_filename << action << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0189 //---------------------------------------------------------------------------------------------- 0190 // Test counterproposal without a UI delegat set 0191 expectedResult = ITIPHandler::ResultError; 0192 data_filename = QStringLiteral("invited_us"); 0193 expectedNumIncidences = 0; 0194 expectedPartStat = KCalendarCore::Attendee::Accepted; 0195 action = QStringLiteral("counter"); 0196 incidenceUid = QStringLiteral("b6f0466a-8877-49d0-a4fc-8ee18ffd8e07"); 0197 QTest::newRow("counter error") << data_filename << action << receiver << incidenceUid << expectedResult << expectedNumIncidences << expectedPartStat; 0198 //---------------------------------------------------------------------------------------------- 0199 } 0200 0201 void ITIPHandlerTest::testProcessITIPMessage() 0202 { 0203 QFETCH(QString, data_filename); 0204 QFETCH(QString, action); 0205 QFETCH(QString, receiver); 0206 QFETCH(QString, incidenceUid); 0207 QFETCH(Akonadi::ITIPHandler::Result, expectedResult); 0208 QFETCH(int, expectedNumIncidences); 0209 QFETCH(KCalendarCore::Attendee::PartStat, expectedPartStat); 0210 0211 FakeMessageQueueJob::sUnitTestResults.clear(); 0212 createITIPHandler(); 0213 0214 m_expectedResult = expectedResult; 0215 0216 QString iCalData = icalData(data_filename); 0217 Akonadi::Item::List items; 0218 processItip(iCalData, receiver, action, expectedNumIncidences, items); 0219 0220 if (expectedNumIncidences == 1) { 0221 auto incidence = items.first().payload<KCalendarCore::Incidence::Ptr>(); 0222 QVERIFY(incidence); 0223 QCOMPARE(incidence->schedulingID(), incidenceUid); 0224 QVERIFY(incidence->schedulingID() != incidence->uid()); 0225 0226 KCalendarCore::Attendee me = ourAttendee(incidence); 0227 QVERIFY(!me.isNull()); 0228 QCOMPARE(me.status(), expectedPartStat); 0229 } 0230 0231 cleanup(); 0232 } 0233 0234 void ITIPHandlerTest::testProcessITIPMessages_data() 0235 { 0236 QTest::addColumn<QStringList>("invitation_filenames"); // filename to create incidence (inputs) 0237 QTest::addColumn<QString>("expected_filename"); // filename with expected data (reference) 0238 QTest::addColumn<QStringList>("actions"); // we must specify the METHOD. This is an ITipHandler API workaround, not sure why we must pass it as argument 0239 // since it's already inside the icaldata. 0240 QStringList invitation_filenames; 0241 QString expected_filename; 0242 QStringList actions; 0243 actions << QStringLiteral("accepted") << QStringLiteral("accepted"); 0244 0245 //---------------------------------------------------------------------------------------------- 0246 // Someone invited us to an event, we accept, then organizer changes event, and we record update: 0247 invitation_filenames.clear(); 0248 invitation_filenames << QStringLiteral("invited_us") << QStringLiteral("invited_us_update01"); 0249 expected_filename = QStringLiteral("expected_data/update1"); 0250 QTest::newRow("accept update") << invitation_filenames << expected_filename << actions; 0251 //---------------------------------------------------------------------------------------------- 0252 // Someone invited us to an event, we accept, then organizer changes event, and we record update: 0253 invitation_filenames.clear(); 0254 invitation_filenames << QStringLiteral("invited_us") << QStringLiteral("invited_us_daily_update01"); 0255 expected_filename = QStringLiteral("expected_data/update2"); 0256 QTest::newRow("accept recurringupdate") << invitation_filenames << expected_filename << actions; 0257 //---------------------------------------------------------------------------------------------- 0258 // We accept a recurring event, then the organizer changes the summary to the second instance (RECID) 0259 expected_filename = QStringLiteral("expected_data/update3"); 0260 invitation_filenames.clear(); 0261 invitation_filenames << QStringLiteral("invited_us_daily") << QStringLiteral("invited_us_daily_update_recid01"); 0262 QTest::newRow("accept recid update") << invitation_filenames << expected_filename << actions; 0263 //---------------------------------------------------------------------------------------------- 0264 // We accept a recurring event, then we accept a CANCEL with recuring-id. 0265 // The result is that a exception with status CANCELLED should be created, and our main incidence 0266 // should not be touched 0267 invitation_filenames.clear(); 0268 invitation_filenames << QStringLiteral("invited_us_daily") << QStringLiteral("invited_us_daily_cancel_recid01"); 0269 expected_filename = QStringLiteral("expected_data/cancel1"); 0270 actions << QStringLiteral("accepted") << QStringLiteral("cancel"); 0271 QTest::newRow("accept recid cancel") << invitation_filenames << expected_filename << actions; 0272 0273 //---------------------------------------------------------------------------------------------- 0274 } 0275 0276 void ITIPHandlerTest::testProcessITIPMessages() 0277 { 0278 QFETCH(QStringList, invitation_filenames); 0279 QFETCH(QString, expected_filename); 0280 QFETCH(QStringList, actions); 0281 0282 const QString receiver = QLatin1StringView(s_ourEmail); 0283 0284 FakeMessageQueueJob::sUnitTestResults.clear(); 0285 createITIPHandler(); 0286 0287 m_expectedResult = Akonadi::ITIPHandler::ResultSuccess; 0288 0289 for (int i = 0; i < invitation_filenames.count(); i++) { 0290 // First accept the invitation that creates the incidence: 0291 QString iCalData = icalData(invitation_filenames.at(i)); 0292 Item::List items; 0293 qDebug() << "Processing " << invitation_filenames.at(i); 0294 processItip(iCalData, receiver, actions.at(i), -1, items); 0295 } 0296 0297 QString expectedICalData = icalData(expected_filename); 0298 KCalendarCore::MemoryCalendar::Ptr expectedCalendar = KCalendarCore::MemoryCalendar::Ptr(new KCalendarCore::MemoryCalendar(QTimeZone::utc())); 0299 KCalendarCore::ICalFormat format; 0300 format.fromString(expectedCalendar, expectedICalData); 0301 compareCalendars(expectedCalendar); // Here's where the cool and complex comparisons are done 0302 0303 cleanup(); 0304 } 0305 0306 void ITIPHandlerTest::testProcessITIPMessageCancel_data() 0307 { 0308 QTest::addColumn<QString>("creation_data_filename"); // filename to create incidence 0309 QTest::addColumn<QString>("cancel_data_filename"); // filename with incidence cancellation 0310 QTest::addColumn<QString>("incidenceUid"); // uid of incidence in invitation 0311 0312 QString creation_data_filename; 0313 QString cancel_data_filename; 0314 QString incidenceUid = QStringLiteral("uosj936i6arrtl9c2i5r2mfuvg"); 0315 //---------------------------------------------------------------------------------------------- 0316 // Someone invited us to an event, we accept, then organizer cancels event 0317 creation_data_filename = QStringLiteral("invited_us"); 0318 cancel_data_filename = QStringLiteral("invited_us_cancel01"); 0319 0320 QTest::newRow("cancel1") << creation_data_filename << cancel_data_filename << incidenceUid; 0321 //---------------------------------------------------------------------------------------------- 0322 // Someone invited us to daily event, we accept, then organizer cancels the whole recurrence series 0323 creation_data_filename = QStringLiteral("invited_us_daily"); 0324 cancel_data_filename = QStringLiteral("invited_us_daily_cancel01"); 0325 0326 QTest::newRow("cancel_daily") << creation_data_filename << cancel_data_filename << incidenceUid; 0327 //---------------------------------------------------------------------------------------------- 0328 } 0329 0330 void ITIPHandlerTest::testProcessITIPMessageCancel() 0331 { 0332 QFETCH(QString, creation_data_filename); 0333 QFETCH(QString, cancel_data_filename); 0334 QFETCH(QString, incidenceUid); 0335 0336 const QString receiver = QLatin1StringView(s_ourEmail); 0337 FakeMessageQueueJob::sUnitTestResults.clear(); 0338 createITIPHandler(); 0339 0340 m_expectedResult = Akonadi::ITIPHandler::ResultSuccess; 0341 0342 // First accept the invitation that creates the incidence: 0343 QString iCalData = icalData(creation_data_filename); 0344 Item::List items; 0345 processItip(iCalData, receiver, QStringLiteral("accepted"), 1, items); 0346 0347 auto incidence = items.first().payload<KCalendarCore::Incidence::Ptr>(); 0348 QVERIFY(incidence); 0349 0350 // good, now accept the invitation that has the CANCEL 0351 iCalData = icalData(cancel_data_filename); 0352 processItip(iCalData, receiver, QStringLiteral("accepted"), 0, items); 0353 } 0354 0355 void ITIPHandlerTest::testOutgoingInvitations_data() 0356 { 0357 QTest::addColumn<Akonadi::Item>("item"); // existing incidence that will be target of creation, deletion or modification 0358 QTest::addColumn<Akonadi::IncidenceChanger::ChangeType>("changeType"); // creation, deletion, modification 0359 QTest::addColumn<int>("expectedEmailCount"); 0360 QTest::addColumn<IncidenceChanger::InvitationPolicy>("invitationPolicy"); 0361 QTest::addColumn<bool>("userCancels"); 0362 0363 const bool userDoesntCancel = false; 0364 const bool userCancels = true; 0365 0366 Akonadi::Item item; 0367 KCalendarCore::Incidence::Ptr incidence; 0368 IncidenceChanger::ChangeType changeType; 0369 const IncidenceChanger::InvitationPolicy invitationPolicyAsk = IncidenceChanger::InvitationPolicyAsk; 0370 const IncidenceChanger::InvitationPolicy invitationPolicySend = IncidenceChanger::InvitationPolicySend; 0371 const IncidenceChanger::InvitationPolicy invitationPolicyDontSend = IncidenceChanger::InvitationPolicyDontSend; 0372 int expectedEmailCount = 0; 0373 Q_UNUSED(invitationPolicyAsk) 0374 0375 const QString ourEmail = QLatin1StringView(s_ourEmail); 0376 Attendee us(QString(), ourEmail); 0377 const Attendee mia(QStringLiteral("Mia Wallace"), QStringLiteral("mia@dev.nul")); 0378 const Attendee vincent(QStringLiteral("Vincent"), QStringLiteral("vincent@dev.nul")); 0379 const Attendee jules(QStringLiteral("Jules"), QStringLiteral("jules@dev.nul")); 0380 const QString uid = QStringLiteral("random-uid-123"); 0381 0382 //---------------------------------------------------------------------------------------------- 0383 // Creation. We are organizer. We invite another person. 0384 changeType = IncidenceChanger::ChangeTypeCreate; 0385 item = generateIncidence(uid, /**organizer=*/ourEmail); 0386 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0387 incidence->addAttendee(vincent); 0388 incidence->addAttendee(jules); 0389 expectedEmailCount = 1; 0390 QTest::newRow("Creation. We organize.") << item << changeType << expectedEmailCount << invitationPolicySend << userDoesntCancel; 0391 //---------------------------------------------------------------------------------------------- 0392 // Creation. We are organizer. We invite another person. But we choose not to send invitation e-mail. 0393 changeType = IncidenceChanger::ChangeTypeCreate; 0394 item = generateIncidence(uid, /**organizer=*/ourEmail); 0395 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0396 incidence->addAttendee(vincent); 0397 incidence->addAttendee(jules); 0398 expectedEmailCount = 0; 0399 QTest::newRow("Creation. We organize.2") << item << changeType << expectedEmailCount << invitationPolicyDontSend << userDoesntCancel; 0400 //---------------------------------------------------------------------------------------------- 0401 // We delete an event that we organized, and has attendees, that will be notified. 0402 changeType = IncidenceChanger::ChangeTypeDelete; 0403 item = generateIncidence(uid, /**organizer=*/ourEmail); 0404 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0405 incidence->addAttendee(vincent); 0406 incidence->addAttendee(jules); 0407 expectedEmailCount = 1; 0408 QTest::newRow("Deletion. We organized.") << item << changeType << expectedEmailCount << invitationPolicySend << userDoesntCancel; 0409 //---------------------------------------------------------------------------------------------- 0410 // We delete an event that we organized, and has attendees. We won't send e-mail notifications. 0411 changeType = IncidenceChanger::ChangeTypeDelete; 0412 item = generateIncidence(uid, /**organizer=*/ourEmail); 0413 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0414 incidence->addAttendee(vincent); 0415 incidence->addAttendee(jules); 0416 expectedEmailCount = 0; 0417 QTest::newRow("Deletion. We organized.2") << item << changeType << expectedEmailCount << invitationPolicyDontSend << userDoesntCancel; 0418 //---------------------------------------------------------------------------------------------- 0419 // We delete an event that we organized, and has attendees, who will be notified. 0420 changeType = IncidenceChanger::ChangeTypeModify; 0421 item = generateIncidence(uid, /**organizer=*/ourEmail); 0422 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0423 incidence->addAttendee(vincent); 0424 incidence->addAttendee(jules); 0425 expectedEmailCount = 1; 0426 QTest::newRow("Modification. We organizd.") << item << changeType << expectedEmailCount << invitationPolicySend << userDoesntCancel; 0427 //---------------------------------------------------------------------------------------------- 0428 // We delete an event that we organized, and has attendees, who won't be notified. 0429 changeType = IncidenceChanger::ChangeTypeModify; 0430 item = generateIncidence(uid, /**organizer=*/ourEmail); 0431 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0432 incidence->addAttendee(vincent); // TODO: test that all attendees got the e-mail 0433 incidence->addAttendee(jules); 0434 expectedEmailCount = 0; 0435 QTest::newRow("Modification. We organizd.2") << item << changeType << expectedEmailCount << invitationPolicyDontSend << userDoesntCancel; 0436 //---------------------------------------------------------------------------------------------- 0437 // We delete an event which we're not the organizer of. Organizer gets REPLY with PartState=Declined 0438 changeType = IncidenceChanger::ChangeTypeDelete; 0439 item = generateIncidence(uid, /**organizer=*/mia.email()); 0440 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0441 incidence->addAttendee(vincent); 0442 incidence->addAttendee(jules); 0443 us.setStatus(Attendee::Accepted); // TODO: Test without accepted status 0444 incidence->addAttendee(us); // TODO: test that attendees didn't receive the REPLY 0445 expectedEmailCount = 1; // REPLY is always sent, there are no dialogs to control this. Dialogs only control REQUEST and CANCEL. Bug or feature ? 0446 QTest::newRow("Deletion. We didn't organize.") << item << changeType << expectedEmailCount << invitationPolicyDontSend << userDoesntCancel; 0447 //---------------------------------------------------------------------------------------------- 0448 // We delete an event which we're not the organizer of. Organizer gets REPLY with PartState=Declined 0449 changeType = IncidenceChanger::ChangeTypeDelete; 0450 item = generateIncidence(uid, /**organizer=*/mia.email()); 0451 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0452 incidence->addAttendee(vincent); 0453 incidence->addAttendee(jules); // TODO: test that attendees didn't receive the REPLY 0454 us.setStatus(Attendee::Accepted); // TODO: Test without accepted status 0455 incidence->addAttendee(us); 0456 expectedEmailCount = 1; 0457 QTest::newRow("Deletion. We didn't organize.2") << item << changeType << expectedEmailCount << invitationPolicySend << userDoesntCancel; 0458 //---------------------------------------------------------------------------------------------- 0459 // We modified an event which we're not the organizer of. And, when the "do you really want to modify", we choose "yes". 0460 changeType = IncidenceChanger::ChangeTypeModify; 0461 item = generateIncidence(uid, /**organizer=*/mia.email()); 0462 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0463 incidence->addAttendee(vincent); 0464 incidence->addAttendee(jules); 0465 us.setStatus(Attendee::Accepted); 0466 incidence->addAttendee(us); 0467 expectedEmailCount = 0; 0468 QTest::newRow("Modification. We didn't organize") << item << changeType << expectedEmailCount << invitationPolicySend << userDoesntCancel; 0469 //---------------------------------------------------------------------------------------------- 0470 // We modified an event which we're not the organizer of. And, when the "do you really want to modify", we choose "no". 0471 changeType = IncidenceChanger::ChangeTypeModify; 0472 item = generateIncidence(uid, /**organizer=*/mia.email()); 0473 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0474 incidence->addAttendee(vincent); 0475 incidence->addAttendee(jules); 0476 us.setStatus(Attendee::Accepted); 0477 incidence->addAttendee(us); 0478 expectedEmailCount = 0; 0479 QTest::newRow("Modification. We didn't organize.2") << item << changeType << expectedEmailCount << invitationPolicyDontSend << userCancels; 0480 //---------------------------------------------------------------------------------------------- 0481 } 0482 0483 void ITIPHandlerTest::testOutgoingInvitations() 0484 { 0485 QFETCH(Akonadi::Item, item); 0486 QFETCH(IncidenceChanger::ChangeType, changeType); 0487 QFETCH(int, expectedEmailCount); 0488 QFETCH(IncidenceChanger::InvitationPolicy, invitationPolicy); 0489 QFETCH(bool, userCancels); 0490 auto incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0491 0492 m_pendingIncidenceChangerSignal = 1; 0493 FakeMessageQueueJob::sUnitTestResults.clear(); 0494 m_changer->setInvitationPolicy(invitationPolicy); 0495 0496 m_cancelExpected = userCancels; 0497 0498 switch (changeType) { 0499 case IncidenceChanger::ChangeTypeCreate: 0500 m_changer->createIncidence(incidence, mCollection); 0501 waitForIt(); 0502 QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), expectedEmailCount); 0503 break; 0504 case IncidenceChanger::ChangeTypeModify: { 0505 // Create if first, so we have something to modify 0506 m_changer->setGroupwareCommunication(false); // we disable groupware because creating an incidence which we're not the organizer of is not permitted. 0507 m_changer->createIncidence(incidence, mCollection); 0508 waitForIt(); 0509 m_changer->setGroupwareCommunication(true); 0510 QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), 0); 0511 QVERIFY(mLastInsertedItem.isValid()); 0512 0513 m_pendingIncidenceChangerSignal = 1; 0514 Incidence::Ptr oldIncidence = Incidence::Ptr(incidence->clone()); 0515 incidence->setSummary(QStringLiteral("the-new-summary")); 0516 int changeId = m_changer->modifyIncidence(mLastInsertedItem, oldIncidence); 0517 QVERIFY(changeId != 1); 0518 waitForIt(); 0519 QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), expectedEmailCount); 0520 break; 0521 } 0522 case IncidenceChanger::ChangeTypeDelete: 0523 // Create if first, so we have something to delete 0524 m_changer->setGroupwareCommunication(false); 0525 m_changer->createIncidence(incidence, mCollection); 0526 waitForIt(); 0527 m_changer->setGroupwareCommunication(true); 0528 QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), 0); 0529 0530 QVERIFY(mLastInsertedItem.isValid()); 0531 m_pendingIncidenceChangerSignal = 1; 0532 m_changer->deleteIncidence(mLastInsertedItem); 0533 waitForIt(); 0534 QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), expectedEmailCount); 0535 break; 0536 default: 0537 Q_ASSERT(false); 0538 } 0539 } 0540 0541 void ITIPHandlerTest::testIdentity_data() 0542 { 0543 QTest::addColumn<QString>("email"); 0544 QTest::addColumn<bool>("expectedResult"); 0545 0546 const QString myEmail = QLatin1StringView(s_ourEmail); 0547 QString myEmail2 = QStringLiteral("Some name <%1>").arg(myEmail); 0548 0549 const QString myAlias1 = QStringLiteral("alias1@kde.org"); // hardcoded in emailidentities, do not change 0550 const QString myIdentity2 = QLatin1StringView(s_outEmail2); 0551 0552 QTest::newRow("Me") << myEmail << true; 0553 QTest::newRow("Also me") << myEmail2 << true; 0554 QTest::newRow("My identity2") << myIdentity2 << true; 0555 QTest::newRow("Not me") << QStringLiteral("laura.palmer@twinpeaks.com") << false; 0556 0557 QTest::newRow("My alias") << myAlias1 << true; 0558 } 0559 0560 void ITIPHandlerTest::testIdentity() 0561 { 0562 QFETCH(QString, email); 0563 QFETCH(bool, expectedResult); 0564 0565 if (CalendarUtils::thatIsMe(email) != expectedResult) { 0566 qDebug() << email; 0567 QVERIFY(false); 0568 } 0569 } 0570 0571 void ITIPHandlerTest::cleanup() 0572 { 0573 const Akonadi::Item::List items = calendarItems(); 0574 for (const Akonadi::Item &item : items) { 0575 auto job = new ItemDeleteJob(item); 0576 AKVERIFYEXEC(job); 0577 } 0578 0579 delete m_itipHandler; 0580 m_itipHandler = nullptr; 0581 } 0582 0583 void ITIPHandlerTest::createITIPHandler() 0584 { 0585 m_itipHandler = new Akonadi::ITIPHandler(new FakeITIPHandlerComponentFactory(this), this); 0586 m_itipHandler->setShowDialogsOnError(false); 0587 connect(m_itipHandler, &ITIPHandler::iTipMessageProcessed, this, &ITIPHandlerTest::oniTipMessageProcessed); 0588 } 0589 0590 QString ITIPHandlerTest::icalData(const QString &data_filename) 0591 { 0592 QString absolutePath = QFINDTESTDATA(QLatin1StringView("itip_data/") + data_filename); 0593 return QString::fromLatin1(readFile(absolutePath)); 0594 } 0595 0596 void ITIPHandlerTest::processItip(const QString &icaldata, 0597 const QString &receiver, 0598 const QString &action, 0599 int expectedNumIncidences, 0600 Akonadi::Item::List &items) 0601 { 0602 items.clear(); 0603 m_pendingItipMessageSignal = 1; 0604 m_itipHandler->processiTIPMessage(receiver, icaldata, action); 0605 waitForIt(); 0606 0607 // 0 e-mails are sent because the status update e-mail is sent by 0608 // kmail's text_calendar.cpp. 0609 QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), 0); 0610 0611 items = calendarItems(); 0612 0613 if (expectedNumIncidences != -1) { 0614 QCOMPARE(items.count(), expectedNumIncidences); 0615 } 0616 } 0617 0618 Attendee ITIPHandlerTest::ourAttendee(const KCalendarCore::Incidence::Ptr &incidence) const 0619 { 0620 const KCalendarCore::Attendee::List attendees = incidence->attendees(); 0621 KCalendarCore::Attendee me; 0622 for (const KCalendarCore::Attendee &attendee : attendees) { 0623 if (attendee.email() == QLatin1StringView(s_ourEmail)) { 0624 me = attendee; 0625 break; 0626 } 0627 } 0628 0629 return me; 0630 } 0631 0632 void ITIPHandlerTest::oniTipMessageProcessed(ITIPHandler::Result result, const QString &errorMessage) 0633 { 0634 if (result != ITIPHandler::ResultSuccess && result != m_expectedResult) { 0635 qDebug() << "ITIPHandlerTest::oniTipMessageProcessed() error = " << errorMessage; 0636 } 0637 0638 m_pendingItipMessageSignal--; 0639 QVERIFY(m_pendingItipMessageSignal >= 0); 0640 if (m_pendingItipMessageSignal == 0) { 0641 stopWaiting(); 0642 } 0643 0644 QCOMPARE(m_expectedResult, result); 0645 } 0646 0647 void ITIPHandlerTest::onCreateFinished(int changeId, const Item &item, IncidenceChanger::ResultCode resultCode, const QString &errorString) 0648 { 0649 Q_UNUSED(changeId) 0650 Q_UNUSED(errorString) 0651 mLastInsertedItem = item; 0652 m_pendingIncidenceChangerSignal--; 0653 QVERIFY(m_pendingIncidenceChangerSignal >= 0); 0654 if (m_pendingIncidenceChangerSignal == 0) { 0655 stopWaiting(); 0656 } 0657 QCOMPARE(resultCode, IncidenceChanger::ResultCodeSuccess); 0658 } 0659 0660 void ITIPHandlerTest::onDeleteFinished(int changeId, 0661 const QList<Akonadi::Item::Id> &deletedIds, 0662 IncidenceChanger::ResultCode resultCode, 0663 const QString &errorString) 0664 { 0665 Q_UNUSED(changeId) 0666 Q_UNUSED(errorString) 0667 Q_UNUSED(deletedIds) 0668 0669 m_pendingIncidenceChangerSignal--; 0670 QVERIFY(m_pendingIncidenceChangerSignal >= 0); 0671 if (m_pendingIncidenceChangerSignal == 0) { 0672 stopWaiting(); 0673 } 0674 QCOMPARE(resultCode, IncidenceChanger::ResultCodeSuccess); 0675 } 0676 0677 void ITIPHandlerTest::onModifyFinished(int changeId, const Item &item, IncidenceChanger::ResultCode resultCode, const QString &errorString) 0678 { 0679 Q_UNUSED(changeId) 0680 Q_UNUSED(errorString) 0681 Q_UNUSED(item) 0682 0683 m_pendingIncidenceChangerSignal--; 0684 QVERIFY(m_pendingIncidenceChangerSignal >= 0); 0685 if (m_pendingIncidenceChangerSignal == 0) { 0686 stopWaiting(); 0687 } 0688 QCOMPARE(resultCode, m_cancelExpected ? IncidenceChanger::ResultCodeUserCanceled : IncidenceChanger::ResultCodeSuccess); 0689 } 0690 0691 QTEST_AKONADIMAIN(ITIPHandlerTest) 0692 0693 #include "moc_itiphandlertest.cpp"