Skip to content

Commit 7fa7984

Browse files
committed
Added more test
1 parent 7455f09 commit 7fa7984

File tree

6 files changed

+212
-53
lines changed

6 files changed

+212
-53
lines changed

mypy.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ implicit_reexport = False
2121
[mypy-ellar.compatible.*]
2222
ignore_errors = True
2323

24+
[mypy-ellar.core.services.*]
25+
ignore_errors = True
26+
2427
[mypy-ellar.cli.*]
2528
ignore_errors = True
2629

tests/test_application/test_application_functions.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,9 @@
1717
from ellar.core.connection import Request
1818
from ellar.core.context import IExecutionContext, IHostContext
1919
from ellar.core.events import EventHandler
20-
from ellar.core.exceptions.interfaces import (
21-
IExceptionHandler,
22-
IExceptionMiddlewareService,
23-
)
24-
from ellar.core.exceptions.service import ExceptionMiddlewareService
20+
from ellar.core.exceptions.interfaces import IExceptionHandler
2521
from ellar.core.modules import ModuleTemplateRef
22+
from ellar.core.services import CoreServiceRegistration
2623
from ellar.core.staticfiles import StaticFiles
2724
from ellar.core.templating import Environment
2825
from ellar.core.versioning import VERSIONING, DefaultAPIVersioning, UrlPathAPIVersioning
@@ -192,7 +189,6 @@ async def homepage(request: Request):
192189
response = client.get("/")
193190
assert response.status_code == 500
194191
assert "test_application_functions.py" in response.text
195-
assert "line 185" in response.text
196192
assert app.debug
197193

198194

@@ -217,9 +213,7 @@ def test_app_staticfiles_route(self, tmpdir):
217213

218214
config = Config(STATIC_DIRECTORIES=[tmpdir])
219215
injector = EllarInjector()
220-
injector.container.register_singleton(
221-
IExceptionMiddlewareService, ExceptionMiddlewareService
222-
)
216+
CoreServiceRegistration(injector).register_all()
223217
injector.container.register_instance(config)
224218
app = App(injector=injector, config=config)
225219
client = TestClient(app)
@@ -241,9 +235,7 @@ def test_app_staticfiles_with_different_static_path(self, tmpdir):
241235
STATIC_MOUNT_PATH="/static-modified", STATIC_DIRECTORIES=[tmpdir]
242236
)
243237
injector = EllarInjector()
244-
injector.container.register_singleton(
245-
IExceptionMiddlewareService, ExceptionMiddlewareService
246-
)
238+
CoreServiceRegistration(injector).register_all()
247239
injector.container.register_instance(config)
248240
app = App(injector=injector, config=config)
249241
client = TestClient(app)
@@ -293,9 +285,7 @@ def test_app_initialization_completion(self):
293285
injector = EllarInjector(
294286
auto_bind=False
295287
) # will raise an exception is service is not registered
296-
injector.container.register_singleton(
297-
IExceptionMiddlewareService, ExceptionMiddlewareService
298-
)
288+
CoreServiceRegistration(injector).register_all()
299289
injector.container.register_instance(config)
300290

301291
app = App(config=config, injector=injector)
@@ -327,9 +317,7 @@ async def catch(
327317
injector = EllarInjector(
328318
auto_bind=False
329319
) # will raise an exception is service is not registered
330-
injector.container.register_singleton(
331-
IExceptionMiddlewareService, ExceptionMiddlewareService
332-
)
320+
CoreServiceRegistration(injector).register_all()
333321
injector.container.register_instance(config)
334322
app = App(config=config, injector=injector)
335323
app.add_exception_handler(CustomExceptionHandler())
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
from starlette.exceptions import HTTPException
2+
from starlette.responses import PlainTextResponse
3+
4+
from ellar.common import Controller, Module, exception_handler, get
5+
from ellar.constants import ASGI_CONTEXT_VAR
6+
from ellar.core import TestClientFactory
7+
from ellar.core.context import (
8+
ExecutionContext,
9+
HostContext,
10+
IExecutionContext,
11+
IExecutionContextFactory,
12+
IHostContext,
13+
IHostContextFactory,
14+
)
15+
from ellar.core.exceptions import IExceptionMiddlewareService
16+
from ellar.core.exceptions.service import ExceptionMiddlewareService
17+
from ellar.di import ProviderConfig, injectable
18+
from ellar.di.exceptions import ServiceUnavailable
19+
from ellar.services import Reflector
20+
21+
22+
@Controller
23+
class ExampleController:
24+
@get()
25+
def index(self):
26+
return self.context.__class__.__name__
27+
28+
@get("/exception")
29+
def exception(self):
30+
raise HTTPException(status_code=400)
31+
32+
33+
class NewExecutionContext(ExecutionContext):
34+
def __init__(self, **kwargs):
35+
super().__init__(**kwargs)
36+
self.instantiated = True
37+
self.__class__.worked = True
38+
39+
40+
@injectable()
41+
class NewExecutionHostFactory(IExecutionContextFactory):
42+
def __init__(self, reflector: Reflector):
43+
self.reflector = reflector
44+
45+
def create_context(self, operation) -> IExecutionContext:
46+
scoped_request_args = ASGI_CONTEXT_VAR.get()
47+
48+
if not scoped_request_args:
49+
raise ServiceUnavailable()
50+
51+
scope, receive, send = scoped_request_args.get_args()
52+
i_execution_context = NewExecutionContext(
53+
scope=scope,
54+
receive=receive,
55+
send=send,
56+
operation_handler=operation.endpoint,
57+
reflector=self.reflector,
58+
)
59+
i_execution_context.get_service_provider().update_scoped_context(
60+
IExecutionContext, i_execution_context
61+
)
62+
63+
return i_execution_context
64+
65+
66+
@injectable()
67+
class NewHostContextFactory(IHostContextFactory):
68+
def create_context(self) -> IHostContext:
69+
scoped_request_args = ASGI_CONTEXT_VAR.get()
70+
71+
if not scoped_request_args:
72+
raise ServiceUnavailable()
73+
74+
scope, receive, send = scoped_request_args.get_args()
75+
host_context = NewHostContext(scope=scope, receive=receive, send=send)
76+
host_context.get_service_provider().update_scoped_context(
77+
IHostContext, host_context
78+
)
79+
return host_context
80+
81+
82+
class NewHostContext(HostContext):
83+
def __init__(self, **kwargs):
84+
super().__init__(**kwargs)
85+
self.instantiated = True
86+
self.__class__.worked = True
87+
88+
89+
@injectable()
90+
class NewExceptionMiddlewareService(ExceptionMiddlewareService):
91+
def lookup_status_code_exception_handler(self, status_code: int):
92+
self.__class__.worked = True
93+
return self._status_handlers.get(status_code)
94+
95+
96+
def test_can_replace_host_context():
97+
tm = TestClientFactory.create_test_module(controllers=[ExampleController])
98+
tm.app.injector.container.register(IHostContextFactory, NewHostContextFactory)
99+
100+
assert hasattr(NewHostContext, "worked") is False
101+
client = tm.get_client()
102+
res = client.get("/example/")
103+
assert res.status_code == 200
104+
assert res.text == '"ExecutionContext"'
105+
106+
assert hasattr(NewHostContext, "worked") is True
107+
assert NewHostContext.worked is True
108+
109+
110+
def test_can_replace_exception_service():
111+
@Module(
112+
controllers=[ExampleController],
113+
providers=[
114+
ProviderConfig(
115+
IExceptionMiddlewareService, use_class=NewExceptionMiddlewareService
116+
)
117+
],
118+
)
119+
class ExampleModule:
120+
@exception_handler(400)
121+
def exception_400(self, context: IHostContext, exc: Exception):
122+
return PlainTextResponse(
123+
"Exception 400 handled by ExampleModule.exception_400"
124+
)
125+
126+
tm = TestClientFactory.create_test_module_from_module(ExampleModule)
127+
128+
assert hasattr(NewExceptionMiddlewareService, "worked") is False
129+
client = tm.get_client()
130+
res = client.get("/example/exception")
131+
assert res.status_code == 200
132+
assert res.text == "Exception 400 handled by ExampleModule.exception_400"
133+
134+
assert hasattr(NewExceptionMiddlewareService, "worked") is True
135+
assert NewExceptionMiddlewareService.worked is True
136+
137+
138+
def test_can_replace_execution_context():
139+
tm = TestClientFactory.create_test_module(controllers=[ExampleController])
140+
tm.app.injector.container.register(
141+
IExecutionContextFactory, NewExecutionHostFactory
142+
)
143+
144+
assert hasattr(NewExecutionContext, "worked") is False
145+
client = tm.get_client()
146+
res = client.get("/example/")
147+
assert res.status_code == 200
148+
assert res.text == '"NewExecutionContext"'
149+
150+
assert hasattr(NewExecutionContext, "worked") is True
151+
assert NewExecutionContext.worked is True

tests/test_di/test_injector.py

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from injector import Binder, Injector, UnsatisfiedRequirement
55

66
from ellar.common import Module
7-
from ellar.constants import MODULE_REF_TYPES
7+
from ellar.constants import ASGI_CONTEXT_VAR, MODULE_REF_TYPES
88
from ellar.core import Config, ModuleBase
99
from ellar.core.modules.ref import create_module_ref_factor
1010
from ellar.di import Container, EllarInjector
@@ -62,40 +62,53 @@ def test_default_container_registration():
6262

6363

6464
@pytest.mark.asyncio
65-
async def test_request_service_provider():
65+
async def test_request_service_context():
6666
injector = EllarInjector(auto_bind=False)
6767
injector.container.register_scoped(Foo1)
6868
injector.container.register_exact_transient(Foo2)
6969

70-
def assert_attributes(provider, expected):
71-
assert hasattr(provider, "parent") is expected
72-
assert hasattr(provider, "injector") is expected
73-
assert hasattr(provider, "_context") is expected
70+
def assert_attributes():
71+
asgi_context_var = ASGI_CONTEXT_VAR.get()
72+
assert len(asgi_context_var.context) == 0
73+
assert len(asgi_context_var.scope) == 0
74+
assert asgi_context_var.receive is None
75+
assert asgi_context_var.send is None
7476

75-
async with injector.create_request_service_provider() as request_provider:
76-
assert_attributes(request_provider, True)
77-
foo_instance = Foo()
78-
request_provider.update_context(Foo, foo_instance)
79-
request_provider.update_context(
80-
Foo1, Foo1()
81-
) # overrides parent provider config for Foo1
77+
async with injector.create_asgi_args({}, None, None):
78+
assert_attributes()
79+
asgi_context = ASGI_CONTEXT_VAR.get()
8280

83-
assert isinstance(
84-
injector.container.get_binding(Foo1)[0].provider, ClassProvider
85-
)
86-
assert isinstance(
87-
request_provider.get_binding(Foo1)[0].provider, InstanceProvider
88-
)
81+
foo1 = injector.get(Foo1) # result will be tracked by asgi_context
82+
injector.get(Foo2) # registered as transient scoped
8983

90-
assert request_provider.get(Foo) is foo_instance
91-
# request_provider defaults to parent when binding is not in its context
92-
assert isinstance(request_provider.get(Foo2), Foo2)
84+
assert isinstance(asgi_context.context[Foo1], InstanceProvider)
85+
assert asgi_context.context.get(Foo2) is None
86+
87+
# transient scoped
88+
foo2 = injector.get(Foo2)
89+
assert isinstance(foo2, Foo2)
90+
assert injector.get(Foo2) != foo2 # transient scoped
91+
92+
assert injector.get(Foo1) == foo1 # still within the context....
9393

9494
with pytest.raises(UnsatisfiedRequirement):
95-
# parent can not resolve bindings added to request service provider
9695
injector.get(Foo)
9796

98-
assert_attributes(request_provider, False)
97+
assert injector.get(Foo1) != foo1
98+
99+
100+
@pytest.mark.asyncio
101+
async def test_injector_update_scoped_context():
102+
injector = EllarInjector(auto_bind=False)
103+
104+
async with injector.create_asgi_args({}, None, None):
105+
asgi_context = ASGI_CONTEXT_VAR.get()
106+
107+
injector.update_scoped_context(Foo1, Foo1())
108+
injector.update_scoped_context(Foo, ClassProvider(Foo))
109+
110+
assert isinstance(asgi_context.context[Foo1], InstanceProvider)
111+
assert isinstance(asgi_context.context[Foo], ClassProvider)
99112

100113

101114
class TestInjectorModuleFunctions:

tests/test_di/test_middleware.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
from ellar.constants import SCOPE_SERVICE_PROVIDER
66
from ellar.core.connection import HTTPConnection, Request, WebSocket
7-
from ellar.core.context import IHostContext
7+
from ellar.core.context import HostContextException, IHostContext
88
from ellar.core.middleware import RequestServiceProviderMiddleware
99
from ellar.core.response import Response
10+
from ellar.core.services import CoreServiceRegistration
1011
from ellar.di import EllarInjector
1112

1213
from ..injector_module import Configuration, DummyModule
@@ -54,10 +55,10 @@ async def assert_iexecute_context_app(scope, receive, send):
5455
)
5556
assert service_provider is host_context.get_service_provider()
5657

57-
with pytest.raises(Exception):
58+
with pytest.raises(HostContextException):
5859
host_context.switch_to_websocket()
5960

60-
with pytest.raises(Exception):
61+
with pytest.raises(HostContextException):
6162
service_provider.get(WebSocket)
6263

6364
await send(
@@ -90,10 +91,10 @@ async def assert_iexecute_context_app_websocket(scope, receive, send):
9091
is host_context.switch_to_http_connection().get_client()
9192
)
9293

93-
with pytest.raises(Exception):
94+
with pytest.raises(HostContextException):
9495
service_provider.get(Request)
9596

96-
with pytest.raises(Exception):
97+
with pytest.raises(HostContextException):
9798
service_provider.get(Response)
9899

99100
await websocket.accept()
@@ -107,7 +108,7 @@ def test_di_middleware(test_client_factory):
107108
asgi_app = RequestServiceProviderMiddleware(
108109
assert_service_provider_app, debug=False, injector=injector_
109110
)
110-
111+
CoreServiceRegistration(injector_).register_all()
111112
client = test_client_factory(asgi_app)
112113
response = client.get("/")
113114

@@ -118,9 +119,11 @@ def test_di_middleware(test_client_factory):
118119

119120

120121
def test_di_middleware_execution_context_initialization(test_client_factory):
122+
injector_ = EllarInjector()
121123
asgi_app = RequestServiceProviderMiddleware(
122-
assert_iexecute_context_app, debug=False, injector=EllarInjector()
124+
assert_iexecute_context_app, debug=False, injector=injector_
123125
)
126+
CoreServiceRegistration(injector_).register_all()
124127

125128
client = test_client_factory(asgi_app)
126129
response = client.get("/")
@@ -131,9 +134,11 @@ def test_di_middleware_execution_context_initialization(test_client_factory):
131134

132135

133136
def test_di_middleware_execution_context_initialization_websocket(test_client_factory):
137+
injector_ = EllarInjector()
134138
asgi_app = RequestServiceProviderMiddleware(
135-
assert_iexecute_context_app_websocket, debug=False, injector=EllarInjector()
139+
assert_iexecute_context_app_websocket, debug=False, injector=injector_
136140
)
141+
CoreServiceRegistration(injector_).register_all()
137142

138143
client = test_client_factory(asgi_app)
139144
with client.websocket_connect("/") as session:

0 commit comments

Comments
 (0)