chore: rename project coven → hack-house ⛧
Rebrand the Rust client crate (coven/ → hh/, package+binary "hack-house"), README, CLI strings, and branch (coven → hack-house). Gitea repo renamed cmd-chat → hack-house to match. Crypto/server logic unchanged; selftest + golden-vector test still green, binary is now `hack-house`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
from dataclasses import dataclass
|
||||
from itertools import count
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import Request, json, text
|
||||
from sanic.exceptions import SanicException
|
||||
from sanic.views import HTTPMethodView
|
||||
|
||||
from sanic_ext import Extend
|
||||
|
||||
|
||||
@dataclass
|
||||
class Name:
|
||||
name: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class PersonID:
|
||||
person_id: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class NamedPerson:
|
||||
name: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Person:
|
||||
person_id: PersonID
|
||||
name: str
|
||||
age: int
|
||||
|
||||
@classmethod
|
||||
async def create(cls, person_id: int) -> Person:
|
||||
return cls(person_id=PersonID(person_id), name="noname", age=111)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AsyncName:
|
||||
name: str
|
||||
|
||||
def __await__(self):
|
||||
return self
|
||||
|
||||
|
||||
counter = count()
|
||||
|
||||
|
||||
class A:
|
||||
@classmethod
|
||||
def create(cls, request: Request):
|
||||
next(counter)
|
||||
return cls()
|
||||
|
||||
|
||||
class B:
|
||||
def __init__(self, a: A):
|
||||
self.a = a
|
||||
|
||||
@classmethod
|
||||
def create(cls, request: Request, a: A):
|
||||
next(counter)
|
||||
return cls(a)
|
||||
|
||||
|
||||
class C:
|
||||
def __init__(self, b: B):
|
||||
self.b = b
|
||||
|
||||
@classmethod
|
||||
def create(cls, request: Request, b: B):
|
||||
next(counter)
|
||||
return cls(b)
|
||||
|
||||
|
||||
class D:
|
||||
def __init__(self, a: A):
|
||||
self.a = a
|
||||
|
||||
@classmethod
|
||||
def create(cls, request: Request, a: A):
|
||||
next(counter)
|
||||
return cls(a)
|
||||
|
||||
|
||||
class E:
|
||||
def __init__(self, c: C, d: D):
|
||||
self.c = c
|
||||
self.d = d
|
||||
|
||||
@classmethod
|
||||
def create(cls, request: Request, c: C, d: D):
|
||||
next(counter)
|
||||
return cls(c, d)
|
||||
|
||||
|
||||
class Alpha: ...
|
||||
|
||||
|
||||
class Beta:
|
||||
def __init__(self, alpha: Alpha) -> None:
|
||||
self.alpha = alpha
|
||||
|
||||
|
||||
class Gamma:
|
||||
def __init__(self, beta: Beta, request: Optional[Request] = None) -> None:
|
||||
self.beta = beta
|
||||
self.request = request
|
||||
|
||||
|
||||
class Delta:
|
||||
def __init__(self, gamma: Gamma, request: Request) -> None:
|
||||
self.gamma = gamma
|
||||
self.request = request
|
||||
|
||||
|
||||
@dataclass
|
||||
class Foo:
|
||||
ident: UUID
|
||||
|
||||
|
||||
def make_gamma_with_request(beta: Beta, request: Request):
|
||||
return Gamma(beta, request)
|
||||
|
||||
|
||||
def make_gamma_without_request(beta: Beta):
|
||||
return Gamma(beta)
|
||||
|
||||
|
||||
def test_injection_not_allowed_when_ext_disabled(bare_app):
|
||||
ext = Extend(bare_app, built_in_extensions=False)
|
||||
|
||||
with pytest.raises(
|
||||
SanicException, match="Injection extension not enabled"
|
||||
):
|
||||
ext.add_dependency(1, 2)
|
||||
|
||||
|
||||
def test_injection_of_matched_object(app):
|
||||
@app.get("/person/<name:str>")
|
||||
def handler(request, name: Name):
|
||||
request.ctx.name = name
|
||||
return text(name.name)
|
||||
|
||||
app.ext.add_dependency(Name)
|
||||
|
||||
request, response = app.test_client.get("/person/george")
|
||||
|
||||
assert response.body == b"george"
|
||||
assert isinstance(request.ctx.name, Name)
|
||||
assert request.ctx.name.name == "george"
|
||||
|
||||
|
||||
def test_injection_of_matched_object_as_deprecated_injection(app):
|
||||
@app.get("/person/<name:str>")
|
||||
def handler(request, name: Name):
|
||||
request.ctx.name = name
|
||||
return text(name.name)
|
||||
|
||||
message = (
|
||||
"The 'ext.injection' method has been deprecated and will be removed "
|
||||
"in v22.6. Please use 'ext.add_dependency' instead."
|
||||
)
|
||||
with pytest.warns(DeprecationWarning, match=message):
|
||||
app.ext.injection(Name)
|
||||
|
||||
request, response = app.test_client.get("/person/george")
|
||||
|
||||
assert response.body == b"george"
|
||||
assert isinstance(request.ctx.name, Name)
|
||||
assert request.ctx.name.name == "george"
|
||||
|
||||
|
||||
def test_injection_of_simple_object(app):
|
||||
@app.get("/person/<name>")
|
||||
def handler(request, name: str, person: NamedPerson):
|
||||
request.ctx.person = person
|
||||
return text(person.name)
|
||||
|
||||
app.ext.add_dependency(NamedPerson)
|
||||
|
||||
request, response = app.test_client.get("/person/george")
|
||||
|
||||
assert response.body == b"george"
|
||||
assert isinstance(request.ctx.person, NamedPerson)
|
||||
assert request.ctx.person.name == "george"
|
||||
|
||||
|
||||
def test_injection_of_object_with_constructor(app):
|
||||
@app.get("/person/<person_id:int>")
|
||||
async def person_details(request, person_id: PersonID, person: Person):
|
||||
request.ctx.person_id = person_id
|
||||
request.ctx.person = person
|
||||
return text(
|
||||
f"{person.person_id.person_id}\n{person.name}\n{person.age}"
|
||||
)
|
||||
|
||||
app.ext.add_dependency(Person, Person.create)
|
||||
app.ext.add_dependency(PersonID)
|
||||
|
||||
request, response = app.test_client.get("/person/999")
|
||||
|
||||
assert response.body == b"999\nnoname\n111"
|
||||
assert isinstance(request.ctx.person_id, PersonID)
|
||||
assert isinstance(request.ctx.person, Person)
|
||||
assert request.ctx.person.person_id == request.ctx.person_id
|
||||
assert request.ctx.person.person_id.person_id == 999
|
||||
assert request.ctx.person.name == "noname"
|
||||
assert request.ctx.person.age == 111
|
||||
|
||||
|
||||
def test_injection_on_cbv(app):
|
||||
class View(HTTPMethodView, attach=app, uri="/person/<name:str>"):
|
||||
async def get(self, request, name: Name):
|
||||
request.ctx.name = name
|
||||
return text(name.name)
|
||||
|
||||
@staticmethod
|
||||
async def post(request, name: Name):
|
||||
request.ctx.name = name
|
||||
return text(name.name)
|
||||
|
||||
app.ext.add_dependency(Name)
|
||||
|
||||
for client in (app.test_client.get, app.test_client.post):
|
||||
request, response = client("/person/george")
|
||||
|
||||
assert response.body == b"george"
|
||||
assert isinstance(request.ctx.name, Name)
|
||||
assert request.ctx.name.name == "george"
|
||||
|
||||
|
||||
def test_nested_dependencies(app):
|
||||
app.ext.add_dependency(A, A.create)
|
||||
app.ext.add_dependency(B, B.create)
|
||||
app.ext.add_dependency(C, C.create)
|
||||
app.ext.add_dependency(D, D.create)
|
||||
app.ext.add_dependency(E, E.create)
|
||||
|
||||
@app.get("/")
|
||||
async def nested(request: Request, c: C, e: E):
|
||||
return json(
|
||||
[
|
||||
isinstance(c, C),
|
||||
isinstance(c.b, B),
|
||||
isinstance(c.b.a, A),
|
||||
isinstance(e, E),
|
||||
isinstance(e.c, C),
|
||||
isinstance(e.d, D),
|
||||
isinstance(e.d.a, A),
|
||||
]
|
||||
)
|
||||
|
||||
_, response = app.test_client.get("/")
|
||||
|
||||
assert all(response.json)
|
||||
# TODO:
|
||||
# - After implementing https://github.com/sanic-org/sanic-ext/issues/77
|
||||
# this should be == 5
|
||||
assert next(counter) == 9
|
||||
|
||||
|
||||
def test_injection_on_websocket(app):
|
||||
ev = asyncio.Event()
|
||||
|
||||
app.ext.dependency(A())
|
||||
|
||||
@app.websocket("/foo")
|
||||
async def handler(request, ws, foo: A):
|
||||
assert isinstance(foo, A)
|
||||
if isinstance(foo, A):
|
||||
ev.set()
|
||||
|
||||
request, response = app.test_client.websocket("/foo")
|
||||
assert ev.is_set()
|
||||
|
||||
|
||||
def test_injection_of_awaitable_variable_in_do_cast(app):
|
||||
"""Test for do_cast() iscoroutine() check"""
|
||||
|
||||
@app.get("/person/<name:str>")
|
||||
def handler(request, name: AsyncName):
|
||||
request.ctx.name = name
|
||||
return text(name.name)
|
||||
|
||||
app.ext.add_dependency(AsyncName)
|
||||
|
||||
request, response = app.test_client.get("/person/george")
|
||||
|
||||
assert response.body == b"george"
|
||||
assert isinstance(request.ctx.name, AsyncName)
|
||||
assert request.ctx.name.name == "george"
|
||||
|
||||
|
||||
def test_injection_of_awaitable_variable_in_call(app):
|
||||
"""Test for __call__() iscoroutine() check"""
|
||||
|
||||
@app.get("/person/<name:str>")
|
||||
def handler(request, name: AsyncName):
|
||||
request.ctx.name = name
|
||||
return text(name.name)
|
||||
|
||||
def test():
|
||||
return AsyncName("george")
|
||||
|
||||
app.ext.dependency(test())
|
||||
|
||||
request, response = app.test_client.get("/person/george")
|
||||
|
||||
assert response.body == b"george"
|
||||
assert isinstance(request.ctx.name, AsyncName)
|
||||
assert request.ctx.name.name == "george"
|
||||
|
||||
|
||||
def test_injection_class_constructors(app):
|
||||
app.ext.add_dependency(Alpha)
|
||||
app.ext.add_dependency(Beta)
|
||||
|
||||
@app.get("/")
|
||||
def handler(request: Request, beta: Beta):
|
||||
return json({"is_beta": isinstance(beta, Beta)})
|
||||
|
||||
_, response = app.test_client.get("/")
|
||||
assert response.json == {"is_beta": True}
|
||||
|
||||
|
||||
def test_injection_class_constructors_with_optional_request(app):
|
||||
app.ext.add_dependency(Alpha)
|
||||
app.ext.add_dependency(Beta)
|
||||
app.ext.add_dependency(Gamma)
|
||||
|
||||
@app.get("/")
|
||||
def handler(request: Request, gamma: Gamma):
|
||||
return json(
|
||||
{
|
||||
"is_gamma": isinstance(gamma, Gamma),
|
||||
"has_request": bool(gamma.request),
|
||||
}
|
||||
)
|
||||
|
||||
_, response = app.test_client.get("/")
|
||||
assert response.json == {"is_gamma": True, "has_request": True}
|
||||
|
||||
|
||||
def test_injection_class_constructors_with_request(app):
|
||||
app.ext.add_dependency(Alpha)
|
||||
app.ext.add_dependency(Beta)
|
||||
app.ext.add_dependency(Gamma)
|
||||
app.ext.add_dependency(Delta)
|
||||
|
||||
@app.get("/")
|
||||
def handler(request: Request, delta: Delta):
|
||||
return json(
|
||||
{
|
||||
"is_delta": isinstance(delta, Delta),
|
||||
"has_request": bool(delta.request),
|
||||
}
|
||||
)
|
||||
|
||||
_, response = app.test_client.get("/")
|
||||
assert response.json == {"is_delta": True, "has_request": True}
|
||||
|
||||
|
||||
def test_injection_class_constructors_with_func_and_request(app):
|
||||
app.ext.add_dependency(Alpha)
|
||||
app.ext.add_dependency(Beta)
|
||||
app.ext.add_dependency(Gamma, make_gamma_with_request)
|
||||
|
||||
@app.get("/")
|
||||
def handler(request: Request, gamma: Gamma):
|
||||
return json(
|
||||
{
|
||||
"is_gamma": isinstance(gamma, Gamma),
|
||||
"has_request": bool(gamma.request),
|
||||
}
|
||||
)
|
||||
|
||||
_, response = app.test_client.get("/")
|
||||
assert response.json == {"is_gamma": True, "has_request": True}
|
||||
|
||||
|
||||
def test_injection_class_constructors_with_func_and_no_request(app):
|
||||
app.ext.add_dependency(Alpha)
|
||||
app.ext.add_dependency(Beta)
|
||||
app.ext.add_dependency(Gamma, make_gamma_without_request)
|
||||
|
||||
@app.get("/")
|
||||
def handler(request: Request, gamma: Gamma):
|
||||
return json(
|
||||
{
|
||||
"is_gamma": isinstance(gamma, Gamma),
|
||||
"has_request": bool(gamma.request),
|
||||
}
|
||||
)
|
||||
|
||||
_, response = app.test_client.get("/")
|
||||
assert response.json == {"is_gamma": True, "has_request": False}
|
||||
|
||||
|
||||
def test_injection_of_lambda_properties(app):
|
||||
@app.get("/foo")
|
||||
def handler(request, foo: Foo):
|
||||
return json(request.id == foo.ident and isinstance(foo.ident, UUID))
|
||||
|
||||
app.ext.add_dependency(Foo, lambda request: Foo(request.id), "request")
|
||||
|
||||
_, response = app.test_client.get("/foo")
|
||||
|
||||
assert response.body == b"true"
|
||||
@@ -0,0 +1,69 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import Sanic, json
|
||||
|
||||
|
||||
@dataclass
|
||||
class Foo:
|
||||
bar: int
|
||||
|
||||
|
||||
def test_constant_in_registry(app: Sanic):
|
||||
assert len(app.ext._constant_registry._registry) == 0
|
||||
app.ext.add_constant("bar", 999)
|
||||
assert len(app.ext._constant_registry._registry) == 1
|
||||
assert "bar" in app.ext._constant_registry
|
||||
|
||||
|
||||
def test_constant_is_injected(app: Sanic):
|
||||
app.ext.add_constant("bar", 999)
|
||||
|
||||
@app.get("/")
|
||||
async def handler(_, bar: int):
|
||||
return json({"bar": bar})
|
||||
|
||||
_, response = app.test_client.get("/")
|
||||
|
||||
assert response.json["bar"] == 999
|
||||
assert app.config.BAR == 999
|
||||
|
||||
|
||||
def test_constant_is_injected_into_constructor(app: Sanic):
|
||||
app.ext.add_constant("bar", 999)
|
||||
app.ext.add_dependency(Foo)
|
||||
|
||||
@app.get("/")
|
||||
async def handler(_, foo: Foo):
|
||||
return json({"foo": foo.bar})
|
||||
|
||||
_, response = app.test_client.get("/")
|
||||
|
||||
assert response.json["foo"] == 999
|
||||
assert app.config.BAR == 999
|
||||
|
||||
|
||||
def test_load_config(app: Sanic):
|
||||
app.ext.load_constants()
|
||||
assert len(app.ext._constant_registry._registry) == 0
|
||||
app.ext.load_constants({**app.config}, overwrite=True)
|
||||
assert len(app.ext._constant_registry._registry) == len(
|
||||
[k for k in app.config.keys() if k.isupper()]
|
||||
)
|
||||
assert "request_buffer_size" in app.ext._constant_registry
|
||||
|
||||
|
||||
def test_load_custom(app: Sanic):
|
||||
app.ext.load_constants({"foo": "bar"})
|
||||
assert len(app.ext._constant_registry._registry) == 1
|
||||
assert app.ext._constant_registry.get("foo") == "bar"
|
||||
|
||||
|
||||
def test_load_overwrite(app: Sanic):
|
||||
app.ext.load_constants({"foo": "bar"})
|
||||
with pytest.raises(
|
||||
ValueError, match="A value for FOO has already been assigned"
|
||||
):
|
||||
app.ext.load_constants({"foo": "bar"})
|
||||
app.ext.load_constants({"foo": "bar"}, True)
|
||||
@@ -0,0 +1,45 @@
|
||||
from sanic import Request, text
|
||||
|
||||
|
||||
class Foo:
|
||||
def bar(self):
|
||||
return "foobar"
|
||||
|
||||
|
||||
def test_dependency_added(app):
|
||||
foo = Foo()
|
||||
foobar = Foo()
|
||||
|
||||
app.ext.dependency(foo)
|
||||
app.ext.dependency(foobar, name="something")
|
||||
|
||||
assert app.ctx._dependencies.foo is foo
|
||||
assert app.ctx._dependencies.something is foobar
|
||||
|
||||
|
||||
def test_dependency_injection(app):
|
||||
foo = Foo()
|
||||
|
||||
app.ext.dependency(foo)
|
||||
|
||||
@app.get("/getfoo")
|
||||
async def getfoo(request: Request, foo: Foo):
|
||||
return text(foo.bar())
|
||||
|
||||
_, response = app.test_client.get("/getfoo")
|
||||
|
||||
assert response.text == "foobar"
|
||||
|
||||
|
||||
def test_dependency_injection_head(app):
|
||||
foo = Foo()
|
||||
|
||||
app.ext.dependency(foo)
|
||||
|
||||
@app.get("/getfoo")
|
||||
async def getfoo(request: Request, foo: Foo):
|
||||
return text(foo.bar())
|
||||
|
||||
_, response = app.test_client.head("/getfoo")
|
||||
|
||||
assert int(response.headers.get("content-length", 0)) == 6
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic.exceptions import SanicException
|
||||
from sanic.signals import Event
|
||||
|
||||
from sanic_ext.config import Config
|
||||
from sanic_ext.extensions.injection.injector import add_injection
|
||||
|
||||
|
||||
def test_default_config_signal():
|
||||
config = Config()
|
||||
assert config.INJECTION_SIGNAL == Event.HTTP_ROUTING_AFTER
|
||||
|
||||
|
||||
def test_not_allowed_signals_error():
|
||||
with pytest.raises(SanicException):
|
||||
Config(injection_signal=Event.HTTP_LIFECYCLE_REQUEST)
|
||||
|
||||
|
||||
def test_http_routing_after_succeeds():
|
||||
Config(injection_signal=Event.HTTP_ROUTING_AFTER)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not hasattr(Event, "HTTP_HANDLER_BEFORE"),
|
||||
reason="Sanic version does not support HTTP_HANDLER_BEFORE",
|
||||
)
|
||||
def test_http_handler_before_succeeds():
|
||||
Config(injection_signal=Event.HTTP_HANDLER_BEFORE)
|
||||
|
||||
|
||||
def test_add_injection_uses_signal_config():
|
||||
app = Mock()
|
||||
|
||||
app.signal = Mock(return_value=Mock())
|
||||
app.ext.config.INJECTION_SIGNAL = "random_string"
|
||||
app.ext.config.INJECTION_PRIORITY = 99999
|
||||
add_injection(app, Mock(), Mock())
|
||||
|
||||
app.signal.assert_called_once_with("random_string", priority=99999)
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock, Mock, call
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import Request
|
||||
from sanic.exceptions import ServerError
|
||||
|
||||
from sanic_ext.exceptions import InitError
|
||||
from sanic_ext.extensions.injection.constructor import Constructor, gather_args
|
||||
from sanic_ext.extensions.injection.registry import (
|
||||
ConstantRegistry,
|
||||
InjectionRegistry,
|
||||
)
|
||||
|
||||
|
||||
class Foo:
|
||||
def __init__(self, num: int):
|
||||
self.num = num
|
||||
self.mock = AsyncMock()
|
||||
|
||||
@classmethod
|
||||
async def create(cls, request: Request, bar: int):
|
||||
instance = cls(bar)
|
||||
await instance.mock(request, bar)
|
||||
return instance
|
||||
|
||||
|
||||
class Bar:
|
||||
def __init__(self, foo: Foo):
|
||||
self.foo = foo
|
||||
|
||||
@classmethod
|
||||
async def create(cls, request: Request, foo: Foo):
|
||||
return cls(foo)
|
||||
|
||||
|
||||
class Chicken:
|
||||
@classmethod
|
||||
def create(cls, egg: Egg):
|
||||
return cls()
|
||||
|
||||
|
||||
class Egg:
|
||||
@classmethod
|
||||
def create(cls, chicken: Chicken):
|
||||
return cls()
|
||||
|
||||
|
||||
class InheritedRequest(Request): ...
|
||||
|
||||
|
||||
class Baz:
|
||||
@classmethod
|
||||
async def create(cls, request: InheritedRequest):
|
||||
return cls()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_gather_args():
|
||||
func = AsyncMock()
|
||||
func.return_value = 999
|
||||
injections = {"some_foo": (Foo, func)}
|
||||
request = object()
|
||||
kwargs = {"zero": 0, "one": True, "two": "2", "three": None}
|
||||
|
||||
args = await gather_args(injections, request, **kwargs)
|
||||
assert args == {"some_foo": 999}
|
||||
assert func.call_args == call(request, **kwargs)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_circular_refs():
|
||||
injections = InjectionRegistry()
|
||||
injections.register(Chicken, Chicken.create)
|
||||
injections.register(Egg, Egg.create)
|
||||
|
||||
assert isinstance(injections[Chicken], Constructor)
|
||||
assert isinstance(injections[Egg], Constructor)
|
||||
|
||||
message = (
|
||||
"Circular dependency injection detected on 'create'. Check "
|
||||
"dependencies of 'create' which may contain circular dependency "
|
||||
f"chain with {Chicken}."
|
||||
)
|
||||
with pytest.raises(InitError, match=message):
|
||||
injections.finalize(Mock(), ConstantRegistry({}), [])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("raises,allowed", ((False, (int,)), (True, [])))
|
||||
async def test_finalize_allowed_types(raises, allowed):
|
||||
injections = InjectionRegistry()
|
||||
injections.register(Foo, Foo.create)
|
||||
|
||||
if raises:
|
||||
with pytest.raises(
|
||||
InitError, match=".Could not find the following dependencies."
|
||||
):
|
||||
injections.finalize(Mock(), ConstantRegistry({}), allowed)
|
||||
else:
|
||||
injections.finalize(Mock(), ConstantRegistry({}), allowed)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_constructor():
|
||||
injections = InjectionRegistry()
|
||||
injections.register(Foo, Foo.create)
|
||||
injections.finalize(Mock(), ConstantRegistry({}), {int})
|
||||
request = object()
|
||||
constructor = injections[Foo]
|
||||
|
||||
foo = await constructor(request, bar=999)
|
||||
|
||||
assert isinstance(foo, Foo)
|
||||
assert foo.num == 999
|
||||
assert constructor.pass_kwargs
|
||||
foo.mock.assert_awaited_once_with(request, 999)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_constructor_nested():
|
||||
injections = InjectionRegistry()
|
||||
injections.register(Foo, Foo.create)
|
||||
injections.register(Bar, Bar.create)
|
||||
injections.finalize(Mock(), ConstantRegistry({}), {int})
|
||||
request = object()
|
||||
constructor_bar = injections[Bar]
|
||||
|
||||
bar = await constructor_bar(request, bar=999)
|
||||
|
||||
assert isinstance(bar, Bar)
|
||||
assert isinstance(bar.foo, Foo)
|
||||
assert bar.foo.num == 999
|
||||
assert not constructor_bar.pass_kwargs
|
||||
bar.foo.mock.assert_awaited_once_with(request, 999)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_constructor_failure_kwargs():
|
||||
injections = InjectionRegistry()
|
||||
injections.register(Foo, Foo.create)
|
||||
injections.finalize(Mock(), ConstantRegistry({}), {int})
|
||||
request = object()
|
||||
constructor = injections[Foo]
|
||||
|
||||
message = (
|
||||
"Failure to inject dependencies. Make sure that all dependencies "
|
||||
"for 'create' have been registered."
|
||||
)
|
||||
with pytest.raises(ServerError, match=message):
|
||||
await constructor(request)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_constructor_failure_nested():
|
||||
injections = InjectionRegistry()
|
||||
injections.register(Foo, Foo.create)
|
||||
injections.register(Bar, Bar.create)
|
||||
injections.finalize(Mock(), ConstantRegistry({}), {int})
|
||||
request = object()
|
||||
constructor: Bar = injections[Bar]
|
||||
|
||||
message = (
|
||||
"Failure to inject dependencies. Make sure that all dependencies "
|
||||
"for 'create' have been registered."
|
||||
)
|
||||
with pytest.raises(ServerError, match=message):
|
||||
await constructor(request)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_constructor_with_inherited_request():
|
||||
injections = InjectionRegistry()
|
||||
injections.register(Baz, Baz.create)
|
||||
injections.finalize(Mock(), ConstantRegistry({}), [])
|
||||
|
||||
request = object()
|
||||
constructor_baz = injections[Baz]
|
||||
|
||||
baz = await constructor_baz(request)
|
||||
|
||||
assert isinstance(baz, Baz)
|
||||
assert not constructor_baz.pass_kwargs
|
||||
Reference in New Issue
Block a user