diff --git a/CMakeLists.txt b/CMakeLists.txt index 50e9be8..28a8c0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,3 +90,88 @@ target_include_directories(wino-mail-qt PRIVATE ${GMIME_INCLUDE_DIRS}) # Install (optional) install(TARGETS wino-mail-qt DESTINATION bin) + +# Enable testing +enable_testing() + +# Find Qt Test +find_package(Qt6 COMPONENTS Test REQUIRED) + +# Unit tests for DAOs +add_executable(tests_dao_unit + tests/unit/test_accountdao.cpp + tests/unit/test_folderdao.cpp + tests/unit/test_mailitemdao.cpp +) + +target_link_libraries(tests_dao_unit + PRIVATE Qt6::Core Qt6::Test +) + +if(USE_DTKWIDGET AND DTKWIDGET_FOUND) + target_link_libraries(tests_dao_unit PRIVATE ${DTKWIDGET_LIBS}) +endif() +if(GMIME_FOUND) + target_link_libraries(tests_dao_unit PRIVATE ${GMIME_LIBRARIES}) + target_include_directories(tests_dao_unit PRIVATE ${GMIME_INCLUDE_DIRS}) +endif() + +add_test(NAME dao_unit COMMAND tests_dao_unit) + +# Unit tests for Translator +add_executable(tests_translator_unit + tests/unit/test_translator.cpp +) + +target_link_libraries(tests_translator_unit + PRIVATE Qt6::Core Qt6::Test +) + +if(USE_DTKWIDGET AND DTKWIDGET_FOUND) + target_link_libraries(tests_translator_unit PRIVATE ${DTKWIDGET_LIBS}) +endif() +if(GMIME_FOUND) + target_link_libraries(tests_translator_unit PRIVATE ${GMIME_LIBRARIES}) + target_include_directories(tests_translator_unit PRIVATE ${GMIME_INCLUDE_DIRS}) +endif() + +add_test(NAME translator_unit COMMAND tests_translator_unit) + +# Unit tests for EventBus +add_executable(tests_eventbus_unit + tests/unit/test_eventbus.cpp +) + +target_link_libraries(tests_eventbus_unit + PRIVATE Qt6::Core Qt6::Test +) + +if(USE_DTKWIDGET AND DTKWIDGET_FOUND) + target_link_libraries(tests_eventbus_unit PRIVATE ${DTKWIDGET_LIBS}) +endif() +if(GMIME_FOUND) + target_link_libraries(tests_eventbus_unit PRIVATE ${GMIME_LIBRARIES}) + target_include_directories(tests_eventbus_unit PRIVATE ${GMIME_INCLUDE_DIRS}) +endif() + +add_test(NAME eventbus_unit COMMAND tests_eventbus_unit) + +# Integration tests (require full application context) +add_executable(tests_sync_integration + tests/integration/test_syncscheduler.cpp + tests/integration/test_notificationmanager.cpp +) + +target_link_libraries(tests_sync_integration + PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Sql Qt6::Test +) + +if(USE_DTKWIDGET AND DTKWIDGET_FOUND) + target_link_libraries(tests_sync_integration PRIVATE ${DTKWIDGET_LIBS}) +endif() +if(GMIME_FOUND) + target_link_libraries(tests_sync_integration PRIVATE ${GMIME_LIBRARIES}) + target_include_directories(tests_sync_integration PRIVATE ${GMIME_INCLUDE_DIRS}) +endif() + +add_test(NAME sync_integration COMMAND tests_sync_integration) diff --git a/tests/integration/test_notificationmanager.cpp b/tests/integration/test_notificationmanager.cpp new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/test_syncscheduler.cpp b/tests/integration/test_syncscheduler.cpp new file mode 100644 index 0000000..2a2ed40 --- /dev/null +++ b/tests/integration/test_syncscheduler.cpp @@ -0,0 +1,20 @@ +#include +#include "../../src/syncscheduler.h" + +class TestSyncScheduler : public QObject +{ + Q_OBJECT +private slots: + void testShouldRunSync(); +}; + +void TestSyncScheduler::testShouldRunSync() +{ + SyncScheduler scheduler; + // This test would need to mock QSettings and QDateTime + // For now, we just verify the object can be created + QVERIFY(true); +} + +QTEST_MAIN(TestSyncScheduler) +#include "test_syncscheduler.moc" diff --git a/tests/unit/test_accountdao.cpp b/tests/unit/test_accountdao.cpp new file mode 100644 index 0000000..3f4d354 --- /dev/null +++ b/tests/unit/test_accountdao.cpp @@ -0,0 +1,106 @@ +#include +#include "../../src/db/dao/accountdao.h" +#include "../../src/db/databasemanager.h" +#include "../../src/core/models/account.h" + +class TestAccountDao : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + void testInsertAndFind(); + void testUpdate(); + void testRemove(); +}; + +void TestAccountDao::initTestCase() +{ + DatabaseManager::instance().openDatabase(":memory:"); + DatabaseManager::instance().createTables(); +} + +void TestAccountDao::cleanupTestCase() +{ + DatabaseManager::instance().closeDatabase(); +} + +void TestAccountDao::testInsertAndFind() +{ + Account account; + account.setEmail("test@example.com"); + account.setProvider("IMAP"); + account.setHost("imap.example.com"); + account.setPort(993); + account.setUsername("testuser"); + account.setPassword("testpass"); + account.setUseSsl(true); + + bool inserted = AccountDao::insert(account); + QVERIFY(inserted); + QVERIFY(account.id() > 0); + + Account found = AccountDao::findById(account.id()); + QVERIFY(found.isValid()); + QCOMPARE(found.email(), QString("test@example.com")); + QCOMPARE(found.provider(), QString("IMAP")); + QCOMPARE(found.host(), QString("imap.example.com")); + QCOMPARE(found.port(), 993); + QCOMPARE(found.username(), QString("testuser")); + QCOMPARE(found.password(), QString("testpass")); + QVERIFY(found.useSsl()); +} + +void TestAccountDao::testUpdate() +{ + Account account; + account.setEmail("original@example.com"); + account.setProvider("IMAP"); + account.setHost("imap.example.com"); + account.setPort(993); + account.setUsername("user"); + account.setPassword("pass"); + account.setUseSsl(true); + + bool inserted = AccountDao::insert(account); + QVERIFY(inserted); + qint64 id = account.id(); + + account.setEmail("updated@example.com"); + account.setProvider("Gmail"); + account.setUseSsl(false); + + bool updated = AccountDao::update(account); + QVERIFY(updated); + + Account found = AccountDao::findById(id); + QVERIFY(found.isValid()); + QCOMPARE(found.email(), QString("updated@example.com")); + QCOMPARE(found.provider(), QString("Gmail")); + QVERIFY(!found.useSsl()); +} + +void TestAccountDao::testRemove() +{ + Account account; + account.setEmail("todelete@example.com"); + account.setProvider("IMAP"); + account.setHost("imap.example.com"); + account.setPort(993); + account.setUsername("user"); + account.setPassword("pass"); + account.setUseSsl(true); + + bool inserted = AccountDao::insert(account); + QVERIFY(inserted); + qint64 id = account.id(); + + bool removed = AccountDao::remove(id); + QVERIFY(removed); + + Account found = AccountDao::findById(id); + QVERIFY(!found.isValid()); +} + +QTEST_MAIN(TestAccountDao) +#include "test_accountdao.moc" diff --git a/tests/unit/test_eventbus.cpp b/tests/unit/test_eventbus.cpp new file mode 100644 index 0000000..225173c --- /dev/null +++ b/tests/unit/test_eventbus.cpp @@ -0,0 +1,34 @@ +#include +#include "../../src/core/eventbus.h" +#include "../../src/core/events.h" + +class TestEventBus : public QObject +{ + Q_OBJECT +private slots: + void testPublishAndSubscribe(); +}; + +void TestEventBus::testPublishAndSubscribe() +{ + bool received = false; + int value = 0; + + // Subscribe to an event of type int + auto subscription = EventBus::instance().subscribe([&](int data) { + received = true; + value = data; + }); + + // Publish an event + EventBus::instance().publish(42); + + // Check that we received the event + QVERIFY(received); + QCOMPARE(value, 42); + + // Unsubscribe (optional, subscription goes out of scope) +} + +QTEST_MAIN(TestEventBus) +#include "test_eventbus.moc" diff --git a/tests/unit/test_folderdao.cpp b/tests/unit/test_folderdao.cpp new file mode 100644 index 0000000..6c7ebd1 --- /dev/null +++ b/tests/unit/test_folderdao.cpp @@ -0,0 +1,92 @@ +#include +#include "../../src/db/dao/folderdao.h" +#include "../../src/db/databasemanager.h" +#include "../../src/core/models/folder.h" + +class TestFolderDao : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + void testInsertAndFind(); + void testUpdate(); + void testRemove(); +}; + +void TestFolderDao::initTestCase() +{ + DatabaseManager::instance().openDatabase(":memory:"); + DatabaseManager::instance().createTables(); +} + +void TestFolderDao::cleanupTestCase() +{ + DatabaseManager::instance().closeDatabase(); +} + +void TestFolderDao::testInsertAndFind() +{ + Folder folder; + folder.setAccountId(1); + folder.setName("Inbox"); + folder.setPath("INBOX"); + folder.setIsInbox(true); + + bool inserted = FolderDao::insert(folder); + QVERIFY(inserted); + QVERIFY(folder.id() > 0); + + Folder found = FolderDao::findById(folder.id()); + QVERIFY(found.isValid()); + QCOMPARE(found.accountId(), 1); + QCOMPARE(found.name(), QString("Inbox")); + QCOMPARE(found.path(), QString("INBOX")); + QVERIFY(found.isInbox()); +} + +void TestFolderDao::testUpdate() +{ + Folder folder; + folder.setAccountId(1); + folder.setName("Drafts"); + folder.setPath("Drafts"); + folder.setIsInbox(false); + + bool inserted = FolderDao::insert(folder); + QVERIFY(inserted); + qint64 id = folder.id(); + + folder.setName("Updated Drafts"); + folder.setPath("Updated/Drafts"); + + bool updated = FolderDao::update(folder); + QVERIFY(updated); + + Folder found = FolderDao::findById(id); + QVERIFY(found.isValid()); + QCOMPARE(found.name(), QString("Updated Drafts")); + QCOMPARE(found.path(), QString("Updated/Drafts")); +} + +void TestFolderDao::testRemove() +{ + Folder folder; + folder.setAccountId(1); + folder.setName("ToDelete"); + folder.setPath("ToDelete"); + folder.setIsInbox(false); + + bool inserted = FolderDao::insert(folder); + QVERIFY(inserted); + qint64 id = folder.id(); + + bool removed = FolderDao::remove(id); + QVERIFY(removed); + + Folder found = FolderDao::findById(id); + QVERIFY(!found.isValid()); +} + +QTEST_MAIN(TestFolderDao) +#include "test_folderdao.moc" diff --git a/tests/unit/test_mailitemdao.cpp b/tests/unit/test_mailitemdao.cpp new file mode 100644 index 0000000..f82cf7f --- /dev/null +++ b/tests/unit/test_mailitemdao.cpp @@ -0,0 +1,128 @@ +#include +#include "../../src/db/dao/mailitemdao.h" +#include "../../src/db/databasemanager.h" +#include "../../src/core/models/mailitem.h" +#include "../../src/core/models/account.h" +#include "../../src/core/models/folder.h" + +class TestMailItemDao : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + void testInsertAndFind(); + void testUpdate(); + void testRemove(); +}; + +void TestMailItemDao::initTestCase() +{ + DatabaseManager::instance().openDatabase(":memory:"); + DatabaseManager::instance().createTables(); + // We need an account and folder for the mailitem + Account account; + account.setEmail("test@example.com"); + account.setProvider("IMAP"); + account.setHost("imap.example.com"); + account.setPort(993); + account.setUsername("testuser"); + account.setPassword("testpass"); + account.setUseSsl(true); + bool accountInserted = AccountDao::insert(account); + QVERIFY(accountInserted); + + Folder folder; + folder.setAccountId(account.id()); + folder.setName("Inbox"); + folder.setPath("INBOX"); + folder.setIsInbox(true); + bool folderInserted = FolderDao::insert(folder); + QVERIFY(folderInserted); +} + +void TestMailItemDao::cleanupTestCase() +{ + DatabaseManager::instance().closeDatabase(); +} + +void TestMailItemDao::testInsertAndFind() +{ + MailItem item; + item.setAccountId(1); // from account inserted in init + item.setFolderId(1); // from folder inserted in init + item.setSubject("Test Subject"); + item.setFrom("sender@example.com"); + item.setTo("receiver@example.com"); + item.setDate(QDateTime::currentDateTimeUtc()); + item.setContent("Test content"); + item.setUid("12345"); + + bool inserted = MailItemDao::insert(item); + QVERIFY(inserted); + QVERIFY(item.id() > 0); + + MailItem found = MailItemDao::findById(item.id()); + QVERIFY(found.isValid()); + QCOMPARE(found.accountId(), 1); + QCOMPARE(found.folderId(), 1); + QCOMPARE(found.subject(), QString("Test Subject")); + QCOMPARE(found.from(), QString("sender@example.com")); + QCOMPARE(found.to(), QString("receiver@example.com")); + QCOMPARE(found.content(), QString("Test content")); + QCOMPARE(found.uid(), QString("12345")); +} + +void TestMailItemDao::testUpdate() +{ + MailItem item; + item.setAccountId(1); + item.setFolderId(1); + item.setSubject("Original Subject"); + item.setFrom("sender@example.com"); + item.setTo("receiver@example.com"); + item.setDate(QDateTime::currentDateTimeUtc()); + item.setContent("Original content"); + item.setUid("12345"); + + bool inserted = MailItemDao::insert(item); + QVERIFY(inserted); + qint64 id = item.id(); + + item.setSubject("Updated Subject"); + item.setContent("Updated content"); + + bool updated = MailItemDao::update(item); + QVERIFY(updated); + + MailItem found = MailItemDao::findById(id); + QVERIFY(found.isValid()); + QCOMPARE(found.subject(), QString("Updated Subject")); + QCOMPARE(found.content(), QString("Updated content")); +} + +void TestMailItemDao::testRemove() +{ + MailItem item; + item.setAccountId(1); + item.setFolderId(1); + item.setSubject("To Delete"); + item.setFrom("sender@example.com"); + item.setTo("receiver@example.com"); + item.setDate(QDateTime::currentDateTimeUtc()); + item.setContent("To be deleted"); + item.setUid("12345"); + + bool inserted = MailItemDao::insert(item); + QVERIFY(inserted); + qint64 id = item.id(); + + bool removed = MailItemDao::remove(id); + QVERIFY(removed); + + MailItem found = MailItemDao::findById(id); + QVERIFY(!found.isValid()); +} + +QTEST_MAIN(TestMailItemDao) +#include "test_mailitemdao.moc" diff --git a/tests/unit/test_translator.cpp b/tests/unit/test_translator.cpp new file mode 100644 index 0000000..def797e --- /dev/null +++ b/tests/unit/test_translator.cpp @@ -0,0 +1,47 @@ +#include +#include "../../src/core/translator.h" + +class TestTranslator : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void testLoadLanguage(); + void testTranslation(); + void testCleanup(); +}; + +void TestTranslator::initTestCase() +{ + // Ensure we start with a clean state (singleton may persist) + // Since Translator is a singleton, we can reload language. + Translator& translator = Translator::instance(); + translator.loadLanguage("en_US"); +} + +void TestTranslator::testLoadLanguage() +{ + Translator& translator = Translator::instance(); + QVERIFY(translator.loadLanguage("en_US")); + QCOMPARE(translator.currentLanguage(), QString("en_US")); +} + +void TestTranslator::testTranslation() +{ + Translator& translator = Translator::instance(); + // Test known keys + QCOMPARE(translator.tr("appName"), QString("Wino Mail")); + QCOMPARE(translator.tr("welcomeMessage"), QString("Welcome to Wino Mail")); + QCOMPARE(translator.tr("inbox"), QString("Inbox")); + QCOMPARE(translator.tr("settings"), QString("Settings")); + // Test unknown key returns the key itself + QCOMPARE(translator.tr("unknown.key"), QString("unknown.key")); +} + +void TestTranslator::testCleanup() +{ + // Nothing to clean up +} + +QTEST_MAIN(TestTranslator) +#include "test_translator.moc"