mirror of
https://github.com/jorijn/meshcore-stats.git
synced 2026-03-28 17:42:55 +01:00
* tests: cache integration/report fixtures to speed up tests * fix: speed up yearly aggregation and refresh timings report * chore: remove the report * fix: unrecognized named-value: 'runner'. Located at position 1 within expression: runner.temp * fix: ruff linting error * test: strengthen assertions and stabilize tests * test(integration): expand rendered chart metrics
204 lines
6.3 KiB
Python
204 lines
6.3 KiB
Python
"""Tests for database maintenance functions."""
|
|
|
|
import os
|
|
import sqlite3
|
|
|
|
from meshmon.db import (
|
|
get_db_path,
|
|
init_db,
|
|
vacuum_db,
|
|
)
|
|
|
|
|
|
class TestVacuumDb:
|
|
"""Tests for vacuum_db function."""
|
|
|
|
def test_vacuums_existing_db(self, initialized_db):
|
|
"""Vacuum should run without error on initialized database."""
|
|
# Add some data then vacuum
|
|
conn = sqlite3.connect(initialized_db)
|
|
conn.execute(
|
|
"INSERT INTO metrics (ts, role, metric, value) VALUES (1, 'companion', 'test', 1.0)"
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
# Should not raise
|
|
vacuum_db(initialized_db)
|
|
|
|
def test_runs_analyze(self, initialized_db):
|
|
"""ANALYZE should be run after VACUUM."""
|
|
conn = sqlite3.connect(initialized_db)
|
|
conn.execute(
|
|
"INSERT INTO metrics (ts, role, metric, value) VALUES (1, 'companion', 'test', 1.0)"
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
# Vacuum includes ANALYZE
|
|
vacuum_db(initialized_db)
|
|
|
|
# Check that database stats were updated
|
|
conn = sqlite3.connect(initialized_db)
|
|
cursor = conn.execute("SELECT COUNT(*) FROM sqlite_stat1")
|
|
count = cursor.fetchone()[0]
|
|
conn.close()
|
|
assert count > 0
|
|
|
|
def test_uses_default_path_when_none(self, configured_env, monkeypatch):
|
|
"""Uses get_db_path() when no path provided."""
|
|
# Initialize db at default location
|
|
init_db()
|
|
|
|
# vacuum_db with None should use default path
|
|
vacuum_db(None)
|
|
|
|
def test_can_vacuum_empty_db(self, initialized_db):
|
|
"""Can vacuum an empty database."""
|
|
vacuum_db(initialized_db)
|
|
|
|
def test_reclaims_space_after_delete(self, initialized_db):
|
|
"""Vacuum should reclaim space after deleting rows."""
|
|
conn = sqlite3.connect(initialized_db)
|
|
|
|
# Insert many rows
|
|
for i in range(1000):
|
|
conn.execute(
|
|
"INSERT INTO metrics (ts, role, metric, value) VALUES (?, 'companion', 'test', 1.0)",
|
|
(i,)
|
|
)
|
|
conn.commit()
|
|
|
|
# Get size before delete
|
|
conn.close()
|
|
size_before = os.path.getsize(initialized_db)
|
|
|
|
# Delete all rows
|
|
conn = sqlite3.connect(initialized_db)
|
|
conn.execute("DELETE FROM metrics")
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
# Vacuum
|
|
vacuum_db(initialized_db)
|
|
|
|
# Size should be smaller (or at least not larger)
|
|
size_after = os.path.getsize(initialized_db)
|
|
# Note: Due to WAL mode, this might not always shrink dramatically
|
|
# but vacuum should at least complete without error
|
|
assert size_after <= size_before + 4096 # Allow for some overhead
|
|
|
|
|
|
class TestGetDbPath:
|
|
"""Tests for get_db_path function."""
|
|
|
|
def test_returns_path_in_state_dir(self, configured_env):
|
|
"""Path should be in the configured state directory."""
|
|
path = get_db_path()
|
|
|
|
assert path.name == "metrics.db"
|
|
assert str(configured_env["state_dir"]) in str(path)
|
|
|
|
def test_returns_path_object(self, configured_env):
|
|
"""Should return a Path object."""
|
|
from pathlib import Path
|
|
|
|
path = get_db_path()
|
|
|
|
assert isinstance(path, Path)
|
|
|
|
|
|
class TestDatabaseIntegrity:
|
|
"""Tests for database integrity after operations."""
|
|
|
|
def test_wal_mode_enabled(self, initialized_db):
|
|
"""Database should be in WAL mode."""
|
|
conn = sqlite3.connect(initialized_db)
|
|
cursor = conn.execute("PRAGMA journal_mode")
|
|
mode = cursor.fetchone()[0]
|
|
conn.close()
|
|
|
|
assert mode.lower() == "wal"
|
|
|
|
def test_foreign_keys_disabled_by_default(self, initialized_db):
|
|
"""Foreign keys should be disabled (SQLite default)."""
|
|
conn = sqlite3.connect(initialized_db)
|
|
cursor = conn.execute("PRAGMA foreign_keys")
|
|
enabled = cursor.fetchone()[0]
|
|
conn.close()
|
|
|
|
# Default is off, and we don't explicitly enable them
|
|
assert enabled == 0
|
|
|
|
def test_metrics_table_exists(self, initialized_db):
|
|
"""Metrics table should exist after init."""
|
|
conn = sqlite3.connect(initialized_db)
|
|
cursor = conn.execute(
|
|
"SELECT name FROM sqlite_master WHERE type='table' AND name='metrics'"
|
|
)
|
|
result = cursor.fetchone()
|
|
conn.close()
|
|
|
|
assert result is not None
|
|
assert result[0] == "metrics"
|
|
|
|
def test_db_meta_table_exists(self, initialized_db):
|
|
"""db_meta table should exist after init."""
|
|
conn = sqlite3.connect(initialized_db)
|
|
cursor = conn.execute(
|
|
"SELECT name FROM sqlite_master WHERE type='table' AND name='db_meta'"
|
|
)
|
|
result = cursor.fetchone()
|
|
conn.close()
|
|
|
|
assert result is not None
|
|
|
|
def test_metrics_index_exists(self, initialized_db):
|
|
"""Index on metrics(role, ts) should exist."""
|
|
conn = sqlite3.connect(initialized_db)
|
|
cursor = conn.execute(
|
|
"SELECT name FROM sqlite_master WHERE type='index' AND name='idx_metrics_role_ts'"
|
|
)
|
|
result = cursor.fetchone()
|
|
conn.close()
|
|
|
|
assert result is not None
|
|
|
|
def test_vacuum_preserves_data(self, initialized_db):
|
|
"""Vacuum should not lose any data."""
|
|
conn = sqlite3.connect(initialized_db)
|
|
for i in range(100):
|
|
conn.execute(
|
|
"INSERT INTO metrics (ts, role, metric, value) VALUES (?, 'companion', 'test', ?)",
|
|
(i, float(i))
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
# Vacuum
|
|
vacuum_db(initialized_db)
|
|
|
|
# Check data is still there
|
|
conn = sqlite3.connect(initialized_db)
|
|
cursor = conn.execute("SELECT COUNT(*) FROM metrics")
|
|
count = cursor.fetchone()[0]
|
|
conn.close()
|
|
|
|
assert count == 100
|
|
|
|
def test_vacuum_preserves_schema_version(self, initialized_db):
|
|
"""Vacuum should not change schema version."""
|
|
from meshmon.db import _get_schema_version
|
|
|
|
conn = sqlite3.connect(initialized_db)
|
|
version_before = _get_schema_version(conn)
|
|
conn.close()
|
|
|
|
vacuum_db(initialized_db)
|
|
|
|
conn = sqlite3.connect(initialized_db)
|
|
version_after = _get_schema_version(conn)
|
|
conn.close()
|
|
|
|
assert version_before == version_after
|