diff --git a/backend/tests/test_skill_mutation_host_lifecycle.py b/backend/tests/test_skill_mutation_host_lifecycle.py index ad5a4ccc39..3cc3ab8685 100644 --- a/backend/tests/test_skill_mutation_host_lifecycle.py +++ b/backend/tests/test_skill_mutation_host_lifecycle.py @@ -1,6 +1,7 @@ """The host owns recovery even when every extension is disabled.""" import asyncio +import os import threading from contextlib import contextmanager from types import SimpleNamespace @@ -21,6 +22,8 @@ from deerflow.persistence.skill_mutations.model import SkillOwnerRow, SkillPropo from deerflow.persistence.user.model import UserRow from deerflow.runtime.events.store.db import DbRunEventStore +pytestmark = pytest.mark.skipif(os.name != "posix", reason="host skill mutation tests require POSIX filesystem primitives") + @pytest_asyncio.fixture async def durable(tmp_path): diff --git a/backend/tests/test_skill_mutation_package_cost.py b/backend/tests/test_skill_mutation_package_cost.py index 7e17c2c248..e83d3d766a 100644 --- a/backend/tests/test_skill_mutation_package_cost.py +++ b/backend/tests/test_skill_mutation_package_cost.py @@ -1,5 +1,6 @@ """Reproducible local capture measurements; not a model/provider benchmark.""" +import os import statistics import time @@ -7,6 +8,8 @@ import pytest from deerflow.skills.mutations.assets import capture_package +pytestmark = pytest.mark.skipif(os.name != "posix", reason="package capture requires POSIX filesystem primitives") + @pytest.mark.parametrize("files,size", [(4, 1024), (256, 65535)]) def test_capture_cost_at_typical_and_package_limit(tmp_path, files, size): diff --git a/backend/tests/test_skill_mutation_validation.py b/backend/tests/test_skill_mutation_validation.py index 5f30babce2..6a0d14859d 100644 --- a/backend/tests/test_skill_mutation_validation.py +++ b/backend/tests/test_skill_mutation_validation.py @@ -54,6 +54,7 @@ def test_non_finite_frontmatter_has_stable_error_code(): validate_candidate(content, content, "example") +@pytest.mark.skipif(os.name != "posix", reason="package capture requires POSIX filesystem primitives") def test_package_snapshot_covers_support_bytes_and_execute_flags(tmp_path): (tmp_path / "SKILL.md").write_text(BASE, encoding="utf-8") support = tmp_path / "script.sh" @@ -68,6 +69,7 @@ def test_package_snapshot_covers_support_bytes_and_execute_flags(tmp_path): assert before_mode.digest != capture_package(tmp_path).digest +@pytest.mark.skipif(os.name != "posix", reason="package capture requires POSIX filesystem primitives") def test_snapshot_detached_from_live_files(tmp_path): main = tmp_path / "SKILL.md" main.write_text(BASE, encoding="utf-8") @@ -109,6 +111,7 @@ def test_automatic_capture_rejects_links_and_special_files(tmp_path, kind): capture_package(root) +@pytest.mark.skipif(os.name != "posix", reason="package capture requires POSIX filesystem primitives") def test_snapshot_never_returns_a_partially_captured_package(tmp_path): (tmp_path / "SKILL.md").write_text(BASE, encoding="utf-8") for index in range(256): diff --git a/backend/tests/test_skill_mutations.py b/backend/tests/test_skill_mutations.py index cf9a540672..de53c6f160 100644 --- a/backend/tests/test_skill_mutations.py +++ b/backend/tests/test_skill_mutations.py @@ -2,6 +2,7 @@ import asyncio import importlib.util +import os from dataclasses import asdict, dataclass from types import SimpleNamespace @@ -24,6 +25,8 @@ from deerflow.skills.mutations.repository import SkillMutationRepository from deerflow.skills.mutations.scanner import ScanVerdict from deerflow.skills.storage.user_scoped_skill_storage import UserScopedSkillStorage +pytestmark = pytest.mark.skipif(os.name != "posix", reason="host skill mutation tests require POSIX filesystem primitives") + CONTENT = "---\nname: example\ndescription: Before\n---\nA\n" NEW = CONTENT.replace("\nA\n", "\nB\n") diff --git a/backend/tests/test_skill_sdk_enrollment.py b/backend/tests/test_skill_sdk_enrollment.py index 65392bad31..f25089b993 100644 --- a/backend/tests/test_skill_sdk_enrollment.py +++ b/backend/tests/test_skill_sdk_enrollment.py @@ -47,6 +47,9 @@ def sdk_home(tmp_path): sessions = sessionmaker(engine, expire_on_commit=False) with sessions.begin() as session: session.add(UserRow(id="owner", email="owner@example.test")) + # Release the fixture's pooled SQLite connection before SDK subprocesses + # exercise database removal, which Windows forbids while the file is open. + engine.dispose() repository = SkillMutationRepository(sessions) yield tmp_path, config, root, repository engine.dispose() @@ -84,6 +87,7 @@ else: return json.loads(result.stdout.splitlines()[-1]) +@pytest.mark.skipif(os.name != "posix", reason="package capture requires POSIX filesystem primitives") @pytest.mark.parametrize( "action", [ @@ -105,6 +109,7 @@ def test_fresh_sdk_refuses_enrolled_owner_without_gateway_runtime(sdk_home, acti assert after.mutation_seq == before.mutation_seq +@pytest.mark.skipif(os.name != "posix", reason="package capture requires POSIX filesystem primitives") def test_fresh_sdk_global_toggle_cannot_bypass_another_enrolled_owner(sdk_home): _, _, root, repository = sdk_home repository.observe("owner", "example", capture_package(root).digest, True, exists=True) diff --git a/backend/tests/test_skill_storage_support_files.py b/backend/tests/test_skill_storage_support_files.py index 94fbbcdd15..3baacc25fa 100644 --- a/backend/tests/test_skill_storage_support_files.py +++ b/backend/tests/test_skill_storage_support_files.py @@ -124,6 +124,7 @@ def _write_literal(path: Path, data: bytes) -> None: path.write_bytes(data) +@pytest.mark.skipif(os.name == "nt", reason="Windows cannot create reserved AUX device names") def test_remove_preexisting_nonportable_support_file(storage, skill_dir): """Deletion of a stored non-portable name succeeds; creating one still fails.""" target = skill_dir / "scripts" / "AUX.py"