TasmotaManager/test_get_device_hostname.py
Mike Geppert 126cd39555 Major code improvements and bug fixes:
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
2025-08-08 19:04:33 -05:00

253 lines
8.2 KiB
Python

#!/usr/bin/env python3
"""
Test script to verify the get_device_hostname function in TasmotaManager.py.
This script tests:
1. Successful hostname retrieval
2. Empty hostname in response
3. Invalid JSON response
4. Non-200 status code
5. Network error (connection failure)
6. Timeout error
"""
import logging
import unittest
import requests
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 TestGetDeviceHostname(unittest.TestCase):
"""Test cases for the get_device_hostname function."""
def setUp(self):
"""Set up test environment."""
self.discovery = TasmotaDiscovery(debug=True)
# Create a minimal config to initialize the TasmotaDiscovery instance
self.discovery.config = {
'unifi': {
'network_filter': {}
}
}
@patch('requests.get')
def test_successful_hostname_retrieval(self, mock_get):
"""Test successful hostname retrieval."""
# Mock response for Status 5 command
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
'StatusNET': {
'Hostname': 'test-device'
}
}
mock_get.return_value = mock_response
# Call the function
hostname, success = self.discovery.get_device_hostname("192.168.1.100")
# Verify results
self.assertEqual(hostname, 'test-device')
self.assertTrue(success)
# Verify that requests.get was called with the correct URL and timeout
mock_get.assert_called_once_with("http://192.168.1.100/cm?cmnd=Status%205", timeout=5)
logger.info("Test for successful hostname retrieval passed")
@patch('requests.get')
def test_empty_hostname_in_response(self, mock_get):
"""Test empty hostname in response."""
# Mock response for Status 5 command
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
'StatusNET': {
'Hostname': ''
}
}
mock_get.return_value = mock_response
# Call the function
hostname, success = self.discovery.get_device_hostname("192.168.1.100")
# Verify results
self.assertEqual(hostname, '')
self.assertFalse(success)
logger.info("Test for empty hostname in response passed")
@patch('requests.get')
def test_missing_hostname_in_response(self, mock_get):
"""Test missing hostname in response."""
# Mock response for Status 5 command
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
'StatusNET': {} # No Hostname key
}
mock_get.return_value = mock_response
# Call the function
hostname, success = self.discovery.get_device_hostname("192.168.1.100")
# Verify results
self.assertEqual(hostname, '')
self.assertFalse(success)
logger.info("Test for missing hostname in response passed")
@patch('requests.get')
def test_invalid_json_response(self, mock_get):
"""Test invalid JSON response."""
# 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
# Call the function
hostname, success = self.discovery.get_device_hostname("192.168.1.100")
# Verify results
self.assertEqual(hostname, '')
self.assertFalse(success)
logger.info("Test for invalid JSON response passed")
@patch('requests.get')
def test_non_200_status_code(self, mock_get):
"""Test non-200 status code."""
# Mock response for Status 5 command
mock_response = MagicMock()
mock_response.status_code = 404
mock_get.return_value = mock_response
# Call the function
hostname, success = self.discovery.get_device_hostname("192.168.1.100")
# Verify results
self.assertEqual(hostname, '')
self.assertFalse(success)
logger.info("Test for non-200 status code passed")
@patch('requests.get')
def test_connection_error(self, mock_get):
"""Test connection error."""
# Mock requests.get to raise a connection error
mock_get.side_effect = requests.exceptions.ConnectionError("Connection refused")
# Call the function
hostname, success = self.discovery.get_device_hostname("192.168.1.100")
# Verify results
self.assertEqual(hostname, '')
self.assertFalse(success)
logger.info("Test for connection error passed")
@patch('requests.get')
def test_timeout_error(self, mock_get):
"""Test timeout error."""
# Mock requests.get to raise a timeout error
mock_get.side_effect = requests.exceptions.Timeout("Request timed out")
# Call the function
hostname, success = self.discovery.get_device_hostname("192.168.1.100")
# Verify results
self.assertEqual(hostname, '')
self.assertFalse(success)
logger.info("Test for timeout error passed")
@patch('requests.get')
def test_custom_timeout(self, mock_get):
"""Test custom timeout parameter."""
# Mock response for Status 5 command
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
'StatusNET': {
'Hostname': 'test-device'
}
}
mock_get.return_value = mock_response
# Call the function with custom timeout
hostname, success = self.discovery.get_device_hostname("192.168.1.100", timeout=10)
# Verify results
self.assertEqual(hostname, 'test-device')
self.assertTrue(success)
# Verify that requests.get was called with the custom timeout
mock_get.assert_called_once_with("http://192.168.1.100/cm?cmnd=Status%205", timeout=10)
logger.info("Test for custom timeout passed")
@patch('requests.get')
def test_with_device_name(self, mock_get):
"""Test with device_name parameter."""
# Mock response for Status 5 command
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
'StatusNET': {
'Hostname': 'test-device'
}
}
mock_get.return_value = mock_response
# Call the function with device_name
hostname, success = self.discovery.get_device_hostname("192.168.1.100", device_name="Living Room Light")
# Verify results
self.assertEqual(hostname, 'test-device')
self.assertTrue(success)
logger.info("Test with device_name parameter passed")
@patch('requests.get')
def test_with_log_level(self, mock_get):
"""Test with log_level parameter."""
# Mock response for Status 5 command
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
'StatusNET': {
'Hostname': 'test-device'
}
}
mock_get.return_value = mock_response
# Call the function with log_level
hostname, success = self.discovery.get_device_hostname("192.168.1.100", log_level="info")
# Verify results
self.assertEqual(hostname, 'test-device')
self.assertTrue(success)
logger.info("Test with log_level parameter passed")
def main():
"""Run the tests."""
unittest.main()
if __name__ == "__main__":
main()