Files
temp/features/steps/context_service_uncovered_lines_steps.py
freemo 4ad51561fc perf(tests): optimize medium-slow BDD features (10-100s tier)
Cap time.sleep and asyncio.sleep globally at 10ms in before_all to
eliminate retry/backoff waits (tenacity wait_fixed, retry_auto_debug
exponential backoff). Save originals as time._original_sleep and
asyncio._original_sleep for tests that need real wall-clock delays
(CircuitBreaker recovery, debounce timers, validation timeouts).

Enhance template-DB patch: add "db." prefix to _SCENARIO_DB_PREFIXES
and check db_path.stat().st_size > 0 so 0-byte auto-created SQLite
files receive the template copy instead of falling through to real
migrations.

Replace subprocess.run CLI invocations with typer.testing.CliRunner
in module_coverage, main_coverage_complete, and coverage_extras step
files, eliminating ~6s Python cold-start overhead per call.

Switch plan_persistence and action_persistence to in-memory SQLite by
default; only cross-restart scenarios use file-based via an explicit
Given step.

Replace MigrationRunner.run_migrations with Base.metadata.create_all
in plan_service_steps for freshly created in-memory engines.

Removed leftover debug comment in environment.py.

Total tier runtime: 565s -> 21s (96% reduction), 20 features all
under 5s behave-internal time.

ISSUES CLOSED: #480
2026-03-02 02:01:27 +00:00

167 lines
5.6 KiB
Python

"""Behave steps to cover uncovered branches in context_service."""
import os
import tempfile
from pathlib import Path
from behave import given, then, when
from cleveragents.application.services.context_service import ContextService
from cleveragents.application.services.plan_service import PlanService
from cleveragents.application.services.project_service import ProjectService
from cleveragents.config.settings import Settings
from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
from features.mocks.mock_ai_provider import MockAIProvider
@given("a context service coverage workspace")
def step_context_service_workspace(context):
temp_dir = Path(tempfile.mkdtemp())
context.temp_dir = temp_dir
os.chdir(temp_dir)
(temp_dir / ".cleveragents").mkdir(exist_ok=True)
settings = Settings()
db_path = temp_dir / "test_coverage.db"
unit_of_work = UnitOfWork(f"sqlite:///{db_path}")
unit_of_work.init_database()
project_service = ProjectService(settings, unit_of_work)
project = project_service.initialize_project(
name="coverage-project", path=temp_dir, force=True
)
plan_service = PlanService(settings, unit_of_work, ai_provider=MockAIProvider())
plan_service.create_plan(project=project, prompt="coverage plan")
context.settings = settings
context.unit_of_work = unit_of_work
context.project = project
context.context_service = ContextService(settings, unit_of_work)
@when("I try to add a default ignored file")
def step_add_default_ignored_file(context):
ignored_dir = context.temp_dir / "__pycache__"
ignored_dir.mkdir(exist_ok=True)
ignored_file = ignored_dir / "ignored.pyc"
ignored_file.write_text("ignored cache")
context.added_files, context.already_in_context = (
context.context_service.add_to_context(
project=context.project, path=ignored_file
)
)
@then("the file is skipped without context entries")
def step_verify_default_ignored_skip(context):
assert context.added_files == []
assert context.already_in_context == []
assert context.context_service.list_context(context.project) == []
@given("a .agentsignore file with directory and anchored rules")
def step_create_agentsignore(context):
ignore_file = context.temp_dir / ".agentsignore"
ignore_file.write_text("# comment line\nignored_dir/\n/anchored.txt\n")
@when("I check ignore status for matching files")
def step_check_agentsignore_matches(context):
dir_path = context.temp_dir / "ignored_dir"
dir_path.mkdir(exist_ok=True)
dir_file = dir_path / "nested.txt"
dir_file.write_text("nested")
anchored_file = context.temp_dir / "anchored.txt"
anchored_file.write_text("anchored")
context.dir_ignored = context.context_service._should_ignore(
context.project, dir_file
)
context.anchored_ignored = context.context_service._should_ignore(
context.project, anchored_file
)
@then("the directory rule and anchored rule are both applied")
def step_verify_agentsignore_rules(context):
assert context.dir_ignored is True
assert context.anchored_ignored is True
@given("project include paths are set to allow only matching files")
def step_set_include_paths(context):
context.project.settings.include_paths = ["allowed/**"]
@given("project exclude paths block temporary files")
def step_set_exclude_paths(context):
context.project.settings.exclude_paths = ["*.tmp"]
@when("I check ignore status for included and excluded files")
def step_check_include_exclude(context):
allowed_dir = context.temp_dir / "allowed"
allowed_dir.mkdir(exist_ok=True)
included_file = allowed_dir / "keep.py"
included_file.write_text("keep")
excluded_file = context.temp_dir / "notes.tmp"
excluded_file.write_text("skip")
outside_include = context.temp_dir / "other" / "skip.py"
outside_include.parent.mkdir(parents=True, exist_ok=True)
outside_include.write_text("skip")
context.included_ignored = context.context_service._should_ignore(
context.project, included_file
)
context.excluded_ignored = context.context_service._should_ignore(
context.project, excluded_file
)
context.outside_include_ignored = context.context_service._should_ignore(
context.project, outside_include
)
@then("excluded files are ignored and included files allowed")
def step_verify_include_exclude(context):
assert context.excluded_ignored is True
assert context.included_ignored is False
@then("files outside the include globs are ignored")
def step_verify_include_guardrail(context):
assert context.outside_include_ignored is True
@then("paths outside the project are treated as non matches")
def step_verify_project_path_mismatch(context):
external_path = context.temp_dir.parent / "external.txt"
external_path.write_text("outside")
assert (
context.context_service._project_path_matches(
context.project, external_path, "*.txt"
)
is False
)
@when("I run ignore matching against an unrelated base directory")
def step_non_relative_match(context):
unrelated_base = context.temp_dir / "unrelated"
unrelated_base.mkdir(exist_ok=True)
target_file = context.temp_dir / "nested" / "file.txt"
target_file.parent.mkdir(parents=True, exist_ok=True)
target_file.write_text("data")
context.non_relative_match = context.context_service._matches_ignore(
unrelated_base, "*.txt", target_file
)
@then("non-relative paths are treated as not ignored")
def step_verify_non_relative(context):
assert context.non_relative_match is False