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
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate. ISSUES CLOSED: #1232
216 lines
8.3 KiB
Python
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}"
|
|
)
|