"""A13: >100-line method inventory (17 items, checkable in code). The split summary's section 4 listed 16 >100-line methods; the missing 17th is ``FullAutoMixin._full_auto_execute`` (``cloud/handler_fullauto.py:182-304``, 123 lines). This test pins the full 17-method list by AST over ``cloud/handler_*.py`` plus ``cloud/server.py`` -- no external summary document is the source of truth, the code itself is. Reconcile re-pin (code-to10-reconcile): the unified line keeps the target's 6-mixin split, so 16 of the 17 methods live in ``cloud/server.py`` (``CloudControlPlaneHandler``) rather than in the source line's lifecycle/core/admin/browser/webhook mixins; the method NAMES are the same 17, only their homes differ, and no method was split or dropped to reach this list. The module-level >100-line definition likewise lives in ``cloud/server.py`` (``migrate_tasks_table``, 114 lines) on this line, not in ``handler_support.py``. Counting rule, stated because a naive AST count gives 18: the 17 are class methods. The 18th >100-line definition, ``server.migrate_tasks_table`` (module-level function, not a mixin method), is asserted separately as the ONLY module-level >100-line definition, so the 17/18 difference is explained, not hidden. No method is split by this batch: internal splitting stays at 0 items. """ import ast import unittest from pathlib import Path EXPECTED_METHODS = { ("server.py", "CloudControlPlaneHandler._task_pr"), ("server.py", "CloudControlPlaneHandler._metrics"), ("server.py", "CloudControlPlaneHandler._browser_stream"), ("server.py", "CloudControlPlaneHandler._create_task"), ("server.py", "CloudControlPlaneHandler._create_followup"), ("handler_fullauto.py", "FullAutoMixin._full_auto_execute"), ("server.py", "CloudControlPlaneHandler._task_thread_read"), ("server.py", "CloudControlPlaneHandler._deliver_to_thread"), ("server.py", "CloudControlPlaneHandler._task_wait"), ("server.py", "CloudControlPlaneHandler._merge_task"), ("server.py", "CloudControlPlaneHandler._loop_run"), ("server.py", "CloudControlPlaneHandler._release_task"), ("server.py", "CloudControlPlaneHandler._continue_task"), ("server.py", "CloudControlPlaneHandler._handle_github_webhook"), ("server.py", "CloudControlPlaneHandler._dispatch_task_route"), ("server.py", "CloudControlPlaneHandler._dispatch"), ("server.py", "CloudControlPlaneHandler._dispatch_project"), } class LongFuncInventoryTestCase(unittest.TestCase): def test_seventeen_long_methods_including_full_auto_execute(self): cloud_dir = Path(__file__).resolve().parents[1] methods = set() module_funcs = set() for path in sorted(cloud_dir.glob("handler_*.py")) + [ cloud_dir / "server.py"]: tree = ast.parse(path.read_text(encoding="utf-8")) for node in tree.body: if isinstance(node, ast.ClassDef): for member in node.body: if isinstance(member, (ast.FunctionDef, ast.AsyncFunctionDef)): lines = (member.end_lineno - member.lineno + 1) if lines > 100: methods.add( (path.name, "%s.%s" % (node.name, member.name))) elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): lines = node.end_lineno - node.lineno + 1 if lines > 100: module_funcs.add((path.name, node.name)) self.assertEqual(len(EXPECTED_METHODS), 17) self.assertIn( ("handler_fullauto.py", "FullAutoMixin._full_auto_execute"), EXPECTED_METHODS) self.assertEqual(methods, EXPECTED_METHODS) # The single non-method >100-line definition, documented # rather than silently dropped from the count. self.assertEqual( module_funcs, {("server.py", "migrate_tasks_table")}) if __name__ == "__main__": unittest.main()