Skip to content

Commit b8840d5

Browse files
committed
chore: improve test coverage
1 parent 1519a75 commit b8840d5

4 files changed

Lines changed: 143 additions & 19 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,15 @@ omit = [".tox/*"]
7272
branch = true
7373

7474
[tool.coverage.report]
75-
fail_under = 99
75+
fail_under = 100
7676
exclude_lines = [
7777
"@pytest.mark.skip",
7878
"pragma: no cover",
7979
"raise NotImplementedError",
8080
"except ImportError",
8181
"if TYPE_CHECKING:",
8282
"if app.debug",
83+
"...",
8384
]
8485

8586
[tool.ruff.lint]

scim2_tester/filling.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import base64
21
import random
32
import uuid
43
from enum import Enum
@@ -83,9 +82,6 @@ def generate_random_value(
8382
elif field_type is bool:
8483
value = random.choice([True, False])
8584

86-
elif field_type is bytes:
87-
value = base64.b64encode(str(uuid.uuid4()).encode("utf-8"))
88-
8985
elif get_origin(field_type) is Reference:
9086
ref_type = get_args(field_type)[0]
9187
if ref_type not in (ExternalReference, URIReference):

scim2_tester/utils.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -177,20 +177,6 @@ def create_and_register(self, model: type[Resource[Any]]) -> Resource[Any]:
177177
# This shouldn't happen with valid inputs, but handle for type safety
178178
raise ValueError(f"Failed to create resource: {created}")
179179

180-
def register(self, resource: Resource[Any]) -> None:
181-
"""Manually register a resource for cleanup.
182-
183-
:param resource: The Resource instance to register for cleanup
184-
"""
185-
self.resources.append(resource)
186-
187-
def register_dependencies(self, dependencies: list[Resource[Any]]) -> None:
188-
"""Register multiple dependencies for cleanup.
189-
190-
:param dependencies: List of Resource instances to register for cleanup
191-
"""
192-
self.resources.extend(dependencies)
193-
194180
def cleanup(self) -> None:
195181
"""Clean up all registered resources in reverse order."""
196182
for resource in reversed(self.resources):

tests/test_utils.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import sys
2+
from unittest.mock import Mock
3+
from unittest.mock import patch
24

35
import pytest
6+
from scim2_client import SCIMClientError
7+
from scim2_models import User
48

59
from scim2_tester.utils import CheckConfig
610
from scim2_tester.utils import CheckContext
711
from scim2_tester.utils import CheckResult
12+
from scim2_tester.utils import ResourceManager
813
from scim2_tester.utils import SCIMTesterError
914
from scim2_tester.utils import Status
15+
from scim2_tester.utils import _matches_hierarchical_tags
1016
from scim2_tester.utils import checker
17+
from scim2_tester.utils import get_registered_tags
1118

1219

1320
def test_checker_decorator_with_tags():
@@ -261,3 +268,137 @@ def test_func(context):
261268
context_exclude = CheckContext(client=None, conf=conf_exclude)
262269
result = test_func(context_exclude) # Call with decorator signature
263270
assert result[0].status.name == "SKIPPED"
271+
272+
273+
def test_get_registered_tags():
274+
"""Test get_registered_tags function returns a copy of registered tags."""
275+
276+
@checker("test_tag1", "test_tag2")
277+
def dummy_check(context):
278+
pass
279+
280+
tags = get_registered_tags()
281+
assert isinstance(tags, set)
282+
assert "test_tag1" in tags
283+
assert "test_tag2" in tags
284+
285+
286+
def test_hierarchical_tag_exact_match():
287+
"""Test exact tag matching without hierarchy."""
288+
func_tags = {"crud:read", "schemas"}
289+
filter_tags = {"crud:read"}
290+
291+
assert _matches_hierarchical_tags(func_tags, filter_tags) is True
292+
293+
294+
def test_hierarchical_tag_no_match():
295+
"""Test when tags don't match."""
296+
func_tags = {"crud:read", "schemas"}
297+
filter_tags = {"auth", "other"}
298+
299+
assert _matches_hierarchical_tags(func_tags, filter_tags) is False
300+
301+
302+
def test_scim_client_error_handling():
303+
"""Test SCIMClientError handling in checker decorator."""
304+
305+
@checker("test")
306+
def scim_error_check(context):
307+
raise SCIMClientError("SCIM error", source={"detail": "test data"})
308+
309+
conf = CheckConfig(raise_exceptions=False)
310+
context = CheckContext(client=None, conf=conf)
311+
312+
result = scim_error_check(context)
313+
assert result[0].status == Status.ERROR
314+
assert "SCIM error" in result[0].reason
315+
assert result[0].data == {"detail": "test data"}
316+
317+
318+
def test_resource_manager_create_and_cleanup():
319+
"""Test ResourceManager create_and_register and cleanup."""
320+
mock_client = Mock()
321+
mock_created_user = User(id="123", user_name="test_user")
322+
mock_client.create.return_value = mock_created_user
323+
mock_client.delete.return_value = None
324+
325+
conf = CheckConfig()
326+
context = CheckContext(client=mock_client, conf=conf)
327+
resource_manager = ResourceManager(context)
328+
329+
with patch("scim2_tester.filling.fill_with_random_values") as mock_fill:
330+
mock_fill.return_value = User(user_name="test_user")
331+
created = resource_manager.create_and_register(User)
332+
333+
assert created == mock_created_user
334+
assert len(resource_manager.resources) == 1
335+
assert resource_manager.resources[0] == mock_created_user
336+
337+
# Test cleanup
338+
resource_manager.cleanup()
339+
mock_client.delete.assert_called_once_with(User, "123")
340+
341+
342+
def test_resource_manager_cleanup_with_errors():
343+
"""Test ResourceManager cleanup ignores errors."""
344+
mock_client = Mock()
345+
mock_created_user = User(id="123", user_name="test_user")
346+
mock_client.delete.side_effect = Exception("Delete failed")
347+
348+
conf = CheckConfig()
349+
context = CheckContext(client=mock_client, conf=conf)
350+
resource_manager = ResourceManager(context)
351+
352+
resource_manager.resources.append(mock_created_user)
353+
354+
resource_manager.cleanup()
355+
mock_client.delete.assert_called_once_with(User, "123")
356+
357+
358+
def test_resource_manager_cleanup_no_id():
359+
"""Test ResourceManager cleanup skips resources without ID."""
360+
mock_client = Mock()
361+
362+
conf = CheckConfig()
363+
context = CheckContext(client=mock_client, conf=conf)
364+
resource_manager = ResourceManager(context)
365+
366+
resource_no_id = User(user_name="test_user")
367+
resource_manager.resources.append(resource_no_id)
368+
369+
resource_manager.cleanup()
370+
mock_client.delete.assert_not_called()
371+
372+
373+
def test_resource_manager_create_failure():
374+
"""Test ResourceManager create_and_register handles non-Resource response."""
375+
mock_client = Mock()
376+
mock_client.create.return_value = {"error": "Creation failed"}
377+
378+
conf = CheckConfig()
379+
context = CheckContext(client=mock_client, conf=conf)
380+
resource_manager = ResourceManager(context)
381+
382+
with patch("scim2_tester.filling.fill_with_random_values") as mock_fill:
383+
mock_fill.return_value = User(user_name="test_user")
384+
with pytest.raises(ValueError) as exc_info:
385+
resource_manager.create_and_register(User)
386+
387+
assert "Failed to create resource" in str(exc_info.value)
388+
389+
390+
def test_checker_skip_with_include_tags():
391+
"""Test checker skips execution when include_tags don't match."""
392+
393+
@checker("crud:write")
394+
def write_check(context):
395+
return [CheckResult(status=Status.SUCCESS)]
396+
397+
conf = CheckConfig(include_tags={"crud:read", "schemas"})
398+
context = CheckContext(client=None, conf=conf)
399+
400+
result = write_check(context)
401+
assert len(result) == 1
402+
assert result[0].status == Status.SKIPPED
403+
assert result[0].reason == "Skipped due to tag filtering"
404+
assert result[0].title == "write_check"

0 commit comments

Comments
 (0)