|
1 | 1 | """Tests for MqttApi._handle_message, focusing on bytes payload decoding.""" |
2 | | -from unittest.mock import MagicMock, call |
| 2 | +from unittest.mock import MagicMock, call, patch |
| 3 | + |
| 4 | +import pytest |
3 | 5 |
|
4 | 6 | from batcontrol.core import Batcontrol |
5 | 7 | from batcontrol.logic import PeakShavingConfig |
@@ -498,6 +500,89 @@ def test_skips_publish_when_disconnected(self): |
498 | 500 | api.client.publish.assert_not_called() |
499 | 501 |
|
500 | 502 |
|
| 503 | +class TestTlsSetup: |
| 504 | + """MqttApi.__init__ must configure TLS correctly and reject bad configs.""" |
| 505 | + |
| 506 | + BASE_CONFIG = { |
| 507 | + 'topic': 'house/batcontrol', |
| 508 | + 'broker': 'localhost', |
| 509 | + 'port': 8883, |
| 510 | + 'retry_attempts': 1, |
| 511 | + 'retry_delay': 0, |
| 512 | + } |
| 513 | + |
| 514 | + def _make_config(self, **overrides): |
| 515 | + return {**self.BASE_CONFIG, **overrides} |
| 516 | + |
| 517 | + def test_tls_disabled_does_not_call_tls_set(self): |
| 518 | + with patch('batcontrol.mqtt_api.mqtt.Client') as MockClient: |
| 519 | + client = MockClient.return_value |
| 520 | + client.is_connected.return_value = True |
| 521 | + cfg = self._make_config(tls=False) |
| 522 | + MqttApi(cfg) |
| 523 | + client.tls_set.assert_not_called() |
| 524 | + |
| 525 | + def test_tls_true_calls_tls_set_with_cafile(self): |
| 526 | + with patch('batcontrol.mqtt_api.mqtt.Client') as MockClient: |
| 527 | + client = MockClient.return_value |
| 528 | + client.is_connected.return_value = True |
| 529 | + cfg = self._make_config( |
| 530 | + tls=True, |
| 531 | + cafile='/etc/ssl/ca.crt', |
| 532 | + ) |
| 533 | + MqttApi(cfg) |
| 534 | + client.tls_set.assert_called_once_with( |
| 535 | + ca_certs='/etc/ssl/ca.crt', |
| 536 | + certfile=None, |
| 537 | + keyfile=None, |
| 538 | + ciphers=None, |
| 539 | + ) |
| 540 | + |
| 541 | + def test_tls_true_passes_mutual_tls_params(self): |
| 542 | + with patch('batcontrol.mqtt_api.mqtt.Client') as MockClient: |
| 543 | + client = MockClient.return_value |
| 544 | + client.is_connected.return_value = True |
| 545 | + cfg = self._make_config( |
| 546 | + tls=True, |
| 547 | + cafile='/etc/ssl/ca.crt', |
| 548 | + certfile='/etc/ssl/client.crt', |
| 549 | + keyfile='/etc/ssl/client.key', |
| 550 | + ) |
| 551 | + MqttApi(cfg) |
| 552 | + client.tls_set.assert_called_once_with( |
| 553 | + ca_certs='/etc/ssl/ca.crt', |
| 554 | + certfile='/etc/ssl/client.crt', |
| 555 | + keyfile='/etc/ssl/client.key', |
| 556 | + ciphers=None, |
| 557 | + ) |
| 558 | + |
| 559 | + def test_tls_missing_cafile_raises_valueerror(self): |
| 560 | + with patch('batcontrol.mqtt_api.mqtt.Client'): |
| 561 | + cfg = self._make_config(tls=True) |
| 562 | + with pytest.raises(ValueError, match='cafile'): |
| 563 | + MqttApi(cfg) |
| 564 | + |
| 565 | + def test_tls_certfile_without_keyfile_raises_valueerror(self): |
| 566 | + with patch('batcontrol.mqtt_api.mqtt.Client'): |
| 567 | + cfg = self._make_config( |
| 568 | + tls=True, |
| 569 | + cafile='/etc/ssl/ca.crt', |
| 570 | + certfile='/etc/ssl/client.crt', |
| 571 | + ) |
| 572 | + with pytest.raises(ValueError, match='certfile and keyfile'): |
| 573 | + MqttApi(cfg) |
| 574 | + |
| 575 | + def test_tls_keyfile_without_certfile_raises_valueerror(self): |
| 576 | + with patch('batcontrol.mqtt_api.mqtt.Client'): |
| 577 | + cfg = self._make_config( |
| 578 | + tls=True, |
| 579 | + cafile='/etc/ssl/ca.crt', |
| 580 | + keyfile='/etc/ssl/client.key', |
| 581 | + ) |
| 582 | + with pytest.raises(ValueError, match='certfile and keyfile'): |
| 583 | + MqttApi(cfg) |
| 584 | + |
| 585 | + |
501 | 586 | class TestPublishSolarActive: |
502 | 587 | def test_publishes_true_to_correct_topic(self): |
503 | 588 | api = _make_solar_publish_stub() |
|
0 commit comments