diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-31 12:43:21 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-31 12:43:21 +0100 |
| commit | 2b6ecd2a1f624c12c071320ff1ed96404d75f839 (patch) | |
| tree | 1d2eb2a875098e97558b4cbcff07fd32e841b7e9 /tests/test_mainwindow.cpp | |
| parent | c9882b609ade04a8b17a89f6b05d51bf939e330c (diff) | |
| download | qtmaildir-2b6ecd2a1f624c12c071320ff1ed96404d75f839.tar.gz qtmaildir-2b6ecd2a1f624c12c071320ff1ed96404d75f839.zip | |
fix: handle every rule answer, and keep its failure out of errorOccurred
A second Apply overwrote the pending token, so the first rule's answer
was dropped as stale although its writes had landed: no undo, no pending
count, no accounts. A write's answer cannot be superseded, so the token
is gone and every ruleApplied() is handled. Pending counts are keyed by
generation for the same reason, since a second Apply before the first
count returned dropped the first Apply entirely.
applyRule() no longer emits errorOccurred(). That path reverted an
unrelated in-flight user write and its message was then overwritten by
"nothing to change". The failure travels in the reply instead:
ruleApplied(description, changes, error). openReadOnly() and writeTags()
take an optional error out-parameter so the rule's path stays quiet.
The window shows the error, still records and offers undo for whatever
landed first, and names the rule from the reply's description.
Item 212.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests/test_mainwindow.cpp')
| -rw-r--r-- | tests/test_mainwindow.cpp | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index 17c6f8c..5eed336 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -301,6 +301,7 @@ private slots: void applyingARuleConfirmsWithTheCountAndUndoesExactly(); void applyingARuleWhoseQueryNamesItsOwnTagWritesAllOfIt(); void aBackfillLeavesAnArmedUserCommandAlone(); + void twoRulesAppliedBackToBackEachGetTheirUndo(); void aPurgeTakesTheRowsOutOfTheViewWithoutARefresh(); void theDashboardFollowsAWriteToTheConversationItShows(); void anEditHeldByASyncSaysSoInsteadOfClaimingItLanded(); @@ -10161,6 +10162,56 @@ void TestMainWindow::aBackfillLeavesAnArmedUserCommandAlone() QStringList{ QStringLiteral("armed@example.org") }); } +void TestMainWindow::twoRulesAppliedBackToBackEachGetTheirUndo() +{ + // Item 212, from review. A second Apply while the first is in flight must + // not make the first's answer look stale: its writes landed, so it needs + // its undo entry and its pending count like any other write. + WorkerBackedWindow backed; + QVERIFY(backed.fixture().addMessage( + QStringLiteral("inbox"), QStringLiteral("one@example.org"), + QStringLiteral("One"), QStringLiteral("deals@example.net"), + QStringLiteral("Fri, 14 Aug 2026 10:00:00 +0200"), + QStringLiteral("Body."))); + QVERIFY2(backed.build(), qPrintable(backed.error())); + const QString cfg = backed.fixture().configPath(); + + MainWindow window(backed.config()); + QCOMPARE(window.pendingEditCount(), 0); + + TagRule first; + first.id = QStringLiteral("first"); + first.query = QStringLiteral("from:deals@example.net"); + first.add = { QStringLiteral("ruleone") }; + TagRule second = first; + second.id = QStringLiteral("second"); + second.add = { QStringLiteral("ruletwo") }; + + // Answer every box, however they nest. + QStringList asked; + QTimer poll; + poll.setInterval(20); + QObject::connect(&poll, &QTimer::timeout, [&asked]() { + auto *box = qobject_cast<QMessageBox *>(QApplication::activeModalWidget()); + if (!box) + return; + asked.append(box->text()); + box->button(QMessageBox::Yes)->click(); + }); + poll.start(); + + window.applyRuleToExistingMail(first); + window.applyRuleToExistingMail(second); + QTRY_COMPARE_WITH_TIMEOUT(asked.size(), 2, 15000); + QTRY_COMPARE_WITH_TIMEOUT(window.undoDepthForTesting(), 2, 15000); + + QCOMPARE(fixtureNotmuch(cfg, { QStringLiteral("count"), + QStringLiteral("tag:ruleone and tag:ruletwo") }), + QStringLiteral("1")); + // One message, two (message, tag) edits outstanding. + QCOMPARE(window.pendingEditCount(), 2); +} + /// Item 181, from the user: "the thread dashboard doesn't update live with the /// modifications applied to the list pane. If I mark the thread as read, the /// dash still reports N unread". |
