diff --git a/artifact.001/tests/test_core.py b/artifact.001/tests/test_core.py new file mode 100644 index 0000000..fe83897 --- /dev/null +++ b/artifact.001/tests/test_core.py @@ -0,0 +1,98 @@ +import json +import os +import tempfile +from datetime import datetime +from pathlib import Path +import pytest + +# Wir importieren aus dem Projektpaket artifact_001.core +import importlib.util + +def load_log_measurement(): + # Dynamisches Laden der log_measurement Funktion aus src/artifact_001/core.py + # Dies ist CI-safe, da ansonsten ImportError entstehen könnte, falls sys.path anders konfiguriert ist. + pkg_path = Path(__file__).parent.parent / 'src' / 'artifact_001' / 'core.py' + spec = importlib.util.spec_from_file_location('artifact_001.core', pkg_path) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod.log_measurement + +@pytest.fixture +def sample_measurement(): + return { + 'timestamp': datetime(2024, 6, 10, 7, 5, 0), + 'measured_p': 0.134, + 'freeze_ok': False, + 'setup_fingerprint': 'setupA', + 'policy_hash': 'ph123' + } + +@pytest.fixture(autouse=True) +def temp_output_dir(monkeypatch, tmp_path): + output_path = tmp_path / 'measurement_log.json' + os.makedirs(tmp_path, exist_ok=True) + # Patch the output path in core.py to use this temp file. + monkeypatch.setenv('AUX3_OUTPUT_PATH', str(output_path)) + yield output_path + +@pytest.mark.parametrize('invalid_case', [ + {'measured_p': 'no_float'}, # falscher Typ + {'freeze_ok': 'not_bool'}, # falscher Typ + {'timestamp': 'invalid-date'}, # ungültiges Datum +]) +def test_log_measurement_invalid_input(invalid_case, sample_measurement, temp_output_dir): + log_measurement = load_log_measurement() + bad_input = sample_measurement.copy() + bad_input.update(invalid_case) + with pytest.raises(Exception): + log_measurement(**bad_input) + + +def test_log_measurement_creates_valid_json(sample_measurement, temp_output_dir): + log_measurement = load_log_measurement() + fpath = temp_output_dir + # Sicherstellen, dass Datei leer ist + if fpath.exists(): + fpath.unlink() + + # Aufruf der Funktion + log_measurement(**sample_measurement) + + assert fpath.exists(), 'JSON-Logdatei sollte erstellt werden.' + + content = json.loads(fpath.read_text(encoding='utf-8')) + + assert isinstance(content, list), 'JSON-Datei sollte eine Liste enthalten.' + assert len(content) == 1, 'Ein Messpunkt sollte geloggt sein.' + entry = content[0] + + # Strukturprüfung basierend auf MeasurementLog data_model + expected_fields = ['timestamp', 'measured_p', 'freeze_ok', 'setup_fingerprint', 'policy_hash'] + for field in expected_fields: + assert field in entry, f'Feld {field} sollte vorhanden sein.' + + # Typ-Assertions (Nominal Case) + datetime.fromisoformat(entry['timestamp']) + assert isinstance(entry['measured_p'], float) + assert isinstance(entry['freeze_ok'], bool) + assert isinstance(entry['setup_fingerprint'], str) + assert isinstance(entry['policy_hash'], str) + + +def test_log_measurement_appends_to_existing(temp_output_dir, sample_measurement): + log_measurement = load_log_measurement() + fpath = temp_output_dir + + # Erstes Logging + log_measurement(**sample_measurement) + + # Zweites Logging – sollte anhängen + modified = sample_measurement.copy() + modified['measured_p'] = 0.555 + log_measurement(**modified) + + content = json.loads(fpath.read_text(encoding='utf-8')) + assert len(content) == 2, 'Zwei Messpunkte sollten geloggt sein.' + + # Letzter Eintrag überprüfen + assert pytest.approx(content[-1]['measured_p'], 1e-6) == 0.555 \ No newline at end of file