""" Coverage tests for benchmarks/datasets/browsecomp.py. Targets the 37 missing lines: - get_dataset_info / get_default_dataset_path - process_example() with canary and without canary - process_example() known_answer_map lookup - process_example() still-encrypted answer fallback keys - process_example() decryption exception - get_question / get_answer """ from unittest.mock import patch MODULE = "local_deep_research.benchmarks.datasets.browsecomp" def _make_dataset(**kwargs): from local_deep_research.benchmarks.datasets.browsecomp import ( BrowseCompDataset, ) return BrowseCompDataset(**kwargs) # --------------------------------------------------------------------------- # Dataset metadata # --------------------------------------------------------------------------- class TestDatasetInfo: def test_get_dataset_info_has_browsecomp_id(self): from local_deep_research.benchmarks.datasets.browsecomp import ( BrowseCompDataset, ) info = BrowseCompDataset.get_dataset_info() assert info["id"] == "browsecomp" assert "name" in info assert "description" in info def test_get_default_dataset_path_is_url(self): from local_deep_research.benchmarks.datasets.browsecomp import ( BrowseCompDataset, ) path = BrowseCompDataset.get_default_dataset_path() assert path.startswith("https://") assert "browse_comp" in path or "browsecomp" in path.lower() # --------------------------------------------------------------------------- # process_example – no canary # --------------------------------------------------------------------------- class TestProcessExampleNoCanary: @patch(f"{MODULE}.get_known_answer_map", return_value={}) @patch(f"{MODULE}.decrypt") def test_no_canary_returns_original(self, mock_decrypt, mock_known_map): ds = _make_dataset() example = {"problem": "encoded_problem", "answer": "encoded_answer"} result = ds.process_example(example) # Without canary, decryption is skipped mock_decrypt.assert_not_called() assert result["problem"] == "encoded_problem" @patch(f"{MODULE}.get_known_answer_map", return_value={}) @patch(f"{MODULE}.decrypt") def test_empty_canary_returns_original(self, mock_decrypt, mock_known_map): ds = _make_dataset() example = { "problem": "some_problem", "answer": "some_answer", "canary": "", } ds.process_example(example) mock_decrypt.assert_not_called() # --------------------------------------------------------------------------- # process_example – with canary, problem too short to decrypt # --------------------------------------------------------------------------- class TestProcessExampleShortProblem: @patch(f"{MODULE}.get_known_answer_map", return_value={}) @patch(f"{MODULE}.decrypt", return_value="decrypted_answer") def test_short_problem_not_decrypted(self, mock_decrypt, mock_known_map): ds = _make_dataset() example = { "problem": "short", # len <= 20, not decrypted "answer": "some_answer", "canary": "mykey", } ds.process_example(example) # decrypt called for answer but not problem decrypt_calls = [c[0][1] for c in mock_decrypt.call_args_list] assert "short" not in decrypt_calls # --------------------------------------------------------------------------- # process_example – known_answer_map lookup # --------------------------------------------------------------------------- class TestProcessExampleKnownAnswerMap: @patch(f"{MODULE}.get_known_answer_map") @patch(f"{MODULE}.decrypt") def test_known_answer_used_without_decrypt( self, mock_decrypt, mock_known_map ): known_map = {"encoded_answer_xyz": "Real Answer"} mock_known_map.return_value = known_map # decrypt returns something different to confirm it's not called for known answers mock_decrypt.return_value = "decrypted_problem_text" ds = _make_dataset() problem_str = "A" * 25 # long enough to trigger problem decryption example = { "problem": problem_str, "answer": "encoded_answer_xyz", "canary": "key", } result = ds.process_example(example) assert result["answer"] == "Real Answer" assert result["correct_answer"] == "Real Answer" # --------------------------------------------------------------------------- # process_example – regular decryption when not in known map # --------------------------------------------------------------------------- class TestProcessExampleRegularDecryption: @patch(f"{MODULE}.get_known_answer_map", return_value={}) @patch(f"{MODULE}.decrypt") def test_decrypt_called_for_long_problem_and_answer( self, mock_decrypt, mock_known_map ): mock_decrypt.side_effect = lambda text, key: f"decrypted_{text}" ds = _make_dataset() long_problem = "P" * 25 example = { "problem": long_problem, "answer": "some_answer", "canary": "key", } result = ds.process_example(example) assert result["problem"] == f"decrypted_{long_problem}" assert result["answer"] == "decrypted_some_answer" assert result["correct_answer"] == "decrypted_some_answer" @patch(f"{MODULE}.get_known_answer_map", return_value={}) @patch(f"{MODULE}.decrypt") def test_no_answer_field_not_processed(self, mock_decrypt, mock_known_map): mock_decrypt.return_value = "decrypted" ds = _make_dataset() example = {"problem": "P" * 25, "canary": "key"} result = ds.process_example(example) # No answer field → answer decryption not attempted assert "answer" not in result or result.get("answer", "") == "" # --------------------------------------------------------------------------- # process_example – still-encrypted answer with fallback keys # --------------------------------------------------------------------------- class TestProcessExampleFallbackKeys: @patch(f"{MODULE}.get_known_answer_map", return_value={}) @patch(f"{MODULE}.decrypt") def test_fallback_key_used_when_still_looks_encrypted( self, mock_decrypt, mock_known_map ): # First call (with canary) returns same base64 text (still encrypted) # Second call (with fallback key "MHGGF2022!") returns plaintext base64_answer = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=" call_count = [0] def decrypt_side_effect(text, key): call_count[0] += 1 if call_count[0] == 1: return text # problem decryption if call_count[0] == 2: return text # answer with canary – still looks encrypted if key == "MHGGF2022!": return "Plaintext Answer From Fallback" return text mock_decrypt.side_effect = decrypt_side_effect ds = _make_dataset() example = { "problem": "P" * 25, "answer": base64_answer, "canary": "wrongkey", } result = ds.process_example(example) # The answer should have been updated by the fallback decryption assert "answer" in result # --------------------------------------------------------------------------- # process_example – decryption exception # --------------------------------------------------------------------------- class TestProcessExampleDecryptException: @patch(f"{MODULE}.get_known_answer_map", return_value={}) @patch(f"{MODULE}.decrypt", side_effect=Exception("decode error")) def test_exception_during_decrypt_logged_not_raised( self, mock_decrypt, mock_known_map ): ds = _make_dataset() example = { "problem": "P" * 25, "answer": "some_answer", "canary": "key", } # Should not raise result = ds.process_example(example) # Result should still be a dict assert isinstance(result, dict) # --------------------------------------------------------------------------- # get_question / get_answer # --------------------------------------------------------------------------- class TestGetQuestionAndAnswer: def test_get_question_returns_problem(self): ds = _make_dataset() example = {"problem": "What is 2+2?", "answer": "4"} assert ds.get_question(example) == "What is 2+2?" def test_get_question_returns_empty_when_missing(self): ds = _make_dataset() assert ds.get_question({}) == "" def test_get_answer_prefers_correct_answer(self): ds = _make_dataset() example = {"answer": "raw_answer", "correct_answer": "preferred_answer"} assert ds.get_answer(example) == "preferred_answer" def test_get_answer_falls_back_to_answer(self): ds = _make_dataset() example = {"answer": "fallback"} assert ds.get_answer(example) == "fallback"