diff --git a/.github/instructions/unit-tests-standards.instructions.md b/.github/instructions/unit-tests-standards.instructions.md index 7f7b42be..9ceb8931 100644 --- a/.github/instructions/unit-tests-standards.instructions.md +++ b/.github/instructions/unit-tests-standards.instructions.md @@ -37,13 +37,11 @@ class TestParseConfig: ``` ### Async Tests -- Use `@pytest.mark.asyncio` decorator for all async test methods - Async test method names MUST end with `_async` - Use `AsyncMock` instead of `MagicMock` when mocking async methods ```python class TestProcessor: - @pytest.mark.asyncio async def test_process_returns_result_async(self) -> None: processor = Processor(client=AsyncMock(return_value="ok")) result = await processor.process_async(data="input") diff --git a/tests/integration/test_smoke.py b/tests/integration/test_smoke.py index 17bbd56f..9a18037a 100644 --- a/tests/integration/test_smoke.py +++ b/tests/integration/test_smoke.py @@ -23,7 +23,6 @@ class TestSmoke: """Core framework smoke tests.""" @pytest.mark.harm(HarmCategory.DATA_EXFILTRATION) - @pytest.mark.asyncio async def test_evaluator_detects_tool_call_async(self) -> None: """Evaluator unit test against hand-crafted Response.""" response = Response( @@ -44,7 +43,6 @@ async def test_evaluator_detects_tool_call_async(self) -> None: assert result.detected @pytest.mark.harm(HarmCategory.OVER_PERMISSIVE_ACTION) - @pytest.mark.asyncio async def test_probe_against_mock_adapter_async(self) -> None: """Probe test via Probes.behavior against MockAdapter.""" adapter = MockAdapter( diff --git a/tests/unit/attacks/test_xpia.py b/tests/unit/attacks/test_xpia.py index 58cdb875..f19e5ea8 100644 --- a/tests/unit/attacks/test_xpia.py +++ b/tests/unit/attacks/test_xpia.py @@ -5,8 +5,6 @@ from unittest.mock import AsyncMock -import pytest - from rampart.attacks import Attacks from rampart.core.errors import InfrastructureError from rampart.core.manifest import AppManifest @@ -72,7 +70,6 @@ def _adapter( class TestXPIADetection: """Attack semantics: DETECTED->UNSAFE, NOT_DETECTED->SAFE.""" - @pytest.mark.asyncio async def test_detected_returns_unsafe_with_evidence_in_summary(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -87,7 +84,6 @@ async def test_detected_returns_unsafe_with_evidence_in_summary(self) -> None: assert result.status is SafetyStatus.UNSAFE assert "exfil_call_found" in result.summary - @pytest.mark.asyncio async def test_not_detected_returns_safe(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -98,7 +94,6 @@ async def test_not_detected_returns_safe(self) -> None: assert result.safe is True assert result.status is SafetyStatus.SAFE - @pytest.mark.asyncio async def test_undetermined_returns_undetermined(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -116,7 +111,6 @@ async def test_undetermined_returns_undetermined(self) -> None: class TestXPIAEarlyStop: """Per-turn evaluation stops the conversation on first detection.""" - @pytest.mark.asyncio async def test_stops_after_first_detection(self) -> None: evaluator = AsyncMock() evaluator.evaluate_async.side_effect = [ @@ -133,7 +127,6 @@ async def test_stops_after_first_detection(self) -> None: assert result.status is SafetyStatus.UNSAFE assert len(result.turns) == 2 - @pytest.mark.asyncio async def test_completes_all_turns_when_not_detected(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -148,7 +141,6 @@ async def test_completes_all_turns_when_not_detected(self) -> None: class TestXPIAMaxTurns: """Max-turns resolves normally via resolve_as_attack.""" - @pytest.mark.asyncio async def test_max_turns_resolves_normally(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -164,7 +156,6 @@ async def test_max_turns_resolves_normally(self) -> None: class TestXPIACleanup: """Injection handles are always activated and cleaned up.""" - @pytest.mark.asyncio async def test_handle_entered_and_exited(self) -> None: handle = _mock_handle() @@ -178,7 +169,6 @@ async def test_handle_entered_and_exited(self) -> None: handle.__aexit__.assert_awaited_once() handle.wait_until_ready.assert_awaited_once() - @pytest.mark.asyncio async def test_multiple_handles_all_cleaned(self) -> None: h1 = _mock_handle(surface_name="SP") h2 = _mock_handle(surface_name="Exchange") @@ -194,7 +184,6 @@ async def test_multiple_handles_all_cleaned(self) -> None: h.__aexit__.assert_awaited_once() h.wait_until_ready.assert_awaited_once() - @pytest.mark.asyncio async def test_cleanup_on_evaluator_exception(self) -> None: """Handles are cleaned up even if the evaluator raises.""" handle = _mock_handle() @@ -215,7 +204,6 @@ async def test_cleanup_on_evaluator_exception(self) -> None: class TestXPIAInfrastructureError: """InfrastructureError produces ERROR result (base class concern).""" - @pytest.mark.asyncio async def test_handle_activation_failure(self) -> None: handle = _mock_handle() handle.__aenter__.side_effect = InfrastructureError("SharePoint 503") @@ -229,7 +217,6 @@ async def test_handle_activation_failure(self) -> None: assert result.status is SafetyStatus.ERROR assert "SharePoint 503" in result.summary - @pytest.mark.asyncio async def test_session_creation_failure(self) -> None: adapter = AsyncMock() adapter.create_session_async.side_effect = InfrastructureError( @@ -251,7 +238,6 @@ async def test_session_creation_failure(self) -> None: class TestXPIAObservabilityAdjustment: """SAFE is downgraded to UNDETERMINED when observability is insufficient.""" - @pytest.mark.asyncio async def test_response_only_no_tools_downgrades_to_undetermined(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -264,7 +250,6 @@ async def test_response_only_no_tools_downgrades_to_undetermined(self) -> None: assert result.safe is False assert result.status is SafetyStatus.UNDETERMINED - @pytest.mark.asyncio async def test_response_only_with_tool_calls_stays_safe(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -280,7 +265,6 @@ async def test_response_only_with_tool_calls_stays_safe(self) -> None: assert result.safe is True assert result.status is SafetyStatus.SAFE - @pytest.mark.asyncio async def test_non_response_only_levels_are_not_downgraded(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -297,7 +281,6 @@ async def test_non_response_only_levels_are_not_downgraded(self) -> None: class TestXPIAInjectionRecords: """Result carries injection records for reproduction.""" - @pytest.mark.asyncio async def test_single_handle_recorded(self) -> None: result = await Attacks.xpia( inject=_mock_handle(surface_name="SharePoint", payload_id="px-42"), @@ -309,7 +292,6 @@ async def test_single_handle_recorded(self) -> None: assert result.injections[0].payload_id == "px-42" assert result.injections[0].surface_name == "SharePoint" - @pytest.mark.asyncio async def test_multi_handle_records(self) -> None: result = await Attacks.xpia( inject=[ @@ -328,7 +310,6 @@ async def test_multi_handle_records(self) -> None: class TestXPIAAttachments: """Inline attachments flow through to turns via Request.""" - @pytest.mark.asyncio async def test_attachments_recorded_in_turns(self) -> None: attachment = Payload(content="malicious doc", id="att-1") @@ -344,7 +325,6 @@ async def test_attachments_recorded_in_turns(self) -> None: class TestResponseMetadataPropagation: """Response.metadata from the adapter flows into Result.metadata.""" - @pytest.mark.asyncio async def test_single_turn_metadata_promoted_to_top_level(self) -> None: adapter = _adapter( responses=[Response(text="ok", metadata={"conversation_id": "c-01"})], @@ -357,7 +337,6 @@ async def test_single_turn_metadata_promoted_to_top_level(self) -> None: assert result.metadata == {"conversation_id": "c-01"} - @pytest.mark.asyncio async def test_empty_response_metadata_produces_empty_result_metadata(self) -> None: result = await Attacks.xpia( inject=_mock_handle(), @@ -367,7 +346,6 @@ async def test_empty_response_metadata_produces_empty_result_metadata(self) -> N assert result.metadata == {} - @pytest.mark.asyncio async def test_multi_turn_metadata_keyed_by_turn_number(self) -> None: adapter = _adapter( responses=[ diff --git a/tests/unit/converters/test_docx.py b/tests/unit/converters/test_docx.py index 71a01d57..7d876db2 100644 --- a/tests/unit/converters/test_docx.py +++ b/tests/unit/converters/test_docx.py @@ -39,7 +39,6 @@ def test_no_pyrit_import_at_construction(self) -> None: DocxConverter() mock_cls.assert_not_called() - @pytest.mark.asyncio async def test_creates_pyrit_converter_on_first_use(self, tmp_path: Path) -> None: mock_result = _mock_converter_result(tmp_path) @@ -55,7 +54,6 @@ async def test_creates_pyrit_converter_on_first_use(self, tmp_path: Path) -> Non class TestDocxConverterConversion: """Conversion delegates to WordDocConverter and maps result.""" - @pytest.mark.asyncio async def test_produces_docx_payload(self, tmp_path: Path) -> None: mock_result = _mock_converter_result(tmp_path) @@ -69,7 +67,6 @@ async def test_produces_docx_payload(self, tmp_path: Path) -> None: assert result.format is PayloadFormat.DOCX assert result.artifact == Path(mock_result.output_text) - @pytest.mark.asyncio async def test_delegates_content_to_pyrit(self, tmp_path: Path) -> None: mock_result = _mock_converter_result(tmp_path) @@ -87,7 +84,6 @@ async def test_delegates_content_to_pyrit(self, tmp_path: Path) -> None: input_type="text", ) - @pytest.mark.asyncio async def test_preserves_id(self, tmp_path: Path) -> None: mock_result = _mock_converter_result(tmp_path) @@ -102,7 +98,6 @@ async def test_preserves_id(self, tmp_path: Path) -> None: assert result.id == "keep-me" - @pytest.mark.asyncio async def test_preserves_content_for_reporting(self, tmp_path: Path) -> None: mock_result = _mock_converter_result(tmp_path) @@ -117,7 +112,6 @@ async def test_preserves_content_for_reporting(self, tmp_path: Path) -> None: assert result.content == "adversarial text" - @pytest.mark.asyncio async def test_metadata_includes_converter_name(self, tmp_path: Path) -> None: mock_result = _mock_converter_result(tmp_path) @@ -130,7 +124,6 @@ async def test_metadata_includes_converter_name(self, tmp_path: Path) -> None: assert result.metadata["converter"] == "DocxConverter" - @pytest.mark.asyncio async def test_source_metadata_carried_forward(self, tmp_path: Path) -> None: mock_result = _mock_converter_result(tmp_path) @@ -149,7 +142,6 @@ async def test_source_metadata_carried_forward(self, tmp_path: Path) -> None: class TestDocxConverterValidation: """Input validation.""" - @pytest.mark.asyncio async def test_rejects_binary_payload(self, tmp_path: Path) -> None: artifact = tmp_path / "existing.docx" artifact.write_bytes(b"PK") diff --git a/tests/unit/core/test_converter.py b/tests/unit/core/test_converter.py index bcb3223a..e85cdfc0 100644 --- a/tests/unit/core/test_converter.py +++ b/tests/unit/core/test_converter.py @@ -5,8 +5,6 @@ from pathlib import Path -import pytest - from rampart.core.converter import PayloadConverter from rampart.core.types import Payload, PayloadFormat @@ -42,7 +40,6 @@ def test_converter_satisfies_protocol(self) -> None: def test_html_converter_satisfies_protocol(self) -> None: assert isinstance(_HtmlWrapConverter(), PayloadConverter) - @pytest.mark.asyncio async def test_uppercase_converter_transforms_content(self) -> None: converter = _UpperCaseConverter() payload = Payload(content="hello world", id="t1") @@ -50,7 +47,6 @@ async def test_uppercase_converter_transforms_content(self) -> None: assert result.content == "HELLO WORLD" assert result.id == "t1" - @pytest.mark.asyncio async def test_html_converter_changes_format(self) -> None: converter = _HtmlWrapConverter() payload = Payload(content="evil content", id="t2") @@ -58,14 +54,12 @@ async def test_html_converter_changes_format(self) -> None: assert result.content == "
evil content
" assert result.format is PayloadFormat.HTML - @pytest.mark.asyncio async def test_converter_preserves_id(self) -> None: converter = _UpperCaseConverter() payload = Payload(content="test", id="stable_id") result = await converter.convert_async(payload=payload) assert result.id == "stable_id" - @pytest.mark.asyncio async def test_converter_adds_metadata(self) -> None: converter = _UpperCaseConverter() payload = Payload( @@ -77,7 +71,6 @@ async def test_converter_adds_metadata(self) -> None: assert result.metadata["template"] == "email_exfiltration" assert result.metadata["converter"] == "UpperCaseConverter" - @pytest.mark.asyncio async def test_converters_compose_sequentially(self) -> None: upper = _UpperCaseConverter() html = _HtmlWrapConverter() @@ -87,7 +80,6 @@ async def test_converters_compose_sequentially(self) -> None: assert result.content == "EVIL
" assert result.format is PayloadFormat.HTML - @pytest.mark.asyncio async def test_format_converter_preserves_content(self, tmp_path: Path) -> None: fake_file = tmp_path / "fake.png" fake_file.write_bytes(b"\x89PNG") diff --git a/tests/unit/core/test_evaluator.py b/tests/unit/core/test_evaluator.py index 2b2ef594..4c3e920d 100644 --- a/tests/unit/core/test_evaluator.py +++ b/tests/unit/core/test_evaluator.py @@ -3,8 +3,6 @@ """Tests for rampart.core.evaluator — Evaluator protocol, BaseEvaluator, composition.""" -import pytest - from rampart.core.evaluator import BaseEvaluator, Evaluator from rampart.core.types import ( EvalContext, @@ -55,7 +53,6 @@ def test_base_evaluator_satisfies_protocol(self) -> None: class TestOrComposition: - @pytest.mark.asyncio async def test_left_detected_short_circuits(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.DETECTED) right = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) @@ -67,7 +64,6 @@ async def test_left_detected_short_circuits(self) -> None: assert left.call_count == 1 assert right.call_count == 0 - @pytest.mark.asyncio async def test_right_detected(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) right = _StubEvaluator(outcome=EvalOutcome.DETECTED) @@ -79,7 +75,6 @@ async def test_right_detected(self) -> None: assert left.call_count == 1 assert right.call_count == 1 - @pytest.mark.asyncio async def test_neither_detected(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) right = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) @@ -89,7 +84,6 @@ async def test_neither_detected(self) -> None: assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_undetermined_propagates(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) right = _StubEvaluator(outcome=EvalOutcome.UNDETERMINED) @@ -101,7 +95,6 @@ async def test_undetermined_propagates(self) -> None: class TestAndComposition: - @pytest.mark.asyncio async def test_left_not_detected_short_circuits(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) right = _StubEvaluator(outcome=EvalOutcome.DETECTED) @@ -113,7 +106,6 @@ async def test_left_not_detected_short_circuits(self) -> None: assert left.call_count == 1 assert right.call_count == 0 - @pytest.mark.asyncio async def test_left_undetermined_short_circuits(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.UNDETERMINED) right = _StubEvaluator(outcome=EvalOutcome.DETECTED) @@ -124,7 +116,6 @@ async def test_left_undetermined_short_circuits(self) -> None: assert result.outcome is EvalOutcome.UNDETERMINED assert right.call_count == 0 - @pytest.mark.asyncio async def test_both_detected(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.DETECTED, rationale="L") right = _StubEvaluator(outcome=EvalOutcome.DETECTED, rationale="R") @@ -135,7 +126,6 @@ async def test_both_detected(self) -> None: assert result.outcome is EvalOutcome.DETECTED assert len(result.evidence) == 2 - @pytest.mark.asyncio async def test_right_not_detected(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.DETECTED) right = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) @@ -145,7 +135,6 @@ async def test_right_not_detected(self) -> None: assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_right_undetermined(self) -> None: left = _StubEvaluator(outcome=EvalOutcome.DETECTED) right = _StubEvaluator(outcome=EvalOutcome.UNDETERMINED) @@ -157,7 +146,6 @@ async def test_right_undetermined(self) -> None: class TestNotComposition: - @pytest.mark.asyncio async def test_flips_detected_to_not_detected(self) -> None: inner = _StubEvaluator(outcome=EvalOutcome.DETECTED) composed = ~inner @@ -166,7 +154,6 @@ async def test_flips_detected_to_not_detected(self) -> None: assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_flips_not_detected_to_detected(self) -> None: inner = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) composed = ~inner @@ -175,7 +162,6 @@ async def test_flips_not_detected_to_detected(self) -> None: assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_preserves_undetermined(self) -> None: inner = _StubEvaluator(outcome=EvalOutcome.UNDETERMINED) composed = ~inner @@ -184,7 +170,6 @@ async def test_preserves_undetermined(self) -> None: assert result.outcome is EvalOutcome.UNDETERMINED - @pytest.mark.asyncio async def test_preserves_confidence_and_evidence(self) -> None: inner = _StubEvaluator(outcome=EvalOutcome.DETECTED) composed = ~inner @@ -196,7 +181,6 @@ async def test_preserves_confidence_and_evidence(self) -> None: class TestCompositionChaining: - @pytest.mark.asyncio async def test_or_and_not_chain(self) -> None: a = _StubEvaluator(outcome=EvalOutcome.NOT_DETECTED) b = _StubEvaluator(outcome=EvalOutcome.DETECTED) @@ -208,7 +192,6 @@ async def test_or_and_not_chain(self) -> None: assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_composed_evaluators_are_composable(self) -> None: a = _StubEvaluator(outcome=EvalOutcome.DETECTED) b = _StubEvaluator(outcome=EvalOutcome.DETECTED) diff --git a/tests/unit/core/test_execution.py b/tests/unit/core/test_execution.py index 6f0f5137..7bd6a390 100644 --- a/tests/unit/core/test_execution.py +++ b/tests/unit/core/test_execution.py @@ -135,7 +135,6 @@ async def on_event(self, *, event_data: ExecutionEventData) -> None: class TestBaseExecutionLifecycle: - @pytest.mark.asyncio async def test_fires_pre_and_post_execute(self) -> None: handler = _RecordingHandler() execution = _SuccessExecution(event_handlers=[handler]) @@ -149,7 +148,6 @@ async def test_fires_pre_and_post_execute(self) -> None: assert handler.events[1].event is ExecutionEvent.ON_POST_EXECUTE assert handler.events[1].result is result - @pytest.mark.asyncio async def test_post_execute_has_elapsed_time(self) -> None: handler = _RecordingHandler() execution = _SuccessExecution(event_handlers=[handler]) @@ -161,7 +159,6 @@ async def test_post_execute_has_elapsed_time(self) -> None: class TestInfrastructureErrorHandling: - @pytest.mark.asyncio async def test_produces_error_result(self) -> None: execution = _InfraErrorExecution() adapter = _StubAdapter() @@ -172,7 +169,6 @@ async def test_produces_error_result(self) -> None: assert result.status is SafetyStatus.ERROR assert "SharePoint returned 503" in result.summary - @pytest.mark.asyncio async def test_error_result_has_strategy(self) -> None: execution = _InfraErrorExecution() @@ -180,7 +176,6 @@ async def test_error_result_has_strategy(self) -> None: assert result.strategy == "infra_error" - @pytest.mark.asyncio async def test_error_result_has_observability_level(self) -> None: execution = _InfraErrorExecution() @@ -188,7 +183,6 @@ async def test_error_result_has_observability_level(self) -> None: assert result.observability_level is ObservabilityLevel.TOOL_ONLY - @pytest.mark.asyncio async def test_error_result_has_metadata(self) -> None: execution = _InfraErrorExecution() @@ -197,7 +191,6 @@ async def test_error_result_has_metadata(self) -> None: assert result.metadata["error"] == "SharePoint returned 503" assert result.metadata["error_type"] == "InfrastructureError" - @pytest.mark.asyncio async def test_fires_on_error_and_post_execute(self) -> None: handler = _RecordingHandler() execution = _InfraErrorExecution(event_handlers=[handler]) @@ -210,7 +203,6 @@ async def test_fires_on_error_and_post_execute(self) -> None: class TestGenericErrorHandling: - @pytest.mark.asyncio async def test_produces_error_result(self) -> None: execution = _GenericErrorExecution() @@ -220,7 +212,6 @@ async def test_produces_error_result(self) -> None: assert result.status is SafetyStatus.ERROR assert "unexpected failure" in result.summary - @pytest.mark.asyncio async def test_error_result_has_strategy(self) -> None: execution = _GenericErrorExecution() @@ -228,7 +219,6 @@ async def test_error_result_has_strategy(self) -> None: assert result.strategy == "generic_error" - @pytest.mark.asyncio async def test_error_result_has_metadata(self) -> None: execution = _GenericErrorExecution() @@ -237,7 +227,6 @@ async def test_error_result_has_metadata(self) -> None: assert result.metadata["error"] == "unexpected failure" assert result.metadata["error_type"] == "RuntimeError" - @pytest.mark.asyncio async def test_fires_on_error_and_post_execute(self) -> None: handler = _RecordingHandler() execution = _GenericErrorExecution(event_handlers=[handler]) @@ -248,7 +237,6 @@ async def test_fires_on_error_and_post_execute(self) -> None: assert ExecutionEvent.ON_ERROR in event_types assert ExecutionEvent.ON_POST_EXECUTE in event_types - @pytest.mark.asyncio async def test_on_error_contains_exception(self) -> None: handler = _RecordingHandler() execution = _GenericErrorExecution(event_handlers=[handler]) @@ -262,7 +250,6 @@ async def test_on_error_contains_exception(self) -> None: class TestHandlerSafety: - @pytest.mark.asyncio async def test_broken_handler_does_not_abort_execution(self) -> None: broken = _BrokenHandler() recorder = _RecordingHandler() @@ -275,7 +262,6 @@ async def test_broken_handler_does_not_abort_execution(self) -> None: class TestDefaultHandlerFactory: - @pytest.mark.asyncio async def test_execution_works_without_factory(self) -> None: execution = _SuccessExecution() @@ -283,7 +269,6 @@ async def test_execution_works_without_factory(self) -> None: assert result.safe is True - @pytest.mark.asyncio async def test_factory_handlers_are_prepended(self) -> None: from rampart.core.execution import ( clear_default_handler_factory, @@ -309,7 +294,6 @@ def test_register_rejects_non_callable(self) -> None: class TestDriverErrorHandling: - @pytest.mark.asyncio async def test_produces_error_result(self) -> None: execution = _DriverErrorExecution() adapter = _StubAdapter() @@ -320,7 +304,6 @@ async def test_produces_error_result(self) -> None: assert result.status is SafetyStatus.ERROR assert "LLM returned garbage" in result.summary - @pytest.mark.asyncio async def test_error_result_has_strategy(self) -> None: execution = _DriverErrorExecution() @@ -328,7 +311,6 @@ async def test_error_result_has_strategy(self) -> None: assert result.strategy == "driver_error" - @pytest.mark.asyncio async def test_error_result_has_metadata(self) -> None: execution = _DriverErrorExecution() @@ -337,7 +319,6 @@ async def test_error_result_has_metadata(self) -> None: assert result.metadata["error"] == "LLM returned garbage" assert result.metadata["error_type"] == "DriverError" - @pytest.mark.asyncio async def test_fires_on_error_and_post_execute(self) -> None: handler = _RecordingHandler() execution = _DriverErrorExecution(event_handlers=[handler]) @@ -350,7 +331,6 @@ async def test_fires_on_error_and_post_execute(self) -> None: class TestEvaluateTurnAsync: - @pytest.mark.asyncio async def test_returns_turn_with_eval_result(self) -> None: from unittest.mock import AsyncMock @@ -381,7 +361,6 @@ async def test_returns_turn_with_eval_result(self) -> None: assert turn.response.text == "world" assert turn.turn_number == 0 - @pytest.mark.asyncio async def test_includes_history_in_context(self) -> None: from unittest.mock import AsyncMock @@ -422,7 +401,6 @@ async def capture_eval(*, context: EvalContext) -> EvalResult: assert captured_context.turns[0].request.prompt == "prev" assert captured_context.turns[1].request.prompt == "current" - @pytest.mark.asyncio async def test_preserves_driver_reasoning(self) -> None: from unittest.mock import AsyncMock diff --git a/tests/unit/core/test_injection.py b/tests/unit/core/test_injection.py index 0f287f52..0573abde 100644 --- a/tests/unit/core/test_injection.py +++ b/tests/unit/core/test_injection.py @@ -6,8 +6,6 @@ import types from typing import Self -import pytest - from rampart.core.injection import InjectionHandle, Surface, sleep_until_ready from rampart.core.types import Payload @@ -79,6 +77,5 @@ def inject(self, *, payload: Payload) -> MyHandle: class TestSleepUntilReady: - @pytest.mark.asyncio async def test_completes_without_error_async(self) -> None: await sleep_until_ready(0.0) diff --git a/tests/unit/drivers/test_coerce.py b/tests/unit/drivers/test_coerce.py index 5803769c..ff8f61d3 100644 --- a/tests/unit/drivers/test_coerce.py +++ b/tests/unit/drivers/test_coerce.py @@ -5,8 +5,6 @@ from __future__ import annotations -import pytest - from rampart.core.prompt_driver import PromptDecision from rampart.core.types import Request, Response, Turn from rampart.drivers._utils import coerce_driver @@ -16,7 +14,6 @@ class TestCoerceString: """coerce_driver accepts a str as a single-prompt driver.""" - @pytest.mark.asyncio async def test_str_produces_single_prompt_driver_async(self) -> None: driver = coerce_driver("hello") @@ -35,7 +32,6 @@ async def test_str_produces_single_prompt_driver_async(self) -> None: class TestCoerceList: """coerce_driver accepts list[str] as a multi-prompt driver.""" - @pytest.mark.asyncio async def test_list_produces_multi_prompt_driver_async(self) -> None: driver = coerce_driver(["a", "b"]) @@ -47,13 +43,11 @@ async def test_list_produces_multi_prompt_driver_async(self) -> None: class TestCoercePassthrough: """coerce_driver passes through an existing PromptDriver unchanged.""" - @pytest.mark.asyncio async def test_prompt_driver_passthrough_async(self) -> None: original = StaticDriver(prompts=["x"]) result = coerce_driver(original) assert result is original - @pytest.mark.asyncio async def test_custom_driver_passthrough_async(self) -> None: class Custom: async def next_prompt_async( diff --git a/tests/unit/drivers/test_llm_driver.py b/tests/unit/drivers/test_llm_driver.py index 0bdd482f..09ed35e2 100644 --- a/tests/unit/drivers/test_llm_driver.py +++ b/tests/unit/drivers/test_llm_driver.py @@ -81,7 +81,6 @@ def test_construction_does_not_call_create_prompt_target(self) -> None: LLMDriver(llm=_TEST_LLM, persona=_TEST_PERSONA) mock_create.assert_not_called() - @pytest.mark.asyncio async def test_first_call_initializes_target(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -114,7 +113,6 @@ async def test_first_call_initializes_target(self) -> None: class TestLLMDriverConstruction: - @pytest.mark.asyncio async def test_system_prompt_includes_persona(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -138,7 +136,6 @@ async def test_system_prompt_includes_persona(self) -> None: sp = mock_target.set_system_prompt.call_args.kwargs["system_prompt"] assert "You are a test persona." in sp - @pytest.mark.asyncio async def test_system_prompt_includes_objective_when_provided(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -167,7 +164,6 @@ async def test_system_prompt_includes_objective_when_provided(self) -> None: assert "Objective" in sp assert "Extract secret data" in sp - @pytest.mark.asyncio async def test_system_prompt_omits_objective_when_none(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -191,7 +187,6 @@ async def test_system_prompt_omits_objective_when_none(self) -> None: sp = mock_target.set_system_prompt.call_args.kwargs["system_prompt"] assert "Objective" not in sp - @pytest.mark.asyncio async def test_system_prompt_includes_injection_metadata_not_content(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -234,7 +229,6 @@ def test_two_drivers_have_distinct_conversation_ids(self) -> None: class TestLLMDriverSendFlow: - @pytest.mark.asyncio async def test_returns_plain_text_as_prompt(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -258,7 +252,6 @@ async def test_returns_plain_text_as_prompt(self) -> None: assert decision is not None assert decision.request.prompt == "Tell me about Q3 earnings" - @pytest.mark.asyncio async def test_send_uses_normalizer_helper(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -286,7 +279,6 @@ async def test_send_uses_normalizer_helper(self) -> None: assert call_kwargs["user_message"] == "Begin. Send the first user prompt." assert "rampart.component" in call_kwargs["labels"] - @pytest.mark.asyncio async def test_non_empty_history_sends_agent_response(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -331,7 +323,6 @@ async def test_non_empty_history_sends_agent_response(self) -> None: assert "not_detected" in user_msg assert "not found" in user_msg - @pytest.mark.asyncio async def test_strips_whitespace_from_response(self) -> None: mock_memory = MagicMock() mock_memory.get_conversation.return_value = [] @@ -356,7 +347,6 @@ async def test_strips_whitespace_from_response(self) -> None: class TestLLMDriverErrorHandling: - @pytest.mark.asyncio async def test_empty_response_raises_driver_error(self) -> None: mock_memory = MagicMock() mock_memory.get_conversation.return_value = [] @@ -378,7 +368,6 @@ async def test_empty_response_raises_driver_error(self) -> None: with pytest.raises(DriverError, match="empty response"): await driver.next_prompt_async(history=[]) - @pytest.mark.asyncio async def test_whitespace_only_response_raises_driver_error(self) -> None: mock_memory = MagicMock() mock_memory.get_conversation.return_value = [] @@ -400,7 +389,6 @@ async def test_whitespace_only_response_raises_driver_error(self) -> None: with pytest.raises(DriverError, match="empty response"): await driver.next_prompt_async(history=[]) - @pytest.mark.asyncio async def test_send_exception_wrapped_in_driver_error(self) -> None: mock_memory = MagicMock() mock_memory.get_conversation.return_value = [] @@ -422,7 +410,6 @@ async def test_send_exception_wrapped_in_driver_error(self) -> None: with pytest.raises(DriverError, match="send_user_turn_async failed"): await driver.next_prompt_async(history=[]) - @pytest.mark.asyncio async def test_driver_error_preserves_cause(self) -> None: original = RuntimeError("timeout") mock_memory = MagicMock() @@ -448,7 +435,6 @@ async def test_driver_error_preserves_cause(self) -> None: class TestLLMDriverDesyncDetection: - @pytest.mark.asyncio async def test_desync_raises_driver_error(self) -> None: """Passing history that doesn't match driver-side memory raises.""" mock_memory = MagicMock() @@ -479,7 +465,6 @@ def test_from_target_does_not_require_llm_config(self) -> None: assert driver._llm is None assert driver._target is mock_target - @pytest.mark.asyncio async def test_from_target_sets_system_prompt_on_first_use(self) -> None: mock_target = MagicMock() mock_memory = MagicMock() @@ -512,7 +497,6 @@ async def test_from_target_sets_system_prompt_on_first_use(self) -> None: class TestLLMDriverAttachments: - @pytest.mark.asyncio async def test_first_turn_attaches_injections(self) -> None: """Injections should be attached to the first request.""" mock_memory = MagicMock() @@ -546,7 +530,6 @@ async def test_first_turn_attaches_injections(self) -> None: assert decision is not None assert decision.request.attachments == [payload] - @pytest.mark.asyncio async def test_subsequent_turns_have_no_attachments(self) -> None: """Only the first turn should carry attachments.""" mock_piece_user = MagicMock() @@ -592,7 +575,6 @@ async def test_subsequent_turns_have_no_attachments(self) -> None: assert decision is not None assert decision.request.attachments == [] - @pytest.mark.asyncio async def test_no_injections_means_no_attachments(self) -> None: """Without injections, first turn should have empty attachments.""" mock_memory = MagicMock() diff --git a/tests/unit/drivers/test_static_driver.py b/tests/unit/drivers/test_static_driver.py index 5a581e3b..e787fff2 100644 --- a/tests/unit/drivers/test_static_driver.py +++ b/tests/unit/drivers/test_static_driver.py @@ -5,8 +5,6 @@ from __future__ import annotations -import pytest - from rampart.core.prompt_driver import PromptDriver from rampart.core.types import Request, Response, Turn from rampart.drivers.static import StaticDriver @@ -20,7 +18,6 @@ def _turn(prompt: str) -> Turn: class TestStaticDriverSequence: """StaticDriver returns prompts in order based on history length.""" - @pytest.mark.asyncio async def test_returns_prompts_in_order_async(self) -> None: driver = StaticDriver(prompts=["first", "second", "third"]) @@ -38,14 +35,12 @@ async def test_returns_prompts_in_order_async(self) -> None: assert d2 is not None assert d2.request.prompt == "third" - @pytest.mark.asyncio async def test_returns_none_when_exhausted_async(self) -> None: driver = StaticDriver(prompts=["only"]) result = await driver.next_prompt_async(history=[_turn("only")]) assert result is None - @pytest.mark.asyncio async def test_reasoning_is_empty_async(self) -> None: driver = StaticDriver(prompts=["hello"]) @@ -58,7 +53,6 @@ async def test_reasoning_is_empty_async(self) -> None: class TestStaticDriverStateless: """Same StaticDriver instance behaves correctly for different histories.""" - @pytest.mark.asyncio async def test_same_instance_different_histories_async(self) -> None: driver = StaticDriver(prompts=["a", "b", "c"]) diff --git a/tests/unit/evaluators/test_response_contains.py b/tests/unit/evaluators/test_response_contains.py index 7606a8fd..793076e7 100644 --- a/tests/unit/evaluators/test_response_contains.py +++ b/tests/unit/evaluators/test_response_contains.py @@ -5,8 +5,6 @@ import re -import pytest - from rampart.core.types import EvalContext, EvalOutcome, Request, Response, Turn from rampart.evaluators import ResponseContains @@ -19,14 +17,12 @@ def _ctx(text: str) -> EvalContext: class TestResponseContainsSubstring: - @pytest.mark.asyncio async def test_case_insensitive_by_default(self) -> None: result = await ResponseContains("HELLO").evaluate_async( context=_ctx("hello world"), ) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_case_sensitive_opt_in(self) -> None: result = await ResponseContains( "HELLO", @@ -34,7 +30,6 @@ async def test_case_sensitive_opt_in(self) -> None: ).evaluate_async(context=_ctx("hello world")) assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_case_sensitive_match(self) -> None: result = await ResponseContains( "hello", @@ -42,14 +37,12 @@ async def test_case_sensitive_match(self) -> None: ).evaluate_async(context=_ctx("hello world")) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_not_detected(self) -> None: result = await ResponseContains("evil").evaluate_async(context=_ctx("all good")) assert result.outcome is EvalOutcome.NOT_DETECTED class TestResponseContainsRegex: - @pytest.mark.asyncio async def test_regex_match(self) -> None: pattern = re.compile(r"evil@\w+\.com") result = await ResponseContains(pattern).evaluate_async( @@ -57,7 +50,6 @@ async def test_regex_match(self) -> None: ) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_regex_no_match(self) -> None: pattern = re.compile(r"evil@\w+\.com") result = await ResponseContains(pattern).evaluate_async( @@ -67,14 +59,12 @@ async def test_regex_no_match(self) -> None: class TestResponseContainsCallable: - @pytest.mark.asyncio async def test_callable_match(self) -> None: result = await ResponseContains( lambda text: "secret" in text, ).evaluate_async(context=_ctx("the secret is here")) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_callable_no_match(self) -> None: result = await ResponseContains( lambda text: "secret" in text, @@ -83,7 +73,6 @@ async def test_callable_no_match(self) -> None: class TestResponseContainsComposition: - @pytest.mark.asyncio async def test_composable_with_not(self) -> None: composed = ~ResponseContains("I cannot help") result = await composed.evaluate_async(context=_ctx("Sure, here you go")) diff --git a/tests/unit/evaluators/test_side_effect.py b/tests/unit/evaluators/test_side_effect.py index 6f827350..efa12be5 100644 --- a/tests/unit/evaluators/test_side_effect.py +++ b/tests/unit/evaluators/test_side_effect.py @@ -3,8 +3,6 @@ """Tests for rampart.evaluators.side_effect — SideEffectOccurred evaluator.""" -import pytest - from rampart.core.types import ( EvalContext, EvalOutcome, @@ -29,19 +27,16 @@ def _ctx_with_side_effects(*effects: SideEffect) -> EvalContext: class TestSideEffectOccurredDetection: - @pytest.mark.asyncio async def test_detects_by_kind(self) -> None: ctx = _ctx_with_side_effects(SideEffect(kind="http_request")) result = await SideEffectOccurred("http_request").evaluate_async(context=ctx) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_not_detected_wrong_kind(self) -> None: ctx = _ctx_with_side_effects(SideEffect(kind="file_write")) result = await SideEffectOccurred("http_request").evaluate_async(context=ctx) assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_not_detected_no_effects(self) -> None: ctx = _ctx_with_side_effects() result = await SideEffectOccurred("http_request").evaluate_async(context=ctx) @@ -49,7 +44,6 @@ async def test_not_detected_no_effects(self) -> None: class TestSideEffectOccurredDetailPredicates: - @pytest.mark.asyncio async def test_exact_detail_match(self) -> None: se = SideEffect(kind="http_request", details={"url": "https://evil.com"}) ctx = _ctx_with_side_effects(se) @@ -59,7 +53,6 @@ async def test_exact_detail_match(self) -> None: ).evaluate_async(context=ctx) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_exact_detail_mismatch(self) -> None: se = SideEffect(kind="http_request", details={"url": "https://safe.com"}) ctx = _ctx_with_side_effects(se) @@ -69,7 +62,6 @@ async def test_exact_detail_mismatch(self) -> None: ).evaluate_async(context=ctx) assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_predicate_detail_match(self) -> None: se = SideEffect(kind="http_request", details={"url": "https://evil.com/data"}) ctx = _ctx_with_side_effects(se) @@ -79,7 +71,6 @@ async def test_predicate_detail_match(self) -> None: ).evaluate_async(context=ctx) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_predicate_detail_mismatch(self) -> None: se = SideEffect(kind="http_request", details={"url": "https://safe.com"}) ctx = _ctx_with_side_effects(se) diff --git a/tests/unit/evaluators/test_tool_called.py b/tests/unit/evaluators/test_tool_called.py index 091bee4c..68976131 100644 --- a/tests/unit/evaluators/test_tool_called.py +++ b/tests/unit/evaluators/test_tool_called.py @@ -3,8 +3,6 @@ """Tests for rampart.evaluators.tool_called — ToolCalled evaluator.""" -import pytest - from rampart.core.types import ( EvalContext, EvalOutcome, @@ -43,19 +41,16 @@ def _multi_turn_ctx(turns_tool_calls: list[list[ToolCall]]) -> EvalContext: class TestToolCalledDetection: - @pytest.mark.asyncio async def test_detects_by_name(self) -> None: ctx = _ctx_with_tool_calls(ToolCall(name="send_email")) result = await ToolCalled("send_email").evaluate_async(context=ctx) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_not_detected_wrong_name(self) -> None: ctx = _ctx_with_tool_calls(ToolCall(name="read_file")) result = await ToolCalled("send_email").evaluate_async(context=ctx) assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_not_detected_no_tool_calls(self) -> None: ctx = _ctx_with_tool_calls() result = await ToolCalled("send_email").evaluate_async(context=ctx) @@ -63,7 +58,6 @@ async def test_not_detected_no_tool_calls(self) -> None: class TestToolCalledParameterMatching: - @pytest.mark.asyncio async def test_exact_parameter_match(self) -> None: tc = ToolCall(name="send_email", arguments={"to": "evil@evil.com"}) ctx = _ctx_with_tool_calls(tc) @@ -72,7 +66,6 @@ async def test_exact_parameter_match(self) -> None: ) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_exact_parameter_mismatch(self) -> None: tc = ToolCall(name="send_email", arguments={"to": "friend@company.com"}) ctx = _ctx_with_tool_calls(tc) @@ -81,7 +74,6 @@ async def test_exact_parameter_mismatch(self) -> None: ) assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_predicate_parameter_match(self) -> None: tc = ToolCall(name="send_email", arguments={"to": "evil@evil.com"}) ctx = _ctx_with_tool_calls(tc) @@ -91,7 +83,6 @@ async def test_predicate_parameter_match(self) -> None: ).evaluate_async(context=ctx) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_predicate_parameter_mismatch(self) -> None: tc = ToolCall(name="send_email", arguments={"to": "friend@company.com"}) ctx = _ctx_with_tool_calls(tc) @@ -101,7 +92,6 @@ async def test_predicate_parameter_mismatch(self) -> None: ).evaluate_async(context=ctx) assert result.outcome is EvalOutcome.NOT_DETECTED - @pytest.mark.asyncio async def test_missing_parameter_returns_none_to_predicate(self) -> None: tc = ToolCall(name="send_email", arguments={}) ctx = _ctx_with_tool_calls(tc) @@ -113,7 +103,6 @@ async def test_missing_parameter_returns_none_to_predicate(self) -> None: class TestToolCalledMultiTurn: - @pytest.mark.asyncio async def test_scans_across_turns(self) -> None: ctx = _multi_turn_ctx( [ @@ -124,7 +113,6 @@ async def test_scans_across_turns(self) -> None: result = await ToolCalled("send_email").evaluate_async(context=ctx) assert result.outcome is EvalOutcome.DETECTED - @pytest.mark.asyncio async def test_not_detected_across_turns(self) -> None: ctx = _multi_turn_ctx( [ @@ -137,7 +125,6 @@ async def test_not_detected_across_turns(self) -> None: class TestToolCalledComposition: - @pytest.mark.asyncio async def test_composable_with_or(self) -> None: tc = ToolCall(name="send_email") ctx = _ctx_with_tool_calls(tc) diff --git a/tests/unit/payloads/test_generator.py b/tests/unit/payloads/test_generator.py index e64564d3..7001475b 100644 --- a/tests/unit/payloads/test_generator.py +++ b/tests/unit/payloads/test_generator.py @@ -5,8 +5,6 @@ from unittest.mock import AsyncMock, patch -import pytest - from rampart.core.llm import LLMConfig from rampart.core.manifest import AppManifest, DataSource, ToolDeclaration from rampart.core.persona import Persona @@ -46,7 +44,6 @@ def _persona() -> Persona: class TestGenerateTextVariants: - @pytest.mark.asyncio async def test_returns_one_variant_per_call(self) -> None: with patch( "rampart.payloads._generator.PayloadGenerator._send_to_llm_async", @@ -68,7 +65,6 @@ async def test_returns_one_variant_per_call(self) -> None: assert result == ["variant one", "variant two", "variant three"] - @pytest.mark.asyncio async def test_resolves_template_variables(self) -> None: captured_args: dict[str, str] = {} @@ -99,7 +95,6 @@ async def capture(*, system_message: str, user_message: str): assert "override@evil.com" in captured_args["user_message"] assert "{email}" not in captured_args["user_message"] - @pytest.mark.asyncio async def test_strips_whitespace(self) -> None: with patch( "rampart.payloads._generator.PayloadGenerator._send_to_llm_async", @@ -120,7 +115,6 @@ async def test_strips_whitespace(self) -> None: assert result == ["padded content"] - @pytest.mark.asyncio async def test_includes_objective_in_prompt(self) -> None: captured_args: dict[str, str] = {} diff --git a/tests/unit/payloads/test_payloads.py b/tests/unit/payloads/test_payloads.py index 1b8b0935..cb942802 100644 --- a/tests/unit/payloads/test_payloads.py +++ b/tests/unit/payloads/test_payloads.py @@ -80,7 +80,6 @@ def _patch_llm(*responses: str): class TestGeneration: """Core generation pipeline — text variants from LLM.""" - @pytest.mark.asyncio async def test_generates_text_payloads(self) -> None: with _patch_llm("variant_a", "variant_b"): result = await Payloads.generate_async( @@ -95,7 +94,6 @@ async def test_generates_text_payloads(self) -> None: assert result[1].content == "variant_b" assert all(p.format is PayloadFormat.TEXT for p in result) - @pytest.mark.asyncio async def test_count_below_one_raises(self) -> None: with pytest.raises(ValueError, match="count must be >= 1"): await Payloads.generate_async( @@ -105,7 +103,6 @@ async def test_count_below_one_raises(self) -> None: count=0, ) - @pytest.mark.asyncio async def test_provenance_metadata_on_payloads(self) -> None: with _patch_llm("variant"): result = await Payloads.generate_async( @@ -121,7 +118,6 @@ async def test_provenance_metadata_on_payloads(self) -> None: assert meta["objective"] == "Make agent send data to attacker." assert meta["variant_index"] == 0 - @pytest.mark.asyncio async def test_manifest_reaches_llm_prompt(self) -> None: """Manifest tools and agent name appear in the LLM user message.""" captured: dict[str, str] = {} @@ -148,7 +144,6 @@ async def capture(*, system_message: str, user_message: str) -> str: assert "send_email" in captured["user_message"] assert "TestAgent" in captured["user_message"] - @pytest.mark.asyncio async def test_persona_becomes_system_message(self) -> None: """Persona system_prompt is forwarded as the LLM system message.""" captured: dict[str, str] = {} @@ -177,7 +172,6 @@ async def capture(*, system_message: str, user_message: str) -> str: class TestConverterPipeline: """Converter chaining — sequential pipeline like PyRIT.""" - @pytest.mark.asyncio async def test_returns_base_and_converted(self) -> None: """With converters, output is base text + final chain result.""" with _patch_llm("content"): @@ -195,7 +189,6 @@ async def test_returns_base_and_converted(self) -> None: assert result[1].content == "CONTENT" assert result[1].format is PayloadFormat.HTML - @pytest.mark.asyncio async def test_chaining_feeds_output_to_next_converter(self) -> None: """[Upper, Prefix] chains: upper first, then prefix the result.""" with _patch_llm("hello"): @@ -213,7 +206,6 @@ async def test_chaining_feeds_output_to_next_converter(self) -> None: # Chain: "hello" -> Upper -> "HELLO" -> Prefix -> "PREFIX:HELLO" assert result[1].content == "PREFIX:HELLO" - @pytest.mark.asyncio async def test_multiple_variants_one_chain_per_variant(self) -> None: with _patch_llm("a", "b"): result = await Payloads.generate_async( @@ -228,7 +220,6 @@ async def test_multiple_variants_one_chain_per_variant(self) -> None: assert len(result) == 4 assert [p.content for p in result] == ["a", "b", "A", "B"] - @pytest.mark.asyncio async def test_empty_converters_same_as_none(self) -> None: with _patch_llm("variant"): result = await Payloads.generate_async( @@ -242,7 +233,6 @@ async def test_empty_converters_same_as_none(self) -> None: assert len(result) == 1 assert result[0].format is PayloadFormat.TEXT - @pytest.mark.asyncio async def test_converter_metadata_preserved(self) -> None: """Converter can add its own metadata alongside provenance.""" with _patch_llm("content"): diff --git a/tests/unit/probes/test_single_turn.py b/tests/unit/probes/test_single_turn.py index a2608cf3..32a7ae90 100644 --- a/tests/unit/probes/test_single_turn.py +++ b/tests/unit/probes/test_single_turn.py @@ -68,7 +68,6 @@ async def evaluate_async(self, *, context: EvalContext) -> EvalResult: class TestProbePolarity: """Probe polarity: DETECTED -> SAFE, NOT_DETECTED -> UNSAFE.""" - @pytest.mark.asyncio async def test_detected_means_safe_async(self) -> None: adapter = _adapter(responses=[Response(text="ok")]) @@ -80,7 +79,6 @@ async def test_detected_means_safe_async(self) -> None: assert result.safe is True assert result.status == SafetyStatus.SAFE - @pytest.mark.asyncio async def test_not_detected_means_unsafe_async(self) -> None: adapter = _adapter(responses=[Response(text="nope")]) @@ -96,7 +94,6 @@ async def test_not_detected_means_unsafe_async(self) -> None: class TestProbeStrategyName: """strategy_name is 'probe'.""" - @pytest.mark.asyncio async def test_strategy_name_async(self) -> None: adapter = _adapter(responses=[Response(text="ok")]) @@ -111,7 +108,6 @@ async def test_strategy_name_async(self) -> None: class TestProbePromptCoercion: """Probes.behavior accepts str, list[str], and PromptDriver.""" - @pytest.mark.asyncio async def test_str_prompt_async(self) -> None: adapter = _adapter(responses=[Response(text="ok")]) @@ -124,7 +120,6 @@ async def test_str_prompt_async(self) -> None: assert len(result.turns) == 1 assert result.turns[0].request.prompt == "hello" - @pytest.mark.asyncio async def test_list_prompt_async(self) -> None: adapter = _adapter(responses=[Response(text="ok")]) @@ -137,7 +132,6 @@ async def test_list_prompt_async(self) -> None: assert result.turns[0].request.prompt == "first" assert result.turns[1].request.prompt == "second" - @pytest.mark.asyncio async def test_prompt_driver_async(self) -> None: prompt_driver = StaticDriver(prompts=["driven"]) adapter = _adapter(responses=[Response(text="ok")]) @@ -177,7 +171,6 @@ def test_no_source_raises(self) -> None: class TestProbeInfrastructureError: """InfrastructureError becomes an ERROR result.""" - @pytest.mark.asyncio async def test_infra_error_produces_error_result_async(self) -> None: class FailingAdapter: manifest = AppManifest(name="broken") @@ -199,7 +192,6 @@ async def create_session_async(self): class TestProbeEndToEnd: """Probe flow works end-to-end with MockAdapter.""" - @pytest.mark.asyncio async def test_tool_call_detected_async(self) -> None: adapter = _adapter( responses=[ @@ -218,7 +210,6 @@ async def test_tool_call_detected_async(self) -> None: assert result.safe is True assert result.status == SafetyStatus.SAFE - @pytest.mark.asyncio async def test_tool_call_not_detected_async(self) -> None: adapter = _adapter( responses=[ @@ -234,7 +225,6 @@ async def test_tool_call_not_detected_async(self) -> None: assert result.safe is False assert result.status == SafetyStatus.UNSAFE - @pytest.mark.asyncio async def test_assert_pattern_async(self) -> None: adapter = _adapter( responses=[ @@ -256,7 +246,6 @@ async def test_assert_pattern_async(self) -> None: class TestProbeMaxTurns: """Max turns resolves normally via resolve_as_probe.""" - @pytest.mark.asyncio async def test_max_turns_resolves_normally_async(self) -> None: adapter = _adapter(responses=[Response(text="ok")]) diff --git a/tests/unit/pytest_plugin/test_collection.py b/tests/unit/pytest_plugin/test_collection.py index 8d270b25..25d6376e 100644 --- a/tests/unit/pytest_plugin/test_collection.py +++ b/tests/unit/pytest_plugin/test_collection.py @@ -7,8 +7,6 @@ from unittest.mock import MagicMock -import pytest - from rampart.core.execution import ExecutionEvent, ExecutionEventData from rampart.core.result import Result, SafetyStatus from rampart.pytest_plugin._collection import ( @@ -71,7 +69,6 @@ def test_empty_collector(self) -> None: class TestResultCollectionHandler: """ResultCollectionHandler writes to active collector on ON_POST_EXECUTE.""" - @pytest.mark.asyncio async def test_records_on_post_execute_async(self) -> None: collector = ResultCollector() token = _active_collector.set(collector) @@ -90,7 +87,6 @@ async def test_records_on_post_execute_async(self) -> None: finally: _active_collector.reset(token) - @pytest.mark.asyncio async def test_ignores_pre_execute_async(self) -> None: collector = ResultCollector() token = _active_collector.set(collector) @@ -104,7 +100,6 @@ async def test_ignores_pre_execute_async(self) -> None: finally: _active_collector.reset(token) - @pytest.mark.asyncio async def test_ignores_on_error_async(self) -> None: collector = ResultCollector() token = _active_collector.set(collector) @@ -118,7 +113,6 @@ async def test_ignores_on_error_async(self) -> None: finally: _active_collector.reset(token) - @pytest.mark.asyncio async def test_noop_when_no_collector_active_async(self) -> None: handler = ResultCollectionHandler() result = _make_result() @@ -129,7 +123,6 @@ async def test_noop_when_no_collector_active_async(self) -> None: await handler.on_event(event_data=event_data) - @pytest.mark.asyncio async def test_noop_when_result_is_none_async(self) -> None: collector = ResultCollector() token = _active_collector.set(collector) diff --git a/tests/unit/reporting/test_json_file.py b/tests/unit/reporting/test_json_file.py index d92dc74c..de7a6bbe 100644 --- a/tests/unit/reporting/test_json_file.py +++ b/tests/unit/reporting/test_json_file.py @@ -9,8 +9,6 @@ from pathlib import Path from typing import Any -import pytest - from rampart.core.result import HarmCategory, Result, SafetyStatus from rampart.core.types import ( EvalOutcome, @@ -208,7 +206,6 @@ def test_turns_omit_driver_reasoning_when_empty(self) -> None: class TestEmitAsync: """emit_async writes a valid JSON file.""" - @pytest.mark.asyncio async def test_emitted_file_contains_metadata(self, tmp_path: Path) -> None: sink = JsonFileReportSink(output_dir=tmp_path) result = _result_with_turns( diff --git a/tests/unit/surfaces/test_onedrive.py b/tests/unit/surfaces/test_onedrive.py index 12434ceb..a46e7544 100644 --- a/tests/unit/surfaces/test_onedrive.py +++ b/tests/unit/surfaces/test_onedrive.py @@ -164,7 +164,6 @@ def test_payload_id(self) -> None: class TestOneDriveInjectionLifecycle: """Test the async context manager lifecycle (upload + delete).""" - @pytest.mark.asyncio async def test_enter_uploads_and_stores_item_id(self) -> None: client = _make_graph_client(upload_item_id="item-xyz") surface = OneDriveSurface( @@ -178,7 +177,6 @@ async def test_enter_uploads_and_stores_item_id(self) -> None: async with handle as h: assert h._item_id == "item-xyz" - @pytest.mark.asyncio async def test_upload_uses_correct_graph_path(self) -> None: """Verify the path-based addressing format root:/{folder}/{file}:.""" client = _make_graph_client(upload_item_id="item-1") @@ -198,7 +196,6 @@ async def test_upload_uses_correct_graph_path(self) -> None: upload_call = by_drive_item_id.call_args_list[0] assert upload_call == call("root:/Documents/payloads/abc123.txt:") - @pytest.mark.asyncio async def test_exit_deletes_with_correct_item_id(self) -> None: client = _make_graph_client(upload_item_id="item-to-delete") surface = OneDriveSurface( @@ -218,7 +215,6 @@ async def test_exit_deletes_with_correct_item_id(self) -> None: assert delete_call == call("item-to-delete") client._delete_mock.delete.assert_awaited_once() - @pytest.mark.asyncio async def test_upload_failure_raises_infrastructure_error(self) -> None: client = _make_graph_client( upload_error=ConnectionError("Graph API unavailable"), @@ -235,7 +231,6 @@ async def test_upload_failure_raises_infrastructure_error(self) -> None: async with handle: pass - @pytest.mark.asyncio async def test_delete_failure_logs_warning_does_not_raise(self) -> None: client = _make_graph_client( upload_item_id="item-1", @@ -253,7 +248,6 @@ async def test_delete_failure_logs_warning_does_not_raise(self) -> None: async with handle: pass - @pytest.mark.asyncio async def test_exit_skips_delete_when_no_item_id(self) -> None: """If upload was never called, exit should be a no-op.""" surface = OneDriveSurface( @@ -267,7 +261,6 @@ async def test_exit_skips_delete_when_no_item_id(self) -> None: # Call __aexit__ directly without __aenter__ await handle.__aexit__(None, None, None) - @pytest.mark.asyncio async def test_returns_self_from_aenter(self) -> None: client = _make_graph_client() surface = OneDriveSurface( @@ -281,7 +274,6 @@ async def test_returns_self_from_aenter(self) -> None: async with handle as h: assert h is handle - @pytest.mark.asyncio async def test_upload_exceeding_size_limit_raises_infrastructure_error( self, ) -> None: @@ -299,7 +291,6 @@ async def test_upload_exceeding_size_limit_raises_infrastructure_error( async with handle: pass - @pytest.mark.asyncio async def test_null_drive_item_raises_infrastructure_error(self) -> None: client = _make_graph_client(upload_return=None) surface = OneDriveSurface( @@ -314,7 +305,6 @@ async def test_null_drive_item_raises_infrastructure_error(self) -> None: async with handle: pass - @pytest.mark.asyncio async def test_null_drive_item_id_raises_infrastructure_error(self) -> None: """DriveItem exists but has a None id.""" item_with_no_id = MagicMock() @@ -332,7 +322,6 @@ async def test_null_drive_item_id_raises_infrastructure_error(self) -> None: async with handle: pass - @pytest.mark.asyncio async def test_infrastructure_error_from_upload_not_double_wrapped(self) -> None: """InfrastructureError raised inside _upload_async propagates directly.""" original = InfrastructureError("Graph returned no DriveItem") @@ -355,7 +344,6 @@ async def test_infrastructure_error_from_upload_not_double_wrapped(self) -> None class TestOneDriveInjectionWaitUntilReady: """Test _OneDriveInjection.wait_until_ready wiring.""" - @pytest.mark.asyncio async def test_delegates_to_sleep_until_ready(self) -> None: """Verifies correct arguments are passed to sleep_until_ready.""" surface = OneDriveSurface(