| 1 | """
|
|---|
| 2 | Persistence and integrity verification tests for Flex-O entities.
|
|---|
| 3 | Ensures fingerprints survive JSON export/import and detect tampering.
|
|---|
| 4 | """
|
|---|
| 5 |
|
|---|
| 6 | import os
|
|---|
| 7 | import sys
|
|---|
| 8 | import json
|
|---|
| 9 | import pytest
|
|---|
| 10 |
|
|---|
| 11 | from datetime import datetime
|
|---|
| 12 |
|
|---|
| 13 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|---|
| 14 |
|
|---|
| 15 | from flexoentity import FlexOID, EntityType, EntityState, FlexoEntity
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 | @pytest.fixture
|
|---|
| 19 | def approved_entity():
|
|---|
| 20 | q = FlexoEntity(
|
|---|
| 21 | domain="AF",
|
|---|
| 22 | etype=EntityType.QUESTION,
|
|---|
| 23 | text_seed="What is Ohm’s law?",
|
|---|
| 24 | state=EntityState.DRAFT,
|
|---|
| 25 | )
|
|---|
| 26 | q.approve()
|
|---|
| 27 | q.sign()
|
|---|
| 28 | q.publish()
|
|---|
| 29 | return q
|
|---|
| 30 |
|
|---|
| 31 | def test_json_roundtrip_preserves_integrity(approved_entity):
|
|---|
| 32 | """
|
|---|
| 33 | Export to JSON and reload — ensure state-aware and content-only integrity behave as expected.
|
|---|
| 34 | """
|
|---|
| 35 |
|
|---|
| 36 | json_str = approved_entity.to_json()
|
|---|
| 37 | loaded = FlexoEntity.from_json(json_str)
|
|---|
| 38 |
|
|---|
| 39 | # Because the signature encodes lifecycle state, any state change breaks strict integrity
|
|---|
| 40 | assert not FlexoEntity.verify_integrity(loaded)
|
|---|
| 41 |
|
|---|
| 42 | assert approved_entity.flexo_id.signature == loaded.flexo_id.signature
|
|---|
| 43 | assert approved_entity.flexo_id == loaded.flexo_id
|
|---|
| 44 | assert loaded.state == approved_entity.state
|
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 | def test_json_tampering_detection(approved_entity):
|
|---|
| 48 | """Tampering with content should invalidate fingerprint verification."""
|
|---|
| 49 | json_str = approved_entity.to_json()
|
|---|
| 50 | tampered_data = json.loads(json_str)
|
|---|
| 51 | tampered_data["text_seed"] = "Tampered content injection"
|
|---|
| 52 | tampered_json = json.dumps(tampered_data)
|
|---|
| 53 | loaded = FlexoEntity.from_json(tampered_json)
|
|---|
| 54 | assert not FlexoEntity.verify_integrity(loaded)
|
|---|
| 55 |
|
|---|
| 56 | def test_json_file_corruption(approved_entity, tmp_path):
|
|---|
| 57 | """Simulate file corruption — integrity check must fail."""
|
|---|
| 58 | file = tmp_path / "entity.json"
|
|---|
| 59 | json_str = approved_entity.to_json()
|
|---|
| 60 | file.write_text(json_str)
|
|---|
| 61 |
|
|---|
| 62 | # Corrupt the file
|
|---|
| 63 | corrupted = json_str.replace("Ohm’s", "Omm’s")
|
|---|
| 64 | file.write_text(corrupted)
|
|---|
| 65 |
|
|---|
| 66 | loaded = FlexoEntity.from_json(file.read_text())
|
|---|
| 67 | assert not FlexoEntity.verify_integrity(loaded)
|
|---|