Release v3.0.0
- Bump version to 3.0.0 and update docs - Fix Debian payload to include TUI and install /usr/bin/sensorpajen-tui wrapper - Make systemd unit upgrades safer and ignore deb build artifacts
This commit is contained in:
@@ -1,8 +1,225 @@
|
||||
import pytest
|
||||
from sensorpajen.tui.app import SensorpajenApp
|
||||
import tempfile
|
||||
import json
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from sensorpajen.config import SensorConfig
|
||||
from sensorpajen.discovery_manager import DiscoveryManager
|
||||
|
||||
def test_tui_app_init():
|
||||
# Just test that we can instantiate it
|
||||
app = SensorpajenApp()
|
||||
assert app.discovery_manager is not None
|
||||
assert app.sensor_config is not None
|
||||
def test_tui_sensor_config_edit():
|
||||
"""Integration test: Test that editing a sensor works end-to-end"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
config_file = Path(tmpdir) / "sensors.json"
|
||||
db_file = Path(tmpdir) / "test.db"
|
||||
|
||||
# Create initial config
|
||||
initial_data = {
|
||||
"sensors": [
|
||||
{"mac": "AA:BB:CC:DD:EE:FF", "name": "Living Room Sensor"}
|
||||
]
|
||||
}
|
||||
config_file.write_text(json.dumps(initial_data, indent=2))
|
||||
|
||||
# Initialize database
|
||||
conn = sqlite3.connect(str(db_file))
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS discovered_sensors (
|
||||
mac TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
rssi INTEGER,
|
||||
first_seen TIMESTAMP,
|
||||
last_seen TIMESTAMP,
|
||||
count INTEGER DEFAULT 0,
|
||||
last_temp REAL,
|
||||
last_humidity REAL,
|
||||
last_battery_percent INTEGER,
|
||||
last_battery_voltage INTEGER,
|
||||
status TEXT DEFAULT 'pending',
|
||||
reviewed BOOLEAN DEFAULT 0,
|
||||
ignored_at TIMESTAMP,
|
||||
ignore_reason TEXT
|
||||
)
|
||||
""")
|
||||
|
||||
now = datetime.now().isoformat()
|
||||
cursor.execute("""
|
||||
INSERT INTO discovered_sensors
|
||||
(mac, name, rssi, first_seen, last_seen, count, last_temp, last_humidity,
|
||||
last_battery_percent, last_battery_voltage, status, reviewed)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'approved', 1)
|
||||
""", ("AA:BB:CC:DD:EE:FF", "Living Room Sensor", -65, now, now, 50, 23.5, 55, 85, 2950))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# Load config and discovery manager (simulating TUI)
|
||||
config = SensorConfig(str(config_file))
|
||||
dm = DiscoveryManager(str(db_file), config)
|
||||
|
||||
# Verify initial state
|
||||
assert config.sensors["AA:BB:CC:DD:EE:FF"] == "Living Room Sensor"
|
||||
|
||||
# Edit sensor (simulate user action in TUI)
|
||||
config.add_sensor("AA:BB:CC:DD:EE:FF", "Bedroom Sensor")
|
||||
|
||||
# Verify in-memory update
|
||||
assert config.sensors["AA:BB:CC:DD:EE:FF"] == "Bedroom Sensor"
|
||||
|
||||
# Verify disk update
|
||||
saved_data = json.loads(config_file.read_text())
|
||||
assert saved_data["sensors"][0]["name"] == "Bedroom Sensor"
|
||||
|
||||
# Simulate refresh_data() - create new config instance and verify
|
||||
config2 = SensorConfig(str(config_file))
|
||||
assert config2.sensors["AA:BB:CC:DD:EE:FF"] == "Bedroom Sensor"
|
||||
|
||||
def test_sensor_config_edit_updates_memory():
|
||||
"""Test that editing a sensor updates both disk and memory"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
config_file = Path(tmpdir) / "sensors.json"
|
||||
|
||||
# Create initial config
|
||||
initial_data = {
|
||||
"sensors": [
|
||||
{"mac": "AA:BB:CC:DD:EE:FF", "name": "Original Name"}
|
||||
]
|
||||
}
|
||||
config_file.write_text(json.dumps(initial_data, indent=2))
|
||||
|
||||
# Load config
|
||||
config = SensorConfig(str(config_file))
|
||||
assert config.sensors["AA:BB:CC:DD:EE:FF"] == "Original Name"
|
||||
|
||||
# Edit sensor
|
||||
config.add_sensor("AA:BB:CC:DD:EE:FF", "Updated Name")
|
||||
|
||||
# Check in-memory is updated
|
||||
assert config.sensors["AA:BB:CC:DD:EE:FF"] == "Updated Name"
|
||||
|
||||
# Check disk is updated
|
||||
saved_data = json.loads(config_file.read_text())
|
||||
assert saved_data["sensors"][0]["name"] == "Updated Name"
|
||||
|
||||
# Reload from disk and verify
|
||||
config2 = SensorConfig(str(config_file))
|
||||
assert config2.sensors["AA:BB:CC:DD:EE:FF"] == "Updated Name"
|
||||
|
||||
def test_sensor_config_remove_sensor():
|
||||
"""Test that removing a sensor works correctly"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
config_file = Path(tmpdir) / "sensors.json"
|
||||
|
||||
# Create config with multiple sensors
|
||||
initial_data = {
|
||||
"sensors": [
|
||||
{"mac": "AA:BB:CC:DD:EE:FF", "name": "Sensor 1"},
|
||||
{"mac": "AA:BB:CC:DD:EE:11", "name": "Sensor 2"}
|
||||
]
|
||||
}
|
||||
config_file.write_text(json.dumps(initial_data, indent=2))
|
||||
|
||||
# Load and verify
|
||||
config = SensorConfig(str(config_file))
|
||||
assert len(config.sensors) == 2
|
||||
|
||||
# Remove one sensor
|
||||
config.remove_sensor("AA:BB:CC:DD:EE:FF")
|
||||
|
||||
# Check in-memory removal
|
||||
assert "AA:BB:CC:DD:EE:FF" not in config.sensors
|
||||
assert "AA:BB:CC:DD:EE:11" in config.sensors
|
||||
|
||||
# Check disk update
|
||||
saved_data = json.loads(config_file.read_text())
|
||||
assert len(saved_data["sensors"]) == 1
|
||||
assert saved_data["sensors"][0]["mac"] == "AA:BB:CC:DD:EE:11"
|
||||
|
||||
def test_sensor_config_reload():
|
||||
"""Test that reload() re-reads from disk"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
config_file = Path(tmpdir) / "sensors.json"
|
||||
|
||||
# Create initial config
|
||||
initial_data = {
|
||||
"sensors": [
|
||||
{"mac": "AA:BB:CC:DD:EE:FF", "name": "Original Name"}
|
||||
]
|
||||
}
|
||||
config_file.write_text(json.dumps(initial_data, indent=2))
|
||||
|
||||
# Load config
|
||||
config = SensorConfig(str(config_file))
|
||||
assert config.sensors["AA:BB:CC:DD:EE:FF"] == "Original Name"
|
||||
|
||||
# Manually modify file on disk
|
||||
new_data = {
|
||||
"sensors": [
|
||||
{"mac": "AA:BB:CC:DD:EE:FF", "name": "Externally Modified"}
|
||||
]
|
||||
}
|
||||
config_file.write_text(json.dumps(new_data, indent=2))
|
||||
|
||||
# Reload should pick up the changes
|
||||
config.load()
|
||||
assert config.sensors["AA:BB:CC:DD:EE:FF"] == "Externally Modified"
|
||||
|
||||
def test_discovery_manager_approve_sensor():
|
||||
"""Test that approving a sensor works correctly"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
config_file = Path(tmpdir) / "sensors.json"
|
||||
db_file = Path(tmpdir) / "test.db"
|
||||
|
||||
# Create empty config
|
||||
config_file.write_text(json.dumps({"sensors": []}, indent=2))
|
||||
|
||||
# Initialize database with pending sensor
|
||||
conn = sqlite3.connect(str(db_file))
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS discovered_sensors (
|
||||
mac TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
rssi INTEGER,
|
||||
first_seen TIMESTAMP,
|
||||
last_seen TIMESTAMP,
|
||||
count INTEGER DEFAULT 0,
|
||||
last_temp REAL,
|
||||
last_humidity REAL,
|
||||
last_battery_percent INTEGER,
|
||||
last_battery_voltage INTEGER,
|
||||
status TEXT DEFAULT 'pending',
|
||||
reviewed BOOLEAN DEFAULT 0,
|
||||
ignored_at TIMESTAMP,
|
||||
ignore_reason TEXT
|
||||
)
|
||||
""")
|
||||
|
||||
now = datetime.now().isoformat()
|
||||
cursor.execute("""
|
||||
INSERT INTO discovered_sensors
|
||||
(mac, name, rssi, first_seen, last_seen, count, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""", ("AA:BB:CC:DD:EE:33", "Unknown Sensor", -80, now, now, 1, "pending"))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# Load config and DM
|
||||
config = SensorConfig(str(config_file))
|
||||
dm = DiscoveryManager(str(db_file), config)
|
||||
|
||||
# Verify sensor is pending
|
||||
pending = dm.get_pending()
|
||||
assert len(pending) == 1
|
||||
assert pending[0].mac == "AA:BB:CC:DD:EE:33"
|
||||
|
||||
# Approve and add to config (simulate TUI action)
|
||||
config.add_sensor("AA:BB:CC:DD:EE:33", "Kitchen Sensor")
|
||||
dm.approve("AA:BB:CC:DD:EE:33")
|
||||
|
||||
# Verify sensor is no longer pending (filtered by config)
|
||||
pending = dm.get_pending()
|
||||
assert len(pending) == 0
|
||||
|
||||
# Verify it's in config
|
||||
assert config.sensors["AA:BB:CC:DD:EE:33"] == "Kitchen Sensor"
|
||||
|
||||
Reference in New Issue
Block a user