Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ set(SOURCES

add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})

target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/inc;include)
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include)
9 changes: 5 additions & 4 deletions app/common/include/constants/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@

namespace constants
{
std::string OK{"OK"};
std::string NOK{"NOK"};

const std::unordered_set<std::string> validTaxNames{
const std::string OK{"OK"};
const std::string NOK{"NOK"};
// clang-format off
const std::unordered_set<std::string> validTaxNames = {
"corporate income tax",
"individual income tax",
"value added tax",
"withholding tax"
"property tax",
"exit tax"
};
// clang-format on
}

#endif //DESIGN_PATTERNS_CONSTANTS_H
17 changes: 17 additions & 0 deletions app/parsers/include/parsers/ValidatedReportParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,21 @@
#ifndef DESIGN_PATTERNS_VALIDATEDREPORTPARSER_H
#define DESIGN_PATTERNS_VALIDATEDREPORTPARSER_H

#include "parsers/IReportParser.h"

#include <type_traits>


namespace parsers
{
template<typename BaseReportParser>
class ValidatedReportParser : public BaseReportParser
{
public:
static_assert(std::is_base_of<IReportParser, BaseReportParser>::value);

std::optional<types::Report> parseReport(const std::string &) const override;
};
}

#endif //DESIGN_PATTERNS_VALIDATEDREPORTPARSER_H
2 changes: 2 additions & 0 deletions app/parsers/include/parsers/XmlParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace parsers
{
class XmlParser : public IReportParser, public ICredentialsParser
{
public:

std::optional<types::Report> parseReport(const std::string &) const override;

std::optional<types::User> parseCredentials(const std::string &) const override;
Expand Down
49 changes: 48 additions & 1 deletion app/parsers/src/ValidatedReportParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,51 @@

#include "constants/Constants.h"
#include "parsers/JsonParser.h"
#include "parsers/XmlParser.h"
#include "parsers/XmlParser.h"

namespace
{
bool validateTaxYear(const types::Report &report)
{
using Clock = std::chrono::system_clock;

const auto now = Clock::now();
const std::time_t now_c = Clock::to_time_t(now);
const struct tm *parts = std::localtime(&now_c);

const auto currentyear = 1900 + parts->tm_year;
return report.year <= currentyear;
}
}

namespace parsers
{
template<typename BaseReportParser>
std::optional<types::Report> ValidatedReportParser<BaseReportParser>::parseReport(const std::string &rawReport)
const try
{
if (const auto report = BaseReportParser::parseReport(rawReport))
{
if (report->amount > 0.0 && validateTaxYear(*report))
{
std::string tax = report->tax;
boost::algorithm::to_lower(tax);
if (constants::validTaxNames.count(tax))
{
return report;
}
}
}
return std::nullopt;
} catch (const std::exception &e)
{
std::cerr << __FILE__ << ' ' << e.what() << '\n';
return std::nullopt;
}

template
class ValidatedReportParser<JsonParser>;

template
class ValidatedReportParser<XmlParser>;
}
2 changes: 2 additions & 0 deletions app/parsers/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ include_directories(${GTEST_INCLUDE_DIR})

set(SOURCES
JsonParser_test.cpp
XmlParser_test.cpp
ValidatedReportParser_tests.cpp
)

add_executable(${PROJECT_NAME} ${SOURCES})
Expand Down
53 changes: 53 additions & 0 deletions app/parsers/test/ValidatedReportParser_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Created by sajith on 6/22/22.
//

#include "parsers/ValidatedReportParser.h"

#include <gtest/gtest.h>
#include <optional>
#include <string>
#include <vector>

#include "constants/Constants.h"
#include "nlohmann/json.hpp"
#include "parsers/JsonParser.h"
#include "parsers/XmlParser.h"
#include "types/Report.h"

using json = nlohmann::json;

namespace parsers
{
struct ValidatedReportParserTest : testing::Test
{
const ValidatedReportParser<JsonParser> sut;
const std::string taxName = *constants::validTaxNames.begin();

const std::string validReport =
nlohmann::to_string(json{{"payer", 1},
{"tax", taxName},
{"amount", 1000},
{"year", 2020}});

const std::vector<std::string> invalidReports = {
to_string(json{{"payer", 1},
{"tax", taxName},
{"amount", 0.0},
{"year", 2020}}),
to_string(json{{"payer", 1},
{"tax", taxName},
{"amount", 1000},
{"year", 2222}}),
to_string(json{{"payer", 1},
{"tax", "Unknown tax"},
{"amount", 0},
{"year", 2020}}),
};
};

TEST_F(ValidatedReportParserTest, reportValidationSucceeds)
{
// ASSERT_NE(sut.parseReport(validReport), std::nullopt);
}
}
75 changes: 75 additions & 0 deletions app/parsers/test/XmlParser_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// Created by sajith on 6/22/22.
//

#include "parsers/XmlParser.h"

#include <gtest/gtest.h>
#include <optional>
#include <string>

#include "types/Report.h"

struct XmlParserTest : testing::Test
{
parsers::XmlParser sut;
};

TEST_F(XmlParserTest, whenValidData_parseReportReturnsReport)
{
const std::string xmlRequest{"<report>"
"<payer>2</payer>"
"<tax>VAT</tax>"
"<amount>10</amount>"
"<year>2020</year>"
"</report>"};

const std::optional<types::Report> parserReport = sut.parseReport(xmlRequest);
const types::Report expectedReport{2, "VAT", 10, 2020};
ASSERT_TRUE(parserReport);
ASSERT_EQ(parserReport, expectedReport);
}



TEST_F(XmlParserTest, whenMissingFields_parseReportReturnsNull)
{
const std::string xmlReport = "<report><payer>2</payer><year>2020</year></report>";
ASSERT_EQ(sut.parseReport(xmlReport), std::nullopt);
}

TEST_F(XmlParserTest, whenEmptyReport_parseReportReturnsNull)
{
const std::string xmlReport = "";
ASSERT_EQ(sut.parseReport(xmlReport), std::nullopt);
}

TEST_F(XmlParserTest, whenInvalidXml_parseReportReturnsNull)
{
const std::string xmlReport = "<<report>>";
ASSERT_EQ(sut.parseReport(xmlReport), std::nullopt);
}

TEST_F(XmlParserTest, whenNumericDataInvalid_parseReportReturnsNull)
{
const std::string xmlReport = "<report><payer>Two</payer><tax>VAT</tax><amount>One"
"</amount><year>Three</year></report>";
ASSERT_EQ(sut.parseReport(xmlReport), std::nullopt);
}

TEST_F(XmlParserTest, whenValidData_parseCredentialsReturnsUser)
{
const std::string xmlReport = "<credentials><login>Jhon Doe</login>"
"<password>123</password></credentials>";
const std::optional<types::User> parsedUser = sut.parseCredentials(xmlReport);
ASSERT_TRUE(parsedUser);
ASSERT_EQ(parsedUser->login.value, std::string("Jhon Doe"));
ASSERT_EQ(parsedUser->password.value, std::string("123"));
}

TEST_F(XmlParserTest, whenMissingFields_parseCredentialsReturnsNull)
{
const std::string xmlReport = "<credentials><login>Jhon Doe</login></credentials>";
const std::optional<types::User> parsedUser = sut.parseCredentials(xmlReport);
ASSERT_EQ(parsedUser, std::nullopt);
}