""" Stress tests for the Flex-O ID lifecycle. Focus: collision avoidance, version ceiling, reproducibility. """ import os import sys import pytest sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from flexoentity import FlexOID, EntityType, EntityState, FlexoEntity # ────────────────────────────────────────────────────────────────────────────── def test_bulk_generation_uniqueness(): """Generate 10,000 IDs and assert uniqueness (statistical test).""" domain = "AF" etype = EntityType.QUESTION estate = EntityState.DRAFT seeds = [f"question {i}" for i in range(10_000)] ids = [] for seed in seeds: flexo_id = FlexOID.generate(domain, etype, estate, seed) ids.append(flexo_id) assert len(ids) == len(set(ids)), "ID collisions detected in bulk generation" def test_disambiguator_trigger(): """ Force a deterministic collision by hashing same text twice. Should produce -A suffix for second one. """ domain = "AF" etype = EntityType.QUESTION estate = EntityState.DRAFT text = "identical question text" id1 = FlexOID.generate(domain, etype, estate, text) id2 = FlexOID.generate(domain, etype, estate, text) assert id1 != id2 assert id1.signature == id2.signature assert "-A" in str(id2), f"Expected '-A' suffix in disambiguated ID, got {id2}" def test_id_reproducibility_across_runs(): """ The same seed on a new process (fresh _seen_hashes) should yield the same base ID (without suffix). """ domain = "AF" etype = EntityType.CATALOG estate = EntityState.DRAFT seed = "reproducibility test seed" id1 = FlexOID.generate(domain, etype, estate, seed) # Reset hash cache FlexOID._seen_hashes.clear() id2 = FlexOID.generate(domain, etype, estate, seed) assert id1 == id2 assert id1.signature == id2.signature def test_version_ceiling_enforcement(): """Simulate approaching @999 to trigger obsolescence guard.""" entity = FlexoEntity("AF", EntityType.EXAM, "Final Exam 2025", EntityState.DRAFT) entity.approve() # artificially bump version number to near ceiling entity.flexo_id = FlexOID.from_oid_and_version(entity.flexo_id, 998) # 998 → 999 is allowed entity.sign() assert entity.flexo_id.version == 999 # 999 → 1000 should raise RuntimeError with pytest.raises(RuntimeError): entity.sign() def test_massive_lifecycle_simulation(): """ Generate 100 random entities, simulate multiple edits and state transitions, ensure all final IDs and fingerprints are unique and valid. """ import random texts = [f"random question {i}" for i in range(100)] entities = [FlexoEntity("AF", EntityType.QUESTION, t) for t in texts] for e in entities: # random edit, approval, signing e.modify_content(e.text_seed + " updated") e.approve() if random.random() > 0.3: e.sign() if random.random() > 0.6: e.publish() ids = [e.flexo_id for e in entities] fps = [e.flexo_id.signature for e in entities] assert len(ids) == len(set(ids)), "Duplicate IDs after random lifecycle" assert len(fps) == len(set(fps)), "Duplicate fingerprints after random lifecycle"