/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
 */
#include "account/actions_p.h"

#include "../../test-utils/spy.h"
#include "../test-utils/secret.h"

#include <QSignalSpy>
#include <QTest>

class ComputeTotpTest : public QObject
{
    Q_OBJECT
private Q_SLOTS:
    void initTestCase(void);
    void testDefaults(void);
    void testDefaults_data(void);

private:
    accounts::AccountSecret m_secret;
};

// RFC test vector uses the key: 12345678901234567890
static QByteArray rfcSecret("12345678901234567890");

// the RFC test vector consists of 6-character tokens
static uint tokenLength = 6;

// the default TOTP timestep is 30s
static uint timeStep = 30;

// the default TOTP epoch is the Unix epoch
static QDateTime epoch = QDateTime::fromMSecsSinceEpoch(0);

void ComputeTotpTest::initTestCase(void)
{
    QVERIFY2(test::useDummyPassword(&m_secret), "should be able to set up the master key");
}

void ComputeTotpTest::testDefaults(void)
{
    QFETCH(qint64, counter);
    std::function<qint64(void)> clock([counter](void) -> qint64 {
        return counter * timeStep * 1000;
    });

    std::optional<secrets::EncryptedSecret> tokenSecret = test::encrypt(&m_secret, rfcSecret);
    QVERIFY2(tokenSecret, "should be able to encrypt the token secret");

    accounts::ComputeTotp uut(&m_secret, *tokenSecret, tokenLength, epoch, timeStep, accounts::Account::Hash::Sha1, clock);
    QSignalSpy tokenGenerated(&uut, &accounts::ComputeTotp::otp);
    QSignalSpy jobFinished(&uut, &accounts::ComputeTotp::finished);

    uut.run();

    QVERIFY2(test::signal_eventually_emitted_once(tokenGenerated), "token should be generated by now");
    QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished by now");

    QTEST(tokenGenerated.at(0).at(0).toString(), "rfc-test-vector");
    QCOMPARE(tokenGenerated.at(0).at(2).toDateTime(), QDateTime::fromMSecsSinceEpoch(timeStep * 1000 * (counter + 1))); // from
    QCOMPARE(tokenGenerated.at(0).at(3).toDateTime(), QDateTime::fromMSecsSinceEpoch(timeStep * 1000 * (counter + 2))); // until
}

static void define_test_case(int k, const char *expected)
{
    QByteArray output(expected, tokenLength);
    QTest::newRow(qPrintable(QStringLiteral("RFC 4226 test vector, # time steps = %1").arg(k))) << (qint64)k << QString::fromLocal8Bit(output);
}

void ComputeTotpTest::testDefaults_data(void)
{
    static const char *corpus[10]{"755224", "287082", "359152", "969429", "338314", "254676", "287922", "162583", "399871", "520489"};

    QTest::addColumn<qint64>("counter");
    QTest::addColumn<QString>("rfc-test-vector");

    for (int k = 0; k < 10; ++k) {
        define_test_case(k, corpus[k]);
    }
}

QTEST_MAIN(ComputeTotpTest)

#include "compute-totp.moc"
