Spaces:
Build error
Build error
| """ | |
| Unit tests for logger module | |
| Tests Google Sheets logging functionality | |
| """ | |
| import unittest | |
| from unittest.mock import MagicMock, Mock, patch | |
| from src.logger import _log_response_impl, log_response, log_timing_data | |
| class TestLogger(unittest.TestCase): | |
| """Test cases for logger module""" | |
| def setUp(self): | |
| """Set up test fixtures""" | |
| self.question = "How do I create an account?" | |
| self.answer = "You can create an account by visiting our website." | |
| self.source_ids = "KB001, KB002" | |
| self.knowledge_pairs = [ | |
| ("Question 1?", "Answer 1."), | |
| ("Question 2?", "Answer 2."), | |
| ] | |
| self.session_id = "test_session_123" | |
| def test_log_response_impl(self, mock_sheet): | |
| """Test internal response logging implementation""" | |
| _log_response_impl( | |
| self.question, | |
| self.answer, | |
| self.source_ids, | |
| self.knowledge_pairs, | |
| self.session_id, | |
| ) | |
| # Verify append_row was called | |
| mock_sheet.append_row.assert_called_once() | |
| # Check the row data | |
| call_args = mock_sheet.append_row.call_args | |
| row = call_args[0][0] | |
| # Verify row structure | |
| self.assertEqual( | |
| len(row), 9 | |
| ) # timestamp, session_id, question, answer, source_ids, 4 knowledge fields | |
| self.assertEqual(row[1], self.session_id) | |
| self.assertEqual(row[2], self.question) | |
| self.assertEqual(row[3], self.answer) | |
| self.assertEqual(row[4], self.source_ids) | |
| self.assertEqual(row[5], "Question 1?") | |
| self.assertEqual(row[6], "Answer 1.") | |
| self.assertEqual(row[7], "Question 2?") | |
| self.assertEqual(row[8], "Answer 2.") | |
| def test_log_response_with_timer(self, mock_sheet): | |
| """Test log_response with timer""" | |
| mock_timer = Mock() | |
| mock_timer.time_step = MagicMock() | |
| mock_timer.time_step.return_value.__enter__ = Mock() | |
| mock_timer.time_step.return_value.__exit__ = Mock() | |
| log_response( | |
| self.question, | |
| self.answer, | |
| self.source_ids, | |
| self.knowledge_pairs, | |
| self.session_id, | |
| timer=mock_timer, | |
| ) | |
| # Verify timer was used | |
| mock_timer.time_step.assert_called_once_with("response_logging") | |
| def test_log_response_empty_knowledge_pairs(self, mock_sheet): | |
| """Test logging with empty knowledge pairs""" | |
| _log_response_impl( | |
| self.question, self.answer, self.source_ids, [], self.session_id | |
| ) | |
| # Should still work | |
| mock_sheet.append_row.assert_called_once() | |
| # Check that N/A is used for missing pairs | |
| row = mock_sheet.append_row.call_args[0][0] | |
| self.assertEqual(row[5], "N/A") | |
| self.assertEqual(row[6], "N/A") | |
| def test_log_response_single_knowledge_pair(self, mock_sheet): | |
| """Test logging with single knowledge pair""" | |
| single_pair = [("Single question?", "Single answer.")] | |
| _log_response_impl( | |
| self.question, self.answer, self.source_ids, single_pair, self.session_id | |
| ) | |
| row = mock_sheet.append_row.call_args[0][0] | |
| # First pair should be present | |
| self.assertEqual(row[5], "Single question?") | |
| self.assertEqual(row[6], "Single answer.") | |
| # Second pair should be N/A | |
| self.assertEqual(row[7], "N/A") | |
| self.assertEqual(row[8], "N/A") | |
| def test_log_response_fallback_on_error(self, mock_open, mock_sheet): | |
| """Test fallback to file logging on error""" | |
| # Make append_row raise an exception | |
| mock_sheet.append_row.side_effect = Exception("Connection error") | |
| # Mock file operations | |
| mock_file = MagicMock() | |
| mock_open.return_value.__enter__.return_value = mock_file | |
| # Should not raise exception | |
| _log_response_impl( | |
| self.question, | |
| self.answer, | |
| self.source_ids, | |
| self.knowledge_pairs, | |
| self.session_id, | |
| ) | |
| # Verify fallback file was opened | |
| mock_open.assert_called_once_with("/tmp/response_log.txt", "a") | |
| mock_file.write.assert_called_once() | |
| def test_log_timing_data(self, mock_sheet): | |
| """Test timing data logging""" | |
| timing_summary = { | |
| "total_time_ms": 1500, | |
| "step_times": { | |
| "intent_classification": 50, | |
| "memory_retrieval": 100, | |
| "rag_retrieval": 200, | |
| "embedding_generation": 300, | |
| "similarity_calculation": 150, | |
| "context_processing": 100, | |
| "llm_generation": 500, | |
| "memory_update": 50, | |
| "response_logging": 50, | |
| }, | |
| } | |
| log_timing_data( | |
| self.question, | |
| self.session_id, | |
| timing_summary, | |
| error_step=None, | |
| notes="Test note", | |
| ) | |
| # Verify append_row was called | |
| mock_sheet.append_row.assert_called_once() | |
| # Check row structure | |
| row = mock_sheet.append_row.call_args[0][0] | |
| # Should have 15 fields | |
| self.assertEqual(len(row), 15) | |
| self.assertEqual(row[1], self.session_id) | |
| self.assertEqual(row[3], 1500) # total_time_ms | |
| self.assertEqual(row[4], 50) # intent_classification | |
| self.assertEqual(row[5], 100) # memory_retrieval | |
| self.assertEqual(row[14], "Test note") # notes | |
| def test_log_timing_data_with_error(self, mock_sheet): | |
| """Test timing data logging with error""" | |
| timing_summary = { | |
| "total_time_ms": 500, | |
| "step_times": {"intent_classification": 50}, | |
| } | |
| log_timing_data( | |
| self.question, | |
| self.session_id, | |
| timing_summary, | |
| error_step="rag_retrieval", | |
| notes="Error occurred", | |
| ) | |
| row = mock_sheet.append_row.call_args[0][0] | |
| # Check error_step is logged | |
| self.assertEqual(row[13], "rag_retrieval") | |
| self.assertEqual(row[14], "Error occurred") | |
| def test_log_timing_data_missing_steps(self, mock_sheet): | |
| """Test timing data with missing step times""" | |
| timing_summary = { | |
| "total_time_ms": 100, | |
| "step_times": { | |
| "intent_classification": 100 | |
| # Other steps missing | |
| }, | |
| } | |
| log_timing_data(self.question, self.session_id, timing_summary) | |
| row = mock_sheet.append_row.call_args[0][0] | |
| # Missing steps should default to 0 | |
| self.assertEqual(row[5], 0) # memory_retrieval | |
| self.assertEqual(row[6], 0) # rag_retrieval | |
| def test_log_timing_data_long_question(self, mock_sheet): | |
| """Test timing data logging with long question (truncation)""" | |
| long_question = "A" * 150 # 150 characters | |
| timing_summary = {"total_time_ms": 100, "step_times": {}} | |
| log_timing_data(long_question, self.session_id, timing_summary) | |
| row = mock_sheet.append_row.call_args[0][0] | |
| # Question should be truncated to 103 chars (100 + "...") | |
| self.assertEqual(len(row[2]), 103) | |
| self.assertTrue(row[2].endswith("...")) | |
| def test_log_timing_data_fallback_on_error(self, mock_open, mock_sheet): | |
| """Test fallback to file logging for timing data on error""" | |
| mock_sheet.append_row.side_effect = Exception("Connection error") | |
| mock_file = MagicMock() | |
| mock_open.return_value.__enter__.return_value = mock_file | |
| timing_summary = {"total_time_ms": 100, "step_times": {}} | |
| log_timing_data(self.question, self.session_id, timing_summary) | |
| # Verify fallback file was opened | |
| mock_open.assert_called_once_with("/tmp/timing_log.txt", "a") | |
| mock_file.write.assert_called_once() | |
| if __name__ == "__main__": | |
| unittest.main() | |