From 26254240249f037fb38344aaf066db4a9a4256af Mon Sep 17 00:00:00 2001 From: xiaodu55 <1704788159@qq.com> Date: Fri, 2 Oct 2026 22:45:26 +0800 Subject: [PATCH] test(studio): stop the dev server gracefully so its persistence flush lands on Windows (#6197) terminate() is TerminateProcess on Windows: langgraph dev never runs its shutdown hooks, so .langgraph_ops.pckl is never flushed and state does not survive the restart the two cross-restart tests exercise. Create the child with CREATE_NEW_PROCESS_GROUP and stop it via CTRL_BREAK_EVENT (handled by uvicorn as SIGBREAK) on Windows, keeping terminate() on POSIX and a hard kill as the timeout fallback. Fixes #6196 Co-authored-by: xiaodu55 --- backend/tests/test_langgraph_studio_routes.py | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/backend/tests/test_langgraph_studio_routes.py b/backend/tests/test_langgraph_studio_routes.py index 11634149d0..8cf2c793a0 100644 --- a/backend/tests/test_langgraph_studio_routes.py +++ b/backend/tests/test_langgraph_studio_routes.py @@ -5,6 +5,7 @@ from __future__ import annotations import json import os import shutil +import signal import socket import subprocess import sys @@ -173,6 +174,26 @@ def _run_stateless_scope(client: httpx.Client, run_id: str) -> str: return messages[-1]["content"][0]["text"] +def _stop_studio_process(process: subprocess.Popen) -> None: + """Stop the dev server the way its persistence layer survives. + + ``terminate()`` is TerminateProcess on Windows: the dev server never runs + its shutdown hooks, so the dev persistence flush never lands and state + does not survive the restart under test. uvicorn handles SIGBREAK, + delivered via CTRL_BREAK_EVENT to the child's process group; fall back to + a hard kill when the graceful stop does not finish in time. + """ + if os.name == "nt": + process.send_signal(signal.CTRL_BREAK_EVENT) + else: + process.terminate() + try: + process.wait(timeout=10) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=10) + + @contextmanager def _running_studio_server( runtime_dir: Path, @@ -231,6 +252,7 @@ def _running_studio_server( stdout=log_file, stderr=subprocess.STDOUT, text=True, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == "nt" else 0, ) base_url = f"http://127.0.0.1:{port}" @@ -249,12 +271,7 @@ def _running_studio_server( last_error = exc time.sleep(0.1) else: - process.terminate() - try: - process.wait(timeout=10) - except subprocess.TimeoutExpired: - process.kill() - process.wait(timeout=10) + _stop_studio_process(process) pytest.fail(f"LangGraph dev server failed to start ({last_error!r}).\n{log_path.read_text(encoding='utf-8')}") client = httpx.Client( @@ -267,12 +284,7 @@ def _running_studio_server( yield client finally: client.close() - process.terminate() - try: - process.wait(timeout=10) - except subprocess.TimeoutExpired: - process.kill() - process.wait(timeout=10) + _stop_studio_process(process) @pytest.fixture(scope="module")