forked from HAL9000/cleveragents-core
051ee7c290
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
169 lines
6.8 KiB
Python
169 lines
6.8 KiB
Python
"""Step definitions for tui_slash_command_overlay_coverage.feature.
|
|
|
|
These steps target uncovered lines in slash_command_overlay.py:
|
|
- Line 16: _FallbackStatic.__init__ sets self._text = ""
|
|
- Line 19: _FallbackStatic.update sets self._text = text
|
|
- Lines 31-32: set_commands filtering logic (query vs empty query)
|
|
- Line 34: lines = [f"/{query}"]
|
|
- Lines 35-36: loop appending filtered commands
|
|
- Lines 37-38: "(no commands)" fallback when nothing matches
|
|
- Line 39: self.update("\n".join(lines))
|
|
"""
|
|
|
|
import importlib
|
|
from unittest.mock import patch
|
|
|
|
from behave import given, then, when
|
|
|
|
import cleveragents.tui.widgets.slash_command_overlay as _sco_mod
|
|
from cleveragents.tui.widgets.slash_command_overlay import _load_static_base
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Background — force FallbackStatic so tests never need a live Textual app
|
|
# ---------------------------------------------------------------------------
|
|
@given("the slash command overlay module is imported")
|
|
def step_module_imported(context):
|
|
"""Reload the module with textual import patched out to force fallback."""
|
|
with patch("importlib.import_module", side_effect=ImportError("no textual")):
|
|
importlib.reload(_sco_mod)
|
|
# Re-bind to module-level names after reload
|
|
context._sco_overlay_cls = _sco_mod.SlashCommandOverlay
|
|
assert context._sco_overlay_cls is not None
|
|
assert _load_static_base is not None
|
|
|
|
def _restore():
|
|
importlib.reload(_sco_mod)
|
|
|
|
context.add_cleanup(_restore)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Instantiation (line 16)
|
|
# ---------------------------------------------------------------------------
|
|
@when("I create a SlashCommandOverlay instance")
|
|
def step_create_overlay(context):
|
|
"""Instantiate SlashCommandOverlay, hitting _FallbackStatic.__init__."""
|
|
context.overlay = context._sco_overlay_cls()
|
|
|
|
|
|
@then("the overlay internal text should be empty")
|
|
def step_verify_empty_text(context):
|
|
"""Verify _text was initialised to empty string (line 16)."""
|
|
assert context.overlay._text == ""
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# update method (line 19)
|
|
# ---------------------------------------------------------------------------
|
|
@given("I have a SlashCommandOverlay instance")
|
|
def step_have_overlay(context):
|
|
"""Create and store an overlay instance for subsequent steps."""
|
|
context.overlay = context._sco_overlay_cls()
|
|
|
|
|
|
@when('I call update with "{text}"')
|
|
def step_call_update(context, text):
|
|
"""Call update on the overlay, hitting _FallbackStatic.update (line 19)."""
|
|
context.overlay.update(text)
|
|
|
|
|
|
@then('the overlay internal text should be "{expected}"')
|
|
def step_verify_text(context, expected):
|
|
"""Verify _text matches the expected value."""
|
|
assert context.overlay._text == expected, (
|
|
f"Expected '{expected}', got '{context.overlay._text}'"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# set_commands with query (lines 31-36, 39)
|
|
# ---------------------------------------------------------------------------
|
|
@when('I call set_commands with query "{query}" and commands "{commands_csv}"')
|
|
def step_call_set_commands(context, query, commands_csv):
|
|
"""Call set_commands with a query and a CSV list of commands."""
|
|
commands = [c.strip() for c in commands_csv.split(",") if c.strip()]
|
|
context.overlay.set_commands(query, commands)
|
|
|
|
|
|
@when('I call set_commands with empty query and commands "{commands_csv}"')
|
|
def step_call_set_commands_empty_query(context, commands_csv):
|
|
"""Call set_commands with an empty query and a CSV list of commands."""
|
|
commands = [c.strip() for c in commands_csv.split(",") if c.strip()]
|
|
context.overlay.set_commands("", commands)
|
|
|
|
|
|
@then('the overlay text should contain "{substring}"')
|
|
def step_text_contains(context, substring):
|
|
"""Verify the overlay text contains the expected substring."""
|
|
assert substring in context.overlay._text, (
|
|
f"Expected '{substring}' in:\n{context.overlay._text}"
|
|
)
|
|
|
|
|
|
@then('the overlay text should not contain "{substring}"')
|
|
def step_text_not_contains(context, substring):
|
|
"""Verify the overlay text does NOT contain the substring."""
|
|
assert substring not in context.overlay._text, (
|
|
f"Did not expect '{substring}' in:\n{context.overlay._text}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# set_commands with empty query (line 32 else branch)
|
|
# ---------------------------------------------------------------------------
|
|
# Reuses the same step definitions above with query ""
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# set_commands no matches (lines 37-38)
|
|
# ---------------------------------------------------------------------------
|
|
# Reuses step_call_set_commands; the "zzz" query triggers the no-commands path
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# set_commands truncation to 12 entries (line 35 [:12])
|
|
# ---------------------------------------------------------------------------
|
|
@when(
|
|
'I call set_commands with query "" and {count:d} commands prefixed with "{prefix}"'
|
|
)
|
|
def step_call_set_commands_many(context, count, prefix):
|
|
"""Call set_commands with a large number of commands."""
|
|
commands = [f"{prefix}{i}" for i in range(count)]
|
|
context.overlay.set_commands("", commands)
|
|
|
|
|
|
@then("the overlay text should have exactly {expected:d} lines")
|
|
def step_verify_line_count(context, expected):
|
|
"""Verify the number of lines in overlay text."""
|
|
lines = context.overlay._text.split("\n")
|
|
assert len(lines) == expected, (
|
|
f"Expected {expected} lines, got {len(lines)}: {lines}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Force fallback static base (lines 16, 19 via except path)
|
|
# ---------------------------------------------------------------------------
|
|
@when("I force the fallback static base to load")
|
|
def step_force_fallback(context):
|
|
"""Patch importlib.import_module to raise, forcing the fallback path."""
|
|
with patch("importlib.import_module", side_effect=ImportError("no textual")):
|
|
fallback_cls = _load_static_base()
|
|
context.fallback_cls = fallback_cls
|
|
context.fallback_instance = fallback_cls()
|
|
|
|
|
|
@then("the fallback class should be usable as a standalone object")
|
|
def step_verify_fallback_class(context):
|
|
"""Verify the fallback instance has the expected _text attribute."""
|
|
assert hasattr(context.fallback_instance, "_text")
|
|
assert context.fallback_instance._text == ""
|
|
|
|
|
|
@then("calling update on the fallback instance should store the text")
|
|
def step_verify_fallback_update(context):
|
|
"""Verify update stores text on the fallback instance (line 19)."""
|
|
context.fallback_instance.update("test content")
|
|
assert context.fallback_instance._text == "test content"
|