""" Tests for the GraphManager class. """ import os import pytest import pandas as pd import tkinter as tk from tkinter import ttk from unittest.mock import Mock, patch import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) from src.graph_manager import GraphManager class TestGraphManager: """Test cases for the GraphManager class.""" @pytest.fixture def root_window(self): """Create a root window for testing.""" root = tk.Tk() yield root root.destroy() @pytest.fixture def parent_frame(self, root_window): """Create a parent frame for testing.""" frame = ttk.LabelFrame(root_window, text="Test Frame") frame.pack() return frame def test_init(self, parent_frame): """Test GraphManager initialization.""" gm = GraphManager(parent_frame) assert gm.parent_frame == parent_frame assert isinstance(gm.toggle_vars, dict) # Check symptom toggles assert "depression" in gm.toggle_vars assert "anxiety" in gm.toggle_vars assert "sleep" in gm.toggle_vars assert "appetite" in gm.toggle_vars # Check medicine toggles assert "bupropion" in gm.toggle_vars assert "hydroxyzine" in gm.toggle_vars assert "gabapentin" in gm.toggle_vars assert "propranolol" in gm.toggle_vars assert "quetiapine" in gm.toggle_vars # Check that symptom toggles are initially True for symptom in ["depression", "anxiety", "sleep", "appetite"]: assert gm.toggle_vars[symptom].get() is True # Check that some medicine toggles are True by default assert gm.toggle_vars["bupropion"].get() is True assert gm.toggle_vars["propranolol"].get() is True # Check that some medicine toggles are False by default assert gm.toggle_vars["hydroxyzine"].get() is False assert gm.toggle_vars["gabapentin"].get() is False assert gm.toggle_vars["quetiapine"].get() is False def test_toggle_controls_creation(self, parent_frame): """Test that toggle controls are created properly.""" gm = GraphManager(parent_frame) # Check that control frame exists assert hasattr(gm, 'control_frame') assert isinstance(gm.control_frame, ttk.Frame) # Check that all toggle variables exist expected_toggles = ["depression", "anxiety", "sleep", "appetite", "bupropion", "hydroxyzine", "gabapentin", "propranolol", "quetiapine"] for toggle in expected_toggles: assert toggle in gm.toggle_vars assert isinstance(gm.toggle_vars[toggle], tk.BooleanVar) def test_graph_frame_creation(self, parent_frame): """Test that graph frame is created properly.""" gm = GraphManager(parent_frame) assert hasattr(gm, 'graph_frame') assert isinstance(gm.graph_frame, ttk.Frame) @patch('matplotlib.pyplot.subplots') def test_matplotlib_initialization(self, mock_subplots, parent_frame): """Test matplotlib figure and canvas initialization.""" mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) assert gm.fig == mock_fig assert gm.ax == mock_ax assert gm.canvas == mock_canvas mock_canvas_class.assert_called_once_with(figure=mock_fig, master=gm.graph_frame) def test_update_graph_empty_dataframe(self, parent_frame): """Test updating graph with empty DataFrame.""" with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg'): gm = GraphManager(parent_frame) # Test with empty DataFrame empty_df = pd.DataFrame() gm.update_graph(empty_df) # Verify ax.clear() was called mock_ax.clear.assert_called() def test_update_graph_with_data(self, parent_frame, sample_dataframe): """Test updating graph with valid data.""" with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) gm.update_graph(sample_dataframe) # Verify methods were called mock_ax.clear.assert_called() mock_canvas.draw.assert_called() def test_toggle_functionality(self, parent_frame, sample_dataframe): """Test that toggle variables affect graph display.""" with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # Turn off depression toggle gm.toggle_vars["depression"].set(False) gm.update_graph(sample_dataframe) # The graph should still update (specific plotting logic would need more detailed testing) mock_ax.clear.assert_called() mock_canvas.draw.assert_called() def test_close_method(self, parent_frame): """Test the close method.""" with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas with patch('matplotlib.pyplot.close') as mock_plt_close: gm = GraphManager(parent_frame) gm.close() mock_plt_close.assert_called_once_with(mock_fig) def test_date_parsing_in_update_graph(self, parent_frame): """Test that date parsing works correctly in update_graph.""" # Create a DataFrame with date strings df_with_dates = pd.DataFrame({ 'date': ['2024-01-01', '2024-01-02', '2024-01-03'], 'depression': [3, 2, 4], 'anxiety': [2, 3, 1], 'sleep': [4, 3, 5], 'appetite': [3, 4, 2], 'bupropion': [1, 1, 0], 'hydroxyzine': [0, 1, 0], 'gabapentin': [2, 2, 1], 'propranolol': [1, 0, 1], 'note': ['Test note 1', 'Test note 2', ''] }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas with patch('pandas.to_datetime') as mock_to_datetime: gm = GraphManager(parent_frame) gm.update_graph(df_with_dates) # Verify pandas.to_datetime was called mock_to_datetime.assert_called() @patch('matplotlib.pyplot.subplots') def test_exception_handling_in_update_graph(self, mock_subplots, parent_frame, sample_dataframe): """Test exception handling in update_graph method.""" mock_fig = Mock() mock_ax = Mock() mock_ax.plot.side_effect = Exception("Plot error") mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # This should not raise an exception, but handle it gracefully try: gm.update_graph(sample_dataframe) except Exception as e: pytest.fail(f"update_graph should handle exceptions gracefully, but raised: {e}") def test_grid_configuration(self, parent_frame): """Test that grid configuration is set up correctly.""" gm = GraphManager(parent_frame) # The parent frame should have grid configuration # Note: In a real test, you might need to check grid_info() or similar # This is a basic structure test assert hasattr(gm, 'parent_frame') assert hasattr(gm, 'control_frame') assert hasattr(gm, 'graph_frame') def test_canvas_widget_packing(self, parent_frame): """Test that canvas widget is properly packed.""" with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas.get_tk_widget.return_value = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # Verify get_tk_widget was called (for packing) mock_canvas.get_tk_widget.assert_called() def test_multiple_toggle_combinations(self, parent_frame, sample_dataframe): """Test various combinations of toggle states.""" with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # Test all toggles off for toggle in gm.toggle_vars.values(): toggle.set(False) gm.update_graph(sample_dataframe) # Test mixed toggles gm.toggle_vars["depression"].set(True) gm.toggle_vars["anxiety"].set(False) gm.update_graph(sample_dataframe) # Verify the graph was updated in each case assert mock_ax.clear.call_count >= 2 assert mock_canvas.draw.call_count >= 2 def test_calculate_daily_dose_empty_input(self, parent_frame): """Test dose calculation with empty/invalid input.""" gm = GraphManager(parent_frame) # Test empty string assert gm._calculate_daily_dose("") == 0.0 # Test NaN values assert gm._calculate_daily_dose("nan") == 0.0 assert gm._calculate_daily_dose("NaN") == 0.0 # Test None (will be converted to string) assert gm._calculate_daily_dose(None) == 0.0 def test_calculate_daily_dose_standard_format(self, parent_frame): """Test dose calculation with standard timestamp:dose format.""" gm = GraphManager(parent_frame) # Single dose dose_str = "2025-07-28 18:59:45:150mg" assert gm._calculate_daily_dose(dose_str) == 150.0 # Multiple doses dose_str = "2025-07-28 18:59:45:150mg|2025-07-28 19:34:19:75mg" assert gm._calculate_daily_dose(dose_str) == 225.0 # Doses without units dose_str = "2025-07-28 18:59:45:10|2025-07-28 19:34:19:5" assert gm._calculate_daily_dose(dose_str) == 15.0 def test_calculate_daily_dose_with_symbols(self, parent_frame): """Test dose calculation with bullet symbols.""" gm = GraphManager(parent_frame) # With bullet symbols dose_str = "• • • • 2025-07-30 07:50:00:300" assert gm._calculate_daily_dose(dose_str) == 300.0 # Multiple bullets dose_str = "• 2025-07-30 22:50:00:10|• 2025-07-30 23:50:00:5" assert gm._calculate_daily_dose(dose_str) == 15.0 def test_calculate_daily_dose_no_timestamp(self, parent_frame): """Test dose calculation without timestamp.""" gm = GraphManager(parent_frame) # Just dose value dose_str = "150mg" assert gm._calculate_daily_dose(dose_str) == 150.0 # Multiple values without timestamp dose_str = "100|50" assert gm._calculate_daily_dose(dose_str) == 150.0 def test_calculate_daily_dose_decimal_values(self, parent_frame): """Test dose calculation with decimal values.""" gm = GraphManager(parent_frame) # Decimal dose dose_str = "2025-07-28 18:59:45:12.5mg" assert gm._calculate_daily_dose(dose_str) == 12.5 # Multiple decimal doses dose_str = "2025-07-28 18:59:45:12.5mg|2025-07-28 19:34:19:7.5mg" assert gm._calculate_daily_dose(dose_str) == 20.0 def test_medicine_dose_plotting(self, parent_frame): """Test that medicine doses are plotted correctly.""" # Create a DataFrame with dose data df_with_doses = pd.DataFrame({ 'date': ['2024-01-01', '2024-01-02', '2024-01-03'], 'depression': [3, 2, 4], 'anxiety': [2, 3, 1], 'sleep': [4, 3, 5], 'appetite': [3, 4, 2], 'bupropion': [1, 1, 0], 'bupropion_doses': ['2024-01-01 08:00:00:150mg', '2024-01-02 08:00:00:300mg', ''], 'hydroxyzine': [0, 1, 0], 'hydroxyzine_doses': ['', '2024-01-02 20:00:00:25mg', ''], 'gabapentin': [0, 0, 0], 'gabapentin_doses': ['', '', ''], 'propranolol': [1, 0, 1], 'propranolol_doses': ['2024-01-01 12:00:00:10mg', '', '2024-01-03 12:00:00:20mg'], 'quetiapine': [0, 0, 0], 'quetiapine_doses': ['', '', ''], }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) gm.update_graph(df_with_doses) # Verify that bar plots were called (for medicines with doses) mock_ax.bar.assert_called() # Verify canvas was redrawn mock_canvas.draw.assert_called() def test_medicine_toggle_functionality(self, parent_frame): """Test that medicine toggles affect dose display.""" df_with_doses = pd.DataFrame({ 'date': ['2024-01-01'], 'depression': [3], 'anxiety': [2], 'sleep': [4], 'appetite': [3], 'bupropion': [1], 'bupropion_doses': ['2024-01-01 08:00:00:150mg'], 'hydroxyzine': [0], 'hydroxyzine_doses': [''], 'gabapentin': [0], 'gabapentin_doses': [''], 'propranolol': [1], 'propranolol_doses': ['2024-01-01 12:00:00:10mg'], 'quetiapine': [0], 'quetiapine_doses': [''], }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # Turn off bupropion toggle gm.toggle_vars["bupropion"].set(False) gm.update_graph(df_with_doses) # Turn on hydroxyzine toggle (though it has no doses) gm.toggle_vars["hydroxyzine"].set(True) gm.update_graph(df_with_doses) # Verify the graph was updated assert mock_ax.clear.call_count >= 2 assert mock_canvas.draw.call_count >= 2 def test_enhanced_legend_functionality(self, parent_frame): """Test that the enhanced legend displays correctly with medicine data.""" df_with_doses = pd.DataFrame({ 'date': ['2024-01-01', '2024-01-02'], 'depression': [3, 2], 'anxiety': [2, 3], 'sleep': [4, 3], 'appetite': [3, 4], 'bupropion': [1, 1], 'bupropion_doses': ['2024-01-01 08:00:00:150mg', '2024-01-02 08:00:00:200mg'], 'hydroxyzine': [0, 0], 'hydroxyzine_doses': ['', ''], 'gabapentin': [0, 0], 'gabapentin_doses': ['', ''], 'propranolol': [1, 1], 'propranolol_doses': ['2024-01-01 12:00:00:10mg', '2024-01-02 12:00:00:15mg'], 'quetiapine': [0, 0], 'quetiapine_doses': ['', ''], }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_ax.get_legend_handles_labels.return_value = ([], []) mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # Enable some medicine toggles gm.toggle_vars["bupropion"].set(True) gm.toggle_vars["propranolol"].set(True) gm.toggle_vars["hydroxyzine"].set(True) # No dose data gm.update_graph(df_with_doses) # Verify that legend is called with enhanced parameters mock_ax.legend.assert_called() legend_call = mock_ax.legend.call_args # Check that enhanced legend parameters are used assert 'ncol' in legend_call.kwargs assert legend_call.kwargs['ncol'] == 2 assert 'fontsize' in legend_call.kwargs assert legend_call.kwargs['fontsize'] == 'small' assert 'frameon' in legend_call.kwargs assert legend_call.kwargs['frameon'] is True def test_legend_with_medicines_without_data(self, parent_frame): """Test that medicines without dose data are properly tracked in legend.""" df_with_partial_doses = pd.DataFrame({ 'date': ['2024-01-01'], 'depression': [3], 'anxiety': [2], 'sleep': [4], 'appetite': [3], 'bupropion': [1], 'bupropion_doses': ['2024-01-01 08:00:00:150mg'], 'hydroxyzine': [0], 'hydroxyzine_doses': [''], # No dose data 'gabapentin': [0], 'gabapentin_doses': [''], # No dose data 'propranolol': [0], 'propranolol_doses': [''], 'quetiapine': [0], 'quetiapine_doses': [''], }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() # Mock the legend handles and labels original_handles = [Mock()] original_labels = ['Bupropion (avg: 150.0mg)'] mock_ax.get_legend_handles_labels.return_value = (original_handles, original_labels) mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # Enable medicines with and without data gm.toggle_vars["bupropion"].set(True) # Has data gm.toggle_vars["hydroxyzine"].set(True) # No data gm.toggle_vars["gabapentin"].set(True) # No data gm.update_graph(df_with_partial_doses) # Verify legend was called mock_ax.legend.assert_called() # Check that the legend call includes additional handles/labels legend_call = mock_ax.legend.call_args handles, labels = legend_call.args[:2] # Should have more labels than just the original ones assert len(labels) > len(original_labels) def test_average_dose_calculation_in_legend(self, parent_frame): """Test that average doses are correctly calculated and displayed in legend.""" df_with_varying_doses = pd.DataFrame({ 'date': ['2024-01-01', '2024-01-02', '2024-01-03'], 'depression': [3, 2, 4], 'anxiety': [2, 3, 1], 'sleep': [4, 3, 5], 'appetite': [3, 4, 2], 'bupropion': [1, 1, 1], 'bupropion_doses': ['2024-01-01 08:00:00:100mg', '2024-01-02 08:00:00:200mg', '2024-01-03 08:00:00:150mg'], # Average should be 150mg 'propranolol': [1, 1, 0], 'propranolol_doses': ['2024-01-01 12:00:00:10mg', '2024-01-02 12:00:00:20mg', ''], # Average should be 15mg 'hydroxyzine': [0, 0, 0], 'hydroxyzine_doses': ['', '', ''], 'gabapentin': [0, 0, 0], 'gabapentin_doses': ['', '', ''], 'quetiapine': [0, 0, 0], 'quetiapine_doses': ['', '', ''], }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # Test the average calculation directly bup_avg = gm._calculate_daily_dose('2024-01-01 08:00:00:100mg') assert bup_avg == 100.0 prop_avg = gm._calculate_daily_dose('2024-01-01 12:00:00:10mg') assert prop_avg == 10.0 # Test with full data gm.toggle_vars["bupropion"].set(True) gm.toggle_vars["propranolol"].set(True) gm.update_graph(df_with_varying_doses) # Verify that bars were plotted (indicating dose data was processed) mock_ax.bar.assert_called() def test_legend_positioning_and_styling(self, parent_frame): """Test that legend positioning and styling parameters are correctly applied.""" df_simple = pd.DataFrame({ 'date': ['2024-01-01'], 'depression': [3], 'anxiety': [2], 'sleep': [4], 'appetite': [3], 'bupropion': [1], 'bupropion_doses': ['2024-01-01 08:00:00:150mg'], 'hydroxyzine': [0], 'hydroxyzine_doses': [''], 'gabapentin': [0], 'gabapentin_doses': [''], 'propranolol': [0], 'propranolol_doses': [''], 'quetiapine': [0], 'quetiapine_doses': [''], }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_ax.get_legend_handles_labels.return_value = ([Mock()], ['Test Label']) mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) gm.update_graph(df_simple) # Verify legend styling parameters mock_ax.legend.assert_called() legend_call = mock_ax.legend.call_args expected_params = { 'loc': 'upper left', 'bbox_to_anchor': (0, 1), 'ncol': 2, 'fontsize': 'small', 'frameon': True, 'fancybox': True, 'shadow': True, 'framealpha': 0.9 } for param, expected_value in expected_params.items(): assert param in legend_call.kwargs assert legend_call.kwargs[param] == expected_value def test_medicine_tracking_lists(self, parent_frame): """Test that medicines are correctly categorized into with_data and without_data lists.""" df_mixed_data = pd.DataFrame({ 'date': ['2024-01-01', '2024-01-02'], 'depression': [3, 2], 'anxiety': [2, 3], 'sleep': [4, 3], 'appetite': [3, 4], # Medicines with data 'bupropion': [1, 1], 'bupropion_doses': ['2024-01-01 08:00:00:150mg', '2024-01-02 08:00:00:200mg'], 'propranolol': [1, 1], 'propranolol_doses': ['2024-01-01 12:00:00:10mg', '2024-01-02 12:00:00:15mg'], # Medicines without data (but toggled on) 'hydroxyzine': [0, 0], 'hydroxyzine_doses': ['', ''], 'gabapentin': [0, 0], 'gabapentin_doses': ['', ''], 'quetiapine': [0, 0], 'quetiapine_doses': ['', ''], }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_ax.get_legend_handles_labels.return_value = ([], []) mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) # Enable all medicines gm.toggle_vars["bupropion"].set(True) # Has data gm.toggle_vars["propranolol"].set(True) # Has data gm.toggle_vars["hydroxyzine"].set(True) # No data gm.toggle_vars["gabapentin"].set(True) # No data gm.toggle_vars["quetiapine"].set(False) # Disabled gm.update_graph(df_mixed_data) # Verify that the method was called and plotting occurred mock_ax.bar.assert_called() # Should be called for medicines with data mock_ax.legend.assert_called() # Legend should be created def test_legend_dummy_handle_creation(self, parent_frame): """Test that dummy handles are created for medicines without data.""" df_no_dose_data = pd.DataFrame({ 'date': ['2024-01-01'], 'depression': [3], 'anxiety': [2], 'sleep': [4], 'appetite': [3], 'bupropion': [0], 'bupropion_doses': [''], 'hydroxyzine': [0], 'hydroxyzine_doses': [''], 'gabapentin': [0], 'gabapentin_doses': [''], 'propranolol': [0], 'propranolol_doses': [''], 'quetiapine': [0], 'quetiapine_doses': [''], }) with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_ax.get_legend_handles_labels.return_value = ([Mock()], ['Depression']) mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas # Mock Rectangle import for dummy handle creation with patch('matplotlib.patches.Rectangle') as mock_rectangle: mock_dummy_handle = Mock() mock_rectangle.return_value = mock_dummy_handle gm = GraphManager(parent_frame) # Enable some medicines without data gm.toggle_vars["hydroxyzine"].set(True) gm.toggle_vars["gabapentin"].set(True) gm.update_graph(df_no_dose_data) # If there are medicines without data, Rectangle should be called # to create dummy handles if gm.toggle_vars["hydroxyzine"].get() or gm.toggle_vars["gabapentin"].get(): mock_rectangle.assert_called() def test_empty_dataframe_legend_handling(self, parent_frame): """Test that legend is handled correctly with empty DataFrame.""" empty_df = pd.DataFrame() with patch('matplotlib.pyplot.subplots') as mock_subplots: mock_fig = Mock() mock_ax = Mock() mock_subplots.return_value = (mock_fig, mock_ax) with patch('graph_manager.FigureCanvasTkAgg') as mock_canvas_class: mock_canvas = Mock() mock_canvas_class.return_value = mock_canvas gm = GraphManager(parent_frame) gm.update_graph(empty_df) # With empty data, legend should not be called mock_ax.legend.assert_not_called() mock_ax.clear.assert_called() mock_canvas.draw.assert_called() def test_dose_calculation_comprehensive(self, parent_frame, sample_dose_data): """Test dose calculation with comprehensive test cases.""" gm = GraphManager(parent_frame) # Test all sample dose data cases assert gm._calculate_daily_dose(sample_dose_data['standard_format']) == 225.0 assert gm._calculate_daily_dose(sample_dose_data['with_bullets']) == 300.0 assert gm._calculate_daily_dose(sample_dose_data['decimal_doses']) == 20.0 assert gm._calculate_daily_dose(sample_dose_data['no_timestamp']) == 150.0 assert gm._calculate_daily_dose(sample_dose_data['mixed_format']) == 85.0 assert gm._calculate_daily_dose(sample_dose_data['empty_string']) == 0.0 assert gm._calculate_daily_dose(sample_dose_data['nan_value']) == 0.0 assert gm._calculate_daily_dose(sample_dose_data['no_units']) == 15.0 def test_dose_calculation_edge_cases(self, parent_frame): """Test dose calculation with edge cases.""" gm = GraphManager(parent_frame) # Test with malformed data assert gm._calculate_daily_dose("malformed:data") == 0.0 assert gm._calculate_daily_dose("::::") == 0.0 assert gm._calculate_daily_dose("2025-07-28:") == 0.0 assert gm._calculate_daily_dose("2025-07-28::mg") == 0.0 # Test with partial data assert gm._calculate_daily_dose("2025-07-28 18:59:45:150") == 150.0 # no units assert gm._calculate_daily_dose("150mg") == 150.0 # no timestamp # Test with spaces and special characters assert gm._calculate_daily_dose(" 2025-07-28 18:59:45:150mg ") == 150.0 assert gm._calculate_daily_dose("••• 2025-07-28 18:59:45:150mg •••") == 150.0