@@ 35,6 35,15 @@ ContactRecordInterface::ContactRecordInterface(ContactsDB *db)
auto ContactRecordInterface::Add(ContactRecord &rec) -> bool
{
+ if (rec.numbers.size() > 2) {
+ LOG_WARN("Contact has more than 2 numbers");
+ }
+
+ if (hasContactRecordSameNumbers(rec)) {
+ LOG_ERROR("New record can not have 2 same numbers");
+ return false;
+ }
+
bool result = contactDB->contacts.add(ContactsTableRow{Record(DB_ID_NONE), .speedDial = rec.speeddial});
if (!result) {
return false;
@@ 125,6 134,10 @@ auto ContactRecordInterface::RemoveByID(uint32_t id) -> bool
auto ContactRecordInterface::Update(const ContactRecord &rec) -> bool
{
+ if (rec.numbers.size() > 2) {
+ LOG_WARN("Contact has more than 2 numbers");
+ }
+
ContactsTableRow contact = contactDB->contacts.getByIdWithTemporary(rec.ID);
if (!contact.isValid()) {
return false;
@@ 132,6 145,12 @@ auto ContactRecordInterface::Update(const ContactRecord &rec) -> bool
auto oldNumberIDs = splitNumberIDs(contact.numbersID);
auto newNumbers = rec.numbers;
+
+ if (hasContactRecordSameNumbers(rec)) {
+ LOG_ERROR("Updated record can not have 2 same numbers");
+ return false;
+ }
+
if (!changeNumberRecordInPlaceIfCountryCodeIsOnlyDifferent(oldNumberIDs, newNumbers)) {
return false;
}
@@ 1595,3 1614,24 @@ auto ContactRecordInterface::changeNumberRecordInPlaceIfCountryCodeIsOnlyDiffere
}
return true;
}
+auto ContactRecordInterface::hasContactRecordSameNumbers(const ContactRecord &rec) -> bool
+{
+ if (rec.numbers.size() >= 2) {
+ if (rec.numbers.size() > 2) {
+ LOG_WARN("Contact record has more than 2 numbers. Checking similarity for first 2 numbers only");
+ }
+ utils::PhoneNumber firstPhoneNumber(rec.numbers.at(0).number);
+ utils::PhoneNumber secondPhoneNumber(rec.numbers.at(1).number);
+
+ utils::PhoneNumber::Match matchLevel = firstPhoneNumber.match(secondPhoneNumber);
+
+ switch (matchLevel) {
+ case utils::PhoneNumber::Match::EXACT:
+ case utils::PhoneNumber::Match::POSSIBLE:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
@@ 319,4 319,6 @@ class ContactRecordInterface : public RecordInterface<ContactRecord, ContactReco
*/
auto changeNumberRecordInPlaceIfCountryCodeIsOnlyDifferent(const std::vector<std::uint32_t> &oldNumberIDs,
std::vector<ContactRecord::Number> &newNumbers) -> bool;
+
+ auto hasContactRecordSameNumbers(const ContactRecord &rec) -> bool;
};
@@ 767,7 767,6 @@ TEST_CASE("Check replacement of number in place in db when only different is hav
// Preparation of DB initial state
auto records = ContactRecordInterface(&contactsDb.get());
- ContactRecord testContactRecord;
std::array<std::string, 2> numbers = {{{"500600100"}, {"500600200"}}};
std::array<std::string, 2> numbersPL = {{{"+48500600100"}, {"+48500600200"}}}; // Poland country code
@@ 924,3 923,253 @@ TEST_CASE("Check replacement of number in place in db when only different is hav
REQUIRE(contactsDb.get().number.getById(SECOND_NEW_CONTACT_ID).numberUser == numbersFR[0]);
}
}
+
+TEST_CASE("Check if contact can have 2 similar number (exactly the same or just with/with no country code))")
+{
+ db::tests::DatabaseUnderTest<ContactsDB> contactsDb{"contacts.db", db::tests::getPurePhoneScriptsPath()};
+
+ // Preparation of DB initial state
+ auto records = ContactRecordInterface(&contactsDb.get());
+
+ std::string number = "500600100";
+ std::string numberPL = "+48500600100"; // Poland country code
+ constexpr int32_t FIRST_CONTACT_ID = 1;
+
+ SECTION("Add a new contact with 2 exactly the same number (without country code)")
+ {
+ // Adding of normal contact with 2 exactly the same numbers
+ ContactRecord noTempContactRecordExactlySameNumbers;
+ noTempContactRecordExactlySameNumbers.primaryName = "PrimaryNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.alternativeName = "AlternativeNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.numbers = std::vector<ContactRecord::Number>({
+ ContactRecord::Number(number, std::string(""), ContactNumberType::HOME),
+ ContactRecord::Number(number, std::string(""), ContactNumberType::HOME),
+ });
+
+ REQUIRE(records.Add(noTempContactRecordExactlySameNumbers) ==
+ false); // Try to add contact witch the same numbers
+ REQUIRE(contactsDb.get().number.count() == 0); // There should be no contact
+
+ // No new contact
+ auto validationRecord = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+
+ // No new temporary contact
+ validationRecord = records.GetByIdWithTemporary(FIRST_CONTACT_ID);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+ }
+
+ SECTION("Add a new contact with 2 exactly the same number (with country code)")
+ {
+ // Adding of normal contact with 2 exactly the same numbers
+ ContactRecord noTempContactRecordExactlySameNumbers;
+ noTempContactRecordExactlySameNumbers.primaryName = "PrimaryNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.alternativeName = "AlternativeNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.numbers = std::vector<ContactRecord::Number>({
+ ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME),
+ ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME),
+ });
+
+ REQUIRE(records.Add(noTempContactRecordExactlySameNumbers) ==
+ false); // Try to add contact witch the same numbers
+ REQUIRE(contactsDb.get().number.count() == 0); // There should be no contact
+
+ // No new contact
+ auto validationRecord = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+
+ // No new temporary contact
+ validationRecord = records.GetByIdWithTemporary(FIRST_CONTACT_ID);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+ }
+
+ SECTION("Add a new contact with 2 same numbers but one with country code and one without")
+ {
+ // Adding of normal contact with 2 same numbers
+ ContactRecord noTempContactRecordExactlySameNumbers;
+ noTempContactRecordExactlySameNumbers.primaryName = "PrimaryNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.alternativeName = "AlternativeNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.numbers = std::vector<ContactRecord::Number>({
+ ContactRecord::Number(number, std::string(""), ContactNumberType::HOME),
+ ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME),
+ });
+
+ REQUIRE(records.Add(noTempContactRecordExactlySameNumbers) ==
+ false); // Try to add contact witch the same numbers
+ REQUIRE(contactsDb.get().number.count() == 0); // There should be no contact
+
+ // No new contact
+ auto validationRecord = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+
+ // No new temporary contact
+ validationRecord = records.GetByIdWithTemporary(FIRST_CONTACT_ID);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+ }
+
+ SECTION("Update contact to contact with 2 exactly same numbers (without country code)")
+ {
+ // Adding of normal contact with
+ ContactRecord noTempContactRecordExactlySameNumbers;
+ noTempContactRecordExactlySameNumbers.primaryName = "PrimaryNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.alternativeName = "AlternativeNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.numbers = std::vector<ContactRecord::Number>(
+ {ContactRecord::Number(number, std::string(""), ContactNumberType::HOME)});
+
+ // Add normal valid contact and check it
+ REQUIRE(records.Add(noTempContactRecordExactlySameNumbers) == true);
+ REQUIRE(contactsDb.get().number.count() == 1); // There should be no contact
+ auto normalContactRecord = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(normalContactRecord.numbers.size() == 1);
+ REQUIRE(normalContactRecord.numbers[0].number.getEntered() == number); // without country code
+
+ // Update contact to contact with the same numers
+ auto recordToUpdate = records.GetByID(FIRST_CONTACT_ID);
+ recordToUpdate.numbers = std::vector<ContactRecord::Number>({
+ ContactRecord::Number(number, std::string(""), ContactNumberType::HOME),
+ ContactRecord::Number(number, std::string(""), ContactNumberType::HOME),
+ });
+ REQUIRE(records.Update(recordToUpdate) == false);
+
+ // Check contact record after Update
+ auto recordAfterUpdate = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(recordAfterUpdate.numbers.size() == 1);
+ REQUIRE(recordAfterUpdate.numbers[0].number.getEntered() == number); // without country code
+
+ // No new contact
+ auto validationRecord = records.GetByID(FIRST_CONTACT_ID + 1);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+
+ // No new temporary contact
+ validationRecord = records.GetByIdWithTemporary(FIRST_CONTACT_ID + 1);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+ }
+
+ SECTION("Update contact to contact with 2 exactly same numbers (with country code)")
+ {
+ // Adding of normal contact
+ ContactRecord noTempContactRecordExactlySameNumbers;
+ noTempContactRecordExactlySameNumbers.primaryName = "PrimaryNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.alternativeName = "AlternativeNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.numbers = std::vector<ContactRecord::Number>(
+ {ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME)});
+
+ // Add normal valid contact and check it
+ REQUIRE(records.Add(noTempContactRecordExactlySameNumbers) == true);
+ REQUIRE(contactsDb.get().number.count() == 1); // There should be no contact
+ auto normalContactRecord = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(normalContactRecord.numbers.size() == 1);
+ REQUIRE(normalContactRecord.numbers[0].number.getEntered() == numberPL); // without country code
+
+ // Update contact to contact with the same numbers
+ auto recordToUpdate = records.GetByID(FIRST_CONTACT_ID);
+ recordToUpdate.numbers = std::vector<ContactRecord::Number>({
+ ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME),
+ ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME),
+ });
+ REQUIRE(records.Update(recordToUpdate) == false);
+
+ // Check contact record after Update
+ auto recordAfterUpdate = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(recordAfterUpdate.numbers.size() == 1);
+ REQUIRE(recordAfterUpdate.numbers[0].number.getEntered() == numberPL); // without country code
+
+ // No new contact
+ auto validationRecord = records.GetByID(FIRST_CONTACT_ID + 1);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+
+ // No new temporary contact
+ validationRecord = records.GetByIdWithTemporary(FIRST_CONTACT_ID + 1);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+ }
+
+ SECTION("Update contact to contact with 2 same numbers but one without country code and new with country code")
+ {
+ // Adding of normal contact
+ ContactRecord noTempContactRecordExactlySameNumbers;
+ noTempContactRecordExactlySameNumbers.primaryName = "PrimaryNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.alternativeName = "AlternativeNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.numbers = std::vector<ContactRecord::Number>(
+ {ContactRecord::Number(number, std::string(""), ContactNumberType::HOME)});
+
+ // Add normal valid contact and check it
+ REQUIRE(records.Add(noTempContactRecordExactlySameNumbers) == true);
+ REQUIRE(contactsDb.get().number.count() == 1); // There should be no contact
+ auto normalContactRecord = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(normalContactRecord.numbers.size() == 1);
+ REQUIRE(normalContactRecord.numbers[0].number.getEntered() == number); // without country code
+
+ // Update contact to contact with the same numbers
+ auto recordToUpdate = records.GetByID(FIRST_CONTACT_ID);
+ recordToUpdate.numbers = std::vector<ContactRecord::Number>({
+ ContactRecord::Number(number, std::string(""), ContactNumberType::HOME),
+ ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME),
+ });
+ REQUIRE(records.Update(recordToUpdate) == false);
+
+ // Check contact record after Update
+ auto recordAfterUpdate = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(recordAfterUpdate.numbers.size() == 1);
+ REQUIRE(recordAfterUpdate.numbers[0].number.getEntered() == number); // without country code
+
+ // No new contact
+ auto validationRecord = records.GetByID(FIRST_CONTACT_ID + 1);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+
+ // No new temporary contact
+ validationRecord = records.GetByIdWithTemporary(FIRST_CONTACT_ID + 1);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+ }
+
+ SECTION("Update contact to contact with 2 same numbers but one with country code and new without")
+ {
+ // Adding of normal contact
+ ContactRecord noTempContactRecordExactlySameNumbers;
+ noTempContactRecordExactlySameNumbers.primaryName = "PrimaryNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.alternativeName = "AlternativeNameNoTemporary";
+ noTempContactRecordExactlySameNumbers.numbers = std::vector<ContactRecord::Number>(
+ {ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME)});
+
+ // Add normal valid contact and check it
+ REQUIRE(records.Add(noTempContactRecordExactlySameNumbers) == true);
+ REQUIRE(contactsDb.get().number.count() == 1); // There should be no contact
+ auto normalContactRecord = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(normalContactRecord.numbers.size() == 1);
+ REQUIRE(normalContactRecord.numbers[0].number.getEntered() == numberPL); // without country code
+
+ // Update contact to contact with the same numbers
+ auto recordToUpdate = records.GetByID(FIRST_CONTACT_ID);
+ recordToUpdate.numbers = std::vector<ContactRecord::Number>({
+ ContactRecord::Number(numberPL, std::string(""), ContactNumberType::HOME),
+ ContactRecord::Number(number, std::string(""), ContactNumberType::HOME),
+ });
+ REQUIRE(records.Update(recordToUpdate) == false);
+
+ // Check contact record after Update
+ auto recordAfterUpdate = records.GetByID(FIRST_CONTACT_ID);
+ REQUIRE(recordAfterUpdate.numbers.size() == 1);
+ REQUIRE(recordAfterUpdate.numbers[0].number.getEntered() == numberPL); // without country code
+
+ // No new contact
+ auto validationRecord = records.GetByID(FIRST_CONTACT_ID + 1);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+
+ // No new temporary contact
+ validationRecord = records.GetByIdWithTemporary(FIRST_CONTACT_ID + 1);
+ REQUIRE(validationRecord.ID == DB_ID_NONE);
+ REQUIRE(validationRecord.numbers.empty());
+ }
+}