band-aider-pub/tests/band_aider/test_file_salience_check.py
2026-10-17 11:57:37 -07:00

113 lines
3.8 KiB
Python

# SPDX-FileCopyrightText: 2026 Art Sea LLC
#
# SPDX-License-Identifier: AGPL-3.0-only
from __future__ import annotations
from datetime import datetime
from pathlib import Path
from band_aider.context.class_meta import ClassMeta
from band_aider.judgement.file_salience_check import (
FastFileSalienceCheck,
FileSalienceCheck,
FileSalienceCheckFactory,
FastFileSalienceCheckFactory,
LegacyFileSalienceCheck,
)
from band_aider.judgement.prompt_formatter import MarkdownPromptFormatterBuilder
from tests.support.completion_clients import StaticOpenAICompatibleClient
from tests.support.memory_repo_exec import MemoryRepoExec
class _StationaryFastFileSalienceCheck(FastFileSalienceCheck):
"""Exercise fast selection with fixed semantic scores and no model lookup."""
def _score(self, task: str, candidate: ClassMeta) -> float:
"""Score auth work above cache work for the fixed test task."""
del task
return 1.0 if candidate.location == "src/auth.py" else 0.0
class _UnavailableModelFileSalienceCheck(FastFileSalienceCheck):
"""Exercise the real legacy fallback after a deterministic local-model miss."""
def _score(self, task: str, candidate: ClassMeta) -> float:
"""Report the stationary unavailable-model condition without filesystem access."""
del task, candidate
raise FileNotFoundError("test model is intentionally unavailable")
def _meta(location: str, summary: str, *, relevance=None, symbols=None) -> ClassMeta:
return ClassMeta(
location=location,
summary=summary,
relevance=list(relevance or []),
relevant_symbols=dict(symbols or {}),
cdc=[(datetime(2026, 7, 14), "dev", summary)],
)
def test_file_salience_aliases_fast_implementation() -> None:
assert FileSalienceCheck is FastFileSalienceCheck
assert FileSalienceCheckFactory is FastFileSalienceCheckFactory
def test_fast_file_salience_prefers_file_most_similar_to_current_cot_task() -> None:
check = _StationaryFastFileSalienceCheck(
changes=[],
prompt="global goal",
repo_root=Path("/repo"),
repo_exec=MemoryRepoExec(),
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
)
first = _meta(
"src/auth.py",
"auth login flow",
relevance=["fix login auth failure"],
symbols={"login_user": "handles auth"},
)
second = _meta(
"src/cache.py",
"cache invalidation",
relevance=["cache cleanup"],
symbols={"flush_cache": "handles cache"},
)
assert check("fix login auth bug", first, second) is False
def test_fast_file_salience_falls_back_to_legacy_when_model2vec_unavailable() -> None:
check = _UnavailableModelFileSalienceCheck(
changes=[],
prompt="global goal",
repo_root=Path("/repo"),
repo_exec=MemoryRepoExec(),
client=StaticOpenAICompatibleClient('{"prefer_second": true, "reason": "The second file owns the task."}'),
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
)
first = _meta("src/a.py", "auth")
second = _meta("src/b.py", "cache")
assert check("task", first, second) is True
def test_legacy_file_salience_reads_repo_relative_file() -> None:
adapter = MemoryRepoExec()
repo_root = Path("/testbed")
path = repo_root / "band_aider" / "ai_client" / "foo.py"
adapter.write_text(path, "def foo():\n return 1\n")
check = LegacyFileSalienceCheck(
changes=[],
prompt="goal",
client=StaticOpenAICompatibleClient('{"prefer_second": false, "reason": "The first file owns the task."}'),
repo_root=repo_root,
repo_exec=adapter,
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
)
assert "def foo" in check._read_file("band_aider/ai_client/foo.py")