File indexing completed on 2024-05-05 05:53:09

0001 /*
0002  * SPDX-License-Identifier: GPL-3.0-or-later
0003  * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
0004  */
0005 #include "app/cli.h"
0006 
0007 #include "../test-utils/spy.h"
0008 
0009 #include <QCoreApplication>
0010 #include <QSignalSpy>
0011 #include <QTest>
0012 #include <QThreadPool>
0013 #include <QtDebug>
0014 
0015 class CommandLineOptionsTest: public QObject // clazy:exclude=ctor-missing-parent-argument
0016 {
0017     Q_OBJECT
0018 private Q_SLOTS:
0019     void initTestCase(void);
0020     void testValidAccountUri(void);
0021     void testInvalidAccountUri(void);
0022     void testInvalidCommandLine(void);
0023 };
0024 
0025 void CommandLineOptionsTest::initTestCase(void)
0026 {
0027     auto threadPool = QThreadPool::globalInstance();
0028     QVERIFY2(threadPool, "should have a global thread pool by now");
0029     threadPool->setMaxThreadCount(3);
0030 }
0031 
0032 static bool prime(QCommandLineParser &parser, const QStringList &argv)
0033 {
0034     app::CommandLineOptions::addOptions(parser);
0035     return parser.parse(argv);
0036 }
0037 
0038 void CommandLineOptionsTest::testValidAccountUri(void)
0039 {
0040     QCommandLineParser parser;
0041     model::AccountInput recipient;
0042     const auto argv = QStringList()
0043         << QStringLiteral("<dummy app>")
0044         << QStringLiteral("otpauth://hotp/issuer:valid?secret=VALUE&digits=8&period=60&issuer=issuer&counter=42&algorithm=sha512");
0045 
0046     app::CommandLineOptions uut(parser, prime(parser, argv));
0047     QSignalSpy invalid(&uut, &app::CommandLineOptions::newAccountInvalid);
0048     QSignalSpy processed(&uut, &app::CommandLineOptions::newAccountProcessed);
0049 
0050     QVERIFY2(uut.newAccountRequested(), "Account URI parsing should be requested");
0051     QVERIFY2(uut.optionsOk(), "Commandline options should be marked as 'ok' (valid)");
0052     QCOMPARE(uut.errorText(), QString());
0053 
0054     uut.handleNewAccount(&recipient);
0055     QVERIFY2(test::signal_eventually_emitted_once(processed), "Account URI should be processed by now");
0056     QCOMPARE(invalid.count(), 0);
0057 
0058     QCOMPARE(recipient.name(), QStringLiteral("valid"));
0059     QCOMPARE(recipient.issuer(), QStringLiteral("issuer"));
0060     QCOMPARE(recipient.counter(), QStringLiteral("42"));
0061     QCOMPARE(recipient.secret(), QStringLiteral("VALUE==="));
0062     QCOMPARE(recipient.tokenLength(), 8U);
0063     QCOMPARE(recipient.timeStep(), 60U);
0064     QCOMPARE(recipient.type(), model::AccountInput::TokenType::Hotp);
0065     QCOMPARE(recipient.algorithm(), model::AccountInput::TOTPAlgorithm::Sha512);
0066 
0067     QVERIFY2(QThreadPool::globalInstance()->waitForDone(500), "the global thread pool should be done by now");
0068 }
0069 
0070 void CommandLineOptionsTest::testInvalidAccountUri(void)
0071 {
0072     QCommandLineParser parser;
0073     model::AccountInput recipient;
0074     const auto argv = QStringList()
0075         << QStringLiteral("<dummy app>")
0076         << QStringLiteral("not a valid otpauth:// URI");
0077 
0078     app::CommandLineOptions uut(parser, prime(parser, argv));
0079     QSignalSpy invalid(&uut, &app::CommandLineOptions::newAccountInvalid);
0080     QSignalSpy processed(&uut, &app::CommandLineOptions::newAccountProcessed);
0081 
0082     QVERIFY2(uut.newAccountRequested(), "Account URI parsing should be requested");
0083     QVERIFY2(uut.optionsOk(), "Commandline options should be marked as 'ok' (valid)");
0084     QCOMPARE(uut.errorText(), QString());
0085 
0086     uut.handleNewAccount(&recipient);
0087     QVERIFY2(test::signal_eventually_emitted_once(invalid), "Account URI should have been rejected by now");
0088     QCOMPARE(processed.count(), 0);
0089 
0090     QCOMPARE(recipient.name(), QString());
0091     QCOMPARE(recipient.issuer(), QString());
0092     QCOMPARE(recipient.counter(), QStringLiteral("0"));
0093     QCOMPARE(recipient.secret(), QString());
0094     QCOMPARE(recipient.tokenLength(), 6U);
0095     QCOMPARE(recipient.timeStep(), 30U);
0096     QCOMPARE(recipient.type(), model::AccountInput::TokenType::Totp);
0097     QCOMPARE(recipient.algorithm(), model::AccountInput::TOTPAlgorithm::Sha1);
0098 
0099     QVERIFY2(QThreadPool::globalInstance()->waitForDone(500), "the global thread pool should be done by now");
0100 }
0101 
0102 void CommandLineOptionsTest::testInvalidCommandLine(void)
0103 {
0104     QCommandLineParser parser;
0105     const auto argv = QStringList()
0106         << QStringLiteral("<dummy app>")
0107         << QStringLiteral("--invalid-option");
0108 
0109     app::CommandLineOptions uut(parser, prime(parser, argv));
0110     QSignalSpy invalid(&uut, &app::CommandLineOptions::newAccountInvalid);
0111     QSignalSpy processed(&uut, &app::CommandLineOptions::newAccountProcessed);
0112 
0113     QVERIFY2(!uut.optionsOk(), "Commandline options should not be marked as 'ok' (invalid)");
0114     QVERIFY2(!uut.errorText().isEmpty(), "Commandline error message should not be empty");
0115     QVERIFY2(!uut.newAccountRequested(), "Account URI parsing should not be requested");
0116 
0117     QCoreApplication::processEvents(QEventLoop::AllEvents, 500);
0118     QCOMPARE(processed.count(), 0);
0119     QCOMPARE(invalid.count(), 0);
0120 
0121     QVERIFY2(QThreadPool::globalInstance()->waitForDone(500), "the global thread pool should be done by now");
0122 }
0123 
0124 QTEST_GUILESS_MAIN(CommandLineOptionsTest)
0125 
0126 #include "commandline-options.moc"