Feature: Consolidated Misc Combined scenarios from: a2a_facade, agents_base_uncovered_lines, bridge_coverage, coverage_improvements, m6_autonomy_acceptance, scale_test, container_coverage_r2, lsp_registry, memory_service_coverage, migration_runner_coverage, system_coverage_boost, yaml_engine_direct_coverage # ============================================================ # Originally from: a2a_facade.feature # Feature: A2A Local Facade and Server Stubs # ============================================================ Scenario: Create facade with no services Given a new A2aLocalFacade with no services Then the facade should be created successfully Scenario: Create facade with services dict Given a new A2aLocalFacade with services {"session": "mock_session"} Then the facade should be created successfully Scenario: Register a service on the facade Given a new A2aLocalFacade with no services When I register a service named "planner" on the facade Then the service should be registered successfully Scenario: Register service with empty name raises error Given a new A2aLocalFacade with no services When I try to register a service with an empty name Then an A2A ValueError should be raised # ----------------------------------------------------------------------- # A2aLocalFacade — dispatch for each supported operation # ----------------------------------------------------------------------- Scenario: Dispatch session.create returns session_id and status Given a new A2aLocalFacade with no services When I dispatch operation "session.create" with params {} Then the response status should be "ok" And response data includes key "session_id" And response data key "status" equals "created" Scenario: Dispatch session.close returns status closed Given a new A2aLocalFacade with no services When I dispatch operation "session.close" with params {"session_id": "01M6SM0KESESS0N00000000000"} Then the response status should be "ok" And response data key "status" equals "closed" Scenario: Dispatch plan.create returns plan_id and status Given a new A2aLocalFacade with no services When I dispatch operation "plan.create" with params {} Then the response status should be "ok" And response data includes key "plan_id" And response data key "status" equals "created" Scenario: Dispatch plan.execute returns plan_id and status queued Given a new A2aLocalFacade with no services When I dispatch operation "plan.execute" with params {"plan_id": "PLAN001"} Then the response status should be "ok" And response data key "plan_id" equals "PLAN001" And response data key "status" equals "queued" Scenario: Dispatch plan.status returns plan_id and phase Given a new A2aLocalFacade with no services When I dispatch operation "plan.status" with params {"plan_id": "PLAN001"} Then the response status should be "ok" And response data key "plan_id" equals "PLAN001" And response data key "phase" equals "unknown" Scenario: Dispatch plan.diff returns plan_id and empty changes Given a new A2aLocalFacade with no services When I dispatch operation "plan.diff" with params {"plan_id": "PLAN001"} Then the response status should be "ok" And response data key "plan_id" equals "PLAN001" And response data includes key "changes" Scenario: Dispatch plan.apply returns plan_id and status applied Given a new A2aLocalFacade with no services When I dispatch operation "plan.apply" with params {"plan_id": "PLAN001"} Then the response status should be "ok" And response data key "plan_id" equals "PLAN001" And response data key "status" equals "applied" Scenario: Dispatch registry.list_tools returns empty tools Given a new A2aLocalFacade with no services When I dispatch operation "registry.list_tools" with params {} Then the response status should be "ok" And response data includes key "tools" Scenario: Dispatch registry.list_resources returns empty resources Given a new A2aLocalFacade with no services When I dispatch operation "registry.list_resources" with params {} Then the response status should be "ok" And response data includes key "resources" Scenario: Dispatch context.get returns empty context Given a new A2aLocalFacade with no services When I dispatch operation "context.get" with params {} Then the response status should be "ok" And response data includes key "context" Scenario: Dispatch event.subscribe returns subscription_id and status Given a new A2aLocalFacade with no services When I dispatch operation "event.subscribe" with params {} Then the response status should be "ok" And response data includes key "subscription_id" And response data key "status" equals "subscribed" # ----------------------------------------------------------------------- # A2aLocalFacade — unknown operation # ----------------------------------------------------------------------- Scenario: Dispatch unknown operation raises A2aOperationNotFoundError Given a new A2aLocalFacade with no services When I dispatch an unknown operation "does.not.exist" Then an A2aOperationNotFoundError should be raised And the error operation attribute should be "does.not.exist" # ----------------------------------------------------------------------- # A2aLocalFacade — list_operations # ----------------------------------------------------------------------- Scenario: list_operations returns all supported operations Given a new A2aLocalFacade with no services When I call list_operations on the facade Then the operations list should contain "session.create" And the operations list should contain "plan.create" And the operations list should contain "plan.execute" And the operations list should contain "registry.list_tools" And the operations list should contain "context.get" And the operations list should contain "event.subscribe" And the operations list should have 44 items # ----------------------------------------------------------------------- # A2aHttpTransport — server-mode HTTP transport # ----------------------------------------------------------------------- Scenario Outline: Transport connect accepts valid base URLs Given a new A2aHttpTransport When I try to connect via the transport to "" Then the transport should be connected when using url Examples: | url | | http://localhost:8080 | | https://localhost:8443 | | http://server.example.com/ | | https://a2a.cleverthis.com/api/| Scenario Outline: Transport connect rejects invalid base URLs Given a new A2aHttpTransport When I try to connect via the transport to "" Then an A2aValueError should be raised with message containing "" Examples: | url | reason | | "" | empty string | | "ftp://localhost:8080" | invalid scheme | | None | not a non-empty string| Scenario: Transport send requires A2aRequest instance Given a new A2aHttpTransport When the transport is connected to "http://localhost:8080" And I try to send a non-A2aRequest via the transport Then an A2aTypeError should be raised for the transport Scenario: Transport send while disconnected raises RuntimeError Given a new A2aHttpTransport When I try to send a request via the unconnected transport Then an A2aRuntimeError should be raised for the transport Scenario: Server response is parsed successfully (mocked) Given a new A2aHttpTransport When the transport is connected to "http://localhost:8080" And HTTP responses are mocked with JSON-RPC success response And I send a request via the transport Then the response has no error Scenario: Server returns HTTP 500 error (mocked) Given a new A2aHttpTransport When the transport is connected to "http://localhost:8080" And HTTP responses are mocked with HTTP 500 status And I send a request via the transport Then the response has an error Scenario: Transport disconnect after connect clears state Given a new A2aHttpTransport When the transport is connected to "http://localhost:8080" Then the transport should be connected when using url And I disconnect the transport Then the transport should not be connected # ----------------------------------------------------------------------- # A2aEventQueue — local mode works # ----------------------------------------------------------------------- Scenario: Publish event to local queue Given a new A2aEventQueue When I publish an event with type "plan.started" Then the event queue should have 1 event Scenario: Subscribe locally and receive event Given a new A2aEventQueue And I subscribe locally with a callback When I publish an event with type "plan.completed" Then the callback should have been called with event type "plan.completed" Scenario: Unsubscribe removes subscription Given a new A2aEventQueue And I subscribe locally with a callback When I unsubscribe using the subscription id Then the unsubscribe should return True Scenario: Unsubscribe non-existent returns False Given a new A2aEventQueue When I unsubscribe using a non-existent subscription id Then the unsubscribe should return False Scenario: Get events respects limit Given a new A2aEventQueue When I publish 5 events And I get events with limit 3 Then I should receive 3 events # ----------------------------------------------------------------------- # A2aEventQueue — remote stub raises # ----------------------------------------------------------------------- Scenario: Remote subscribe raises A2aNotAvailableError Given a new A2aEventQueue When I try to subscribe remotely to "http://remote:9090/events" Then an A2aNotAvailableError should be raised # ----------------------------------------------------------------------- # A2aVersionNegotiator # ----------------------------------------------------------------------- Scenario: Negotiate supported version succeeds Given a new A2aVersionNegotiator When I negotiate version "2.0" Then the negotiated version should be "2.0" Scenario: Negotiate unsupported version raises error Given a new A2aVersionNegotiator When I try to negotiate version "99.0" Then an A2aVersionMismatchError should be raised And the error requested_version should be "99.0" Scenario: is_supported returns True for valid version Given a new A2aVersionNegotiator Then version "2.0" should be supported Scenario: is_supported returns False for invalid version Given a new A2aVersionNegotiator Then version "99.0" should not be supported Scenario: get_current returns current version Given a new A2aVersionNegotiator Then the current version should be "2.0" # ----------------------------------------------------------------------- # A2aRequest model validation # ----------------------------------------------------------------------- Scenario: A2aRequest with valid operation succeeds When I create an A2aRequest with operation "session.create" Then the request should have a non-empty request_id And the request a2a_version should be "2.0" Scenario: A2aRequest with empty operation fails validation When I try to create an A2aRequest with empty operation Then a validation error should be raised # ----------------------------------------------------------------------- # A2aResponse model validation # ----------------------------------------------------------------------- Scenario: A2aResponse with valid status ok succeeds When I create an A2aResponse with status "ok" and request_id "REQ001" Then the response should be valid Scenario: A2aResponse with invalid status fails validation When I try to create an A2aResponse with status "maybe" Then a validation error should be raised # ----------------------------------------------------------------------- # A2aErrorDetail model validation # ----------------------------------------------------------------------- Scenario: A2aErrorDetail with valid fields succeeds When I create an A2aErrorDetail with code "NOT_FOUND" and message "gone" Then the error detail should be valid Scenario: A2aErrorDetail with empty code fails validation When I try to create an A2aErrorDetail with empty code Then a validation error should be raised # ----------------------------------------------------------------------- # A2aEvent model construction # ----------------------------------------------------------------------- Scenario: A2aEvent auto-generates event_id and timestamp When I create an A2aEvent with type "plan.progress" Then the event should have a non-empty event_id And the event should have a non-empty timestamp # ----------------------------------------------------------------------- # Error hierarchy # ----------------------------------------------------------------------- Scenario: A2aError is a CleverAgentsError Then A2aError should be a subclass of CleverAgentsError Scenario: A2aNotAvailableError is an A2aError Then A2aNotAvailableError should be a subclass of A2aError Scenario: A2aVersionMismatchError is an A2aError Then A2aVersionMismatchError should be a subclass of A2aError Scenario: A2aOperationNotFoundError is an A2aError Then A2aOperationNotFoundError should be a subclass of A2aError Scenario: A2aNotAvailableError has default message When I create an A2aNotAvailableError with default message Then the error message should contain "not available in local mode" # ============================================================ # Originally from: agents_base_uncovered_lines.feature # Feature: Agents base uncovered lines # ============================================================ Scenario: Processing pipeline schedules the process wrapper Given a basic test agent setup When I patch asyncio create_task and send a message Then the processing pipeline should schedule the process wrapper task Scenario: Process wrapper handles tuple input and emits result Given a basic test agent setup When I process a tuple message through the wrapper Then the tuple message and context should be passed and emitted Scenario: Process wrapper handles non-tuple input with empty context Given a basic test agent setup When I process a non tuple message through the wrapper Then the non tuple message should be processed with empty context Scenario: Process wrapper wraps exceptions with ExecutionError Given a basic test agent setup When process_message raises an error inside the wrapper Then an ExecutionError should be raised with the agent name Scenario: process_message_sync runs coroutine on the event loop Given a basic test agent setup When I call process_message_sync on the agent Then the synchronous result should match the async process output Scenario: Dispose cleans up streams that expose dispose Given a basic test agent setup When I call dispose on an agent with disposable streams Then both input and output streams should be disposed if available Scenario: AgentWithMemory stores and recalls values with its lock Given a basic test agent setup When I remember a value and recall it later Then the remembered value should be returned from memory # ============================================================ # Originally from: bridge_coverage.feature # Feature: RxPyLangGraphBridge uncovered lines and branches # ============================================================ Scenario: __del__ suppresses AttributeError when cleanup_tasks is unavailable Given a bridge with its cleanup_tasks method removed When __del__ is invoked on the crippled bridge Then no exception should propagate from __del__ Scenario: cleanup_tasks_async cancels tasks that are still running Given a bridge with a long-running async task When I run cleanup_tasks_async on the bridge Then the running task should be cancelled And the bridge active tasks set should be empty Scenario: Graph executor processes a string message through execute_graph Given a bridge with a mock graph that returns messages When I run the graph executor with a string content message Then the executor result should contain the graph output And the result metadata should include graph execution history Scenario: Graph executor processes a dict message through execute_graph Given a bridge with a mock graph that returns messages When I run the graph executor with a dict content message Then the executor result should contain the graph output Scenario: Graph executor processes a non-str non-dict message through execute_graph Given a bridge with a mock graph that returns messages When I run the graph executor with a numeric content message Then the executor result should contain the graph output Scenario: Graph executor falls back to state dict when messages list is empty Given a bridge with a mock graph that returns empty messages When I run the graph executor with a string content message Then the executor result should be the full state dict Scenario: create_graph_stream returns StreamConfig for a known graph Given a bridge that owns a registered graph named "demo" When I call create_graph_stream with "demo" Then the returned StreamConfig name should be "graph_demo" Scenario: _create_state_checkpointer succeeds for a valid graph name Given a bridge that owns a registered graph named "ckpt_graph" When I build a state checkpointer for "ckpt_graph" Then the checkpointer operator should be returned without error # ============================================================ # Originally from: coverage_improvements.feature # Feature: Improve Code Coverage # ============================================================ @coverage Scenario: Test __main__ module direct execution When I test the __main__ module if name equals main block Then the sys.exit should be called with the run return value @coverage Scenario: Test platform module when module needs importing Given I remove "cleveragents.cli" from sys.modules When I test ensure_cli_importable with missing module Then the module should be freshly imported @coverage @tdd_issue @tdd_issue_4240 @tdd_expected_fail Scenario: Test CLI main function pass statement When I call the CLI main function with no commands Then the function should complete without error @coverage Scenario: Test exit code normalization with negative code Given the CLI returns a negative exit code -5 When I normalize the exit code Then the normalized code should be -5 @coverage Scenario: Test exit code normalization with code over 255 Given the CLI returns an exit code 300 When I normalize the exit code Then the normalized code should be 255 @coverage Scenario: Test exit code normalization with None returning 0 Given the CLI returns None as exit code When I normalize the exit code Then the normalized code should be 0 @coverage Scenario: Test exit code normalization with invalid string Given the CLI returns "invalid" as exit code When I normalize the exit code Then the normalized code should be 1 # ============================================================ # Originally from: m6_autonomy_acceptance.feature # Feature: M6 autonomy acceptance smoke tests # ============================================================ Scenario: M6 smoke load A2A facade flows fixture Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke load the A2A facade flows fixture Then the m6 smoke facade fixture should have a session lifecycle entry And the m6 smoke facade fixture should have a plan lifecycle entry Scenario: M6 smoke load autonomy guardrails fixture Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke load the autonomy guardrails fixture Then the m6 smoke guardrails fixture should have a denylist entry And the m6 smoke guardrails fixture should have an allowlist entry And the m6 smoke guardrails fixture should have a cost budget entry Scenario: M6 smoke load automation profiles fixture Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke load the automation profiles fixture Then the m6 smoke profiles fixture should list all 8 built-in names And the m6 smoke profiles fixture should have a custom profile entry # --- A2A facade session operations --- Scenario: M6 smoke A2A session create returns session id Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "session.create" with params {} Then the m6 smoke response status should be "ok" And the m6 smoke response data should contain key "session_id" And the m6 smoke response data should contain key "status" Scenario: M6 smoke A2A session close returns closed status Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "session.close" with params {"session_id": "01M6SM0KESESS0N00000000000"} Then the m6 smoke response status should be "ok" And the m6 smoke response data "status" should equal "closed" # --- A2A facade plan operations --- Scenario: M6 smoke A2A plan create returns plan id Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "plan.create" with params {} Then the m6 smoke response status should be "ok" And the m6 smoke response data should contain key "plan_id" Scenario: M6 smoke A2A plan execute queues plan Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "plan.execute" with params {"plan_id": "01M6SM0KE00000000000000001"} Then the m6 smoke response status should be "ok" And the m6 smoke response data "status" should equal "queued" Scenario: M6 smoke A2A plan status returns phase Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "plan.status" with params {"plan_id": "01M6SM0KE00000000000000001"} Then the m6 smoke response status should be "ok" And the m6 smoke response data should contain key "phase" Scenario: M6 smoke A2A plan diff returns changes list Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "plan.diff" with params {"plan_id": "01M6SM0KE00000000000000001"} Then the m6 smoke response status should be "ok" And the m6 smoke response data should contain key "changes" Scenario: M6 smoke A2A plan apply returns applied status Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "plan.apply" with params {"plan_id": "01M6SM0KE00000000000000001"} Then the m6 smoke response status should be "ok" And the m6 smoke response data "status" should equal "applied" # --- A2A facade registry and context operations --- Scenario: M6 smoke A2A registry list tools returns empty list Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "registry.list_tools" with params {} Then the m6 smoke response status should be "ok" And the m6 smoke response data should contain key "tools" Scenario: M6 smoke A2A registry list resources returns empty list Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "registry.list_resources" with params {} Then the m6 smoke response status should be "ok" And the m6 smoke response data should contain key "resources" Scenario: M6 smoke A2A context get returns context dict Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "context.get" with params {} Then the m6 smoke response status should be "ok" And the m6 smoke response data should contain key "context" Scenario: M6 smoke A2A event subscribe returns subscription id Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch "event.subscribe" with params {} Then the m6 smoke response status should be "ok" And the m6 smoke response data should contain key "subscription_id" # --- A2A facade error handling --- Scenario: M6 smoke A2A unknown operation raises error Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch unknown operation "nonexistent.op" Then the m6 smoke facade should raise A2aOperationNotFoundError Scenario: M6 smoke A2A dispatch with invalid request type raises TypeError Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke dispatch with a non-A2aRequest object Then the m6 smoke facade should raise TypeError # --- A2A facade service registration --- Scenario: M6 smoke A2A register service stores service Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke register service "plan_service" on the facade Then the m6 smoke facade should have service "plan_service" Scenario: M6 smoke A2A list operations returns all supported Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke list facade operations Then the m6 smoke operations should include "session.create" And the m6 smoke operations should include "plan.execute" And the m6 smoke operations should include "event.subscribe" And the m6 smoke operations count should be 44 # --- A2A event queue --- Scenario: M6 smoke A2A event queue publish and retrieve Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke A2A event queue When I m6 smoke publish an event with type "plan.progress" Then the m6 smoke event queue should have 1 event And the m6 smoke last event type should be "plan.progress" Scenario: M6 smoke A2A event queue subscribe local callback Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke A2A event queue When I m6 smoke subscribe a local callback And I m6 smoke publish an event with type "plan.complete" Then the m6 smoke callback should have been called once Scenario: M6 smoke A2A event queue unsubscribe Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke A2A event queue When I m6 smoke subscribe a local callback And I m6 smoke unsubscribe the callback And I m6 smoke publish an event with type "plan.complete" Then the m6 smoke callback should not have been called Scenario: M6 smoke A2A event queue close prevents publish Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke A2A event queue When I m6 smoke close the event queue Then the m6 smoke publishing should raise RuntimeError Scenario: M6 smoke A2A event queue remote subscribe raises error Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke A2A event queue When I m6 smoke attempt remote subscribe to "https://example.com/events" Then the m6 smoke facade should raise A2aNotAvailableError # --- A2A HTTP transport — server-mode --- Scenario: M6 smoke A2A transport send while disconnected raises RuntimeError Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke attempt transport send Then the m6 smoke facade should raise RuntimeError Scenario: M6 smoke A2A transport connect succeeds with valid URL Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke attempt transport connect to "https://example.com/a2a" Then the m6 smoke transport should be connected Scenario: M6 smoke A2A transport is_connected returns false before connect Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke check transport is_connected Then the m6 smoke transport should not be connected # --- A2A version negotiation --- Scenario: M6 smoke A2A version negotiation accepts 2.0 Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke negotiate A2A version "2.0" Then the m6 smoke negotiated version should be "2.0" Scenario: M6 smoke A2A version negotiation rejects unsupported Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke negotiate A2A version "99.0" Then the m6 smoke facade should raise A2aVersionMismatchError Scenario: M6 smoke A2A version is_supported returns correct result Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke check if version "2.0" is supported Then the m6 smoke version support should be true When I m6 smoke check if version "99.0" is supported Then the m6 smoke version support should be false # --- Automation profiles built-in --- Scenario: M6 smoke all 8 built-in profiles exist Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke list all built-in profiles Then the m6 smoke profile count should be 8 And the m6 smoke profiles should include "manual" And the m6 smoke profiles should include "full-auto" Scenario: M6 smoke manual profile has all thresholds at 1.0 Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke load built-in profile "manual" Then the m6 smoke profile decompose_task should be 1.0 And the m6 smoke profile create_tool should be 1.0 And the m6 smoke profile select_tool should be 1.0 And the m6 smoke profile require_sandbox should be true Scenario: M6 smoke full-auto profile has no gates Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke load built-in profile "full-auto" Then the m6 smoke profile decompose_task should be 0.0 And the m6 smoke profile create_tool should be 0.0 And the m6 smoke profile select_tool should be 0.0 And the m6 smoke profile require_sandbox should be false And the m6 smoke profile allow_unsafe_tools should be true # --- Automation profile creation and validation --- Scenario: M6 smoke create custom namespaced profile Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke create a profile named "acme/strict" with select_tool 1.0 Then the m6 smoke created profile name should be "acme/strict" And the m6 smoke created profile select_tool should be 1.0 Scenario: M6 smoke profile name validation rejects invalid Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke create a profile with invalid name "has spaces" Then the m6 smoke creation should raise ValueError Scenario: M6 smoke profile threshold validation rejects out of range Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke create a profile with decompose_task 1.5 Then the m6 smoke creation should raise ValueError Scenario: M6 smoke profile from_yaml loads correctly Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke temporary profile YAML file When I m6 smoke load profile from the temp YAML Then the m6 smoke loaded profile name should be "test-yaml-profile" # --- Guard enforcement --- Scenario: M6 smoke guard denylist blocks denied tool Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke profile with denylist guard for "rm_rf" When I m6 smoke check guard for tool "rm_rf" Then the m6 smoke guard result should not be allowed And the m6 smoke guard reason should contain "denylist" Scenario: M6 smoke guard denylist allows non-denied tool Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke profile with denylist guard for "rm_rf" When I m6 smoke check guard for tool "read_file" Then the m6 smoke guard result should be allowed Scenario: M6 smoke guard allowlist blocks unlisted tool Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke profile with allowlist guard for "read_file" and "search" When I m6 smoke check guard for tool "write_file" Then the m6 smoke guard result should not be allowed And the m6 smoke guard reason should contain "allowlist" Scenario: M6 smoke guard max tool calls blocks at limit Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke profile with max 5 tool calls per step When I m6 smoke check guard for tool "llm_call" with 5 calls so far Then the m6 smoke guard result should not be allowed And the m6 smoke guard reason should contain "limit" Scenario: M6 smoke guard cost budget blocks at cap Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke profile with max cost 10.0 When I m6 smoke check guard for tool "llm_call" with cost 10.0 Then the m6 smoke guard result should not be allowed And the m6 smoke guard reason should contain "budget" Scenario: M6 smoke guard write approval blocks write operations Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke profile with write approval required When I m6 smoke check guard for tool "write_file" as a write operation Then the m6 smoke guard result should not be allowed And the m6 smoke guard reason should contain "Write operations" Scenario: M6 smoke guard apply approval blocks apply phase Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke profile with apply approval required When I m6 smoke check guard for tool "__apply__" Then the m6 smoke guard result should not be allowed And the m6 smoke guard reason should contain "Apply phase" Scenario: M6 smoke guard with no guards allows everything Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke profile with no guards When I m6 smoke check guard for tool "anything" Then the m6 smoke guard result should be allowed # --- Profile service resolution precedence --- Scenario: M6 smoke profile resolution plan takes precedence Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke automation profile service When I m6 smoke resolve profile with plan "ci" action "auto" project "manual" Then the m6 smoke resolved profile name should be "ci" Scenario: M6 smoke profile resolution action takes precedence over project Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke automation profile service When I m6 smoke resolve profile with plan null action "auto" project "manual" Then the m6 smoke resolved profile name should be "auto" Scenario: M6 smoke profile resolution falls back to global default Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke automation profile service When I m6 smoke resolve profile with plan null action null project null Then the m6 smoke resolved profile name should be "manual" # --- Profile service guard evaluation --- Scenario: M6 smoke service evaluate guard delegates to profile Given a m6 smoke test runner And a m6 smoke A2A local facade Given a m6 smoke automation profile service When I m6 smoke evaluate guard for profile "manual" and tool "read_file" Then the m6 smoke guard result should be allowed # --- A2A model validation --- Scenario: M6 smoke A2aRequest validates non-empty operation Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke create A2aRequest with empty operation Then the m6 smoke creation should raise ValueError Scenario: M6 smoke A2aResponse validates status values Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke create A2aResponse with invalid status "maybe" Then the m6 smoke creation should raise ValueError Scenario: M6 smoke A2aEvent validates non-empty event_type Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke create A2aEvent with empty event_type Then the m6 smoke creation should raise ValueError Scenario: M6 smoke A2aErrorDetail validates non-empty fields Given a m6 smoke test runner And a m6 smoke A2A local facade When I m6 smoke create A2aErrorDetail with empty code Then the m6 smoke creation should raise ValueError # ============================================================ # Originally from: scale_test.feature # Feature: Scale test fixtures and performance baselines # ============================================================ Scenario: Load scale metadata fixture from disk Given a scale test environment is ready When I scale test load the scale metadata fixture Then the scale test metadata should contain a schema version And the scale test metadata should contain scale profiles Scenario: Scale metadata contains all required profiles Given a scale test environment is ready When I scale test load the scale metadata fixture Then the scale test metadata should have exactly 5 profiles And the scale test profile names should be "small, medium, large, xlarge, xxlarge" Scenario: Each scale profile has required fields Given a scale test environment is ready When I scale test load the scale metadata fixture Then each scale test profile should have a file count And each scale test profile should have a total size in megabytes And each scale test profile should have a language mix And each scale test profile should have expected index time And each scale test profile should have expected decomposition time Scenario: Scale metadata lists supported languages Given a scale test environment is ready When I scale test load the scale metadata fixture Then the scale test metadata should list supported languages And the scale test supported languages should include "python" And the scale test supported languages should include "javascript" # ────────────────────────────────────────────────── # Section 2: Threshold matrix validation # ────────────────────────────────────────────────── Scenario: Load baseline thresholds fixture from disk Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then the scale test thresholds should contain a schema version And the scale test thresholds should have indexing section And the scale test thresholds should have decomposition section Scenario: Indexing thresholds cover all file counts Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then the scale test indexing thresholds should cover "1000_files" And the scale test indexing thresholds should cover "5000_files" And the scale test indexing thresholds should cover "10000_files" And the scale test indexing thresholds should cover "50000_files" And the scale test indexing thresholds should cover "100000_files" Scenario: Each indexing threshold has percentile fields Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then each scale test indexing threshold should have p50 p95 and p99 Scenario: Decomposition thresholds cover all file counts Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then the scale test decomposition thresholds should cover "1000_files" And the scale test decomposition thresholds should cover "5000_files" And the scale test decomposition thresholds should cover "10000_files" And the scale test decomposition thresholds should cover "50000_files" And the scale test decomposition thresholds should cover "100000_files" Scenario: Each decomposition threshold has percentile fields Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then each scale test decomposition threshold should have p50 p95 and p99 Scenario: Thresholds are monotonically increasing across percentiles Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then scale test indexing p50 should be less than p95 for all file counts And scale test indexing p95 should be less than p99 for all file counts # ────────────────────────────────────────────────── # Section 3: Language mix distribution validation # ────────────────────────────────────────────────── Scenario: Language mix sums to 1.0 for each profile Given a scale test environment is ready When I scale test load the scale metadata fixture Then the scale test language mix should sum to 1.0 for each profile Scenario: Language mix values are between 0 and 1 Given a scale test environment is ready When I scale test load the scale metadata fixture Then each scale test language mix value should be between 0 and 1 Scenario: All languages in mix are in supported languages list Given a scale test environment is ready When I scale test load the scale metadata fixture Then all scale test language mix keys should be in supported languages # ────────────────────────────────────────────────── # Section 4: Scale profile simulation # ────────────────────────────────────────────────── Scenario: Generate file count distribution for small profile Given a scale test environment is ready When I scale test generate file distribution for the "small" profile Then the scale test total generated file count should be 1000 And the scale test generated distribution should match the language mix Scenario: Generate file count distribution for medium profile Given a scale test environment is ready When I scale test generate file distribution for the "medium" profile Then the scale test total generated file count should be 5000 And the scale test generated distribution should match the language mix Scenario: Generate file count distribution for large profile Given a scale test environment is ready When I scale test generate file distribution for the "large" profile Then the scale test total generated file count should be 10000 And the scale test generated distribution should match the language mix # ────────────────────────────────────────────────── # Section 5: Baseline threshold realism checks # ────────────────────────────────────────────────── Scenario: Indexing thresholds scale sub-linearly with file count Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then scale test 10K indexing p50 should be less than 10x the 1K p50 Scenario: Decomposition thresholds exceed indexing thresholds Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then scale test decomposition p50 should be greater than indexing p50 for each file count Scenario: Memory thresholds are present and realistic Given a scale test environment is ready When I scale test load the baseline thresholds fixture Then the scale test thresholds should have memory usage section And scale test memory peak should exceed steady state for each file count # ============================================================ # Originally from: container_coverage_r2.feature # Feature: Container module coverage round 2 # ============================================================ Scenario: AI provider returns None when mock disabled and no providers configured Given r2cont- the mock AI environment flag is disabled And r2cont- a provider registry with no configured providers When r2cont- I call get_ai_provider with the mocked registry Then r2cont- the ai provider result should be None Scenario: AI provider returns real provider when mock disabled and providers exist Given r2cont- the mock AI environment flag is disabled And r2cont- a provider registry with configured providers When r2cont- I call get_ai_provider with the mocked registry Then r2cont- the ai provider result should not be None # ------------------------------------------------------------------- # get_ai_provider – features path already on sys.path (line 57 partial) # ------------------------------------------------------------------- Scenario: AI provider with features path already on sys.path does not duplicate it Given r2cont- the mock AI environment flag is enabled And r2cont- the features path is already on sys.path When r2cont- I call get_ai_provider without registry args Then r2cont- the features path should appear exactly once on sys.path # ------------------------------------------------------------------- # _build_project_resource_link_repo (lines 125-130) # ------------------------------------------------------------------- Scenario: Build project resource link repo creates valid repository When r2cont- I build a project resource link repo with an in-memory database Then r2cont- the result should be a ProjectResourceLinkRepository instance # ------------------------------------------------------------------- # get_database_url – env var branches (lines 82, 87) # ------------------------------------------------------------------- Scenario: get_database_url returns fallback when no env vars are set Given r2cont- no database URL environment variables are set When r2cont- I call get_database_url Then r2cont- the database URL should start with "sqlite:///" Scenario: get_database_url returns CLEVERAGENTS_DATABASE_URL when set Given r2cont- the env var "CLEVERAGENTS_DATABASE_URL" is set to "sqlite:///explicit.db" When r2cont- I call get_database_url Then r2cont- the database URL should be "sqlite:///explicit.db" Scenario: get_database_url returns CLEVERAGENTS_TEST_DATABASE_URL when primary is unset Given r2cont- the env var "CLEVERAGENTS_DATABASE_URL" is cleared And r2cont- the env var "CLEVERAGENTS_TEST_DATABASE_URL" is set to "sqlite:///test.db" When r2cont- I call get_database_url Then r2cont- the database URL should be "sqlite:///test.db" Scenario: get_database_url skips empty env var values Given r2cont- the env var "CLEVERAGENTS_DATABASE_URL" is set to empty And r2cont- the env var "CLEVERAGENTS_TEST_DATABASE_URL" is set to empty When r2cont- I call get_database_url Then r2cont- the database URL should start with "sqlite:///" # ------------------------------------------------------------------- # get_container – cached instance (line 256 False branch) # ------------------------------------------------------------------- Scenario: get_container returns the cached instance on subsequent calls Given r2cont- the global container has been reset When r2cont- I call get_container twice Then r2cont- both calls should return the same container instance # ------------------------------------------------------------------- # override_providers – non-existent attribute (line 285 False) # ------------------------------------------------------------------- Scenario: override_providers ignores non-existent provider names Given r2cont- the global container has been reset When r2cont- I override a provider with a non-existent name Then r2cont- no error should have been raised # ------------------------------------------------------------------- # override_providers – non-database_url provider (line 288 else) # ------------------------------------------------------------------- Scenario: override_providers handles non-database_url providers via Object Given r2cont- the global container has been reset When r2cont- I override the settings provider with a mock value Then r2cont- the settings provider should return the mock value # ============================================================ # Originally from: lsp_registry.feature # Feature: LSP Registry and Runtime Stubs # ============================================================ @lsp_reg_create Scenario: Register a new LSP server config Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" When the LSP server is registered Then the LSP registry should contain "local/pyright" And the LSP registry should have 1 server @lsp_reg_create Scenario: Register LSP server with multiple languages Given a clean LSP registry Given an LSP server config named "local/tsserver" for languages "typescript,javascript" with command "typescript-language-server" When the LSP server is registered Then the LSP registry should contain "local/tsserver" And the LSP server "local/tsserver" should support language "typescript" And the LSP server "local/tsserver" should support language "javascript" @lsp_reg_create Scenario: Register LSP server with capabilities Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" And the LSP server config has capabilities "diagnostics,completions,hover" When the LSP server is registered Then the LSP server "local/pyright" should have 3 capabilities @lsp_reg_create @error_handling Scenario: Registering a duplicate server name is rejected Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" And the LSP server is registered When a second LSP server with the same name "local/pyright" is registered Then a ValueError should be raised mentioning "already registered" # --------------------------------------------------------------------------- # Lookup by name # --------------------------------------------------------------------------- @lsp_reg_get Scenario: Lookup a registered server by name Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" And the LSP server is registered When I look up LSP server "local/pyright" Then the LSP lookup result should not be None And the LSP lookup result name should be "local/pyright" @lsp_reg_get Scenario: Lookup a non-existent server returns None Given a clean LSP registry When I look up LSP server "local/nonexistent" Then the LSP lookup result should be None @lsp_reg_get @error_handling Scenario: Lookup with get_or_raise for missing server raises error Given a clean LSP registry When I look up LSP server "local/missing" with get_or_raise Then an LspServerNotFoundError should be raised for "local/missing" # --------------------------------------------------------------------------- # List with namespace filter # --------------------------------------------------------------------------- @lsp_reg_list Scenario: List all registered servers Given a clean LSP registry Given the following LSP servers are registered: | name | language | command | | local/pyright | python | pyright-langserver | | local/tsserver | typescript | typescript-language-server | | devops/clangd | c | clangd | When I list all LSP servers Then the LSP server list should contain 3 servers @lsp_reg_list Scenario: List servers filtered by namespace Given a clean LSP registry Given the following LSP servers are registered: | name | language | command | | local/pyright | python | pyright-langserver | | local/tsserver | typescript | typescript-language-server | | devops/clangd | c | clangd | When I list LSP servers with namespace "local" Then the LSP server list should contain 2 servers # --------------------------------------------------------------------------- # List with language filter # --------------------------------------------------------------------------- @lsp_reg_list Scenario: List servers filtered by language Given a clean LSP registry Given the following LSP servers are registered: | name | language | command | | local/pyright | python | pyright-langserver | | local/tsserver | typescript | typescript-language-server | | devops/pylsp | python | pylsp | When I list LSP servers with language "python" Then the LSP server list should contain 2 servers # --------------------------------------------------------------------------- # Remove server # --------------------------------------------------------------------------- @lsp_reg_remove Scenario: Remove a registered server Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" And the LSP server is registered When I remove LSP server "local/pyright" Then the LSP removal result should be True And the LSP registry should have 0 servers @lsp_reg_remove Scenario: Remove a non-existent server returns False Given a clean LSP registry When I remove LSP server "local/nonexistent" Then the LSP removal result should be False # --------------------------------------------------------------------------- # Runtime raises LspServerNotFoundError for unregistered servers # --------------------------------------------------------------------------- @lsp_runtime Scenario: Runtime start_server raises error for unregistered server Given a clean LSP registry Given a local-mode LSP runtime When I call start_server on the runtime with name "local/pyright" and workspace "/tmp" Then an LSP error should be raised @lsp_runtime Scenario: Runtime stop_server raises error for unregistered server Given a clean LSP registry Given a local-mode LSP runtime When I call stop_server on the runtime with name "local/pyright" Then an LSP error should be raised @lsp_runtime Scenario: Runtime get_diagnostics raises error for unregistered server Given a clean LSP registry Given a local-mode LSP runtime When I call get_diagnostics on the runtime with name "local/pyright" and file "/tmp/test.py" Then an LSP error should be raised @lsp_runtime Scenario: Runtime get_completions raises error for unregistered server Given a clean LSP registry Given a local-mode LSP runtime When I call get_completions on the runtime with name "local/pyright" file "/tmp/test.py" line 1 column 1 Then an LSP error should be raised # --------------------------------------------------------------------------- # Tool adapter generates tool specs from capabilities # --------------------------------------------------------------------------- @lsp_tool_adapter Scenario: Tool adapter generates specs for each capability Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" And the LSP server config has capabilities "diagnostics,completions" When I generate tool specs from the adapter Then the tool spec list should contain 2 specs And the tool spec names should include "local/pyright/diagnostics" And the tool spec names should include "local/pyright/completions" @lsp_tool_adapter Scenario: Tool adapter handler raises LspNotAvailableError in local mode Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" And the LSP server config has capabilities "diagnostics" When I generate tool specs from the adapter And I invoke the first tool spec handler Then an LspNotAvailableError should be raised # --------------------------------------------------------------------------- # Binding validation (valid/invalid references) # --------------------------------------------------------------------------- @lsp_binding Scenario: Valid binding with registered server passes Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" And the LSP server is registered When I create a binding for node "code-editor" to server "local/pyright" Then the binding should be valid And the binding node_name should be "code-editor" And the binding lsp_server_name should be "local/pyright" @lsp_binding @error_handling Scenario: Binding with non-namespaced server name is rejected Given a clean LSP registry When I create a binding for node "code-editor" to server "pyright" Then a ValidationError should be raised mentioning "namespaced" @lsp_binding Scenario: Binding auto_detect defaults to True Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" And the LSP server is registered When I create a binding for node "code-editor" to server "local/pyright" Then the binding auto_detect should be True # --------------------------------------------------------------------------- # Model validation # --------------------------------------------------------------------------- @lsp_model_validation Scenario: LspServerConfig rejects non-namespaced name Given a clean LSP registry When I try to create an LSP server config with name "pyright" and command "pyright-langserver" Then a ValidationError should be raised mentioning "namespaced" @lsp_model_validation Scenario: LspServerConfig normalizes language strings to lowercase Given a clean LSP registry Given an LSP server config named "local/pyright" for language "Python" with command "pyright-langserver" Then the LSP server config language "python" should be present @lsp_model_validation Scenario: LspServerConfig namespace property extracts prefix Given a clean LSP registry Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver" Then the LSP server config namespace should be "local" And the LSP server config short_name should be "pyright" # --------------------------------------------------------------------------- # Error hierarchy # --------------------------------------------------------------------------- @lsp_errors Scenario: LspError inherits from CleverAgentsError Given a clean LSP registry Then LspError should be a subclass of CleverAgentsError @lsp_errors Scenario: LspNotAvailableError has default message Given a clean LSP registry When I create an LspNotAvailableError with default message Then the LSP error message should contain "not available in local mode" @lsp_errors Scenario: LspServerNotFoundError includes server name Given a clean LSP registry When I create an LspServerNotFoundError for server "local/pyright" Then the LSP error message should contain "local/pyright" And the error server_name should be "local/pyright" @lsp_errors @error_handling Scenario: LspServerNotFoundError rejects empty server name Given a clean LSP registry When I try to create an LspServerNotFoundError with empty name Then a ValueError should be raised # ============================================================ # Originally from: memory_service_coverage.feature # Feature: Memory Service Coverage # ============================================================ Scenario: Conversation adapter returns buffer text when configured for summaries Given a conversation adapter configured to return text values When I load memory variables from the adapter Then the adapter provides a string buffer representation Scenario: Conversation adapter handles nested context and async operations Given a conversation adapter with prune tracking When I save nested user and ai context through the adapter And I invoke the adapter async operations Then the adapter normalizes all values and prunes history three times Scenario: Memory service prunes history to the configured window Given a memory service limited to 3 messages When I add five interactions via save context And I ask for the two most recent messages And I ask for more messages than available Then the service keeps only the three latest messages And the recent message requests respect their sizes And the service summary honors a 20 character limit And memory variables expose history and counts Scenario: Memory service manages direct messages and adapters Given a memory service with default settings When I add direct message objects to the service And I inspect the default conversation adapter And I reset the max message limit to 1 And I save a new interaction to enforce pruning And I clear the service memory Then the service computed token counts and summaries before clearing And the service history is empty after clearing And a custom conversation adapter returns text values Scenario: Memory service uses SQL chat history when available Given a patched SQL chat history for memory service When I create a memory service with a connection string Then the SQL history constructor receives the session and connection And stored messages flow through the patched SQL history # ==================== EntityMemory Tests ==================== Scenario: Track a new entity Given a memory service with entity tracking enabled When I track a project entity named "my-project" Then the entity should be stored successfully And the entity should have type "project" And the entity mention count should be 1 Scenario: Track entity with metadata Given a memory service with entity tracking enabled When I track a plan entity named "feature-plan" with metadata Then the entity metadata should include the provided values And the entity should be retrievable by name and type Scenario: Update existing entity increments mention count Given a memory service with entity tracking enabled And I have tracked a file entity named "main.py" When I track the same entity again Then the entity mention count should be 2 And the entity last_seen should be updated Scenario: Get entities by type Given a memory service with multiple entities tracked When I get all entities of type "project" Then I should receive only project entities And I should not receive entities of other types Scenario: Get all entities Given a memory service with multiple entities tracked When I get all tracked entities Then I should receive all entities regardless of type Scenario: Get recent entities Given a memory service with multiple entities tracked over time When I get the 3 most recent entities Then I should receive entities sorted by last access time And I should receive at most 3 entities Scenario: Search entities by name Given a memory service with multiple entities tracked When I search for entities with "main" in the name Then I should receive entities whose names contain "main" And the search should be case-insensitive Scenario: Remove specific entity Given a memory service with entity tracking enabled And I have tracked a context entity named "src/utils.py" When I remove the entity Then the entity should no longer exist And attempting to get the removed entity should return None Scenario: Clear all entities Given a memory service with multiple entities tracked When I clear all entities Then the entity store should be empty And get_entities should return an empty list Scenario: Clear entities by type Given a memory service with multiple entities tracked When I clear only project entities Then project entities should be removed And entities of other types should remain Scenario: Get entity summary Given a memory service with multiple entities tracked When I get the entity summary Then the summary should include total entity count And the summary should include counts by type And the summary should include recent entities Scenario: Entity type can be specified as string Given a memory service with entity tracking enabled When I track an entity with type specified as string "custom" Then the entity should be tracked with EntityType.CUSTOM And the entity should be retrievable using either string or enum type Scenario: Entity store is accessible directly Given a memory service with entity tracking enabled When I access the entity_store property Then I should receive the underlying EntityStore instance And I can track entities through it directly # ==================== Coverage Enhancement Tests ==================== Scenario: Entity from_dict deserializes entity from dictionary Given an entity dictionary with valid data When I create an Entity from the dictionary using from_dict Then the Entity should have the correct name and type And the Entity should have the correct metadata And the Entity should have the correct timestamps And the Entity should have the correct mention count Scenario: Updating existing entity also updates metadata Given a memory service with entity tracking enabled And I have tracked a project entity named "metadata-project" with initial metadata When I track the same project entity again with additional metadata Then the entity metadata should contain both initial and additional values And the entity mention count should be 2 Scenario: Remove non-existent entity returns False Given a memory service with entity tracking enabled When I attempt to remove an entity that does not exist Then the remove operation should return False Scenario: EntityStore with connection string triggers persistence Given an entity store with a mock connection string When I track an entity to trigger dirty state Then the persist callback should mark the store as not dirty And subsequent reads should find the entity Scenario: Get summary returns empty string for empty conversation Given a memory service with no messages When I request the conversation summary Then the summary should be an empty string # ============================================================ # Originally from: migration_runner_coverage.feature # Feature: Migration runner coverage # ============================================================ Scenario: Missing Alembic config raises helpful error Given a migration runner configured for "sqlite:///:memory:" When I attempt to load the alembic config without an alembic.ini file Then a FileNotFoundError should be raised mentioning "alembic.ini" Scenario: Running migrations without engine uses Alembic-managed connection Given a migration runner configured for "sqlite:///:memory:" When I run migrations without providing an engine Then the upgrade command should be invoked without a connection attribute And the CLEVERAGENTS_DATABASE_URL environment variable should be restored Scenario: In-memory SQLite initialization reuses cached engine Given a migration runner configured for "sqlite:///:memory:" When I initialize or upgrade the database with cached in-memory engine Then migrations should run using the cached engine connection And the cached engine should remain available without disposal Scenario: Legacy database is stamped when tables exist without alembic_version Given a migration runner configured for "postgresql://user:pass@localhost/testdb" When I initialize or upgrade the database with legacy tables present Then the stamp command should run using the active connection And the external database engine should be disposed after initialization Scenario: Pending migrations include newest revisions when no current version exists Given a migration runner configured for "sqlite:///:memory:" When I request pending migrations for a database with no current revision Then the pending migration list should be ordered from oldest to newest Scenario: Get current revision queries migration context Given a migration runner configured for "sqlite:///:memory:" When I request the current revision from the database Then the migration context should be queried for the current revision And the temporary connection should be closed afterward Scenario: get_current_revision uses check_same_thread=False for SQLite engines Given a migration runner configured for "sqlite:///:memory:" When I request the current revision from the database Then the SQLite engine for get_current_revision should use check_same_thread=False Scenario: File-based SQLite database directory is created if missing Given a migration runner configured for "sqlite:///tmp/test-db/mydb.db" When I initialize or upgrade a file-based SQLite database Then the parent directory should be created if it does not exist And the database engine should be disposed after initialization Scenario: File-based SQLite uses engine with proper connection args Given a migration runner configured for "sqlite:///tmp/test-db2/mydb.db" When I initialize or upgrade a file-based SQLite database Then the engine should be created with check_same_thread set to False And the database engine should be disposed after initialization Scenario: Running migrations when database already up to date Given a migration runner configured for "sqlite:///:memory:" When I initialize the database and migrations are already applied Then no additional migrations should be run And check migrations needed should return False Scenario: Pending migrations trigger upgrade when alembic_version exists Given a migration runner configured for "sqlite:///:memory:" When I initialize the database with pending migrations detected Then run migrations should be invoked with the existing engine And the in-memory engine should not be disposed Scenario: Pending migrations require approval before running Given a migration runner configured for "sqlite:///:memory:" When I initialize the database with pending migrations requiring confirmation Then the migration approval prompt should be invoked And migrations should run after approval Scenario: Declining migration approval raises an error Given a migration runner configured for "sqlite:///:memory:" When I decline migration approval when pending migrations exist Then a migration approval error should be raised Scenario: Default prompt auto-approves when running in CI Given a migration runner configured for "sqlite:///:memory:" When I evaluate the default migration prompt while CI is set Then the default prompt should auto approve without interaction Scenario: Default prompt delegates to typer confirm in TTY Given a migration runner configured for "sqlite:///:memory:" When I evaluate the default migration prompt in an interactive TTY Then the default prompt should return the typer confirmation result Scenario: Default prompt rejects migration after interactive prompt IOError Given a migration runner configured for "sqlite:///:memory:" When the default migration prompt raises an IOError while prompting Then the default prompt should reject migration after the IO failure Scenario: Default prompt rejects migration in non-interactive environment Given a migration runner configured for "sqlite:///:memory:" When I evaluate the default migration prompt in a non-interactive environment Then the default prompt should reject without interaction # ============================================================ # Originally from: system_coverage_boost.feature # Feature: System commands coverage boost # ============================================================ Scenario: git sha returns unknown when git not available Given a system module for coverage testing When I call git_sha with git not on path Then the git sha should be "unknown" Scenario: dep_version returns not installed for missing package Given a system module for coverage testing When I call dep_version for a missing package Then the dep version should be "not installed" Scenario: build_info_data with missing database path Given a system module for coverage testing When I call build_info_data with no database file Then the info data should contain db_size as "0 MB" Scenario: build_info_data with missing log directory Given a system module for coverage testing When I call build_info_data with no log directory Then the info data should contain logs as "0 MB" Scenario: check_data_dir with non-existent dir Given a system module for coverage testing When I call check_data_dir with non-existent directory Then the check result should have WARN status Scenario: check_database with non-sqlite URL Given a system module for coverage testing When I call check_database with postgres URL Then the check result should have OK status and details "configured" Scenario: check_database with missing parent dir Given a system module for coverage testing When I call check_database with sqlite and non-writable parent Then the check result should have ERROR status Scenario: check_file_permissions with non-existent data dir Given a system module for coverage testing When I call check_file_permissions with non-existent directory Then the check result should have WARN status Scenario: check_file_permissions with read-only data dir Given a system module for coverage testing When I call check_file_permissions with read-only directory Then the file perm result status should not be OK Scenario: check_git when git is unavailable Given a system module for coverage testing When I call check_git with git unavailable Then the check result should have ERROR status Scenario: render_version_rich produces output Given a system module for coverage testing When I call render_version_rich with sample data Then no system exception should be raised Scenario: render_info_rich produces output Given a system module for coverage testing When I call render_info_rich with sample data Then no system exception should be raised Scenario: render_diagnostics_rich with errors Given a system module for coverage testing When I call render_diagnostics_rich with error data Then no system exception should be raised Scenario: render_diagnostics_rich with warnings only Given a system module for coverage testing When I call render_diagnostics_rich with warning data Then no system exception should be raised Scenario: render_diagnostics_rich with all OK Given a system module for coverage testing When I call render_diagnostics_rich with all OK data Then no system exception should be raised # ============================================================ # Originally from: yaml_engine_direct_coverage.feature # Feature: YAML engine direct coverage # ============================================================ Scenario: Engine construction registers filters and globals When I construct a new YAMLTemplateEngine for direct testing Then the direct engine should have yaml indent sum selectattr filters And the direct engine globals should include builtins Scenario: load_file reads and parses YAML from a temp file Given I construct a new YAMLTemplateEngine for direct testing And I write a temp YAML file with mapping content When I call load_file on the direct engine without context Then the direct load_file result should be a dict with app key Scenario: load_string parses plain YAML mapping Given I construct a new YAMLTemplateEngine for direct testing When I call load_string with plain mapping YAML on direct engine Then the direct result should have key server Scenario: load_string raises for plain YAML list Given I construct a new YAMLTemplateEngine for direct testing When I call load_string with plain list YAML on direct engine Then a direct ValueError mentioning dict should be raised Scenario: load_string with context renders and parses template Given I construct a new YAMLTemplateEngine for direct testing When I call load_string with template and context on direct engine Then the direct rendered result should have substituted values Scenario: load_string without context defers rendering Given I construct a new YAMLTemplateEngine for direct testing When I call load_string with template but no context on direct engine Then the direct deferred result should be a dict or raise YAMLError Scenario: preprocess inserts indent hints for for loops Given I construct a new YAMLTemplateEngine for direct testing When I call preprocess on content with a for loop Then the direct preprocessed result should contain indent hint Scenario: postprocess removes blank lines before unindented content Given I construct a new YAMLTemplateEngine for direct testing When I call postprocess on content with blank lines before unindented Then blank lines before unindented content should be gone Scenario: fix_common_yaml_issues splits multi-colon lines Given I construct a new YAMLTemplateEngine for direct testing When I call fix_common_yaml_issues on multi-colon content Then the fixed content should have multiple lines Scenario: analyze_yaml_structure finds blocks and inline templates Given I construct a new YAMLTemplateEngine for direct testing When I call analyze_yaml_structure on mixed content Then template_blocks and inline_templates should be populated And hierarchy should have root key Scenario: yaml_filter converts dict to YAML string When I call the static yaml_filter with a dict Then the yaml_filter result should contain key colon Scenario: indent_filter indents lines When I call the static indent_filter with 4 spaces Then each direct line should start with 4 spaces Scenario: sum_filter returns numeric sum When I call the static sum_filter on a number list Then the direct sum should be 10 Scenario: selectattr_filter extracts with defaults When I call the static selectattr_filter for score Then items with score return their value and others return 0 Scenario: create_render_context merges nested and top-level When I call the static create_render_context with nested data Then the direct context should have both nested and explicit keys Scenario: render_and_parse falls back on initial YAML error Given I construct a new YAMLTemplateEngine for direct testing When I call render_and_parse with colon-heavy value Then the direct engine should attempt fix or raise Scenario: render_and_parse raises ValueError for non-dict result Given I construct a new YAMLTemplateEngine for direct testing When I call render_and_parse that produces a list Then a direct ValueError mentioning dict should be raised Scenario: simple_template_extraction raises YAMLError for invalid Given I construct a new YAMLTemplateEngine for direct testing When I call simple_template_extraction with invalid YAML Then a direct YAML error should be raised Scenario: simple_template_extraction raises ValueError for non-dict Given I construct a new YAMLTemplateEngine for direct testing When I call simple_template_extraction with list YAML Then a direct ValueError mentioning dict should be raised Scenario: load_file with context renders templates from disk Given I construct a new YAMLTemplateEngine for direct testing And I write a temp YAML file with template content When I call load_file with template context on direct engine Then the direct file result should contain rendered values