Co-authored-by: Carlo <carloantonio.patti@mistral.ai>
Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai>
Co-authored-by: Clement Sirieix <clem.sirieix@gmail.com>
Co-authored-by: Michel Thomazo <michel.thomazo@mistral.ai>
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai>
Co-authored-by: Vincent Guilloux <vincent.guilloux@mistral.ai>
Co-authored-by: Thomas Kenbeek <thomas.kenbeek@mistral.ai>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Quentin 2026-02-27 17:31:58 +01:00 committed by GitHub
parent a560a47ce8
commit 5d2e01a6d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
139 changed files with 7152 additions and 1457 deletions

View file

@ -3,29 +3,33 @@ from __future__ import annotations
from pathlib import Path
from unittest.mock import patch
from vibe.core.paths.config_paths import resolve_local_skills_dirs
from vibe.core.paths.config_paths import (
discover_local_agents_dirs,
discover_local_skills_dirs,
discover_local_tools_dirs,
)
class TestResolveLocalSkillsDirs:
class TestDiscoverLocalSkillsDirs:
def test_returns_empty_list_when_dir_not_trusted(self, tmp_path: Path) -> None:
(tmp_path / ".vibe" / "skills").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = False
assert resolve_local_skills_dirs(tmp_path) == []
assert discover_local_skills_dirs(tmp_path) == []
def test_returns_empty_list_when_trusted_but_no_skills_dirs(
self, tmp_path: Path
) -> None:
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
assert resolve_local_skills_dirs(tmp_path) == []
assert discover_local_skills_dirs(tmp_path) == []
def test_returns_vibe_skills_only_when_only_it_exists(self, tmp_path: Path) -> None:
vibe_skills = tmp_path / ".vibe" / "skills"
vibe_skills.mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = resolve_local_skills_dirs(tmp_path)
result = discover_local_skills_dirs(tmp_path)
assert result == [vibe_skills]
def test_returns_agents_skills_only_when_only_it_exists(
@ -35,7 +39,7 @@ class TestResolveLocalSkillsDirs:
agents_skills.mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = resolve_local_skills_dirs(tmp_path)
result = discover_local_skills_dirs(tmp_path)
assert result == [agents_skills]
def test_returns_both_in_order_when_both_exist(self, tmp_path: Path) -> None:
@ -45,7 +49,7 @@ class TestResolveLocalSkillsDirs:
agents_skills.mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = resolve_local_skills_dirs(tmp_path)
result = discover_local_skills_dirs(tmp_path)
assert result == [vibe_skills, agents_skills]
def test_ignores_vibe_skills_when_file_not_dir(self, tmp_path: Path) -> None:
@ -53,5 +57,128 @@ class TestResolveLocalSkillsDirs:
(tmp_path / ".vibe" / "skills").write_text("", encoding="utf-8")
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = resolve_local_skills_dirs(tmp_path)
result = discover_local_skills_dirs(tmp_path)
assert result == []
def test_finds_skills_dirs_recursively_in_trusted_folder(
self, tmp_path: Path
) -> None:
(tmp_path / ".vibe" / "skills").mkdir(parents=True)
(tmp_path / "sub" / ".agents" / "skills").mkdir(parents=True)
(tmp_path / "sub" / "deep" / ".vibe" / "skills").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_skills_dirs(tmp_path)
assert result == [
tmp_path / ".vibe" / "skills",
tmp_path / "sub" / ".agents" / "skills",
tmp_path / "sub" / "deep" / ".vibe" / "skills",
]
def test_does_not_descend_into_ignored_dirs(self, tmp_path: Path) -> None:
(tmp_path / ".vibe" / "skills").mkdir(parents=True)
(tmp_path / "node_modules" / ".vibe" / "skills").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_skills_dirs(tmp_path)
assert result == [tmp_path / ".vibe" / "skills"]
class TestDiscoverLocalToolsDirs:
def test_returns_empty_list_when_dir_not_trusted(self, tmp_path: Path) -> None:
(tmp_path / ".vibe" / "tools").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = False
assert discover_local_tools_dirs(tmp_path) == []
def test_returns_empty_list_when_trusted_but_no_tools_dir(
self, tmp_path: Path
) -> None:
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
assert discover_local_tools_dirs(tmp_path) == []
def test_returns_tools_dir_when_exists(self, tmp_path: Path) -> None:
vibe_tools = tmp_path / ".vibe" / "tools"
vibe_tools.mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_tools_dirs(tmp_path)
assert result == [vibe_tools]
def test_ignores_tools_when_file_not_dir(self, tmp_path: Path) -> None:
(tmp_path / ".vibe").mkdir()
(tmp_path / ".vibe" / "tools").write_text("", encoding="utf-8")
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_tools_dirs(tmp_path)
assert result == []
def test_finds_tools_dirs_recursively(self, tmp_path: Path) -> None:
(tmp_path / ".vibe" / "tools").mkdir(parents=True)
(tmp_path / "sub" / ".vibe" / "tools").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_tools_dirs(tmp_path)
assert result == [
tmp_path / ".vibe" / "tools",
tmp_path / "sub" / ".vibe" / "tools",
]
def test_does_not_descend_into_ignored_dirs(self, tmp_path: Path) -> None:
(tmp_path / ".vibe" / "tools").mkdir(parents=True)
(tmp_path / ".git" / ".vibe" / "tools").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_tools_dirs(tmp_path)
assert result == [tmp_path / ".vibe" / "tools"]
class TestDiscoverLocalAgentsDirs:
def test_returns_empty_list_when_dir_not_trusted(self, tmp_path: Path) -> None:
(tmp_path / ".vibe" / "agents").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = False
assert discover_local_agents_dirs(tmp_path) == []
def test_returns_empty_list_when_trusted_but_no_agents_dir(
self, tmp_path: Path
) -> None:
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
assert discover_local_agents_dirs(tmp_path) == []
def test_returns_agents_dir_when_exists(self, tmp_path: Path) -> None:
vibe_agents = tmp_path / ".vibe" / "agents"
vibe_agents.mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_agents_dirs(tmp_path)
assert result == [vibe_agents]
def test_ignores_agents_when_file_not_dir(self, tmp_path: Path) -> None:
(tmp_path / ".vibe").mkdir()
(tmp_path / ".vibe" / "agents").write_text("", encoding="utf-8")
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_agents_dirs(tmp_path)
assert result == []
def test_finds_agents_dirs_recursively(self, tmp_path: Path) -> None:
(tmp_path / ".vibe" / "agents").mkdir(parents=True)
(tmp_path / "sub" / "deep" / ".vibe" / "agents").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_agents_dirs(tmp_path)
assert result == [
tmp_path / ".vibe" / "agents",
tmp_path / "sub" / "deep" / ".vibe" / "agents",
]
def test_does_not_descend_into_ignored_dirs(self, tmp_path: Path) -> None:
(tmp_path / ".vibe" / "agents").mkdir(parents=True)
(tmp_path / "__pycache__" / ".vibe" / "agents").mkdir(parents=True)
with patch("vibe.core.paths.config_paths.trusted_folders_manager") as mock_tfm:
mock_tfm.is_trusted.return_value = True
result = discover_local_agents_dirs(tmp_path)
assert result == [tmp_path / ".vibe" / "agents"]

View file

@ -8,7 +8,7 @@ from unittest.mock import MagicMock, patch
import pytest
from vibe.core.utils import StructuredLogFormatter, apply_logging_config
from vibe.core.logger import StructuredLogFormatter, apply_logging_config
@pytest.fixture
@ -19,8 +19,8 @@ def mock_log_dir(tmp_path: Path):
mock_file = MagicMock()
mock_file.path = tmp_path / "vibe.log"
with (
patch("vibe.core.utils.LOG_DIR", mock_dir),
patch("vibe.core.utils.LOG_FILE", mock_file),
patch("vibe.core.logger.LOG_DIR", mock_dir),
patch("vibe.core.logger.LOG_FILE", mock_file),
):
yield tmp_path
@ -222,8 +222,8 @@ class TestApplyLoggingConfig:
mock_file = MagicMock()
mock_file.path = log_dir / "vibe.log"
with (
patch("vibe.core.utils.LOG_DIR", mock_dir),
patch("vibe.core.utils.LOG_FILE", mock_file),
patch("vibe.core.logger.LOG_DIR", mock_dir),
patch("vibe.core.logger.LOG_FILE", mock_file),
):
monkeypatch.setenv("LOG_LEVEL", "DEBUG")
test_logger = logging.getLogger("test_mkdir")

View file

@ -47,7 +47,7 @@ class TestTelemetryClient:
def test_send_telemetry_event_does_nothing_when_api_key_is_none(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
env_key = config.get_provider_for_model(
config.get_active_model()
).api_key_env_var
@ -57,7 +57,7 @@ class TestTelemetryClient:
client._client = MagicMock()
client._client.post = AsyncMock()
client.send_telemetry_event("vibe/test", {})
client.send_telemetry_event("vibe.test", {})
_run_telemetry_tasks()
client._client.post.assert_not_called()
@ -65,7 +65,10 @@ class TestTelemetryClient:
def test_send_telemetry_event_does_nothing_when_disabled(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
config = build_test_vibe_config(disable_telemetry=True)
monkeypatch.setattr(
TelemetryClient, "send_telemetry_event", _original_send_telemetry_event
)
config = build_test_vibe_config(enable_telemetry=False)
env_key = config.get_provider_for_model(
config.get_active_model()
).api_key_env_var
@ -74,7 +77,7 @@ class TestTelemetryClient:
client._client = MagicMock()
client._client.post = AsyncMock()
client.send_telemetry_event("vibe/test", {})
client.send_telemetry_event("vibe.test", {})
_run_telemetry_tasks()
client._client.post.assert_not_called()
@ -86,7 +89,7 @@ class TestTelemetryClient:
monkeypatch.setattr(
TelemetryClient, "send_telemetry_event", _original_send_telemetry_event
)
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
env_key = config.get_provider_for_model(
config.get_active_model()
).api_key_env_var
@ -97,12 +100,12 @@ class TestTelemetryClient:
client._client.post = mock_post
client._client.aclose = AsyncMock()
client.send_telemetry_event("vibe/test_event", {"key": "value"})
client.send_telemetry_event("vibe.test_event", {"key": "value"})
await client.aclose()
mock_post.assert_called_once_with(
DATALAKE_EVENTS_URL,
json={"event": "vibe/test_event", "properties": {"key": "value"}},
json={"event": "vibe.test_event", "properties": {"key": "value"}},
headers={
"Content-Type": "application/json",
"Authorization": "Bearer sk-test",
@ -113,7 +116,7 @@ class TestTelemetryClient:
def test_send_tool_call_finished_payload_shape(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
tool_call = _make_resolved_tool_call("todo", {})
decision = ToolDecision(
@ -129,7 +132,7 @@ class TestTelemetryClient:
assert len(telemetry_events) == 1
event_name = telemetry_events[0]["event_name"]
assert event_name == "vibe/tool_call_finished"
assert event_name == "vibe.tool_call_finished"
properties = telemetry_events[0]["properties"]
assert properties["tool_name"] == "todo"
assert properties["status"] == "success"
@ -142,7 +145,7 @@ class TestTelemetryClient:
def test_send_tool_call_finished_nb_files_created_write_file_new(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
tool_call = _make_resolved_tool_call("write_file", {"overwrite": False})
@ -160,7 +163,7 @@ class TestTelemetryClient:
def test_send_tool_call_finished_nb_files_modified_write_file_overwrite(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
tool_call = _make_resolved_tool_call("write_file", {"overwrite": True})
@ -178,7 +181,7 @@ class TestTelemetryClient:
def test_send_tool_call_finished_decision_none(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
tool_call = _make_resolved_tool_call("todo", {})
@ -195,49 +198,49 @@ class TestTelemetryClient:
def test_send_user_copied_text_payload(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
client.send_user_copied_text("hello world")
assert len(telemetry_events) == 1
assert telemetry_events[0]["event_name"] == "vibe/user_copied_text"
assert telemetry_events[0]["event_name"] == "vibe.user_copied_text"
assert telemetry_events[0]["properties"]["text_length"] == 11
def test_send_user_cancelled_action_payload(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
client.send_user_cancelled_action("interrupt_agent")
assert len(telemetry_events) == 1
assert telemetry_events[0]["event_name"] == "vibe/user_cancelled_action"
assert telemetry_events[0]["event_name"] == "vibe.user_cancelled_action"
assert telemetry_events[0]["properties"]["action"] == "interrupt_agent"
def test_send_auto_compact_triggered_payload(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
client.send_auto_compact_triggered()
assert len(telemetry_events) == 1
assert telemetry_events[0]["event_name"] == "vibe/auto_compact_triggered"
assert telemetry_events[0]["event_name"] == "vibe.auto_compact_triggered"
def test_send_slash_command_used_payload(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
client.send_slash_command_used("help", "builtin")
client.send_slash_command_used("my_skill", "skill")
assert len(telemetry_events) == 2
assert telemetry_events[0]["event_name"] == "vibe/slash_command_used"
assert telemetry_events[0]["event_name"] == "vibe.slash_command_used"
assert telemetry_events[0]["properties"]["command"] == "help"
assert telemetry_events[0]["properties"]["command_type"] == "builtin"
assert telemetry_events[1]["properties"]["command"] == "my_skill"
@ -246,7 +249,7 @@ class TestTelemetryClient:
def test_send_new_session_payload(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(disable_telemetry=False)
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
client.send_new_session(
@ -255,15 +258,17 @@ class TestTelemetryClient:
nb_mcp_servers=1,
nb_models=3,
entrypoint="cli",
terminal_emulator="vscode",
)
assert len(telemetry_events) == 1
event_name = telemetry_events[0]["event_name"]
assert event_name == "vibe/new_session"
assert event_name == "vibe.new_session"
properties = telemetry_events[0]["properties"]
assert properties["has_agents_md"] is True
assert properties["nb_skills"] == 2
assert properties["nb_mcp_servers"] == 1
assert properties["nb_models"] == 3
assert properties["entrypoint"] == "cli"
assert properties["terminal_emulator"] == "vscode"
assert "version" in properties

View file

@ -237,11 +237,11 @@ class TestHasAgentsMdFile:
class TestHasTrustableContent:
def test_returns_true_when_vibe_dir_exists(self, tmp_path: Path) -> None:
(tmp_path / ".vibe").mkdir()
(tmp_path / ".vibe" / "skills").mkdir(parents=True)
assert has_trustable_content(tmp_path) is True
def test_returns_true_when_agents_dir_exists(self, tmp_path: Path) -> None:
(tmp_path / ".agents").mkdir()
(tmp_path / ".agents" / "skills").mkdir(parents=True)
assert has_trustable_content(tmp_path) is True
def test_returns_true_when_agents_md_filename_exists(self, tmp_path: Path) -> None:
@ -253,3 +253,17 @@ class TestHasTrustableContent:
def test_returns_false_when_no_trustable_content(self, tmp_path: Path) -> None:
(tmp_path / "other.txt").write_text("", encoding="utf-8")
assert has_trustable_content(tmp_path) is False
def test_returns_true_when_vibe_config_in_subfolder(self, tmp_path: Path) -> None:
(tmp_path / "sub" / ".vibe" / "skills").mkdir(parents=True)
assert has_trustable_content(tmp_path) is True
def test_returns_true_when_agents_skills_in_subfolder(self, tmp_path: Path) -> None:
(tmp_path / "deep" / "nested" / ".agents" / "skills").mkdir(parents=True)
assert has_trustable_content(tmp_path) is True
def test_returns_false_when_config_only_inside_ignored_dir(
self, tmp_path: Path
) -> None:
(tmp_path / "node_modules" / ".vibe" / "skills").mkdir(parents=True)
assert has_trustable_content(tmp_path) is False