Phase 3.1: Type Safety - Add bridge_types.py for shared type aliases (EventDict, PowerWatts, Timestamp, DeviceID) - Define protocols for callbacks and message parsers - Strict type annotations on all core modules (session_detector, event_queue, device_manager) - Fix Optional handling and type guards throughout codebase - Achieve full mypy compliance: 0 errors across 47 source files Phase 3.2: Logging Unification - Migrate from stdlib logging to pure structlog across all runtime modules - Convert all logs to structured event+fields format (snake_case event names) - Remove f-string and printf-style logger calls - Add contextvars support for per-request correlation - Implement FastAPI middleware to bind request_id, http_method, http_path - Propagate X-Request-ID header in responses - Remove stdlib logging imports except setup layer (utils/logging.py) - Ensure log-level consistency across all modules Files Modified: - iot_bridge/bridge_types.py (new) - Central type definitions - iot_bridge/core/* - Type safety and logging unification - iot_bridge/clients/* - Structured logging with request context - iot_bridge/parsers/* - Type-safe parsing with structured logs - iot_bridge/utils/logging.py - Pure structlog setup with contextvars - iot_bridge/api/server.py - Added request correlation middleware - iot_bridge/tests/* - Test fixtures updated for type safety - iot_bridge/OPTIMIZATION_PLAN.md - Phase 3 status updated Validation: - mypy . → 0 errors (47 files) - All unit tests pass - Runtime behavior unchanged - API response headers include X-Request-ID
77 lines
2.4 KiB
Python
77 lines
2.4 KiB
Python
"""Unit tests for dependency injection runtime wiring."""
|
|
|
|
from types import SimpleNamespace
|
|
from typing import Any, cast
|
|
|
|
from dependencies import RuntimeContainer, build_runtime_context, create_service_manager
|
|
|
|
|
|
def test_create_service_manager_uses_factory_with_boot_config_fields():
|
|
"""create_service_manager should pass boot config fields to injected factory."""
|
|
calls = {}
|
|
|
|
def fake_service_manager_factory(**kwargs):
|
|
calls.update(kwargs)
|
|
return "service-manager-instance"
|
|
|
|
boot_config = SimpleNamespace(
|
|
config={"some": "config"},
|
|
logger="logger-instance",
|
|
bridge_port=8080,
|
|
bridge_token="token-123",
|
|
)
|
|
|
|
result = cast(
|
|
Any,
|
|
create_service_manager(
|
|
boot_config=cast(Any, boot_config),
|
|
service_manager_factory=fake_service_manager_factory,
|
|
),
|
|
)
|
|
|
|
assert result == "service-manager-instance"
|
|
assert calls["config"] == {"some": "config"}
|
|
assert calls["logger"] == "logger-instance"
|
|
assert calls["bridge_port"] == 8080
|
|
assert calls["bridge_token"] == "token-123"
|
|
|
|
|
|
def test_build_runtime_context_uses_injected_container_factories():
|
|
"""Runtime context should be fully constructed from injected container factories."""
|
|
boot_config = SimpleNamespace(
|
|
config={"bridge": "config"},
|
|
logger="logger",
|
|
bridge_port=9000,
|
|
bridge_token="abc",
|
|
)
|
|
|
|
called = {"bootstrap": 0, "mqtt": 0, "service": 0}
|
|
|
|
def fake_bootstrap_factory():
|
|
called["bootstrap"] += 1
|
|
return boot_config
|
|
|
|
def fake_mqtt_factory(config):
|
|
called["mqtt"] += 1
|
|
assert config == {"bridge": "config"}
|
|
return {"broker": "test-broker", "port": 1883}
|
|
|
|
def fake_service_manager_factory(**kwargs):
|
|
called["service"] += 1
|
|
assert kwargs["bridge_port"] == 9000
|
|
assert kwargs["bridge_token"] == "abc"
|
|
return "service-manager"
|
|
|
|
container = RuntimeContainer(
|
|
bootstrap_factory=fake_bootstrap_factory,
|
|
mqtt_config_factory=fake_mqtt_factory,
|
|
service_manager_factory=fake_service_manager_factory,
|
|
)
|
|
|
|
runtime = cast(Any, build_runtime_context(container))
|
|
|
|
assert runtime.boot_config is boot_config
|
|
assert runtime.service_manager == "service-manager"
|
|
assert runtime.mqtt_config == {"broker": "test-broker", "port": 1883}
|
|
assert called == {"bootstrap": 1, "mqtt": 1, "service": 1}
|