diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-13 17:07:16 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-13 17:07:16 +0200 |
| commit | 4d84062e48ec35598b7c2ecdf8446939be6064c4 (patch) | |
| tree | ab280755094321666d29d50694c48722809d953c /tests/test_notmuchworker.cpp | |
| parent | df6461fa9136f2110c8a24567a3e4a32432a9476 (diff) | |
| parent | b8de7ce746718c7193e40b0f70cd3e2178f4ed8a (diff) | |
| download | qtmaildir-4d84062e48ec35598b7c2ecdf8446939be6064c4.tar.gz qtmaildir-4d84062e48ec35598b7c2ecdf8446939be6064c4.zip | |
Merge branch 'rule-builder': a row builder for the tagging rules
Item 76 replaces the four free-text fields with a row builder: field and
operator dropdowns per condition, +/- to add and remove them, a match
all/any choice, and a separate "but not" block. The stored format does
not change, so mailctl needs no edit. The query string stays
authoritative and remains visible, and a rule the builder cannot
represent opens in a text mode every rule carries.
Along the way, items 75, 77 and 80, and a data-loss defect released in
0.16.0 (item 79): opening the dialog and pressing Save destroyed the
first rule with nothing edited. That one damaged a real rule in the
user's own file, which was repaired by hand.
Four defects in this work were found by hand rather than by the suite,
and each is recorded where it was missed: a lost note, a one-way text
mode toggle, a geometry save on a path neither button takes, and a rule
list squeezed to one row by a long rule. Two Qt traps and one about the
user's compositor went into CLAUDE.md.
Diffstat (limited to 'tests/test_notmuchworker.cpp')
| -rw-r--r-- | tests/test_notmuchworker.cpp | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/tests/test_notmuchworker.cpp b/tests/test_notmuchworker.cpp index 1db26c7..9068ca3 100644 --- a/tests/test_notmuchworker.cpp +++ b/tests/test_notmuchworker.cpp @@ -80,6 +80,9 @@ private slots: void messageCountsCountMessagesNotThreads(); void messageCountsReportAnInvalidQueryAsMinusOne(); + void requestFoldersListsEveryMaildirFolder(); + void requestFoldersOnUnreadableConfigEmitsError(); + private: /// Tags of one message, read back through a fresh worker query. QStringList tagsOf(const QString &messageId); @@ -900,5 +903,79 @@ void TestNotmuchWorker::requestDatabaseStatsOnUnreadableConfigEmitsError() QVERIFY(ready.isEmpty()); } +void TestNotmuchWorker::requestFoldersListsEveryMaildirFolder() +{ + // Its own fixture rather than the shared one: this needs a NESTED folder, + // which is the shape a real account has (<account>/Drafts, not a flat + // "drafts"), and adding a message to the shared fixture would move seven + // count assertions in other tests for nothing. + NotmuchFixture fixture; + QVERIFY(fixture.isValid()); + QVERIFY(fixture.addMessage(QStringLiteral("work/INBOX"), + QStringLiteral("g1@example.org"), + QStringLiteral("Something"), + QStringLiteral("Alice <alice@example.org>"), + QStringLiteral("Mon, 1 Jun 2026 10:00:00 +0000"), + QStringLiteral("body"), false)); + QVERIFY(fixture.addMessage(QStringLiteral("work/Drafts"), + QStringLiteral("g2@example.org"), + QStringLiteral("Half written"), + QStringLiteral("You <you@example.org>"), + QStringLiteral("Tue, 2 Jun 2026 10:00:00 +0000"), + QStringLiteral("body"), false)); + QVERIFY2(fixture.index(), qPrintable(fixture.error())); + + // An EMPTY folder, created but never written to. mbsync makes these, and a + // list derived from indexed messages would not offer it. A rule may + // legitimately target a folder that has nothing in it yet. + QDir dir; + const QString empty = fixture.maildirPath() + QStringLiteral("/work/Archive"); + QVERIFY(dir.mkpath(empty + QStringLiteral("/cur"))); + QVERIFY(dir.mkpath(empty + QStringLiteral("/new"))); + QVERIFY(dir.mkpath(empty + QStringLiteral("/tmp"))); + + NotmuchWorker worker(fixture.configPath()); + QSignalSpy ready(&worker, &NotmuchWorker::foldersReady); + + worker.requestFolders(); + + QCOMPARE(ready.count(), 1); + const QStringList folders = ready.first().at(0).toStringList(); + + // Paths relative to the database root, which is what a Folder term + // compiles a path: against. Drafts is the whole point of the item: the + // dialog used to offer one entry per account and nothing below it. + QVERIFY(folders.contains(QStringLiteral("work/INBOX"))); + QVERIFY(folders.contains(QStringLiteral("work/Drafts"))); + QVERIFY(folders.contains(QStringLiteral("work/Archive"))); + + // Not the maildir plumbing, which is not a folder anyone files mail into, + // and not the account directory itself, which holds no cur/. + QVERIFY(!folders.contains(QStringLiteral("work/INBOX/cur"))); + QVERIFY(!folders.contains(QStringLiteral("work/INBOX/new"))); + QVERIFY(!folders.contains(QStringLiteral("work"))); + + // Sorted, so the dropdown does not reorder itself between openings with + // the same tree on disk. QDir's own order is filesystem order. + QStringList sorted = folders; + sorted.sort(); + QCOMPARE(folders, sorted); +} + +void TestNotmuchWorker::requestFoldersOnUnreadableConfigEmitsError() +{ + // Fails closed like every other entry point. The dialog then leaves the + // dropdown as it was rather than emptying it, since an empty list reads as + // "this account has no folders". + NotmuchWorker worker(QStringLiteral("/nonexistent/qtmaildir-test/config")); + QSignalSpy ready(&worker, &NotmuchWorker::foldersReady); + QSignalSpy errors(&worker, &NotmuchWorker::errorOccurred); + + worker.requestFolders(); + + QCOMPARE(errors.size(), 1); + QVERIFY(ready.isEmpty()); +} + QTEST_MAIN(TestNotmuchWorker) #include "test_notmuchworker.moc" |
