diff --git a/tests/test_browser_agent_parsing.py b/tests/test_browser_agent_parsing.py index 40d0eec..7b43bd5 100644 --- a/tests/test_browser_agent_parsing.py +++ b/tests/test_browser_agent_parsing.py @@ -1,20 +1,37 @@ import unittest import os import sys +from unittest.mock import MagicMock + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) # Add project root to Python path + +# Mock heavy dependencies +for mod_name in [ + 'torch', 'transformers', 'kokoro', 'adaptive_classifier', 'text2emotion', + 'ollama', 'openai', 'together', 'IPython', 'IPython.display', + 'playsound3', 'soundfile', 'pyaudio', 'librosa', + 'pypdf', 'langid', 'pypinyin', 'fake_useragent', + 'chromedriver_autoinstaller', 'num2words', 'sentencepiece', 'sacremoses', + 'scipy', 'numpy', 'selenium_stealth', 'undetected_chromedriver', + 'markdownify', +]: + if mod_name not in sys.modules: + sys.modules[mod_name] = MagicMock() + +os.environ.setdefault('WORK_DIR', '/tmp') + from sources.agents.browser_agent import BrowserAgent class TestBrowserAgentParsing(unittest.TestCase): def setUp(self): - # Initialize a basic BrowserAgent instance for testing - self.agent = BrowserAgent( - name="TestAgent", - prompt_path="../prompts/base/browser_agent.txt", - provider=None - ) + self.agent = BrowserAgent.__new__(BrowserAgent) + self.agent.notes = [] + self.agent.navigable_links = [] + self.agent.search_history = [] + self.agent.current_page = "" + self.agent.logger = MagicMock() def test_extract_links(self): - # Test various link formats test_text = """ Check this out: https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of, and www.google.com! Also try https://test.org/about?page=1, hey this one as well bro https://weatherstack.com/documentation/. @@ -28,8 +45,17 @@ class TestBrowserAgentParsing(unittest.TestCase): result = self.agent.extract_links(test_text) self.assertEqual(result, expected) + def test_extract_links_no_links(self): + """Test that text without links returns empty list.""" + result = self.agent.extract_links("No links here at all.") + self.assertEqual(result, []) + + def test_extract_links_single_link(self): + """Test extraction of a single link.""" + result = self.agent.extract_links("Visit https://example.com for details") + self.assertEqual(result, ["https://example.com"]) + def test_extract_form(self): - # Test form extraction test_text = """ Fill this: [username](john) and [password](secret123) Not a form: [random]text @@ -38,8 +64,18 @@ class TestBrowserAgentParsing(unittest.TestCase): result = self.agent.extract_form(test_text) self.assertEqual(result, expected) + def test_extract_form_empty(self): + """Test form extraction with no form inputs.""" + result = self.agent.extract_form("Just regular text here.") + self.assertEqual(result, []) + + def test_extract_form_checkbox(self): + """Test form extraction with checkbox values.""" + text = "[agree](checked) and [newsletter](unchecked)" + result = self.agent.extract_form(text) + self.assertEqual(len(result), 2) + def test_clean_links(self): - # Test link cleaning test_links = [ "https://example.com.", "www.test.com,", @@ -55,8 +91,13 @@ class TestBrowserAgentParsing(unittest.TestCase): result = self.agent.clean_links(test_links) self.assertEqual(result, expected) + def test_clean_links_with_slash(self): + """Test that trailing slash is stripped since it's not alphanumeric.""" + links = ["https://example.com/path/"] + result = self.agent.clean_links(links) + self.assertEqual(result, ["https://example.com/path"]) + def test_parse_answer(self): - # Test parsing answer with notes and links test_text = """ Here's some info Note: This is important. We are doing test it's very cool. @@ -66,5 +107,102 @@ class TestBrowserAgentParsing(unittest.TestCase): self.agent.parse_answer(test_text) self.assertEqual(self.agent.notes[0], "Note: This is important. We are doing test it's very cool.") + def test_parse_answer_extracts_links(self): + """Test that parse_answer returns extracted links.""" + text = "Navigate to https://example.com and https://test.org" + links = self.agent.parse_answer(text) + self.assertIn("https://example.com", links) + self.assertIn("https://test.org", links) + + def test_parse_answer_no_notes(self): + """Test parse_answer with no notes section.""" + text = "Go to https://example.com" + self.agent.parse_answer(text) + # Notes should have an empty entry + self.assertEqual(len(self.agent.notes), 1) + + def test_select_link_unvisited(self): + """Test selecting first unvisited link.""" + self.agent.search_history = ["https://visited.com"] + self.agent.current_page = "https://current.com" + links = ["https://visited.com", "https://current.com", "https://new.com"] + result = self.agent.select_link(links) + self.assertEqual(result, "https://new.com") + + def test_select_link_all_visited(self): + """Test that None is returned when all links are visited.""" + self.agent.search_history = ["https://a.com", "https://b.com"] + self.agent.current_page = "" + links = ["https://a.com", "https://b.com"] + result = self.agent.select_link(links) + self.assertIsNone(result) + + def test_select_link_empty(self): + """Test with empty links list.""" + result = self.agent.select_link([]) + self.assertIsNone(result) + + def test_jsonify_search_results(self): + """Test parsing search result text into structured data.""" + text = """Title: Result One +Snippet: First result snippet +Link: https://one.com + +Title: Result Two +Snippet: Second result snippet +Link: https://two.com""" + results = self.agent.jsonify_search_results(text) + self.assertEqual(len(results), 2) + self.assertEqual(results[0]["title"], "Result One") + self.assertEqual(results[0]["link"], "https://one.com") + self.assertEqual(results[1]["snippet"], "Second result snippet") + + def test_jsonify_search_results_empty(self): + """Test with empty search results.""" + results = self.agent.jsonify_search_results("") + self.assertEqual(results, []) + + def test_jsonify_search_results_partial(self): + """Test with partial result (only title and link).""" + text = """Title: Partial Result +Link: https://partial.com""" + results = self.agent.jsonify_search_results(text) + self.assertEqual(len(results), 1) + self.assertEqual(results[0]["title"], "Partial Result") + self.assertNotIn("snippet", results[0]) + + def test_stringify_search_results(self): + """Test converting structured results back to string.""" + results = [ + {"link": "https://one.com", "snippet": "First snippet"}, + {"link": "https://two.com", "snippet": "Second snippet"} + ] + output = self.agent.stringify_search_results(results) + self.assertIn("https://one.com", output) + self.assertIn("First snippet", output) + self.assertIn("https://two.com", output) + + def test_select_unvisited(self): + """Test filtering visited results.""" + self.agent.search_history = ["https://visited.com"] + results = [ + {"link": "https://visited.com", "title": "Old"}, + {"link": "https://new.com", "title": "New"} + ] + unvisited = self.agent.select_unvisited(results) + self.assertEqual(len(unvisited), 1) + self.assertEqual(unvisited[0]["link"], "https://new.com") + + def test_select_unvisited_all_new(self): + """Test when no results are visited.""" + self.agent.search_history = [] + results = [ + {"link": "https://a.com", "title": "A"}, + {"link": "https://b.com", "title": "B"} + ] + unvisited = self.agent.select_unvisited(results) + self.assertEqual(len(unvisited), 2) + + if __name__ == "__main__": unittest.main() \ No newline at end of file diff --git a/tests/test_logger.py b/tests/test_logger.py new file mode 100644 index 0000000..4c2d6ea --- /dev/null +++ b/tests/test_logger.py @@ -0,0 +1,92 @@ +import unittest +import os +import sys +import shutil +import logging +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from sources.logger import Logger + + +class TestLogger(unittest.TestCase): + """Test suite for the Logger class.""" + + def setUp(self): + self.logger = Logger("test_logger.log") + + def tearDown(self): + if os.path.exists('.logs'): + for handler in self.logger.logger.handlers[:]: + handler.close() + self.logger.logger.removeHandler(handler) + log_path = os.path.join('.logs', 'test_logger.log') + if os.path.exists(log_path): + os.remove(log_path) + + def test_initialization(self): + """Test logger initializes correctly.""" + self.assertTrue(self.logger.enabled) + self.assertIsNotNone(self.logger.logger) + self.assertTrue(os.path.exists('.logs')) + + def test_log_creates_file(self): + """Test that logging creates a log file.""" + self.logger.info("test message") + self.assertTrue(os.path.exists(self.logger.log_path)) + + def test_log_writes_message(self): + """Test that log messages are written to file.""" + self.logger.info("hello world") + with open(self.logger.log_path, 'r') as f: + content = f.read() + self.assertIn("hello world", content) + + def test_log_deduplication(self): + """Test that consecutive identical messages are not duplicated.""" + self.logger.info("duplicate message") + self.logger.info("duplicate message") + with open(self.logger.log_path, 'r') as f: + content = f.read() + self.assertEqual(content.count("duplicate message"), 1) + + def test_log_different_messages(self): + """Test that different messages are all written.""" + self.logger.info("message one") + self.logger.info("message two") + with open(self.logger.log_path, 'r') as f: + content = f.read() + self.assertIn("message one", content) + self.assertIn("message two", content) + + def test_error_level(self): + """Test error level logging.""" + self.logger.error("error occurred") + with open(self.logger.log_path, 'r') as f: + content = f.read() + self.assertIn("ERROR", content) + self.assertIn("error occurred", content) + + def test_warning_level(self): + """Test warning level logging.""" + self.logger.warning("warning issued") + with open(self.logger.log_path, 'r') as f: + content = f.read() + self.assertIn("WARNING", content) + self.assertIn("warning issued", content) + + def test_create_folder(self): + """Test folder creation.""" + test_path = ".test_log_folder" + result = self.logger.create_folder(test_path) + self.assertTrue(result) + self.assertTrue(os.path.exists(test_path)) + os.rmdir(test_path) + + def test_create_folder_already_exists(self): + """Test folder creation when folder already exists.""" + result = self.logger.create_folder('.logs') + self.assertTrue(result) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_utility.py b/tests/test_utility.py new file mode 100644 index 0000000..ddeb06b --- /dev/null +++ b/tests/test_utility.py @@ -0,0 +1,42 @@ +import unittest +import os +import sys +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from sources.utility import get_color_map + + +class TestUtility(unittest.TestCase): + """Test suite for utility module functions.""" + + def test_get_color_map_returns_dict(self): + """Test that get_color_map returns a dictionary.""" + color_map = get_color_map() + self.assertIsInstance(color_map, dict) + + def test_get_color_map_has_required_keys(self): + """Test that color map contains all required color keys.""" + color_map = get_color_map() + required_keys = ["success", "failure", "status", "code", "warning", "output", "info"] + for key in required_keys: + self.assertIn(key, color_map, f"Missing key: {key}") + + def test_get_color_map_values_are_strings(self): + """Test that all color values are strings.""" + color_map = get_color_map() + for key, value in color_map.items(): + self.assertIsInstance(value, str, f"Value for '{key}' should be a string") + + def test_success_is_green(self): + """Test that success maps to green.""" + color_map = get_color_map() + self.assertEqual(color_map["success"], "green") + + def test_failure_is_red(self): + """Test that failure maps to red.""" + color_map = get_color_map() + self.assertEqual(color_map["failure"], "red") + + +if __name__ == '__main__': + unittest.main()