Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 47 additions & 169 deletions tests/batcontrol/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +50 to +78

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)
Comment on lines 88 to 90

# 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)
Comment on lines 99 to 101

# 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)
Comment on lines 109 to 111

# 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)
Comment on lines 120 to 122

# 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
Comment on lines 131 to 133

# 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
Comment on lines 142 to 144

# 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)
Comment on lines 154 to 157

# 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)
Comment on lines 167 to 170
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)

Expand Down