From 9797efd274e5d75f5306097e16b4faa1bb672545 Mon Sep 17 00:00:00 2001 From: Mohammed Alkindi Date: Mon, 14 Sep 2026 16:04:38 +0400 Subject: [PATCH] fix(tests): skip symlink tests when symlink creation is unprivileged os.symlink needs Administrator rights or Developer Mode on Windows, and Developer Mode is off by default. Five functional tests called it unguarded, so a local Windows contributor running make pr saw WinError 1314 rather than a clean run. Probe the capability in tests/testing_utils.py and skipUnless on it, matching the Windows-awareness already in read_link_without_junction_prefix. --- tests/functional/test_actions.py | 9 ++++++++- tests/functional/test_utils.py | 8 +++++++- tests/testing_utils.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_actions.py b/tests/functional/test_actions.py index d26647e2e..ae6898c4b 100644 --- a/tests/functional/test_actions.py +++ b/tests/functional/test_actions.py @@ -1,13 +1,17 @@ import os from pathlib import Path import tempfile +import unittest from unittest import TestCase from parameterized import parameterized from aws_lambda_builders.actions import CopyDependenciesAction, LinkSinglePathAction, MoveDependenciesAction from aws_lambda_builders.utils import copytree -from tests.testing_utils import read_link_without_junction_prefix +from tests.testing_utils import read_link_without_junction_prefix, symlinks_supported + +SYMLINKS_SUPPORTED = symlinks_supported() +SYMLINKS_UNSUPPORTED_REASON = "Creating symlinks requires Administrator privileges or Developer Mode on this platform" class TestCopyDependenciesAction(TestCase): @@ -32,6 +36,7 @@ def test_copy_dependencies_action(self, source_folder): self.assertEqual(set(os.listdir(test_folder)), set(os.listdir(target))) + @unittest.skipUnless(SYMLINKS_SUPPORTED, SYMLINKS_UNSUPPORTED_REASON) def test_must_maintain_symlinks_if_enabled(self): with tempfile.TemporaryDirectory() as tmpdir: source_dir = os.path.join(tmpdir, "source") @@ -56,6 +61,7 @@ def test_must_maintain_symlinks_if_enabled(self): destination_node_modules_target = read_link_without_junction_prefix(destination_node_modules) self.assertEqual(destination_node_modules_target, source_node_modules) + @unittest.skipUnless(SYMLINKS_SUPPORTED, SYMLINKS_UNSUPPORTED_REASON) def test_must_not_maintain_symlinks_by_default(self): with tempfile.TemporaryDirectory() as tmpdir: source_dir = os.path.join(tmpdir, "source") @@ -78,6 +84,7 @@ def test_must_not_maintain_symlinks_by_default(self): class TestLinkSinglePathAction(TestCase): + @unittest.skipUnless(SYMLINKS_SUPPORTED, SYMLINKS_UNSUPPORTED_REASON) def test_link_directory(self): with tempfile.TemporaryDirectory() as tmpdir: source_dir = os.path.join(tmpdir, "source") diff --git a/tests/functional/test_utils.py b/tests/functional/test_utils.py index a10b57d41..19cc08124 100644 --- a/tests/functional/test_utils.py +++ b/tests/functional/test_utils.py @@ -2,12 +2,16 @@ import tempfile import shutil import platform +import unittest from tarfile import ExtractError from unittest import TestCase from aws_lambda_builders.utils import copytree, get_goarch, extract_tarfile -from tests.testing_utils import read_link_without_junction_prefix +from tests.testing_utils import read_link_without_junction_prefix, symlinks_supported + +SYMLINKS_SUPPORTED = symlinks_supported() +SYMLINKS_UNSUPPORTED_REASON = "Creating symlinks requires Administrator privileges or Developer Mode on this platform" class TestCopyTree(TestCase): @@ -65,6 +69,7 @@ def test_must_return_valid_go_architecture(self): self.assertEqual(get_goarch("x86_64"), "amd64") self.assertEqual(get_goarch(""), "amd64") + @unittest.skipUnless(SYMLINKS_SUPPORTED, SYMLINKS_UNSUPPORTED_REASON) def test_must_maintain_symlinks_if_enabled(self): # set up symlinked file and directory source_target_file_path = file(self.source, "targetfile.txt") @@ -92,6 +97,7 @@ def test_must_maintain_symlinks_if_enabled(self): dest_symlink_dir_target = read_link_without_junction_prefix(dest_symlink_file_path) self.assertEqual(dest_symlink_dir_target, source_target_file_path) + @unittest.skipUnless(SYMLINKS_SUPPORTED, SYMLINKS_UNSUPPORTED_REASON) def test_must_not_maintain_symlinks_by_default(self): # set up symlinked file and directory source_target_file_path = file(self.source, "targetfile.txt") diff --git a/tests/testing_utils.py b/tests/testing_utils.py index 345f62e50..8b1787ff6 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -1,4 +1,34 @@ import os +import sys +import tempfile + + +def symlinks_supported() -> bool: + """ + Returns True if the current platform and user are able to create symlinks. + + On Windows, os.symlink() requires either Administrator privileges or Developer + Mode to be enabled (Windows 10+). Without either, it raises + ``OSError: [WinError 1314] A required privilege is not held by the client``. + Developer Mode is off by default, so this is the common case for a Windows + contributor running the test suite locally. + + Returns + ------- + bool + True if os.symlink() is expected to succeed on this machine. + """ + if sys.platform != "win32": + return True + with tempfile.TemporaryDirectory() as tmpdir: + target = os.path.join(tmpdir, "target") + link = os.path.join(tmpdir, "link") + open(target, "w").close() + try: + os.symlink(target, link) + except OSError: + return False + return True def read_link_without_junction_prefix(path: str) -> str: