diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-20 09:47:11 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-20 09:47:11 +0200 |
| commit | 98914f2c184539c9be9cac7c3ae8def6820868b8 (patch) | |
| tree | c87a7fe3ecd4b90e3222ba41e95bb5dc6eecf51b /tests/notmuchfixture.h | |
| parent | 40059782a5f5658ba02b47c4ab42fcfa4234989d (diff) | |
| download | qtmaildir-98914f2c184539c9be9cac7c3ae8def6820868b8.tar.gz qtmaildir-98914f2c184539c9be9cac7c3ae8def6820868b8.zip | |
fix(worker): read the mail root, not the index directory
notmuch can be configured with `mail_root` and `path` as separate keys,
which puts the Xapian index outside the Maildir. Under that layout
notmuch_database_get_path() returns the INDEX directory, and the worker
treated it as the mail root at four sites.
The consequences are not symmetric. Message paths resolved to `../..`
escapes that match no account prefix, which is a display defect. But
moveMessages() composes its destination from the same root, so Delete
would have written into the Xapian tree: outside the Maildir, invisible
to mbsync, and gone from every other client. That is the stranded-mail
failure of item 103 with a new cause.
notmuch_config_get(NOTMUCH_CONFIG_MAIL_ROOT) is correct under both
layouts, so no conditional is needed. Verified against the live database:
with only `path` set it returns the same string as get_path(), making
this a no-op for the current configuration.
The fixture gains an opt-in splitIndex(). That is load-bearing rather
than convenience: in the ordinary layout the index lives inside the mail
root and both accessors return the same string, so a test written
against it passes whichever one the code uses. All three new tests fail
against the old accessor, confirmed by mutation.
Also records the finding as backlog item 124, and corrects item 121's
timings, which had been copied from item 74 rather than measured. A cold
run seven minutes after boot, with the index verifiably unread, gives
2008 ms to the first rows and 38618 ms to a complete list, against the
642 ms and 5714 ms recorded there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDq53rMd3AQp7QmcZzpuBM
Diffstat (limited to 'tests/notmuchfixture.h')
| -rw-r--r-- | tests/notmuchfixture.h | 33 |
1 files changed, 30 insertions, 3 deletions
diff --git a/tests/notmuchfixture.h b/tests/notmuchfixture.h index bb25526..d1bdf62 100644 --- a/tests/notmuchfixture.h +++ b/tests/notmuchfixture.h @@ -44,6 +44,23 @@ public: QString configPath() const { return m_dir.filePath(QStringLiteral("config")); } QString maildirPath() const { return m_dir.filePath(QStringLiteral("mail")); } + /// Where the Xapian index lives. Equal to maildirPath()/.notmuch in the + /// ordinary layout; a directory of its own once splitIndex() is called. + QString indexPath() const + { + return m_splitIndex ? m_dir.filePath(QStringLiteral("index")) + : maildirPath() + QStringLiteral("/.notmuch"); + } + + /// Puts the index OUTSIDE the mail root, as notmuch's `mail_root`/`path` + /// split does (item 124). + /// + /// This is opt-in because it is the only layout that can tell + /// `notmuch_database_get_path()` apart from the mail root: in the ordinary + /// layout the two return the same string, so a test written against it + /// passes whichever accessor the code uses. Call before index(). + void splitIndex() { m_splitIndex = true; } + /// Writes one message into <folder>/cur (or new/ when unread). /// /// Returns false if the file could not be written. Call index() afterwards. @@ -103,9 +120,18 @@ public: return false; } QTextStream out(&config); - out << "[database]\n" - << "path=" << maildirPath() << "\n" - << "[new]\n" + out << "[database]\n"; + if (m_splitIndex) { + // Two keys: the mail stays put and only the index moves. notmuch + // reads `path` as the database directory ITSELF here, not as a + // parent to create `.notmuch` in. + QDir().mkpath(indexPath()); + out << "mail_root=" << maildirPath() << "\n" + << "path=" << indexPath() << "\n"; + } else { + out << "path=" << maildirPath() << "\n"; + } + out << "[new]\n" << "tags=unread;inbox;\n"; out.flush(); config.close(); @@ -132,4 +158,5 @@ public: private: QTemporaryDir m_dir; QString m_error; + bool m_splitIndex = false; }; |
