v2.5.0 (#495)
Co-authored-by: Quentin Torroba <quentin.torroba@mistral.ai> Co-authored-by: Clement Sirieix <clem.sirieix@gmail.com> Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai> Co-authored-by: Simon Van de Kerckhove <simon.vandekerckhove@mistral.ai> Co-authored-by: Vincent Guilloux <vincent.guilloux@mistral.ai> Co-authored-by: Michel Thomazo <michel.thomazo@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
9421fbc08e
commit
5103019b01
104 changed files with 7277 additions and 691 deletions
|
|
@ -237,6 +237,236 @@ class TestUserAgentsDirs:
|
|||
assert mgr.user_agents_dirs == [agents_dir]
|
||||
|
||||
|
||||
class TestLoadProjectDocs:
|
||||
def test_returns_empty_when_project_not_in_sources(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
mgr = HarnessFilesManager(sources=("user",))
|
||||
assert mgr.load_project_docs() == []
|
||||
|
||||
def test_returns_empty_when_not_trusted(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: False)
|
||||
(tmp_path / "AGENTS.md").write_text("# Hello", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
assert mgr.load_project_docs() == []
|
||||
|
||||
def test_returns_single_doc_when_trust_root_is_cwd(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
monkeypatch.setattr(
|
||||
trusted_folders_manager, "find_trust_root", lambda _: tmp_path.resolve()
|
||||
)
|
||||
(tmp_path / "AGENTS.md").write_text("# Root doc", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
docs = mgr.load_project_docs()
|
||||
assert len(docs) == 1
|
||||
assert docs[0] == (tmp_path.resolve(), "# Root doc")
|
||||
|
||||
def test_walks_up_to_trust_root(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
child = tmp_path / "sub" / "deep"
|
||||
child.mkdir(parents=True)
|
||||
monkeypatch.chdir(child)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
monkeypatch.setattr(
|
||||
trusted_folders_manager, "find_trust_root", lambda _: tmp_path.resolve()
|
||||
)
|
||||
(tmp_path / "AGENTS.md").write_text("# Root", encoding="utf-8")
|
||||
(child / "AGENTS.md").write_text("# Child", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
docs = mgr.load_project_docs()
|
||||
# outermost first
|
||||
assert docs[0] == (tmp_path.resolve(), "# Root")
|
||||
assert docs[-1] == (child.resolve(), "# Child")
|
||||
|
||||
def test_skips_dirs_without_agents_md(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
child = tmp_path / "sub" / "deep"
|
||||
child.mkdir(parents=True)
|
||||
monkeypatch.chdir(child)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
monkeypatch.setattr(
|
||||
trusted_folders_manager, "find_trust_root", lambda _: tmp_path.resolve()
|
||||
)
|
||||
# Only root has AGENTS.md, intermediate "sub" does not
|
||||
(tmp_path / "AGENTS.md").write_text("# Root", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
docs = mgr.load_project_docs()
|
||||
assert len(docs) == 1
|
||||
assert docs[0] == (tmp_path.resolve(), "# Root")
|
||||
|
||||
def test_stops_at_trust_root_boundary(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
trust_root = tmp_path / "root"
|
||||
child = trust_root / "sub"
|
||||
child.mkdir(parents=True)
|
||||
monkeypatch.chdir(child)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
monkeypatch.setattr(
|
||||
trusted_folders_manager, "find_trust_root", lambda _: trust_root.resolve()
|
||||
)
|
||||
# Place AGENTS.md above trust root — should NOT be loaded
|
||||
(tmp_path / "AGENTS.md").write_text("# Above root", encoding="utf-8")
|
||||
(trust_root / "AGENTS.md").write_text("# At root", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
docs = mgr.load_project_docs()
|
||||
assert len(docs) == 1
|
||||
assert docs[0][0] == trust_root.resolve()
|
||||
|
||||
def test_returns_empty_when_trust_root_not_ancestor(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
child = tmp_path / "project" / "sub"
|
||||
child.mkdir(parents=True)
|
||||
trust_root = tmp_path / "other-root"
|
||||
trust_root.mkdir()
|
||||
monkeypatch.chdir(child)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
monkeypatch.setattr(
|
||||
trusted_folders_manager, "find_trust_root", lambda _: trust_root.resolve()
|
||||
)
|
||||
(tmp_path / "AGENTS.md").write_text("# Outside", encoding="utf-8")
|
||||
(child / "AGENTS.md").write_text("# Child", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
assert mgr.load_project_docs() == []
|
||||
|
||||
def test_ignores_empty_agents_md(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
monkeypatch.setattr(
|
||||
trusted_folders_manager, "find_trust_root", lambda _: tmp_path.resolve()
|
||||
)
|
||||
(tmp_path / "AGENTS.md").write_text(" \n ", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
assert mgr.load_project_docs() == []
|
||||
|
||||
|
||||
class TestFindSubdirectoryAgentsMd:
|
||||
def test_returns_empty_when_project_not_in_sources(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
sub = tmp_path / "sub"
|
||||
sub.mkdir()
|
||||
(sub / "AGENTS.md").write_text("# Sub", encoding="utf-8")
|
||||
target = sub / "file.py"
|
||||
target.write_text("", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user",))
|
||||
assert mgr.find_subdirectory_agents_md(target) == []
|
||||
|
||||
def test_returns_empty_when_not_trusted(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "find_trust_root", lambda _: None)
|
||||
sub = tmp_path / "sub"
|
||||
sub.mkdir()
|
||||
(sub / "AGENTS.md").write_text("# Sub", encoding="utf-8")
|
||||
target = sub / "file.py"
|
||||
target.write_text("", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
assert mgr.find_subdirectory_agents_md(target) == []
|
||||
|
||||
def test_returns_empty_when_file_outside_cwd(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
cwd = tmp_path / "project"
|
||||
cwd.mkdir()
|
||||
monkeypatch.chdir(cwd)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
outside = tmp_path / "other" / "file.py"
|
||||
outside.parent.mkdir(parents=True)
|
||||
outside.write_text("", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
assert mgr.find_subdirectory_agents_md(outside) == []
|
||||
|
||||
def test_returns_empty_when_file_directly_in_cwd(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
(tmp_path / "AGENTS.md").write_text("# Root", encoding="utf-8")
|
||||
target = tmp_path / "file.py"
|
||||
target.write_text("", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
# cwd-level AGENTS.md is handled by load_project_docs, not this method
|
||||
assert mgr.find_subdirectory_agents_md(target) == []
|
||||
|
||||
def test_returns_agents_md_from_file_parent(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
sub = tmp_path / "sub"
|
||||
sub.mkdir()
|
||||
(sub / "AGENTS.md").write_text("# Sub instructions", encoding="utf-8")
|
||||
target = sub / "file.py"
|
||||
target.write_text("", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
docs = mgr.find_subdirectory_agents_md(target)
|
||||
assert len(docs) == 1
|
||||
assert docs[0] == (sub.resolve(), "# Sub instructions")
|
||||
|
||||
def test_returns_multiple_agents_md_outermost_first(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
outer = tmp_path / "a"
|
||||
inner = outer / "b"
|
||||
inner.mkdir(parents=True)
|
||||
(outer / "AGENTS.md").write_text("# Outer", encoding="utf-8")
|
||||
(inner / "AGENTS.md").write_text("# Inner", encoding="utf-8")
|
||||
target = inner / "file.py"
|
||||
target.write_text("", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
docs = mgr.find_subdirectory_agents_md(target)
|
||||
assert len(docs) == 2
|
||||
assert docs[0] == (outer.resolve(), "# Outer")
|
||||
assert docs[1] == (inner.resolve(), "# Inner")
|
||||
|
||||
def test_skips_dirs_without_agents_md(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
outer = tmp_path / "a"
|
||||
inner = outer / "b"
|
||||
inner.mkdir(parents=True)
|
||||
# Only inner has AGENTS.md
|
||||
(inner / "AGENTS.md").write_text("# Inner", encoding="utf-8")
|
||||
target = inner / "file.py"
|
||||
target.write_text("", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
docs = mgr.find_subdirectory_agents_md(target)
|
||||
assert len(docs) == 1
|
||||
assert docs[0] == (inner.resolve(), "# Inner")
|
||||
|
||||
def test_ignores_empty_agents_md(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
sub = tmp_path / "sub"
|
||||
sub.mkdir()
|
||||
(sub / "AGENTS.md").write_text(" \n ", encoding="utf-8")
|
||||
target = sub / "file.py"
|
||||
target.write_text("", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
assert mgr.find_subdirectory_agents_md(target) == []
|
||||
|
||||
|
||||
class TestProjectSkillsDirs:
|
||||
def test_returns_empty_list_when_no_skills_dirs(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
|
|
@ -332,3 +562,33 @@ class TestProjectSkillsDirs:
|
|||
(tmp_path / "node_modules" / ".vibe" / "skills").mkdir(parents=True)
|
||||
mgr = HarnessFilesManager(sources=("user", "project"))
|
||||
assert mgr.project_skills_dirs == [tmp_path / ".vibe" / "skills"]
|
||||
|
||||
|
||||
class TestLoadUserDoc:
|
||||
def test_returns_empty_when_user_not_in_sources(self) -> None:
|
||||
mgr = HarnessFilesManager(sources=("project",))
|
||||
assert mgr.load_user_doc() == ""
|
||||
|
||||
def test_returns_empty_when_file_does_not_exist(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.setattr("vibe.core.paths._vibe_home._DEFAULT_VIBE_HOME", tmp_path)
|
||||
mgr = HarnessFilesManager(sources=("user",))
|
||||
assert mgr.load_user_doc() == ""
|
||||
|
||||
def test_returns_file_content_when_file_exists(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.setattr("vibe.core.paths._vibe_home._DEFAULT_VIBE_HOME", tmp_path)
|
||||
(tmp_path / "AGENTS.md").write_text("# User instructions", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user",))
|
||||
assert mgr.load_user_doc() == "# User instructions"
|
||||
|
||||
def test_returns_empty_string_for_whitespace_only_file(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
# load_user_doc strips — consistent with _collect_agents_md
|
||||
monkeypatch.setattr("vibe.core.paths._vibe_home._DEFAULT_VIBE_HOME", tmp_path)
|
||||
(tmp_path / "AGENTS.md").write_text(" \n ", encoding="utf-8")
|
||||
mgr = HarnessFilesManager(sources=("user",))
|
||||
assert mgr.load_user_doc() == ""
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import tomllib
|
||||
|
||||
import pytest
|
||||
import tomli_w
|
||||
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from vibe.core.config import ModelConfig
|
||||
from vibe.core.config import ModelConfig, VibeConfig
|
||||
from vibe.core.config.harness_files import (
|
||||
HarnessFilesManager,
|
||||
init_harness_files_manager,
|
||||
|
|
@ -84,6 +86,60 @@ class TestResolveConfigFile:
|
|||
assert mgr.config_file == VIBE_HOME.path / "config.toml"
|
||||
|
||||
|
||||
class TestMigrateRemovesFindFromBashAllowlist:
|
||||
def test_removes_find_from_config_file(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.setenv("VIBE_HOME", str(tmp_path))
|
||||
config_file = tmp_path / "config.toml"
|
||||
data = {"tools": {"bash": {"allowlist": ["echo", "find", "ls"]}}}
|
||||
with config_file.open("wb") as f:
|
||||
tomli_w.dump(data, f)
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user")
|
||||
VibeConfig._migrate()
|
||||
|
||||
with config_file.open("rb") as f:
|
||||
result = tomllib.load(f)
|
||||
assert "find" not in result["tools"]["bash"]["allowlist"]
|
||||
assert result["tools"]["bash"]["allowlist"] == ["echo", "ls"]
|
||||
|
||||
def test_noop_when_find_not_present(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.setenv("VIBE_HOME", str(tmp_path))
|
||||
config_file = tmp_path / "config.toml"
|
||||
data = {"tools": {"bash": {"allowlist": ["echo", "ls"]}}}
|
||||
with config_file.open("wb") as f:
|
||||
tomli_w.dump(data, f)
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user")
|
||||
VibeConfig._migrate()
|
||||
|
||||
with config_file.open("rb") as f:
|
||||
result = tomllib.load(f)
|
||||
assert result["tools"]["bash"]["allowlist"] == ["echo", "ls"]
|
||||
|
||||
def test_noop_when_no_bash_tools_section(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.setenv("VIBE_HOME", str(tmp_path))
|
||||
config_file = tmp_path / "config.toml"
|
||||
data = {"active_model": "test"}
|
||||
with config_file.open("wb") as f:
|
||||
tomli_w.dump(data, f)
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user")
|
||||
VibeConfig._migrate()
|
||||
|
||||
with config_file.open("rb") as f:
|
||||
result = tomllib.load(f)
|
||||
assert "tools" not in result
|
||||
|
||||
|
||||
class TestAutoCompactThresholdFallback:
|
||||
def test_model_without_explicit_threshold_inherits_global(self) -> None:
|
||||
model = ModelConfig(name="m", provider="p", alias="m")
|
||||
|
|
@ -119,3 +175,52 @@ class TestAutoCompactThresholdFallback:
|
|||
auto_compact_threshold=75_000, models=[model], active_model="m"
|
||||
)
|
||||
assert cfg2.get_active_model().auto_compact_threshold == 75_000
|
||||
|
||||
|
||||
class TestCompactionModel:
|
||||
def test_get_compaction_model_returns_active_when_unset(self) -> None:
|
||||
cfg = build_test_vibe_config()
|
||||
assert cfg.get_compaction_model() == cfg.get_active_model()
|
||||
|
||||
def test_get_compaction_model_returns_configured_model(self) -> None:
|
||||
compaction = ModelConfig(
|
||||
name="compact-model", provider="mistral", alias="compact"
|
||||
)
|
||||
cfg = build_test_vibe_config(compaction_model=compaction)
|
||||
assert cfg.get_compaction_model().name == "compact-model"
|
||||
|
||||
def test_compaction_model_provider_must_match_active(self) -> None:
|
||||
from vibe.core.config import ProviderConfig
|
||||
|
||||
compaction = ModelConfig(
|
||||
name="compact-model", provider="other", alias="compact"
|
||||
)
|
||||
providers = [
|
||||
ProviderConfig(
|
||||
name="mistral",
|
||||
api_base="https://api.mistral.ai/v1",
|
||||
api_key_env_var="MISTRAL_API_KEY",
|
||||
),
|
||||
ProviderConfig(
|
||||
name="other",
|
||||
api_base="https://other.ai/v1",
|
||||
api_key_env_var="MISTRAL_API_KEY",
|
||||
),
|
||||
]
|
||||
with pytest.raises(ValueError, match="must share the same provider"):
|
||||
build_test_vibe_config(compaction_model=compaction, providers=providers)
|
||||
|
||||
def test_compaction_model_provider_must_exist(self) -> None:
|
||||
compaction = ModelConfig(
|
||||
name="compact-model", provider="missing-provider", alias="compact"
|
||||
)
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="Provider 'missing-provider' for model 'compact-model' not found in configuration",
|
||||
):
|
||||
build_test_vibe_config(compaction_model=compaction)
|
||||
|
||||
def test_compaction_model_excluded_from_model_dump_when_none(self) -> None:
|
||||
cfg = build_test_vibe_config()
|
||||
dumped = cfg.model_dump()
|
||||
assert "compaction_model" not in dumped
|
||||
|
|
|
|||
115
tests/core/test_transcribe_config.py
Normal file
115
tests/core/test_transcribe_config.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from vibe.core.config import (
|
||||
DEFAULT_TRANSCRIBE_MODELS,
|
||||
DEFAULT_TRANSCRIBE_PROVIDERS,
|
||||
TranscribeClient,
|
||||
TranscribeModelConfig,
|
||||
TranscribeProviderConfig,
|
||||
)
|
||||
|
||||
|
||||
class TestTranscribeConfigDefaults:
|
||||
def test_default_transcribe_providers_loaded(self) -> None:
|
||||
config = build_test_vibe_config()
|
||||
assert len(config.transcribe_providers) == len(DEFAULT_TRANSCRIBE_PROVIDERS)
|
||||
assert config.transcribe_providers[0].name == "mistral"
|
||||
assert config.transcribe_providers[0].api_base == "wss://api.mistral.ai"
|
||||
|
||||
def test_default_transcribe_models_loaded(self) -> None:
|
||||
config = build_test_vibe_config()
|
||||
assert len(config.transcribe_models) == len(DEFAULT_TRANSCRIBE_MODELS)
|
||||
assert config.transcribe_models[0].alias == "voxtral-realtime"
|
||||
assert (
|
||||
config.transcribe_models[0].name == "voxtral-mini-transcribe-realtime-2602"
|
||||
)
|
||||
|
||||
def test_default_active_transcribe_model(self) -> None:
|
||||
config = build_test_vibe_config()
|
||||
assert config.active_transcribe_model == "voxtral-realtime"
|
||||
|
||||
|
||||
class TestGetActiveTranscribeModel:
|
||||
def test_resolves_by_alias(self) -> None:
|
||||
config = build_test_vibe_config()
|
||||
model = config.get_active_transcribe_model()
|
||||
assert model.alias == "voxtral-realtime"
|
||||
assert model.name == "voxtral-mini-transcribe-realtime-2602"
|
||||
|
||||
def test_raises_for_unknown_alias(self) -> None:
|
||||
config = build_test_vibe_config(active_transcribe_model="nonexistent")
|
||||
with pytest.raises(ValueError, match="not found in configuration"):
|
||||
config.get_active_transcribe_model()
|
||||
|
||||
|
||||
class TestGetTranscribeProviderForModel:
|
||||
def test_resolves_by_name(self) -> None:
|
||||
config = build_test_vibe_config()
|
||||
model = config.get_active_transcribe_model()
|
||||
provider = config.get_transcribe_provider_for_model(model)
|
||||
assert provider.name == "mistral"
|
||||
assert provider.api_base == "wss://api.mistral.ai"
|
||||
|
||||
def test_raises_for_unknown_provider(self) -> None:
|
||||
config = build_test_vibe_config(
|
||||
transcribe_models=[
|
||||
TranscribeModelConfig(
|
||||
name="test-model", provider="nonexistent", alias="test"
|
||||
)
|
||||
],
|
||||
active_transcribe_model="test",
|
||||
)
|
||||
model = config.get_active_transcribe_model()
|
||||
with pytest.raises(ValueError, match="not found in configuration"):
|
||||
config.get_transcribe_provider_for_model(model)
|
||||
|
||||
|
||||
class TestTranscribeModelUniqueness:
|
||||
def test_duplicate_aliases_raise(self) -> None:
|
||||
with pytest.raises(ValueError, match="Duplicate transcribe model alias"):
|
||||
build_test_vibe_config(
|
||||
transcribe_models=[
|
||||
TranscribeModelConfig(
|
||||
name="model-a", provider="mistral", alias="same-alias"
|
||||
),
|
||||
TranscribeModelConfig(
|
||||
name="model-b", provider="mistral", alias="same-alias"
|
||||
),
|
||||
],
|
||||
active_transcribe_model="same-alias",
|
||||
)
|
||||
|
||||
|
||||
class TestTranscribeModelConfig:
|
||||
def test_alias_defaults_to_name(self) -> None:
|
||||
model = TranscribeModelConfig.model_validate({
|
||||
"name": "my-model",
|
||||
"provider": "mistral",
|
||||
})
|
||||
assert model.alias == "my-model"
|
||||
|
||||
def test_explicit_alias(self) -> None:
|
||||
model = TranscribeModelConfig(
|
||||
name="my-model", provider="mistral", alias="custom-alias"
|
||||
)
|
||||
assert model.alias == "custom-alias"
|
||||
|
||||
def test_default_values(self) -> None:
|
||||
model = TranscribeModelConfig(
|
||||
name="my-model", provider="mistral", alias="my-model"
|
||||
)
|
||||
assert model.sample_rate == 16000
|
||||
assert model.encoding == "pcm_s16le"
|
||||
assert model.language == "en"
|
||||
assert model.target_streaming_delay_ms == 500
|
||||
|
||||
|
||||
class TestTranscribeProviderConfig:
|
||||
def test_default_values(self) -> None:
|
||||
provider = TranscribeProviderConfig(name="test")
|
||||
assert provider.api_base == "wss://api.mistral.ai"
|
||||
assert provider.api_key_env_var == ""
|
||||
assert provider.client == TranscribeClient.MISTRAL
|
||||
|
|
@ -7,7 +7,7 @@ from unittest.mock import patch
|
|||
import pytest
|
||||
import tomli_w
|
||||
|
||||
from vibe.core.paths import AGENTS_MD_FILENAMES, TRUSTED_FOLDERS_FILE
|
||||
from vibe.core.paths import AGENTS_MD_FILENAME, TRUSTED_FOLDERS_FILE
|
||||
from vibe.core.trusted_folders import (
|
||||
TrustedFoldersManager,
|
||||
has_agents_md_file,
|
||||
|
|
@ -209,6 +209,99 @@ class TestTrustedFoldersManager:
|
|||
assert manager.is_trusted(tmp_path) is True
|
||||
|
||||
|
||||
class TestIsTrustedInheritance:
|
||||
"""Tests for the walk-up trust inheritance behaviour."""
|
||||
|
||||
def test_child_of_trusted_folder_returns_true(self, tmp_path: Path) -> None:
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_trusted(tmp_path)
|
||||
child = tmp_path / "sub" / "deep"
|
||||
child.mkdir(parents=True)
|
||||
assert manager.is_trusted(child) is True
|
||||
|
||||
def test_child_of_untrusted_folder_returns_false(self, tmp_path: Path) -> None:
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_untrusted(tmp_path)
|
||||
child = tmp_path / "sub"
|
||||
child.mkdir()
|
||||
assert manager.is_trusted(child) is False
|
||||
|
||||
def test_most_specific_ancestor_wins(self, tmp_path: Path) -> None:
|
||||
parent = tmp_path / "parent"
|
||||
child = parent / "child"
|
||||
child.mkdir(parents=True)
|
||||
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_trusted(parent)
|
||||
manager.add_untrusted(child)
|
||||
|
||||
assert manager.is_trusted(parent) is True
|
||||
assert manager.is_trusted(child) is False
|
||||
assert manager.is_trusted(child / "grandchild") is False
|
||||
|
||||
def test_untrusted_parent_trusted_child(self, tmp_path: Path) -> None:
|
||||
parent = tmp_path / "parent"
|
||||
child = parent / "child"
|
||||
child.mkdir(parents=True)
|
||||
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_untrusted(parent)
|
||||
manager.add_trusted(child)
|
||||
|
||||
assert manager.is_trusted(parent) is False
|
||||
assert manager.is_trusted(child) is True
|
||||
assert manager.is_trusted(child / "grandchild") is True
|
||||
|
||||
def test_deep_nesting_inherits_trust(self, tmp_path: Path) -> None:
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_trusted(tmp_path)
|
||||
deep = tmp_path / "a" / "b" / "c" / "d"
|
||||
deep.mkdir(parents=True)
|
||||
assert manager.is_trusted(deep) is True
|
||||
|
||||
def test_no_match_returns_none(self, tmp_path: Path) -> None:
|
||||
manager = TrustedFoldersManager()
|
||||
assert manager.is_trusted(tmp_path / "unknown") is None
|
||||
|
||||
|
||||
class TestFindTrustRoot:
|
||||
def test_returns_path_when_path_is_trusted(self, tmp_path: Path) -> None:
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_trusted(tmp_path)
|
||||
assert manager.find_trust_root(tmp_path) == tmp_path.resolve()
|
||||
|
||||
def test_returns_ancestor_when_child(self, tmp_path: Path) -> None:
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_trusted(tmp_path)
|
||||
child = tmp_path / "sub" / "deep"
|
||||
child.mkdir(parents=True)
|
||||
assert manager.find_trust_root(child) == tmp_path.resolve()
|
||||
|
||||
def test_returns_none_when_no_trusted_ancestor(self, tmp_path: Path) -> None:
|
||||
manager = TrustedFoldersManager()
|
||||
assert manager.find_trust_root(tmp_path) is None
|
||||
|
||||
def test_returns_closest_trusted_ancestor(self, tmp_path: Path) -> None:
|
||||
parent = tmp_path / "parent"
|
||||
child = parent / "child"
|
||||
child.mkdir(parents=True)
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_trusted(tmp_path)
|
||||
manager.add_trusted(parent)
|
||||
# child should find parent (closest), not tmp_path
|
||||
assert manager.find_trust_root(child) == parent.resolve()
|
||||
|
||||
def test_ignores_untrusted_ancestors(self, tmp_path: Path) -> None:
|
||||
parent = tmp_path / "parent"
|
||||
child = parent / "child"
|
||||
child.mkdir(parents=True)
|
||||
manager = TrustedFoldersManager()
|
||||
manager.add_untrusted(parent)
|
||||
manager.add_trusted(tmp_path)
|
||||
# find_trust_root skips untrusted, finds tmp_path
|
||||
assert manager.find_trust_root(child) == tmp_path.resolve()
|
||||
|
||||
|
||||
class TestHasAgentsMdFile:
|
||||
def test_returns_false_for_empty_directory(self, tmp_path: Path) -> None:
|
||||
assert has_agents_md_file(tmp_path) is False
|
||||
|
|
@ -217,21 +310,13 @@ class TestHasAgentsMdFile:
|
|||
(tmp_path / "AGENTS.md").write_text("# Agents", encoding="utf-8")
|
||||
assert has_agents_md_file(tmp_path) is True
|
||||
|
||||
def test_returns_true_when_vibe_md_exists(self, tmp_path: Path) -> None:
|
||||
(tmp_path / "VIBE.md").write_text("# Vibe", encoding="utf-8")
|
||||
assert has_agents_md_file(tmp_path) is True
|
||||
|
||||
def test_returns_true_when_dot_vibe_md_exists(self, tmp_path: Path) -> None:
|
||||
(tmp_path / ".vibe.md").write_text("# Vibe", encoding="utf-8")
|
||||
assert has_agents_md_file(tmp_path) is True
|
||||
|
||||
def test_returns_false_when_only_other_files_exist(self, tmp_path: Path) -> None:
|
||||
(tmp_path / "README.md").write_text("", encoding="utf-8")
|
||||
(tmp_path / ".vibe").mkdir()
|
||||
assert has_agents_md_file(tmp_path) is False
|
||||
|
||||
def test_agents_md_filenames_constant(self) -> None:
|
||||
assert AGENTS_MD_FILENAMES == ["AGENTS.md", "VIBE.md", ".vibe.md"]
|
||||
def test_agents_md_filename_constant(self) -> None:
|
||||
assert AGENTS_MD_FILENAME == "AGENTS.md"
|
||||
|
||||
|
||||
class TestHasTrustableContent:
|
||||
|
|
@ -244,10 +329,9 @@ class TestHasTrustableContent:
|
|||
assert has_trustable_content(tmp_path) is True
|
||||
|
||||
def test_returns_true_when_agents_md_filename_exists(self, tmp_path: Path) -> None:
|
||||
for name in AGENTS_MD_FILENAMES:
|
||||
(tmp_path / name).write_text("", encoding="utf-8")
|
||||
assert has_trustable_content(tmp_path) is True
|
||||
(tmp_path / name).unlink()
|
||||
(tmp_path / AGENTS_MD_FILENAME).write_text("", encoding="utf-8")
|
||||
assert has_trustable_content(tmp_path) is True
|
||||
(tmp_path / AGENTS_MD_FILENAME).unlink()
|
||||
|
||||
def test_returns_false_when_no_trustable_content(self, tmp_path: Path) -> None:
|
||||
(tmp_path / "other.txt").write_text("", encoding="utf-8")
|
||||
|
|
|
|||
136
tests/core/tools/builtins/test_read_file.py
Normal file
136
tests/core/tools/builtins/test_read_file.py
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterator
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from vibe.core.config.harness_files import (
|
||||
init_harness_files_manager,
|
||||
reset_harness_files_manager,
|
||||
)
|
||||
from vibe.core.tools.builtins.read_file import (
|
||||
ReadFile,
|
||||
ReadFileResult,
|
||||
ReadFileState,
|
||||
ReadFileToolConfig,
|
||||
)
|
||||
from vibe.core.trusted_folders import trusted_folders_manager
|
||||
from vibe.core.utils import VIBE_WARNING_TAG
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def _setup_manager(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Iterator[None]:
|
||||
"""Initialize harness files manager for tests, reset after."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: True)
|
||||
monkeypatch.setattr(
|
||||
trusted_folders_manager, "find_trust_root", lambda _: tmp_path.resolve()
|
||||
)
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project")
|
||||
yield
|
||||
reset_harness_files_manager()
|
||||
|
||||
|
||||
def _make_read_file() -> ReadFile:
|
||||
return ReadFile(config=ReadFileToolConfig(), state=ReadFileState())
|
||||
|
||||
|
||||
class TestGetResultExtra:
|
||||
@pytest.mark.usefixtures("_setup_manager")
|
||||
def test_returns_none_when_no_agents_md(self, tmp_path: Path) -> None:
|
||||
sub = tmp_path / "sub"
|
||||
sub.mkdir()
|
||||
target = sub / "file.py"
|
||||
target.write_text("hello", encoding="utf-8")
|
||||
|
||||
tool = _make_read_file()
|
||||
result = ReadFileResult(
|
||||
path=str(target), content="hello", lines_read=1, was_truncated=False
|
||||
)
|
||||
assert tool.get_result_extra(result) is None
|
||||
|
||||
@pytest.mark.usefixtures("_setup_manager")
|
||||
def test_returns_tagged_content_when_agents_md_found(self, tmp_path: Path) -> None:
|
||||
sub = tmp_path / "sub"
|
||||
sub.mkdir()
|
||||
(sub / "AGENTS.md").write_text("# Sub instructions", encoding="utf-8")
|
||||
target = sub / "file.py"
|
||||
target.write_text("hello", encoding="utf-8")
|
||||
|
||||
tool = _make_read_file()
|
||||
result = ReadFileResult(
|
||||
path=str(target), content="hello", lines_read=1, was_truncated=False
|
||||
)
|
||||
annotation = tool.get_result_extra(result)
|
||||
assert annotation is not None
|
||||
assert f"<{VIBE_WARNING_TAG}>" in annotation
|
||||
assert f"</{VIBE_WARNING_TAG}>" in annotation
|
||||
assert "# Sub instructions" in annotation
|
||||
assert "project instructions for this directory" in annotation
|
||||
|
||||
@pytest.mark.usefixtures("_setup_manager")
|
||||
def test_deduplicates_across_calls(self, tmp_path: Path) -> None:
|
||||
sub = tmp_path / "sub"
|
||||
sub.mkdir()
|
||||
(sub / "AGENTS.md").write_text("# Sub", encoding="utf-8")
|
||||
file1 = sub / "a.py"
|
||||
file2 = sub / "b.py"
|
||||
file1.write_text("a", encoding="utf-8")
|
||||
file2.write_text("b", encoding="utf-8")
|
||||
|
||||
tool = _make_read_file()
|
||||
|
||||
result1 = ReadFileResult(
|
||||
path=str(file1), content="a", lines_read=1, was_truncated=False
|
||||
)
|
||||
assert tool.get_result_extra(result1) is not None
|
||||
|
||||
# Second call for a different file in the same dir → no duplicate
|
||||
result2 = ReadFileResult(
|
||||
path=str(file2), content="b", lines_read=1, was_truncated=False
|
||||
)
|
||||
assert tool.get_result_extra(result2) is None
|
||||
|
||||
@pytest.mark.usefixtures("_setup_manager")
|
||||
def test_injects_new_dir_after_dedup(self, tmp_path: Path) -> None:
|
||||
sub_a = tmp_path / "a"
|
||||
sub_b = tmp_path / "b"
|
||||
sub_a.mkdir()
|
||||
sub_b.mkdir()
|
||||
(sub_a / "AGENTS.md").write_text("# A", encoding="utf-8")
|
||||
(sub_b / "AGENTS.md").write_text("# B", encoding="utf-8")
|
||||
file_a = sub_a / "f.py"
|
||||
file_b = sub_b / "f.py"
|
||||
file_a.write_text("", encoding="utf-8")
|
||||
file_b.write_text("", encoding="utf-8")
|
||||
|
||||
tool = _make_read_file()
|
||||
|
||||
r1 = ReadFileResult(
|
||||
path=str(file_a), content="", lines_read=0, was_truncated=False
|
||||
)
|
||||
ann1 = tool.get_result_extra(r1)
|
||||
assert ann1 is not None
|
||||
assert "# A" in ann1
|
||||
|
||||
# Different subdirectory → should inject its AGENTS.md
|
||||
r2 = ReadFileResult(
|
||||
path=str(file_b), content="", lines_read=0, was_truncated=False
|
||||
)
|
||||
ann2 = tool.get_result_extra(r2)
|
||||
assert ann2 is not None
|
||||
assert "# B" in ann2
|
||||
|
||||
def test_returns_none_when_manager_not_initialized(self, tmp_path: Path) -> None:
|
||||
reset_harness_files_manager()
|
||||
tool = _make_read_file()
|
||||
result = ReadFileResult(
|
||||
path=str(tmp_path / "file.py"),
|
||||
content="",
|
||||
lines_read=0,
|
||||
was_truncated=False,
|
||||
)
|
||||
assert tool.get_result_extra(result) is None
|
||||
reset_harness_files_manager()
|
||||
Loading…
Add table
Add a link
Reference in a new issue