From f91707b5b50bf73a90a4ee4837dfa833c5c71944 Mon Sep 17 00:00:00 2001 From: Livio Meuli Date: Thu, 21 May 2026 12:06:32 +0200 Subject: [PATCH] remove some testcases --- tests/test_chat_manager.py | 92 +---------------------------------- tests/test_coding_agent.py | 64 ------------------------ tests/test_debug_logger.py | 64 ++---------------------- tests/test_file_manager.py | 13 ----- tests/test_system_prompter.py | 54 +------------------- 5 files changed, 8 insertions(+), 279 deletions(-) diff --git a/tests/test_chat_manager.py b/tests/test_chat_manager.py index 91aaa92..009e51e 100644 --- a/tests/test_chat_manager.py +++ b/tests/test_chat_manager.py @@ -24,25 +24,10 @@ def _mock_ok(content="AI reply"): return mock -# ── Initialization ──────────────────────────────────────────────────────────── - -class TestChatManagerInit: - """Verify that a fresh ChatManager starts in a clean, predictable state.""" - - def test_chat_history_starts_empty(self): - cm = ChatManager() - assert cm.chat_history == [] - - def test_api_url_contains_endpoint(self): - cm = ChatManager() - assert isinstance(cm.api_url, str) - assert "/v1/chat/completions" in cm.api_url - - # ── History management ──────────────────────────────────────────────────────── class TestHistory: - """Tests for add_message, get_history, and clear_history.""" + """Tests for add_message and clear_history.""" @pytest.fixture def cm(self): @@ -58,22 +43,11 @@ class TestHistory: assert cm.chat_history[0]["role"] == "user" assert cm.chat_history[1]["role"] == "assistant" - def test_get_history_returns_copy_not_reference(self, cm): - """Mutating the returned list must not corrupt internal history.""" - cm.add_message("user", "Hi") - history = cm.get_history() - assert history == cm.chat_history - assert history is not cm.chat_history - def test_clear_history_empties_list(self, cm): cm.add_message("user", "Hi") cm.clear_history() assert cm.chat_history == [] - def test_clear_history_on_empty_is_safe(self, cm): - cm.clear_history() - assert cm.chat_history == [] - # ── send_message (mocked HTTP) ──────────────────────────────────────────────── @@ -108,12 +82,6 @@ class TestSendMessage: assert payload["messages"][0]["role"] == "system" assert payload["messages"][1]["role"] == "user" - def test_history_grows_by_two_per_call(self, cm): - with patch("requests.post", return_value=_mock_ok()): - cm.send_message("First") - cm.send_message("Second") - assert len(cm.chat_history) == 4 - def test_connection_error_raises_and_adds_error_to_history(self, cm): with patch("requests.post", side_effect=requests.exceptions.ConnectionError("refused")): with pytest.raises(Exception, match="Connection Error"): @@ -141,14 +109,6 @@ class TestSendMessage: with pytest.raises(Exception, match="Invalid API response format"): cm.send_message("Hello") - def test_missing_choices_key_raises(self, cm): - mock = MagicMock() - mock.status_code = 200 - mock.json.return_value = {} - with patch("requests.post", return_value=mock): - with pytest.raises(Exception): - cm.send_message("Hello") - def test_api_key_included_in_header_when_set(self, cm): cm.api_key = "test-key-123" with patch("requests.post", return_value=_mock_ok()) as mock_post: @@ -164,13 +124,6 @@ class TestSendMessage: headers = mock_post.call_args.kwargs["headers"] assert "Authorization" not in headers - def test_api_key_excluded_from_header_when_empty_string(self, cm): - cm.api_key = "" - with patch("requests.post", return_value=_mock_ok()) as mock_post: - cm.send_message("Hello") - headers = mock_post.call_args.kwargs["headers"] - assert "Authorization" not in headers - def test_json_decode_error_raises(self, cm): import json mock = MagicMock() @@ -184,15 +137,12 @@ class TestSendMessage: # ── get_chat_display ────────────────────────────────────────────────────────── class TestGetChatDisplay: - """Tests for get_chat_display: correct shape, ordering, isolation, and role coverage.""" + """Tests for get_chat_display: correct shape and ordering.""" @pytest.fixture def cm(self): return ChatManager() - def test_empty_history_returns_empty_list(self, cm): - assert cm.get_chat_display() == [] - def test_display_has_role_and_content_keys(self, cm): cm.add_message("user", "Hello") entry = cm.get_chat_display()[0] @@ -205,41 +155,3 @@ class TestGetChatDisplay: display = cm.get_chat_display() assert display[0]["role"] == "user" assert display[1]["role"] == "assistant" - - def test_display_returns_copy_not_reference(self, cm): - """Mutating the returned list must not corrupt internal history.""" - cm.add_message("user", "Hi") - display = cm.get_chat_display() - display.clear() - assert len(cm.chat_history) == 1 - - def test_system_messages_included_in_display(self, cm): - cm.add_message("system", "Be helpful.") - assert cm.get_chat_display()[0]["role"] == "system" - - -# ── Integration (skipped when API unreachable) ──────────────────────────────── - -class TestSendMessageIntegration: - """End-to-end tests against the live API. Skipped automatically when the API is unreachable.""" - - @pytest.fixture - def cm(self): - return ChatManager() - - def test_real_api_returns_non_empty_string(self, cm): - try: - response = cm.send_message("Reply with exactly the word PONG.") - assert isinstance(response, str) - assert len(response) > 0 - assert len(cm.chat_history) == 2 - except Exception as e: - pytest.skip(f"API not reachable: {e}") - - def test_real_api_multi_turn_history_grows(self, cm): - try: - cm.send_message("Remember the number 42.") - cm.send_message("What number did I ask you to remember?") - assert len(cm.chat_history) == 4 - except Exception as e: - pytest.skip(f"API not reachable: {e}") diff --git a/tests/test_coding_agent.py b/tests/test_coding_agent.py index e04d3f1..bb69056 100644 --- a/tests/test_coding_agent.py +++ b/tests/test_coding_agent.py @@ -73,10 +73,6 @@ class TestTruncateResult: assert len(result) < len(long) assert "TRUNCATED" in result - def test_exact_limit_not_truncated(self): - text = "a" * MAX_RESULT_LENGTH - assert truncate_result(text) == text - def test_truncated_keeps_start_and_end(self): text = "START" + "x" * MAX_RESULT_LENGTH + "END" result = truncate_result(text) @@ -144,9 +140,6 @@ class TestStripCodeFences: text = "```\nhello\n```" assert _strip_code_fences(text) == "hello" - def test_strips_whitespace(self): - assert _strip_code_fences(" hello ") == "hello" - # ═════════════════════════════════════════════════════════════════════════════ # TestCodingAgentInit @@ -162,11 +155,6 @@ class TestCodingAgentInit: assert agent.is_done is False assert agent.iteration == 0 - def test_api_url_is_set(self): - agent = CodingAgent() - assert agent.api_url.startswith("http://") - assert "/v1/chat/completions" in agent.api_url - def test_start_task_sets_messages(self): agent = CodingAgent() agent.start_task("Write fibonacci.py") @@ -397,55 +385,3 @@ class TestReject: assert not list(tmp_path.glob("*")) -# ═════════════════════════════════════════════════════════════════════════════ -# TestFullLoop (integration – real API, skipped if unreachable) -# ═════════════════════════════════════════════════════════════════════════════ - -class TestFullLoop: - """End-to-end test: agent runs a real task against the live API. - Skipped automatically if the API is not reachable. - """ - - MAX_STEPS = 15 - - async def _run_until_done(self, agent) -> list: - steps = [] - for _ in range(self.MAX_STEPS): - action = await agent.propose_next_action() - result = await agent.approve() - steps.append(result) - if result["is_done"]: - break - return steps - - @pytest.mark.asyncio - async def test_agent_completes_hello_world_task(self, tmp_path): - try: - with patch("backend.agent.coding_agent.WORKSPACE", tmp_path): - agent = CodingAgent() - agent.start_task( - "Write a Python file called hello.py that prints 'Hello World'. " - "Validate it and run it." - ) - steps = await self._run_until_done(agent) - assert agent.is_done, "Agent did not reach done state" - tools_used = [s["tool"] for s in steps] - assert "done" in tools_used - except Exception as e: - pytest.skip(f"API not reachable or environment incomplete: {e}") - - @pytest.mark.asyncio - async def test_agent_creates_file_on_disk(self, tmp_path): - try: - with patch("backend.agent.coding_agent.WORKSPACE", tmp_path): - agent = CodingAgent() - agent.start_task("Write a file called output.txt containing the text 'test passed'.") - await self._run_until_done(agent) - py_files = list(tmp_path.glob("*.txt")) + list(tmp_path.glob("*.py")) - assert len(py_files) > 0, "Agent did not create any file" - except Exception as e: - pytest.skip(f"API not reachable or environment incomplete: {e}") - - -if __name__ == "__main__": - pytest.main([__file__, "-v", "-s"]) diff --git a/tests/test_debug_logger.py b/tests/test_debug_logger.py index 50f7b3f..1ee6eca 100644 --- a/tests/test_debug_logger.py +++ b/tests/test_debug_logger.py @@ -17,19 +17,10 @@ def logger(): return DebugLogger() -# ── Initialization ──────────────────────────────────────────────────────────── - -class TestInit: - """Tests that a fresh DebugLogger starts with an empty log list.""" - - def test_logs_start_empty(self, logger): - assert logger.logs == [] - - # ── log() ───────────────────────────────────────────────────────────────────── class TestLog: - """Tests for log(): each call appends an INFO-level entry with message and timestamp.""" + """Tests for log(): each call appends an INFO-level entry with message.""" def test_log_appends_entry(self, logger): logger.log("started") @@ -43,31 +34,16 @@ class TestLog: logger.log("executing file.py") assert logger.logs[0]["message"] == "executing file.py" - def test_log_adds_timestamp(self, logger): - logger.log("started") - ts = logger.logs[0]["timestamp"] - # HH:MM:SS format — exactly 8 chars with two colons - assert len(ts) == 8 - assert ts[2] == ":" and ts[5] == ":" - # ── log_error() ─────────────────────────────────────────────────────────────── class TestLogError: - """Tests for log_error(): same shape as log() but level is ERROR, not INFO.""" - - def test_log_error_appends_entry(self, logger): - logger.log_error("something broke") - assert len(logger.logs) == 1 + """Tests for log_error(): level is ERROR, not INFO.""" def test_log_error_sets_level_error(self, logger): logger.log_error("something broke") assert logger.logs[0]["level"] == "ERROR" - def test_log_error_stores_message(self, logger): - logger.log_error("exit code 1") - assert logger.logs[0]["message"] == "exit code 1" - def test_log_and_log_error_are_distinct_levels(self, logger): logger.log("info message") logger.log_error("error message") @@ -78,24 +54,18 @@ class TestLogError: # ── get_logs() ──────────────────────────────────────────────────────────────── class TestGetLogs: - """Tests for get_logs(): returns all entries and a copy, not a reference to the internal list.""" + """Tests for get_logs().""" def test_get_logs_returns_all_entries(self, logger): logger.log("first") logger.log_error("second") assert len(logger.get_logs()) == 2 - def test_get_logs_returns_copy_not_reference(self, logger): - logger.log("entry") - logs = logger.get_logs() - assert logs == logger.logs - assert logs is not logger.logs - # ── clear() ─────────────────────────────────────────────────────────────────── class TestClear: - """Tests for clear(): empties the log list and leaves the logger ready for reuse.""" + """Tests for clear().""" def test_clear_removes_all_entries(self, logger): logger.log("first") @@ -103,23 +73,11 @@ class TestClear: logger.clear() assert logger.logs == [] - def test_clear_on_empty_is_safe(self, logger): - logger.clear() - assert logger.logs == [] - - def test_log_after_clear_works(self, logger): - logger.log("before") - logger.clear() - logger.log("after") - assert len(logger.logs) == 1 - assert logger.logs[0]["message"] == "after" - # ── format_debug_output() ───────────────────────────────────────────────────── class TestFormatDebugOutput: - """Tests for format_debug_output(): renders a dict of {rc, stdout, stderr} plus - accumulated log entries into a human-readable string for the UI.""" + """Tests for format_debug_output(): renders rc, stdout, stderr, and log entries.""" def test_success_exit_code_shows_success(self, logger): result = logger.format_debug_output({"rc": 0, "stdout": "", "stderr": ""}) @@ -129,10 +87,6 @@ class TestFormatDebugOutput: result = logger.format_debug_output({"rc": 1, "stdout": "", "stderr": ""}) assert "[FAILED]" in result - def test_exit_code_included_in_output(self, logger): - result = logger.format_debug_output({"rc": 42, "stdout": "", "stderr": ""}) - assert "42" in result - def test_stdout_included_when_present(self, logger): result = logger.format_debug_output({"rc": 0, "stdout": "Hello", "stderr": ""}) assert "Hello" in result @@ -143,10 +97,6 @@ class TestFormatDebugOutput: assert "NameError" in result assert "stderr" in result - def test_no_output_message_when_both_empty(self, logger): - result = logger.format_debug_output({"rc": 0, "stdout": "", "stderr": ""}) - assert "No output produced." in result - def test_log_entries_appended_to_output(self, logger): logger.log("Executing file.py...") logger.log_error("exit code 1") @@ -154,10 +104,6 @@ class TestFormatDebugOutput: assert "Executing file.py..." in result assert "exit code 1" in result - def test_returns_string(self, logger): - result = logger.format_debug_output({"rc": 0, "stdout": "", "stderr": ""}) - assert isinstance(result, str) - def test_missing_keys_do_not_raise(self, logger): # Defensive: format_debug_output uses .get() so missing keys are safe result = logger.format_debug_output({}) diff --git a/tests/test_file_manager.py b/tests/test_file_manager.py index f1e4184..5f9f522 100644 --- a/tests/test_file_manager.py +++ b/tests/test_file_manager.py @@ -99,11 +99,6 @@ class TestReadFile: def test_nonexistent_file_returns_empty_string(self, fm, tmp_path): assert fm.read_file(tmp_path / "ghost.py") == "" - def test_directory_path_returns_empty_string(self, fm, tmp_path): - sub = tmp_path / "subdir" - sub.mkdir() - assert fm.read_file(sub) == "" - def test_file_outside_workspace_returns_empty_string(self, fm, tmp_path): outside = tmp_path.parent / "outside.py" outside.write_text("secret") @@ -195,9 +190,6 @@ class TestDeleteFolder: assert result is True assert not sub.exists() - def test_nonexistent_folder_returns_false(self, fm): - assert fm.delete_folder("ghost") is False - def test_path_traversal_returns_false(self, fm): assert fm.delete_folder("../../") is False @@ -226,8 +218,3 @@ class TestGetFileTree: tree = fm.get_file_tree() assert tree["src"]["app.py"] is None - def test_entries_are_sorted(self, fm, tmp_path): - (tmp_path / "z_file.py").touch() - (tmp_path / "a_file.py").touch() - keys = list(fm.get_file_tree().keys()) - assert keys == sorted(keys) diff --git a/tests/test_system_prompter.py b/tests/test_system_prompter.py index e256d87..b17c69d 100644 --- a/tests/test_system_prompter.py +++ b/tests/test_system_prompter.py @@ -6,33 +6,17 @@ from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) import pytest -from backend.managers.system_prompter import SystemPrompter, MAX_FILE_CHARS # MAX_FILE_CHARS: character limit before content is truncated +from backend.managers.system_prompter import SystemPrompter, MAX_FILE_CHARS class TestSystemPrompterBasePrompt: """Tests for generate_prompt() without file context.""" - def test_returns_non_empty_string(self): - prompt = SystemPrompter.generate_prompt() - assert isinstance(prompt, str) - assert len(prompt) > 0 - - def test_describes_code_assistant(self): - prompt = SystemPrompter.generate_prompt() - assert "code assistant" in prompt.lower() - def test_contains_no_file_xml_tag(self): prompt = SystemPrompter.generate_prompt() assert "" not in prompt - def test_none_equals_no_argument(self): - assert SystemPrompter.generate_prompt(file_context=None) == SystemPrompter.generate_prompt() - - def test_empty_dict_behaves_like_none(self): - # {} is falsy in Python, so the implementation treats it the same as None (no file context). - assert SystemPrompter.generate_prompt(file_context={}) == SystemPrompter.generate_prompt() - class TestSystemPrompterWithFileContext: """Tests for generate_prompt() with file_context provided.""" @@ -53,10 +37,6 @@ class TestSystemPrompterWithFileContext: prompt = SystemPrompter.generate_prompt(file_context={"name": "f.py", "content": "pass"}) assert "" in prompt - def test_missing_name_key_uses_unknown(self): - prompt = SystemPrompter.generate_prompt(file_context={"content": "some code"}) - assert "unknown" in prompt - def test_missing_content_key_does_not_raise(self): prompt = SystemPrompter.generate_prompt(file_context={"name": "empty.py"}) assert "empty.py" in prompt @@ -76,11 +56,6 @@ class TestSystemPrompterTruncation: assert "[truncated]" not in prompt assert content in prompt - def test_file_exactly_at_limit_is_not_truncated(self): - content = "x" * MAX_FILE_CHARS - prompt = SystemPrompter.generate_prompt(file_context={"name": "f.py", "content": content}) - assert "[truncated]" not in prompt - def test_file_one_over_limit_is_truncated(self): content = "x" * (MAX_FILE_CHARS + 1) prompt = SystemPrompter.generate_prompt(file_context={"name": "f.py", "content": content}) @@ -90,24 +65,6 @@ class TestSystemPrompterTruncation: class TestSystemPrompterSpecialCharacters: """Tests that XML special characters in file content are handled without breaking the prompt.""" - def test_less_than_in_content_does_not_break_prompt(self): - prompt = SystemPrompter.generate_prompt( - file_context={"name": "f.py", "content": "if x < 10:"} - ) - assert "if x < 10:" in prompt - - def test_greater_than_in_content_does_not_break_prompt(self): - prompt = SystemPrompter.generate_prompt( - file_context={"name": "f.py", "content": "if x > 0:"} - ) - assert "if x > 0:" in prompt - - def test_ampersand_in_content_does_not_break_prompt(self): - prompt = SystemPrompter.generate_prompt( - file_context={"name": "f.py", "content": "# true & false"} - ) - assert "true & false" in prompt - def test_xml_tags_in_content_are_preserved_literally(self): # User code often contains HTML or XML. The prompt builder must embed it # verbatim — escaping or stripping tags would corrupt the file content. @@ -115,12 +72,3 @@ class TestSystemPrompterSpecialCharacters: file_context={"name": "template.html", "content": "
hello
"} ) assert "
hello
" in prompt - - def test_prompt_still_contains_file_xml_tag_with_special_content(self): - # Verifies that special characters in the content don't corrupt the surrounding - # structural tags ( and ) that the LLM relies on for context. - prompt = SystemPrompter.generate_prompt( - file_context={"name": "f.py", "content": "x < y and a > b"} - ) - assert "" in prompt