feat: collector tracer bullet (#21)\n\nSmartctl acquisition with validation\nSysfs controller identity acquisition\nIdentity normalization (strip, no case fold, blank handling)\nSQLite store in WAL mode with six entities\nSchema versioning via PRAGMA user_version\nInvariant validation (negative bytes, etc.)\n17 passing tests across two test files\n\nCloses #21
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
"""Test identity normalization per specification.
|
||||
|
||||
From spec §2.3:
|
||||
- strip trailing spaces and newlines
|
||||
- no case folding
|
||||
- empty-after-strip stored blank
|
||||
|
||||
Padded and unpadded renderings of the same field yield byte-identical stored values.
|
||||
"""
|
||||
import pytest
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from fenris.collector import normalize_identity
|
||||
|
||||
|
||||
def test_strip_trailing_spaces():
|
||||
"""Trailing spaces are stripped."""
|
||||
identity = {"subnqn": "nqn.2014-08.org.nvmexpress:uuid:12345678 "}
|
||||
result = normalize_identity(identity)
|
||||
assert result == "nqn.2014-08.org.nvmexpress:uuid:12345678"
|
||||
|
||||
|
||||
def test_strip_trailing_newlines():
|
||||
"""Trailing newlines are stripped."""
|
||||
identity = {"subnqn": "nqn.2014-08.org.nvmexpress:uuid:12345678\n"}
|
||||
result = normalize_identity(identity)
|
||||
assert result == "nqn.2014-08.org.nvmexpress:uuid:12345678"
|
||||
|
||||
|
||||
def test_strip_trailing_spaces_and_newlines():
|
||||
"""Trailing spaces and newlines are stripped."""
|
||||
identity = {"subnqn": "nqn.2014-08.org.nvmexpress:uuid:12345678 \n\n"}
|
||||
result = normalize_identity(identity)
|
||||
assert result == "nqn.2014-08.org.nvmexpress:uuid:12345678"
|
||||
|
||||
|
||||
def test_no_case_folding():
|
||||
"""Case is preserved - no case folding."""
|
||||
identity = {"subnqn": "NQN.2014-08.ORG.NVMEXPRESS:UUID:12345678"}
|
||||
result = normalize_identity(identity)
|
||||
assert result == "NQN.2014-08.ORG.NVMEXPRESS:UUID:12345678"
|
||||
|
||||
|
||||
def test_empty_after_strip_stored_blank():
|
||||
"""Empty after strip is stored as blank string."""
|
||||
identity = {"subnqn": " \n\n "}
|
||||
result = normalize_identity(identity)
|
||||
assert result == ""
|
||||
|
||||
|
||||
def test_fallback_to_model_serial():
|
||||
"""When subnqn is empty, falls back to model|serial."""
|
||||
identity = {
|
||||
"subnqn": "",
|
||||
"mn": "Samsung SSD 970 EVO Plus 1TB",
|
||||
"sn": "S4EWNX0N123456",
|
||||
}
|
||||
result = normalize_identity(identity)
|
||||
assert result == "Samsung SSD 970 EVO Plus 1TB|S4EWNX0N123456"
|
||||
|
||||
|
||||
def test_fallback_model_serial_normalized():
|
||||
"""Model and serial are also normalized."""
|
||||
identity = {
|
||||
"subnqn": "",
|
||||
"mn": "Samsung SSD 970 EVO Plus 1TB\n",
|
||||
"sn": "S4EWNX0N123456 ",
|
||||
}
|
||||
result = normalize_identity(identity)
|
||||
assert result == "Samsung SSD 970 EVO Plus 1TB|S4EWNX0N123456"
|
||||
|
||||
|
||||
def test_all_keys_blank_returns_blank():
|
||||
"""When all keys are blank, returns blank (degraded identity)."""
|
||||
identity = {
|
||||
"subnqn": "",
|
||||
"mn": "",
|
||||
"sn": "",
|
||||
}
|
||||
result = normalize_identity(identity)
|
||||
assert result == ""
|
||||
|
||||
|
||||
def test_byte_identical_for_padded_unpadded():
|
||||
"""Padded and unpadded renderings yield byte-identical values."""
|
||||
padded = {"subnqn": "nqn.2014-08.org.nvmexpress:uuid:12345678 \n"}
|
||||
unpadded = {"subnqn": "nqn.2014-08.org.nvmexpress:uuid:12345678"}
|
||||
|
||||
result_padded = normalize_identity(padded)
|
||||
result_unpadded = normalize_identity(unpadded)
|
||||
|
||||
assert result_padded == result_unpadded
|
||||
assert result_padded == "nqn.2014-08.org.nvmexpress:uuid:12345678"
|
||||
Reference in New Issue
Block a user