source: flexoentity/tests/test_id_stress.py@ 37b5d11

Last change on this file since 37b5d11 was 4ceca57, checked in by Enrico Schwass <ennoausberlin@…>, 3 months ago

some tests improved

  • Property mode set to 100644
File size: 4.0 KB
RevLine 
[59342ce]1"""
2Stress tests for the Flex-O ID lifecycle.
3Focus: collision avoidance, version ceiling, reproducibility.
4"""
[02d288d]5
[59342ce]6import pytest
[8a238e2]7import random
[02d288d]8import logging
9from flexoentity import FlexOID, EntityType, EntityState
10from builder.questions import RadioQuestion, AnswerOption
[59342ce]11
[02d288d]12logger = logging.getLogger(__name__)
[2f650ac]13
[02d288d]14def test_bulk_generation_uniqueness(domain):
15 """
[4ceca57]16 Generate 100,000 IDs and ensure uniqueness using safe_generate().
[02d288d]17 If a collision occurs, safe_generate() must resolve it automatically
18 via salt + date adjustment.
19 """
[59342ce]20 etype = EntityType.QUESTION
21 estate = EntityState.DRAFT
[4ceca57]22 seeds = [f"question {i}" for i in range(100000)]
[02d288d]23
24 # Simulate a simple in-memory repository for collision detection
25 repo = {}
26
27 def repo_get(oid_str):
28 return repo.get(str(oid_str))
[59342ce]29
[02d288d]30 # Generate IDs using safe_generate
31 ids = []
32 for seed in seeds:
33 oid = FlexOID.safe_generate(domain.domain, etype, estate, seed, repo=repo)
34 assert isinstance(oid, FlexOID)
35 ids.append(str(oid))
36 repo[str(oid)] = oid # register for future collision detection
[59342ce]37
[02d288d]38 unique_count = len(set(ids))
39 total_count = len(ids)
40 collisions = total_count - unique_count
[59342ce]41
[02d288d]42 logger.info(f"Generated {total_count} IDs ({collisions} collisions handled).")
[8a238e2]43
[02d288d]44 # Assert that safe_generate avoided duplicates
45 assert total_count == unique_count, f"Unexpected duplicate IDs ({collisions} found)"
46
47 # Sanity check: IDs should look canonical
48 assert all(id_str.startswith("SIG-") for id_str in ids)
49 assert all("@" in id_str for id_str in ids)
50
51def test_id_generation_is_deterministic(domain):
[8a238e2]52 """
53 Generating the same entity twice with same inputs yields identical ID.
54 (No runtime disambiguation; IDs are deterministic by design.)
55 """
56 etype = EntityType.QUESTION
57 estate = EntityState.DRAFT
58 text = "identical question text"
[02d288d]59
60 id1 = FlexOID.generate(domain.domain, etype, estate, text)
61 id2 = FlexOID.generate(domain.domain, etype, estate, text)
62 # IDs must be identical because generation is deterministic
[8a238e2]63 assert id1 == id2
64
65
[02d288d]66def test_id_reproducibility_across_runs(domain):
[59342ce]67 """
68 The same seed on a new process (fresh _seen_hashes)
69 should yield the same base ID (without suffix).
70 """
71 etype = EntityType.CATALOG
72 estate = EntityState.DRAFT
73 seed = "reproducibility test seed"
[02d288d]74
75 id1 = FlexOID.generate(domain.domain, etype, estate, seed)
[59342ce]76 FlexOID._seen_hashes.clear()
[02d288d]77 id2 = FlexOID.generate(domain.domain, etype, estate, seed)
78
[59342ce]79 assert id1 == id2
80
81
[02d288d]82def test_version_ceiling_enforcement(radio_question):
[59342ce]83 """Simulate approaching @999 to trigger obsolescence guard."""
[02d288d]84 q = radio_question
85 q.approve()
86
[59342ce]87 # artificially bump version number to near ceiling
[02d288d]88 q.flexo_id = FlexOID.from_oid_and_version(q.flexo_id, 998)
[59342ce]89
90 # 998 → 999 is allowed
[02d288d]91 q.sign()
92 assert q.flexo_id.version == 999
[59342ce]93
94 # 999 → 1000 should raise RuntimeError
95 with pytest.raises(RuntimeError):
[02d288d]96 q.sign()
[59342ce]97
98
[02d288d]99def test_massive_lifecycle_simulation(domain):
[59342ce]100 """
[02d288d]101 Generate 100 random RadioQuestions, simulate multiple edits and state transitions,
[59342ce]102 ensure all final IDs and fingerprints are unique and valid.
103 """
[02d288d]104 entities = [
105 RadioQuestion(
106 domain=domain,
107 etype=EntityType.QUESTION,
108 state=EntityState.DRAFT,
109 text=f"random question {i}",
110 options=[
111 AnswerOption(id="opt4", text="HF (3–30 MHz)", points=1),
112 AnswerOption(id="opt5", text="VHF (30–300 MHz)", points=0),
113 ],
114 )
115 for i in range(100)
116 ]
[59342ce]117
118 for e in entities:
[02d288d]119 # random edit
120 e.text += " updated"
[8a238e2]121 e._update_fingerprint()
[02d288d]122
123 # lifecycle transitions
[59342ce]124 e.approve()
125 if random.random() > 0.3:
126 e.sign()
127 if random.random() > 0.6:
128 e.publish()
129
[02d288d]130 flexoids = [e.flexo_id for e in entities]
131 assert len(flexoids) == len(set(flexoids)), "Duplicate FlexOIDs after lifecycle simulation"
Note: See TracBrowser for help on using the repository browser.