~aleteoryx/muditaos

1340e6949c0762585b0de844863c91c859fb19b1 — Bartosz 2 years ago c68624e
Revert "[MOS-578] Fix incorrect logic with SMS notifications"

This reverts commit de99e12e000c89088a6d9e79cac3cf2d2e73a90c.
M module-apps/application-messages/ApplicationMessages.cpp => module-apps/application-messages/ApplicationMessages.cpp +18 -20
@@ 175,10 175,10 @@ namespace app
            this, db::Interface::Name::SMSThread, std::make_unique<MarkAsRead>(record->ID, MarkAsRead::Read::True));

        if (record->unreadMsgCount) {
            DBServiceAPI::GetQuery(this,
                                   db::Interface::Name::Notifications,
                                   std::make_unique<notifications::Decrement>(
                                       NotificationsRecord::Key::Sms, record->numberID, record->unreadMsgCount));
            DBServiceAPI::GetQuery(
                this,
                db::Interface::Name::Notifications,
                std::make_unique<notifications::Decrement>(NotificationsRecord::Key::Sms, record->unreadMsgCount));
        }
        return true;
    }


@@ 230,23 230,21 @@ namespace app

        auto query = std::make_unique<ThreadRemove>(record.ID);
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::SMSThread);
        task->setCallback(
            [this, threadId = record.ID, unreadMsgCount = record.unreadMsgCount, numberID = record.numberID](
                auto response) {
                const auto result = dynamic_cast<ThreadRemoveResult *>(response);
                if ((result != nullptr) && result->success()) {
                    if (unreadMsgCount) {
                        DBServiceAPI::GetQuery(this,
                                               db::Interface::Name::Notifications,
                                               std::make_unique<db::query::notifications::Decrement>(
                                                   NotificationsRecord::Key::Sms, numberID, unreadMsgCount));
                    }
                    switchWindow(gui::name::window::main_window);
                    return true;
        task->setCallback([this, threadId = record.ID, unreadMsgCount = record.unreadMsgCount](auto response) {
            const auto result = dynamic_cast<ThreadRemoveResult *>(response);
            if ((result != nullptr) && result->success()) {
                if (unreadMsgCount) {
                    DBServiceAPI::GetQuery(this,
                                           db::Interface::Name::Notifications,
                                           std::make_unique<db::query::notifications::Decrement>(
                                               NotificationsRecord::Key::Sms, unreadMsgCount));
                }
                LOG_ERROR("ThreadRemove id=%" PRIu32 " failed", threadId);
                return false;
            });
                switchWindow(gui::name::window::main_window);
                return true;
            }
            LOG_ERROR("ThreadRemove id=%" PRIu32 " failed", threadId);
            return false;
        });
        task->execute(this, this);
        return true;
    }

M module-apps/application-messages/models/SMSThreadModel.cpp => module-apps/application-messages/models/SMSThreadModel.cpp +4 -4
@@ 150,10 150,10 @@ void SMSThreadModel::markCurrentThreadAsRead()
                db::Interface::Name::SMSThread,
                std::make_unique<db::query::MarkAsRead>(smsThreadID, db::query::MarkAsRead::Read::True));

            DBServiceAPI::GetQuery(application,
                                   db::Interface::Name::Notifications,
                                   std::make_unique<db::query::notifications::Decrement>(
                                       NotificationsRecord::Key::Sms, numberID, unreadMsgCount));
            DBServiceAPI::GetQuery(
                application,
                db::Interface::Name::Notifications,
                std::make_unique<db::query::notifications::Decrement>(NotificationsRecord::Key::Sms, unreadMsgCount));
        }
    }
}

M module-db/Database/Database.cpp => module-db/Database/Database.cpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Database.hpp"


@@ 166,6 166,7 @@ std::unique_ptr<QueryResult> Database::query(const char *format, ...)
    va_start(ap, format);
    sqlite3_vsnprintf(maxQueryLen, queryStatementBuffer, format, ap);
    va_end(ap);

    auto queryResult = std::make_unique<QueryResult>();
    if (const int result = sqlite3_exec(dbConnection, queryStatementBuffer, queryCallback, queryResult.get(), nullptr);
        result != SQLITE_OK) {

M module-db/Interface/NotificationsRecord.cpp => module-db/Interface/NotificationsRecord.cpp +3 -23
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NotificationsRecord.hpp"


@@ 182,7 182,7 @@ std::unique_ptr<db::query::notifications::IncrementResult> NotificationsRecordIn
std::unique_ptr<db::query::notifications::DecrementResult> NotificationsRecordInterface::runQueryImpl(
    const db::query::notifications::Decrement *query)
{
    auto ret = processDecrement(query->getKey(), query->getNumberID(), query->getCount());
    auto ret = processDecrement(query->getKey(), query->getCount());

    return std::make_unique<db::query::notifications::DecrementResult>(ret);
}


@@ 211,7 211,6 @@ std::unique_ptr<db::query::notifications::ClearResult> NotificationsRecordInterf
    if (auto record = GetByKey(query->getKey()); record.isValid()) {
        record.value = 0;
        record.contactRecord.reset();
        unreadMsgNumberIDs.clear();
        ret = Update(record);
    }
    return std::make_unique<db::query::notifications::ClearResult>(ret);


@@ 242,10 241,6 @@ bool NotificationsRecordInterface::processIncrement(NotificationsRecord::Key key
                         numberMatch.value().contactId != currentContactRecord.value().ID) {
                    currentContactRecord.reset();
                }
                if (key == NotificationsRecord::Key::Sms &&
                    std::find(unreadMsgNumberIDs.begin(), unreadMsgNumberIDs.end(), numberMatch.value().numberId) ==
                        unreadMsgNumberIDs.end())
                    unreadMsgNumberIDs.push_back(numberMatch.value().numberId);
            }
            else {
                currentContactRecord.reset();


@@ 261,9 256,7 @@ bool NotificationsRecordInterface::processIncrement(NotificationsRecord::Key key
    return ret;
}

bool NotificationsRecordInterface::processDecrement(const NotificationsRecord::Key key,
                                                    const std::uint32_t numberID,
                                                    const size_t delta)
bool NotificationsRecordInterface::processDecrement(NotificationsRecord::Key key, size_t delta)
{
    auto ret = false;



@@ 271,22 264,9 @@ bool NotificationsRecordInterface::processDecrement(const NotificationsRecord::K
        if (delta >= record.value) {
            record.contactRecord.reset();
            record.value = 0;
            if (key == NotificationsRecord::Key::Sms) {
                unreadMsgNumberIDs.clear();
            }
        }
        else {
            record.value -= delta;
            if (key == NotificationsRecord::Key::Sms) {
                unreadMsgNumberIDs.erase(std::remove_if(unreadMsgNumberIDs.begin(),
                                                        unreadMsgNumberIDs.end(),
                                                        [numberID](std::uint32_t value) { return numberID == value; }),
                                         unreadMsgNumberIDs.end());
                if (unreadMsgNumberIDs.size() == 1) {
                    auto contactRecord   = contactsDb->GetByNumberID(unreadMsgNumberIDs.at(0));
                    record.contactRecord = std::move(contactRecord);
                }
            }
        }

        ret = Update(record);

M module-db/Interface/NotificationsRecord.hpp => module-db/Interface/NotificationsRecord.hpp +2 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 84,7 84,6 @@ class NotificationsRecordInterface : public RecordInterface<NotificationsRecord,
  private:
    NotificationsDB *notificationsDb   = nullptr;
    ContactRecordInterface *contactsDb = nullptr;
    std::vector<std::uint32_t> unreadMsgNumberIDs;

    std::optional<ContactRecord> getContactRecord(uint32_t id) const;
    std::unique_ptr<db::query::notifications::GetResult> runQueryImpl(const db::query::notifications::Get *query);


@@ 99,5 98,5 @@ class NotificationsRecordInterface : public RecordInterface<NotificationsRecord,
    [[nodiscard]] bool processIncrement(NotificationsRecord::Key key,
                                        std::optional<utils::PhoneNumber::View> &&number,
                                        size_t size);
    [[nodiscard]] bool processDecrement(NotificationsRecord::Key key, std::uint32_t numberID, size_t size);
    [[nodiscard]] bool processDecrement(NotificationsRecord::Key key, size_t size);
};

M module-db/queries/notifications/QueryNotificationsDecrement.cpp => module-db/queries/notifications/QueryNotificationsDecrement.cpp +3 -7
@@ 1,12 1,12 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryNotificationsDecrement.hpp"

namespace db::query::notifications
{
    Decrement::Decrement(NotificationsRecord::Key key, size_t numberID, const size_t count)
        : Query(Query::Type::Update), key(key), count(count), numberID(numberID)
    Decrement::Decrement(NotificationsRecord::Key key, const size_t count)
        : Query(Query::Type::Update), key(key), count(count)
    {}

    auto Decrement::getKey() const noexcept -> NotificationsRecord::Key


@@ 22,10 22,6 @@ namespace db::query::notifications
    {
        return "Decrement";
    }
    auto Decrement::getNumberID() const noexcept -> size_t
    {
        return numberID;
    }

    DecrementResult::DecrementResult(bool ret) : ret(ret)
    {}

M module-db/queries/notifications/QueryNotificationsDecrement.hpp => module-db/queries/notifications/QueryNotificationsDecrement.hpp +3 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 15,14 15,13 @@ namespace db::query::notifications
    {
        const NotificationsRecord::Key key;
        const size_t count;
        size_t numberID = 0;

      public:
        Decrement(NotificationsRecord::Key key, size_t numberID, size_t count);
        Decrement(NotificationsRecord::Key key, const size_t count);

        [[nodiscard]] auto getKey() const noexcept -> NotificationsRecord::Key;
        [[nodiscard]] auto getCount() const noexcept -> size_t;
        [[nodiscard]] auto getNumberID() const noexcept -> size_t;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };


M module-db/tests/CMakeLists.txt => module-db/tests/CMakeLists.txt +1 -0
@@ 21,6 21,7 @@ add_catch2_executable(
        MultimediaFilesTable_tests.cpp
        NotesRecord_tests.cpp
        NotesTable_tests.cpp
        NotificationsRecord_tests.cpp
        NotificationsTable_tests.cpp
        QueryInterface.cpp
        SMSRecord_tests.cpp

A module-db/tests/NotificationsRecord_tests.cpp => module-db/tests/NotificationsRecord_tests.cpp +397 -0
@@ 0,0 1,397 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>
#include "Helpers.hpp"
#include <Interface/NotificationsRecord.hpp>
#include <Interface/ContactRecord.hpp>
#include <Database/Database.hpp>
#include "module-db/databases/NotificationsDB.hpp"
#include "module-db/databases/ContactsDB.hpp"
#include <queries/notifications/QueryNotificationsGet.hpp>
#include <queries/notifications/QueryNotificationsIncrement.hpp>
#include <queries/notifications/QueryNotificationsDecrement.hpp>
#include <queries/notifications/QueryNotificationsClear.hpp>
#include <queries/notifications/QueryNotificationsGetAll.hpp>

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>

TEST_CASE("Notifications Record tests")
{

    SECTION("Default Constructor")
    {
        NotificationsRecord testRec;
        REQUIRE_FALSE(testRec.isValid());
        REQUIRE(testRec.ID == DB_ID_NONE);
        REQUIRE(testRec.key == NotificationsRecord::Key::NotValidKey);
        REQUIRE(testRec.value == 0);
        REQUIRE_FALSE(testRec.contactRecord.has_value());
    }

    SECTION("Constructor from NotificationsTableRow")
    {
        NotificationsTableRow tableRow{
            Record(10), .key = static_cast<uint32_t>(NotificationsRecord::Key::Calls), .value = 2};
        NotificationsRecord testRec(tableRow);
        REQUIRE(testRec.isValid());
        REQUIRE(testRec.ID == 10);
        REQUIRE(testRec.key == NotificationsRecord::Key::Calls);
        REQUIRE(testRec.value == 2);
        REQUIRE_FALSE(testRec.contactRecord.has_value());
    }

    db::tests::DatabaseUnderTest<NotificationsDB> notificationsDb{"notifications.db",
                                                                  db::tests::getPurePhoneScriptsPath()};
    db::tests::DatabaseUnderTest<ContactsDB> contactsDb{"contacts.db", db::tests::getPurePhoneScriptsPath()};

    const auto notificationsCount = notificationsDb.get().notifications.count() + 1;
    // clear notifications table
    for (std::size_t id = 1; id <= notificationsCount; id++) {
        REQUIRE(notificationsDb.get().notifications.removeById(id));
    }

    ContactRecordInterface contactRecordInterface(&contactsDb.get());
    NotificationsRecordInterface notificationsRecordInterface(&notificationsDb.get(), &contactRecordInterface);
    REQUIRE(contactRecordInterface.GetCount() == 0);
    REQUIRE(notificationsRecordInterface.GetCount() == 0);

    NotificationsTableRow callsRow{
        Record(DB_ID_NONE), .key = static_cast<uint32_t>(NotificationsRecord::Key::Calls), .value = 0};

    REQUIRE(notificationsDb.get().notifications.add(callsRow));

    NotificationsTableRow smsRow{
        Record(DB_ID_NONE), .key = static_cast<uint32_t>(NotificationsRecord::Key::Sms), .value = 0};

    REQUIRE(notificationsDb.get().notifications.add(smsRow));
    NotificationsRecord testRec;
    auto numberOfNotifcations = notificationsRecordInterface.GetCount();
    REQUIRE(numberOfNotifcations == 2); // calls and sms notifications

    SECTION("Get entry by ID")
    {
        auto callsNotifications = notificationsRecordInterface.GetByID(1);
        REQUIRE(callsNotifications.isValid());
        REQUIRE(callsNotifications.ID == 1);
        REQUIRE(callsNotifications.key == NotificationsRecord::Key::Calls);
        REQUIRE(callsNotifications.value == 0);
        REQUIRE_FALSE(callsNotifications.contactRecord.has_value());

        auto smsNotifications = notificationsRecordInterface.GetByID(2);
        REQUIRE(smsNotifications.isValid());
        REQUIRE(smsNotifications.ID == 2);
        REQUIRE(smsNotifications.key == NotificationsRecord::Key::Sms);
        REQUIRE(smsNotifications.value == 0);
        REQUIRE_FALSE(smsNotifications.contactRecord.has_value());
    }

    SECTION("Get entry by key")
    {
        auto callsNotifications = notificationsRecordInterface.GetByKey(NotificationsRecord::Key::Calls);
        REQUIRE(callsNotifications.isValid());
        REQUIRE(callsNotifications.ID == 1);
        REQUIRE(callsNotifications.key == NotificationsRecord::Key::Calls);
        REQUIRE(callsNotifications.value == 0);

        auto smsNotifications = notificationsRecordInterface.GetByKey(NotificationsRecord::Key::Sms);
        REQUIRE(smsNotifications.isValid());
        REQUIRE(smsNotifications.ID == 2);
        REQUIRE(smsNotifications.key == NotificationsRecord::Key::Sms);
        REQUIRE(smsNotifications.value == 0);
    }

    SECTION("Get entry - invalid ID")
    {
        auto entry = notificationsRecordInterface.GetByID(100);
        REQUIRE_FALSE(entry.isValid());
        REQUIRE(entry.ID == DB_ID_NONE);
        REQUIRE(entry.key == NotificationsRecord::Key::NotValidKey);
        REQUIRE(entry.value == 0);
    }

    SECTION("Get entry by invalid key")
    {
        auto callsNotifications = notificationsRecordInterface.GetByKey(NotificationsRecord::Key::NotValidKey);
        REQUIRE_FALSE(callsNotifications.isValid());
        REQUIRE(callsNotifications.ID == DB_ID_NONE);
        REQUIRE(callsNotifications.key == NotificationsRecord::Key::NotValidKey);
        REQUIRE(callsNotifications.value == 0);

        auto smsNotifications = notificationsRecordInterface.GetByKey(NotificationsRecord::Key::NumberOfKeys);
        REQUIRE_FALSE(smsNotifications.isValid());
        REQUIRE(smsNotifications.ID == DB_ID_NONE);
        REQUIRE(smsNotifications.key == NotificationsRecord::Key::NotValidKey);
        REQUIRE(smsNotifications.value == 0);
    }

    SECTION("Get entries")
    {
        SECTION("Get records using valid offset/limit parameters")
        {
            auto retOffsetLimit = notificationsRecordInterface.GetLimitOffset(0, numberOfNotifcations);
            REQUIRE(retOffsetLimit->size() == numberOfNotifcations);
        }

        SECTION("Get table rows using bigger limit parameters")
        {
            auto retOffsetLimit = notificationsRecordInterface.GetLimitOffset(0, 100);
            REQUIRE(retOffsetLimit->size() == numberOfNotifcations);
        }

        SECTION("Get table rows using invalid offset/limit parameters(should return empty object)")
        {
            auto retOffsetLimit = notificationsRecordInterface.GetLimitOffset(5, 4);
            REQUIRE(retOffsetLimit->size() == 0);
        }

        SECTION("0 - get all")
        {
            auto retOffsetLimit = notificationsRecordInterface.GetLimitOffset(0, 0);
            REQUIRE(retOffsetLimit->size() == numberOfNotifcations);
        }
    }

    SECTION("Entry update value")
    {
        auto entryPre  = notificationsRecordInterface.GetByID(1);
        entryPre.value = entryPre.value + 100;

        REQUIRE(notificationsRecordInterface.Update(entryPre));

        auto entryPost = notificationsRecordInterface.GetByID(1);
        REQUIRE(entryPost.ID == entryPre.ID);
        REQUIRE(entryPost.key == entryPre.key);
        REQUIRE(entryPost.value == entryPre.value);
    }

    SECTION("Entry update key")
    {
        auto entryPre = notificationsRecordInterface.GetByID(1);
        entryPre.key  = NotificationsRecord::Key::Sms;

        REQUIRE_FALSE(notificationsRecordInterface.Update(entryPre));

        auto entryPost = notificationsRecordInterface.GetByID(1);
        REQUIRE(entryPost.ID == entryPre.ID);
        REQUIRE_FALSE(entryPost.key == entryPre.key);
        REQUIRE(entryPost.value == entryPre.value);
    }

    auto getByKey = [&](NotificationsRecord::Key key,
                        uint32_t expectedValue,
                        const std::optional<ContactRecord> &expectedContactRecord = std::nullopt) {
        auto query  = std::make_shared<db::query::notifications::Get>(key);
        auto ret    = notificationsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::notifications::GetResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto record = result->getResult();
        REQUIRE(record.isValid());
        REQUIRE(record.key == key);
        REQUIRE(record.value == expectedValue);
        REQUIRE(record.contactRecord.has_value() == expectedContactRecord.has_value());
        if (expectedContactRecord.has_value()) {
            REQUIRE(record.contactRecord.value().ID == expectedContactRecord.value().ID);
        }
    };

    auto incrementByKey = [&](NotificationsRecord::Key key, const std::string &phoneNumber = "+48500500500") {
        utils::PhoneNumber number(phoneNumber);
        auto query  = std::make_shared<db::query::notifications::Increment>(key, number.getView());
        auto ret    = notificationsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::notifications::IncrementResult *>(ret.get());
        REQUIRE(result != nullptr);
        REQUIRE(result->getResult());
    };

    auto decrementByKey = [&](NotificationsRecord::Key key, size_t count = 1) {
        auto query  = std::make_shared<db::query::notifications::Decrement>(key, count);
        auto ret    = notificationsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::notifications::DecrementResult *>(ret.get());
        REQUIRE(result != nullptr);
        REQUIRE(result->getResult());
    };

    auto clearByKey = [&](NotificationsRecord::Key key) {
        auto query  = std::make_shared<db::query::notifications::Clear>(key);
        auto ret    = notificationsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::notifications::ClearResult *>(ret.get());
        REQUIRE(result != nullptr);
        REQUIRE(result->getResult());
    };

    SECTION("Get via query")
    {
        getByKey(NotificationsRecord::Key::Calls, 0);
        getByKey(NotificationsRecord::Key::Sms, 0);
    }

    SECTION("Increment via query")
    {
        auto incrementAndCheckCase = [&](NotificationsRecord::Key key) {
            getByKey(key, 0);
            incrementByKey(key);
            getByKey(key, 1);
            incrementByKey(key);
            incrementByKey(key);
            getByKey(key, 3);
        };

        incrementAndCheckCase(NotificationsRecord::Key::Calls);
        incrementAndCheckCase(NotificationsRecord::Key::Sms);
    }

    SECTION("Clear via query")
    {
        auto clearCase = [&](NotificationsRecord::Key key) {
            getByKey(key, 0);
            for (int i = 0; i < 100; i++)
                incrementByKey(key);
            getByKey(key, 100);
            clearByKey(key);
            getByKey(key, 0);
        };

        clearCase(NotificationsRecord::Key::Calls);
        clearCase(NotificationsRecord::Key::Sms);
    }

    SECTION("Get All via query")
    {
        auto query  = std::make_shared<db::query::notifications::GetAll>();
        auto ret    = notificationsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::notifications::GetAllResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records->size() == numberOfNotifcations);
    }

    SECTION("Notification DB and Contact DB connection tests")
    {
        const std::string primaryNameTest_1 = "PrimaryTestNameOne";
        const std::string numberUserTest_1  = "600123456";
        ContactRecord testContactRecord_1;
        testContactRecord_1.primaryName = primaryNameTest_1;
        testContactRecord_1.numbers =
            std::vector<ContactRecord::Number>({ContactRecord::Number(utils::PhoneNumber(numberUserTest_1).getView())});

        const std::string primaryNameTest_2 = "PrimaryTestNameTwo";
        const std::string numberUserTest_2  = "600600600";
        ContactRecord testContactRecord_2;
        testContactRecord_2.primaryName = primaryNameTest_2;
        testContactRecord_2.numbers =
            std::vector<ContactRecord::Number>({ContactRecord::Number(utils::PhoneNumber(numberUserTest_2).getView())});

        REQUIRE(contactRecordInterface.Add(testContactRecord_1));
        REQUIRE(contactRecordInterface.Add(testContactRecord_2));

        const auto someUnknownNumber      = "500200300";
        const auto noNotificationExpected = 0;
        const auto contactNotExpected     = std::nullopt;

        SECTION("Tests preconditions")
        {
            getByKey(NotificationsRecord::Key::Calls, noNotificationExpected);
        }

        SECTION("Single call notification from unknown number")
        {
            const auto expectedNotificationValue = 1;
            incrementByKey(NotificationsRecord::Key::Calls, someUnknownNumber);
            getByKey(NotificationsRecord::Key::Calls, expectedNotificationValue, contactNotExpected);
        }

        SECTION("Single call notification from known number")
        {
            const auto expectedNotificationValue = 1;
            incrementByKey(NotificationsRecord::Key::Calls, numberUserTest_1);
            getByKey(NotificationsRecord::Key::Calls, expectedNotificationValue, testContactRecord_1);
            clearByKey(NotificationsRecord::Key::Calls);
            getByKey(NotificationsRecord::Key::Calls, noNotificationExpected, contactNotExpected);
        }

        SECTION("Multiple call notifications from single known number")
        {
            const auto expectedNotificationValue = 3;
            for (int i = 0; i < expectedNotificationValue; i++)
                incrementByKey(NotificationsRecord::Key::Calls, numberUserTest_1);
            getByKey(NotificationsRecord::Key::Calls, expectedNotificationValue, testContactRecord_1);
            clearByKey(NotificationsRecord::Key::Calls);
            getByKey(NotificationsRecord::Key::Calls, noNotificationExpected, contactNotExpected);
        }

        SECTION("Multiple call notifications from multiple known numbers")
        {
            const auto expectedNotificationValue = 2 * 3;
            for (int i = 0; i < expectedNotificationValue / 2; i++) {
                incrementByKey(NotificationsRecord::Key::Calls, numberUserTest_1);
                incrementByKey(NotificationsRecord::Key::Calls, numberUserTest_2);
            }
            getByKey(NotificationsRecord::Key::Calls, expectedNotificationValue, contactNotExpected);
            clearByKey(NotificationsRecord::Key::Calls);
            getByKey(NotificationsRecord::Key::Calls, noNotificationExpected, contactNotExpected);
        }

        SECTION("Multiple call notifications in sequence of known-unknown-known numbers")
        {
            incrementByKey(NotificationsRecord::Key::Calls, numberUserTest_1);
            getByKey(NotificationsRecord::Key::Calls, 1, testContactRecord_1);

            incrementByKey(NotificationsRecord::Key::Calls, someUnknownNumber);
            getByKey(NotificationsRecord::Key::Calls, 2, contactNotExpected);

            incrementByKey(NotificationsRecord::Key::Calls, numberUserTest_1);
            getByKey(NotificationsRecord::Key::Calls, 3, contactNotExpected);
        }

        SECTION("Multiple call notifications in sequence of unknown-known-unknown numbers")
        {
            incrementByKey(NotificationsRecord::Key::Calls, someUnknownNumber);
            getByKey(NotificationsRecord::Key::Calls, 1, contactNotExpected);

            incrementByKey(NotificationsRecord::Key::Calls, numberUserTest_1);
            getByKey(NotificationsRecord::Key::Calls, 2, contactNotExpected);

            incrementByKey(NotificationsRecord::Key::Calls, someUnknownNumber);
            getByKey(NotificationsRecord::Key::Calls, 3, contactNotExpected);
        }

        SECTION("Clear SMS notifications from multiple known numbers")
        {
            const auto expectedNotificationPerContact = 3;
            const auto expectedNotificationValue      = 2 * expectedNotificationPerContact;
            for (int i = 0; i < expectedNotificationValue / 2; i++) {
                incrementByKey(NotificationsRecord::Key::Sms, numberUserTest_1);
                incrementByKey(NotificationsRecord::Key::Sms, numberUserTest_2);
            }
            getByKey(NotificationsRecord::Key::Sms, expectedNotificationValue, contactNotExpected);

            decrementByKey(NotificationsRecord::Key::Sms, expectedNotificationPerContact);
            getByKey(NotificationsRecord::Key::Sms, expectedNotificationPerContact, contactNotExpected);

            decrementByKey(NotificationsRecord::Key::Sms, expectedNotificationPerContact);
            getByKey(NotificationsRecord::Key::Sms, noNotificationExpected, contactNotExpected);
        }

        SECTION("Clear SMS notifications overflowing actual count")
        {
            const auto expectedNotificationPerContact = 3;
            const auto expectedNotificationValue      = 2 * expectedNotificationPerContact;
            for (int i = 0; i < expectedNotificationValue / 2; i++) {
                incrementByKey(NotificationsRecord::Key::Sms, numberUserTest_1);
                incrementByKey(NotificationsRecord::Key::Sms, numberUserTest_2);
            }
            getByKey(NotificationsRecord::Key::Sms, expectedNotificationValue, contactNotExpected);

            decrementByKey(NotificationsRecord::Key::Sms, expectedNotificationPerContact + 1);
            getByKey(NotificationsRecord::Key::Sms, 2, contactNotExpected);

            decrementByKey(NotificationsRecord::Key::Sms, expectedNotificationPerContact);
            getByKey(NotificationsRecord::Key::Sms, noNotificationExpected, contactNotExpected);
        }
    }
}

M pure_changelog.md => pure_changelog.md +0 -1
@@ 65,7 65,6 @@
* Fixed displaying an incorrect window after double ending a phone call
* Fixed templates list not looping
* Fixed inability to import contacts from Orange SIM cards
* Fixed SMS notification behaviour when only one thread is unread
* Fixed improper asterisk button behavior when adding new contact
* Fixed for contacts removed and imported from SIM card once again were added to database without names
* Fixed (removed) redundant leading zero from time representation unless exactly midnight