forked from HAL9000/cleveragents-core
31472b5413
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate. ISSUES CLOSED: #1232
377 lines
14 KiB
Python
377 lines
14 KiB
Python
"""Step definitions for features/acms_service_coverage_r3.feature.
|
|
|
|
Targets uncovered lines in src/cleveragents/application/services/acms_service.py:
|
|
- Lines 34-35: TYPE_CHECKING imports (Settings, UnitOfWork) — validated via DI
|
|
- Line 112: ContextStrategy.can_handle protocol default body (Ellipsis)
|
|
- Line 120: ContextStrategy.assemble protocol default body (Ellipsis)
|
|
- Line 124: ContextStrategy.explain protocol default body (Ellipsis)
|
|
- Line 275: StrategySelector.select protocol default body (Ellipsis)
|
|
- Line 292: BudgetAllocator.allocate protocol default body (Ellipsis)
|
|
- Line 309: StrategyExecutor.execute protocol default body (Ellipsis)
|
|
- Line 396: SkeletonCompressor.compress protocol default body (Ellipsis)
|
|
- Line 577: DefaultBudgetPacker.pack returns fragments unchanged
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.application.services.acms_service import (
|
|
ACMSPipeline,
|
|
BudgetAllocator,
|
|
ContextStrategy,
|
|
DefaultBudgetPacker,
|
|
SkeletonCompressor,
|
|
StrategyCapabilities,
|
|
StrategyExecutor,
|
|
StrategySelector,
|
|
)
|
|
from cleveragents.domain.models.core.context_fragment import (
|
|
ContextBudget,
|
|
ContextFragment,
|
|
FragmentProvenance,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_DEFAULT_PROVENANCE = FragmentProvenance(resource_uri="test://coverage-r3")
|
|
|
|
|
|
def _make_frag(
|
|
uko_node: str = "test://r3",
|
|
content: str = "r3 content",
|
|
token_count: int = 10,
|
|
relevance_score: float = 0.5,
|
|
) -> ContextFragment:
|
|
return ContextFragment(
|
|
uko_node=uko_node,
|
|
content=content,
|
|
token_count=token_count,
|
|
relevance_score=relevance_score,
|
|
provenance=_DEFAULT_PROVENANCE,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Bare Protocol subclasses — inherit default method bodies (the `...` stubs)
|
|
# without overriding them, so calling the methods executes the Ellipsis lines.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class _BareContextStrategy(ContextStrategy):
|
|
"""Inherits ContextStrategy Protocol defaults for can_handle/assemble/explain."""
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "bare_test"
|
|
|
|
@property
|
|
def capabilities(self) -> StrategyCapabilities:
|
|
return StrategyCapabilities()
|
|
|
|
|
|
class _BareStrategySelector(StrategySelector):
|
|
"""Inherits StrategySelector Protocol default for select."""
|
|
|
|
|
|
class _BareBudgetAllocator(BudgetAllocator):
|
|
"""Inherits BudgetAllocator Protocol default for allocate."""
|
|
|
|
|
|
class _BareStrategyExecutor(StrategyExecutor):
|
|
"""Inherits StrategyExecutor Protocol default for execute."""
|
|
|
|
|
|
class _BareSkeletonCompressor(SkeletonCompressor):
|
|
"""Inherits SkeletonCompressor Protocol default for compress."""
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Lines 34-35: TYPE_CHECKING imports — DI validation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("ascov3 I have mock Settings and UnitOfWork objects")
|
|
def step_given_mock_settings_uow(context: Context) -> None:
|
|
context.ascov3_mock_settings = MagicMock(name="MockSettings")
|
|
context.ascov3_mock_uow = MagicMock(name="MockUnitOfWork")
|
|
|
|
|
|
@when("ascov3 I create a pipeline with settings and unit_of_work injected")
|
|
def step_when_create_pipeline_with_di(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
context.ascov3_pipeline = ACMSPipeline(
|
|
settings=context.ascov3_mock_settings,
|
|
unit_of_work=context.ascov3_mock_uow,
|
|
)
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the pipeline should store the injected dependencies")
|
|
def step_then_pipeline_stores_deps(context: Context) -> None:
|
|
assert context.ascov3_error is None, (
|
|
f"Pipeline creation failed: {context.ascov3_error}"
|
|
)
|
|
pipeline = context.ascov3_pipeline
|
|
assert pipeline._settings is context.ascov3_mock_settings, (
|
|
"Pipeline did not store the injected settings"
|
|
)
|
|
assert pipeline._unit_of_work is context.ascov3_mock_uow, (
|
|
"Pipeline did not store the injected unit_of_work"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Line 112: ContextStrategy.can_handle default body
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("ascov3 I have a bare ContextStrategy subclass")
|
|
def step_given_bare_context_strategy(context: Context) -> None:
|
|
context.ascov3_bare_strategy = _BareContextStrategy()
|
|
|
|
|
|
@when("ascov3 I call can_handle on the bare strategy with an empty request")
|
|
def step_when_call_can_handle(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
context.ascov3_can_handle_result = context.ascov3_bare_strategy.can_handle({})
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the can_handle result should be None")
|
|
def step_then_can_handle_none(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"can_handle raised: {context.ascov3_error}"
|
|
assert context.ascov3_can_handle_result is None, (
|
|
f"Expected None, got {context.ascov3_can_handle_result!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Line 120: ContextStrategy.assemble default body
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("ascov3 I call assemble on the bare strategy with empty fragments and a budget")
|
|
def step_when_call_assemble(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
budget = ContextBudget(max_tokens=100, reserved_tokens=0)
|
|
context.ascov3_assemble_result = context.ascov3_bare_strategy.assemble(
|
|
[], budget
|
|
)
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the assemble result should be None")
|
|
def step_then_assemble_none(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"assemble raised: {context.ascov3_error}"
|
|
assert context.ascov3_assemble_result is None, (
|
|
f"Expected None, got {context.ascov3_assemble_result!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Line 124: ContextStrategy.explain default body
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("ascov3 I call explain on the bare strategy")
|
|
def step_when_call_explain(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
context.ascov3_explain_result = context.ascov3_bare_strategy.explain()
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the explain result should be None")
|
|
def step_then_explain_none(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"explain raised: {context.ascov3_error}"
|
|
assert context.ascov3_explain_result is None, (
|
|
f"Expected None, got {context.ascov3_explain_result!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Line 275: StrategySelector.select default body
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("ascov3 I have a bare StrategySelector subclass")
|
|
def step_given_bare_selector(context: Context) -> None:
|
|
context.ascov3_bare_selector = _BareStrategySelector()
|
|
|
|
|
|
@when("ascov3 I call select on the bare selector with empty strategies")
|
|
def step_when_call_select(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
context.ascov3_select_result = context.ascov3_bare_selector.select([], {})
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the select result should be None")
|
|
def step_then_select_none(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"select raised: {context.ascov3_error}"
|
|
assert context.ascov3_select_result is None, (
|
|
f"Expected None, got {context.ascov3_select_result!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Line 292: BudgetAllocator.allocate default body
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("ascov3 I have a bare BudgetAllocator subclass")
|
|
def step_given_bare_allocator(context: Context) -> None:
|
|
context.ascov3_bare_allocator = _BareBudgetAllocator()
|
|
|
|
|
|
@when("ascov3 I call allocate on the bare allocator with empty candidates")
|
|
def step_when_call_allocate(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
context.ascov3_allocate_result = context.ascov3_bare_allocator.allocate([], 100)
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the allocate result should be None")
|
|
def step_then_allocate_none(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"allocate raised: {context.ascov3_error}"
|
|
assert context.ascov3_allocate_result is None, (
|
|
f"Expected None, got {context.ascov3_allocate_result!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Line 309: StrategyExecutor.execute default body
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("ascov3 I have a bare StrategyExecutor subclass")
|
|
def step_given_bare_executor(context: Context) -> None:
|
|
context.ascov3_bare_executor = _BareStrategyExecutor()
|
|
|
|
|
|
@when("ascov3 I call execute on the bare executor with empty allocations")
|
|
def step_when_call_execute(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
budget = ContextBudget(max_tokens=100, reserved_tokens=0)
|
|
context.ascov3_execute_result = context.ascov3_bare_executor.execute(
|
|
[], [], budget
|
|
)
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the execute result should be None")
|
|
def step_then_execute_none(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"execute raised: {context.ascov3_error}"
|
|
assert context.ascov3_execute_result is None, (
|
|
f"Expected None, got {context.ascov3_execute_result!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Line 396: SkeletonCompressor.compress default body
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("ascov3 I have a bare SkeletonCompressor subclass")
|
|
def step_given_bare_compressor(context: Context) -> None:
|
|
context.ascov3_bare_compressor = _BareSkeletonCompressor()
|
|
|
|
|
|
@when("ascov3 I call compress on the bare compressor with empty fragments")
|
|
def step_when_call_compress(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
context.ascov3_compress_result = context.ascov3_bare_compressor.compress(
|
|
(), 100
|
|
)
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the compress result should be None")
|
|
def step_then_compress_none(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"compress raised: {context.ascov3_error}"
|
|
assert context.ascov3_compress_result is None, (
|
|
f"Expected None, got {context.ascov3_compress_result!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Line 577: DefaultBudgetPacker.pack
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("ascov3 I have a DefaultBudgetPacker instance")
|
|
def step_given_default_packer(context: Context) -> None:
|
|
context.ascov3_packer = DefaultBudgetPacker()
|
|
|
|
|
|
@given("ascov3 I have a list of test fragments")
|
|
def step_given_test_fragments(context: Context) -> None:
|
|
context.ascov3_fragments = [
|
|
_make_frag(uko_node="test://r3-a", content="alpha", token_count=10),
|
|
_make_frag(uko_node="test://r3-b", content="beta", token_count=20),
|
|
]
|
|
|
|
|
|
@when("ascov3 I call pack on the packer with those fragments and a budget")
|
|
def step_when_call_pack_with_fragments(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
budget = ContextBudget(max_tokens=200, reserved_tokens=0)
|
|
context.ascov3_pack_result = context.ascov3_packer.pack(
|
|
context.ascov3_fragments, budget
|
|
)
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@when("ascov3 I call pack on the packer with empty fragments and a budget")
|
|
def step_when_call_pack_empty(context: Context) -> None:
|
|
context.ascov3_error = None
|
|
try:
|
|
budget = ContextBudget(max_tokens=200, reserved_tokens=0)
|
|
context.ascov3_pack_result = context.ascov3_packer.pack([], budget)
|
|
except Exception as exc:
|
|
context.ascov3_error = exc
|
|
|
|
|
|
@then("ascov3 the pack result should be the same fragments unchanged")
|
|
def step_then_pack_result_unchanged(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"pack raised: {context.ascov3_error}"
|
|
result = list(context.ascov3_pack_result)
|
|
expected = context.ascov3_fragments
|
|
assert len(result) == len(expected), (
|
|
f"Expected {len(expected)} fragments, got {len(result)}"
|
|
)
|
|
for i, (r, e) in enumerate(zip(result, expected, strict=True)):
|
|
assert r.fragment_id == e.fragment_id, (
|
|
f"Fragment {i}: expected id {e.fragment_id}, got {r.fragment_id}"
|
|
)
|
|
|
|
|
|
@then("ascov3 the pack result should be an empty sequence")
|
|
def step_then_pack_result_empty(context: Context) -> None:
|
|
assert context.ascov3_error is None, f"pack raised: {context.ascov3_error}"
|
|
result = list(context.ascov3_pack_result)
|
|
assert result == [], f"Expected empty list, got {result}"
|