From 439ab7ec57892b32b721303756142779e33864a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 Aug 2025 10:15:48 +0000 Subject: [PATCH 1/3] Initial plan From 66112ee1ac558cd48bf62fbfb3a771816c4998b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 Aug 2025 10:22:30 +0000 Subject: [PATCH 2/3] Implement fabric label 32-character limit with truncation and logging Co-authored-by: Apollon77 <11976694+Apollon77@users.noreply.github.com> --- matter_server/server/device_controller.py | 7 ++ tests/test_fabric_label_validation.py | 114 ++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 tests/test_fabric_label_validation.py diff --git a/matter_server/server/device_controller.py b/matter_server/server/device_controller.py index 1420d37a..9df276c1 100644 --- a/matter_server/server/device_controller.py +++ b/matter_server/server/device_controller.py @@ -296,6 +296,13 @@ def get_node(self, node_id: int) -> MatterNodeData: @api_command(APICommand.SET_DEFAULT_FABRIC_LABEL) async def set_default_fabric_label(self, label: str | None) -> None: """Set the default fabric label.""" + if label is not None and len(label) > 32: + LOGGER.info( + "Fabric label '%s' exceeds 32 characters, truncating to '%s'", + label, + label[:32], + ) + label = label[:32] self._default_fabric_label = label @api_command(APICommand.COMMISSION_WITH_CODE) diff --git a/tests/test_fabric_label_validation.py b/tests/test_fabric_label_validation.py new file mode 100644 index 00000000..409f8cf6 --- /dev/null +++ b/tests/test_fabric_label_validation.py @@ -0,0 +1,114 @@ +"""Test fabric label validation functionality.""" + +import asyncio +import logging +from unittest.mock import Mock, MagicMock + +import pytest + + +class MockDeviceController: + """Mock device controller with fabric label validation logic.""" + + def __init__(self): + self._default_fabric_label = None + self.logger = logging.getLogger(__name__) + + async def set_default_fabric_label(self, label: str | None) -> None: + """Set the default fabric label with validation.""" + if label is not None and len(label) > 32: + self.logger.info( + "Fabric label '%s' exceeds 32 characters, truncating to '%s'", + label, + label[:32], + ) + label = label[:32] + self._default_fabric_label = label + + +class TestFabricLabelValidation: + """Test fabric label length validation.""" + + @pytest.fixture + def mock_device_controller(self): + """Create a mock device controller for testing.""" + return MockDeviceController() + + @pytest.mark.asyncio + async def test_fabric_label_under_32_chars(self, mock_device_controller, caplog): + """Test that fabric labels under 32 characters are not modified.""" + label = "Short Label" + + with caplog.at_level(logging.INFO): + await mock_device_controller.set_default_fabric_label(label) + + assert mock_device_controller._default_fabric_label == label + assert not caplog.records # No log messages should be generated + + @pytest.mark.asyncio + async def test_fabric_label_exactly_32_chars(self, mock_device_controller, caplog): + """Test that fabric labels exactly 32 characters are not modified.""" + label = "A" * 32 # Exactly 32 characters + + with caplog.at_level(logging.INFO): + await mock_device_controller.set_default_fabric_label(label) + + assert mock_device_controller._default_fabric_label == label + assert len(mock_device_controller._default_fabric_label) == 32 + assert not caplog.records # No log messages should be generated + + @pytest.mark.asyncio + async def test_fabric_label_over_32_chars_truncated(self, mock_device_controller, caplog): + """Test that fabric labels over 32 characters are truncated.""" + long_label = "This is a very long fabric label that exceeds the 32 character limit" + expected_truncated = long_label[:32] + + with caplog.at_level(logging.INFO): + await mock_device_controller.set_default_fabric_label(long_label) + + assert mock_device_controller._default_fabric_label == expected_truncated + assert len(mock_device_controller._default_fabric_label) == 32 + + # Check that a log message was generated + assert len(caplog.records) == 1 + assert caplog.records[0].levelname == "INFO" + assert "exceeds 32 characters, truncating" in caplog.records[0].message + assert long_label in caplog.records[0].message + assert expected_truncated in caplog.records[0].message + + @pytest.mark.asyncio + async def test_fabric_label_none_value(self, mock_device_controller, caplog): + """Test that None values are handled properly.""" + with caplog.at_level(logging.INFO): + await mock_device_controller.set_default_fabric_label(None) + + assert mock_device_controller._default_fabric_label is None + assert not caplog.records # No log messages should be generated + + @pytest.mark.asyncio + async def test_fabric_label_empty_string(self, mock_device_controller, caplog): + """Test that empty strings are handled properly.""" + label = "" + + with caplog.at_level(logging.INFO): + await mock_device_controller.set_default_fabric_label(label) + + assert mock_device_controller._default_fabric_label == label + assert not caplog.records # No log messages should be generated + + @pytest.mark.asyncio + async def test_fabric_label_unicode_characters(self, mock_device_controller, caplog): + """Test that unicode characters are handled properly in truncation.""" + # Unicode string that's longer than 32 characters + unicode_label = "Café™ ★ 🏠 This is a unicode string that exceeds 32 chars" + expected_truncated = unicode_label[:32] + + with caplog.at_level(logging.INFO): + await mock_device_controller.set_default_fabric_label(unicode_label) + + assert mock_device_controller._default_fabric_label == expected_truncated + assert len(mock_device_controller._default_fabric_label) == 32 + + # Check that a log message was generated + assert len(caplog.records) == 1 + assert "exceeds 32 characters, truncating" in caplog.records[0].message \ No newline at end of file From e14a83e230e8bca86b973c0e368ac34e3be21cc0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 Aug 2025 10:38:44 +0000 Subject: [PATCH 3/3] Fix lint errors in fabric label validation tests - Add missing docstring to __init__ method - Remove unused imports (asyncio, Mock, MagicMock) - Add pylint disable for protected-access in test file - Fix formatting issues (blank lines, newline at end) - Ensure all ruff and pylint checks pass Co-authored-by: Apollon77 <11976694+Apollon77@users.noreply.github.com> --- tests/test_fabric_label_validation.py | 48 +++++++++++++++------------ 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/tests/test_fabric_label_validation.py b/tests/test_fabric_label_validation.py index 409f8cf6..bc609dcd 100644 --- a/tests/test_fabric_label_validation.py +++ b/tests/test_fabric_label_validation.py @@ -1,19 +1,19 @@ """Test fabric label validation functionality.""" +# pylint: disable=protected-access -import asyncio import logging -from unittest.mock import Mock, MagicMock import pytest class MockDeviceController: """Mock device controller with fabric label validation logic.""" - + def __init__(self): + """Initialize mock device controller.""" self._default_fabric_label = None self.logger = logging.getLogger(__name__) - + async def set_default_fabric_label(self, label: str | None) -> None: """Set the default fabric label with validation.""" if label is not None and len(label) > 32: @@ -38,10 +38,10 @@ def mock_device_controller(self): async def test_fabric_label_under_32_chars(self, mock_device_controller, caplog): """Test that fabric labels under 32 characters are not modified.""" label = "Short Label" - + with caplog.at_level(logging.INFO): await mock_device_controller.set_default_fabric_label(label) - + assert mock_device_controller._default_fabric_label == label assert not caplog.records # No log messages should be generated @@ -49,26 +49,30 @@ async def test_fabric_label_under_32_chars(self, mock_device_controller, caplog) async def test_fabric_label_exactly_32_chars(self, mock_device_controller, caplog): """Test that fabric labels exactly 32 characters are not modified.""" label = "A" * 32 # Exactly 32 characters - + with caplog.at_level(logging.INFO): await mock_device_controller.set_default_fabric_label(label) - + assert mock_device_controller._default_fabric_label == label assert len(mock_device_controller._default_fabric_label) == 32 assert not caplog.records # No log messages should be generated @pytest.mark.asyncio - async def test_fabric_label_over_32_chars_truncated(self, mock_device_controller, caplog): + async def test_fabric_label_over_32_chars_truncated( + self, mock_device_controller, caplog + ): """Test that fabric labels over 32 characters are truncated.""" - long_label = "This is a very long fabric label that exceeds the 32 character limit" + long_label = ( + "This is a very long fabric label that exceeds the 32 character limit" + ) expected_truncated = long_label[:32] - + with caplog.at_level(logging.INFO): await mock_device_controller.set_default_fabric_label(long_label) - + assert mock_device_controller._default_fabric_label == expected_truncated assert len(mock_device_controller._default_fabric_label) == 32 - + # Check that a log message was generated assert len(caplog.records) == 1 assert caplog.records[0].levelname == "INFO" @@ -81,7 +85,7 @@ async def test_fabric_label_none_value(self, mock_device_controller, caplog): """Test that None values are handled properly.""" with caplog.at_level(logging.INFO): await mock_device_controller.set_default_fabric_label(None) - + assert mock_device_controller._default_fabric_label is None assert not caplog.records # No log messages should be generated @@ -89,26 +93,28 @@ async def test_fabric_label_none_value(self, mock_device_controller, caplog): async def test_fabric_label_empty_string(self, mock_device_controller, caplog): """Test that empty strings are handled properly.""" label = "" - + with caplog.at_level(logging.INFO): await mock_device_controller.set_default_fabric_label(label) - + assert mock_device_controller._default_fabric_label == label assert not caplog.records # No log messages should be generated @pytest.mark.asyncio - async def test_fabric_label_unicode_characters(self, mock_device_controller, caplog): + async def test_fabric_label_unicode_characters( + self, mock_device_controller, caplog + ): """Test that unicode characters are handled properly in truncation.""" # Unicode string that's longer than 32 characters unicode_label = "Café™ ★ 🏠 This is a unicode string that exceeds 32 chars" expected_truncated = unicode_label[:32] - + with caplog.at_level(logging.INFO): await mock_device_controller.set_default_fabric_label(unicode_label) - + assert mock_device_controller._default_fabric_label == expected_truncated assert len(mock_device_controller._default_fabric_label) == 32 - + # Check that a log message was generated assert len(caplog.records) == 1 - assert "exceeds 32 characters, truncating" in caplog.records[0].message \ No newline at end of file + assert "exceeds 32 characters, truncating" in caplog.records[0].message