Run unsloth_cli/tests in Backend CI (#7598)
unsloth_cli/tests had no CI at all. unsloth_cli/** was a paths trigger and a ruff target, so the Backend CI job already fired on CLI changes but never ran these 673 tests, which cover the studio launcher, the pre-exposure gate and the auth secret writers. Four had been failing on main unnoticed. Two were stale rather than broken code: - test_studio_default_exposes_parallel_option pinned the plain --parallel default to 1, but #7455 deliberately moved _PARALLEL_DEFAULT_PLAIN to 4 so a new chat does not queue behind the previous one. Assert against the constant so the two cannot drift again. - test_reexec_forwards_api_only expected --secure --api-only to re-exec. The pre-exposure gate now refuses that combination, because api-only serves no login page and the bootstrap deadline does not apply, so a seeded password could never be changed. Drop the case and assert the refusal instead. Two only passed when a built frontend dist happened to be present, which it is not in a fresh clone or on a runner. Both reach a public-launch path where the missing-dist gate exits first, so they never got to the backend check and the run_server call they are about. Stub _find_frontend_dist the way their siblings already do. Own step rather than folding into the tests/ discovery: pyproject's testpaths is tests/, and this suite needs no PYTHONPATH or CUDA spoof, importing neither unsloth nor torch. Its deps are already installed by the job (pydantic and uvicorn, which brings click, via studio.txt; pyyaml explicitly).
This commit is contained in:
parent
c70c1d2d89
commit
0ed26297ed
4 changed files with 40 additions and 3 deletions
12
.github/workflows/studio-backend-ci.yml
vendored
12
.github/workflows/studio-backend-ci.yml
vendored
|
|
@ -223,6 +223,18 @@ jobs:
|
|||
tests/studio/test_is_mlx_dispatch_gate.py \
|
||||
tests/studio/test_xpu_spoof_pipeline.py
|
||||
|
||||
- name: CLI tests (unsloth_cli)
|
||||
# unsloth_cli/tests had no CI at all: `unsloth_cli/**` was only a paths
|
||||
# trigger and a ruff target, so 673 tests covering the studio launcher,
|
||||
# the pre-exposure gate and the auth secret writers ran nowhere, and
|
||||
# four of them had been failing on main unnoticed.
|
||||
# Own step, not folded into the tests/ discovery above: pyproject's
|
||||
# testpaths is tests/, and this suite needs no PYTHONPATH or CUDA spoof
|
||||
# (it self-bootstraps sys.path and imports neither unsloth nor torch).
|
||||
# Run the whole directory in one invocation; some files in it are
|
||||
# order-dependent and only pass in a full-directory run.
|
||||
run: python -m pytest unsloth_cli/tests -q --tb=short
|
||||
|
||||
- name: Shell installer tests
|
||||
# Auto-discovered rather than allowlisted. The old hardcoded list had
|
||||
# silently fallen seven files behind tests/run_all.sh, including
|
||||
|
|
|
|||
|
|
@ -626,6 +626,12 @@ def test_studio_default_in_venv_broken_backend_exits_before_stripping_bootstrap(
|
|||
|
||||
# Pretend we are already inside the studio venv, with a broken backend.
|
||||
monkeypatch.setattr(sys, "prefix", str(tmp_path / "unsloth_studio"))
|
||||
# A built dist is not present in a fresh clone. The missing-frontend gate
|
||||
# runs first and has its own test below; stub it so this one reaches the
|
||||
# backend check it is actually about.
|
||||
monkeypatch.setattr(
|
||||
studio_mod, "_find_frontend_dist", lambda: Path("/fake/studio/frontend/dist")
|
||||
)
|
||||
|
||||
def _boom():
|
||||
raise ImportError("cannot import backend run.py")
|
||||
|
|
|
|||
|
|
@ -606,8 +606,8 @@ def test_studio_default_exposes_parallel_option():
|
|||
assert "--parallel" in decls
|
||||
assert "--n-parallel" in decls
|
||||
assert (
|
||||
getattr(opt, "default", None) == 1
|
||||
), "studio_default --parallel must default to 1 (pre-PR); `run` is 4"
|
||||
getattr(opt, "default", None) == studio_mod._PARALLEL_DEFAULT_PLAIN
|
||||
), "studio_default --parallel must use _PARALLEL_DEFAULT_PLAIN"
|
||||
assert getattr(opt, "min", None) == 1
|
||||
assert getattr(opt, "max", None) == 64
|
||||
|
||||
|
|
@ -679,7 +679,6 @@ def test_api_only_option_is_registered():
|
|||
"extra,present",
|
||||
[
|
||||
(["--api-only"], True),
|
||||
(["--secure", "--api-only"], True), # secure headless path
|
||||
([], False),
|
||||
],
|
||||
)
|
||||
|
|
@ -691,6 +690,21 @@ def test_reexec_forwards_api_only(monkeypatch, extra, present):
|
|||
assert ("--api-only" in argv) is present, argv
|
||||
|
||||
|
||||
def test_secure_api_only_is_refused_before_any_reexec(monkeypatch, tmp_path):
|
||||
"""`--secure --api-only` used to re-exec; the pre-exposure gate now refuses
|
||||
it, because api-only has no login page and the bootstrap deadline does not
|
||||
apply, so the seeded password could never be changed."""
|
||||
studio_mod = _load_run_command()
|
||||
monkeypatch.setattr(studio_mod, "STUDIO_HOME", tmp_path)
|
||||
|
||||
result, captured = _invoke_run(monkeypatch, _BASE + ["--secure", "--api-only"])
|
||||
|
||||
assert captured == [], captured
|
||||
assert result.exit_code != 0
|
||||
combined = (result.output or "") + (getattr(result, "stderr", "") or "")
|
||||
assert "default admin password was never changed" in combined.lower()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("extra,expected", [(["--api-only"], True), ([], False)])
|
||||
def test_in_venv_path_passes_api_only_to_run_server(monkeypatch, extra, expected):
|
||||
"""In-venv path must forward --api-only to run_server(api_only=...)."""
|
||||
|
|
|
|||
|
|
@ -261,6 +261,11 @@ def test_run_in_venv_passes_secure_and_forces_host(monkeypatch, tmp_path):
|
|||
|
||||
fake_venv = tmp_path / "unsloth_studio"
|
||||
monkeypatch.setattr(sys, "prefix", str(fake_venv))
|
||||
# A built dist is not present in a fresh clone, and without it the public
|
||||
# launch gate exits before run_server is ever reached.
|
||||
monkeypatch.setattr(
|
||||
studio_mod, "_find_frontend_dist", lambda: Path("/fake/studio/frontend/dist")
|
||||
)
|
||||
|
||||
from unsloth_cli import _tool_policy as _tp_mod
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue