diff options
Diffstat (limited to 'assets/hooks')
| -rwxr-xr-x | assets/hooks/mailrules.py | 9 | ||||
| -rwxr-xr-x | assets/hooks/test_mailrules.py | 35 |
2 files changed, 43 insertions, 1 deletions
diff --git a/assets/hooks/mailrules.py b/assets/hooks/mailrules.py index dbb80a5..fbd615b 100755 --- a/assets/hooks/mailrules.py +++ b/assets/hooks/mailrules.py @@ -41,7 +41,8 @@ DEFAULT_STAGE = 50 # Fields this version understands. Anything else in a rule object is kept in # `unknown` and written back untouched, which is what makes the file neutral # rather than this tool's file that another program may read. -KNOWN_KEYS = {"id", "stage", "enabled", "add", "remove", "query", "note"} +KNOWN_KEYS = {"id", "stage", "enabled", "add", "remove", "query", "note", + "allow_archive"} # An id is a handle, not a display name: a UI selects on it and a diff tracks # it. Tags may contain '/' and may be renamed; ids may not. @@ -57,6 +58,7 @@ class Rule: stage: int = DEFAULT_STAGE enabled: bool = True note: str = "" + allow_archive: bool = False unknown: dict = field(default_factory=dict) @@ -194,6 +196,10 @@ def _rule_to_dict(rule): "query": rule.query, "note": rule.note, } + # Written only where it does something, matching src/tagrules.cpp, so a + # file without an archiving rule round-trips unchanged (item 213). + if rule.allow_archive and "inbox" in rule.remove: + out["allow_archive"] = True out.update(rule.unknown) return out @@ -257,5 +263,6 @@ def _parse_rule(obj, index, seen, warnings): stage=stage, enabled=bool(obj.get("enabled", True)), note=obj.get("note", "") if isinstance(obj.get("note", ""), str) else "", + allow_archive=obj.get("allow_archive") is True, unknown={k: v for k, v in obj.items() if k not in KNOWN_KEYS}, ) diff --git a/assets/hooks/test_mailrules.py b/assets/hooks/test_mailrules.py index b1f31c9..ae8fe04 100755 --- a/assets/hooks/test_mailrules.py +++ b/assets/hooks/test_mailrules.py @@ -266,6 +266,41 @@ def test_tag_arguments(): assert mailrules.tag_arguments(rule) == ["+one", "+two", "-three"] +def test_allow_archive_round_trips_and_is_omitted_when_false(): + """Item 213. Known, so not carried as unknown; written only when true.""" + with tempfile.TemporaryDirectory() as tmp: + path = write_rules(tmp, { + "version": 1, + "rules": [ + {"id": "promo", "add": ["promo"], "remove": ["inbox"], + "query": "from:a@example.com", "allow_archive": True}, + {"id": "plain", "add": ["x"], "query": "from:b@example.com"}, + ], + }) + store = mailrules.load(path) + assert store.rules[0].allow_archive is True + assert store.rules[1].allow_archive is False + assert "allow_archive" not in store.rules[0].unknown + + mailrules.save(store, path) + raw = json.loads(path.read_text()) + assert raw["rules"][0]["allow_archive"] is True + assert "allow_archive" not in raw["rules"][1] + + +def test_allow_archive_on_a_rule_keeping_inbox_is_accepted_and_ignored(): + """Never a reason to drop a rule: the hook runs unattended.""" + with tempfile.TemporaryDirectory() as tmp: + path = write_rules(tmp, { + "version": 1, + "rules": [{"id": "x", "add": ["x"], "query": "from:a@example.com", + "allow_archive": True}], + }) + store = mailrules.load(path) + assert len(store.rules) == 1 + assert store.warnings == [] + + def run_all(): for name, fn in sorted(globals().items()): if name.startswith("test_") and callable(fn): |
