|
8 | 8 | import importlib.util |
9 | 9 | import io |
10 | 10 | import json |
| 11 | +import os |
11 | 12 | import pathlib |
12 | 13 | import subprocess |
13 | 14 | import tempfile |
14 | | -from contextlib import redirect_stdout |
| 15 | +from contextlib import redirect_stderr, redirect_stdout |
15 | 16 | from types import ModuleType |
16 | 17 | from typing import Any |
17 | 18 |
|
@@ -1792,6 +1793,40 @@ def test_submit_and_cancel_require_event_log_for_queue_mutation() -> None: |
1792 | 1793 | raise AssertionError("cancel without event log was accepted") |
1793 | 1794 |
|
1794 | 1795 |
|
| 1796 | +def test_submit_and_cancel_parse_event_log_default_from_env() -> None: |
| 1797 | + scheduler = load_scheduler() |
| 1798 | + previous = os.environ.get("TC_SCHEDULER_EVENT_LOG_JSONL") |
| 1799 | + os.environ["TC_SCHEDULER_EVENT_LOG_JSONL"] = "/var/lib/tensorcore/events.jsonl" |
| 1800 | + try: |
| 1801 | + submit_args = scheduler.parse_args( |
| 1802 | + [ |
| 1803 | + "submit", |
| 1804 | + "--job-json", |
| 1805 | + "job.json", |
| 1806 | + "--jobs-json", |
| 1807 | + "queue.json", |
| 1808 | + "--inventory-json", |
| 1809 | + "inventory.json", |
| 1810 | + ] |
| 1811 | + ) |
| 1812 | + cancel_args = scheduler.parse_args( |
| 1813 | + [ |
| 1814 | + "cancel", |
| 1815 | + "--jobs-json", |
| 1816 | + "queue.json", |
| 1817 | + "--job-id", |
| 1818 | + "job-1", |
| 1819 | + ] |
| 1820 | + ) |
| 1821 | + finally: |
| 1822 | + if previous is None: |
| 1823 | + os.environ.pop("TC_SCHEDULER_EVENT_LOG_JSONL", None) |
| 1824 | + else: |
| 1825 | + os.environ["TC_SCHEDULER_EVENT_LOG_JSONL"] = previous |
| 1826 | + assert submit_args.event_log_jsonl == "/var/lib/tensorcore/events.jsonl" |
| 1827 | + assert cancel_args.event_log_jsonl == "/var/lib/tensorcore/events.jsonl" |
| 1828 | + |
| 1829 | + |
1795 | 1830 | def test_cancel_accepts_expanded_pool_job_id() -> None: |
1796 | 1831 | scheduler = load_scheduler() |
1797 | 1832 | with tempfile.TemporaryDirectory() as tmp: |
@@ -1941,6 +1976,29 @@ def test_status_reports_gpu_reconciliation_audit_gate() -> None: |
1941 | 1976 | assert gate["cuda_job_count"] == 1 |
1942 | 1977 |
|
1943 | 1978 |
|
| 1979 | +def test_status_rejects_nonpositive_gpu_reconciliation_max_age() -> None: |
| 1980 | + scheduler = load_scheduler() |
| 1981 | + stderr = io.StringIO() |
| 1982 | + with redirect_stderr(stderr): |
| 1983 | + try: |
| 1984 | + scheduler.parse_args( |
| 1985 | + [ |
| 1986 | + "status", |
| 1987 | + "--inventory-json", |
| 1988 | + "inventory.json", |
| 1989 | + "--gpu-reconciliation-audit-json", |
| 1990 | + "audit.json", |
| 1991 | + "--gpu-reconciliation-max-age-sec", |
| 1992 | + "0", |
| 1993 | + ] |
| 1994 | + ) |
| 1995 | + except SystemExit as exc: |
| 1996 | + assert exc.code == 2 |
| 1997 | + else: |
| 1998 | + raise AssertionError("nonpositive status GPU reconciliation max age was accepted") |
| 1999 | + assert "--gpu-reconciliation-max-age-sec must be > 0" in stderr.getvalue() |
| 2000 | + |
| 2001 | + |
1944 | 2002 | def test_scheduler_dry_run_includes_launch_plan() -> None: |
1945 | 2003 | candidate = job("qllm-phase1", priority=50) |
1946 | 2004 | candidate["cwd"] = "scripts" |
@@ -2287,10 +2345,12 @@ def main() -> int: |
2287 | 2345 | test_submit_dry_run_expands_tensorcore_job_v1() |
2288 | 2346 | test_submit_writes_queue_and_cancel_pauses_job() |
2289 | 2347 | test_submit_and_cancel_require_event_log_for_queue_mutation() |
| 2348 | + test_submit_and_cancel_parse_event_log_default_from_env() |
2290 | 2349 | test_cancel_accepts_expanded_pool_job_id() |
2291 | 2350 | test_submit_rejects_malformed_tensorcore_job_v1_fields() |
2292 | 2351 | test_status_offline_reports_inventory_jobs_and_drain_updates_copy() |
2293 | 2352 | test_status_reports_gpu_reconciliation_audit_gate() |
| 2353 | + test_status_rejects_nonpositive_gpu_reconciliation_max_age() |
2294 | 2354 | test_scheduler_dry_run_includes_launch_plan() |
2295 | 2355 | test_scheduler_blocks_cuda_when_gpu_reconciliation_audit_missing() |
2296 | 2356 | test_scheduler_keeps_non_cuda_placement_when_gpu_reconciliation_audit_missing() |
|
0 commit comments