Skip to content

Commit b273203

Browse files
committed
Add backend names listing service
1 parent 353d003 commit b273203

3 files changed

Lines changed: 124 additions & 0 deletions

File tree

src/schnee/services/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
"""Application services."""
22

3+
from .backend import (
4+
ListBackendNamesBackendError,
5+
ListBackendNamesReaderError,
6+
ListBackendNamesService,
7+
ListBackendNamesServiceError,
8+
)
39
from .base import ServiceError

src/schnee/services/backend.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Services for backend selection."""
2+
3+
from __future__ import annotations
4+
5+
from typing import ClassVar
6+
7+
from schnee.adapters.backend.core import Backend
8+
from schnee.adapters.backend.pcsc import PcscReaderProvider
9+
from schnee.services.base import Service, ServiceError
10+
11+
12+
class ListBackendNamesServiceError(ServiceError):
13+
"""Base exception for list backend names service errors."""
14+
15+
msg: ClassVar[str] = "List backend names service error"
16+
17+
18+
class ListBackendNamesBackendError(ListBackendNamesServiceError):
19+
"""Raised when backend name discovery fails."""
20+
21+
msg: ClassVar[str] = "List backend names backend operation failed"
22+
23+
24+
class ListBackendNamesReaderError(ListBackendNamesServiceError):
25+
"""Raised when PC/SC reader discovery fails."""
26+
27+
msg: ClassVar[str] = "List backend names reader discovery failed"
28+
29+
30+
class ListBackendNamesService(Service[list[str]]):
31+
"""List selectable backend names."""
32+
33+
class Request(Service.Request):
34+
"""Request for listing selectable backend names."""
35+
36+
def process(self) -> list[str]:
37+
"""Return selectable backend names."""
38+
try:
39+
return Backend.backend_names()
40+
except PcscReaderProvider.PcscReaderProviderError as exc:
41+
raise ListBackendNamesReaderError from exc
42+
except Backend.BackendError as exc:
43+
raise ListBackendNamesBackendError from exc
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""Tests for backend selection services."""
2+
3+
import pytest
4+
5+
from schnee.adapters.backend.core import Backend
6+
from schnee.adapters.backend.pcsc import PcscReaderProvider
7+
from schnee.services.backend import (
8+
ListBackendNamesBackendError,
9+
ListBackendNamesReaderError,
10+
ListBackendNamesService,
11+
)
12+
13+
14+
def test_list_backend_names_service_returns_selectable_backend_names(
15+
monkeypatch: pytest.MonkeyPatch,
16+
) -> None:
17+
"""Selectable backend names are available through the service layer."""
18+
monkeypatch.setattr(
19+
"schnee.services.backend.Backend.backend_names",
20+
lambda: ["pcsc", "pcsc:Reader A"],
21+
)
22+
23+
names = ListBackendNamesService.call(ListBackendNamesService.Request())
24+
25+
assert names == ["pcsc", "pcsc:Reader A"]
26+
27+
28+
def test_list_backend_names_service_translates_reader_errors(
29+
monkeypatch: pytest.MonkeyPatch,
30+
) -> None:
31+
"""Reader discovery failures are exposed through service-level errors."""
32+
33+
def backend_names() -> list[str]:
34+
raise PcscReaderProvider.PcscReaderProviderError
35+
36+
monkeypatch.setattr(
37+
"schnee.services.backend.Backend.backend_names",
38+
backend_names,
39+
)
40+
41+
with pytest.raises(
42+
ListBackendNamesReaderError,
43+
match=ListBackendNamesReaderError.msg,
44+
) as exc_info:
45+
ListBackendNamesService.call(ListBackendNamesService.Request())
46+
47+
assert isinstance(
48+
exc_info.value.__cause__,
49+
PcscReaderProvider.PcscReaderProviderError,
50+
), "service error should preserve the reader failure as its cause"
51+
52+
53+
def test_list_backend_names_service_translates_backend_errors(
54+
monkeypatch: pytest.MonkeyPatch,
55+
) -> None:
56+
"""Backend selector failures are exposed through service-level errors."""
57+
58+
def backend_names() -> list[str]:
59+
raise Backend.BackendError
60+
61+
monkeypatch.setattr(
62+
"schnee.services.backend.Backend.backend_names",
63+
backend_names,
64+
)
65+
66+
with pytest.raises(
67+
ListBackendNamesBackendError,
68+
match=ListBackendNamesBackendError.msg,
69+
) as exc_info:
70+
ListBackendNamesService.call(ListBackendNamesService.Request())
71+
72+
assert isinstance(
73+
exc_info.value.__cause__,
74+
Backend.BackendError,
75+
), "service error should preserve the backend failure as its cause"

0 commit comments

Comments
 (0)