diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-14 11:26:39 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-14 11:26:39 +0200 |
| commit | 80d78727bf9a73f609e6456fd114951b6daf347b (patch) | |
| tree | fd050f9f3aa5945e062d098390b7ebddf6354e66 /tests | |
| parent | 5694cf6feadd51a047455ae28a02d25d55f83987 (diff) | |
| download | qtmaildir-80d78727bf9a73f609e6456fd114951b6daf347b.tar.gz qtmaildir-80d78727bf9a73f609e6456fd114951b6daf347b.zip | |
test(rules): pin a seeded id against colliding with a rule
The name comes from queries.json, so nothing had ever checked it against
the ids in rules.json. Replacing the matched rule would retag mail
against a query the user never associated with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_tagrules.cpp | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/tests/test_tagrules.cpp b/tests/test_tagrules.cpp index 37dcc19..3954d80 100644 --- a/tests/test_tagrules.cpp +++ b/tests/test_tagrules.cpp @@ -46,6 +46,7 @@ private slots: void theWarningReadsAsAWarningAndSitsBesideSave(); void aDismissedWarningComesBackWhenThereIsSomethingNewToSay(); void aSeededDialogOpensOnTheNewRuleWithoutWritingIt(); + void aSeededIdThatCollidesDoesNotReplaceTheRuleItMatches(); void aNameTypedWithSpacesIsSanitisedInTheField(); void aRuleAddedAndNamedInTheDialogSurvivesAReopen(); void unknownFieldsSurviveASave(); @@ -971,6 +972,45 @@ void TestTagRules::aSeededDialogOpensOnTheNewRuleWithoutWritingIt() QCOMPARE(onDisk.rules().size(), 1); } +void TestTagRules::aSeededIdThatCollidesDoesNotReplaceTheRuleItMatches() +{ + // A saved query named "Vendor" sanitises to "vendor", which is already a + // rule id here. Replacing that rule would silently retag mail against a + // query the user never associated with it. + QTemporaryDir configHome; + QVERIFY(configHome.isValid()); + qputenv("XDG_CONFIG_HOME", configHome.path().toUtf8()); + QVERIFY(QDir().mkpath(configHome.filePath(QStringLiteral("mailrules")))); + + const QString stored = configHome.filePath( + QStringLiteral("mailrules/rules.json")); + QFile out(stored); + QVERIFY(out.open(QIODevice::WriteOnly)); + out.write(R"({ + "version": 1, + "rules": [ + {"id": "vendor", "query": "from:vendor.example.org", + "add": ["vendor"], "stage": 50, "enabled": true} + ] + })"); + out.close(); + + TagRule seed; + seed.id = TagRules::sanitiseId(QStringLiteral("Vendor")); + seed.query = QStringLiteral("from:other.example.org"); + + TagRulesDialog dialog(seed); + + QCOMPARE(dialog.ruleCountForTest(), 2); + QCOMPARE(dialog.nameLineForTest(), QStringLiteral("vendor-2")); + + // And the rule it collided with is untouched. + dialog.selectRuleForTest(0); + QCOMPARE(dialog.nameLineForTest(), QStringLiteral("vendor")); + QCOMPARE(dialog.queryLineForTest(), + QStringLiteral("from:vendor.example.org")); +} + void TestTagRules::aFolderRowUsesTheDropdownAndKeepsItsSuffix() { // A path: without its suffix matches nothing and notmuch says nothing |
