forked from aio-libs/aiohttp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_web_app.py
459 lines (344 loc) · 11.2 KB
/
test_web_app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
import asyncio
from unittest import mock
import pytest
from async_generator import async_generator, yield_
from aiohttp import log, web
from aiohttp.helpers import PY_36
from aiohttp.test_utils import make_mocked_coro
async def test_app_ctor() -> None:
app = web.Application()
assert app.logger is log.web_logger
def test_app_call() -> None:
app = web.Application()
assert app is app()
async def test_app_register_on_finish() -> None:
app = web.Application()
cb1 = make_mocked_coro(None)
cb2 = make_mocked_coro(None)
app.on_cleanup.append(cb1)
app.on_cleanup.append(cb2)
app.freeze()
await app.cleanup()
cb1.assert_called_once_with(app)
cb2.assert_called_once_with(app)
async def test_app_register_coro() -> None:
app = web.Application()
fut = asyncio.get_event_loop().create_future()
async def cb(app):
await asyncio.sleep(0.001)
fut.set_result(123)
app.on_cleanup.append(cb)
app.freeze()
await app.cleanup()
assert fut.done()
assert 123 == fut.result()
def test_logging() -> None:
logger = mock.Mock()
app = web.Application()
app.logger = logger
assert app.logger is logger
async def test_on_shutdown() -> None:
app = web.Application()
called = False
async def on_shutdown(app_param):
nonlocal called
assert app is app_param
called = True
app.on_shutdown.append(on_shutdown)
app.freeze()
await app.shutdown()
assert called
async def test_on_startup() -> None:
app = web.Application()
long_running1_called = False
long_running2_called = False
all_long_running_called = False
async def long_running1(app_param):
nonlocal long_running1_called
assert app is app_param
long_running1_called = True
async def long_running2(app_param):
nonlocal long_running2_called
assert app is app_param
long_running2_called = True
async def on_startup_all_long_running(app_param):
nonlocal all_long_running_called
assert app is app_param
all_long_running_called = True
return await asyncio.gather(long_running1(app_param),
long_running2(app_param))
app.on_startup.append(on_startup_all_long_running)
app.freeze()
await app.startup()
assert long_running1_called
assert long_running2_called
assert all_long_running_called
def test_app_delitem() -> None:
app = web.Application()
app['key'] = 'value'
assert len(app) == 1
del app['key']
assert len(app) == 0
def test_app_freeze() -> None:
app = web.Application()
subapp = mock.Mock()
subapp._middlewares = ()
app._subapps.append(subapp)
app.freeze()
assert subapp.freeze.called
app.freeze()
assert len(subapp.freeze.call_args_list) == 1
def test_equality() -> None:
app1 = web.Application()
app2 = web.Application()
assert app1 == app1
assert app1 != app2
def test_app_run_middlewares() -> None:
root = web.Application()
sub = web.Application()
root.add_subapp('/sub', sub)
root.freeze()
assert root._run_middlewares is False
async def middleware(request, handler):
return await handler(request)
root = web.Application(middlewares=[middleware])
sub = web.Application()
root.add_subapp('/sub', sub)
root.freeze()
assert root._run_middlewares is True
root = web.Application()
sub = web.Application(middlewares=[middleware])
root.add_subapp('/sub', sub)
root.freeze()
assert root._run_middlewares is True
def test_subapp_pre_frozen_after_adding() -> None:
app = web.Application()
subapp = web.Application()
app.add_subapp('/prefix', subapp)
assert subapp.pre_frozen
assert not subapp.frozen
@pytest.mark.skipif(not PY_36,
reason="Python 3.6+ required")
def test_app_inheritance() -> None:
with pytest.raises(TypeError):
class A(web.Application):
pass
def test_app_custom_attr() -> None:
app = web.Application()
with pytest.raises(AttributeError):
app.custom = None
async def test_cleanup_ctx() -> None:
app = web.Application()
out = []
def f(num):
@async_generator
async def inner(app):
out.append('pre_' + str(num))
await yield_(None)
out.append('post_' + str(num))
return inner
app.cleanup_ctx.append(f(1))
app.cleanup_ctx.append(f(2))
app.freeze()
await app.startup()
assert out == ['pre_1', 'pre_2']
await app.cleanup()
assert out == ['pre_1', 'pre_2', 'post_2', 'post_1']
async def test_cleanup_ctx_exception_on_startup() -> None:
app = web.Application()
out = []
exc = Exception('fail')
def f(num, fail=False):
@async_generator
async def inner(app):
out.append('pre_' + str(num))
if fail:
raise exc
await yield_(None)
out.append('post_' + str(num))
return inner
app.cleanup_ctx.append(f(1))
app.cleanup_ctx.append(f(2, True))
app.cleanup_ctx.append(f(3))
app.freeze()
with pytest.raises(Exception) as ctx:
await app.startup()
assert ctx.value is exc
assert out == ['pre_1', 'pre_2']
await app.cleanup()
assert out == ['pre_1', 'pre_2', 'post_1']
async def test_cleanup_ctx_exception_on_cleanup() -> None:
app = web.Application()
out = []
exc = Exception('fail')
def f(num, fail=False):
@async_generator
async def inner(app):
out.append('pre_' + str(num))
await yield_(None)
out.append('post_' + str(num))
if fail:
raise exc
return inner
app.cleanup_ctx.append(f(1))
app.cleanup_ctx.append(f(2, True))
app.cleanup_ctx.append(f(3))
app.freeze()
await app.startup()
assert out == ['pre_1', 'pre_2', 'pre_3']
with pytest.raises(Exception) as ctx:
await app.cleanup()
assert ctx.value is exc
assert out == ['pre_1', 'pre_2', 'pre_3', 'post_3', 'post_2', 'post_1']
async def test_cleanup_ctx_exception_on_cleanup_multiple() -> None:
app = web.Application()
out = []
def f(num, fail=False):
@async_generator
async def inner(app):
out.append('pre_' + str(num))
await yield_(None)
out.append('post_' + str(num))
if fail:
raise Exception('fail_' + str(num))
return inner
app.cleanup_ctx.append(f(1))
app.cleanup_ctx.append(f(2, True))
app.cleanup_ctx.append(f(3, True))
app.freeze()
await app.startup()
assert out == ['pre_1', 'pre_2', 'pre_3']
with pytest.raises(web.CleanupError) as ctx:
await app.cleanup()
exc = ctx.value
assert len(exc.exceptions) == 2
assert str(exc.exceptions[0]) == 'fail_3'
assert str(exc.exceptions[1]) == 'fail_2'
assert out == ['pre_1', 'pre_2', 'pre_3', 'post_3', 'post_2', 'post_1']
async def test_cleanup_ctx_multiple_yields() -> None:
app = web.Application()
out = []
def f(num):
@async_generator
async def inner(app):
out.append('pre_' + str(num))
await yield_(None)
out.append('post_' + str(num))
await yield_(None)
return inner
app.cleanup_ctx.append(f(1))
app.freeze()
await app.startup()
assert out == ['pre_1']
with pytest.raises(RuntimeError) as ctx:
await app.cleanup()
assert "has more than one 'yield'" in str(ctx.value)
assert out == ['pre_1', 'post_1']
async def test_subapp_chained_config_dict_visibility(aiohttp_client) -> None:
async def main_handler(request):
assert request.config_dict['key1'] == 'val1'
assert 'key2' not in request.config_dict
return web.Response(status=200)
root = web.Application()
root['key1'] = 'val1'
root.add_routes([web.get('/', main_handler)])
async def sub_handler(request):
assert request.config_dict['key1'] == 'val1'
assert request.config_dict['key2'] == 'val2'
return web.Response(status=201)
sub = web.Application()
sub['key2'] = 'val2'
sub.add_routes([web.get('/', sub_handler)])
root.add_subapp('/sub', sub)
client = await aiohttp_client(root)
resp = await client.get('/')
assert resp.status == 200
resp = await client.get('/sub/')
assert resp.status == 201
async def test_subapp_chained_config_dict_overriding(aiohttp_client) -> None:
async def main_handler(request):
assert request.config_dict['key'] == 'val1'
return web.Response(status=200)
root = web.Application()
root['key'] = 'val1'
root.add_routes([web.get('/', main_handler)])
async def sub_handler(request):
assert request.config_dict['key'] == 'val2'
return web.Response(status=201)
sub = web.Application()
sub['key'] = 'val2'
sub.add_routes([web.get('/', sub_handler)])
root.add_subapp('/sub', sub)
client = await aiohttp_client(root)
resp = await client.get('/')
assert resp.status == 200
resp = await client.get('/sub/')
assert resp.status == 201
async def test_subapp_on_startup(aiohttp_client) -> None:
subapp = web.Application()
startup_called = False
async def on_startup(app):
nonlocal startup_called
startup_called = True
app['startup'] = True
subapp.on_startup.append(on_startup)
ctx_pre_called = False
ctx_post_called = False
@async_generator
async def cleanup_ctx(app):
nonlocal ctx_pre_called, ctx_post_called
ctx_pre_called = True
app['cleanup'] = True
await yield_(None)
ctx_post_called = True
subapp.cleanup_ctx.append(cleanup_ctx)
shutdown_called = False
async def on_shutdown(app):
nonlocal shutdown_called
shutdown_called = True
subapp.on_shutdown.append(on_shutdown)
cleanup_called = False
async def on_cleanup(app):
nonlocal cleanup_called
cleanup_called = True
subapp.on_cleanup.append(on_cleanup)
app = web.Application()
app.add_subapp('/subapp', subapp)
assert not startup_called
assert not ctx_pre_called
assert not ctx_post_called
assert not shutdown_called
assert not cleanup_called
assert subapp.on_startup.frozen
assert subapp.cleanup_ctx.frozen
assert subapp.on_shutdown.frozen
assert subapp.on_cleanup.frozen
assert subapp.router.frozen
client = await aiohttp_client(app)
assert startup_called
assert ctx_pre_called
assert not ctx_post_called
assert not shutdown_called
assert not cleanup_called
await client.close()
assert startup_called
assert ctx_pre_called
assert ctx_post_called
assert shutdown_called
assert cleanup_called
def test_app_iter():
app = web.Application()
app['a'] = '1'
app['b'] = '2'
assert sorted(list(app)) == ['a', 'b']
def test_app_forbid_nonslot_attr():
app = web.Application()
with pytest.raises(AttributeError):
app.unknow_attr
with pytest.raises(AttributeError):
app.unknow_attr = 1
def test_forbid_changing_frozen_app() -> None:
app = web.Application()
app.freeze()
with pytest.raises(RuntimeError):
app['key'] = 'value'