Files
cleveragents-core/features/steps/detail_level_coverage_steps.py
freemo 051ee7c290
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 21s
CI / quality (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 47s
CI / security (pull_request) Successful in 52s
CI / build (pull_request) Successful in 56s
CI / e2e_tests (pull_request) Successful in 5m1s
CI / integration_tests (pull_request) Successful in 5m30s
CI / unit_tests (pull_request) Successful in 5m42s
CI / docker (pull_request) Successful in 58s
CI / coverage (pull_request) Successful in 7m35s
CI / build (push) Successful in 21s
CI / docker (push) Has been skipped
CI / benchmark-regression (pull_request) Failing after 49m24s
CI / lint (push) Successful in 22s
CI / quality (push) Successful in 39s
CI / security (push) Successful in 48s
CI / typecheck (push) Successful in 1m26s
CI / benchmark-regression (push) Has been skipped
CI / e2e_tests (push) Successful in 5m53s
CI / coverage (push) Successful in 9m4s
CI / benchmark-publish (push) Successful in 19m10s
CI / integration_tests (push) Failing after 19m18s
CI / unit_tests (push) Failing after 19m20s
test(coverage): add Behave BDD tests to improve coverage across 52 source files
Added 52 new .feature files and corresponding _steps.py files targeting
previously uncovered code paths in the following areas:

- TUI layer: app, commands, persona (state/schema/registry), widgets,
  input (shell_exec, reference_parser)
- Application services: plan lifecycle/service/executor, session,
  project, repo indexing, correction, checkpoint, actor, llm_actors,
  strategy coordinator, resource file watcher, service retry wiring
- CLI commands: session, resource, repl, plan, db, automation_profile
- Domain models: retry_policy, resource_type, cost_budget,
  docker_compose_analyzer, detail_level, _sql_string_aware,
  _postgresql_helpers
- Core: circuit_breaker, retry_service_patterns
- Infrastructure: repositories, transaction_sandbox, strategy_registry,
  plugins/loader, container
- Config: settings
- Agents: plan_generation, context_analysis, auto_debug
- A2A: facade

All new tests follow the Behave/Gherkin BDD standard. Resolved step
definition collisions with unique prefixes. Fixed Alembic fileConfig
logger disabling issue (disable_existing_loggers=False).

ISSUES CLOSED: #1068
2026-03-20 21:22:10 +00:00

146 lines
6.0 KiB
Python

"""Step definitions for detail_level_coverage.feature.
These steps target specific uncovered lines in detail_level.py:
- Line 157: __setattr__ defense-in-depth re-freeze when model validator is bypassed
- Lines 166-170: __copy__ (copy.copy) support
- Line 181: __deepcopy__ with memo=None creating a fresh memo dict
- Lines 250-252: register() raising ValueError when depth exceeds max_depth
"""
import copy
import json
from types import MappingProxyType
from unittest.mock import patch
from behave import given, then, when
from pydantic import BaseModel
from cleveragents.domain.models.acms.detail_level import (
DetailLevelMap,
)
# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------
@given("the detail level module is imported")
def step_detail_level_module_imported(context):
"""Ensure the module is importable."""
assert DetailLevelMap is not None
# ---------------------------------------------------------------------------
# Shared construction step
# ---------------------------------------------------------------------------
@given(
'a DetailLevelMap with domain "{domain}" and levels {levels_json} and max_depth {max_depth:d}'
)
def step_create_detail_level_map(context, domain, levels_json, max_depth):
"""Construct a DetailLevelMap from the scenario parameters."""
levels = json.loads(levels_json)
context.dlm = DetailLevelMap(
domain=domain,
levels=levels,
max_depth=max_depth,
)
# ---------------------------------------------------------------------------
# Scenario: copy.copy (lines 166-170)
# ---------------------------------------------------------------------------
@when("I shallow-copy the DetailLevelMap")
def step_shallow_copy(context):
"""Perform a shallow copy via copy.copy()."""
context.dlm_copy = copy.copy(context.dlm)
@then("the copy should be a distinct DetailLevelMap instance")
def step_verify_distinct_copy(context):
"""Verify the copy is a new object."""
assert context.dlm_copy is not context.dlm
assert isinstance(context.dlm_copy, DetailLevelMap)
@then("the copy should have the same domain and levels as the original")
def step_verify_copy_data(context):
"""Verify the copy has identical data."""
assert context.dlm_copy.domain == context.dlm.domain
assert dict(context.dlm_copy.levels) == dict(context.dlm.levels)
assert context.dlm_copy.max_depth == context.dlm.max_depth
# Levels should be frozen in the copy as well
assert isinstance(context.dlm_copy.levels, MappingProxyType)
# ---------------------------------------------------------------------------
# Scenario: __deepcopy__ with None memo (line 181)
# ---------------------------------------------------------------------------
@when("I call __deepcopy__ with None memo")
def step_deepcopy_none_memo(context):
"""Call __deepcopy__ directly with memo=None to hit line 181."""
context.dlm_deepcopy = context.dlm.__deepcopy__(None)
@then("the deep copy should be a valid DetailLevelMap with the same data")
def step_verify_deepcopy(context):
"""Verify the deep copy is valid and matches the original."""
assert isinstance(context.dlm_deepcopy, DetailLevelMap)
assert context.dlm_deepcopy is not context.dlm
assert context.dlm_deepcopy.domain == context.dlm.domain
assert dict(context.dlm_deepcopy.levels) == dict(context.dlm.levels)
assert context.dlm_deepcopy.max_depth == context.dlm.max_depth
assert isinstance(context.dlm_deepcopy.levels, MappingProxyType)
# ---------------------------------------------------------------------------
# Scenario: register rejects depth > max_depth (lines 250-252)
# ---------------------------------------------------------------------------
@when('I register a level "{name}" with depth {depth:d}')
def step_register_exceeding_depth(context, name, depth):
"""Attempt to register a level whose depth exceeds max_depth."""
try:
context.dlm.register(name, depth)
context.register_error = None
except ValueError as exc:
context.register_error = exc
@then("a ValueError should be raised mentioning exceeds max_depth")
def step_verify_register_error(context):
"""Verify the ValueError was raised with the expected message."""
assert context.register_error is not None, "Expected ValueError but none was raised"
msg = str(context.register_error)
assert "exceeds" in msg, f"Expected 'exceeds' in error message, got: {msg}"
assert "max_depth" in msg, f"Expected 'max_depth' in error message, got: {msg}"
# ---------------------------------------------------------------------------
# Scenario: __setattr__ defense-in-depth re-freeze (line 157)
# ---------------------------------------------------------------------------
@when("I assign a plain dict to levels while bypassing Pydantic model validation")
def step_assign_bypassing_pydantic(context):
"""Bypass Pydantic's model validator so __setattr__ defense-in-depth kicks in.
We mock BaseModel.__setattr__ to do a plain object.__setattr__,
which skips Pydantic's validate_assignment (and therefore the
_freeze_levels model validator). The defense-in-depth check in
DetailLevelMap.__setattr__ then detects a non-frozen dict and
re-freezes it via line 157.
"""
def plain_setattr(self, name, value):
object.__setattr__(self, name, value)
with patch.object(BaseModel, "__setattr__", plain_setattr):
# This calls DetailLevelMap.__setattr__ which calls the mocked
# super().__setattr__ (plain assignment), then the defense check.
context.dlm.levels = {"X": 1, "Y": 2}
@then("levels should still be a MappingProxyType after the defense-in-depth re-freeze")
def step_verify_refreeze(context):
"""Verify that line 157 executed and re-froze the levels."""
assert isinstance(context.dlm.levels, MappingProxyType), (
f"Expected MappingProxyType, got {type(context.dlm.levels)}"
)
assert dict(context.dlm.levels) == {"X": 1, "Y": 2}