Skip to content

Commit d2d56d2

Browse files
committed
fix: respect the primary attribute unique truthy value when generating random values
1 parent d7bb973 commit d2d56d2

File tree

3 files changed

+60
-4
lines changed

3 files changed

+60
-4
lines changed

doc/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Fixed
88
^^^^^
99
- Error and critical check results directly raise an exception when ``raise_exceptions=True``.
1010
- Use ``ComplexAttribute`` instead of ``MultiValuedComplexAttribute`` to generate data.
11+
- Respect the ``primary`` attribute unique truthy value when generating random values.
1112

1213
[0.2.2] - 2025-08-13
1314
--------------------

scim2_tester/filling.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ def fill_with_random_values(
171171
value = generate_random_value(context, urn=urn, model=type(obj))
172172
set_value_by_urn(obj, urn, value)
173173

174+
fix_primary_attributes(obj)
175+
174176
return obj
175177

176178

@@ -194,3 +196,30 @@ def fill_complex_attribute_with_random_values(
194196
):
195197
obj.value = ref.rsplit("/", 1)[-1]
196198
return obj
199+
200+
201+
def fix_primary_attributes(obj: Resource[Any]) -> None:
202+
"""Fix primary attributes to respect RFC 7643 constraints.
203+
204+
Ensures that for multi-valued attributes with 'primary' sub-attributes:
205+
- If the list has one item, sets primary=True
206+
- If the list has multiple items, exactly one has primary=True and others primary=False
207+
- If the list is empty, does nothing
208+
209+
According to RFC 7643 §2.4: The primary attribute value "true" MUST appear no more than once.
210+
"""
211+
for field_name, _field_info in type(obj).model_fields.items():
212+
attr_value = getattr(obj, field_name, None)
213+
if not attr_value or not isinstance(attr_value, list) or len(attr_value) == 0:
214+
continue
215+
216+
first_item = attr_value[0]
217+
if not hasattr(first_item, "primary"):
218+
continue
219+
220+
if len(attr_value) == 1:
221+
attr_value[0].primary = True
222+
else:
223+
primary_index = random.randint(0, len(attr_value) - 1)
224+
for i, item in enumerate(attr_value):
225+
item.primary = i == primary_index

tests/test_filling.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
from typing import Literal
44
from unittest.mock import patch
55

6+
from scim2_models import Email
67
from scim2_models import Group
8+
from scim2_models import PhoneNumber
79
from scim2_models import User
810
from scim2_models.resources.user import X509Certificate
911

1012
from scim2_tester.filling import fill_with_random_values
13+
from scim2_tester.filling import fix_primary_attributes
1114
from scim2_tester.filling import generate_random_value
1215
from scim2_tester.filling import get_model_from_ref_type
1316
from scim2_tester.filling import get_random_example_value
@@ -123,8 +126,6 @@ def test_generate_random_value_with_examples(testing_context):
123126

124127
def test_generate_random_value_phone_number(testing_context):
125128
"""Generates phone numbers correctly."""
126-
from scim2_models import PhoneNumber
127-
128129
value = generate_random_value(testing_context, "phoneNumbers.value", PhoneNumber)
129130

130131
assert isinstance(value, str)
@@ -191,8 +192,33 @@ def test_generate_random_value_reference_external(testing_context):
191192

192193
def test_generate_random_value_default_string(testing_context):
193194
"""Generates default string values."""
194-
from scim2_models import User
195-
196195
value = generate_random_value(testing_context, "title", User)
197196

198197
assert isinstance(value, str)
198+
199+
200+
def test_fix_primary_attributes_single_object():
201+
"""Ensures single email gets primary=True."""
202+
user = User(user_name="test")
203+
user.emails = [Email(value="[email protected]", type=Email.Type.work)]
204+
205+
fix_primary_attributes(user)
206+
207+
assert len(user.emails) == 1
208+
assert user.emails[0].primary is True
209+
210+
211+
def test_fix_primary_attributes_multiple_objects():
212+
"""Ensures exactly one email has primary=True when multiple exist."""
213+
user = User(user_name="test")
214+
user.emails = [
215+
Email(value="[email protected]", type=Email.Type.work),
216+
Email(value="[email protected]", type=Email.Type.home),
217+
Email(value="[email protected]", type=Email.Type.other),
218+
]
219+
220+
fix_primary_attributes(user)
221+
222+
assert len(user.emails) == 3
223+
primary_count = sum(1 for email in user.emails if email.primary)
224+
assert primary_count == 1

0 commit comments

Comments
 (0)