File indexing completed on 2024-05-12 05:17:29

0001 /*
0002     SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "../lib/iata/iatabcbp.h"
0008 #include "../lib/iata/iatabcbpparser.h"
0009 
0010 #include <KItinerary/JsonLdDocument>
0011 #include <KItinerary/Organization>
0012 #include <KItinerary/Place>
0013 
0014 #include <QDebug>
0015 #include <QFile>
0016 #include <QJsonArray>
0017 #include <QJsonDocument>
0018 #include <QObject>
0019 #include <QProcess>
0020 #include <QStandardPaths>
0021 #include <QTest>
0022 
0023 using namespace KItinerary;
0024 
0025 class BcbpParserTest : public QObject
0026 {
0027     Q_OBJECT
0028 private Q_SLOTS:
0029     void testParserValid_data()
0030     {
0031         QTest::addColumn<QString>("message");
0032         QTest::addColumn<QString>("refFile");
0033 
0034         // example data from IATA resolution 792 version 5 Attachment B (with security sections shortended or omitted)
0035         QTest::newRow("single leg, mandatory only") << QStringLiteral("M1DESMARAIS/LUC       EABC123 YULFRAAC 0834 326J001A0025 100") << QStringLiteral("iata-resolution792-example1");
0036         QTest::newRow("single leg, all fields") << QStringLiteral("M1DESMARAIS/LUC       EAB12C3 YULFRAAC 0834 326J003A0027 167>5321WW1325BAC 0014123456002001412346700100141234789012A0141234567890 1AC AC 1234567890123    4PCYLX58Z^108ABCDEFGH") << QStringLiteral("iata-resolution792-example2");
0037         QTest::newRow("single leg, partial") << QStringLiteral("M1GRANDMAIRE/MELANIE  EABC123 GVACDGAF 0123 339C002F0025 130>5002A0571234567890  AF AF 1234567890123456    Y^108ABCDEFGH") << QStringLiteral("iata-resolution792-example3");
0038         QTest::newRow("multi leg, all fields") << QStringLiteral("M2DESMARAIS/LUC       EAB12C3 YULFRAAC 0834 326J003A0027 167>5321WW1325BAC 0014123456002001412346700100141234789012A0141234567890 1AC AC 1234567890123    4PCYLX58ZDEF456 FRAGVALH 3664 327C012C0002 12E2A0140987654321 1AC AC 1234567890123    3PCNWQ^108ABCDEFGH") << QStringLiteral("iata-resolution792-example4");
0039         QTest::newRow("multi leg, partial") << QStringLiteral("M2GRANDMAIRE/MELANIE  EABC123 GVACDGAF 0123 339C002F0025 130>5002A0571234567890  AF AF 1234567890123456    YDEF456 CDGDTWNW 0049 339F001A0002 12C2A012098765432101                       2PC ^108ABCDEFGH") << QStringLiteral("iata-resolution792-example5");
0040 
0041         // EW misses the 'E' eticket marker (BCBP item 253)
0042         QTest::newRow("missing eticket indicator") << QStringLiteral("M1DOE/JOHN             XXX007 BRUTXLEW 8103 035Y012C0030 147>1181W 8033BEW 0000000000000291040000000000 0   LH 123456789012345     ") << QStringLiteral("missing-eticket-indicator");
0043 
0044         // boarding pass issue date (BCBP item 22)
0045         QTest::newRow("issue date") << QStringLiteral("M1DOE/JOHN            EXXX007 TXLBRUSN 2588 034Y023D0999 35D>5181WM7034BSN              2A08200000000000 SN LH 123456789012345      *30600000K0902       ") << QStringLiteral("issue-date");
0046 
0047         // EasyJet being easy on the standard interpretation
0048         QTest::newRow("easyjet") << QStringLiteral("M1DOE/JOHN            EABCDEFGMRSLGWEZY8724 99  3C  506  10Axxxxxxxxxx") << QStringLiteral("easyjet");
0049 
0050         // Brussels Airlines short codes on booking confirmation
0051         QTest::newRow("minimal1") << QStringLiteral("M1DOE/JOHN            EXXX007 TXLBRUSN 2592 110Y") << QStringLiteral("minimal");
0052         QTest::newRow("minimal2") << QStringLiteral("M1DOE/JOHN            EXXX007 TXLBRUSN 2592 110") << QStringLiteral("minimal2");
0053 
0054         // TAP missing boarding pass issue date
0055         QTest::newRow("no issue date") << QStringLiteral("M1DOE/JOHN            EXXX007 LISLCGTP 1080 204Y002D0003 35C>2180      B1A              2904712345678900                           *306      09     BRND") << QStringLiteral("tap-missing-issue-date");
0056 
0057         // Qatar claiming zero-length field sizes
0058         QTest::newRow("zero size conditional") << QStringLiteral("M1DOE/JANE            EXXX007 MXPDOHQR 0128 256Y042F0023 100>2180  0255BBR              2963456000789980                            0") << QStringLiteral("qatar-zero-size-conditional-section");
0059     }
0060 
0061     void testParserValid()
0062     {
0063         QFETCH(QString, message);
0064         QFETCH(QString, refFile);
0065 
0066         QVERIFY(IataBcbp::maybeIataBcbp(message));
0067         QVERIFY(IataBcbp::maybeIataBcbp(message.toUtf8()));
0068         IataBcbp bcbp(message);
0069         QVERIFY(bcbp.isValid());
0070 
0071         QFile ref(QLatin1StringView(SOURCE_DIR "/bcbpdata/") + refFile +
0072                   QLatin1String(".txt"));
0073         QVERIFY(ref.open(QFile::ReadOnly));
0074         const auto refOut = ref.readAll();
0075 
0076         const auto exe = QStandardPaths::findExecutable(QStringLiteral("ticket-barcode-dump"), {QCoreApplication::applicationDirPath()});
0077         QVERIFY(!exe.isEmpty());
0078         QProcess proc;
0079         proc.setProcessChannelMode(QProcess::ForwardedErrorChannel);
0080         proc.setProgram(exe);
0081         proc.setArguments({QLatin1StringView("--context-date"),
0082                            QDate(2018, 4, 2).toString(Qt::ISODate)});
0083         proc.start();
0084         QVERIFY(proc.waitForStarted());
0085         proc.write(message.toUtf8());
0086         QVERIFY(proc.waitForBytesWritten());
0087         proc.closeWriteChannel();
0088         QVERIFY(proc.waitForFinished());
0089         const auto ticketDump = proc.readAllStandardOutput();
0090         QCOMPARE(proc.exitCode(), 0);
0091         if (ticketDump != refOut) {
0092             qDebug().noquote() << ticketDump;
0093             QFile failFile(ref.fileName() + QLatin1StringView(".fail"));
0094             QVERIFY(failFile.open(QFile::WriteOnly));
0095             failFile.write(ticketDump);
0096         }
0097         QVERIFY(ticketDump == refOut);
0098 
0099         QFile f(QLatin1StringView(SOURCE_DIR "/bcbpdata/") + refFile +
0100                 QLatin1String(".json"));
0101         QVERIFY(f.open(QFile::ReadOnly));
0102         const auto refArray = QJsonDocument::fromJson(f.readAll()).array();
0103         QVERIFY(!refArray.isEmpty());
0104 
0105         const auto res = IataBcbpParser::parse(bcbp, QDateTime({2018, 4, 2}, {}));
0106         const auto resJson = JsonLdDocument::toJson(res);
0107 
0108         if (refArray != resJson) {
0109             qWarning().noquote() << QJsonDocument(resJson).toJson();
0110             QFile failFile(f.fileName() + QLatin1StringView(".fail"));
0111             QVERIFY(failFile.open(QFile::WriteOnly));
0112             failFile.write(QJsonDocument(resJson).toJson());
0113         }
0114         QCOMPARE(resJson, refArray);
0115     }
0116 
0117     void testParserInvalid_data()
0118     {
0119         QTest::addColumn<QString>("message");
0120 
0121         QTest::newRow("empty") << QString();
0122         QTest::newRow("too short") << QStringLiteral("M1DESMARAIS/LUC       ");
0123         QTest::newRow("wrong leg count") << QStringLiteral("M2DESMARAIS/LUC       EABC123 YULFRAAC 0834 326J001A0025 100");
0124         QTest::newRow("too short repeated mandatory section") << QStringLiteral("M1DOE/JOHN            EXXX007 TXLBRUSN 2592 11");
0125         QTest::newRow("bug 407895") << QStringLiteral("M2Mobi Lab / ML-586 [In Progress]\nSplit Fooo.Config from main Fooo\n\n========================================================");
0126     }
0127 
0128     void testParserInvalid()
0129     {
0130         QFETCH(QString, message);
0131         IataBcbp bcbp(message);
0132         QVERIFY(!bcbp.isValid());
0133     }
0134 };
0135 
0136 QTEST_GUILESS_MAIN(BcbpParserTest)
0137 
0138 #include "bcbpparsertest.moc"