From c1602587c1da679ee47bb5f47cac7b492a32b7f4 Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:07:05 -0700 Subject: [PATCH] fix(tests): drop module-level test calls that break local_testing collection (#29520) * fix(tests): drop module-level test calls that break local_testing collection Several files in tests/local_testing invoked their test functions at module scope (e.g. test_register_model.py ran test_update_model_cost_via_completion() at the bottom of the file). Those calls execute during pytest collection, so they fire real network requests at import time. test_register_model.py's call hit an OpenAI 429 and raised, turning into a collection error. A collection error aborts the whole session for every job that globs tests/local_testing/**/test_*.py, which is why unrelated jobs like langfuse_logging_unit_tests (-k langfuse) and litellm_assistants_api_testing (-k assistants) both failed even though neither touches register_model; the -k filter only applies after collection. pytest discovers and runs these test_* functions on its own, so the top-level calls were dead and harmful. Removes them from test_register_model.py, test_wandb.py, test_lunary.py, and test_multiple_deployments.py, and adds a regression test that scans the directory for module-level test invocations. * test(local_testing): skip unparseable files in module-scope invocation guardrail A syntax error in any tests/local_testing file would make ast.parse raise an unhandled SyntaxError, so the guardrail itself would crash with a confusing traceback instead of its assertion message. Such a file already fails pytest collection on its own, which is the clearer signal, so the guardrail now skips files it cannot parse and stays focused on detecting module-scope test calls. Reads files as utf-8 for deterministic behavior across platforms. --- tests/local_testing/test_lunary.py | 3 -- .../test_multiple_deployments.py | 3 -- .../test_no_top_level_test_invocations.py | 36 +++++++++++++++++++ tests/local_testing/test_register_model.py | 3 -- tests/local_testing/test_wandb.py | 3 -- 5 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 tests/local_testing/test_no_top_level_test_invocations.py diff --git a/tests/local_testing/test_lunary.py b/tests/local_testing/test_lunary.py index d181d24c782..0dbae1b817f 100644 --- a/tests/local_testing/test_lunary.py +++ b/tests/local_testing/test_lunary.py @@ -26,9 +26,6 @@ def test_lunary_logging(): print(e) -test_lunary_logging() - - def test_lunary_template(): import lunary diff --git a/tests/local_testing/test_multiple_deployments.py b/tests/local_testing/test_multiple_deployments.py index f7276d4f14e..72bfd5012c1 100644 --- a/tests/local_testing/test_multiple_deployments.py +++ b/tests/local_testing/test_multiple_deployments.py @@ -49,6 +49,3 @@ def test_multiple_deployments(): except Exception as e: traceback.print_exc() pytest.fail(f"An exception occurred: {e}") - - -test_multiple_deployments() diff --git a/tests/local_testing/test_no_top_level_test_invocations.py b/tests/local_testing/test_no_top_level_test_invocations.py new file mode 100644 index 00000000000..eb1d836a18d --- /dev/null +++ b/tests/local_testing/test_no_top_level_test_invocations.py @@ -0,0 +1,36 @@ +import ast +from pathlib import Path + +LOCAL_TESTING_DIR = Path(__file__).parent + + +def _top_level_test_invocations(tree): + invocations = [] + for node in tree.body: + if not isinstance(node, ast.Expr) or not isinstance(node.value, ast.Call): + continue + func = node.value.func + name = getattr(func, "id", None) or getattr(func, "attr", None) + if name and name.startswith("test_"): + invocations.append((name, node.lineno)) + return invocations + + +def test_no_module_level_test_invocations(): + offenders = [] + for path in sorted(LOCAL_TESTING_DIR.rglob("*.py")): + try: + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + except SyntaxError: + continue + for name, lineno in _top_level_test_invocations(tree): + offenders.append( + f"{path.relative_to(LOCAL_TESTING_DIR)}:{lineno} calls {name}()" + ) + + assert not offenders, ( + "Test functions are invoked at module scope, so they run during pytest " + "collection (making network calls and erroring collection for every job " + "that globs this directory). Remove these calls; pytest collects test " + "functions automatically:\n" + "\n".join(offenders) + ) diff --git a/tests/local_testing/test_register_model.py b/tests/local_testing/test_register_model.py index 6b170798874..635fd79abff 100644 --- a/tests/local_testing/test_register_model.py +++ b/tests/local_testing/test_register_model.py @@ -60,6 +60,3 @@ def test_update_model_cost_via_completion(): assert litellm.model_cost["gpt-3.5-turbo"]["output_cost_per_token"] == 0.4 except Exception as e: pytest.fail(f"An error occurred: {e}") - - -test_update_model_cost_via_completion() diff --git a/tests/local_testing/test_wandb.py b/tests/local_testing/test_wandb.py index 6cdca40492f..58a9c9f5ddf 100644 --- a/tests/local_testing/test_wandb.py +++ b/tests/local_testing/test_wandb.py @@ -51,9 +51,6 @@ def test_wandb_logging_async(): pass -test_wandb_logging_async() - - def test_wandb_logging(): try: response = completion(