Skip to content

Commit cd5d652

Browse files
committed
refactor: raise exceptions from the checker decorator instead of CheckResult
1 parent df5cbeb commit cd5d652

15 files changed

Lines changed: 260 additions & 104 deletions

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ classifiers = [
2727
requires-python = ">= 3.10"
2828
dependencies = [
2929
"scim2-client>=0.4.0",
30+
"exceptiongroup>=1.0.0 ; python_full_version < '3.11'",
3031
]
3132

3233
[project.urls]

scim2_tester/checker.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import argparse
22
import uuid
3+
from typing import Any
34

4-
from scim2_client import SCIMClient
5+
from scim2_client.engines.httpx import SyncSCIMClient
56
from scim2_models import Error
67

78
from scim2_tester.resource import check_resource_type
@@ -19,35 +20,34 @@
1920
def check_random_url(context: CheckContext) -> CheckResult:
2021
"""Check that a request to a random URL returns a 404 Error object."""
2122
probably_invalid_url = f"/{str(uuid.uuid4())}"
22-
response = context.client.query(url=probably_invalid_url, raise_scim_errors=False)
23+
response: Any = context.client.query(
24+
url=probably_invalid_url, raise_scim_errors=False
25+
)
2326

2427
if not isinstance(response, Error):
2528
return CheckResult(
26-
context.conf,
2729
status=Status.ERROR,
2830
reason=f"{probably_invalid_url} did not return an Error object",
2931
data=response,
3032
)
3133

3234
if response.status != 404:
3335
return CheckResult(
34-
context.conf,
3536
status=Status.ERROR,
3637
reason=f"{probably_invalid_url} did return an object, but the status code is {response.status}",
3738
data=response,
3839
)
3940

4041
return CheckResult(
41-
context.conf,
4242
status=Status.SUCCESS,
4343
reason=f"{probably_invalid_url} correctly returned a 404 error",
4444
data=response,
4545
)
4646

4747

4848
def check_server(
49-
client: SCIMClient,
50-
raise_exceptions=False,
49+
client: SyncSCIMClient,
50+
raise_exceptions: bool = False,
5151
include_tags: set[str] | None = None,
5252
exclude_tags: set[str] | None = None,
5353
resource_types: list[str] | None = None,
@@ -191,7 +191,7 @@ def check_server(
191191
headers={"Authorization": f"Bearer {args.token}"} if args.token else None,
192192
)
193193
scim = SyncSCIMClient(client)
194-
scim.discover()
194+
scim.discover() # type: ignore[no-untyped-call]
195195

196196
include_tags: set[str] | None = (
197197
set(args.include_tags) if args.include_tags else None

scim2_tester/filling.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424

2525

2626
def model_from_ref_type(
27-
context: CheckContext, ref_type: type, different_than: type[Resource]
28-
) -> type[Resource]:
27+
context: CheckContext, ref_type: type, different_than: type[Resource[Any]]
28+
) -> type[Resource[Any]]:
2929
"""Return "User" from "Union[Literal['User'], Literal['Group']]"."""
3030

31-
def model_from_ref_type_(ref_type):
31+
def model_from_ref_type_(ref_type: type) -> Any:
3232
if get_origin(ref_type) in UNION_TYPES:
3333
return [
3434
model_from_ref_type_(sub_ref_type)
@@ -47,10 +47,10 @@ def model_from_ref_type_(ref_type):
4747

4848
def generate_random_value(
4949
context: CheckContext,
50-
obj: Resource,
50+
obj: Resource[Any],
5151
resource_manager: "ResourceManager",
5252
field_name: str,
53-
):
53+
) -> Any:
5454
field = obj.__class__.model_fields[field_name]
5555
field_type = obj.get_field_root_type(field_name)
5656

@@ -113,10 +113,10 @@ def generate_random_value(
113113

114114
def fill_with_random_values(
115115
context: CheckContext,
116-
obj: Resource,
116+
obj: Resource[Any],
117117
resource_manager: "ResourceManager",
118118
field_names: list[str] | None = None,
119-
) -> Resource | None:
119+
) -> Resource[Any] | None:
120120
"""Fill an object with random values generated according the attribute types.
121121
122122
:param context: The check context containing the SCIM client and configuration

scim2_tester/resource.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def check_resource_type(
2525
if not model:
2626
return [
2727
CheckResult(
28-
context.conf,
2928
status=Status.ERROR,
3029
reason=f"No Schema matching the ResourceType {resource_type.id}",
3130
)

scim2_tester/resource_delete.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any
2+
13
from scim2_models import Resource
24

35
from scim2_tester.utils import CheckContext
@@ -7,7 +9,9 @@
79

810

911
@checker("crud:delete")
10-
def check_object_deletion(context: CheckContext, model: type[Resource]) -> CheckResult:
12+
def check_object_deletion(
13+
context: CheckContext, model: type[Resource[Any]]
14+
) -> CheckResult:
1115
"""Test object deletion with automatic cleanup.
1216
1317
Creates a test object specifically for deletion testing, performs the
@@ -23,16 +27,16 @@ def check_object_deletion(context: CheckContext, model: type[Resource]) -> Check
2327
if test_obj in context.resource_manager.resources:
2428
context.resource_manager.resources.remove(test_obj)
2529

26-
context.client.delete(
27-
model,
28-
test_obj.id,
29-
expected_status_codes=context.conf.expected_status_codes or [204],
30-
)
30+
if test_obj.id is not None:
31+
context.client.delete(
32+
model,
33+
test_obj.id,
34+
expected_status_codes=context.conf.expected_status_codes or [204],
35+
)
3136

3237
try:
3338
context.client.query(model, test_obj.id)
3439
return CheckResult(
35-
context.conf,
3640
status=Status.ERROR,
3741
reason=f"{model.__name__} object with id {test_obj.id} still exists after deletion",
3842
)
@@ -41,7 +45,6 @@ def check_object_deletion(context: CheckContext, model: type[Resource]) -> Check
4145
pass
4246

4347
return CheckResult(
44-
context.conf,
4548
status=Status.SUCCESS,
4649
reason=f"Successfully deleted {model.__name__} object with id {test_obj.id}",
4750
)

scim2_tester/resource_get.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any
2+
13
from scim2_models import Resource
24
from scim2_models import ResourceType
35

@@ -9,7 +11,7 @@
911

1012
def model_from_resource_type(
1113
context: CheckContext, resource_type: ResourceType
12-
) -> type[Resource] | None:
14+
) -> type[Resource[Any]] | None:
1315
"""Return the Resource model class from a given ResourceType.
1416
1517
ResourceType.name contains the resource type name (e.g., "User", "Group").
@@ -27,7 +29,9 @@ def model_from_resource_type(
2729

2830

2931
@checker("crud:read")
30-
def check_object_query(context: CheckContext, model: type[Resource]) -> CheckResult:
32+
def check_object_query(
33+
context: CheckContext, model: type[Resource[Any]]
34+
) -> CheckResult:
3135
"""Test object query by ID with automatic cleanup.
3236
3337
Creates a temporary test object, queries it by ID to validate the
@@ -46,7 +50,6 @@ def check_object_query(context: CheckContext, model: type[Resource]) -> CheckRes
4650
)
4751

4852
return CheckResult(
49-
context.conf,
5053
status=Status.SUCCESS,
5154
reason=f"Successfully queried {model.__name__} object with id {test_obj.id}",
5255
data=response,
@@ -55,7 +58,7 @@ def check_object_query(context: CheckContext, model: type[Resource]) -> CheckRes
5558

5659
@checker("crud:read")
5760
def check_object_query_without_id(
58-
context: CheckContext, model: type[Resource]
61+
context: CheckContext, model: type[Resource[Any]]
5962
) -> CheckResult:
6063
"""Test object listing without ID with automatic cleanup.
6164
@@ -75,14 +78,12 @@ def check_object_query_without_id(
7578
found = any(test_obj.id == resource.id for resource in response.resources)
7679
if not found:
7780
return CheckResult(
78-
context.conf,
7981
status=Status.ERROR,
8082
reason=f"Could not find {model.__name__} object with id {test_obj.id} in list response",
8183
data=response,
8284
)
8385

8486
return CheckResult(
85-
context.conf,
8687
status=Status.SUCCESS,
8788
reason=f"Successfully found {model.__name__} object with id {test_obj.id} in list response",
8889
data=response,

scim2_tester/resource_post.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any
2+
13
from scim2_models import Resource
24

35
from scim2_tester.utils import CheckContext
@@ -7,7 +9,9 @@
79

810

911
@checker("crud:create")
10-
def check_object_creation(context: CheckContext, model: type[Resource]) -> CheckResult:
12+
def check_object_creation(
13+
context: CheckContext, model: type[Resource[Any]]
14+
) -> CheckResult:
1115
"""Test object creation with automatic cleanup.
1216
1317
Creates a test object of the specified model type, validates the creation
@@ -20,7 +24,6 @@ def check_object_creation(context: CheckContext, model: type[Resource]) -> Check
2024
created_obj = context.resource_manager.create_and_register(model)
2125

2226
return CheckResult(
23-
context.conf,
2427
status=Status.SUCCESS,
2528
reason=f"Successfully created {model.__name__} object with id {created_obj.id}",
2629
data=created_obj,

scim2_tester/resource_put.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any
2+
13
from scim2_models import Mutability
24
from scim2_models import Resource
35

@@ -10,7 +12,7 @@
1012

1113
@checker("crud:update")
1214
def check_object_replacement(
13-
context: CheckContext, model: type[Resource]
15+
context: CheckContext, model: type[Resource[Any]]
1416
) -> CheckResult:
1517
"""Test object replacement (PUT) with automatic cleanup.
1618
@@ -44,7 +46,6 @@ def check_object_replacement(
4446
)
4547

4648
return CheckResult(
47-
context.conf,
4849
status=Status.SUCCESS,
4950
reason=f"Successfully replaced {model.__name__} object with id {test_obj.id}",
5051
data=response,

scim2_tester/resource_types.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ def check_query_all_resource_types(context: CheckContext) -> CheckResult:
3939
)
4040
available = ", ".join([f"'{resource.name}'" for resource in response.resources])
4141
reason = f"Resource types available are: {available}"
42-
return CheckResult(
43-
context.conf, status=Status.SUCCESS, reason=reason, data=response.resources
44-
)
42+
return CheckResult(status=Status.SUCCESS, reason=reason, data=response.resources)
4543

4644

4745
@checker("discovery", "resource-types")
@@ -54,14 +52,10 @@ def check_query_resource_type_by_id(
5452
expected_status_codes=context.conf.expected_status_codes or [200],
5553
)
5654
if isinstance(response, Error):
57-
return CheckResult(
58-
context.conf, status=Status.ERROR, reason=response.detail, data=response
59-
)
55+
return CheckResult(status=Status.ERROR, reason=response.detail, data=response)
6056

6157
reason = f"Successfully accessed the /ResourceTypes/{resource_type.id} endpoint."
62-
return CheckResult(
63-
context.conf, status=Status.SUCCESS, reason=reason, data=response
64-
)
58+
return CheckResult(status=Status.SUCCESS, reason=reason, data=response)
6559

6660

6761
@checker("discovery", "resource-types")
@@ -76,22 +70,19 @@ def check_access_invalid_resource_type(context: CheckContext) -> CheckResult:
7670

7771
if not isinstance(response, Error):
7872
return CheckResult(
79-
context.conf,
8073
status=Status.ERROR,
8174
reason=f"/resource_types/{probably_invalid_id} invalid URL did not return an Error object",
8275
data=response,
8376
)
8477

8578
if response.status != 404:
8679
return CheckResult(
87-
context.conf,
8880
status=Status.ERROR,
8981
reason=f"/resource_types/{probably_invalid_id} invalid URL did return an object, but the status code is {response.status}",
9082
data=response,
9183
)
9284

9385
return CheckResult(
94-
context.conf,
9586
status=Status.SUCCESS,
9687
reason=f"/resource_types/{probably_invalid_id} invalid URL correctly returned a 404 error",
9788
data=response,

scim2_tester/schemas.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ def check_query_all_schemas(context: CheckContext) -> CheckResult:
3939
)
4040
available = ", ".join([f"'{resource.name}'" for resource in response.resources])
4141
return CheckResult(
42-
context.conf,
4342
status=Status.SUCCESS,
4443
reason=f"Schemas available are: {available}",
4544
data=response.resources,
@@ -54,14 +53,10 @@ def check_query_schema_by_id(context: CheckContext, schema: Schema) -> CheckResu
5453
expected_status_codes=context.conf.expected_status_codes or [200],
5554
)
5655
if isinstance(response, Error):
57-
return CheckResult(
58-
context.conf, status=Status.ERROR, reason=response.detail, data=response
59-
)
56+
return CheckResult(status=Status.ERROR, reason=response.detail, data=response)
6057

6158
reason = f"Successfully accessed the /Schemas/{schema.id} endpoint."
62-
return CheckResult(
63-
context.conf, status=Status.SUCCESS, reason=reason, data=response
64-
)
59+
return CheckResult(status=Status.SUCCESS, reason=reason, data=response)
6560

6661

6762
@checker("discovery", "schemas")
@@ -76,22 +71,19 @@ def check_access_invalid_schema(context: CheckContext) -> CheckResult:
7671

7772
if not isinstance(response, Error):
7873
return CheckResult(
79-
context.conf,
8074
status=Status.ERROR,
8175
reason=f"/Schemas/{probably_invalid_id} invalid URL did not return an Error object",
8276
data=response,
8377
)
8478

8579
if response.status != 404:
8680
return CheckResult(
87-
context.conf,
8881
status=Status.ERROR,
8982
reason=f"/Schemas/{probably_invalid_id} invalid URL did return an Error object, but the status code is {response.status}",
9083
data=response,
9184
)
9285

9386
return CheckResult(
94-
context.conf,
9587
status=Status.SUCCESS,
9688
reason=f"/Schemas/{probably_invalid_id} invalid URL correctly returned a 404 error",
9789
data=response,

0 commit comments

Comments
 (0)