aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-10 11:02:46 +0200
committerDanilo M. <danix@danix.xyz>2026-09-10 11:02:46 +0200
commita5bf22b9eefa32322b2587ac930e5680d97500be (patch)
tree2e84821704a94825d4a7117eac61d576e7aefd9f
parentae1b825b458d14cc1fa251a56331767d5fcf4ec6 (diff)
downloadabusectl-a5bf22b9eefa32322b2587ac930e5680d97500be.tar.gz
abusectl-a5bf22b9eefa32322b2587ac930e5680d97500be.zip
fix: restore the footer guard, with the sequence that needs it
The previous commit removed this guard after a mutation check said it was redundant. The check was wrong: it only exercised runs that skipped the identity from the start, where [reporter] is never written and the footer always trails a rendered table. The sequence that breaks is answering the identity and then re-running and skipping it. [reporter] is then PRESERVED rather than rendered, so the footer that trailed it in the old file rides across while this run emits its own, and the file grows a second copy per run. Covered by a test naming that sequence, and both guards now fail when removed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LByBnw83xr9YP85nskzkyE
-rw-r--r--abusectl/init.py7
-rw-r--r--tests/test_init.py21
2 files changed, 28 insertions, 0 deletions
diff --git a/abusectl/init.py b/abusectl/init.py
index 27a4385..0e185c0 100644
--- a/abusectl/init.py
+++ b/abusectl/init.py
@@ -317,6 +317,13 @@ def _preserved_sections(path: pathlib.Path, built: set) -> str:
skipping = stripped.lstrip("[").rstrip("]").strip() in built
if skipping:
continue
+ # build()'s own footer, not a user comment. It trails whichever
+ # table came last, so when that table is PRESERVED rather than
+ # rendered (identity answered once, then skipped on a later run)
+ # it rides across and this run emits a second copy.
+ if stripped.startswith("# Later parts of abusectl"):
+ skipping = True
+ continue
if not skipping:
kept.append(line)
diff --git a/tests/test_init.py b/tests/test_init.py
index 4f2042c..55153f4 100644
--- a/tests/test_init.py
+++ b/tests/test_init.py
@@ -348,3 +348,24 @@ class TestRepeatedInit(unittest.TestCase):
self.assertEqual(text.count("# Later parts of abusectl"), 1)
# The unknown section still rides across untouched.
self.assertEqual(tomllib.loads(text)["misp"]["api_key"], "SECRET")
+
+ def test_the_footer_survives_an_identity_answered_then_skipped(self):
+ # The sequence a hand test actually hit, and the one the first fix
+ # missed: answering the identity, then re-running and skipping it,
+ # leaves [reporter] PRESERVED rather than rendered, so the footer
+ # trailing it rides across while this run emits its own.
+ relays = {"trusted_relays": ["192.0.2.0/24"], "cases": ""}
+ answered = dict(relays, reporter_name="A Reporter",
+ reporter_org="example.org",
+ reporter_email="r@example.org")
+ with tempfile.TemporaryDirectory() as tmp:
+ path = pathlib.Path(tmp) / "config.toml"
+ init.write(path, answered)
+ init.write(path, relays, force=True)
+
+ text = path.read_text()
+ self.assertEqual(text.count("# Later parts of abusectl"), 1)
+ self.assertEqual(text.count("# abusectl configuration."), 1)
+ self.assertEqual(
+ tomllib.loads(text)["reporter"]["name"], "A Reporter"
+ )