0003-tests-add-fixture-decorator-for-db-fixture-with-glob.patch
tests/conftest.py | ||
---|---|---|
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 | |
18 |
import contextlib |
|
19 |
import inspect |
|
18 | 20 |
import urllib.parse |
19 | 21 |
from unittest import mock |
20 | 22 | |
... | ... | |
24 | 26 |
from django.contrib.auth import get_user_model |
25 | 27 |
from django.core.cache import cache |
26 | 28 |
from django.core.management import call_command |
27 |
from django.db import connection |
|
29 |
from django.db import connection, transaction
|
|
28 | 30 |
from django.db.migrations.executor import MigrationExecutor |
29 | 31 | |
30 | 32 |
from authentic2 import hooks as a2_hooks |
... | ... | |
528 | 530 |
required_on_login=True, |
529 | 531 |
user_visible=True, |
530 | 532 |
) |
533 | ||
534 | ||
535 |
@pytest.fixture(scope='session') |
|
536 |
def scoped_db(django_db_setup, django_db_blocker): |
|
537 |
'''Scoped fixture, use like that to load some models for session/module/class scope: |
|
538 | ||
539 |
@pytest.fixture(scope='module') |
|
540 |
def myfixture(scoped_db): |
|
541 |
@scoped_db |
|
542 |
def f(): |
|
543 |
return Model.objects.create(x=1) |
|
544 |
yield from f() |
|
545 |
''' |
|
546 | ||
547 |
@contextlib.contextmanager |
|
548 |
def scoped_db(func): |
|
549 |
with django_db_blocker.unblock(): |
|
550 |
with transaction.atomic(): |
|
551 |
try: |
|
552 |
if inspect.isgeneratorfunction(func): |
|
553 |
generator = func() |
|
554 |
value = next(generator) |
|
555 |
else: |
|
556 |
value = func() |
|
557 |
with django_db_blocker.block(): |
|
558 |
yield value |
|
559 |
if inspect.isgeneratorfunction(func): |
|
560 |
try: |
|
561 |
next(generator) |
|
562 |
except StopIteration: |
|
563 |
pass |
|
564 |
else: |
|
565 |
raise RuntimeError(f'{func} yielded more than one time') |
|
566 |
finally: |
|
567 |
transaction.set_rollback(True) |
|
568 | ||
569 |
return scoped_db |
tests/utils.py | ||
---|---|---|
16 | 16 |
# authentic2 |
17 | 17 | |
18 | 18 |
import base64 |
19 |
import functools |
|
20 |
import inspect |
|
19 | 21 |
import re |
20 | 22 |
import socket |
21 | 23 |
import urllib.parse |
22 | 24 |
from contextlib import closing, contextmanager |
23 | 25 | |
26 |
import pytest |
|
24 | 27 |
from django.contrib.messages.storage.cookie import MessageDecoder, MessageEncoder |
25 | 28 | |
26 | 29 |
try: |
... | ... | |
356 | 359 |
except AttributeError: |
357 | 360 |
# xxx support legacy decoding? |
358 | 361 |
return data |
362 | ||
363 | ||
364 |
def scoped_db_fixture(func=None, /, **kwargs): |
|
365 |
'''Create a db fixture with a scope different than 'function' the default one.''' |
|
366 |
if func is None: |
|
367 |
return functools.partial(scoped_db_fixture, **kwargs) |
|
368 |
assert kwargs.get('scope') in [ |
|
369 |
'session', |
|
370 |
'module', |
|
371 |
'class', |
|
372 |
], 'scoped_db_fixture is only usable with a non function scope' |
|
373 |
signature = inspect.signature(func) |
|
374 |
inner_parameters = [] |
|
375 |
for parameter in signature.parameters: |
|
376 |
inner_parameters.append(parameter) |
|
377 |
outer_parameters = ['scoped_db'] if 'scoped_db' not in inner_parameters else [] |
|
378 |
if inner_parameters and inner_parameters[0] == 'self': |
|
379 |
outer_parameters = ['self'] + outer_parameters + inner_parameters[1:] |
|
380 |
else: |
|
381 |
outer_parameters = outer_parameters + inner_parameters |
|
382 |
# build a new fixture function, inject the scoped_db fixture inside and the old function in a scoped_db context. |
|
383 |
new_function_def = f'''def f({", ".join(outer_parameters)}): |
|
384 |
if inspect.isgeneratorfunction(func): |
|
385 |
def g(): |
|
386 |
yield from func({", ".join(inner_parameters)}) |
|
387 |
else: |
|
388 |
def g(): |
|
389 |
return func({", ".join(inner_parameters)}) |
|
390 |
with scoped_db(g) as result: |
|
391 |
yield result''' |
|
392 |
new_function_bytecode = compile(new_function_def, filename=f'<scoped-db-fixture {func}>', mode='single') |
|
393 |
global_dict = {'func': func, 'inspect': inspect} |
|
394 |
eval(new_function_bytecode, global_dict) # pylint: disable=eval-used |
|
395 |
new_function = global_dict['f'] |
|
396 |
wrapped_func = functools.wraps(func)(new_function) |
|
397 |
# prevent original fixture signature to override the new fixture signature |
|
398 |
# (because of inspect.signature(follow_wrapped=True)) during pytest collection |
|
399 |
del wrapped_func.__wrapped__ |
|
400 |
return pytest.fixture(**kwargs)(wrapped_func) |
|
359 |
- |