/*
 * BSD 2-Clause License
 *
 * Copyright (c) 2020, Agnieszka Cicha-Cisek
 * Copyright (c) 2020, Patryk Cisek
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "gtest/gtest.h"

#include <QString>
#include "totpuriparser.h"

struct TotpUriParserParams
{
    TotpUriParserParams(QString uri, QString user,
                        QString secret, QString issuer)
        : uri(uri), user(user), secret(secret), issuer(issuer)
    {
    }
    QString uri;
    QString user;
    QString secret;
    QString issuer;
};

class TotpUrlParserTestForDiffrentUris :
    public testing::TestWithParam<TotpUriParserParams> {

};

INSTANTIATE_TEST_SUITE_P(InputValuesForParserTests,
                         TotpUrlParserTestForDiffrentUris,
                         testing::Values(
                             TotpUriParserParams("otpauth://totp/salsa.debian.org:patryk@debian.org?secret=4gnjo26kcyvymgillrkdiczxbolzg4su&issuer=salsa.debian.org",
                                "patryk@debian.org",
                                "4GNJO26KCYVYMGILLRKDICZXBOLZG4SU",
                                "salsa.debian.org"),
                             TotpUriParserParams("otpauth://totp/GitHub:patryk-testing?secret=qruc63rxey3scal6&issuer=GitHub",
                                "patryk-testing",
                                "QRUC63RXEY3SCAL6",
                                "GitHub"),
                             TotpUriParserParams("otpauth://totp/LinkedIn:fsdu39d@protonmail.com?secret=ZQN4SYDIGT3B2WF4GHKJZINYAVLMF3IV&issuer=LinkedIn",
                                "fsdu39d@protonmail.com",
                                "ZQN4SYDIGT3B2WF4GHKJZINYAVLMF3IV",
                                "LinkedIn"),
                             TotpUriParserParams("otpauth://totp/patryk%40cisek.email?secret=VvTVO-PAsJDPDA YAh3UTMV33-FWKRDO SI5 &issuer=TOTP%20Test%20Application",
                                "patryk@cisek.email",
                                "VVTVOPASJDPDAYAH3UTMV33FWKRDOSI5",
                                "TOTP Test Application"),
                             TotpUriParserParams("otpauth://totp/Amazon%3Apatryk%40cisek.email?secret=RXH3AUXY5MAZSMDHOH5PLA46HHJK4GWMFMULH3UUTZGPUZ6FE7NQ&issuer=Amazon",
                                "patryk@cisek.email",
                                "RXH3AUXY5MAZSMDHOH5PLA46HHJK4GWMFMULH3UUTZGPUZ6FE7NQ",
                                "Amazon"),
                             TotpUriParserParams("otpauth://totp/OVH:cp123456-ovh?secret=XCYQ3HWNJTXLYVPC&image=https://www.ovh.com/images/totp/ovh.png",
                                "cp123456-ovh",
                                "XCYQ3HWNJTXLYVPC",
                                "")
                             ));

TEST_P(TotpUrlParserTestForDiffrentUris, CorrectSecretIsReturned) {
    // Given
    TotpUriParser parser(GetParam().uri);

    // When
    const auto B32_SECRET = parser.getSecret();

    // Then
    ASSERT_EQ(GetParam().secret, B32_SECRET);
}

TEST_P(TotpUrlParserTestForDiffrentUris, CorrectIssuerIsReturned) {
    // Given
    TotpUriParser parser(GetParam().uri);

    // When
    const auto ISSUER = parser.getIssuer();

    // Then
    ASSERT_EQ(GetParam().issuer, ISSUER);
}

TEST_P(TotpUrlParserTestForDiffrentUris, CorrectAccountNameIsReturned) {
    // Given
    TotpUriParser parser(GetParam().uri);

    // When
    const auto ACCOUNT_NAME = parser.getAccountName();

    // Then
    ASSERT_EQ(GetParam().user, ACCOUNT_NAME);
}

TEST(TotpUrlParserTest, NoSecretInUri) {
    // Given
    const QString VALID_QR_CODE_URL =
            "otpauth://totp/patryk%40cisek.email?"
            "issuer=TOTP%20Test%20Application";

    EXPECT_THROW({
        try {
            // When
            TotpUriParser parser(VALID_QR_CODE_URL);

        } catch(const std::runtime_error& e) {
            // Then
            ASSERT_STREQ("NO secret!", e.what());
            throw;
        }
    }, std::runtime_error);
}
TEST(TotpUrlParserTest, NoQueryInUri) {
    // Given
    const QString VALID_QR_CODE_URL =
            "otpauth://totp/patryk%40cisek.email?";

    EXPECT_THROW({
        try {
            // When
            TotpUriParser parser(VALID_QR_CODE_URL);

        } catch(const std::runtime_error& e) {
            // Then
            ASSERT_STREQ("Empty query!", e.what());
            throw;
        }
    }, std::runtime_error);
}

TEST(TotpUrlParserTest, EmptyUrl) {
    // Given
    const QString VALID_QR_CODE_URL = "";

    EXPECT_THROW({
        try {
            // When
            TotpUriParser parser(VALID_QR_CODE_URL);

        } catch(const std::runtime_error& e) {
            // Then
            ASSERT_STREQ("Empty url!", e.what());
            throw;
        }
    }, std::runtime_error);
}

TEST(TotpUrlParserTest, NoAccountNameInUri) {
    // Given
    const QString VALID_QR_CODE_URL =
            "otpauth://totp/?"
            "secret=VVTVOPASJDPDAYAH3UTMV33FWKRDOSI5"
            "&issuer=TOTP%20Test%20Application";

    EXPECT_THROW({
        try {
            // When
            TotpUriParser parser(VALID_QR_CODE_URL);

        } catch(const std::runtime_error& e) {
            // Then
            ASSERT_STREQ("NO user!", e.what());
            throw;
        }
    }, std::runtime_error);
}

TEST(TotpUrlParserTest, WrongScheme) {
    // Given
    const QString VALID_QR_CODE_URL =
            "otpauth://patryk/patryk%40cisek.email?"
            "secret=VVTVOPASJDPDAYAH3UTMV33FWKRDOSI5"
            "&issuer=TOTP%20Test%20Application";

    EXPECT_THROW({
        try {
            // When
            TotpUriParser parser(VALID_QR_CODE_URL);

        } catch(const std::runtime_error& e) {
            // Then
            ASSERT_STREQ("Wrong scheme!", e.what());
            throw;
        }
    }, std::runtime_error);
}

TEST(TotpUrlParserTest, SecretIncompatibleWithPattern) {
    // Given
    const QString VALID_QR_CODE_URL =
            "otpauth://totp/patryk%40cisek.email?"
            "secret=VVTVOPASJDP!*DAYAH3UT^MV33FW&KRDOSI5"
            "&issuer=TOTP%20Test%20Application";

    EXPECT_THROW({
        try {
            // When
            TotpUriParser parser(VALID_QR_CODE_URL);

        } catch(const std::runtime_error& e) {
            // Then
            ASSERT_STREQ("Secret does not look like Base32-encoded value!", e.what());
            throw;
        }
    }, std::runtime_error);
}

