On this page
Shared test-running logic that auto-detects project types and invokes the correct test runner (pytest, go test, npm test) for releases and checks.
#rlsbl.testing
#rlsbl.testing
Shared test-running logic that auto-detects project types and invokes the correct test runner (pytest, go test, npm test) for releases and checks.
Extracted from the release pipeline so it can be reused by other commands (e.g., pre-push checks, CI, standalone test invocations).
#_overlay_exclusions
def _overlay_exclusions(overlays: list[dict] | None) -> list[str]Return the --no-install-package <pkg> arguments that keep declared dev overlays out of a sync, or [] in registry mode.
Paired with --inexact (which stops the sync removing packages uv did not install), this is the same pair rlsbl dev sync and the sandboxed test runner use. Without it an exact sync reinstalls the locked registry wheel over the editable checkout, with no output saying so.
#collect_active_overlays
def collect_active_overlays(project_dirs) -> list[dict]Union the active dev overlays declared by project_dirs.
A workspace's members share one environment, so a sync at the workspace root must exclude every member's overlaid packages, not just one project's. Raises :class:OverlayModeConflictError when two projects overlay the same package from different checkouts -- one shared environment cannot hold both, and picking either silently would wipe the other.
#_sync_selector_args
def _sync_selector_args(run_cmd: list[str]) -> list[str]Extract the --group/--extra selectors from a resolved uv run invocation, so an explicit uv sync installs exactly what the suite runs with. Reading them back off the command keeps one source for the choice.
#sync_workspace
def sync_workspace(workspace_root: str, *, verbose: bool=False, check_timeout: int=120, overlays: list[dict] | None=None) -> boolRun uv sync --all-packages at the workspace root.
In overlay mode (overlays non-empty) the sync additionally carries --inexact and one --no-install-package per overlaid package, so a workspace whose environment holds editable sibling checkouts is synced without wiping them.
Returns True on success, False on failure.
#_probe_pytest_location
def _probe_pytest_location(project_dir: str) -> tuple[str, str] | NoneDetect where pytest is declared in a project's pyproject.toml.
Checks in order:
- [dependency-groups].* -- any group containing a pytest entry
- [project.optional-dependencies].* -- any extra containing pytest
- [tool.uv].dev-dependencies -- uv legacy dev deps
Returns (source_type, group_name) on match, or None if not found. source_type is one of "dependency-group", "optional-dep", "uv-dev".
#_resolve_pytest_invocation
def _resolve_pytest_invocation(project_dir: str, workspace_root: str | None) -> list[str]Build the pytest command for a project based on its environment.
For workspace members, returns plain uv run python -P -m pytest (the workspace venv has everything). For standalone projects, probes pyproject.toml to determine the correct uv flags.
Raises ConfigError if pytest is not declared anywhere in pyproject.toml.
#_pytest_marker_args
def _pytest_marker_args(config: dict) -> list[str]Return ["-m", <markers>] when a test.pypi.markers string is configured, else [].
Reads the per-target test block via config.get("test", {}).get("pypi", {}) so future per-target options (go tags, npm script selection) slot into the same shape. An absent section/key -- or an empty/falsy markers value -- yields no arguments, keeping the pytest invocation byte-identical to the no-config case. Structural validation (unknown keys, non-string/empty markers) is enforced upstream by config.validate_test_config.
#resolve_test_timeout
def resolve_test_timeout(config: dict | None, check_timeout: int | None) -> intResolve the subprocess budget for a test run.
An explicit check_timeout from the caller wins; otherwise the project's configured check timeout applies.
#run_project_tests
def run_project_tests(target_name: str, *, project_dir: str | None=None, workspace_root: str | None=None, skip_sync: bool=False, config: dict | None=None)Run the built-in test suite for the given target.
Args:
target_name: registry/target identifier (e.g., "pypi", "go", "npm").project_dir: working directory for subprocess calls. None means cwd.workspace_root: uv workspace root for monorepos. When set, uv sync
runs here instead of at project_dir.
skip_sync: if True, skip the uv sync step (caller already synced).config: project config dict. Used to read uv_sync_verbose for pypi.
Returns a SuiteRunOutcome. Does NOT call sys.exit -- that is the caller's responsibility.
The dispatch is the target protocol. This function used to be a chain of name comparisons ending in return True, so a target with no runner -- or a name that was not a target at all -- reported a PASSING test step for a suite that never ran. A target that cannot run tests now answers SKIPPED naming itself, and the caller renders that as a visible skip line.
No dry-run parameter: the test suite reaches this through the impure test-suite check, which the check framework already lists rather than runs under --dry-run. The parameter this function used to carry was never passed by any caller, and a second, hand-rolled skip would be a second place for the two answers to disagree.
#_run_pypi_tests
def _run_pypi_tests(*, project_dir: str | None, workspace_root: str | None=None, skip_sync: bool=False, config: dict, check_timeout: int=120) -> boolRun Python tests via uv or bare pytest.
For workspace members: syncs at workspace root, then runs uv run python -P -m pytest. For standalone projects: skips sync (uv run handles it), uses _resolve_pytest_invocation to build the correct command. Falls back to python -P -m pytest when uv is not installed.
When the project declares dev overlays and the sentinel agrees, every uv invocation here is overlay-preserving: the sync excludes the overlaid packages and uv run is given --no-sync. Otherwise this runner would reinstall the locked registry wheels over the editable checkouts -- testing released code while destroying the state dev-overlay-drift then fails on. A declaration and a sentinel that disagree are a hard error, since neither mode is then true.
#_run_go_tests
def _run_go_tests(*, project_dir: str | None, check_timeout: int=120) -> boolRun Go tests.
#_run_maven_tests
def _run_maven_tests(*, project_dir: str | None, check_timeout: int=120) -> boolRun Maven/Gradle tests.
Prefers ./gradlew test if gradlew exists, otherwise falls back to mvn test if pom.xml exists.
#_run_npm_tests
def _run_npm_tests(*, project_dir: str | None, check_timeout: int=120) -> boolRun npm tests if a test script is defined in package.json.