File indexing completed on 2024-05-12 05:13:46

0001 /*
0002     SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "testhelper.h"
0008 
0009 #include "applicationcontroller.h"
0010 #include "reservationmanager.h"
0011 #include "pkpassmanager.h"
0012 
0013 #include <KItinerary/Flight>
0014 #include <KItinerary/Reservation>
0015 
0016 #include <QtTest/qtest.h>
0017 #include <QSignalSpy>
0018 #include <QStandardPaths>
0019 #include <KItinerary/Place>
0020 #include <KItinerary/Visit>
0021 
0022 using namespace KItinerary;
0023 
0024 class ReservationManagerTest : public QObject
0025 {
0026     Q_OBJECT
0027 private Q_SLOTS:
0028     void initTestCase()
0029     {
0030         QStandardPaths::setTestModeEnabled(true);
0031     }
0032 
0033     void testOperations()
0034     {
0035         ReservationManager mgr;
0036         Test::clearAll(&mgr);
0037 
0038         QSignalSpy addSpy(&mgr, &ReservationManager::reservationAdded);
0039         QVERIFY(addSpy.isValid());
0040         QSignalSpy updateSpy(&mgr, &ReservationManager::reservationChanged);
0041         QVERIFY(updateSpy.isValid());
0042         QSignalSpy rmSpy(&mgr, &ReservationManager::reservationRemoved);
0043         QVERIFY(rmSpy.isValid());
0044 
0045         QVERIFY(mgr.batches().empty());
0046         auto ctrl = Test::makeAppController();
0047         ctrl->setReservationManager(&mgr);
0048         ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/data/4U8465-v1.json")));
0049 
0050         auto res = mgr.batches();
0051         QCOMPARE(res.size(), 1);
0052         const auto &resId = res[0];
0053         QVERIFY(!resId.isEmpty());
0054 
0055         QCOMPARE(addSpy.size(), 1);
0056         QCOMPARE(addSpy.at(0).at(0).toString(), resId);
0057         QVERIFY(updateSpy.isEmpty());
0058         QVERIFY(!mgr.reservation(resId).isNull());
0059 
0060         ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/data/4U8465-v2.json")));
0061         QCOMPARE(addSpy.size(), 1);
0062         QCOMPARE(updateSpy.size(), 1);
0063         QCOMPARE(mgr.batches().size(), 1);
0064         QCOMPARE(updateSpy.at(0).at(0).toString(), resId);
0065         QVERIFY(mgr.reservation(resId).isValid());
0066 
0067         mgr.removeReservation(resId);
0068         QCOMPARE(addSpy.size(), 1);
0069         QCOMPARE(updateSpy.size(), 1);
0070         QCOMPARE(rmSpy.size(), 1);
0071         QCOMPARE(rmSpy.at(0).at(0).toString(), resId);
0072         QVERIFY(mgr.batches().empty());
0073         QVERIFY(mgr.reservation(resId).isNull());
0074 
0075         Test::clearAll(&mgr);
0076         auto attraction = KItinerary::TouristAttraction();
0077         attraction.setName(QStringLiteral("Sky Tree"));
0078         auto visit = KItinerary::TouristAttractionVisit();
0079         visit.setTouristAttraction(attraction);
0080 
0081         const auto addedResId = mgr.addReservation(QVariant::fromValue(visit));
0082         QCOMPARE(addedResId, mgr.batches().at(0));
0083 
0084         QCOMPARE(addSpy.size(), 2);
0085         QCOMPARE(mgr.batches().size(), 1);
0086         QCOMPARE(addSpy.at(1).at(0).toString(), addedResId);
0087         QVERIFY(mgr.reservation(addedResId).isValid());
0088     }
0089 
0090     void testPkPassChanges()
0091     {
0092         PkPassManager passMgr;
0093         Test::clearAll(&passMgr);
0094 
0095         ReservationManager mgr;
0096         Test::clearAll(&mgr);
0097 
0098         auto ctrl = Test::makeAppController();
0099         ctrl->setPkPassManager(&passMgr);
0100         ctrl->setReservationManager(&mgr);
0101 
0102         QSignalSpy addSpy(&mgr, &ReservationManager::reservationAdded);
0103         QVERIFY(addSpy.isValid());
0104         QSignalSpy updateSpy(&mgr, &ReservationManager::reservationChanged);
0105         QVERIFY(updateSpy.isValid());
0106 
0107         QVERIFY(mgr.batches().empty());
0108         const auto passId = QStringLiteral("pass.booking.kde.org/MTIzNA==");
0109 
0110         ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/data/boardingpass-v1.pkpass")));
0111         QCOMPARE(addSpy.size(), 1);
0112         QVERIFY(updateSpy.isEmpty());
0113 
0114         ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/data/boardingpass-v2.pkpass")));
0115         QCOMPARE(addSpy.size(), 1);
0116         QCOMPARE(updateSpy.size(), 1);
0117     }
0118 
0119     void testBatchPersistence()
0120     {
0121         ReservationManager mgr;
0122         Test::clearAll(&mgr);
0123         auto ctrl = Test::makeAppController();
0124         ctrl->setReservationManager(&mgr);
0125 
0126         QCOMPARE(mgr.batches().size(), 0);
0127         ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/data/google-multi-passenger-flight.json")));
0128         QCOMPARE(mgr.batches().size(), 2);
0129 
0130         const auto batchId = mgr.batches()[0];
0131         QVERIFY(!batchId.isEmpty());
0132         QCOMPARE(mgr.batchForReservation(batchId), batchId);
0133 
0134         const auto l = mgr.reservationsForBatch(batchId);
0135         QCOMPARE(l.size(), 2);
0136         QVERIFY(!l.at(0).isEmpty());
0137         QVERIFY(!l.at(1).isEmpty());
0138         QVERIFY(l.at(0) != l.at(1));
0139         QVERIFY(l.at(0) == batchId || l.at(1) == batchId);
0140         const auto resId = l.at(0) == batchId ? l.at(1) : l.at(0);
0141         QCOMPARE(mgr.batchForReservation(resId), batchId);
0142         QCOMPARE(mgr.reservationsForBatch(resId), QStringList());
0143 
0144         // recreating the instance should see the same state
0145         ReservationManager mgr2;
0146         QCOMPARE(mgr2.batches().size(), 2);
0147         QCOMPARE(mgr2.batches()[0], batchId);
0148         const auto l2 = mgr2.reservationsForBatch(batchId);
0149         QCOMPARE(l2.size(), 2);
0150         QVERIFY((l2.at(0) == l.at(0) && l2.at(1) == l.at(1)) || (l2.at(0) == l.at(1) && l2.at(1) == l.at(0)));
0151     }
0152 
0153     void testBatchOperations()
0154     {
0155         ReservationManager mgr;
0156         Test::clearAll(&mgr);
0157         QCOMPARE(mgr.batches().size(), 0);
0158 
0159         QSignalSpy batchAddSpy(&mgr, &ReservationManager::batchAdded);
0160         QSignalSpy batchChangeSpy(&mgr, &ReservationManager::batchChanged);
0161         QSignalSpy batchContentSpy(&mgr, &ReservationManager::batchContentChanged);
0162         QSignalSpy batchRenameSpy(&mgr, &ReservationManager::batchRenamed);
0163         QSignalSpy batchRemovedSpy(&mgr, &ReservationManager::batchRemoved);
0164 
0165         auto ctrl = Test::makeAppController();
0166         ctrl->setReservationManager(&mgr);
0167         ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/data/google-multi-passenger-flight.json")));
0168         QCOMPARE(batchAddSpy.size(), 2);
0169         QCOMPARE(batchChangeSpy.size(), 2);
0170         QCOMPARE(batchRenameSpy.size(), 0);
0171         QCOMPARE(batchRemovedSpy.size(), 0);
0172 
0173         const auto batchId = batchAddSpy.at(0).at(0).toString();
0174         const auto l = mgr.reservationsForBatch(batchId);
0175         QCOMPARE(l.size(), 2);
0176 
0177         // changing secondary reservation triggers batch update
0178         const auto secId = l.at(0) == batchId ? l.at(1) : l.at(0);
0179         QCOMPARE(mgr.batchForReservation(secId), batchId);
0180         auto res = mgr.reservation(secId).value<FlightReservation>();
0181         auto flight = res.reservationFor().value<Flight>();
0182         flight.setArrivalTerminal(QStringLiteral("foo"));
0183         res.setReservationFor(flight);
0184 
0185         batchAddSpy.clear();
0186         batchChangeSpy.clear();
0187         mgr.updateReservation(secId, res);
0188         QCOMPARE(batchAddSpy.size(), 0);
0189         QCOMPARE(batchChangeSpy.size(), 0);
0190         QCOMPARE(batchContentSpy.size(), 1);
0191         QCOMPARE(batchRenameSpy.size(), 0);
0192         QCOMPARE(batchRemovedSpy.size(), 0);
0193         QCOMPARE(batchContentSpy.at(0).at(0), batchId);
0194 
0195         // de-batching by update, moving to new batch
0196         flight.setDepartureTime(flight.departureTime().addYears(1));
0197         res.setReservationFor(flight);
0198 
0199         batchAddSpy.clear();
0200         batchContentSpy.clear();
0201         mgr.updateReservation(secId, res);
0202         QCOMPARE(batchAddSpy.size(), 1);
0203         QCOMPARE(batchChangeSpy.size(), 1);
0204         QCOMPARE(batchContentSpy.size(), 0);
0205         QCOMPARE(batchRenameSpy.size(), 0);
0206         QCOMPARE(batchRemovedSpy.size(), 0);
0207         QCOMPARE(batchAddSpy.at(0).at(0), secId);
0208         QCOMPARE(batchChangeSpy.at(0).at(0), batchId);
0209 
0210         // re-batching by update, moving to existing batch
0211         flight.setDepartureTime(flight.departureTime().addYears(-1));
0212         res.setReservationFor(flight);
0213 
0214         batchAddSpy.clear();
0215         batchChangeSpy.clear();
0216         mgr.updateReservation(secId, res);
0217         QCOMPARE(batchAddSpy.size(), 0);
0218         QCOMPARE(batchChangeSpy.size(), 1);
0219         QCOMPARE(batchContentSpy.size(), 0);
0220         QCOMPARE(batchRenameSpy.size(), 0);
0221         QCOMPARE(batchRemovedSpy.size(), 1);
0222         QCOMPARE(batchChangeSpy.at(0).at(0), batchId);
0223         QCOMPARE(batchRemovedSpy.at(0).at(0), secId);
0224 
0225         QCOMPARE(mgr.reservationsForBatch(batchId).size(), 2);
0226         QCOMPARE(mgr.batchForReservation(batchId), batchId);
0227         QCOMPARE(mgr.batchForReservation(secId), batchId);
0228         QCOMPARE(mgr.hasBatch(batchId), true);
0229         QCOMPARE(mgr.hasBatch(secId), false);
0230 
0231         // changing primary does update batch
0232         auto res2 = mgr.reservation(batchId).value<FlightReservation>();
0233         auto flight2 = res.reservationFor().value<Flight>();
0234         flight2.setArrivalTerminal(QStringLiteral("bar"));
0235         res2.setReservationFor(flight2);
0236 
0237         batchChangeSpy.clear();
0238         batchRemovedSpy.clear();
0239         mgr.updateReservation(batchId, res2);
0240         QCOMPARE(batchAddSpy.size(), 0);
0241         QCOMPARE(batchChangeSpy.size(), 0);
0242         QCOMPARE(batchContentSpy.size(), 1);
0243         QCOMPARE(batchRenameSpy.size(), 0);
0244         QCOMPARE(batchRemovedSpy.size(), 0);
0245         QCOMPARE(batchContentSpy.at(0).at(0), batchId);
0246 
0247         // de-batch by changing the primary one renames the batch
0248         flight2.setDepartureTime(flight2.departureTime().addYears(1));
0249         res2.setReservationFor(flight2);
0250 
0251         batchContentSpy.clear();
0252         mgr.updateReservation(batchId, res2);
0253         QCOMPARE(batchAddSpy.size(), 1);
0254         QCOMPARE(batchChangeSpy.size(), 0);
0255         QCOMPARE(batchContentSpy.size(), 0);
0256         QCOMPARE(batchRenameSpy.size(), 1);
0257         QCOMPARE(batchRemovedSpy.size(), 0);
0258         QCOMPARE(batchAddSpy.at(0).at(0), batchId);
0259         QCOMPARE(batchRenameSpy.at(0).at(0), batchId);
0260         QCOMPARE(batchRenameSpy.at(0).at(1), secId);
0261 
0262         QCOMPARE(mgr.batches().size(), 3);
0263         QCOMPARE(mgr.reservationsForBatch(batchId), QStringList(batchId));
0264         QCOMPARE(mgr.reservationsForBatch(secId), QStringList(secId));
0265         QCOMPARE(mgr.batchForReservation(batchId), batchId);
0266         QCOMPARE(mgr.batchForReservation(secId), secId);
0267 
0268         // re-batch again, should be same as before
0269         flight2.setDepartureTime(flight2.departureTime().addYears(-1));
0270         res2.setReservationFor(flight2);
0271 
0272         batchAddSpy.clear();
0273         batchRenameSpy.clear();
0274         mgr.updateReservation(batchId, res2);
0275         QCOMPARE(batchAddSpy.size(), 0);
0276         QCOMPARE(batchChangeSpy.size(), 1);
0277         QCOMPARE(batchContentSpy.size(), 0);
0278         QCOMPARE(batchRenameSpy.size(), 0);
0279         QCOMPARE(batchRemovedSpy.size(), 1);
0280         QCOMPARE(batchChangeSpy.at(0).at(0), secId);
0281         QCOMPARE(batchRemovedSpy.at(0).at(0), batchId);
0282 
0283         QCOMPARE(mgr.batches().size(), 2);
0284         QCOMPARE(mgr.reservationsForBatch(secId).size(), 2);
0285         QCOMPARE(mgr.batchForReservation(batchId), secId);
0286         QCOMPARE(mgr.batchForReservation(secId), secId);
0287 
0288         // removing primary reservation triggers batch rename
0289         batchChangeSpy.clear();
0290         batchRemovedSpy.clear();
0291         mgr.removeReservation(secId);
0292         QCOMPARE(batchAddSpy.size(), 0);
0293         QCOMPARE(batchChangeSpy.size(), 0);
0294         QCOMPARE(batchRenameSpy.size(), 1);
0295         QCOMPARE(batchRemovedSpy.size(), 0);
0296         QCOMPARE(batchRenameSpy.at(0).at(0), secId);
0297         QCOMPARE(batchRenameSpy.at(0).at(1), batchId);
0298 
0299         QCOMPARE(mgr.batches().size(), 2);
0300         QCOMPARE(mgr.reservationsForBatch(batchId).size(), 1);
0301         QCOMPARE(mgr.batchForReservation(batchId), batchId);
0302         QCOMPARE(mgr.batchForReservation(secId), QString());
0303 
0304         // removing the entire batch
0305         batchRenameSpy.clear();
0306         mgr.removeBatch(batchId);
0307         QCOMPARE(batchAddSpy.size(), 0);
0308         QCOMPARE(batchChangeSpy.size(), 0);
0309         QCOMPARE(batchContentSpy.size(), 0);
0310         QCOMPARE(batchRenameSpy.size(), 0);
0311         QCOMPARE(batchRemovedSpy.size(), 1);
0312         QCOMPARE(batchRemovedSpy.at(0).at(0), batchId);
0313 
0314         QCOMPARE(mgr.batches().size(), 1);
0315         QCOMPARE(mgr.reservationsForBatch(batchId).size(), 0);
0316         QCOMPARE(mgr.batchForReservation(batchId), QString());
0317     }
0318 
0319     void testCancellation()
0320     {
0321         ReservationManager mgr;
0322         Test::clearAll(&mgr);
0323         auto ctrl = Test::makeAppController();
0324         ctrl->setReservationManager(&mgr);
0325         QCOMPARE(mgr.batches().size(), 0);
0326         ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/../tests/randa2017.json")));
0327         QCOMPARE(mgr.batches().size(), 11);
0328 
0329         auto res = mgr.reservation(mgr.batches().at(0));
0330         QCOMPARE(JsonLd::convert<Reservation>(res).reservationStatus(), Reservation::ReservationConfirmed);
0331         res = mgr.reservation(mgr.batches().at(10));
0332         QVERIFY(JsonLd::canConvert<Reservation>(res));
0333         QCOMPARE(JsonLd::convert<Reservation>(res).reservationStatus(), Reservation::ReservationConfirmed);
0334 
0335         QSignalSpy batchAddSpy(&mgr, &ReservationManager::batchAdded);
0336         QSignalSpy batchChangeSpy(&mgr, &ReservationManager::batchChanged);
0337         QSignalSpy batchContentSpy(&mgr, &ReservationManager::batchContentChanged);
0338         QSignalSpy batchRenameSpy(&mgr, &ReservationManager::batchRenamed);
0339         QSignalSpy batchRemovedSpy(&mgr, &ReservationManager::batchRemoved);
0340 
0341         ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/../tests/randa2017-flight-cancellation.json")));
0342         QCOMPARE(mgr.batches().size(), 11);
0343         QCOMPARE(batchAddSpy.size(), 0);
0344         QCOMPARE(batchChangeSpy.size(), 0);
0345         QCOMPARE(batchContentSpy.size(), 2);
0346         QCOMPARE(batchRenameSpy.size(), 0);
0347         QCOMPARE(batchRemovedSpy.size(), 0);
0348 
0349         res = mgr.reservation(mgr.batches().at(0));
0350         QVERIFY(JsonLd::canConvert<Reservation>(res));
0351         QCOMPARE(JsonLd::convert<Reservation>(res).reservationStatus(), Reservation::ReservationCancelled);
0352         res = mgr.reservation(mgr.batches().at(10));
0353         QVERIFY(JsonLd::canConvert<Reservation>(res));
0354         QCOMPARE(JsonLd::convert<Reservation>(res).reservationStatus(), Reservation::ReservationCancelled);
0355     }
0356 };
0357 
0358 QTEST_GUILESS_MAIN(ReservationManagerTest)
0359 
0360 #include "reservationmanagertest.moc"