"""Step definitions for lsp_capability_enum.feature.""" from __future__ import annotations from behave import given, then, when from cleveragents.lsp.models import LspCapability, LspServerConfig from cleveragents.lsp.tool_adapter import ( _CAPABILITY_TOOL_MAP, LspToolAdapter, _input_schema_for, ) # ── Given steps ────────────────────────────────────────────── @given("the LspCapability enum for lsp_cap") def step_load_enum_lsp_cap(context: object) -> None: """Load the LspCapability enum.""" context.lsp_cap_enum = LspCapability # type: ignore[attr-defined] @given("the lsp_cap capability tool map") def step_load_tool_map_lsp_cap(context: object) -> None: """Load the capability tool map.""" context.lsp_cap_tool_map = _CAPABILITY_TOOL_MAP # type: ignore[attr-defined] @given('an LspServerConfig with capability "{capability}" for lsp_cap') def step_config_with_capability_lsp_cap( context: object, capability: str, ) -> None: """Create an LspServerConfig with a single capability.""" cap = LspCapability(capability) context.lsp_cap_config = LspServerConfig( # type: ignore[attr-defined] name="test/server", languages=["python"], command="test-server", capabilities=[cap], ) @given('the lsp_cap input schema for "{capability}"') def step_input_schema_lsp_cap(context: object, capability: str) -> None: """Get the input schema for a capability.""" cap = LspCapability(capability) context.lsp_cap_schema = _input_schema_for(cap) # type: ignore[attr-defined] @given("the lsp_cap stubbed server capabilities") def step_stubbed_caps_lsp_cap(context: object) -> None: """Load the stubbed server capabilities.""" from cleveragents.lsp.server import _STUBBED_CAPABILITIES context.lsp_cap_stubbed = _STUBBED_CAPABILITIES # type: ignore[attr-defined] # ── When steps ─────────────────────────────────────────────── @when("I generate tool specs for lsp_cap") def step_generate_specs_lsp_cap(context: object) -> None: """Generate tool specs from the config.""" config = context.lsp_cap_config # type: ignore[attr-defined] adapter = LspToolAdapter() context.lsp_cap_specs = adapter.generate_tool_specs(config) # type: ignore[attr-defined] # ── Then steps ─────────────────────────────────────────────── @then("the lsp_cap enum should have {count:d} members") def step_enum_count_lsp_cap(context: object, count: int) -> None: """Assert the enum has the expected number of members.""" members = list(context.lsp_cap_enum) # type: ignore[attr-defined] assert len(members) == count, ( f"Expected {count} members, got {len(members)}: {[m.value for m in members]}" ) @then('the lsp_cap enum should include "{member}"') def step_enum_includes_lsp_cap(context: object, member: str) -> None: """Assert the enum includes the given member value.""" values = [m.value for m in context.lsp_cap_enum] # type: ignore[attr-defined] assert member in values, f"'{member}' not in enum values: {values}" @then("the lsp_cap tool map should have {count:d} entries") def step_tool_map_count_lsp_cap(context: object, count: int) -> None: """Assert the tool map has the expected number of entries.""" tool_map = context.lsp_cap_tool_map # type: ignore[attr-defined] assert len(tool_map) == count, f"Expected {count} entries, got {len(tool_map)}" @then("the lsp_cap generated specs should not be empty") def step_specs_not_empty_lsp_cap(context: object) -> None: """Assert generated specs are not empty.""" specs = context.lsp_cap_specs # type: ignore[attr-defined] assert len(specs) > 0, "Expected non-empty tool specs" @then('the lsp_cap tool spec should have keys "name" and "input_schema"') def step_spec_has_keys_lsp_cap(context: object) -> None: """Assert generated specs have required keys.""" specs = context.lsp_cap_specs # type: ignore[attr-defined] for spec in specs: assert "name" in spec, f"Spec missing 'name': {spec}" assert "input_schema" in spec, f"Spec missing 'input_schema': {spec}" @then('the lsp_cap tool name should end with "{suffix}"') def step_tool_name_ends_with_lsp_cap(context: object, suffix: str) -> None: """Assert at least one tool name ends with the suffix.""" specs = context.lsp_cap_specs # type: ignore[attr-defined] names = [s["name"] for s in specs] assert any(n.endswith(f"/{suffix}") for n in names), ( f"No tool name ends with '/{suffix}': {names}" ) @then('the lsp_cap schema should require "{field}"') def step_schema_requires_lsp_cap(context: object, field: str) -> None: """Assert the schema requires the given field.""" schema = context.lsp_cap_schema # type: ignore[attr-defined] required = schema.get("required", []) assert field in required, f"'{field}' not in required: {required}" @then('the lsp_cap schema should not require "{field}"') def step_schema_not_requires_lsp_cap(context: object, field: str) -> None: """Assert the schema does not require the given field.""" schema = context.lsp_cap_schema # type: ignore[attr-defined] required = schema.get("required", []) assert field not in required, f"'{field}' should not be in required: {required}" @then('the lsp_cap stubbed caps should include "{key}"') def step_stubbed_includes_lsp_cap(context: object, key: str) -> None: """Assert the stubbed capabilities dict includes the key.""" caps = context.lsp_cap_stubbed # type: ignore[attr-defined] assert key in caps, f"'{key}' not in stubbed capabilities: {list(caps)}" # ── When/Then steps: negative tests ───────────────────────── @when('I create an LspCapability with value "{value}" for lsp_cap') def step_create_invalid_capability_lsp_cap(context: object, value: str) -> None: """Attempt to create an LspCapability with an invalid value.""" context.lsp_cap_error = None # type: ignore[attr-defined] try: LspCapability(value) except ValueError as exc: context.lsp_cap_error = exc # type: ignore[attr-defined] @then("the lsp_cap creation should raise a ValueError") def step_should_raise_value_error_lsp_cap(context: object) -> None: """Assert that a ValueError was raised.""" err = context.lsp_cap_error # type: ignore[attr-defined] assert err is not None, "Expected ValueError but none was raised" @when("I call _input_schema_for with a fake capability for lsp_cap") def step_call_schema_fake_cap_lsp_cap(context: object) -> None: """Call _input_schema_for with a value not in any category tuple.""" from enum import StrEnum class _FakeCap(StrEnum): FAKE = "fake_capability" context.lsp_cap_schema_error = None # type: ignore[attr-defined] try: _input_schema_for(_FakeCap.FAKE) # type: ignore[arg-type] except ValueError as exc: context.lsp_cap_schema_error = exc # type: ignore[attr-defined] @then("the lsp_cap schema call should raise a ValueError") def step_schema_should_raise_lsp_cap(context: object) -> None: """Assert _input_schema_for raised ValueError.""" err = context.lsp_cap_schema_error # type: ignore[attr-defined] assert err is not None, "Expected ValueError from _input_schema_for but none raised"