File indexing completed on 2024-05-12 05:52:46
0001 /* 0002 * SPDX-License-Identifier: GPL-3.0-or-later 0003 * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> 0004 */ 0005 #include "account/actions_p.h" 0006 0007 #include "../test-utils/output.h" 0008 #include "../test-utils/secret.h" 0009 #include "../../test-utils/spy.h" 0010 #include "../../secrets/test-utils/random.h" 0011 0012 #include <QSignalSpy> 0013 #include <QString> 0014 #include <QTest> 0015 #include <QUuid> 0016 #include <QtDebug> 0017 0018 static QString emptyIniResource(QLatin1String("empty-accounts.ini")); 0019 static QString corpusIniResource(QLatin1String("sample-accounts.ini")); 0020 static QString invalidIniResource(QLatin1String("invalid-accounts.ini")); 0021 0022 class LoadAccountsTest: public QObject 0023 { 0024 Q_OBJECT 0025 private Q_SLOTS: 0026 void initTestCase(void); 0027 void emptyAccountsFile(void); 0028 void sampleAccountsFile(void); 0029 void invalidSampleAccountsFile(void); 0030 private: 0031 accounts::AccountSecret m_secret {&test::fakeRandom}; 0032 }; 0033 0034 static QByteArray rawSecret = QByteArray::fromBase64(QByteArray("8juE9gJFLp3OgL4CxJ5v5q8sw+h7Vbn06+NY4uc="), QByteArray::Base64Encoding); 0035 static QByteArray rawNonce = QByteArray::fromBase64(QByteArray("QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB"), QByteArray::Base64Encoding); 0036 0037 static qint64 dummyClock(void) 0038 { 0039 return 1'234'567'890LL; 0040 } 0041 0042 void LoadAccountsTest::initTestCase(void) 0043 { 0044 QVERIFY2(test::ensureOutputDirectory(), "output directory should be available"); 0045 QVERIFY2(test::copyResource(QStringLiteral(":/load-accounts/empty-accounts.ini"), emptyIniResource), "empty INI resource should be available as file"); 0046 QVERIFY2(test::copyResource(QStringLiteral(":/load-accounts/sample-accounts.ini"), corpusIniResource), "test corpus INI resource should be available as file"); 0047 QVERIFY2(test::copyResource(QStringLiteral(":/load-accounts/invalid-accounts.ini"), invalidIniResource), "invalid INI resource should be available as file"); 0048 QVERIFY2(test::useDummyPassword(&m_secret), "should be able to set up the master key"); 0049 } 0050 0051 void LoadAccountsTest::emptyAccountsFile(void) 0052 { 0053 bool actionRun = false; 0054 const accounts::SettingsProvider settings([&actionRun](const accounts::PersistenceAction &action) -> void 0055 { 0056 QSettings data(test::path(emptyIniResource), QSettings::IniFormat); 0057 actionRun = true; 0058 action(data); 0059 }); 0060 0061 accounts::LoadAccounts uut(settings, &m_secret, &dummyClock); 0062 0063 QSignalSpy hotpFound(&uut, &accounts::LoadAccounts::foundHotp); 0064 QSignalSpy totpFound(&uut, &accounts::LoadAccounts::foundTotp); 0065 QSignalSpy loadingError(&uut, &accounts::LoadAccounts::failedToLoadAllAccounts); 0066 QSignalSpy jobFinished(&uut, &accounts::LoadAccounts::finished); 0067 0068 uut.run(); 0069 0070 QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished"); 0071 QVERIFY2(actionRun, "accounts action should have run"); 0072 QCOMPARE(hotpFound.count(), 0); 0073 QCOMPARE(totpFound.count(), 0); 0074 QCOMPARE(loadingError.count(), 0); 0075 } 0076 0077 void LoadAccountsTest::sampleAccountsFile(void) 0078 { 0079 bool actionRun = false; 0080 const accounts::SettingsProvider settings([&actionRun](const accounts::PersistenceAction &action) -> void 0081 { 0082 QSettings data(test::path(corpusIniResource), QSettings::IniFormat); 0083 actionRun = true; 0084 action(data); 0085 }); 0086 0087 accounts::LoadAccounts uut(settings, &m_secret, &dummyClock); 0088 0089 QSignalSpy hotpFound(&uut, &accounts::LoadAccounts::foundHotp); 0090 QSignalSpy totpFound(&uut, &accounts::LoadAccounts::foundTotp); 0091 QSignalSpy loadingError(&uut, &accounts::LoadAccounts::failedToLoadAllAccounts); 0092 QSignalSpy jobFinished(&uut, &accounts::LoadAccounts::finished); 0093 0094 uut.run(); 0095 0096 QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished"); 0097 QVERIFY2(actionRun, "accounts action should have run"); 0098 QCOMPARE(hotpFound.count(), 2); 0099 QCOMPARE(totpFound.count(), 2); 0100 QCOMPARE(loadingError.count(), 0); 0101 0102 const auto firstHotp = hotpFound.at(0); 0103 QCOMPARE(firstHotp.at(0).toUuid(), QUuid(QLatin1String("072a645d-6c26-57cc-81eb-d9ef3b9b39e2"))); 0104 QCOMPARE(firstHotp.at(1).toString(), QLatin1String("valid-hotp-sample-1")); 0105 QCOMPARE(firstHotp.at(2).toString(), QString()); 0106 QCOMPARE(firstHotp.at(3).toByteArray(), rawSecret); 0107 QCOMPARE(firstHotp.at(4).toByteArray(), rawNonce); 0108 QCOMPARE(firstHotp.at(5).toUInt(), 6U); 0109 QCOMPARE(firstHotp.at(6).toULongLong(), 0ULL); 0110 QCOMPARE(firstHotp.at(7).toBool(), false); 0111 QCOMPARE(firstHotp.at(8).toUInt(), 0U); 0112 QCOMPARE(firstHotp.at(9).toBool(), false); 0113 0114 const auto secondHotp = hotpFound.at(1); 0115 QCOMPARE(secondHotp.at(0).toUuid(), QUuid(QLatin1String("437c23aa-2fb0-519a-9a34-a5a2671eea24"))); 0116 QCOMPARE(secondHotp.at(1).toString(), QLatin1String("valid-hotp-sample-2")); 0117 QCOMPARE(secondHotp.at(2).toString(), QLatin1String("autotests")); 0118 QCOMPARE(secondHotp.at(3).toByteArray(), rawSecret); 0119 QCOMPARE(secondHotp.at(4).toByteArray(), rawNonce); 0120 QCOMPARE(secondHotp.at(5).toUInt(), 6U); 0121 QCOMPARE(secondHotp.at(6).toULongLong(), 0ULL); 0122 QCOMPARE(secondHotp.at(7).toBool(), true); 0123 QCOMPARE(secondHotp.at(8).toUInt(), 12U); 0124 QCOMPARE(secondHotp.at(9).toBool(), true); 0125 0126 const auto firstTotp = totpFound.at(0); 0127 QCOMPARE(firstTotp.at(0).toUuid(), QUuid(QLatin1String("534cc72e-e9ec-5e39-a1ff-9f017c9be8cc"))); 0128 QCOMPARE(firstTotp.at(1).toString(), QLatin1String("valid-totp-sample-1")); 0129 QCOMPARE(firstTotp.at(2).toString(), QString()); 0130 QCOMPARE(firstHotp.at(3).toByteArray(), rawSecret); 0131 QCOMPARE(firstHotp.at(4).toByteArray(), rawNonce); 0132 QCOMPARE(firstTotp.at(5).toUInt(), 6); 0133 QCOMPARE(firstTotp.at(6).toUInt(), 30U); 0134 QCOMPARE(firstTotp.at(7).toDateTime(), QDateTime::fromMSecsSinceEpoch(0)); 0135 QCOMPARE(firstTotp.at(8).value<accounts::Account::Hash>(), accounts::Account::Hash::Sha1); 0136 0137 const auto secondTotp = totpFound.at(1); 0138 QCOMPARE(secondTotp.at(0).toUuid(), QUuid(QLatin1String("6537d6a5-005e-5a92-b560-b09df3c2e676"))); 0139 QCOMPARE(secondTotp.at(1).toString(), QLatin1String("valid-totp-sample-2")); 0140 QCOMPARE(secondTotp.at(2).toString(), QLatin1String("autotests")); 0141 QCOMPARE(secondHotp.at(3).toByteArray(), rawSecret); 0142 QCOMPARE(secondHotp.at(4).toByteArray(), rawNonce); 0143 QCOMPARE(secondTotp.at(5).toUInt(), 6U); 0144 QCOMPARE(secondTotp.at(6).toUInt(), 30U); 0145 QCOMPARE(secondTotp.at(7).toDateTime(), QDateTime::fromMSecsSinceEpoch(1'234'567'890LL).toUTC()); 0146 QCOMPARE(secondTotp.at(8).value<accounts::Account::Hash>(), accounts::Account::Hash::Sha256); 0147 } 0148 0149 void LoadAccountsTest::invalidSampleAccountsFile(void) 0150 { 0151 bool actionRun = false; 0152 const accounts::SettingsProvider settings([&actionRun](const accounts::PersistenceAction &action) -> void 0153 { 0154 QSettings data(test::path(invalidIniResource), QSettings::IniFormat); 0155 actionRun = true; 0156 action(data); 0157 }); 0158 0159 accounts::LoadAccounts uut(settings, &m_secret, &dummyClock); 0160 0161 QSignalSpy hotpFound(&uut, &accounts::LoadAccounts::foundHotp); 0162 QSignalSpy totpFound(&uut, &accounts::LoadAccounts::foundTotp); 0163 QSignalSpy loadingError(&uut, &accounts::LoadAccounts::failedToLoadAllAccounts); 0164 QSignalSpy jobFinished(&uut, &accounts::LoadAccounts::finished); 0165 0166 uut.run(); 0167 0168 QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished"); 0169 QVERIFY2(actionRun, "accounts action should have run"); 0170 QCOMPARE(hotpFound.count(), 0); 0171 QCOMPARE(totpFound.count(), 0); 0172 QCOMPARE(loadingError.count(), 1); 0173 } 0174 0175 QTEST_MAIN(LoadAccountsTest) 0176 0177 #include "load-accounts.moc"