File indexing completed on 2024-05-12 05:52:47

0001 /*
0002  * SPDX-License-Identifier: GPL-3.0-or-later
0003  * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
0004  */
0005 #include "account/account.h"
0006 
0007 #include "../test-utils/output.h"
0008 #include "../../test-utils/spy.h"
0009 
0010 #include <QDateTime>
0011 #include <QFile>
0012 #include <QSignalSpy>
0013 #include <QString>
0014 #include <QTest>
0015 #include <QVector>
0016 #include <QtDebug>
0017 
0018 static QString testIniResource(QLatin1String("test.ini"));
0019 
0020 class StorageDefaultLifeCycleTest: public QObject
0021 {
0022     Q_OBJECT
0023 private Q_SLOTS:
0024     void initTestCase(void);
0025     void testLifecycle(void);
0026 };
0027 
0028 void StorageDefaultLifeCycleTest::initTestCase(void)
0029 {
0030     QVERIFY2(test::ensureOutputDirectory(), "output directory should be available");
0031     QVERIFY2(test::copyResourceAsWritable(QStringLiteral(":/storage-lifecycles/starting.ini"), testIniResource), "test corpus INI resource should be available as file");
0032 }
0033 
0034 void StorageDefaultLifeCycleTest::testLifecycle(void)
0035 {
0036     const QString iniResource = test::path(testIniResource);
0037     const QString samleAccountFullName(QLatin1String("autotests:valid-hotp-sample-1"));
0038 
0039     const accounts::SettingsProvider settings([&iniResource](const accounts::PersistenceAction &action) -> void
0040     {
0041         QSettings data(iniResource, QSettings::IniFormat);
0042         action(data);
0043     });
0044 
0045     accounts::AccountStorage *uut = accounts::AccountStorage::open(settings);
0046     QSignalSpy error(uut, &accounts::AccountStorage::error);
0047     QSignalSpy loaded(uut, &accounts::AccountStorage::loaded);
0048     QSignalSpy accountAdded(uut, &accounts::AccountStorage::added);
0049     QSignalSpy storageDisposed(uut, &accounts::AccountStorage::disposed);
0050     QSignalSpy storageCleaned(uut, &accounts::AccountStorage::destroyed);
0051 
0052     accounts::AccountSecret *secret = uut->secret();
0053     QSignalSpy existingPasswordNeeded(secret, &accounts::AccountSecret::existingPasswordNeeded);
0054     QSignalSpy newPasswordNeeded(secret, &accounts::AccountSecret::newPasswordNeeded);
0055     QSignalSpy passwordAvailable(secret, &accounts::AccountSecret::passwordAvailable);
0056     QSignalSpy keyAvailable(secret, &accounts::AccountSecret::keyAvailable);
0057     QSignalSpy passwordRequestsCancelled(secret, &accounts::AccountSecret::requestsCancelled);
0058     QSignalSpy secretCleaned(secret, &accounts::AccountSecret::destroyed);
0059 
0060     // first phase: check that account objects can be loaded from storage
0061     QCOMPARE(uut->isLoaded(), false);
0062     QCOMPARE(uut->hasError(), false);
0063 
0064     // expect that unlocking is scheduled automatically, so advancing the event loop should trigger the signal
0065     QVERIFY2(test::signal_eventually_emitted_once(existingPasswordNeeded), "(existing) password should be asked by now");
0066     QCOMPARE(newPasswordNeeded.count(), 0);
0067 
0068     QString password(QLatin1String("password"));
0069     secret->answerExistingPassword(password);
0070 
0071     QVERIFY2(test::signal_eventually_emitted_once(passwordAvailable), "(existing) password should have been accepted by now");
0072     QCOMPARE(password, QString(QLatin1String("********")));
0073 
0074     QVERIFY2(test::signal_eventually_emitted_once(keyAvailable, 2500), "key should have been derived by now");
0075 
0076     // expect that loading is scheduled automatically, so advancing the event loop should trigger the signal
0077     QVERIFY2(test::signal_eventually_emitted_once(loaded), "sample account should be loaded by now");
0078     QCOMPARE(uut->isLoaded(), true);
0079     QCOMPARE(uut->hasError(), false);
0080     QCOMPARE(error.count(),  0);
0081     QCOMPARE(accountAdded.count(), 1);
0082     QCOMPARE(accountAdded.at(0).at(0), samleAccountFullName);
0083 
0084     accounts::Account *sampleAccount = uut->get(samleAccountFullName);
0085     QVERIFY2(sampleAccount != nullptr, "get() should return the sample account");
0086 
0087     QSignalSpy sampleAccountCleaned(sampleAccount, &accounts::Account::destroyed);
0088 
0089     // second phase: check that disposing storage cleans up objects properly
0090     uut->dispose();
0091 
0092     QVERIFY2(test::signal_eventually_emitted_once(passwordRequestsCancelled), "account secret should have signalled cancellation by now");
0093     QVERIFY2(test::signal_eventually_emitted_once(storageDisposed), "storage should be disposed of by now");
0094     QVERIFY2(test::signal_eventually_emitted_once(sampleAccountCleaned), "sample account should be cleaned up by now");
0095     QVERIFY2(test::signal_eventually_emitted_once(secretCleaned), "account secret should be cleaned up by now");
0096     QVERIFY2(test::signal_eventually_emitted_once(storageCleaned), "storage should be cleaned up by now");
0097 }
0098 
0099 QTEST_MAIN(StorageDefaultLifeCycleTest)
0100 
0101 #include "storage-default-lifecycle.moc"