| """ |
| Tests for src/primordial_os/audit_store.py — Phase 10 Audit Store |
| """ |
|
|
| import inspect |
| import json |
| from pathlib import Path |
|
|
| import pytest |
|
|
| from primordial_os.audit_log import ( |
| AuditEvent, |
| append_to_audit_chain, |
| create_audit_event, |
| ) |
| from primordial_os.audit_store import ( |
| append_audit_event_jsonl, |
| audit_event_from_dict, |
| audit_event_to_dict, |
| read_audit_events_jsonl, |
| verify_audit_jsonl, |
| ) |
| from primordial_os.hir_kernel import HIRInput |
| from primordial_os.oam_detector import OAMSignals |
| from primordial_os.runtime import run_evaluation |
|
|
|
|
| |
| |
| |
|
|
| def _green_decision(): |
| return run_evaluation( |
| HIRInput(0.9, 0.9, 0.9, 0.9, pressure=0.0), |
| OAMSignals( |
| certainty_level=0.5, |
| decision_made_for_user=False, |
| context_preservation=0.9, |
| dignity_preserved=0.9, |
| identity_assigned=False, |
| session_load_ratio=0.2, |
| uncertainty_disclosed=True, |
| autonomy_scope=0.2, |
| consent_present=True, |
| clinical_language_present=False, |
| memory_accessed_without_consent=False, |
| end_of_life_decision_attempted=False, |
| human_override_available=True, |
| ), |
| ) |
|
|
|
|
| def _red_decision(): |
| return run_evaluation( |
| HIRInput(0.9, 0.9, 0.9, 0.9, pressure=0.0), |
| OAMSignals( |
| certainty_level=0.5, |
| decision_made_for_user=True, |
| context_preservation=0.9, |
| dignity_preserved=0.9, |
| identity_assigned=False, |
| session_load_ratio=0.2, |
| uncertainty_disclosed=True, |
| autonomy_scope=0.2, |
| consent_present=True, |
| clinical_language_present=False, |
| memory_accessed_without_consent=False, |
| end_of_life_decision_attempted=False, |
| human_override_available=True, |
| ), |
| ) |
|
|
|
|
| |
| |
| |
|
|
| class TestAuditEventToDict: |
| def test_returns_dict(self): |
| event = create_audit_event(_green_decision()) |
| assert isinstance(audit_event_to_dict(event), dict) |
|
|
| def test_all_expected_keys_present(self): |
| event = create_audit_event(_green_decision()) |
| d = audit_event_to_dict(event) |
| expected = { |
| "timestamp", "event_type", "final_gate_state", "hard_stop", |
| "resonance", "pressure_adjusted_stability", "max_oam_severity", |
| "summary", "previous_hash", "current_hash", |
| } |
| assert set(d.keys()) == expected |
|
|
| def test_no_extra_keys(self): |
| event = create_audit_event(_green_decision()) |
| d = audit_event_to_dict(event) |
| assert len(d) == 10 |
|
|
| def test_values_match_event_fields(self): |
| event = create_audit_event(_green_decision()) |
| d = audit_event_to_dict(event) |
| assert d["timestamp"] == event.timestamp |
| assert d["event_type"] == event.event_type |
| assert d["final_gate_state"] == event.final_gate_state |
| assert d["hard_stop"] == event.hard_stop |
| assert d["resonance"] == event.resonance |
| assert d["pressure_adjusted_stability"] == event.pressure_adjusted_stability |
| assert d["max_oam_severity"] == event.max_oam_severity |
| assert d["summary"] == event.summary |
| assert d["current_hash"] == event.current_hash |
|
|
| def test_previous_hash_none_preserved(self): |
| event = create_audit_event(_green_decision()) |
| d = audit_event_to_dict(event) |
| assert d["previous_hash"] is None |
|
|
| def test_previous_hash_string_preserved(self): |
| sentinel = "b" * 64 |
| event = create_audit_event(_green_decision(), previous_hash=sentinel) |
| d = audit_event_to_dict(event) |
| assert d["previous_hash"] == sentinel |
|
|
| def test_no_raw_user_data_keys(self): |
| event = create_audit_event(_green_decision()) |
| d = audit_event_to_dict(event) |
| forbidden = {"prompt", "user_input", "phi", "pii", "clinical_data", "raw_input"} |
| assert not (forbidden & set(d.keys())) |
|
|
| def test_is_json_serializable(self): |
| event = create_audit_event(_green_decision()) |
| d = audit_event_to_dict(event) |
| serialized = json.dumps(d) |
| assert isinstance(serialized, str) |
|
|
|
|
| |
| |
| |
|
|
| class TestAuditEventFromDict: |
| def test_returns_audit_event(self): |
| event = create_audit_event(_green_decision()) |
| result = audit_event_from_dict(audit_event_to_dict(event)) |
| assert isinstance(result, AuditEvent) |
|
|
| def test_round_trip_equality(self): |
| event = create_audit_event(_green_decision()) |
| result = audit_event_from_dict(audit_event_to_dict(event)) |
| assert result == event |
|
|
| def test_round_trip_with_previous_hash(self): |
| e1 = create_audit_event(_green_decision()) |
| e2 = create_audit_event(_red_decision(), previous_hash=e1.current_hash) |
| result = audit_event_from_dict(audit_event_to_dict(e2)) |
| assert result == e2 |
|
|
| def test_result_is_frozen(self): |
| event = create_audit_event(_green_decision()) |
| result = audit_event_from_dict(audit_event_to_dict(event)) |
| with pytest.raises((AttributeError, TypeError)): |
| result.summary = "tampered" |
|
|
| def test_round_trip_hash_still_verifies(self): |
| from primordial_os.audit_log import verify_audit_chain |
| e1 = create_audit_event(_green_decision()) |
| e2 = create_audit_event(_red_decision(), previous_hash=e1.current_hash) |
| r1 = audit_event_from_dict(audit_event_to_dict(e1)) |
| r2 = audit_event_from_dict(audit_event_to_dict(e2)) |
| assert verify_audit_chain([r1, r2]) is True |
|
|
|
|
| |
| |
| |
|
|
| class TestAppendAuditEventJsonl: |
| def test_creates_file_when_absent(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, event) |
| assert p.exists() |
|
|
| def test_single_append_produces_one_line(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, event) |
| non_empty = [ln for ln in p.read_text("utf-8").splitlines() if ln.strip()] |
| assert len(non_empty) == 1 |
|
|
| def test_two_appends_produce_two_lines(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| e1 = create_audit_event(_green_decision()) |
| e2 = create_audit_event(_red_decision(), previous_hash=e1.current_hash) |
| append_audit_event_jsonl(p, e1) |
| append_audit_event_jsonl(p, e2) |
| non_empty = [ln for ln in p.read_text("utf-8").splitlines() if ln.strip()] |
| assert len(non_empty) == 2 |
|
|
| def test_five_appends_produce_five_lines(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| prev = None |
| for _ in range(5): |
| e = create_audit_event(_green_decision(), previous_hash=prev) |
| append_audit_event_jsonl(p, e) |
| prev = e.current_hash |
| non_empty = [ln for ln in p.read_text("utf-8").splitlines() if ln.strip()] |
| assert len(non_empty) == 5 |
|
|
| def test_each_line_is_valid_json(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| e1 = create_audit_event(_green_decision()) |
| e2 = create_audit_event(_red_decision(), previous_hash=e1.current_hash) |
| append_audit_event_jsonl(p, e1) |
| append_audit_event_jsonl(p, e2) |
| for raw_line in p.read_text("utf-8").splitlines(): |
| if raw_line.strip(): |
| assert isinstance(json.loads(raw_line), dict) |
|
|
| def test_file_is_valid_utf8(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, event) |
| p.read_bytes().decode("utf-8") |
|
|
| def test_existing_content_preserved_on_second_append(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| e1 = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, e1) |
| first_content = p.read_text("utf-8") |
| e2 = create_audit_event(_red_decision(), previous_hash=e1.current_hash) |
| append_audit_event_jsonl(p, e2) |
| second_content = p.read_text("utf-8") |
| assert second_content.startswith(first_content) |
|
|
| def test_hidden_file_path_rejected(self, tmp_path): |
| p = tmp_path / ".hidden.jsonl" |
| event = create_audit_event(_green_decision()) |
| with pytest.raises(ValueError): |
| append_audit_event_jsonl(p, event) |
|
|
| def test_missing_parent_directory_rejected(self, tmp_path): |
| p = tmp_path / "no_such_dir" / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| with pytest.raises(FileNotFoundError): |
| append_audit_event_jsonl(p, event) |
|
|
| def test_no_forbidden_field_names_written(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, event) |
| content = p.read_text("utf-8") |
| for term in ("prompt", "user_input", "phi", "pii", "clinical_data"): |
| assert term not in content.lower() |
|
|
|
|
| |
| |
| |
|
|
| class TestReadAuditEventsJsonl: |
| def test_returns_tuple(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, event) |
| result = read_audit_events_jsonl(p) |
| assert isinstance(result, tuple) |
|
|
| def test_single_event_round_trip(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, event) |
| result = read_audit_events_jsonl(p) |
| assert len(result) == 1 |
| assert result[0] == event |
|
|
| def test_multiple_events_round_trip(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| e1 = create_audit_event(_green_decision()) |
| e2 = create_audit_event(_red_decision(), previous_hash=e1.current_hash) |
| append_audit_event_jsonl(p, e1) |
| append_audit_event_jsonl(p, e2) |
| result = read_audit_events_jsonl(p) |
| assert len(result) == 2 |
| assert result[0] == e1 |
| assert result[1] == e2 |
|
|
| def test_order_preserved(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| written: list[AuditEvent] = [] |
| prev = None |
| for _ in range(4): |
| e = create_audit_event(_green_decision(), previous_hash=prev) |
| append_audit_event_jsonl(p, e) |
| written.append(e) |
| prev = e.current_hash |
| result = read_audit_events_jsonl(p) |
| assert list(result) == written |
|
|
| def test_all_elements_are_audit_event_instances(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| e1 = create_audit_event(_green_decision()) |
| e2 = create_audit_event(_red_decision(), previous_hash=e1.current_hash) |
| append_audit_event_jsonl(p, e1) |
| append_audit_event_jsonl(p, e2) |
| for item in read_audit_events_jsonl(p): |
| assert isinstance(item, AuditEvent) |
|
|
| def test_missing_file_raises_file_not_found(self, tmp_path): |
| p = tmp_path / "missing.jsonl" |
| with pytest.raises(FileNotFoundError): |
| read_audit_events_jsonl(p) |
|
|
| def test_empty_file_returns_empty_tuple(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| p.write_text("", encoding="utf-8") |
| result = read_audit_events_jsonl(p) |
| assert result == () |
|
|
|
|
| |
| |
| |
|
|
| class TestVerifyAuditJsonl: |
| def test_valid_single_event_returns_true(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, event) |
| assert verify_audit_jsonl(p) is True |
|
|
| def test_valid_three_event_chain_returns_true(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| chain = append_to_audit_chain((), _green_decision()) |
| chain = append_to_audit_chain(chain, _red_decision()) |
| chain = append_to_audit_chain(chain, _green_decision()) |
| for event in chain: |
| append_audit_event_jsonl(p, event) |
| assert verify_audit_jsonl(p) is True |
|
|
| def test_missing_file_returns_false(self, tmp_path): |
| p = tmp_path / "missing.jsonl" |
| assert verify_audit_jsonl(p) is False |
|
|
| def test_tampered_hash_field_returns_false(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| e1 = create_audit_event(_green_decision()) |
| e2 = create_audit_event(_red_decision(), previous_hash=e1.current_hash) |
| append_audit_event_jsonl(p, e1) |
| append_audit_event_jsonl(p, e2) |
| lines = p.read_text("utf-8").splitlines() |
| data = json.loads(lines[0]) |
| data["final_gate_state"] = "RED" |
| lines[0] = json.dumps(data, sort_keys=True, separators=(",", ":")) |
| p.write_text("\n".join(lines) + "\n", encoding="utf-8") |
| assert verify_audit_jsonl(p) is False |
|
|
| def test_broken_chain_link_returns_false(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| e1 = create_audit_event(_green_decision()) |
| e2 = create_audit_event(_red_decision(), previous_hash=None) |
| append_audit_event_jsonl(p, e1) |
| append_audit_event_jsonl(p, e2) |
| assert verify_audit_jsonl(p) is False |
|
|
| def test_tampered_resonance_returns_false(self, tmp_path): |
| p = tmp_path / "audit.jsonl" |
| event = create_audit_event(_green_decision()) |
| append_audit_event_jsonl(p, event) |
| lines = p.read_text("utf-8").splitlines() |
| data = json.loads(lines[0]) |
| data["resonance"] = 0.0 |
| lines[0] = json.dumps(data, sort_keys=True, separators=(",", ":")) |
| p.write_text("\n".join(lines) + "\n", encoding="utf-8") |
| assert verify_audit_jsonl(p) is False |
|
|
|
|
| |
| |
| |
|
|
| class TestSourceHygiene: |
| def test_no_coherence_terminology_in_source(self): |
| import primordial_os.audit_store as mod |
| source = inspect.getsource(mod) |
| assert "coherence" not in source.lower(), ( |
| "Banned term 'coherence' found in audit_store source" |
| ) |
|
|
| def test_no_forbidden_clinical_functionality_terms_in_source(self): |
| import primordial_os.audit_store as mod |
| source = inspect.getsource(mod) |
| for term in ("diagnos", "therapeutic", "clinical_decision", "prescri"): |
| assert term not in source.lower(), ( |
| f"Forbidden clinical-functionality term '{term}' found in audit_store source" |
| ) |
|
|