forked from HAL9000/cleveragents-core
4ad51561fc
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
167 lines
5.6 KiB
Python
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
|