From d072fec3f2f8ebe36c28be34b659990da34d8ffa Mon Sep 17 00:00:00 2001 From: Normann Date: Sun, 11 Oct 2026 08:39:16 +0200 Subject: [PATCH] test: resolve server temporary path aliases before containment checks (#1361) * test: canonicalize temporary server directory * test: compare resolved server configuration paths * test: check canonical system-test configuration path * test: expose path containment through pytest fixture * test: request containment fixture in server checks * test: use injected fixture in system path check --- tests/conftest.py | 11 ++++++++++- tests/test_server.py | 33 +++++++++++++++++++++++---------- tests/test_system.py | 4 ++-- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index fc03a72e..f9636fd1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -526,6 +526,15 @@ def cleanup_eos_eosdash( assert not running, f"Test server cleanup timed out for PIDs {running}" +@pytest.fixture +def is_in_test_dir() -> Callable[[Path, str], bool]: + """Check containment after resolving temporary-directory aliases.""" + def contains(path: Path, eos_dir: str) -> bool: + return path.resolve().is_relative_to(Path(eos_dir).resolve()) + + return contains + + @contextmanager def server_base( xprocess: XProcess, @@ -565,7 +574,7 @@ def server_base( eosdash_server = f"http://{eosdash_host}:{eosdash_port}" eos_tmp_dir = tempfile.TemporaryDirectory() - eos_dir = str(eos_tmp_dir.name) + eos_dir = str(Path(eos_tmp_dir.name).resolve()) eos_general_data_folder_path = str(Path(eos_dir) / "data") process_name = f"eos-{Path(eos_dir).name}" owned_processes: list[psutil.Process] = [] diff --git a/tests/test_server.py b/tests/test_server.py index 71f3fd28..c35bd093 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -24,8 +24,21 @@ from akkudoktoreos.server.server import ( ) +@pytest.mark.skipif(os.name == "nt", reason="Directory symlinks require privileges on Windows") +def test_temp_directory_alias_is_contained(tmp_path, is_in_test_dir): + """Resolved path containment accepts aliases but rejects sibling directories.""" + target = tmp_path / "server" + target.mkdir() + alias = tmp_path / "alias" + alias.symlink_to(target, target_is_directory=True) + + assert is_in_test_dir(alias / "config.json", str(target)) + assert is_in_test_dir(target / "config.json", str(alias)) + assert not is_in_test_dir(tmp_path / "server-other" / "config.json", str(target)) + + class TestServer: - def test_server_setup_for_class(self, server_setup_for_class): + def test_server_setup_for_class(self, server_setup_for_class, is_in_test_dir): """Ensure server is started.""" server = server_setup_for_class["server"] eos_dir = server_setup_for_class["eos_dir"] @@ -47,10 +60,10 @@ class TestServer: data_folder_path = Path(config_json["general"]["data_folder_path"]) data_ouput_path = Path(config_json["general"]["data_output_path"]) # Assure we are working in test environment - assert str(config_folder_path).startswith(eos_dir) - assert str(config_file_path).startswith(eos_dir) - assert str(data_folder_path).startswith(eos_dir) - assert str(data_ouput_path).startswith(eos_dir) + assert is_in_test_dir(config_folder_path, eos_dir) + assert is_in_test_dir(config_file_path, eos_dir) + assert is_in_test_dir(data_folder_path, eos_dir) + assert is_in_test_dir(data_ouput_path, eos_dir) class TestServerSettingsValidation: @@ -238,7 +251,7 @@ class TestServerStartStop: await asyncio.wait_for(starteosdash.eosdash_proc.wait(), timeout=timeout) @pytest.mark.skipif(os.name == "nt", reason="Server restart not supported on Windows") - def test_server_restart(self, server_setup_for_function, is_system_test): + def test_server_restart(self, server_setup_for_function, is_system_test, is_in_test_dir): """Test server restart.""" server = server_setup_for_function["server"] eos_dir = server_setup_for_function["eos_dir"] @@ -257,10 +270,10 @@ class TestServerStartStop: "cachefilestore.json" ) # Assure we are working in test environment - assert str(config_folder_path).startswith(eos_dir) - assert str(config_file_path).startswith(eos_dir) - assert str(data_folder_path).startswith(eos_dir) - assert str(data_ouput_path).startswith(eos_dir) + assert is_in_test_dir(config_folder_path, eos_dir) + assert is_in_test_dir(config_file_path, eos_dir) + assert is_in_test_dir(data_folder_path, eos_dir) + assert is_in_test_dir(data_ouput_path, eos_dir) if is_system_test: # Prepare cache entry and get cached data diff --git a/tests/test_system.py b/tests/test_system.py index 68c32c62..f8581726 100644 --- a/tests/test_system.py +++ b/tests/test_system.py @@ -18,7 +18,7 @@ FILE_TESTDATA_EOSSERVER_CONFIG_1 = DIR_TESTDATA.joinpath("eosserver_config_1.jso class TestSystem: - def test_prediction_brightsky(self, server_setup_for_class, is_system_test): + def test_prediction_brightsky(self, server_setup_for_class, is_system_test, is_in_test_dir): """Test weather prediction by BrightSky.""" server = server_setup_for_class["server"] eos_dir = server_setup_for_class["eos_dir"] @@ -30,7 +30,7 @@ class TestSystem: config_json = result.json() config_folder_path = Path(config_json["general"]["config_folder_path"]) # Assure we are working in test environment - assert str(config_folder_path).startswith(eos_dir) + assert is_in_test_dir(config_folder_path, eos_dir) result = requests.put(f"{server}/v1/config/weather/provider", json="BrightSky") assert result.status_code == HTTPStatus.OK