diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-02 17:21:47 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-02 17:21:47 +0200 |
| commit | b61ce428d81871fbbc6261f274f20785db9d1c38 (patch) | |
| tree | b1ebcf1da244a31ec1c991be42d0062d776ba1b4 /tests/test_config.cpp | |
| parent | db1eb37b26f9ac14be4e1a4e5edbf5b6e77f8db7 (diff) | |
| download | qtmaildir-b61ce428d81871fbbc6261f274f20785db9d1c38.tar.gz qtmaildir-b61ce428d81871fbbc6261f274f20785db9d1c38.zip | |
feat: add Config with account, query, and sync parsing
Accounts use [account.work] rather than [account/work]: QSettings' INI
backend treats "/" as its own hierarchical group separator, so a literal
slash in a section header parses as a nested group and trips
QSettings::FormatError, silently breaking childGroups() enumeration. A
dot carries no such meaning and keeps the format flat.
Saved-query order is alphabetical (QSettings::childKeys() sorts), not
file order; documented in code and tests rather than left to a false
assumption.
Diffstat (limited to 'tests/test_config.cpp')
| -rw-r--r-- | tests/test_config.cpp | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/tests/test_config.cpp b/tests/test_config.cpp new file mode 100644 index 0000000..0caa93c --- /dev/null +++ b/tests/test_config.cpp @@ -0,0 +1,132 @@ +#include <QtTest> +#include <QTemporaryDir> +#include <QSettings> +#include "config.h" + +class TestConfig : public QObject +{ + Q_OBJECT +private slots: + void parsesAccounts(); + void parsesSavedQueries(); + void missingSyncCommandIsEmpty(); + void accountWithoutMaildirIsRejected(); + void scopedQueryWrapsCorrectly(); +}; + +static QString writeIni(const QTemporaryDir &dir, const QString &body) +{ + const QString path = dir.filePath(QStringLiteral("qtmaildir.conf")); + QFile f(path); + f.open(QIODevice::WriteOnly | QIODevice::Text); + f.write(body.toUtf8()); + f.close(); + return path; +} + +void TestConfig::parsesAccounts() +{ + QTemporaryDir dir; + const QString path = writeIni(dir, QStringLiteral( + "[account.work]\n" + "name=Test User\n" + "address=user@example.org\n" + "maildir=work-mail\n" + "drafts=Drafts\n" + "\n" + "[account.personal]\n" + "name=Test User\n" + "address=me@example.net\n" + "maildir=personal\n" + )); + + Config config; + config.load(path); + + QCOMPARE(config.accounts().size(), 2); + + const Account work = config.account(QStringLiteral("work")); + QCOMPARE(work.key, QStringLiteral("work")); + QCOMPARE(work.name, QStringLiteral("Test User")); + QCOMPARE(work.address, QStringLiteral("user@example.org")); + QCOMPARE(work.maildir, QStringLiteral("work-mail")); + QCOMPARE(work.drafts, QStringLiteral("Drafts")); + + // drafts is optional in v1 (send is v2). + const Account personal = config.account(QStringLiteral("personal")); + QVERIFY(personal.drafts.isEmpty()); + QVERIFY(personal.isValid()); +} + +void TestConfig::parsesSavedQueries() +{ + QTemporaryDir dir; + const QString path = writeIni(dir, QStringLiteral( + "[queries]\n" + "Inbox=tag:inbox\n" + "Unread=tag:unread\n" + )); + + Config config; + config.load(path); + + const QList<SavedQuery> queries = config.savedQueries(); + QCOMPARE(queries.size(), 2); + // QSettings::childKeys() returns keys alphabetically, not in file order, + // so the UI button order is alphabetical. This assertion happens to hold + // either way since "Inbox" < "Unread", but the ordering guarantee is + // alphabetical, not "follows the file". + QCOMPARE(queries.at(0).name, QStringLiteral("Inbox")); + QCOMPARE(queries.at(0).query, QStringLiteral("tag:inbox")); +} + +void TestConfig::missingSyncCommandIsEmpty() +{ + QTemporaryDir dir; + const QString path = writeIni(dir, QStringLiteral("[general]\n")); + + Config config; + config.load(path); + + QVERIFY(config.syncCommand().isEmpty()); + // The UI uses this to disable the Sync button with a tooltip. + QVERIFY(!config.warnings().isEmpty()); +} + +void TestConfig::accountWithoutMaildirIsRejected() +{ + QTemporaryDir dir; + const QString path = writeIni(dir, QStringLiteral( + "[sync]\n" + "command=/bin/true\n" + "\n" + "[account.broken]\n" + "name=No Maildir\n" + "address=x@example.org\n" + )); + + Config config; + config.load(path); + + // Rejected, reported, and not offered to the user as a scope. + QCOMPARE(config.accounts().size(), 0); + QCOMPARE(config.warnings().size(), 1); + QVERIFY(config.warnings().first().contains(QStringLiteral("broken"))); +} + +void TestConfig::scopedQueryWrapsCorrectly() +{ + Account account; + account.key = QStringLiteral("work"); + account.maildir = QStringLiteral("work-mail"); + + QCOMPARE(account.scopedQuery(QStringLiteral("tag:inbox")), + QStringLiteral("path:\"work-mail/**\" and (tag:inbox)")); + + // An empty query still scopes to the account rather than matching nothing. + QCOMPARE(account.scopedQuery(QString()), + QStringLiteral("path:\"work-mail/**\"")); +} + +QTEST_MAIN(TestConfig) +#include "test_config.moc" |
