source: flexoentity/tests/test_id_stress.py@ 8aa20c7

Last change on this file since 8aa20c7 was 8aa20c7, checked in by Enrico Schwass <ennoausberlin@…>, 2 months ago

full refactoring of FlexOID

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