"""Collector tracer bullet test. Tests the thinnest complete write path through the system: - Input: smartctl-JSON fixture, sysfs fixture tree, config fixture, injected clock - Output: resulting store contents, run outcome Seam: write side of the observation store database file. """ import os import sqlite3 from datetime import datetime, timezone from pathlib import Path from typing import Any, Dict import pytest # Add src to path for imports import sys sys.path.insert(0, str(Path(__file__).parent.parent / "src")) from fenris.collector import run_collection from fenris.store import init_store # Fixtures @pytest.fixture def smartctl_fixture() -> Dict[str, Any]: """Minimal smartctl -a -j output with required fields.""" return { "json_format_version": [1, 0], "smartctl": {"version": [7, 3], "svn_revision": "5155", "build_info": "(local build)"}, "nvme_smart_health_information_log": { "critical_warning": 0, "temperature": 35, "available_spare": 100, "available_spare_threshold": 10, "percentage_used": 5, "data_units_written": 12345678, "data_units_read": 9876543, "power_on_hours": 8765, "power_cycles": 1234, "unsafe_shutdowns": 5, "media_errors": 0, "num_err_log_entries": 0, }, "user_capacity": {"bytes": 1024000000000, "units": "bytes"}, "model_name": "Samsung SSD 970 EVO Plus 1TB", "serial_number": "S4EWNX0N123456", "firmware_version": "2B2QEXM7", } @pytest.fixture def sysfs_fixture_tree(tmp_path: Path) -> Path: """Create a minimal sysfs fixture tree with controller identity.""" ctrl_dir = tmp_path / "sys" / "class" / "nvme" / "nvme0" ctrl_dir.mkdir(parents=True) # Controller identity files (ctrl_dir / "subsysnqn").write_text("nqn.2014-08.org.nvmexpress:uuid:12345678-1234-1234-1234-123456789abc\n") (ctrl_dir / "model").write_text("Samsung SSD 970 EVO Plus 1TB\n") (ctrl_dir / "serial").write_text("S4EWNX0N123456\n") (ctrl_dir / "firmware_rev").write_text("2B2QEXM7\n") # Transport info (optional, but we'll include it) transport_dir = ctrl_dir / "transport" transport_dir.mkdir() (transport_dir / "address").write_text("0000:03:00.0") (transport_dir / "trstring").write_text("pcie") return tmp_path @pytest.fixture def config_fixture(tmp_path: Path) -> Dict[str, Any]: """Configuration fixture naming the device.""" return { "device": "/dev/nvme0", "store_path": str(tmp_path / "observations.db"), } @pytest.fixture def clock_fixture(): """Injected clock returning fixed time.""" class FakeClock: def __init__(self): self.now = datetime(2026, 9, 1, 12, 0, 0, tzinfo=timezone.utc) def utcnow(self): return self.now return FakeClock() # Test: Collector writes one well-formed sample def test_collector_writes_one_sample( smartctl_fixture: Dict[str, Any], sysfs_fixture_tree: Path, config_fixture: Dict[str, Any], clock_fixture, ): """Given a smartctl fixture and sysfs fixture tree, when the collector runs, then one well-formed sample is written to the observation store.""" result = run_collection( smartctl_data=smartctl_fixture, sysfs_path=sysfs_fixture_tree / "sys" / "class" / "nvme" / "nvme0", config=config_fixture, clock=clock_fixture, ) # Verify run succeeded assert result["ok"] is True, f"Collection failed: {result.get('error')}" # Verify store contents conn = sqlite3.connect(config_fixture["store_path"]) cursor = conn.execute("SELECT COUNT(*) FROM samples") count = cursor.fetchone()[0] assert count == 1 cursor = conn.execute("SELECT * FROM samples") row = cursor.fetchone() assert row is not None # Verify row contents match fixtures # Row structure: id, ts, device, subnqn, sn, mn, fr, capacity_bytes, # percentage_used, available_spare, media_errors, power_on_hours, power_cycles, # unsafe_shutdowns, temperature_c, data_units_written, data_units_read, # bytes_written, bytes_read, critical_warning assert row[2] == "/dev/nvme0" # device assert row[3] == "nqn.2014-08.org.nvmexpress:uuid:12345678-1234-1234-1234-123456789abc" # subnqn assert row[4] == "S4EWNX0N123456" # sn assert row[5] == "Samsung SSD 970 EVO Plus 1TB" # mn assert row[6] == "2B2QEXM7" # fr assert row[7] == 1024000000000 # capacity_bytes assert row[8] == 5 # percentage_used assert row[15] == 12345678 # data_units_written assert row[17] == 12345678 * 512000 # bytes_written conn.close() # Test: Identity normalization applied exactly once at write time def test_identity_normalization( smartctl_fixture: Dict[str, Any], sysfs_fixture_tree: Path, config_fixture: Dict[str, Any], clock_fixture, ): """Given sysfs identity with trailing spaces/newlines, when the collector writes, then identity is normalized exactly once at write time.""" # Create identity with trailing whitespace identity = { "subnqn": "nqn.2014-08.org.nvmexpress:uuid:12345678-1234-1234-1234-123456789abc \n", "mn": "Samsung SSD 970 EVO Plus 1TB\n", "sn": "S4EWNX0N123456\n", "fr": "2B2QEXM7", "transport": "pcie", } from fenris.collector import normalize_identity # Normalize once key1 = normalize_identity(identity) # Normalize again - should be identical key2 = normalize_identity(identity) assert key1 == key2 assert key1 == "nqn.2014-08.org.nvmexpress:uuid:12345678-1234-1234-1234-123456789abc" assert "\n" not in key1 assert key1 == key1.rstrip() # No trailing whitespace # Test: Any acquisition failure fails the whole run def test_acquisition_failure_fails_run( sysfs_fixture_tree: Path, config_fixture: Dict[str, Any], clock_fixture, ): """Given a smartctl fixture with missing fields, when the collector runs, then the whole run fails and writes nothing.""" # Missing required field bad_smartctl = { "json_format_version": [1, 0], "smartctl": {"version": [7, 3]}, # Missing nvme_smart_health_information_log } result = run_collection( smartctl_data=bad_smartctl, sysfs_path=sysfs_fixture_tree / "sys" / "class" / "nvme" / "nvme0", config=config_fixture, clock=clock_fixture, ) # Verify run failed assert result["ok"] is False assert "Missing required field" in result["error"] # Verify nothing was written if os.path.exists(config_fixture["store_path"]): conn = sqlite3.connect(config_fixture["store_path"]) cursor = conn.execute("SELECT COUNT(*) FROM samples") count = cursor.fetchone()[0] assert count == 0 conn.close() else: # Store wasn't even created - also valid pass # Test: Store initializes with six entities def test_store_initialization(config_fixture: Dict[str, Any]): """Given no existing store, when the collector runs, then the store is initialized with six entities.""" store_path = Path(config_fixture["store_path"]) # Store shouldn't exist yet assert not store_path.exists() # Initialize store conn = init_store(store_path) # Verify the observation, derived-history, and metadata tables exist. cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table'") tables = {row[0] for row in cursor.fetchall()} expected_tables = { "samples", "hour_observations", "day_aggregates", "monitoring_periods", "controller_segments", "endurance_baseline", } # sqlite_sequence is a system table created by AUTOINCREMENT expected_tables.add("sqlite_sequence") expected_tables.add("store_metadata") expected_tables.add("local_days") expected_tables.add("pending_publications") expected_tables.add("local_day_unallocated_evidence") expected_tables.add("local_day_segment_totals") assert expected_tables == tables conn.close() # Test: Schema versioning with PRAGMA user_version def test_schema_versioning(config_fixture: Dict[str, Any]): """Given a store with unknown newer version, when the collector runs, then it refuses to proceed.""" store_path = Path(config_fixture["store_path"]) # Create a store with newer version conn = sqlite3.connect(str(store_path)) conn.execute("PRAGMA journal_mode=WAL") conn.execute("PRAGMA user_version=999") # Unknown newer version conn.close() # Try to initialize - should fail with pytest.raises(ValueError, match="newer Fenris"): init_store(store_path) def test_schema_version_current(config_fixture: Dict[str, Any]): """Given a store with current version, when the collector runs, then it proceeds without migration.""" from fenris.store import SCHEMA_VERSION store_path = Path(config_fixture["store_path"]) # Initialize store conn1 = init_store(store_path) conn1.close() # Open again - should succeed conn2 = init_store(store_path) # Verify version is current cursor = conn2.execute("PRAGMA user_version") version = cursor.fetchone()[0] assert version == SCHEMA_VERSION conn2.close() def test_schema_version_older(config_fixture: Dict[str, Any]): """Given a store with older version, when the collector runs, then it applies migrations and proceeds.""" from fenris.store import SCHEMA_VERSION store_path = Path(config_fixture["store_path"]) # Create a store with older version conn = sqlite3.connect(str(store_path)) conn.execute("PRAGMA journal_mode=WAL") conn.execute("PRAGMA user_version=0") # Older version conn.close() # Initialize store - should apply migrations conn = init_store(store_path) # Verify version is current cursor = conn.execute("PRAGMA user_version") version = cursor.fetchone()[0] assert version == SCHEMA_VERSION conn.close() # Test: Invariant-violating run writes nothing def test_invariant_violation_writes_nothing( smartctl_fixture: Dict[str, Any], sysfs_fixture_tree: Path, config_fixture: Dict[str, Any], clock_fixture, ): """Given a smartctl fixture that would violate store invariants, when the collector runs, then it writes nothing and fails visibly.""" # Create a fixture that would cause invariant violation # (negative bytes_written - we'll mock this) bad_smartctl = smartctl_fixture.copy() bad_smartctl["nvme_smart_health_information_log"] = { **smartctl_fixture["nvme_smart_health_information_log"], "data_units_written": -1, # This will cause negative bytes_written } result = run_collection( smartctl_data=bad_smartctl, sysfs_path=sysfs_fixture_tree / "sys" / "class" / "nvme" / "nvme0", config=config_fixture, clock=clock_fixture, ) # Verify run failed assert result["ok"] is False assert "InvariantViolation" in result["error_type"] # Verify nothing was written if os.path.exists(config_fixture["store_path"]): conn = sqlite3.connect(config_fixture["store_path"]) cursor = conn.execute("SELECT COUNT(*) FROM samples") count = cursor.fetchone()[0] assert count == 0 conn.close()