diff --git a/tests/lib/test_bedrock.py b/tests/lib/test_bedrock.py index 901c93aec..311e0b7ad 100644 --- a/tests/lib/test_bedrock.py +++ b/tests/lib/test_bedrock.py @@ -1,3 +1,4 @@ +import os import re import json import base64 @@ -66,12 +67,18 @@ def mock_aws_config( profiles: t.List[AwsConfigProfile], monkeypatch: t.Any, ) -> t.Iterable[None]: - with tempfile.NamedTemporaryFile(mode="w+", delete=True) as temp_file: + # `delete=True` keeps the handle open, and a second open of that file fails + # on Windows while botocore reads it; close and unlink explicitly instead. + temp_file = tempfile.NamedTemporaryFile(mode="w+", delete=False) + try: for profile in profiles: temp_file.write(profile_to_ini(profile)) temp_file.flush() - monkeypatch.setenv("AWS_CONFIG_FILE", str(temp_file.name)) + temp_file.close() + monkeypatch.setenv("AWS_CONFIG_FILE", temp_file.name) yield + finally: + os.unlink(temp_file.name) @pytest.mark.respx() diff --git a/tests/lib/tools/memory_sync/test_delete_discipline.py b/tests/lib/tools/memory_sync/test_delete_discipline.py index 0483d0c23..eb34df90f 100644 --- a/tests/lib/tools/memory_sync/test_delete_discipline.py +++ b/tests/lib/tools/memory_sync/test_delete_discipline.py @@ -19,6 +19,9 @@ import pytest import anthropic.lib.tools._memories as memories_mod + +pytestmark = pytest.mark.skipif(os.name != "posix", reason="FileStore-backed sync suites refuse non-POSIX platforms") + from anthropic.lib.tools._memories import MemoryDeleteMode, SessionMemoryStores from .conftest import Clock, run_sync, listing_interrupted_by @@ -165,7 +168,7 @@ async def test_a_folder_destroyed_mid_sync_sends_no_deletes(tmp_path: Path, cloc assert server.files == {"a.md": "v1", "keep.md": "v1"} -@pytest.mark.skipif(os.geteuid() == 0, reason="permission checks do not bind root") +@pytest.mark.skipif(os.name == "posix" and os.geteuid() == 0, reason="permission checks do not bind root") @pytest.mark.asyncio() async def test_an_unreadable_subdirectory_fails_the_pass_instead_of_reading_as_deletions( tmp_path: Path, clock: Clock, caplog: pytest.LogCaptureFixture diff --git a/tests/lib/tools/memory_sync/test_e2e_memory.py b/tests/lib/tools/memory_sync/test_e2e_memory.py index 164dcf921..1b2f55ad5 100644 --- a/tests/lib/tools/memory_sync/test_e2e_memory.py +++ b/tests/lib/tools/memory_sync/test_e2e_memory.py @@ -10,6 +10,7 @@ from __future__ import annotations +import os import json import time import base64 @@ -24,6 +25,9 @@ import anthropic.lib.tools._memories as memories_mod import anthropic.lib.environments._worker as worker_mod + +pytestmark = pytest.mark.skipif(os.name != "posix", reason="FileStore-backed sync suites refuse non-POSIX platforms") + from anthropic.lib.tools._memories import MemoryDeleteMode from anthropic.lib.environments._worker import EnvironmentWorker diff --git a/tests/lib/tools/memory_sync/test_final_flush.py b/tests/lib/tools/memory_sync/test_final_flush.py index ce3576fa7..708c37fd2 100644 --- a/tests/lib/tools/memory_sync/test_final_flush.py +++ b/tests/lib/tools/memory_sync/test_final_flush.py @@ -8,6 +8,7 @@ from __future__ import annotations +import os from typing import Any from pathlib import Path @@ -15,6 +16,9 @@ import pytest import anthropic.lib.tools._memories as memories_mod + +pytestmark = pytest.mark.skipif(os.name != "posix", reason="FileStore-backed sync suites refuse non-POSIX platforms") + from anthropic.lib.tools._memories import MARKER_PATH, SessionMemoryStores from .conftest import run_sync diff --git a/tests/lib/tools/memory_sync/test_folder_trust.py b/tests/lib/tools/memory_sync/test_folder_trust.py index 83d59af1f..accef2387 100644 --- a/tests/lib/tools/memory_sync/test_folder_trust.py +++ b/tests/lib/tools/memory_sync/test_folder_trust.py @@ -9,12 +9,16 @@ from __future__ import annotations +import os import shutil from pathlib import Path import pytest import anthropic.lib.tools._memories as memories_mod + +pytestmark = pytest.mark.skipif(os.name != "posix", reason="FileStore-backed sync suites refuse non-POSIX platforms") + from anthropic.lib.tools._memories import MARKER_PATH, SessionMemoryStores from .conftest import Clock, run_sync, listing_interrupted_by diff --git a/tests/lib/tools/memory_sync/test_keep_only_copy.py b/tests/lib/tools/memory_sync/test_keep_only_copy.py index 1f6161ea7..843776100 100644 --- a/tests/lib/tools/memory_sync/test_keep_only_copy.py +++ b/tests/lib/tools/memory_sync/test_keep_only_copy.py @@ -6,10 +6,13 @@ from __future__ import annotations +import os from pathlib import Path import pytest +pytestmark = pytest.mark.skipif(os.name != "posix", reason="FileStore-backed sync suites refuse non-POSIX platforms") + from anthropic.lib.tools._memories import SessionMemoryStores from .conftest import run_sync, listing_interrupted_by diff --git a/tests/lib/tools/memory_sync/test_lost_response.py b/tests/lib/tools/memory_sync/test_lost_response.py index 18290c109..dc8eda4e5 100644 --- a/tests/lib/tools/memory_sync/test_lost_response.py +++ b/tests/lib/tools/memory_sync/test_lost_response.py @@ -8,6 +8,7 @@ from __future__ import annotations +import os from typing import Any from pathlib import Path from contextlib import contextmanager @@ -16,6 +17,8 @@ import httpx2 import pytest +pytestmark = pytest.mark.skipif(os.name != "posix", reason="FileStore-backed sync suites refuse non-POSIX platforms") + from anthropic import APIConnectionError from anthropic.lib.tools._memories import SessionMemoryStores diff --git a/tests/lib/tools/memory_sync/test_memory_sync.py b/tests/lib/tools/memory_sync/test_memory_sync.py index df55bc235..593dbca23 100644 --- a/tests/lib/tools/memory_sync/test_memory_sync.py +++ b/tests/lib/tools/memory_sync/test_memory_sync.py @@ -18,6 +18,8 @@ import pytest +pytestmark = pytest.mark.skipif(os.name != "posix", reason="FileStore-backed sync suites refuse non-POSIX platforms") + from anthropic._compat import PYDANTIC_V1 from anthropic.lib.tools import _memories as memories_mod from anthropic.lib.tools._memories import SessionMemoryError, SessionMemoryStores diff --git a/tests/lib/tools/memory_sync/test_two_pass.py b/tests/lib/tools/memory_sync/test_two_pass.py index 278654691..4bb630cdd 100644 --- a/tests/lib/tools/memory_sync/test_two_pass.py +++ b/tests/lib/tools/memory_sync/test_two_pass.py @@ -10,11 +10,14 @@ from __future__ import annotations +import os from typing import Any from pathlib import Path import pytest +pytestmark = pytest.mark.skipif(os.name != "posix", reason="FileStore-backed sync suites refuse non-POSIX platforms") + from anthropic.lib.tools._memories import SessionMemoryStores from .conftest import run_sync diff --git a/tests/lib/tools/test_agent_toolset.py b/tests/lib/tools/test_agent_toolset.py index d52c5e8f1..d7b793f86 100644 --- a/tests/lib/tools/test_agent_toolset.py +++ b/tests/lib/tools/test_agent_toolset.py @@ -784,6 +784,7 @@ async def test_bash_outer_cancel_closes_subprocess_no_stale_state(tmp_path: Path @needs_pydantic_v2 +@needs_symlinks async def test_read_through_symlink_escape_is_rejected(tmp_path: Path) -> None: """resolve_path realpaths, so a symlink that escapes the workdir is caught.""" outside = tmp_path / "outside" @@ -886,6 +887,7 @@ async def test_glob_rejects_dotdot_pattern(tmp_path: Path) -> None: @needs_pydantic_v2 +@needs_symlinks async def test_glob_post_filters_symlink_escape(tmp_path: Path) -> None: """A symlink traversed mid-pattern must not let a glob result escape the workdir.""" outside = tmp_path / "outside" @@ -911,6 +913,7 @@ async def test_glob_drops_matches_that_cannot_be_canonicalised(tmp_path: Path) - @needs_pydantic_v2 +@needs_symlinks async def test_grep_skips_symlinked_files(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """The fallback walker must not read through a symlink that escapes the workdir.""" secret = tmp_path / "secret.txt" diff --git a/tests/lib/tools/test_file_store.py b/tests/lib/tools/test_file_store.py index 3b694a8b2..97f6d10cf 100644 --- a/tests/lib/tools/test_file_store.py +++ b/tests/lib/tools/test_file_store.py @@ -204,7 +204,7 @@ async def test_filestore_hashtree_hashes_regular_files_and_skips_symlinks(tmp_pa assert await store.hashtree("sub/") == {"sub/b.md": _sha("bravo")} -@pytest.mark.skipif(os.geteuid() == 0, reason="permission checks do not bind root") +@pytest.mark.skipif(os.name == "posix" and os.geteuid() == 0, reason="permission checks do not bind root") @pytest.mark.asyncio() async def test_filestore_listings_raise_on_an_unreadable_subdirectory(store: FileStore) -> None: """An unreadable subdirectory fails the listing rather than dropping out of it.""" @@ -314,7 +314,7 @@ async def test_filestore_remove_unlinks_a_dangling_symlink(store: FileStore) -> assert not (store.root().path / "dangling.md").is_symlink() -@pytest.mark.skipif(os.geteuid() == 0, reason="permission checks do not bind root") +@pytest.mark.skipif(os.name == "posix" and os.geteuid() == 0, reason="permission checks do not bind root") @pytest.mark.asyncio() async def test_filestore_open_raises_on_an_unreadable_mount_path(tmp_path: Path) -> None: """A mount path the process cannot stat raises at open — it is never read