diff --git a/tests/batcontrol/test_core.py b/tests/batcontrol/test_core.py index e9b84d9..3371e48 100644 --- a/tests/batcontrol/test_core.py +++ b/tests/batcontrol/test_core.py @@ -47,254 +47,132 @@ def mock_config(self): } } + @pytest.fixture + def core_dependencies(self, mocker): + """Patch Batcontrol dependencies and return the mocked inverter.""" + mock_inverter = mocker.MagicMock() + mock_inverter.max_pv_charge_rate = 3000 + mock_inverter.set_mode_limit_battery_charge = mocker.MagicMock() + mock_inverter.get_max_capacity = mocker.MagicMock(return_value=10000) + + mocker.patch( + 'batcontrol.core.tariff_factory.create_tarif_provider', + autospec=True, + return_value=mocker.MagicMock(), + ) + mocker.patch( + 'batcontrol.core.inverter_factory.create_inverter', + autospec=True, + return_value=mock_inverter, + ) + mocker.patch( + 'batcontrol.core.solar_factory.create_solar_provider', + autospec=True, + return_value=mocker.MagicMock(), + ) + mocker.patch( + 'batcontrol.core.consumption_factory.create_consumption', + autospec=True, + return_value=mocker.MagicMock(), + ) + return mock_inverter + def test_mode_constant_exists(self): """Test that MODE_LIMIT_BATTERY_CHARGE_RATE constant is defined""" assert MODE_LIMIT_BATTERY_CHARGE_RATE == 8 - @patch('batcontrol.core.tariff_factory.create_tarif_provider') - @patch('batcontrol.core.inverter_factory.create_inverter') - @patch('batcontrol.core.solar_factory.create_solar_provider') - @patch('batcontrol.core.consumption_factory.create_consumption') def test_limit_battery_charge_rate_method( - self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff, - mock_config): + self, mock_config, core_dependencies): """Test limit_battery_charge_rate method applies correct limits""" - # Setup mocks - mock_inverter = MagicMock() - mock_inverter.max_pv_charge_rate = 3000 - mock_inverter.set_mode_limit_battery_charge = MagicMock() - mock_inverter.get_max_capacity = MagicMock(return_value=10000) - mock_inverter_factory.return_value = mock_inverter - - mock_tariff.return_value = MagicMock() - mock_solar.return_value = MagicMock() - mock_consumption.return_value = MagicMock() - - # Create Batcontrol instance + mock_inverter = core_dependencies bc = Batcontrol(mock_config) - # Test setting limit within bounds bc.limit_battery_charge_rate(2000) - # Verify inverter method was called with correct value mock_inverter.set_mode_limit_battery_charge.assert_called_once_with(2000) assert bc.last_mode == MODE_LIMIT_BATTERY_CHARGE_RATE - @patch('batcontrol.core.tariff_factory.create_tarif_provider') - @patch('batcontrol.core.inverter_factory.create_inverter') - @patch('batcontrol.core.solar_factory.create_solar_provider') - @patch('batcontrol.core.consumption_factory.create_consumption') def test_limit_battery_charge_rate_capped_by_max( - self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff, - mock_config): + self, mock_config, core_dependencies): """Test that limit is capped by max_pv_charge_rate""" - # Setup mocks - mock_inverter = MagicMock() - mock_inverter.max_pv_charge_rate = 3000 - mock_inverter.set_mode_limit_battery_charge = MagicMock() - mock_inverter.get_max_capacity = MagicMock(return_value=10000) - mock_inverter_factory.return_value = mock_inverter - - mock_tariff.return_value = MagicMock() - mock_solar.return_value = MagicMock() - mock_consumption.return_value = MagicMock() - - # Create Batcontrol instance + mock_inverter = core_dependencies bc = Batcontrol(mock_config) - # Try to set limit above max_pv_charge_rate bc.limit_battery_charge_rate(5000) - # Verify it was capped to max_pv_charge_rate mock_inverter.set_mode_limit_battery_charge.assert_called_once_with(3000) - @patch('batcontrol.core.tariff_factory.create_tarif_provider') - @patch('batcontrol.core.inverter_factory.create_inverter') - @patch('batcontrol.core.solar_factory.create_solar_provider') - @patch('batcontrol.core.consumption_factory.create_consumption') def test_limit_battery_charge_rate_floored_by_min( - self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff, - mock_config): + self, mock_config, core_dependencies): """Test that limit is floored by min_pv_charge_rate""" - # Setup mocks - mock_inverter = MagicMock() - mock_inverter.max_pv_charge_rate = 3000 - mock_inverter.set_mode_limit_battery_charge = MagicMock() - mock_inverter.get_max_capacity = MagicMock(return_value=10000) - mock_inverter_factory.return_value = mock_inverter - - mock_tariff.return_value = MagicMock() - mock_solar.return_value = MagicMock() - mock_consumption.return_value = MagicMock() - - # Create Batcontrol instance + mock_inverter = core_dependencies bc = Batcontrol(mock_config) - # Try to set limit below min_pv_charge_rate bc.limit_battery_charge_rate(50) - # Verify it was floored to min_pv_charge_rate mock_inverter.set_mode_limit_battery_charge.assert_called_once_with(100) - @patch('batcontrol.core.tariff_factory.create_tarif_provider') - @patch('batcontrol.core.inverter_factory.create_inverter') - @patch('batcontrol.core.solar_factory.create_solar_provider') - @patch('batcontrol.core.consumption_factory.create_consumption') def test_limit_battery_charge_rate_zero_allowed( - self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff, - mock_config): + self, mock_config, core_dependencies): """Test that limit=0 blocks charging when min=0""" - # Modify config to allow zero charging mock_config['inverter']['min_pv_charge_rate'] = 0 - - # Setup mocks - mock_inverter = MagicMock() - mock_inverter.max_pv_charge_rate = 3000 - mock_inverter.set_mode_limit_battery_charge = MagicMock() - mock_inverter.get_max_capacity = MagicMock(return_value=10000) - mock_inverter_factory.return_value = mock_inverter - - mock_tariff.return_value = MagicMock() - mock_solar.return_value = MagicMock() - mock_consumption.return_value = MagicMock() - - # Create Batcontrol instance + mock_inverter = core_dependencies bc = Batcontrol(mock_config) - # Set limit to 0 bc.limit_battery_charge_rate(0) - # Verify it was set to 0 (charging blocked) mock_inverter.set_mode_limit_battery_charge.assert_called_once_with(0) - @patch('batcontrol.core.tariff_factory.create_tarif_provider') - @patch('batcontrol.core.inverter_factory.create_inverter') - @patch('batcontrol.core.solar_factory.create_solar_provider') - @patch('batcontrol.core.consumption_factory.create_consumption') def test_limit_battery_charge_rate_min_exceeds_max( - self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff, - mock_config): + self, mock_config, core_dependencies): """Test that when min_pv_charge_rate > max_pv_charge_rate, min is clamped to max at init""" mock_config['inverter']['min_pv_charge_rate'] = 4000 - - # Setup mocks - mock_inverter = MagicMock() - mock_inverter.max_pv_charge_rate = 3000 - mock_inverter.set_mode_limit_battery_charge = MagicMock() - mock_inverter.get_max_capacity = MagicMock(return_value=10000) - mock_inverter_factory.return_value = mock_inverter - - mock_tariff.return_value = MagicMock() - mock_solar.return_value = MagicMock() - mock_consumption.return_value = MagicMock() - - # Create Batcontrol instance - misconfiguration is corrected at init + mock_inverter = core_dependencies bc = Batcontrol(mock_config) - # min_pv_charge_rate should have been clamped to max_pv_charge_rate at init assert bc.min_pv_charge_rate == 3000 - # Set any positive limit - should be clamped to max_pv_charge_rate (3000) bc.limit_battery_charge_rate(1000) - # Verify effective limit does not exceed max_pv_charge_rate mock_inverter.set_mode_limit_battery_charge.assert_called_once_with(3000) - @patch('batcontrol.core.tariff_factory.create_tarif_provider') - @patch('batcontrol.core.inverter_factory.create_inverter') - @patch('batcontrol.core.solar_factory.create_solar_provider') - @patch('batcontrol.core.consumption_factory.create_consumption') def test_api_set_mode_accepts_mode_8( - self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff, - mock_config): + self, mock_config, core_dependencies): """Test that api_set_mode accepts MODE_LIMIT_BATTERY_CHARGE_RATE""" - # Setup mocks - mock_inverter = MagicMock() - mock_inverter.max_pv_charge_rate = 3000 - mock_inverter.set_mode_limit_battery_charge = MagicMock() - mock_inverter.get_max_capacity = MagicMock(return_value=10000) - mock_inverter_factory.return_value = mock_inverter - - mock_tariff.return_value = MagicMock() - mock_solar.return_value = MagicMock() - mock_consumption.return_value = MagicMock() - - # Create Batcontrol instance bc = Batcontrol(mock_config) - # Set a valid limit first (otherwise default -1 will fall back to mode 10) bc._limit_battery_charge_rate = 2000 - - # Call api_set_mode with mode 8 bc.api_set_mode(MODE_LIMIT_BATTERY_CHARGE_RATE) - # Verify mode was set assert bc.last_mode == MODE_LIMIT_BATTERY_CHARGE_RATE assert bc.api_overwrite is True - @patch('batcontrol.core.tariff_factory.create_tarif_provider', autospec=True) - @patch('batcontrol.core.inverter_factory.create_inverter', autospec=True) - @patch('batcontrol.core.solar_factory.create_solar_provider', autospec=True) - @patch('batcontrol.core.consumption_factory.create_consumption', autospec=True) def test_api_set_limit_battery_charge_rate( - self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff, - mock_config): + self, mock_config, core_dependencies, mocker): """Test api_set_limit_battery_charge_rate updates the dynamic value""" - # Setup mocks - mock_inverter = MagicMock() - mock_inverter.max_pv_charge_rate = 3000 - mock_inverter.set_mode_limit_battery_charge = MagicMock() - mock_inverter.get_max_capacity = MagicMock(return_value=10000) - mock_inverter_factory.return_value = mock_inverter - - mock_tariff.return_value = MagicMock() - mock_solar.return_value = MagicMock() - mock_consumption.return_value = MagicMock() - - # Create Batcontrol instance + mock_inverter = core_dependencies bc = Batcontrol(mock_config) - bc.mqtt_api = MagicMock() + bc.mqtt_api = mocker.MagicMock() - # Call api_set_limit_battery_charge_rate bc.api_set_limit_battery_charge_rate(2500) - # Verify the value was stored and published without applying mode 8 assert bc._limit_battery_charge_rate == 2500 bc.mqtt_api.publish_limit_battery_charge_rate.assert_called_once_with(2500) mock_inverter.set_mode_limit_battery_charge.assert_not_called() - @patch('batcontrol.core.tariff_factory.create_tarif_provider', autospec=True) - @patch('batcontrol.core.inverter_factory.create_inverter', autospec=True) - @patch('batcontrol.core.solar_factory.create_solar_provider', autospec=True) - @patch('batcontrol.core.consumption_factory.create_consumption', autospec=True) def test_api_set_limit_applies_immediately_in_mode_8( - self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff, - mock_config): + self, mock_config, core_dependencies, mocker): """Test that changing limit applies immediately when in mode 8""" - # Setup mocks - mock_inverter = MagicMock() - mock_inverter.max_pv_charge_rate = 3000 - mock_inverter.set_mode_limit_battery_charge = MagicMock() - mock_inverter.get_max_capacity = MagicMock(return_value=10000) - mock_inverter_factory.return_value = mock_inverter - - mock_tariff.return_value = MagicMock() - mock_solar.return_value = MagicMock() - mock_consumption.return_value = MagicMock() - - # Create Batcontrol instance + mock_inverter = core_dependencies bc = Batcontrol(mock_config) - bc.mqtt_api = MagicMock() + bc.mqtt_api = mocker.MagicMock() - # Set mode to 8 first bc.limit_battery_charge_rate(1000) mock_inverter.set_mode_limit_battery_charge.reset_mock() bc.mqtt_api.reset_mock() - # Now change the limit bc.api_set_limit_battery_charge_rate(2000) - # Verify the new limit was applied immediately mock_inverter.set_mode_limit_battery_charge.assert_called_once_with(2000) bc.mqtt_api.publish_limit_battery_charge_rate.assert_called_once_with(2000)