diff --git a/tests/test_live_gui_workspace_fixture.py b/tests/test_live_gui_workspace_fixture.py index 4d8a0b92..2f65ac18 100644 --- a/tests/test_live_gui_workspace_fixture.py +++ b/tests/test_live_gui_workspace_fixture.py @@ -1,4 +1,6 @@ """Tests for the live_gui_workspace fixture (Phase 3, Task 3.2).""" +import importlib.util +import shutil from pathlib import Path @@ -29,3 +31,34 @@ def test_live_gui_workspace_writable(live_gui_workspace) -> None: test_file = live_gui_workspace / "test_write.txt" test_file.write_text("hello", encoding="utf-8") assert test_file.read_text(encoding="utf-8") == "hello" + + +def test_live_gui_workspace_recreates_missing_workspace(live_gui) -> None: + """[TDD red] Captures the xdist race in live_gui_workspace: when the owner + worker's live_gui teardown rmtree's the shared workspace between client + workers' tests, a client worker calling live_gui_workspace must recreate + the directory before returning the path. The current fixture returns the + path without mkdir, so the returned path does not exist after rmtree. + NOTE: on Windows, the live_gui subprocess holds the live workspace as its + CWD, which blocks shutil.rmtree on the live path. We instead point the + handle at a fresh, never-existed path under tests/artifacts/ to simulate + the post-teardown state deterministically on all platforms. + """ + import time + fake_workspace = Path(f"tests/artifacts/_live_gui_workspace_race_sim_{int(time.time() * 1000000)}") + assert not fake_workspace.exists(), f"sanity: fake workspace must not exist pre-call: {fake_workspace}" + original_workspace = live_gui._workspace + live_gui._workspace = fake_workspace + try: + spec = importlib.util.spec_from_file_location( + "tests.conftest", + str(Path(__file__).parent / "conftest.py"), + ) + conftest = importlib.util.module_from_spec(spec) + spec.loader.exec_module(conftest) + result = conftest.live_gui_workspace.__wrapped__(live_gui) + assert result.exists(), f"live_gui_workspace did not recreate missing workspace at {result}" + finally: + live_gui._workspace = original_workspace + if fake_workspace.exists(): + shutil.rmtree(fake_workspace, ignore_errors=True)