1. Restructured configuration: Moved config_other and console to top level 2. Added common _match_pattern function for regex pattern matching 3. Implemented Unifi Hostname bug fix in is_hostname_unknown 4. Created common get_device_hostname function to eliminate code duplication 5. Added comprehensive test scripts for all new functionality 6. Added detailed documentation for all changes
237 lines
9.6 KiB
Python
237 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Test script to verify the Unifi Hostname bug fix in the is_hostname_unknown function.
|
|
|
|
This script tests:
|
|
1. A device affected by the Unifi Hostname bug (UniFi-reported hostname matches unknown patterns,
|
|
but self-reported hostname doesn't)
|
|
2. A device not affected by the bug (both hostnames match or don't match unknown patterns)
|
|
3. Various combinations of parameters (with/without from_unifi_os, with/without IP)
|
|
"""
|
|
|
|
import logging
|
|
import unittest
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.DEBUG,
|
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Import TasmotaManager class
|
|
import sys
|
|
import os
|
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
from TasmotaManager import TasmotaDiscovery
|
|
|
|
class TestUnifiHostnameBugFix(unittest.TestCase):
|
|
"""Test cases for the Unifi Hostname bug fix."""
|
|
|
|
def setUp(self):
|
|
"""Set up test environment."""
|
|
self.discovery = TasmotaDiscovery(debug=True)
|
|
|
|
# Create a mock config
|
|
self.discovery.config = {
|
|
'unifi': {
|
|
'network_filter': {
|
|
'test_network': {
|
|
'unknown_device_patterns': [
|
|
"^tasmota_*",
|
|
"^tasmota-*",
|
|
"^esp-*",
|
|
"^ESP-*"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Define test patterns
|
|
self.test_patterns = [
|
|
"^tasmota_*",
|
|
"^tasmota-*",
|
|
"^esp-*",
|
|
"^ESP-*"
|
|
]
|
|
|
|
@patch('requests.get')
|
|
def test_bug_affected_device(self, mock_get):
|
|
"""Test a device affected by the Unifi Hostname bug."""
|
|
# Mock response for Status 5 command
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
'StatusNET': {
|
|
'Hostname': 'my_proper_device' # Self-reported hostname doesn't match unknown patterns
|
|
}
|
|
}
|
|
mock_get.return_value = mock_response
|
|
|
|
# Test with a hostname that matches unknown patterns (as reported by UniFi)
|
|
# but with a self-reported hostname that doesn't match unknown patterns
|
|
result = self.discovery.is_hostname_unknown(
|
|
hostname="tasmota_123", # UniFi-reported hostname (matches unknown patterns)
|
|
patterns=self.test_patterns,
|
|
from_unifi_os=True, # Enable Unifi Hostname bug handling
|
|
ip="192.168.1.100" # Provide IP to query the device
|
|
)
|
|
|
|
# The function should return False because the self-reported hostname doesn't match unknown patterns
|
|
self.assertFalse(result)
|
|
|
|
# Verify that requests.get was called with the correct URL
|
|
mock_get.assert_called_once_with("http://192.168.1.100/cm?cmnd=Status%205", timeout=5)
|
|
|
|
logger.info("Test for bug-affected device passed")
|
|
|
|
@patch('requests.get')
|
|
def test_non_bug_affected_device_both_match(self, mock_get):
|
|
"""Test a device not affected by the bug (both hostnames match unknown patterns)."""
|
|
# Mock response for Status 5 command
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
'StatusNET': {
|
|
'Hostname': 'tasmota_456' # Self-reported hostname matches unknown patterns
|
|
}
|
|
}
|
|
mock_get.return_value = mock_response
|
|
|
|
# Test with a hostname that matches unknown patterns (as reported by UniFi)
|
|
# and with a self-reported hostname that also matches unknown patterns
|
|
result = self.discovery.is_hostname_unknown(
|
|
hostname="tasmota_123", # UniFi-reported hostname (matches unknown patterns)
|
|
patterns=self.test_patterns,
|
|
from_unifi_os=True, # Enable Unifi Hostname bug handling
|
|
ip="192.168.1.100" # Provide IP to query the device
|
|
)
|
|
|
|
# The function should return True because both hostnames match unknown patterns
|
|
self.assertTrue(result)
|
|
|
|
# Verify that requests.get was called with the correct URL
|
|
mock_get.assert_called_once_with("http://192.168.1.100/cm?cmnd=Status%205", timeout=5)
|
|
|
|
logger.info("Test for non-bug-affected device (both match) passed")
|
|
|
|
@patch('requests.get')
|
|
def test_non_bug_affected_device_both_dont_match(self, mock_get):
|
|
"""Test a device not affected by the bug (both hostnames don't match unknown patterns)."""
|
|
# Mock response for Status 5 command
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
'StatusNET': {
|
|
'Hostname': 'my_proper_device' # Self-reported hostname doesn't match unknown patterns
|
|
}
|
|
}
|
|
mock_get.return_value = mock_response
|
|
|
|
# Test with a hostname that doesn't match unknown patterns (as reported by UniFi)
|
|
# and with a self-reported hostname that also doesn't match unknown patterns
|
|
result = self.discovery.is_hostname_unknown(
|
|
hostname="my_device", # UniFi-reported hostname (doesn't match unknown patterns)
|
|
patterns=self.test_patterns,
|
|
from_unifi_os=True, # Enable Unifi Hostname bug handling
|
|
ip="192.168.1.100" # Provide IP to query the device
|
|
)
|
|
|
|
# The function should return False because neither hostname matches unknown patterns
|
|
self.assertFalse(result)
|
|
|
|
# Verify that requests.get was called with the correct URL
|
|
mock_get.assert_called_once_with("http://192.168.1.100/cm?cmnd=Status%205", timeout=5)
|
|
|
|
logger.info("Test for non-bug-affected device (both don't match) passed")
|
|
|
|
def test_without_from_unifi_os(self):
|
|
"""Test without the from_unifi_os parameter."""
|
|
# Test with a hostname that matches unknown patterns
|
|
result = self.discovery.is_hostname_unknown(
|
|
hostname="tasmota_123", # Matches unknown patterns
|
|
patterns=self.test_patterns,
|
|
from_unifi_os=False, # Disable Unifi Hostname bug handling
|
|
ip="192.168.1.100" # Provide IP (should be ignored since from_unifi_os is False)
|
|
)
|
|
|
|
# The function should return True because the hostname matches unknown patterns
|
|
# and from_unifi_os is False, so no bug handling is performed
|
|
self.assertTrue(result)
|
|
|
|
logger.info("Test without from_unifi_os passed")
|
|
|
|
def test_without_ip(self):
|
|
"""Test without the IP parameter."""
|
|
# Test with a hostname that matches unknown patterns
|
|
result = self.discovery.is_hostname_unknown(
|
|
hostname="tasmota_123", # Matches unknown patterns
|
|
patterns=self.test_patterns,
|
|
from_unifi_os=True, # Enable Unifi Hostname bug handling
|
|
ip=None # No IP provided, so can't query the device
|
|
)
|
|
|
|
# The function should return True because the hostname matches unknown patterns
|
|
# and no IP is provided, so no bug handling is performed
|
|
self.assertTrue(result)
|
|
|
|
logger.info("Test without IP passed")
|
|
|
|
@patch('requests.get')
|
|
def test_request_exception(self, mock_get):
|
|
"""Test handling of request exceptions."""
|
|
# Mock requests.get to raise an exception
|
|
mock_get.side_effect = Exception("Test exception")
|
|
|
|
# Test with a hostname that matches unknown patterns
|
|
result = self.discovery.is_hostname_unknown(
|
|
hostname="tasmota_123", # Matches unknown patterns
|
|
patterns=self.test_patterns,
|
|
from_unifi_os=True, # Enable Unifi Hostname bug handling
|
|
ip="192.168.1.100" # Provide IP to query the device
|
|
)
|
|
|
|
# The function should return True because the hostname matches unknown patterns
|
|
# and the request failed, so no bug handling is performed
|
|
self.assertTrue(result)
|
|
|
|
# Verify that requests.get was called with the correct URL
|
|
mock_get.assert_called_once_with("http://192.168.1.100/cm?cmnd=Status%205", timeout=5)
|
|
|
|
logger.info("Test for request exception passed")
|
|
|
|
@patch('requests.get')
|
|
def test_invalid_json_response(self, mock_get):
|
|
"""Test handling of invalid JSON responses."""
|
|
# Mock response for Status 5 command
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.side_effect = ValueError("Invalid JSON")
|
|
mock_get.return_value = mock_response
|
|
|
|
# Test with a hostname that matches unknown patterns
|
|
result = self.discovery.is_hostname_unknown(
|
|
hostname="tasmota_123", # Matches unknown patterns
|
|
patterns=self.test_patterns,
|
|
from_unifi_os=True, # Enable Unifi Hostname bug handling
|
|
ip="192.168.1.100" # Provide IP to query the device
|
|
)
|
|
|
|
# The function should return True because the hostname matches unknown patterns
|
|
# and the JSON parsing failed, so no bug handling is performed
|
|
self.assertTrue(result)
|
|
|
|
# Verify that requests.get was called with the correct URL
|
|
mock_get.assert_called_once_with("http://192.168.1.100/cm?cmnd=Status%205", timeout=5)
|
|
|
|
logger.info("Test for invalid JSON response passed")
|
|
|
|
def main():
|
|
"""Run the tests."""
|
|
unittest.main()
|
|
|
|
if __name__ == "__main__":
|
|
main() |