diff --git a/noxfile.py b/noxfile.py index 7cf3e6a9b..01ae1f963 100644 --- a/noxfile.py +++ b/noxfile.py @@ -313,9 +313,48 @@ def integration_tests(session: nox.Session): @nox.session(python=SUPPORTED_PYTHONS, reuse_venv=True, venv_backend="uv") def slow_integration_tests(session: nox.Session): - """Run Robot Framework integration tests.""" + """Run Robot Framework slow integration tests (parallel via pabot). + + Runs all tests tagged ``slow`` that are excluded from the standard + ``integration_tests`` session. Defaults to conservative parallelism + (<=2 processes) to avoid resource pressure in CI. Override via + TEST_PROCESSES or by passing --processes/--processes=N in session + arguments. + """ session.install("-e", ".[tests]") session.env["CLEVERAGENTS_AUTO_APPLY_MIGRATIONS"] = "true" + session.env["NO_COLOR"] = "1" + # Override PYTHONPATH so the editable install (src/) for this project + # is used instead of any inherited PYTHONPATH from the outer environment. + session.env["PYTHONPATH"] = "src" + + # Propagate venv bin to PATH so Run Process in robot files finds + # the venv's python/robot rather than the system copies. + venv_bin = os.path.join(session.virtualenv.location, "bin") + session.env["PATH"] = venv_bin + os.pathsep + os.environ.get("PATH", "") + + # Ensure output directory exists (CI starts with a clean checkout) + os.makedirs("build/reports/robot", exist_ok=True) + + # Pass the venv Python path explicitly so Run Process calls use it + # instead of relying on PATH (which may not propagate to subprocesses). + venv_python = os.path.join(venv_bin, "python") + + # Pre-compile bytecode so that parallel pabot workers (and the + # Python sub-processes they spawn via ``Run Python Script``) can + # read cached .pyc files instead of each cold-compiling every + # module from source simultaneously — avoids a thundering-herd + # race on CI runners with high core counts. + session.run("python", "-m", "compileall", "-q", "src/") + + # Build a pre-migrated template DB so helper scripts that call + # setup_workspace() can copy it instead of running 25+ Alembic + # migrations per test — critical for parallel pabot execution. + template_path = _create_template_db(session) + session.env["CLEVERAGENTS_TEMPLATE_DB"] = template_path + + pabot_args, robot_args = _split_pabot_args(session.posargs) + parallel_args = _pabot_parallel_args(pabot_args) # TDD expected-fail listener — inverts results for @tdd_expected_fail # tagged tests and validates TDD tag combinations. @@ -327,7 +366,9 @@ def slow_integration_tests(session: nox.Session): ) session.run( - "robot", + "pabot", + *parallel_args, + *pabot_args, "--outputdir", "build/reports/robot", "--loglevel", @@ -338,12 +379,24 @@ def slow_integration_tests(session: nox.Session): "log.html", "--xunit", "xunit.xml", + "--variable", + f"PYTHON:{venv_python}", "--listener", tdd_listener, + "--include", + "slow", + "--exclude", + "discovery", + "--exclude", + "code_blocks", + "--exclude", + "wip", + "--exclude", + "E2E", "--exclude", "tdd_fixture", + *robot_args, "robot/", - *session.posargs, ) @@ -378,6 +431,12 @@ def e2e_tests(session: nox.Session): # Pre-compile bytecode to avoid cold-compilation overhead. session.run("python", "-m", "compileall", "-q", "src/") + # Build a pre-migrated template DB so helper scripts that call + # setup_workspace() can copy it instead of running 25+ Alembic + # migrations per test — reduces per-test setup from ~1-3 s to ~1 ms. + template_path = _create_template_db(session) + session.env["CLEVERAGENTS_TEMPLATE_DB"] = template_path + # Propagate LLM API keys from the environment into the session # so that real E2E tests can authenticate with providers. for key in (