From ae1b825b458d14cc1fa251a56331767d5fcf4ec6 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Thu, 10 Sep 2026 10:49:27 +0200 Subject: fix: stop init duplicating its own preamble on every rewrite The carry-across preserved every line before the first [table], but that text is build()'s own header, not the user's. A second init emitted it twice and a third three times, with [general] missing from the tail half, so the file grew a copy per run. It still parsed, which is why the suite stayed green and only a hand test found it. Preserving from the first table onward fixes the trailing "Later parts" block too: it trails whichever table came last, so it is already inside a skipped section. A separate guard for it passed its own mutation check and was removed as redundant. Required prompts now say so. The relay question and the hop picker cannot be skipped, because with no trust boundary parse refuses and init is the route out, but they read like the reporter questions that do offer a skip. The cases prompt says "Enter for " rather than showing the default in brackets, which was the same ambiguity in the other direction. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01LByBnw83xr9YP85nskzkyE --- tests/test_init.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'tests/test_init.py') diff --git a/tests/test_init.py b/tests/test_init.py index 46b1a26..4f2042c 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -321,3 +321,30 @@ class TestWriteGuard(unittest.TestCase): if __name__ == "__main__": unittest.main() + + +class TestRepeatedInit(unittest.TestCase): + """build()'s own preamble and footer must not accrete across rewrites. + + Found by hand test, not by the suite: a single rewrite looks fine, and + the file still parses, so only the third run makes it obvious. The + carried text is build()'s, not the user's, so preserving it duplicated + the header once per run. + """ + + def test_the_preamble_and_footer_survive_four_rewrites_exactly_once(self): + answers = {"trusted_relays": ["192.0.2.0/24"], "cases": ""} + with tempfile.TemporaryDirectory() as tmp: + path = pathlib.Path(tmp) / "config.toml" + init.write(path, answers) + path.write_text( + path.read_text() + '\n[misp]\napi_key = "SECRET"\n' + ) + for _ in range(3): + init.write(path, answers, force=True) + + text = path.read_text() + self.assertEqual(text.count("# abusectl configuration."), 1) + 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") -- cgit v1.2.3