Merge pull request #34 from ElVit/desk/unit-tests

Added Unit Tests for Maidesite Desk
This commit is contained in:
ElVit 2026-04-18 16:32:18 +02:00 committed by GitHub
commit e3b26cc7c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 841 additions and 1 deletions

View File

@ -0,0 +1,188 @@
name: Test Maidesite Desk Component
permissions:
contents: read
packages: read
on:
push:
branches:
- main
- 'desk**'
paths:
- 'components/maidesite_desk/**'
- 'tests/maidesite_desk/**'
- '.github/workflows/test_maidesite_desk.yml'
pull_request:
branches:
- main
paths:
- 'components/maidesite_desk/**'
- 'tests/maidesite_desk/**'
- '.github/workflows/test_maidesite_desk.yml'
workflow_dispatch:
jobs:
test-build:
name: Test ESPHome Build
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
board: [esp32s2, esp32c3, full]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build ESPHome firmware for ${{ matrix.board }}
uses: esphome/build-action@v7
with:
yaml-file: tests/maidesite_desk/test_maidesite_desk_${{ matrix.board == 'full' && 'full' || matrix.board }}.yaml
version: latest
lint-code:
name: Lint C++ Code
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install clang-format
run: |
sudo apt-get update
sudo apt-get install -y clang-format
- name: Run clang-format check
run: |
find components/maidesite_desk -name '*.cpp' -o -name '*.h' | \
xargs clang-format -style=file --dry-run --Werror
lint-python:
name: Lint Python Code
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install linting tools
run: |
pip install --upgrade pip
pip install flake8 pylint black
- name: Run black check
run: |
black --check components/maidesite_desk/
- name: Run flake8
run: |
flake8 components/maidesite_desk/ --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 components/maidesite_desk/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
test-python:
name: Python Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install --upgrade pip
pip install pytest pytest-cov esphome
- name: Run Python unit tests
run: |
pytest tests/maidesite_desk/ -v --cov=components/maidesite_desk --cov-report=xml --cov-report=term
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
flags: unittests
name: codecov-maidesite-desk
fail_ci_if_error: false
test-minimal-config:
name: Test Minimal Configuration
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install ESPHome
run: |
pip install --upgrade pip
pip install esphome
- name: Validate minimal config
run: |
esphome config tests/maidesite_desk/test_maidesite_desk_mini.yaml
test-full-config:
name: Test Full Configuration
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install ESPHome
run: |
pip install --upgrade pip
pip install esphome
- name: Validate full config
run: |
esphome config tests/maidesite_desk/test_maidesite_desk_full.yaml
summary:
name: Test Summary
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [test-build, lint-code, lint-python, test-python, test-minimal-config, test-full-config]
if: always()
steps:
- name: Check test results
run: |
echo "Test Build: ${{ needs.test-build.result }}"
echo "Lint C++: ${{ needs.lint-code.result }}"
echo "Lint Python: ${{ needs.lint-python.result }}"
echo "Python Unit Tests: ${{ needs.test-python.result }}"
echo "Minimal Config: ${{ needs.test-minimal-config.result }}"
echo "Full Config: ${{ needs.test-full-config.result }}"
if [ "${{ needs.test-build.result }}" != "success" ] || \
[ "${{ needs.lint-code.result }}" != "success" ] || \
[ "${{ needs.lint-python.result }}" != "success" ] || \
[ "${{ needs.test-python.result }}" != "success" ] || \
[ "${{ needs.test-minimal-config.result }}" != "success" ] || \
[ "${{ needs.test-full-config.result }}" != "success" ]; then
echo "One or more tests failed"
exit 1
fi
echo "All tests passed successfully!"

View File

@ -1,6 +1,5 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins
from esphome.components import uart from esphome.components import uart
from esphome.const import CONF_ID from esphome.const import CONF_ID

View File

@ -98,17 +98,25 @@ void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
if (new_height == this->current_height_) if (new_height == this->current_height_)
return; return;
this->current_height_ = new_height; this->current_height_ = new_height;
#ifdef USE_SENSOR
if (this->height_abs_sensor_ != nullptr) if (this->height_abs_sensor_ != nullptr)
this->height_abs_sensor_->publish_state(this->current_height_); this->height_abs_sensor_->publish_state(this->current_height_);
#endif
#ifdef USE_NUMBER
if (this->height_abs_number_ != nullptr) if (this->height_abs_number_ != nullptr)
this->height_abs_number_->publish_state(this->current_height_); this->height_abs_number_->publish_state(this->current_height_);
#endif
#ifdef USE_SENSOR
if (this->height_pct_sensor_ != nullptr && limit_max_ != 0) if (this->height_pct_sensor_ != nullptr && limit_max_ != 0)
this->height_pct_sensor_->publish_state((this->current_height_ - this->limit_min_) / this->height_pct_sensor_->publish_state((this->current_height_ - this->limit_min_) /
(this->limit_max_ - this->limit_min_) * 100); (this->limit_max_ - this->limit_min_) * 100);
#endif
#ifdef USE_NUMBER
if (this->height_pct_number_ != nullptr && limit_max_ != 0) if (this->height_pct_number_ != nullptr && limit_max_ != 0)
this->height_pct_number_->publish_state( this->height_pct_number_->publish_state(
roundf((this->current_height_ - this->limit_min_) / (this->limit_max_ - this->limit_min_) * 1000) / 10); roundf((this->current_height_ - this->limit_min_) / (this->limit_max_ - this->limit_min_) * 1000) / 10);
#endif
break; break;
case 0x20: case 0x20:
@ -117,17 +125,25 @@ void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
if ((message[4] & 1) == 0) { // low nibble 0 -> no max limit, use physical_max_ if ((message[4] & 1) == 0) { // low nibble 0 -> no max limit, use physical_max_
this->limit_max_ = this->physical_max_; this->limit_max_ = this->physical_max_;
#ifdef USE_SENSOR
if (this->height_max_sensor_ != nullptr) if (this->height_max_sensor_ != nullptr)
this->height_max_sensor_->publish_state(this->limit_max_); this->height_max_sensor_->publish_state(this->limit_max_);
#endif
#ifdef USE_NUMBER
if (height_abs_number_ != nullptr) if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_max_value(this->limit_max_); this->height_abs_number_->traits.set_max_value(this->limit_max_);
#endif
} }
if ((message[4] >> 4) == 0) { // high nibble 0 -> no min limit, use physical_min_ if ((message[4] >> 4) == 0) { // high nibble 0 -> no min limit, use physical_min_
this->limit_min_ = this->physical_min_; this->limit_min_ = this->physical_min_;
#ifdef USE_SENSOR
if (this->height_min_sensor_ != nullptr) if (this->height_min_sensor_ != nullptr)
this->height_min_sensor_->publish_state(limit_min_); this->height_min_sensor_->publish_state(limit_min_);
#endif
#ifdef USE_NUMBER
if (height_abs_number_ != nullptr) if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_min_value(limit_min_); this->height_abs_number_->traits.set_min_value(limit_min_);
#endif
} }
break; break;
@ -145,10 +161,14 @@ void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
delay(10); delay(10);
this->limit_max_ = this->byte2float(message[4], message[5]); this->limit_max_ = this->byte2float(message[4], message[5]);
#ifdef USE_SENSOR
if (this->height_max_sensor_ != nullptr) if (this->height_max_sensor_ != nullptr)
this->height_max_sensor_->publish_state(limit_max_); this->height_max_sensor_->publish_state(limit_max_);
#endif
#ifdef USE_NUMBER
if (height_abs_number_ != nullptr) if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_max_value(limit_max_); this->height_abs_number_->traits.set_max_value(limit_max_);
#endif
break; break;
case 0x22: case 0x22:
@ -156,42 +176,54 @@ void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
delay(10); delay(10);
this->limit_min_ = this->byte2float(message[4], message[5]); this->limit_min_ = this->byte2float(message[4], message[5]);
#ifdef USE_SENSOR
if (this->height_min_sensor_ != nullptr) if (this->height_min_sensor_ != nullptr)
this->height_min_sensor_->publish_state(limit_min_); this->height_min_sensor_->publish_state(limit_min_);
#endif
#ifdef USE_NUMBER
if (height_abs_number_ != nullptr) if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_min_value(limit_min_); this->height_abs_number_->traits.set_min_value(limit_min_);
#endif
break; break;
case 0x25: case 0x25:
ESP_LOGI(TAG, "Set position m1 to 0x%02X%02X", message[4], message[5]); ESP_LOGI(TAG, "Set position m1 to 0x%02X%02X", message[4], message[5]);
delay(10); delay(10);
#ifdef USE_SENSOR
if (this->position_m1_sensor_ != nullptr) if (this->position_m1_sensor_ != nullptr)
this->position_m1_sensor_->publish_state(this->byte2float(message[4], message[5])); this->position_m1_sensor_->publish_state(this->byte2float(message[4], message[5]));
#endif
break; break;
case 0x26: case 0x26:
ESP_LOGI(TAG, "Set position m2 to 0x%02X%02X", message[4], message[5]); ESP_LOGI(TAG, "Set position m2 to 0x%02X%02X", message[4], message[5]);
delay(10); delay(10);
#ifdef USE_SENSOR
if (this->position_m2_sensor_ != nullptr) if (this->position_m2_sensor_ != nullptr)
this->position_m2_sensor_->publish_state(this->byte2float(message[4], message[5])); this->position_m2_sensor_->publish_state(this->byte2float(message[4], message[5]));
#endif
break; break;
case 0x27: case 0x27:
ESP_LOGI(TAG, "Set position m3 to 0x%02X%02X", message[4], message[5]); ESP_LOGI(TAG, "Set position m3 to 0x%02X%02X", message[4], message[5]);
delay(10); delay(10);
#ifdef USE_SENSOR
if (this->position_m3_sensor_ != nullptr) if (this->position_m3_sensor_ != nullptr)
this->position_m3_sensor_->publish_state(this->byte2float(message[4], message[5])); this->position_m3_sensor_->publish_state(this->byte2float(message[4], message[5]));
#endif
break; break;
case 0x28: case 0x28:
ESP_LOGI(TAG, "Set position m4 to 0x%02X%02X", message[4], message[5]); ESP_LOGI(TAG, "Set position m4 to 0x%02X%02X", message[4], message[5]);
delay(10); delay(10);
#ifdef USE_SENSOR
if (this->position_m4_sensor_ != nullptr) if (this->position_m4_sensor_ != nullptr)
this->position_m4_sensor_->publish_state(this->byte2float(message[4], message[5])); this->position_m4_sensor_->publish_state(this->byte2float(message[4], message[5]));
#endif
break; break;
// case 0x0E: // case 0x0E:

View File

@ -0,0 +1,74 @@
# Testing Documentation for Maidesite Desk Component
This directory contains comprehensive tests for the `maidesite_desk` ESPHome component.
## Test Structure
### Unit Tests (`test_maidesite_desk_unit.py`)
- Tests Python configuration validation
- Tests component metadata (CODEOWNERS, DEPENDENCIES, etc.)
- Tests configuration schema structure
- Tests all platform imports (sensor, number, button)
- Tests code generation logic
### Integration Tests (`test_maidesite_desk_integration.py`)
- Tests minimal feature configuration on ESP32 board
- Tests full feature configuration on ESP32 board
- Tests configuration on ESP32-S2 board
- Tests configuration on ESP32-C3 board
### Test Configuration Files
- `test_maidesite_desk_esp32s2.yaml` - ESP32-S2 Wemos S2 Mini
- `test_maidesite_desk_esp32c3.yaml` - ESP32-C3 mini (RISC-V)
- `test_maidesite_desk_mini.yaml` - Minimal ESP32 configuration for basic testing
- `test_maidesite_desk_full.yaml` - Comprehensive ESP32 configuration with all features
## Running Tests Locally
### Prerequisites
```bash
pip install -r ../requirements.txt
```
### Run All Tests
```bash
pytest tests/maidesite_desk/ -v
```
### Run Only Unit Tests
```bash
pytest tests/maidesite_desk/test_maidesite_desk_unit.py -v
```
### Run Only Integration Tests
```bash
pytest tests/maidesite_desk/test_maidesite_desk_integration.py -v
```
### Run with Coverage
```bash
pytest tests/maidesite_desk/ --cov=components.maidesite_desk --cov-report=html
```
## Test Coverage
The test suite covers:
- Component configuration validation
- All sensor entities (height_abs, height_pct, height_min, height_max, position_m1-m4)
- All number entities (height_abs, height_pct)
- All button entities (stop, step_up, step_down, goto_max, goto_min, goto_m1-m4, save_m1-m4)
- Multiple ESP32 board variants (ESP32, ESP32-S2, ESP32-C3)
- Minimal and full configuration scenarios
## CI/CD Integration
These tests are designed to run in GitHub Actions CI/CD pipelines. See `.github/workflows/test_maidesite_desk.yml` for the workflow configuration.

View File

@ -0,0 +1,47 @@
---
# Test configuration for maidesite_desk component on ESP32-C3
# ESP32-C3 is a compact chip with limited GPIO pins
esp32:
board: esp32-c3-devkitm-1
variant: esp32c3
framework:
type: esp-idf
esphome:
name: test-maidesite-desk-esp32c3
friendly_name: "Test Maidesite Desk ESP32-C3"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [maidesite_desk]
uart:
- id: uart_desk
tx_pin: GPIO3
rx_pin: GPIO4
baud_rate: 9600
# Test basic configuration
maidesite_desk:
id: my_desk
uart_id: uart_desk
sensor:
- platform: maidesite_desk
height_abs:
name: "Height Absolute"
button:
- platform: maidesite_desk
stop:
name: "Stop Movement"
step_up:
name: "Step Up"
step_down:
name: "Step Down"

View File

@ -0,0 +1,47 @@
---
# Test configuration for maidesite_desk component on ESP32-S2
# ESP32-S2 has GPIO pins but limited compared to ESP32
esp32:
board: lolin_s2_mini
variant: esp32s2
framework:
type: esp-idf
esphome:
name: test-maidesite-desk-esp32s2
friendly_name: "Test Maidesite Desk ESP32-S2"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [maidesite_desk]
uart:
- id: uart_desk
tx_pin: GPIO3
rx_pin: GPIO4
baud_rate: 9600
# Test basic configuration
maidesite_desk:
id: my_desk
uart_id: uart_desk
sensor:
- platform: maidesite_desk
height_abs:
name: "Height Absolute"
button:
- platform: maidesite_desk
stop:
name: "Stop Movement"
step_up:
name: "Step Up"
step_down:
name: "Step Down"

View File

@ -0,0 +1,93 @@
---
# Test configuration for maidesite_desk component
# This file validates all component features and configurations
esp32:
board: esp32dev
framework:
type: esp-idf
esphome:
name: test-maidesite-desk-full
friendly_name: "Test Maidesite Desk Full"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [maidesite_desk]
uart:
- id: uart_desk
tx_pin: GPIO3
rx_pin: GPIO4
baud_rate: 9600
# Test basic configuration
maidesite_desk:
id: my_desk
uart_id: uart_desk
log_uart_msg: true
# Test all sensor types
sensor:
- platform: maidesite_desk
height_abs:
name: "Height Absolute"
height_pct:
name: "Height Percent"
height_min:
name: "Height Minimum"
height_max:
name: "Height Maximum"
position_m1:
name: "Memory Position M1"
position_m2:
name: "Memory Position M2"
position_m3:
name: "Memory Position M3"
position_m4:
name: "Memory Position M4"
# Test all number controls
number:
- platform: maidesite_desk
height_abs:
name: "Height Absolute Control"
mode: SLIDER
height_pct:
name: "Height Percent Control"
mode: SLIDER
# Test all button controls
button:
- platform: maidesite_desk
stop:
name: "Stop Movement"
step_up:
name: "Step Up"
step_down:
name: "Step Down"
goto_max:
name: "Go to Maximum Height"
goto_min:
name: "Go to Minimum Height"
goto_m1:
name: "Go to Memory 1"
goto_m2:
name: "Go to Memory 2"
goto_m3:
name: "Go to Memory 3"
goto_m4:
name: "Go to Memory 4"
save_m1:
name: "Save Memory 1"
save_m2:
name: "Save Memory 2"
save_m3:
name: "Save Memory 3"
save_m4:
name: "Save Memory 4"

View File

@ -0,0 +1,135 @@
"""
Integration tests for maidesite_desk component.
Tests the component's behavior with actual ESPHome compilation.
"""
import pytest
import subprocess
import os
class TestMaidesiteDeskIntegration:
"""Integration tests using ESPHome CLI."""
@pytest.fixture
def test_yaml_full(self):
"""Get the path to the full test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(
base_dir, "tests", "maidesite_desk", "test_maidesite_desk_full.yaml"
)
@pytest.fixture
def test_yaml_mini(self):
"""Get the path to the minimal test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(
base_dir, "tests", "maidesite_desk", "test_maidesite_desk_mini.yaml"
)
@pytest.fixture
def test_yaml_esp32s2(self):
"""Get the path to the ESP32-S2 test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(
base_dir,
"tests",
"maidesite_desk",
"test_maidesite_desk_esp32s2.yaml",
)
@pytest.fixture
def test_yaml_esp32c3(self):
"""Get the path to the ESP32-C3 test YAML file."""
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
return os.path.join(
base_dir,
"tests",
"maidesite_desk",
"test_maidesite_desk_esp32c3.yaml",
)
def test_validate_full_config(self, test_yaml_full: str):
"""Test that the full configuration is valid."""
if not os.path.exists(test_yaml_full):
pytest.skip(f"Test file not found: {test_yaml_full}")
try:
result = subprocess.run(
["esphome", "config", test_yaml_full],
capture_output=True,
text=True,
timeout=120,
)
assert (
result.returncode == 0
), f"Full config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
def test_validate_mini_config(self, test_yaml_mini: str):
"""Test that the minimal configuration is valid."""
if not os.path.exists(test_yaml_mini):
pytest.skip(f"Test file not found: {test_yaml_mini}")
try:
result = subprocess.run(
["esphome", "config", test_yaml_mini],
capture_output=True,
text=True,
timeout=120,
)
assert (
result.returncode == 0
), f"Mini config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
def test_validate_esp32s2_config(self, test_yaml_esp32s2: str):
"""Test that the ESP32-S2 configuration is valid."""
if not os.path.exists(test_yaml_esp32s2):
pytest.skip(f"Test file not found: {test_yaml_esp32s2}")
try:
result = subprocess.run(
["esphome", "config", test_yaml_esp32s2],
capture_output=True,
text=True,
timeout=120,
)
assert (
result.returncode == 0
), f"ESP32-S2 config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
def test_validate_esp32c3_config(self, test_yaml_esp32c3: str):
"""Test that the ESP32-C3 configuration is valid."""
if not os.path.exists(test_yaml_esp32c3):
pytest.skip(f"Test file not found: {test_yaml_esp32c3}")
try:
result = subprocess.run(
["esphome", "config", test_yaml_esp32c3],
capture_output=True,
text=True,
timeout=120,
)
assert (
result.returncode == 0
), f"ESP32-C3 config validation failed: {result.stderr}"
except FileNotFoundError:
pytest.skip("ESPHome not installed")
except subprocess.TimeoutExpired:
pytest.fail("ESPHome config validation timed out")
if __name__ == "__main__":
pytest.main([__file__, "-v"])

View File

@ -0,0 +1,32 @@
---
# Test configuration for maidesite_desk component - minimal configuration
# Tests basic component functionality with minimal required settings
esp32:
board: esp32dev
framework:
type: esp-idf
esphome:
name: test-maidesite-desk-mini
friendly_name: "Test Maidesite Desk Minimal"
logger:
level: DEBUG
external_components:
- source:
type: local
path: ../../components
components: [maidesite_desk]
uart:
- id: uart_desk
tx_pin: GPIO3
rx_pin: GPIO4
baud_rate: 9600
# Test minimal configuration
maidesite_desk:
id: my_desk
uart_id: uart_desk

View File

@ -0,0 +1,193 @@
"""
Unit tests for maidesite_desk component configuration validation.
Tests the Python configuration schema and validation logic.
"""
import pytest
from unittest.mock import MagicMock, patch
from esphome.core import CORE
class TestMaidesiteDeskConfig:
"""Test suite for maidesite_desk configuration validation."""
@pytest.fixture(autouse=True)
def setup(self):
"""Setup test fixtures."""
# Mock CORE to avoid ESPHome initialization issues
CORE.data = {}
yield
@pytest.fixture
def mock_uart(self):
"""Mock UART component."""
with patch("esphome.components.uart") as mock:
yield mock
@pytest.fixture
def mock_cg(self):
"""Mock code generator."""
with patch("esphome.codegen") as mock:
mock.esphome_ns = MagicMock()
mock.esphome_ns.namespace.return_value.class_ = MagicMock()
yield mock
def test_import_component(self):
"""Test that the component can be imported."""
try:
import sys
import os
# Add components directory to path
components_path = os.path.join(
os.path.dirname(__file__), "..", "..", "components"
)
sys.path.insert(0, components_path)
import maidesite_desk as md_init
assert md_init is not None
except ImportError as e:
pytest.fail(f"Failed to import maidesite_desk: {e}")
def test_component_metadata(self):
"""Test component metadata is correctly defined."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), "..", "..", "components"
)
sys.path.insert(0, components_path)
import maidesite_desk as md_init
assert hasattr(md_init, "CODEOWNERS")
assert md_init.CODEOWNERS == ["@elvit"]
assert hasattr(md_init, "MULTICONF")
assert md_init.MULTICONF is True
assert hasattr(md_init, "DEPENDENCIES")
assert "uart" in md_init.DEPENDENCIES
def test_config_constants(self):
"""Test that configuration constants are defined."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), "..", "..", "components"
)
sys.path.insert(0, components_path)
import maidesite_desk as md_init
assert hasattr(md_init, "CONF_MAIDESITE_DESK_ID")
assert md_init.CONF_MAIDESITE_DESK_ID == "maidesite_desk"
assert hasattr(md_init, "CONF_LOG_UART_MSG")
assert md_init.CONF_LOG_UART_MSG == "log_uart_msg"
@patch("esphome.components.uart")
@patch("esphome.codegen")
def test_config_schema_structure(self, mock_cg, mock_uart):
"""Test that CONFIG_SCHEMA has the correct structure."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), "..", "..", "components"
)
sys.path.insert(0, components_path)
import maidesite_desk as md_init
assert hasattr(md_init, "CONFIG_SCHEMA")
assert md_init.CONFIG_SCHEMA is not None
def test_valid_minimal_config(self):
"""Test validation of minimal valid configuration."""
config = {
"id": "my_desk",
"uart_id": "uart_bus",
}
# This would require full ESPHome environment to validate
# Just verify the config structure is reasonable
assert "id" in config
assert "uart_id" in config
def test_valid_full_config(self):
"""Test validation of full configuration with all options."""
config = {
"id": "my_desk",
"uart_id": "uart_bus",
"log_uart_msg": True,
}
assert "id" in config
assert "uart_id" in config
assert "log_uart_msg" in config
def test_log_uart_msg_default(self):
"""Test that log_uart_msg defaults to False."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), "..", "..", "components"
)
sys.path.insert(0, components_path)
import maidesite_desk as md_init
# The default is specified in the schema as False
# This test verifies the constant exists
assert md_init.CONF_LOG_UART_MSG == "log_uart_msg"
def test_multiconf_support(self):
"""Test that component supports multiple instances."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), "..", "..", "components"
)
sys.path.insert(0, components_path)
import maidesite_desk as md_init
# Verify MULTICONF is True, allowing multiple instances
assert md_init.MULTICONF is True
def test_uart_dependency(self):
"""Test that UART is listed as a dependency."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), "..", "..", "components"
)
sys.path.insert(0, components_path)
import maidesite_desk as md_init
assert "uart" in md_init.DEPENDENCIES
def test_namespace_definition(self):
"""Test that component namespace is correctly defined."""
import sys
import os
components_path = os.path.join(
os.path.dirname(__file__), "..", "..", "components"
)
sys.path.insert(0, components_path)
import maidesite_desk as md_init
assert hasattr(md_init, "maidesite_desk_ns")
assert md_init.maidesite_desk_ns is not None
assert hasattr(md_init, "MaidesiteDeskComponent")
assert md_init.MaidesiteDeskComponent is not None