Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Fixed
^^^^^
- Sort results in get_all_available_tags, so it can be used with pytest-xdist.
- `generate_random_value` generate coherent `ref` and `value` values for complex attributes. :pr:`30` :pr:`37`
- Include PATCH operations in get_all_available_tags.

[0.2.0] - 2025-08-12
--------------------
Expand Down
6 changes: 6 additions & 0 deletions scim2_tester/checkers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"""

from .misc import random_url
from .patch_add import check_add_attribute
from .patch_remove import check_remove_attribute
from .patch_replace import check_replace_attribute
from .resource import resource_type_tests
from .resource_delete import object_deletion
from .resource_get import object_query
Expand Down Expand Up @@ -49,5 +52,8 @@
"object_replacement",
"object_deletion",
"resource_type_tests",
"check_add_attribute",
"check_remove_attribute",
"check_replace_attribute",
"random_url",
]
49 changes: 45 additions & 4 deletions scim2_tester/checkers/patch_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def check_add_attribute(

for urn, source_model in all_urns:
patch_value = generate_random_value(context, urn=urn, model=source_model)
mutability = get_annotation_by_urn(Mutability, urn, source_model)

patch_op = PatchOp[type(base_resource)](
operations=[
Expand All @@ -81,7 +82,7 @@ def check_add_attribute(
)

try:
updated_resource = context.client.modify(
modify_result = context.client.modify(
resource_model=type(base_resource),
id=base_resource.id,
patch_op=patch_op,
Expand All @@ -101,11 +102,51 @@ def check_add_attribute(
)
continue

if modify_result is not None:
if modify_actual_value := get_value_by_urn(modify_result, urn):
if not (
mutability == Mutability.write_only
or compare_field(patch_value, modify_actual_value)
):
results.append(
CheckResult(
status=Status.ERROR,
reason=f"PATCH modify() returned incorrect value for '{urn}'",
resource_type=model.__name__,
data={
"urn": urn,
"expected": patch_value,
"modify_actual": modify_actual_value,
},
)
)
continue

try:
updated_resource = context.client.query(
type(base_resource),
base_resource.id,
)
except SCIMClientError as exc:
results.append(
CheckResult(
status=Status.ERROR,
reason=f"Failed to query resource after add on '{urn}': {exc}",
resource_type=model.__name__,
data={
"urn": urn,
"error": exc,
"patch_value": patch_value,
},
)
)
continue

actual_value = get_value_by_urn(updated_resource, urn)

if get_annotation_by_urn(
Mutability, urn, source_model
) == Mutability.write_only or compare_field(patch_value, actual_value):
if mutability == Mutability.write_only or compare_field(
patch_value, actual_value
):
results.append(
CheckResult(
status=Status.SUCCESS,
Expand Down
50 changes: 43 additions & 7 deletions scim2_tester/checkers/patch_remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def check_remove_attribute(

for urn, source_model in all_urns:
initial_value = get_value_by_urn(full_resource, urn)
mutability = get_annotation_by_urn(Mutability, urn, source_model)
if initial_value is None:
continue

Expand All @@ -81,7 +82,7 @@ def check_remove_attribute(
)

try:
updated_resource = context.client.modify(
modify_result = context.client.modify(
resource_model=type(full_resource),
id=full_resource.id,
patch_op=remove_op,
Expand All @@ -101,13 +102,48 @@ def check_remove_attribute(
)
continue

actual_value = get_value_by_urn(updated_resource, urn)
if modify_result is not None:
if modify_actual_value := get_value_by_urn(modify_result, urn):
if (
mutability != Mutability.write_only
and modify_actual_value is not None
):
results.append(
CheckResult(
status=Status.ERROR,
reason=f"PATCH modify() did not remove attribute '{urn}'",
resource_type=model.__name__,
data={
"urn": urn,
"initial_value": initial_value,
"modify_actual": modify_actual_value,
},
)
)
continue

try:
updated_resource = context.client.query(
type(full_resource),
full_resource.id,
)
except SCIMClientError as exc:
results.append(
CheckResult(
status=Status.ERROR,
reason=f"Failed to query resource after remove on '{urn}': {exc}",
resource_type=model.__name__,
data={
"urn": urn,
"error": exc,
"initial_value": initial_value,
},
)
)
continue

if (
get_annotation_by_urn(Mutability, urn, source_model)
== Mutability.write_only
or actual_value is None
):
actual_value = get_value_by_urn(updated_resource, urn)
if mutability == Mutability.write_only or actual_value is None:
results.append(
CheckResult(
status=Status.SUCCESS,
Expand Down
51 changes: 45 additions & 6 deletions scim2_tester/checkers/patch_replace.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def check_replace_attribute(

for urn, source_model in all_urns:
patch_value = generate_random_value(context, urn=urn, model=source_model)
mutability = get_annotation_by_urn(Mutability, urn, source_model)

patch_op = PatchOp[type(base_resource)](
operations=[
Expand All @@ -79,8 +80,7 @@ def check_replace_attribute(
)

try:
# Perform the PATCH replace operation
updated_resource = context.client.modify(
modify_result = context.client.modify(
resource_model=type(base_resource),
id=base_resource.id,
patch_op=patch_op,
Expand All @@ -100,11 +100,50 @@ def check_replace_attribute(
)
continue

actual_value = get_value_by_urn(updated_resource, urn)
if modify_result is not None:
if modify_actual_value := get_value_by_urn(modify_result, urn):
if not (
mutability == Mutability.write_only
or compare_field(patch_value, modify_actual_value)
):
results.append(
CheckResult(
status=Status.ERROR,
reason=f"PATCH modify() returned incorrect value for '{urn}'",
resource_type=model.__name__,
data={
"urn": urn,
"expected": patch_value,
"modify_actual": modify_actual_value,
},
)
)
continue

try:
updated_resource = context.client.query(
type(base_resource),
base_resource.id,
)
except SCIMClientError as exc:
results.append(
CheckResult(
status=Status.ERROR,
reason=f"Failed to query resource after replace on '{urn}': {exc}",
resource_type=model.__name__,
data={
"urn": urn,
"error": exc,
"patch_value": patch_value,
},
)
)
continue

if get_annotation_by_urn(
Mutability, urn, source_model
) == Mutability.write_only or compare_field(patch_value, actual_value):
actual_value = get_value_by_urn(updated_resource, urn)
if mutability == Mutability.write_only or compare_field(
patch_value, actual_value
):
results.append(
CheckResult(
status=Status.SUCCESS,
Expand Down
8 changes: 8 additions & 0 deletions scim2_tester/checkers/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from ..utils import CheckContext
from ..utils import CheckResult
from ..utils import Status
from .patch_add import check_add_attribute
from .patch_remove import check_remove_attribute
from .patch_replace import check_replace_attribute
from .resource_delete import object_deletion
from .resource_get import _model_from_resource_type
from .resource_get import object_query
Expand Down Expand Up @@ -49,4 +52,9 @@ def resource_type_tests(
results.extend(object_replacement(context, model))
results.extend(object_deletion(context, model))

# PATCH operations
results.extend(check_add_attribute(context, model))
results.extend(check_remove_attribute(context, model))
results.extend(check_replace_attribute(context, model))

return results
Loading
Loading