"""Tests for bugfixes in tasks.py from 2026-03-30 audit. Covers: - NET_LINE_LIMIT raised from 10 → 500 to stop false-positive PR rejections - memory_compress reads actions from tick_record["decision"]["actions"] - good_morning_report reads yesterday's tick log, not today's """ import json from datetime import datetime, timezone, timedelta from pathlib import Path # ── NET_LINE_LIMIT ─────────────────────────────────────────────────── def test_net_line_limit_is_sane(): """NET_LINE_LIMIT = 10 caused every real PR to be spam-rejected. Any value below ~200 is dangerously restrictive for a production repo. 500 is the current target: large enough for feature PRs, small enough to flag bulk commits. """ # Import at top level would pull in huey/orchestration; just grep instead. tasks_path = Path(__file__).resolve().parent.parent / "tasks.py" text = tasks_path.read_text() # Find the NET_LINE_LIMIT assignment for line in text.splitlines(): stripped = line.strip() if stripped.startswith("NET_LINE_LIMIT") and "=" in stripped: value = int(stripped.split("=")[1].strip()) assert value >= 200, ( f"NET_LINE_LIMIT = {value} is too low. " "Any value < 200 will reject most real PRs as over-limit." ) assert value <= 2000, ( f"NET_LINE_LIMIT = {value} is too high — it won't catch bulk commits." ) break else: raise AssertionError("NET_LINE_LIMIT not found in tasks.py") # ── memory_compress action path ────────────────────────────────────── def test_memory_compress_reads_decision_actions(): """Actions live in tick_record['decision']['actions'], not tick_record['actions']. The old code read t.get("actions", []) which always returned [] because the key is nested inside the decision dict. """ tasks_path = Path(__file__).resolve().parent.parent / "tasks.py" text = tasks_path.read_text() # Find the memory_compress function body and verify the action path. # We look for the specific pattern that reads decision.get("actions") # within the ticks loop inside memory_compress. in_memory_compress = False found_correct_pattern = False for line in text.splitlines(): if "def memory_compress" in line or "def _memory_compress" in line: in_memory_compress = True elif in_memory_compress and line.strip().startswith("def "): break elif in_memory_compress: # The correct pattern: decision = t.get("decision", {}) if 'decision' in line and 't.get(' in line and '"decision"' in line: found_correct_pattern = True # The OLD bug: directly reading t.get("actions") if 't.get("actions"' in line and 'decision' not in line: raise AssertionError( "Bug: memory_compress reads t.get('actions') directly. " "Actions are nested under t['decision']['actions']." ) assert found_correct_pattern, ( "memory_compress does not read decision = t.get('decision', {})" ) # ── good_morning_report date bug ──────────────────────────────────── def test_good_morning_report_reads_yesterday_ticks(): """good_morning_report runs at 6 AM. It should read YESTERDAY'S tick log, not today's (which is mostly empty at 6 AM). The old code used `now.strftime('%Y%m%d')` which gives today. The fix uses `(now - timedelta(days=1)).strftime('%Y%m%d')`. """ tasks_path = Path(__file__).resolve().parent.parent / "tasks.py" text = tasks_path.read_text() # Find the good_morning_report function and check for the timedelta fix in_gmr = False uses_timedelta_for_yesterday = False old_bug_pattern = False for line in text.splitlines(): if "def good_morning_report" in line: in_gmr = True elif in_gmr and line.strip().startswith("def "): break elif in_gmr: # Check for the corrected pattern: timedelta subtraction if "timedelta" in line and "days=1" in line: uses_timedelta_for_yesterday = True # Check for the old bug: yesterday = now.strftime(...) # This is the direct assignment without timedelta if 'yesterday = now.strftime' in line and 'timedelta' not in line: old_bug_pattern = True assert not old_bug_pattern, ( "Bug: good_morning_report sets yesterday = now.strftime(...) " "which gives TODAY's date, not yesterday's." ) assert uses_timedelta_for_yesterday, ( "good_morning_report should use timedelta(days=1) to compute yesterday's date." ) # ── review_prs includes file list ──────────────────────────────────── def test_review_prs_rejection_includes_file_list(): """When review_prs rejects a PR, the comment should include the file list so the author knows WHERE the bloat is, not just the net line count. """ tasks_path = Path(__file__).resolve().parent.parent / "tasks.py" text = tasks_path.read_text() in_review_prs = False has_file_list = False for line in text.splitlines(): if "def review_prs" in line: in_review_prs = True elif in_review_prs and line.strip().startswith("def "): break elif in_review_prs: if "file_list" in line and "filename" in line: has_file_list = True assert has_file_list, ( "review_prs rejection comment should include a file_list " "so the author knows which files contribute to the net diff." )