Files
club-3090/tools/test-console/tests/test_runner.py
T
496f82d899 Add c3t — the club-3090 test-console TUI (built by Qwen Max from the spec) (#428)
A lazydocker-style terminal UI for the stack's test scripts (verify / bench /
verify-stress-NIAH / quality / soak / rebench-full): auto-detects the running
model + endpoint, injects MODEL=/URL=, streams live progress, and browses the
results/ history. Built by Qwen Max from docs/club3090-test-tui-prompt.md.

- scripts/c3t — launcher that derives the repo root from its own location and
  runs the tool from tools/test-console's own venv (no global installs).
- tools/test-console/ — Python + Textual package, pinned pyproject + uv.lock,
  and an 83-test offline pytest suite (parsers vs fixtures, mocked docker /
  /v1/models detection, BENCH_MOCK bench parse) — all green, no GPU required.

Fixed before merge: __main__.py and a test fixture hardcoded the rig path
/opt/ai/github/club-3090 — now derived from __file__ (parents[3]; override via
C3T_REPO_ROOT) so it works from any clone, not just this rig. .gitignore
excludes .venv / caches.

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
2026-06-18 07:31:02 +05:00

216 lines
8.1 KiB
Python

"""Tests for the runner module — subprocess management and env injection."""
from __future__ import annotations
import asyncio
import os
from pathlib import Path
from unittest.mock import AsyncMock, patch, MagicMock
import pytest
from club3090_test_console.runner import TestRunner, TestConfig, RunState
from club3090_test_console.detect import ServingTarget, GpuInfo
from club3090_test_console.parsers import TestType
# ============================================================================
# Test command building
# ============================================================================
class TestCommandBuilding:
"""Test that _build_command produces correct commands and env."""
def _make_runner_with_target(self, target: ServingTarget) -> TestRunner:
runner = TestRunner(repo_root=Path("/repo"))
state = RunState(
test_type=TestType.BENCH,
config=TestConfig(test_type=TestType.BENCH),
target=target,
)
runner.current_run = state
return runner
def test_bench_command(self):
target = ServingTarget(url="http://localhost:8010", model="qwen3.6-27b", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(test_type=TestType.BENCH, run_count=3, warmups=2, only="narr")
runner.current_run.config = config
cmd, env = runner._build_command(config)
assert "scripts/bench.sh" in cmd
assert env["URL"] == "http://localhost:8010"
assert env["MODEL"] == "qwen3.6-27b"
assert env["CONTAINER"] == "vllm-test"
assert env["RUNS"] == "3"
assert env["WARMUPS"] == "2"
assert env["ONLY"] == "narr"
assert env["PYTHONUNBUFFERED"] == "1"
def test_bench_with_thinking(self):
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(test_type=TestType.BENCH, enable_thinking=True)
cmd, env = runner._build_command(config)
assert env["ENABLE_THINKING"] == "1"
def test_bench_with_force_tokens(self):
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(test_type=TestType.BENCH, force_tokens=2000)
cmd, env = runner._build_command(config)
assert env["FORCE_TOKENS"] == "2000"
def test_verify_full_command(self):
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(test_type=TestType.VERIFY_FULL, skip_tools=True, run_bench=True)
cmd, env = runner._build_command(config)
assert "scripts/verify-full.sh" in cmd
assert "--bench" in cmd
assert env["SKIP_TOOLS"] == "1"
def test_verify_stress_command(self):
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(
test_type=TestType.VERIFY_STRESS,
skip_longctx=True,
skip_tool_prefill=True,
)
cmd, env = runner._build_command(config)
assert "scripts/verify-stress.sh" in cmd
assert env["SKIP_LONGCTX"] == "1"
assert env["SKIP_TOOL_PREFILL"] == "1"
def test_quality_command(self):
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(
test_type=TestType.QUALITY,
quality_tier="full",
quality_pack="toolcall-15",
quality_repeat=3,
)
cmd, env = runner._build_command(config)
assert "scripts/quality-test.sh" in cmd
assert "--full" in cmd
assert "--pack" in cmd
assert "toolcall-15" in cmd
assert "--repeat" in cmd
assert "3" in cmd
assert env["BENCHLOCAL_HERMES_RESOLVE_LOCALHOST"] == "1" # localhost
def test_quality_non_localhost_no_hermes(self):
target = ServingTarget(url="http://192.168.1.50:8887", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(test_type=TestType.QUALITY, quality_tier="medium")
cmd, env = runner._build_command(config)
assert "BENCHLOCAL_HERMES_RESOLVE_LOCALHOST" not in env
def test_soak_command(self):
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(
test_type=TestType.SOAK,
soak_mode="fresh",
soak_sessions=20,
soak_turns=10,
soak_max_growth=300,
)
cmd, env = runner._build_command(config)
assert "scripts/soak-test.sh" in cmd
assert "--fresh" in cmd
assert env["SOAK_SESSIONS"] == "20"
assert env["SOAK_TURNS"] == "10"
assert env["SOAK_MAX_GROWTH_MIB"] == "300"
def test_rebench_command(self):
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(
test_type=TestType.REBENCH,
rebench_8pack="both",
rebench_skip=["soak"],
rebench_tag="test-run",
)
cmd, env = runner._build_command(config)
assert "scripts/rebench-full.sh" in cmd
assert "--with-8pack-thinking=both" in cmd
assert "--skip=soak" in cmd
assert "--tag=test-run" in cmd
def test_rebench_external_endpoint(self):
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
config = TestConfig(
test_type=TestType.REBENCH,
external_url="http://192.168.1.50:8887",
external_model="Qwen3.6-27B",
external_engine="llama-cpp",
)
cmd, env = runner._build_command(config)
assert "--url" in cmd
assert "http://192.168.1.50:8887" in cmd
assert "--model" in cmd
assert "Qwen3.6-27B" in cmd
assert "--engine" in cmd
assert "llama-cpp" in cmd
assert env["PREFLIGHT_NO_AUTODETECT"] == "1"
assert env["CONTAINER"] == "none"
def test_stdbuf_wrapping(self):
"""All commands should be wrapped in stdbuf for line-buffered output."""
target = ServingTarget(url="http://localhost:8010", model="qwen", container="vllm-test")
runner = self._make_runner_with_target(target)
for tt in TestType:
config = TestConfig(test_type=tt)
cmd, _ = runner._build_command(config)
assert cmd[0] == "stdbuf", f"{tt} command not wrapped in stdbuf"
# ============================================================================
# Test RunState
# ============================================================================
class TestRunState:
def test_elapsed_while_running(self):
import time
state = RunState(
test_type=TestType.BENCH,
config=TestConfig(test_type=TestType.BENCH),
target=ServingTarget(),
started=time.time() - 10,
)
assert state.elapsed_s >= 9
assert state.is_running is True
assert state.is_finished is False
def test_elapsed_after_finish(self):
state = RunState(
test_type=TestType.BENCH,
config=TestConfig(test_type=TestType.BENCH),
target=ServingTarget(),
started=1000.0,
finished=1060.0,
)
assert state.elapsed_s == 60.0
assert state.is_running is False
assert state.is_finished is True
def test_not_started(self):
state = RunState(
test_type=TestType.BENCH,
config=TestConfig(test_type=TestType.BENCH),
target=ServingTarget(),
)
assert state.elapsed_s == 0
assert state.is_running is False