Files
cleveragents-core/features/steps/lsp_discovery_coverage_steps.py
T
freemo 31472b5413
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 21s
CI / helm (pull_request) Successful in 22s
CI / lint (pull_request) Successful in 3m20s
CI / quality (pull_request) Successful in 3m43s
CI / typecheck (pull_request) Successful in 3m58s
CI / security (pull_request) Successful in 4m8s
CI / integration_tests (pull_request) Successful in 9m33s
CI / unit_tests (pull_request) Successful in 10m12s
CI / docker (pull_request) Successful in 1m28s
CI / coverage (pull_request) Successful in 12m18s
CI / e2e_tests (pull_request) Successful in 19m51s
CI / status-check (pull_request) Successful in 1s
CI / build (push) Successful in 15s
CI / helm (push) Successful in 22s
CI / lint (push) Successful in 3m18s
CI / quality (push) Successful in 3m41s
CI / typecheck (push) Successful in 3m57s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 4m10s
CI / unit_tests (push) Failing after 6m58s
CI / docker (push) Has been skipped
CI / integration_tests (push) Successful in 9m15s
CI / coverage (push) Successful in 12m26s
CI / e2e_tests (push) Successful in 23m11s
CI / status-check (push) Failing after 1s
CI / benchmark-publish (push) Successful in 28m31s
CI / benchmark-regression (pull_request) Successful in 54m53s
test(coverage): add Behave scenarios for 39 under-covered modules
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate.

ISSUES CLOSED: #1232
2026-03-31 21:47:12 +00:00

216 lines
8.3 KiB
Python

"""Step definitions for lsp_discovery_coverage.feature.
Targets uncovered lines in src/cleveragents/lsp/discovery.py:
- Line 142: _detect_from_shebang returns None (no shebang prefix)
- Line 146: _detect_from_shebang returns None (empty after #!)
- Line 157: _detect_from_shebang returns None (unknown interpreter)
- Line 201: detect_file_language cache hit
- Lines 219-231: detect_directory_languages body + OSError branch
- Line 234: return sorted(languages) from detect_directory_languages
- Line 246: invalidate_all clears cache
- Line 257: Makefile basename detection
- Line 259: CMakeLists.txt basename detection
- Line 276: UKO classification layer
- Lines 298-301: get_servers_for_language list comprehension
"""
from __future__ import annotations
from types import SimpleNamespace
from unittest.mock import patch
from behave import given, then, when
from behave.runner import Context
from cleveragents.lsp.discovery import LanguageDiscovery, _detect_from_shebang
# ── helpers ──────────────────────────────────────────────────────────
def _make_config(languages: list[str]) -> SimpleNamespace:
"""Create a lightweight object with a ``languages`` attribute."""
return SimpleNamespace(languages=languages)
# ── shebang givens ───────────────────────────────────────────────────
@given('ldcov a first line "{line}"')
def step_ldcov_given_first_line(context: Context, line: str) -> None:
context.ldcov_first_line = line
# ── shebang whens ────────────────────────────────────────────────────
@when("ldcov I detect from shebang")
def step_ldcov_when_detect_shebang(context: Context) -> None:
context.ldcov_shebang_result = _detect_from_shebang(context.ldcov_first_line)
# ── shebang thens ────────────────────────────────────────────────────
@then("ldcov the shebang result is None")
def step_ldcov_then_shebang_none(context: Context) -> None:
assert context.ldcov_shebang_result is None, (
f"Expected None, got {context.ldcov_shebang_result!r}"
)
# ── LanguageDiscovery instance ───────────────────────────────────────
@given("ldcov a LanguageDiscovery instance")
def step_ldcov_given_discovery(context: Context) -> None:
context.ldcov_discovery = LanguageDiscovery()
@given('ldcov a LanguageDiscovery instance with UKO mapping "{path}" to "{lang}"')
def step_ldcov_given_discovery_with_uko(context: Context, path: str, lang: str) -> None:
context.ldcov_discovery = LanguageDiscovery(uko_classifications={path: lang})
# ── cache manipulation ───────────────────────────────────────────────
@given('ldcov the cache already contains "{path}" mapped to "{lang}"')
def step_ldcov_given_cache_entry(context: Context, path: str, lang: str) -> None:
context.ldcov_discovery._cache[path] = lang
# ── detect_file_language ─────────────────────────────────────────────
@when('ldcov I detect file language for "{path}"')
def step_ldcov_when_detect_file(context: Context, path: str) -> None:
context.ldcov_result = context.ldcov_discovery.detect_file_language(path)
# ── detect via _detect_uncached (avoids real file I/O for shebang) ──
@when('ldcov I detect uncached language for "{path}"')
def step_ldcov_when_detect_uncached(context: Context, path: str) -> None:
# Mock open so Layer-2 (shebang) doesn't try to read a real file.
patcher = patch("builtins.open", side_effect=OSError("mocked"))
patcher.start()
context.add_cleanup(patcher.stop)
context.ldcov_result = context.ldcov_discovery._detect_uncached(path)
# ── invalidate_all ───────────────────────────────────────────────────
@when("ldcov I call invalidate_all")
def step_ldcov_when_invalidate_all(context: Context) -> None:
context.ldcov_discovery.invalidate_all()
@then("ldcov the cache is empty")
def step_ldcov_then_cache_empty(context: Context) -> None:
assert len(context.ldcov_discovery._cache) == 0, (
f"Cache still has {len(context.ldcov_discovery._cache)} entries"
)
# ── detect_directory_languages (happy path) ──────────────────────────
@given('ldcov a mock directory tree with files "{file_list}"')
def step_ldcov_given_mock_dir(context: Context, file_list: str) -> None:
filenames = [f.strip() for f in file_list.split(",")]
mock_root = "/fake/project"
context.ldcov_mock_dir = mock_root
# Mock os.walk to yield our fake file list
walk_result = [(mock_root, [], filenames)]
patcher_walk = patch(
"cleveragents.lsp.discovery.os.walk", return_value=iter(walk_result)
)
patcher_walk.start()
context.add_cleanup(patcher_walk.stop)
# Mock open so shebang analysis doesn't hit the real filesystem.
patcher_open = patch("builtins.open", side_effect=OSError("mocked"))
patcher_open.start()
context.add_cleanup(patcher_open.stop)
@when("ldcov I detect directory languages")
def step_ldcov_when_detect_dir(context: Context) -> None:
context.ldcov_dir_result = context.ldcov_discovery.detect_directory_languages(
context.ldcov_mock_dir,
)
@then('ldcov the directory languages include "{lang}"')
def step_ldcov_then_dir_includes(context: Context, lang: str) -> None:
assert lang in context.ldcov_dir_result, (
f"Expected {lang!r} in {context.ldcov_dir_result}"
)
# ── detect_directory_languages (OSError branch) ─────────────────────
@given("ldcov os.walk will raise an OSError")
def step_ldcov_given_walk_oserror(context: Context) -> None:
patcher = patch(
"cleveragents.lsp.discovery.os.walk",
side_effect=OSError("permission denied"),
)
patcher.start()
context.add_cleanup(patcher.stop)
@when('ldcov I detect directory languages for "{directory}"')
def step_ldcov_when_detect_dir_for(context: Context, directory: str) -> None:
context.ldcov_dir_result = context.ldcov_discovery.detect_directory_languages(
directory,
)
@then("ldcov the directory languages result is an empty list")
def step_ldcov_then_dir_empty(context: Context) -> None:
assert context.ldcov_dir_result == [], (
f"Expected empty list, got {context.ldcov_dir_result!r}"
)
# ── get_servers_for_language ─────────────────────────────────────────
@given('ldcov server configs with languages "{langs_a}" and "{langs_b}"')
def step_ldcov_given_server_configs(
context: Context, langs_a: str, langs_b: str
) -> None:
config_a = _make_config([lang.strip() for lang in langs_a.split(",")])
config_b = _make_config([lang.strip() for lang in langs_b.split(",")])
context.ldcov_configs = [config_a, config_b]
@when('ldcov I get servers for language "{lang}"')
def step_ldcov_when_get_servers(context: Context, lang: str) -> None:
context.ldcov_matched = context.ldcov_discovery.get_servers_for_language(
lang,
context.ldcov_configs,
)
@then("ldcov the matched server count is {count:d}")
def step_ldcov_then_matched_count(context: Context, count: int) -> None:
assert len(context.ldcov_matched) == count, (
f"Expected {count} matched configs, got {len(context.ldcov_matched)}"
)
# ── generic language assertion ───────────────────────────────────────
@then('ldcov the detected language is "{expected}"')
def step_ldcov_then_detected_lang(context: Context, expected: str) -> None:
assert context.ldcov_result == expected, (
f"Expected {expected!r}, got {context.ldcov_result!r}"
)