Automated code quality improvements: Black (Code Formatter): - Reformatted 30 files to match PEP 8 style - Consistent line length (100 chars) - Consistent quote style and spacing isort (Import Sorter): - Fixed import order in 30+ files - Grouped imports: stdlib → third-party → first-party → local - Consistent multi-line import formatting Ruff (Linter): - Auto-fixed 161 issues: * Modernized type hints (List → list, Optional[X] → X | None) * Removed unused imports (json, os, typing utilities) * Updated deprecated typing imports (typing.List → list) * Simplified type annotations with | operator (Python 3.10+) - Remaining 20 issues (acceptable): * 9x unused-method-argument (callback signatures, cannot be changed) * 2x bare-except (intentional for resilience) * 2x unused-variable (will be addressed in Phase 2) * Minor simplification opportunities (not critical) Code Quality Stats: - 32 files modified - +1749/-1589 lines (net cleanup) - Code is now consistent and formatted - Ready for gradual type safety improvements (Phase 3) All changes are non-functional - pure formatting and import organization.
104 lines
2.5 KiB
Python
104 lines
2.5 KiB
Python
"""
|
|
Test helper utilities for IoT Bridge tests.
|
|
|
|
This module provides utility functions for common test operations:
|
|
- Assertions
|
|
- Test data generation
|
|
- Mock helpers
|
|
"""
|
|
|
|
from typing import Any
|
|
|
|
|
|
def assert_event_valid(event: dict[str, Any]) -> None:
|
|
"""
|
|
Assert that an event dictionary has required fields.
|
|
|
|
Args:
|
|
event: Event dictionary to validate
|
|
|
|
Raises:
|
|
AssertionError: If required fields are missing
|
|
"""
|
|
required_fields = ["device_id", "event_type", "timestamp"]
|
|
for field in required_fields:
|
|
assert field in event, f"Event missing required field: {field}"
|
|
assert event[field], f"Event field '{field}' is empty"
|
|
|
|
|
|
def assert_mqtt_topic_valid(topic: str) -> None:
|
|
"""
|
|
Assert that an MQTT topic is valid.
|
|
|
|
Args:
|
|
topic: MQTT topic string
|
|
|
|
Raises:
|
|
AssertionError: If topic is invalid
|
|
"""
|
|
assert topic, "Topic cannot be empty"
|
|
assert not topic.startswith("/"), "Topic cannot start with /"
|
|
assert not topic.endswith("/"), "Topic cannot end with /"
|
|
assert "//" not in topic, "Topic cannot contain consecutive slashes"
|
|
|
|
|
|
def create_mock_mqtt_message(topic: str, payload: dict[str, Any]) -> Any:
|
|
"""
|
|
Create a mock MQTT message object.
|
|
|
|
Args:
|
|
topic: MQTT topic
|
|
payload: Message payload (will be converted to bytes)
|
|
|
|
Returns:
|
|
Mock MQTT message object
|
|
"""
|
|
import json
|
|
from unittest.mock import MagicMock
|
|
|
|
msg = MagicMock()
|
|
msg.topic = topic
|
|
msg.payload = json.dumps(payload).encode("utf-8")
|
|
return msg
|
|
|
|
|
|
def create_device_config_dict(
|
|
device_id: str = "test_device",
|
|
topic: str = "test/device/#",
|
|
parser: str = "shelly",
|
|
) -> dict[str, Any]:
|
|
"""
|
|
Create a device configuration dictionary.
|
|
|
|
Args:
|
|
device_id: Device identifier
|
|
topic: MQTT topic pattern
|
|
parser: Parser type
|
|
|
|
Returns:
|
|
Device configuration dictionary
|
|
"""
|
|
return {"device_id": device_id, "topic": topic, "parser": parser}
|
|
|
|
|
|
def wait_for_condition(condition_fn, timeout: float = 5.0, interval: float = 0.1) -> bool:
|
|
"""
|
|
Wait for a condition to become true.
|
|
|
|
Args:
|
|
condition_fn: Function that returns bool
|
|
timeout: Maximum wait time in seconds
|
|
interval: Check interval in seconds
|
|
|
|
Returns:
|
|
True if condition met, False if timeout
|
|
"""
|
|
import time
|
|
|
|
start_time = time.time()
|
|
while time.time() - start_time < timeout:
|
|
if condition_fn():
|
|
return True
|
|
time.sleep(interval)
|
|
return False
|