mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
100% Coverage
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
"""Coverage for roboco.services.base — SingletonHolder pattern."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from roboco.services.base import (
|
||||
BaseService,
|
||||
ConflictError,
|
||||
NotFoundError,
|
||||
ServiceError,
|
||||
ServiceUnavailableError,
|
||||
SingletonHolder,
|
||||
SingletonService,
|
||||
UnauthorizedError,
|
||||
ValidationError,
|
||||
)
|
||||
|
||||
|
||||
class _DummyService:
|
||||
def __init__(self, value: str = "default") -> None:
|
||||
self.value = value
|
||||
|
||||
|
||||
class _Holder(SingletonHolder[_DummyService]):
|
||||
def create_instance(self) -> _DummyService:
|
||||
return _DummyService("first")
|
||||
|
||||
|
||||
def test_singleton_holder_get_creates_instance() -> None:
|
||||
holder = _Holder()
|
||||
assert holder.is_initialized is False
|
||||
inst = holder.get()
|
||||
assert inst.value == "first"
|
||||
assert holder.is_initialized is True
|
||||
|
||||
|
||||
def test_singleton_holder_get_returns_same_instance() -> None:
|
||||
holder = _Holder()
|
||||
a = holder.get()
|
||||
b = holder.get()
|
||||
assert a is b
|
||||
|
||||
|
||||
def test_singleton_holder_set_overrides_instance() -> None:
|
||||
holder = _Holder()
|
||||
custom = _DummyService("custom")
|
||||
holder.set(custom)
|
||||
assert holder.get() is custom
|
||||
|
||||
|
||||
def test_singleton_holder_clear_resets_instance() -> None:
|
||||
holder = _Holder()
|
||||
holder.get()
|
||||
holder.clear()
|
||||
assert holder.is_initialized is False
|
||||
# Subsequent get() rebuilds.
|
||||
new = holder.get()
|
||||
assert new.value == "first"
|
||||
|
||||
|
||||
def test_singleton_holder_create_instance_default_raises() -> None:
|
||||
"""Base SingletonHolder must require subclass override."""
|
||||
holder: SingletonHolder[_DummyService] = SingletonHolder()
|
||||
with pytest.raises(NotImplementedError):
|
||||
holder.get()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Error hierarchy — round-trip the constructors so the dataclass-style fields
|
||||
# get exercised end-to-end.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_service_error_basic() -> None:
|
||||
err = ServiceError("oops", details={"k": "v"})
|
||||
assert err.message == "oops"
|
||||
assert err.details == {"k": "v"}
|
||||
|
||||
|
||||
def test_not_found_error_with_id() -> None:
|
||||
err = NotFoundError("Task", resource_id="abc")
|
||||
assert "abc" in err.message
|
||||
assert err.resource_id == "abc"
|
||||
|
||||
|
||||
def test_not_found_error_without_id() -> None:
|
||||
err = NotFoundError("Task")
|
||||
assert err.message == "Task not found"
|
||||
|
||||
|
||||
def test_validation_error_with_field() -> None:
|
||||
err = ValidationError("bad", field="title")
|
||||
assert err.field == "title"
|
||||
|
||||
|
||||
def test_conflict_error_records_resource() -> None:
|
||||
err = ConflictError("dup", resource_type="Project")
|
||||
assert err.resource_type == "Project"
|
||||
|
||||
|
||||
def test_unauthorized_error_with_reason() -> None:
|
||||
err = UnauthorizedError("delete", reason="role")
|
||||
assert "delete" in err.message
|
||||
assert "role" in err.message
|
||||
|
||||
|
||||
def test_service_unavailable_error_with_reason() -> None:
|
||||
err = ServiceUnavailableError("postgres", reason="down")
|
||||
assert "postgres" in err.message
|
||||
assert "down" in err.message
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# BaseService + SingletonService instantiation — bind logger, etc.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_base_service_binds_session_and_logger() -> None:
|
||||
class _Svc(BaseService):
|
||||
service_name = "x"
|
||||
|
||||
fake_session = object()
|
||||
svc = _Svc(fake_session) # type: ignore[arg-type]
|
||||
assert svc.session is fake_session
|
||||
assert svc.log is not None
|
||||
|
||||
|
||||
def test_singleton_service_binds_logger() -> None:
|
||||
class _Svc(SingletonService):
|
||||
service_name = "y"
|
||||
|
||||
svc = _Svc()
|
||||
assert svc.log is not None
|
||||
Reference in New Issue
Block a user