import unittest from unittest.mock import MagicMock, patch from agents.rag_graph import ( build_initial_graph_state, decide_next_action_node, refine_query_node, retrieve_node, ) class RefineQueryNodeTests(unittest.TestCase): @patch("agents.rag_graph.generate_sub_queries") def test_stores_refined_query_from_shared_query_transform(self, mock_generate_sub_queries): state = build_initial_graph_state("What is machine learning?") mock_generate_sub_queries.return_value = ["machine learning definition and types"] updated_state = refine_query_node(state, llm=MagicMock()) self.assertEqual(updated_state["refined_query"], "machine learning definition and types") @patch("agents.rag_graph.generate_sub_queries") def test_increments_retry_count(self, mock_generate_sub_queries): state = build_initial_graph_state("What is machine learning?") mock_generate_sub_queries.return_value = ["machine learning definition"] updated_state = refine_query_node(state, llm=MagicMock()) self.assertEqual(updated_state["retry_count"], 1) @patch("agents.rag_graph.generate_sub_queries") def test_increments_retry_count_on_second_retry(self, mock_generate_sub_queries): state = build_initial_graph_state("What is machine learning?") state["retry_count"] = 1 mock_generate_sub_queries.return_value = ["supervised learning algorithms"] updated_state = refine_query_node(state, llm=MagicMock()) self.assertEqual(updated_state["retry_count"], 2) @patch("agents.rag_graph.generate_sub_queries") def test_falls_back_to_original_query_when_query_transform_returns_empty(self, mock_generate_sub_queries): state = build_initial_graph_state("What is machine learning?") mock_generate_sub_queries.return_value = [] updated_state = refine_query_node(state, llm=MagicMock()) self.assertEqual(updated_state["refined_query"], "What is machine learning?") @patch("agents.rag_graph.generate_sub_queries") def test_records_refined_query_in_debug_data_when_debug_mode(self, mock_generate_sub_queries): state = build_initial_graph_state("What is ML?", debug_mode=True) state["debug_data"] = {"stage_counts": {}} mock_generate_sub_queries.return_value = ["machine learning overview"] updated_state = refine_query_node(state, llm=MagicMock()) self.assertEqual(updated_state["debug_data"]["refined_query"], "machine learning overview") self.assertEqual(updated_state["debug_data"]["retry_count"], 1) class RetryCapTests(unittest.TestCase): def _state_with_partial_evidence(self, retry_count): state = build_initial_graph_state("What is deep learning?") state["retry_count"] = retry_count state["retrieved_documents"] = [MagicMock()] state["expanded_documents"] = [MagicMock()] state["grounding"] = {"passed": False, "reason": "low_rerank_score"} return state def test_retries_when_partial_evidence_and_under_cap(self): state = self._state_with_partial_evidence(retry_count=0) updated_state = decide_next_action_node(state) self.assertEqual(updated_state["next_action"], "retry_retrieval") def test_retries_when_partial_evidence_and_one_retry_done(self): state = self._state_with_partial_evidence(retry_count=1) updated_state = decide_next_action_node(state) self.assertEqual(updated_state["next_action"], "retry_retrieval") def test_falls_back_when_retry_cap_reached(self): state = self._state_with_partial_evidence(retry_count=2) updated_state = decide_next_action_node(state) self.assertEqual(updated_state["next_action"], "fallback") self.assertEqual(updated_state["decision_reason"], "local_retry_cap_reached_research_disabled") class RetrieveNodeUsesRefinedQueryTests(unittest.TestCase): def test_uses_refined_query_when_available(self): state = build_initial_graph_state("original query") state["refined_query"] = "refined query" with patch("agents.rag_graph.retrieve_documents_with_query_transform") as mock_retrieve: mock_retrieve.return_value = [] retrieve_node( state, vectorstore=MagicMock(), reranker=None, bm25_index=None, llm=MagicMock(), retrieval_k=4, rerank_candidate_k=8, bm25_candidate_k=8, enable_query_transform=False, ) used_query = mock_retrieve.call_args[0][1] self.assertEqual(used_query, "refined query") self.assertFalse(mock_retrieve.call_args.kwargs["enable_query_transform"]) def test_uses_original_query_when_no_refined_query(self): state = build_initial_graph_state("original query") with patch("agents.rag_graph.retrieve_documents_with_query_transform") as mock_retrieve: mock_retrieve.return_value = [] retrieve_node( state, vectorstore=MagicMock(), reranker=None, bm25_index=None, llm=MagicMock(), retrieval_k=4, rerank_candidate_k=8, bm25_candidate_k=8, enable_query_transform=False, ) used_query = mock_retrieve.call_args[0][1] self.assertEqual(used_query, "original query") self.assertFalse(mock_retrieve.call_args.kwargs["enable_query_transform"]) def test_keeps_query_transform_enabled_when_no_refined_query_and_flag_is_true(self): state = build_initial_graph_state("original query") with patch("agents.rag_graph.retrieve_documents_with_query_transform") as mock_retrieve: mock_retrieve.return_value = [] retrieve_node( state, vectorstore=MagicMock(), reranker=None, bm25_index=None, llm=MagicMock(), retrieval_k=4, rerank_candidate_k=8, bm25_candidate_k=8, enable_query_transform=True, ) self.assertTrue(mock_retrieve.call_args.kwargs["enable_query_transform"])