summaryrefslogtreecommitdiffstats
path: root/tests/test_notmuchworker.cpp
AgeCommit message (Collapse)AuthorFilesLines
2026-08-10feat(worker): load a thread as a reply tree with per-message depthDanilo M.1-0/+52
loadThread could not be extended to do this. It walks notmuch_query_search_messages, and a message obtained that way returns NULL from notmuch_message_get_replies (notmuch.h:1617-1628), so that walk cannot produce reply depth at all. The tree comes from notmuch_thread_get_toplevel_messages instead, and the pane keeps the flat list it wants. walkReplies takes raw notmuch_message_t*, against this file's rule that every handle is RAII-owned. Messages reached through a thread are freed with it (notmuch.h:1637), so an NmMessage wrapper would destroy memory the thread frees again. The NmThread in the caller is what keeps them alive. Every message in the thread gets a node regardless of the query: the list is where the reply count is read, and hiding unmatched replies would make that count disagree with the rows under it. Both tests mutation-checked. Flattening depth fails the depth assertion, and skipping the thread walk fails it too, so neither passes against the two mistakes the notmuch API invites.
2026-08-07feat(ui): add a Maildir overview under HelpDanilo M.1-0/+44
Nothing in the UI reported database-level facts: every query gave a thread count for that query, and nothing said how much mail there is overall. A dialog under Help now shows messages, threads and tags from notmuch, plus the account list from config, since notmuch does not model accounts at all. A separate worker call rather than a reuse of requestCounts, which counts threads to match the row count of a query. This counts messages, which is what a user means by "how much mail is in here". The test pins 4 messages in 3 threads against the fixture and fails if they are ever made equal, so routing both through one count cannot pass unnoticed. Every field starts at -1 and renders as "unknown" when notmuch could not answer it. Printing 0 would say the Maildir is empty, and telling someone their mail is gone is the worst way to report an index that failed to open. The dialog opens showing "Counting..." rather than blocking, since counting every message is not free on a large database. That makes two lifetimes matter: the reply can arrive after the dialog is closed, so the label is a QPointer, and the dialog can be closed and reopened while a count runs, so a generation counter drops the older answer. The test drains DeferredDelete before firing the late reply, because close() deletes through deleteLater and without that the dangling case is never actually reached.
2026-08-07feat(ui): fill the blank message pane with a branded placeholderDanilo M.1-0/+51
An empty right pane said nothing, and multi-select made it a routine sight. It now carries the wordmark, thread counts that run their query when clicked, and a sync line that appears only when something needs attention. Rendered into the existing web view as a third document shape, so there is one document path and one set of security rules. The brand palette is a deliberate exception to deriving colours from the desktop theme, since a logo is brand rather than chrome; the theme still picks which of the two sets is used. Counts refresh when the pane is about to show rather than in the background: one goes stale the moment a tag is edited, and refreshing one nobody is looking at is work for nothing. A generation counter discards a superseded reply, and a late answer cannot repaint over an opened thread. The helper lines are real links because JavaScript is off in this profile. The handler is gated on the placeholder actually being displayed, so the same URL inside a message body is dropped: a stranger's mail must not drive the thread list, even to run a harmless query. Three defects found while building, all silent: - Every CSS percentage was invalid. QString::arg does not collapse "%%" into "%", so the document carried "50%%" and the browser dropped each declaration holding one, disabling the mask, the glow and both radial gradients while still rendering something plausible. Substitution is by named token now, which cannot collide with a percent sign. - A geometry probe endorsed the layout while that was live, because it measured only properties without percentages. - The font test passed against a build with one face missing, since the other satisfied both of its checks on its own. The mockup's light values needed correcting against a real pane: the grid vanished at a 2% luminance step on white, and the glow subtracts light there rather than adding it, washing the pane. Strength only, not hue.
2026-08-04feat(worker): list every tag in the databaseDanilo M.1-0/+39
Query bar completion cannot offer tag names without a way to enumerate them, and libnotmuch had no call wired up for it. Follows the existing generation-counter pattern; the result crosses the thread boundary as a QStringList. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04Add GPLv2-only license and per-file headersDanilo M.1-0/+18
Confirmed with the maintainer as v2-only rather than v2-or-later. LICENSE is the official text from gnu.org. Every file under src/ and tests/ carries the matching notice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04feat: add NotmuchWorker with batched queries and tag mutationDanilo M.1-0/+419
Owns the only notmuch database handle. Queries run read-only and emit threads in batches of 200 with a generation counter so the UI can discard superseded results. Tag mutation closes the read-only handle, opens read-write, applies, and closes, holding the process-wide write lock for milliseconds rather than blocking a concurrent `notmuch new`. Tested against a throwaway database built in a QTemporaryDir, superseding the spec's original "no unit test" position: applyTags is the only code here that writes to a notmuch index. The fixture never touches ~/Mail or ~/.notmuch-config. Two fixes against the drafted implementation, both caught by mutating the code and confirming exactly one test failed: - loadThread conflated "no query given" with "query matched nothing in this thread", so filtering a thread down to zero matches rendered every message expanded. Tracked with an explicit haveMatchSet flag. - applyTags now documents why a stale message id must skip rather than abort: notmuch_database_find_message reports SUCCESS with a null message for an unknown id, and the live ids alongside it still need tagging. Note for fixture authors: notmuch synchronizes maildir flags with tags at index time, so a file named `...:2,S` is indexed without the unread tag no matter what [new] tags requests. Unread fixture messages go in new/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>