"""Characterization tests for the legacy logreport script. These tests pin what the script DOES today, including its quirks and one latent bug, following Michael Feathers' method: write the test, run it, paste the actual output in as the expectation, then name the test after what you learned. Run: python -m unittest add-tests-to-a-legacy-script-with-an-agent_test.py Python 3.13, standard library only. """ from __future__ import annotations import importlib.util import io import os import unittest HERE = os.path.dirname(os.path.abspath(__file__)) _spec = importlib.util.spec_from_file_location( "logreport", os.path.join(HERE, "add-tests-to-a-legacy-script-with-an-agent.py")) logreport = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(logreport) LINES = [ "2026-09-05 14:03:11 ERROR auth: token expired user=42\n", "2026-09-05 14:03:12 INFO auth: refreshed user=42\n", "garbage line without a timestamp\n", "2026-09-05 14:04:00 INFO web: GET /health 200\n", "2026-09-05 14:05:30 WARN db: slow query 1.2s\n", ] def run(lines): out = io.StringIO() logreport.run(lines, out) return out.getvalue() class ReportCharacterization(unittest.TestCase): def test_report_for_five_lines_is_exactly_this(self): # Pasted from the first run; every character is intentional. self.assertEqual(run(LINES), "WARN skipping line 3: 'garbage line without a timestamp'\n" "lines: 5 (skipped 1)\n" " INFO 2\n" " ERROR 1\n" " WARN 1\n" "first: 05/09/26 14:03:11\n" "last: 05/09/26 14:05:30\n") def test_empty_input_prints_only_the_line_count(self): self.assertEqual(run([]), "lines: 0 (skipped 0)\n") def test_levels_sort_by_count_then_name(self): out = run(["2026-01-01 00:00:00 B x\n", "2026-01-01 00:00:00 A x\n", "2026-01-01 00:00:00 A x\n", "2026-01-01 00:00:00 C x\n"]) self.assertEqual(out.splitlines()[1:4], [" A 2", " B 1", " C 1"]) def test_level_match_is_case_sensitive(self): out = run(["2026-01-01 00:00:00 error x\n", "2026-01-01 00:00:00 ERROR x\n"]) self.assertIn(" ERROR 1\n", out) self.assertIn(" error 1\n", out) def test_short_or_malformed_stamps_are_skipped_with_a_warning(self): cases = { "2026-9-5 14:03:11 INFO x\n": "WARN skipping line 1: '2026-9-5 14:03:11 INFO x'", "2026-09-05 14:03 INFO x\n": "WARN skipping line 1: '2026-09-05 14:03 INFO x'", "2026-09-05 14:03:11 INFO\n": "WARN skipping line 1: '2026-09-05 14:03:11 INFO'", } for line, expected in cases.items(): with self.subTest(line=line): self.assertEqual(run([line]).splitlines()[0], expected) def test_run_resets_state_between_calls(self): run(LINES) self.assertEqual(run([]), "lines: 0 (skipped 0)\n") class DateQuirks(unittest.TestCase): def test_dates_are_reformatted_to_dd_mm_yy(self): for iso, expected in {"2026-09-05": "05/09/26", "1999-12-31": "31/12/99", "2000-01-01": "01/01/00"}.items(): with self.subTest(iso=iso): self.assertEqual(logreport.fmt_date(iso), expected) def test_last_seen_is_wrong_across_a_month_boundary_documented_bug(self): # 1 September sorts before 31 August once both are DD/MM/YY strings. out = run(["2026-08-31 23:59:59 INFO x\n", "2026-09-01 00:00:01 INFO y\n"]) self.assertIn("first: 01/09/26 00:00:01\n", out) # wrong, pinned on purpose self.assertIn("last: 31/08/26 23:59:59\n", out) # wrong, pinned on purpose if __name__ == "__main__": unittest.main()