Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: allansimon-mistral <allan.simon@ext.mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
145 lines
4.6 KiB
Python
145 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from vibe.cli.history_manager import HistoryManager
|
|
|
|
|
|
def test_history_manager_normalizes_loaded_entries_like_numbers_to_strings(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
# ideally, we would not use real I/O; but this test is a quick bugfix, thus it
|
|
# does not intend to refactor the HistoryManager
|
|
history_file = tmp_path / "history.jsonl"
|
|
history_entries = ["hello", 123]
|
|
history_file.write_text(
|
|
"\n".join(json.dumps(entry) for entry in history_entries) + "\n",
|
|
encoding="utf-8",
|
|
)
|
|
manager = HistoryManager(history_file)
|
|
|
|
result = manager.get_previous(current_input="")
|
|
|
|
assert result == "123"
|
|
|
|
|
|
def test_history_manager_retains_a_fixed_number_of_entries(tmp_path: Path) -> None:
|
|
history_file = tmp_path / "history.jsonl"
|
|
manager = HistoryManager(history_file, max_entries=3)
|
|
|
|
manager.add("first")
|
|
manager.add("second")
|
|
manager.add("third")
|
|
manager.add("fourth")
|
|
|
|
reloaded = HistoryManager(history_file)
|
|
|
|
assert reloaded.get_previous(current_input="") == "fourth"
|
|
assert reloaded.get_previous(current_input="") == "third"
|
|
assert reloaded.get_previous(current_input="") == "second"
|
|
# "first" is not proposed as we defined number of entries to 3
|
|
assert reloaded.get_previous(current_input="") is None
|
|
|
|
|
|
def test_history_manager_filters_invalid_and_duplicated_entries(tmp_path: Path) -> None:
|
|
history_file = tmp_path / "history.jsonl"
|
|
manager = HistoryManager(history_file, max_entries=5)
|
|
manager.add("") # empty
|
|
manager.add(" ") # is trimmed
|
|
manager.add("first")
|
|
manager.add("second")
|
|
manager.add("second") # duplicate
|
|
manager.add("third")
|
|
|
|
reloaded = HistoryManager(history_file)
|
|
|
|
assert reloaded.get_previous(current_input="") == "third"
|
|
assert reloaded.get_previous(current_input="") == "second"
|
|
assert reloaded.get_previous(current_input="") == "first"
|
|
assert reloaded.get_previous(current_input="") is None
|
|
assert reloaded.get_previous(current_input="") is None
|
|
|
|
|
|
def test_history_manager_stores_slash_prefixed_entries(tmp_path: Path) -> None:
|
|
history_file = tmp_path / "history.jsonl"
|
|
manager = HistoryManager(history_file, max_entries=5)
|
|
manager.add("first")
|
|
manager.add("/tool_call arg1 arg2")
|
|
|
|
reloaded = HistoryManager(history_file)
|
|
|
|
assert reloaded.get_previous(current_input="") == "/tool_call arg1 arg2"
|
|
assert reloaded.get_previous(current_input="") == "first"
|
|
assert reloaded.get_previous(current_input="") is None
|
|
|
|
|
|
def test_history_manager_keeps_entries_when_reload_read_fails(
|
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
history_file = tmp_path / "history.jsonl"
|
|
manager = HistoryManager(history_file)
|
|
manager.add("first")
|
|
|
|
def raise_os_error(*args: object, **kwargs: object) -> None:
|
|
raise OSError
|
|
|
|
monkeypatch.setattr("vibe.cli.history_manager.read_safe", raise_os_error)
|
|
|
|
manager.add("second")
|
|
|
|
assert manager.get_previous(current_input="") == "second"
|
|
assert manager.get_previous(current_input="") == "first"
|
|
|
|
|
|
def test_history_manager_resets_navigation_when_add_is_duplicate(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
history_file = tmp_path / "history.jsonl"
|
|
manager = HistoryManager(history_file, max_entries=2)
|
|
manager.add("a")
|
|
manager.add("b")
|
|
|
|
other = HistoryManager(history_file, max_entries=10)
|
|
other.add("c")
|
|
other.add("d")
|
|
other.add("e")
|
|
|
|
assert manager.get_previous(current_input="") == "b"
|
|
manager.add("e")
|
|
|
|
assert manager.get_previous(current_input="") == "e"
|
|
assert manager.get_previous(current_input="") == "d"
|
|
assert manager.get_previous(current_input="") is None
|
|
|
|
|
|
def test_history_manager_allows_navigation_round_trip(tmp_path: Path) -> None:
|
|
history_file = tmp_path / "history.jsonl"
|
|
manager = HistoryManager(history_file)
|
|
|
|
manager.add("alpha")
|
|
manager.add("beta")
|
|
|
|
assert manager.get_previous(current_input="typed") == "beta"
|
|
assert manager.get_previous(current_input="typed") == "alpha"
|
|
assert manager.get_next() == "beta"
|
|
assert manager.get_next() == "typed"
|
|
assert manager.get_next() is None
|
|
|
|
|
|
def test_history_manager_preserves_original_draft_during_navigation(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
history_file = tmp_path / "history.jsonl"
|
|
manager = HistoryManager(history_file)
|
|
|
|
manager.add("foo")
|
|
manager.add("bar")
|
|
manager.add("fizz")
|
|
|
|
assert manager.get_previous(current_input="draft") == "fizz"
|
|
assert manager.get_previous(current_input="overwritten draft") == "bar"
|
|
assert manager.get_next() == "fizz"
|
|
assert manager.get_next() == "draft"
|