rework tests
This commit is contained in:
parent
2a471988cc
commit
1c6e4a2b9d
@ -105,13 +105,9 @@ class TestReadFile:
|
|||||||
assert fm.read_file(sub) == ""
|
assert fm.read_file(sub) == ""
|
||||||
|
|
||||||
def test_file_outside_workspace_returns_empty_string(self, fm, tmp_path):
|
def test_file_outside_workspace_returns_empty_string(self, fm, tmp_path):
|
||||||
# tmp_path.parent is a shared pytest temp dir, so we clean up to avoid
|
|
||||||
# leaving stray files that could affect other tests.
|
|
||||||
outside = tmp_path.parent / "outside.py"
|
outside = tmp_path.parent / "outside.py"
|
||||||
outside.write_text("secret")
|
outside.write_text("secret")
|
||||||
result = fm.read_file(outside)
|
assert fm.read_file(outside) == ""
|
||||||
assert result == ""
|
|
||||||
outside.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
# ── save_file ─────────────────────────────────────────────────────────────────
|
# ── save_file ─────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -1,242 +0,0 @@
|
|||||||
"""Tests for frontend/state.py — init_state() session initialisation."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
import pytest
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
||||||
|
|
||||||
from backend.managers.chat_manager import ChatManager
|
|
||||||
|
|
||||||
|
|
||||||
# ── Fake session_state ────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class FakeSessionState:
|
|
||||||
"""Minimal stand-in for st.session_state that supports attribute and 'in' access."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._data = {}
|
|
||||||
|
|
||||||
def __contains__(self, key):
|
|
||||||
return key in self._data
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
if name == "_data":
|
|
||||||
super().__setattr__(name, value)
|
|
||||||
else:
|
|
||||||
self._data[name] = value
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
try:
|
|
||||||
return self._data[name]
|
|
||||||
except KeyError:
|
|
||||||
raise AttributeError(name)
|
|
||||||
|
|
||||||
def get(self, key, default=None):
|
|
||||||
return self._data.get(key, default)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def fake_state():
|
|
||||||
"""Inject a FakeSessionState in place of st.session_state for the duration of a test."""
|
|
||||||
state = FakeSessionState()
|
|
||||||
with patch("frontend.state.st.session_state", state):
|
|
||||||
yield state
|
|
||||||
|
|
||||||
|
|
||||||
# ── Key initialisation ────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class TestInitStateKeys:
|
|
||||||
"""Tests that init_state() creates all required session keys with the correct default values."""
|
|
||||||
|
|
||||||
EXPECTED_KEYS = [
|
|
||||||
"last_selected",
|
|
||||||
"selected_folder",
|
|
||||||
"selected_folder_rel",
|
|
||||||
"chat_manager",
|
|
||||||
"open_files",
|
|
||||||
"files_content",
|
|
||||||
"active_file",
|
|
||||||
"active_tab",
|
|
||||||
"is_editing",
|
|
||||||
"code_suggestions",
|
|
||||||
"code_execution_output",
|
|
||||||
"chat_history",
|
|
||||||
"agent_mode",
|
|
||||||
"coding_agent",
|
|
||||||
"agent_status",
|
|
||||||
"agent_log",
|
|
||||||
"agent_pending_action",
|
|
||||||
]
|
|
||||||
|
|
||||||
def test_all_expected_keys_are_set(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
for key in self.EXPECTED_KEYS:
|
|
||||||
assert key in fake_state, f"Missing key: {key}"
|
|
||||||
|
|
||||||
def test_open_files_initialised_as_empty_list(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
assert fake_state.open_files == []
|
|
||||||
|
|
||||||
def test_files_content_initialised_as_empty_dict(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
assert fake_state.files_content == {}
|
|
||||||
|
|
||||||
def test_active_file_initialised_as_none(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
assert fake_state.active_file is None
|
|
||||||
|
|
||||||
def test_chat_history_initialised_as_empty_list(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
assert fake_state.chat_history == []
|
|
||||||
|
|
||||||
def test_agent_mode_initialised_as_false(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
assert fake_state.agent_mode is False
|
|
||||||
|
|
||||||
def test_agent_status_initialised_as_idle(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
assert fake_state.agent_status == "idle"
|
|
||||||
|
|
||||||
def test_chat_manager_is_chat_manager_instance(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
assert isinstance(fake_state.chat_manager, ChatManager)
|
|
||||||
|
|
||||||
|
|
||||||
# ── Idempotency ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class TestInitStateIdempotency:
|
|
||||||
"""Tests that calling init_state() a second time never resets keys that were already set.
|
|
||||||
This mirrors Streamlit's own pattern: initialise only when the key is absent."""
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_open_files(self, fake_state):
|
|
||||||
"""init_state() must not reset state that was set by the user."""
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.open_files = ["/workspace/file.py"]
|
|
||||||
init_state()
|
|
||||||
assert fake_state.open_files == ["/workspace/file.py"]
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_chat_history(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.chat_history = [{"role": "user", "content": "Hi"}]
|
|
||||||
init_state()
|
|
||||||
assert len(fake_state.chat_history) == 1
|
|
||||||
|
|
||||||
def test_second_call_does_not_replace_chat_manager(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
original = fake_state.chat_manager
|
|
||||||
init_state()
|
|
||||||
assert fake_state.chat_manager is original
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_active_file(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.active_file = "/workspace/main.py"
|
|
||||||
init_state()
|
|
||||||
assert fake_state.active_file == "/workspace/main.py"
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_last_selected(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.last_selected = "src/app.py"
|
|
||||||
init_state()
|
|
||||||
assert fake_state.last_selected == "src/app.py"
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_selected_folder(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.selected_folder = "/workspace/src"
|
|
||||||
init_state()
|
|
||||||
assert fake_state.selected_folder == "/workspace/src"
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_selected_folder_rel(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.selected_folder_rel = "src"
|
|
||||||
init_state()
|
|
||||||
assert fake_state.selected_folder_rel == "src"
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_files_content(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.files_content = {"/workspace/main.py": "x = 1"}
|
|
||||||
init_state()
|
|
||||||
assert fake_state.files_content == {"/workspace/main.py": "x = 1"}
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_active_tab(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.active_tab = 2
|
|
||||||
init_state()
|
|
||||||
assert fake_state.active_tab == 2
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_is_editing(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.is_editing = True
|
|
||||||
init_state()
|
|
||||||
assert fake_state.is_editing is True
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_code_suggestions(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.code_suggestions = ["use f-strings"]
|
|
||||||
init_state()
|
|
||||||
assert fake_state.code_suggestions == ["use f-strings"]
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_code_execution_output(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.code_execution_output = "previous output"
|
|
||||||
init_state()
|
|
||||||
assert fake_state.code_execution_output == "previous output"
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_agent_mode(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.agent_mode = True
|
|
||||||
init_state()
|
|
||||||
assert fake_state.agent_mode is True
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_coding_agent(self, fake_state):
|
|
||||||
# object() is used as a sentinel: a unique instance that cannot accidentally
|
|
||||||
# equal any value init_state() might write, so identity check is unambiguous.
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
sentinel = object()
|
|
||||||
fake_state.coding_agent = sentinel
|
|
||||||
init_state()
|
|
||||||
assert fake_state.coding_agent is sentinel
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_agent_status(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.agent_status = "waiting_approval"
|
|
||||||
init_state()
|
|
||||||
assert fake_state.agent_status == "waiting_approval"
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_agent_log(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.agent_log = [{"step": 1, "tool": "write_file"}]
|
|
||||||
init_state()
|
|
||||||
assert len(fake_state.agent_log) == 1
|
|
||||||
|
|
||||||
def test_second_call_does_not_overwrite_agent_pending_action(self, fake_state):
|
|
||||||
from frontend.state import init_state
|
|
||||||
init_state()
|
|
||||||
fake_state.agent_pending_action = {"tool": "write_file", "arguments": {}}
|
|
||||||
init_state()
|
|
||||||
assert fake_state.agent_pending_action is not None
|
|
||||||
@ -53,11 +53,6 @@ class TestSystemPrompterWithFileContext:
|
|||||||
prompt = SystemPrompter.generate_prompt(file_context={"name": "f.py", "content": "pass"})
|
prompt = SystemPrompter.generate_prompt(file_context={"name": "f.py", "content": "pass"})
|
||||||
assert "<code>" in prompt
|
assert "<code>" in prompt
|
||||||
|
|
||||||
def test_with_context_is_longer_than_base(self):
|
|
||||||
base = SystemPrompter.generate_prompt()
|
|
||||||
with_ctx = SystemPrompter.generate_prompt(file_context={"name": "f.py", "content": "x=1"})
|
|
||||||
assert len(with_ctx) > len(base)
|
|
||||||
|
|
||||||
def test_missing_name_key_uses_unknown(self):
|
def test_missing_name_key_uses_unknown(self):
|
||||||
prompt = SystemPrompter.generate_prompt(file_context={"content": "some code"})
|
prompt = SystemPrompter.generate_prompt(file_context={"content": "some code"})
|
||||||
assert "unknown" in prompt
|
assert "unknown" in prompt
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user