mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
- Split documentation into separate User and Developer sites - Migrated existing docs to proper Docusaurus structure - Added custom Tibber-themed header logos (light + dark mode variants) - Implemented custom color scheme matching integration branding - Hero gradient: Cyan → Dark Cyan → Gold - Removed standard Docusaurus purple/green theme - Integrated Giscus comments system for community collaboration - User docs: Comments enabled on guides, examples, FAQ - User docs: Comments disabled on reference pages (glossary, sensors, troubleshooting) - Developer docs: No comments (GitHub Issues/PRs preferred) - Added categorized sidebars with emoji navigation - Created 8 new placeholder documentation pages - Fixed image paths for baseUrl compatibility (local + GitHub Pages) - Escaped MDX special characters in performance metrics - Added GitHub Actions workflow for automated deployment - Created helper scripts: dev-user, dev-developer, build-all Breaking changes: - Moved /docs/user/*.md to /docs/user/docs/*.md - Moved /docs/development/*.md to /docs/developer/docs/*.md
286 lines
10 KiB
Markdown
286 lines
10 KiB
Markdown
---
|
||
comments: false
|
||
---
|
||
|
||
# Critical Behavior Patterns - Testing Guide
|
||
|
||
**Purpose:** This documentation lists essential behavior patterns that must be tested to ensure production-quality code and prevent resource leaks.
|
||
|
||
**Last Updated:** 2025-11-22
|
||
**Test Coverage:** 41 tests implemented (100% of critical patterns)
|
||
|
||
## 🎯 Why Are These Tests Critical?
|
||
|
||
Home Assistant integrations run **continuously** in the background. Resource leaks lead to:
|
||
- **Memory Leaks**: RAM usage grows over days/weeks until HA becomes unstable
|
||
- **Callback Leaks**: Listeners remain registered after entity removal → CPU load increases
|
||
- **Timer Leaks**: Timers continue running after unload → unnecessary background tasks
|
||
- **File Handle Leaks**: Storage files remain open → system resources exhausted
|
||
|
||
## ✅ Test Categories
|
||
|
||
### 1. Resource Cleanup (Memory Leak Prevention)
|
||
|
||
**File:** `tests/test_resource_cleanup.py`
|
||
|
||
#### 1.1 Listener Cleanup ✅
|
||
|
||
**What is tested:**
|
||
- Time-sensitive listeners are correctly removed (`async_add_time_sensitive_listener()`)
|
||
- Minute-update listeners are correctly removed (`async_add_minute_update_listener()`)
|
||
- Lifecycle callbacks are correctly unregistered (`register_lifecycle_callback()`)
|
||
- Sensor cleanup removes ALL registered listeners
|
||
- Binary sensor cleanup removes ALL registered listeners
|
||
|
||
**Why critical:**
|
||
- Each registered listener holds references to Entity + Coordinator
|
||
- Without cleanup: Entities are not freed by GC → Memory Leak
|
||
- With 80+ sensors × 3 listener types = 240+ callbacks that must be cleanly removed
|
||
|
||
**Code Locations:**
|
||
- `coordinator/listeners.py` → `async_add_time_sensitive_listener()`, `async_add_minute_update_listener()`
|
||
- `coordinator/core.py` → `register_lifecycle_callback()`
|
||
- `sensor/core.py` → `async_will_remove_from_hass()`
|
||
- `binary_sensor/core.py` → `async_will_remove_from_hass()`
|
||
|
||
#### 1.2 Timer Cleanup ✅
|
||
|
||
**What is tested:**
|
||
- Quarter-hour timer is cancelled and reference cleared
|
||
- Minute timer is cancelled and reference cleared
|
||
- Both timers are cancelled together
|
||
- Cleanup works even when timers are `None`
|
||
|
||
**Why critical:**
|
||
- Uncancelled timers continue running after integration unload
|
||
- HA's `async_track_utc_time_change()` creates persistent callbacks
|
||
- Without cleanup: Timers keep firing → CPU load + unnecessary coordinator updates
|
||
|
||
**Code Locations:**
|
||
- `coordinator/listeners.py` → `cancel_timers()`
|
||
- `coordinator/core.py` → `async_shutdown()`
|
||
|
||
#### 1.3 Config Entry Cleanup ✅
|
||
|
||
**What is tested:**
|
||
- Options update listener is registered via `async_on_unload()`
|
||
- Cleanup function is correctly passed to `async_on_unload()`
|
||
|
||
**Why critical:**
|
||
- `entry.add_update_listener()` registers permanent callback
|
||
- Without `async_on_unload()`: Listener remains active after reload → duplicate updates
|
||
- Pattern: `entry.async_on_unload(entry.add_update_listener(handler))`
|
||
|
||
**Code Locations:**
|
||
- `coordinator/core.py` → `__init__()` (listener registration)
|
||
- `__init__.py` → `async_unload_entry()`
|
||
|
||
### 2. Cache Invalidation ✅
|
||
|
||
**File:** `tests/test_resource_cleanup.py`
|
||
|
||
#### 2.1 Config Cache Invalidation
|
||
|
||
**What is tested:**
|
||
- DataTransformer config cache is invalidated on options change
|
||
- PeriodCalculator config + period cache is invalidated
|
||
- Trend calculator cache is cleared on coordinator update
|
||
|
||
**Why critical:**
|
||
- Stale config → Sensors use old user settings
|
||
- Stale period cache → Incorrect best/peak price periods
|
||
- Stale trend cache → Outdated trend analysis
|
||
|
||
**Code Locations:**
|
||
- `coordinator/data_transformation.py` → `invalidate_config_cache()`
|
||
- `coordinator/periods.py` → `invalidate_config_cache()`
|
||
- `sensor/calculators/trend.py` → `clear_trend_cache()`
|
||
|
||
### 3. Storage Cleanup ✅
|
||
|
||
**File:** `tests/test_resource_cleanup.py` + `tests/test_coordinator_shutdown.py`
|
||
|
||
#### 3.1 Persistent Storage Removal
|
||
|
||
**What is tested:**
|
||
- Storage file is deleted on config entry removal
|
||
- Cache is saved on shutdown (no data loss)
|
||
|
||
**Why critical:**
|
||
- Without storage removal: Old files remain after uninstallation
|
||
- Without cache save on shutdown: Data loss on HA restart
|
||
- Storage path: `.storage/tibber_prices.{entry_id}`
|
||
|
||
**Code Locations:**
|
||
- `__init__.py` → `async_remove_entry()`
|
||
- `coordinator/core.py` → `async_shutdown()`
|
||
|
||
### 4. Timer Scheduling ✅
|
||
|
||
**File:** `tests/test_timer_scheduling.py`
|
||
|
||
**What is tested:**
|
||
- Quarter-hour timer is registered with correct parameters
|
||
- Minute timer is registered with correct parameters
|
||
- Timers can be re-scheduled (override old timer)
|
||
- Midnight turnover detection works correctly
|
||
|
||
**Why critical:**
|
||
- Wrong timer parameters → Entities update at wrong times
|
||
- Without timer override on re-schedule → Multiple parallel timers → Performance problem
|
||
|
||
### 5. Sensor-to-Timer Assignment ✅
|
||
|
||
**File:** `tests/test_sensor_timer_assignment.py`
|
||
|
||
**What is tested:**
|
||
- All `TIME_SENSITIVE_ENTITY_KEYS` are valid entity keys
|
||
- All `MINUTE_UPDATE_ENTITY_KEYS` are valid entity keys
|
||
- Both lists are disjoint (no overlap)
|
||
- Sensor and binary sensor platforms are checked
|
||
|
||
**Why critical:**
|
||
- Wrong timer assignment → Sensors update at wrong times
|
||
- Overlap → Duplicate updates → Performance problem
|
||
|
||
## 🚨 Additional Analysis (Nice-to-Have Patterns)
|
||
|
||
These patterns were analyzed and classified as **not critical**:
|
||
|
||
### 6. Async Task Management
|
||
|
||
**Current Status:** Fire-and-forget pattern for short tasks
|
||
- `sensor/core.py` → Chart data refresh (short-lived, max 1-2 seconds)
|
||
- `coordinator/core.py` → Cache storage (short-lived, max 100ms)
|
||
|
||
**Why no tests needed:**
|
||
- No long-running tasks (all < 2 seconds)
|
||
- HA's event loop handles short tasks automatically
|
||
- Task exceptions are already logged
|
||
|
||
**If needed:** `_chart_refresh_task` tracking + cancel in `async_will_remove_from_hass()`
|
||
|
||
### 7. API Session Cleanup
|
||
|
||
**Current Status:** ✅ Correctly implemented
|
||
- `async_get_clientsession(hass)` is used (shared session)
|
||
- No new sessions are created
|
||
- HA manages session lifecycle automatically
|
||
|
||
**Code:** `api/client.py` + `__init__.py`
|
||
|
||
### 8. Translation Cache Memory
|
||
|
||
**Current Status:** ✅ Bounded cache
|
||
- Max ~5-10 languages × 5KB = 50KB total
|
||
- Module-level cache without re-loading
|
||
- Practically no memory issue
|
||
|
||
**Code:** `const.py` → `_TRANSLATIONS_CACHE`, `_STANDARD_TRANSLATIONS_CACHE`
|
||
|
||
### 9. Coordinator Data Structure Integrity
|
||
|
||
**Current Status:** Manually tested via `./scripts/develop`
|
||
- Midnight turnover works correctly (observed over several days)
|
||
- Missing keys are handled via `.get()` with defaults
|
||
- 80+ sensors access `coordinator.data` without errors
|
||
|
||
**Structure:**
|
||
```python
|
||
coordinator.data = {
|
||
"user_data": {...},
|
||
"priceInfo": [...], # Flat list of all enriched intervals
|
||
"currency": "EUR" # Top-level for easy access
|
||
}
|
||
```
|
||
|
||
### 10. Service Response Memory
|
||
|
||
**Current Status:** HA's response lifecycle
|
||
- HA automatically frees service responses after return
|
||
- ApexCharts ~20KB response is one-time per call
|
||
- No response accumulation in integration code
|
||
|
||
**Code:** `services/apexcharts.py`
|
||
|
||
## 📊 Test Coverage Status
|
||
|
||
### ✅ Implemented Tests (41 total)
|
||
|
||
| Category | Status | Tests | File | Coverage |
|
||
|----------|--------|-------|------|----------|
|
||
| Listener Cleanup | ✅ | 5 | `test_resource_cleanup.py` | 100% |
|
||
| Timer Cleanup | ✅ | 4 | `test_resource_cleanup.py` | 100% |
|
||
| Config Entry Cleanup | ✅ | 1 | `test_resource_cleanup.py` | 100% |
|
||
| Cache Invalidation | ✅ | 3 | `test_resource_cleanup.py` | 100% |
|
||
| Storage Cleanup | ✅ | 1 | `test_resource_cleanup.py` | 100% |
|
||
| Storage Persistence | ✅ | 2 | `test_coordinator_shutdown.py` | 100% |
|
||
| Timer Scheduling | ✅ | 8 | `test_timer_scheduling.py` | 100% |
|
||
| Sensor-Timer Assignment | ✅ | 17 | `test_sensor_timer_assignment.py` | 100% |
|
||
| **TOTAL** | **✅** | **41** | | **100% (critical)** |
|
||
|
||
### 📋 Analyzed but Not Implemented (Nice-to-Have)
|
||
|
||
| Category | Status | Rationale |
|
||
|----------|--------|-----------|
|
||
| Async Task Management | 📋 | Fire-and-forget pattern used (no long-running tasks) |
|
||
| API Session Cleanup | ✅ | Pattern correct (`async_get_clientsession` used) |
|
||
| Translation Cache | ✅ | Cache size bounded (~50KB max for 10 languages) |
|
||
| Data Structure Integrity | 📋 | Would add test time without finding real issues |
|
||
| Service Response Memory | 📋 | HA automatically frees service responses |
|
||
|
||
**Legend:**
|
||
- ✅ = Fully tested or pattern verified correct
|
||
- 📋 = Analyzed, low priority for testing (no known issues)
|
||
|
||
## 🎯 Development Status
|
||
|
||
### ✅ All Critical Patterns Tested
|
||
|
||
All essential memory leak prevention patterns are covered by 41 tests:
|
||
- ✅ Listeners are correctly removed (no callback leaks)
|
||
- ✅ Timers are cancelled (no background task leaks)
|
||
- ✅ Config entry cleanup works (no dangling listeners)
|
||
- ✅ Caches are invalidated (no stale data issues)
|
||
- ✅ Storage is saved and cleaned up (no data loss)
|
||
- ✅ Timer scheduling works correctly (no update issues)
|
||
- ✅ Sensor-timer assignment is correct (no wrong updates)
|
||
|
||
### 📋 Nice-to-Have Tests (Optional)
|
||
|
||
If problems arise in the future, these tests can be added:
|
||
|
||
1. **Async Task Management** - Pattern analyzed (fire-and-forget for short tasks)
|
||
2. **Data Structure Integrity** - Midnight rotation manually tested
|
||
3. **Service Response Memory** - HA's response lifecycle automatic
|
||
|
||
**Conclusion:** The integration has production-quality test coverage for all critical resource leak patterns.
|
||
|
||
## 🔍 How to Run Tests
|
||
|
||
```bash
|
||
# Run all resource cleanup tests (14 tests)
|
||
./scripts/test tests/test_resource_cleanup.py -v
|
||
|
||
# Run all critical pattern tests (41 tests)
|
||
./scripts/test tests/test_resource_cleanup.py tests/test_coordinator_shutdown.py \
|
||
tests/test_timer_scheduling.py tests/test_sensor_timer_assignment.py -v
|
||
|
||
# Run all tests with coverage
|
||
./scripts/test --cov=custom_components.tibber_prices --cov-report=html
|
||
|
||
# Type checking and linting
|
||
./scripts/check
|
||
|
||
# Manual memory leak test
|
||
# 1. Start HA: ./scripts/develop
|
||
# 2. Monitor RAM: watch -n 1 'ps aux | grep home-assistant'
|
||
# 3. Reload integration multiple times (HA UI: Settings → Devices → Tibber Prices → Reload)
|
||
# 4. RAM should stabilize (not grow continuously)
|
||
```
|
||
|
||
## 📚 References
|
||
|
||
- **Home Assistant Cleanup Patterns**: https://developers.home-assistant.io/docs/integration_setup_failures/#cleanup
|
||
- **Async Best Practices**: https://developers.home-assistant.io/docs/asyncio_101/
|
||
- **Memory Profiling**: https://docs.python.org/3/library/tracemalloc.html
|