Skip to content

Commit 6f6c348

Browse files
committed
refactor: use scim2-models path management
1 parent 9925ee9 commit 6f6c348

9 files changed

Lines changed: 792 additions & 596 deletions

File tree

doc/changelog.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Changelog
22
=========
33

4+
[0.2.5] - Unreleased
5+
--------------------
6+
7+
Changed
8+
^^^^^^^
9+
- Use scim2-models native path management.
10+
411
[0.2.4] - 2025-10-10
512
--------------------
613

pyproject.toml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "uv_build"
55
[project]
66
name = "scim2-tester"
77
version = "0.2.4"
8-
description = " Check SCIM RFCs server compliance"
8+
description = "Check SCIM RFCs server compliance"
99
authors = [{name="Yaal Coop", email="contact@yaal.coop"}]
1010
license = {file = "LICENSE.md"}
1111
readme = "README.md"
@@ -27,8 +27,8 @@ classifiers = [
2727

2828
requires-python = ">= 3.10"
2929
dependencies = [
30-
"scim2-client>=0.6.1",
31-
"scim2-models>=0.5.0",
30+
"scim2-client>=0.7.0",
31+
"scim2-models>=0.6.1",
3232
]
3333

3434
[project.urls]
@@ -40,9 +40,6 @@ funding = "https://github.com/sponsors/python-scim"
4040
[tool.uv.build-backend]
4141
module-root = ""
4242

43-
[tool.uv.sources]
44-
scim2-models = { path = "../scim2-models", editable = true }
45-
4643
[project.optional-dependencies]
4744
httpx = [
4845
"scim2-client[httpx]>=0.4.0",

scim2_tester/checkers/resource_get.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def _model_from_resource_type(
1919
corresponding Python model class registered in the SCIM client.
2020
"""
2121
for resource_model in context.client.resource_models:
22-
if resource_model.model_fields["schemas"].default[0] == resource_type.schema_:
22+
if resource_model.__schema__ == resource_type.schema_:
2323
return resource_model
2424

2525
return None

scim2_tester/filling.py

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,15 @@
55
from inspect import isclass
66
from typing import TYPE_CHECKING
77
from typing import Any
8-
from typing import get_args
9-
from typing import get_origin
108

9+
from pydantic import Base64Bytes
1110
from scim2_models import ComplexAttribute
1211
from scim2_models import Extension
13-
from scim2_models import ExternalReference
1412
from scim2_models import Mutability
1513
from scim2_models import Reference
1614
from scim2_models import Required
1715
from scim2_models import Resource
18-
from scim2_models import URIReference
1916
from scim2_models.path import Path
20-
from scim2_models.utils import UNION_TYPES
21-
from scim2_models.utils import Base64Bytes
2217

2318
if TYPE_CHECKING:
2419
from scim2_tester.utils import CheckContext
@@ -37,23 +32,13 @@ def get_random_example_value(path: Path[Any]) -> Any | None:
3732

3833

3934
def get_model_from_ref_type(
40-
context: "CheckContext", ref_type: type, different_than: type[Resource[Any]] | None
35+
context: "CheckContext",
36+
ref_type: type[Reference],
37+
different_than: type[Resource[Any]] | None,
4138
) -> type[Resource[Any]]:
42-
"""Return "User" from "Union[Literal['User'], Literal['Group']]"."""
43-
44-
def get_model_from_ref_type_(ref_type: type) -> Any:
45-
if get_origin(ref_type) in UNION_TYPES:
46-
return [
47-
get_model_from_ref_type_(sub_ref_type)
48-
for sub_ref_type in get_args(ref_type)
49-
]
50-
51-
model_name = get_args(ref_type)[0]
52-
model = context.client.get_resource_model(model_name)
53-
return model
54-
55-
models = get_model_from_ref_type_(ref_type)
56-
models = models if isinstance(models, list) else [models]
39+
"""Return the Resource model referenced by a Reference type."""
40+
ref_names = ref_type.__reference_types__
41+
models = [context.client.get_resource_model(ref_name) for ref_name in ref_names]
5742
acceptable_models = [model for model in models if model != different_than]
5843

5944
if not acceptable_models:
@@ -116,16 +101,17 @@ def generate_random_value(
116101
elif field_type is bool:
117102
value = random.choice([True, False])
118103

119-
elif get_origin(field_type) is Reference and get_args(field_type)[0] != Any:
120-
ref_type = get_args(field_type)[0]
121-
if ref_type not in (ExternalReference, URIReference):
122-
ref_model = get_model_from_ref_type(context, ref_type, different_than=model)
104+
elif isclass(field_type) and issubclass(field_type, Reference):
105+
ref_types = field_type.__reference_types__
106+
if not ref_types or "external" in ref_types or "uri" in ref_types:
107+
value = f"https://{str(uuid.uuid4())}.test"
108+
else:
109+
ref_model = get_model_from_ref_type(
110+
context, field_type, different_than=model
111+
)
123112
ref_obj = context.resource_manager.create_and_register(ref_model)
124113
value = ref_obj.meta.location if ref_obj.meta else None
125114

126-
else:
127-
value = f"https://{str(uuid.uuid4())}.test"
128-
129115
elif isclass(field_type) and issubclass(field_type, Enum):
130116
value = random.choice(list(field_type))
131117

@@ -134,11 +120,6 @@ def generate_random_value(
134120
context, field_type(), mutability=mutability, required=required
135121
) # type: ignore[arg-type]
136122

137-
elif isclass(field_type) and issubclass(field_type, Extension):
138-
value = fill_with_random_values(
139-
context, field_type(), mutability=mutability, required=required
140-
) # type: ignore[arg-type]
141-
142123
elif field_type is None and isclass(model) and issubclass(model, Extension):
143124
value = fill_with_random_values(
144125
context, model(), mutability=mutability, required=required

tests/conftest.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111

1212
@pytest.fixture
1313
def scim_client(httpserver):
14-
client = Client(base_url=f"http://localhost:{httpserver.port}")
15-
scim_client = SyncSCIMClient(client, resource_models=[User[EnterpriseUser], Group])
16-
scim_client.register_naive_resource_types()
17-
return scim_client
14+
with Client(base_url=f"http://localhost:{httpserver.port}") as client:
15+
scim_client = SyncSCIMClient(
16+
client, resource_models=[User[EnterpriseUser], Group]
17+
)
18+
scim_client.register_naive_resource_types()
19+
yield scim_client
1820

1921

2022
@pytest.fixture

tests/test_filling.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
"""Test automatic field filling functionality."""
22

33
from typing import Annotated
4-
from typing import Literal
4+
from typing import Union
55
from unittest.mock import patch
66

77
from scim2_models import Email
88
from scim2_models import EnterpriseUser
99
from scim2_models import Group
1010
from scim2_models import Mutability
1111
from scim2_models import PhoneNumber
12+
from scim2_models import Reference
1213
from scim2_models import Required
1314
from scim2_models import User
1415
from scim2_models.attributes import ComplexAttribute
@@ -32,7 +33,7 @@ def test_generate_random_value_bytes_field(testing_context):
3233

3334
def test_model_resolution_from_reference_type(testing_context):
3435
"""Ensures model resolution from reference type excludes specified models."""
35-
ref_type = Literal["User"] | Literal["Group"]
36+
ref_type = Reference[Union["User", "Group"]]
3637
different_than = Group
3738

3839
result = get_model_from_ref_type(testing_context, ref_type, different_than)
@@ -239,7 +240,7 @@ class TestComplexAttr(ComplexAttribute):
239240
immutable_field: Annotated[str | None, Mutability.immutable] = None
240241

241242
class TestResource(Resource):
242-
schemas: list[str] = ["urn:test:TestResource"]
243+
__schema__: str = "urn:test:TestResource"
243244
test_attr: TestComplexAttr | None = None
244245

245246
filled = fill_with_random_values(testing_context, TestResource())
@@ -261,7 +262,7 @@ class TestResource(Resource):
261262

262263
def test_get_model_from_ref_type_fallback_when_no_acceptable_models(testing_context):
263264
"""Ensures fallback to first model when all models are excluded by different_than."""
264-
ref_type = Literal["User"]
265+
ref_type = Reference["User"]
265266
result = get_model_from_ref_type(
266267
testing_context, ref_type, different_than=User[EnterpriseUser]
267268
)

tests/test_resource.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Complex(ComplexAttribute):
2727

2828

2929
class CustomModel(Resource):
30-
schemas: list[str] = ["org:test:CustomModel"]
30+
__schema__: str = "org:test:CustomModel"
3131

3232
class Type(str, Enum):
3333
foo = "foo"

tests/test_utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from scim2_tester.utils import checker
1515
from scim2_tester.utils import fields_equality
1616
from scim2_tester.utils import get_registered_tags
17+
from tests.utils import build_nested_response
1718

1819

1920
def test_checker_decorator_with_tags():
@@ -335,3 +336,14 @@ def test_check_result_repr():
335336
assert "title=" not in repr_str
336337
assert "reason='Success without name'" in repr_str
337338
assert "Status.SUCCESS" in repr_str
339+
340+
341+
def test_build_nested_response_with_unknown_extension_urn():
342+
"""Test build_nested_response initializes namespace for unknown extension URNs."""
343+
base_response = {"id": "123", "userName": "test"}
344+
path = "urn:custom:extension:fieldName"
345+
346+
result = build_nested_response(base_response, path, "value")
347+
348+
assert "urn:custom:extension" in result
349+
assert result["urn:custom:extension"]["fieldName"] == "value"

0 commit comments

Comments
 (0)