Skip to content

Commit

Permalink
add generating ssn (#397)
Browse files Browse the repository at this point in the history
  • Loading branch information
cieslarmichal authored Jan 2, 2024
1 parent d1000bb commit 18f450f
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 14 deletions.
2 changes: 1 addition & 1 deletion include/faker-cxx/Helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ class Helper
* Helper::replaceSymbolWithNumber("Your pin is: !####") // "29841"
* @endcode
*/
static std::string replaceSymbolWithNumber(std::string str, const char& symbol = '#');
static std::string replaceSymbolWithNumber(const std::string& str, const char& symbol = '#');

/**
* @brief Returns credit card schema with replaced symbols and patterns in a credit card including Luhn checksum
Expand Down
15 changes: 15 additions & 0 deletions include/faker-cxx/Person.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "faker-cxx/types/Country.h"
#include "faker-cxx/types/Sex.h"
#include "faker-cxx/types/SsnCountry.h"

namespace faker
{
Expand Down Expand Up @@ -206,6 +207,20 @@ class Person
*/
static std::string nationality();

/**
* @brief Returns a random SSN.
*
* @param country The optional country to use.
*
* @returns Social Security Number.
*
* @code
* Person::ssn() // "437-12-6854"
* Person::ssn(SsnCountry::Polish) // "95111901567"
* @endcode
*/
static std::string ssn(std::optional<SsnCountry> country);

/**
* @brief Returns a random Western Zodiac
*
Expand Down
45 changes: 45 additions & 0 deletions include/faker-cxx/types/SsnCountry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <vector>

namespace faker
{
enum class SsnCountry
{
Poland,
UnitedStates,
UnitedKingdom,
Germany,
France,
Italy,
Spain,
India,
};

const std::vector<SsnCountry> supportedSsnCountries{
SsnCountry::Poland, SsnCountry::UnitedStates, SsnCountry::UnitedKingdom, SsnCountry::Germany,
SsnCountry::France, SsnCountry::Italy, SsnCountry::Spain, SsnCountry::India,
};

inline std::string toString(SsnCountry country)
{
std::map<SsnCountry, std::string> countryToStringMapping{
{SsnCountry::UnitedStates, "UnitedStates"},
{SsnCountry::UnitedKingdom, "UnitedKingdom"},
{SsnCountry::Poland, "Poland"},
{SsnCountry::Italy, "Italy"},
{SsnCountry::France, "France"},
{SsnCountry::Germany, "Germany"},
{SsnCountry::India, "India"},
{SsnCountry::Spain, "Spain"},
};

return countryToStringMapping.at(country);
}

inline std::ostream& operator<<(std::ostream& os, SsnCountry country)
{
return os << toString(country);
}

}
17 changes: 12 additions & 5 deletions src/modules/helper/Helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,27 @@ std::string Helper::shuffleString(std::string data)
return data;
}

std::string Helper::replaceSymbolWithNumber(std::string str, const char& symbol)
std::string Helper::replaceSymbolWithNumber(const std::string& str, const char& symbol)
{
for (char& ch : str)
std::string result;

for (const auto& ch : str)
{
if (ch == symbol)
{
ch = static_cast<char>(Number::integer(0, 9) + '0');
result += std::to_string(Number::integer(0, 9));
}
else if (ch == '!')
{
ch = static_cast<char>(Number::integer(2, 9) + '0');
result += std::to_string(Number::integer(2, 9));
}
else
{
result += ch;
}
}
return str;

return result;
}

std::string Helper::replaceCreditCardSymbols(const std::string& inputString, char symbol)
Expand Down
44 changes: 40 additions & 4 deletions src/modules/person/Person.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "data/slovakia/SlovakPeopleNames.h"
#include "data/slovenia/SlovenianPeopleNames.h"
#include "data/spain/SpanishPeopleNames.h"
#include "data/SsnFormats.h"
#include "data/sweden/SwedishPeopleNames.h"
#include "data/switzerland/SwissPeopleNames.h"
#include "data/turkey/TurkishPeopleNames.h"
Expand All @@ -61,13 +62,13 @@
#include "data/ZodiacSigns.h"
#include "faker-cxx/Helper.h"
#include "faker-cxx/Internet.h"
#include "faker-cxx/String.h"
#include "faker-cxx/Word.h"

namespace faker
{
namespace
{
const std::vector<std::string> sexes{"Male", "Female"};

const std::map<Country, PeopleNames> countryToPeopleNamesMapping{
{Country::England, englishPeopleNames}, {Country::France, frenchPeopleNames},
{Country::Germany, germanPeopleNames}, {Country::Italy, italianPeopleNames},
Expand Down Expand Up @@ -311,9 +312,11 @@ std::string Person::suffix()

std::string Person::sex(Language language)
{
std::string chosenSex = Helper::arrayElement<std::string>(sexes);
const std::vector<std::string> sexes{"Male", "Female"};

const auto chosenSex = Helper::arrayElement<std::string>(sexes);

Sex sexEnum = chosenSex == "Male" ? Sex::Male : Sex::Female;
const auto sexEnum = chosenSex == "Male" ? Sex::Male : Sex::Female;

return translateSex(sexEnum, language);
}
Expand Down Expand Up @@ -358,6 +361,39 @@ std::string Person::nationality()
return Helper::arrayElement<std::string>(nationalities);
}

std::string Person::ssn(std::optional<SsnCountry> country)
{
const auto ssnCountry = country ? *country : Helper::arrayElement<SsnCountry>(supportedSsnCountries);

const auto& ssnFormat = ssnFormats.at(ssnCountry);

auto ssnWithoutRegexes = Helper::regexpStyleStringParse(ssnFormat);

std::string ssn;

for (const auto& ssnFormatCharacter : ssnWithoutRegexes)
{
if (ssnFormatCharacter == 'L')
{
ssn += String::alpha(1, StringCasing::Upper);
}
else if (ssnFormatCharacter == 'F')
{
ssn += String::alphanumeric(1, StringCasing::Upper);
}
else if (ssnFormatCharacter == '#')
{
ssn += std::to_string(Number::integer(0, 9));
}
else
{
ssn += ssnFormatCharacter;
}
}

return ssn;
}

std::string Person::westernZodiac()
{
return Helper::arrayElement<std::string>(westernZodiacs);
Expand Down
34 changes: 30 additions & 4 deletions src/modules/person/PersonTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,11 +486,11 @@ class PersonSexSuite : public TestWithParam<std::pair<Language, Sex>>

TEST_P(PersonSexSuite, shouldTranslateSexCorrectly)
{
Language language = GetParam().first;
Sex sex = GetParam().second;
const auto language = GetParam().first;
const auto sex = GetParam().second;

std::string expectedTranslation = sexTranslations.at(language).at(sex);
std::string actualTranslation = translateSex(sex, language);
const auto expectedTranslation = sexTranslations.at(language).at(sex);
const auto actualTranslation = translateSex(sex, language);

ASSERT_EQ(expectedTranslation, actualTranslation);
}
Expand Down Expand Up @@ -526,3 +526,29 @@ INSTANTIATE_TEST_SUITE_P(TestPersonSexTranslation, PersonSexSuite, testing::Valu
auto param = info.param;
return toString(param.first) + "_" + toString(param.second);
});

const std::map<SsnCountry, unsigned> ssnLengths{
{SsnCountry::Poland, 11}, {SsnCountry::UnitedStates, 11}, {SsnCountry::UnitedKingdom, 13},
{SsnCountry::Germany, 12}, {SsnCountry::France, 19}, {SsnCountry::Italy, 19},
{SsnCountry::Spain, 10}, {SsnCountry::India, 10},
};

class PersonSsnSuite : public TestWithParam<SsnCountry>
{
};

// TODO: add more precise tests
TEST_P(PersonSsnSuite, shouldGenerateSsn)
{
const auto country = GetParam();

const auto ssn = Person::ssn(country);

const auto expectedSsnLength = ssnLengths.at(country);

ASSERT_EQ(ssn.size(), expectedSsnLength);
}

INSTANTIATE_TEST_SUITE_P(TestPersonSsn, PersonSsnSuite, testing::ValuesIn(supportedSsnCountries),
[](const testing::TestParamInfo<PersonSsnSuite::ParamType>& info)
{ return "shouldGenerate" + toString(info.param) + "Ssn"; });
24 changes: 24 additions & 0 deletions src/modules/person/data/SsnFormats.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <map>
#include <string>
#include <vector>

#include "faker-cxx/types/SsnCountry.h"

namespace faker
{
const std::map<SsnCountry, std::string> ssnFormats{
{SsnCountry::Poland, "##[0-1][0-2][0-2]######"},
{SsnCountry::UnitedStates, "###-##-####"},
// TODO: handle letters
{SsnCountry::UnitedKingdom, "LL ## ## ## L"},
// TODO: handle conditional values like if year starts with 2 then second number must be 0-3
{SsnCountry::Germany, "####[0-2]#[0-1][0-2][1-2][5-9]##"},
{SsnCountry::France, "## [0-1][0-2] [0-2]# ### ### ##"},
// TODO: add alfa-numeric support
{SsnCountry::Italy, "FFFF FFFF FFFF FFFF"},
{SsnCountry::Spain, "X########L"},
{SsnCountry::India, "LLLLL####L"},
};
}

0 comments on commit 18f450f

Please sign in to comment.