odoo_mqtt/iot_bridge/tests/unit/test_dependencies.py
matthias.lotz ff4ef2f563 Phase 3: Complete type safety & logging unification (3.1-3.2)
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
2026-02-18 23:54:27 +01:00

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}