1+ import importlib
2+ import sys
13import uuid
4+ from functools import lru_cache
5+ from pathlib import Path
26
37import pytest
8+ from _pytest .monkeypatch import MonkeyPatch
9+ from alembic import command
10+ from alembic .config import Config as AlembicConfig
411from fastapi .testclient import TestClient
512from sqlalchemy import create_engine
6- from sqlalchemy .orm import Session , sessionmaker
13+ from sqlalchemy .orm import sessionmaker
14+ from testcontainers .postgres import PostgresContainer
715
816from rating_api .models .db import *
917from rating_api .routes import app
10- from rating_api .settings import Settings
18+ from rating_api .settings import Settings , get_settings
19+
20+
21+ class PostgresConfig :
22+ """Дата-класс со значениями для контейнера с тестовой БД и alembic-миграции."""
23+
24+ container_name : str = 'rating_test'
25+ username : str = 'postgres'
26+ host : str = 'localhost'
27+ image : str = 'postgres:15'
28+ external_port : int = 5433
29+ ham : str = 'trust'
30+ alembic_ini : str = Path (__file__ ).resolve ().parent .parent / 'alembic.ini'
31+
32+ @classmethod
33+ def get_url (cls ):
34+ """Возвращает URI для подключения к БД."""
35+ return f'postgresql://{ cls .username } @{ cls .host } :{ cls .external_port } /postgres'
36+
37+
38+ @pytest .fixture (scope = "session" )
39+ def session_mp ():
40+ """Аналог monkeypatch, но с session-scope."""
41+ mp = MonkeyPatch ()
42+ yield mp
43+ mp .undo ()
44+
45+
46+ @pytest .fixture (scope = 'session' )
47+ def get_settings_mock (session_mp ):
48+ """Переопределение get_settings в rating_api/settings.py и перезагрузка base.app."""
49+
50+ @lru_cache
51+ def get_test_settings () -> Settings :
52+ settings = Settings ()
53+ settings .DB_DSN = PostgresConfig .get_url ()
54+ return settings
55+
56+ get_settings .cache_clear ()
57+ dsn_mock = session_mp .setattr ('rating_api.settings.get_settings' , get_test_settings )
58+ reloaded_module = sys .modules ['rating_api.routes.base' ]
59+ importlib .reload (reloaded_module )
60+ importlib .reload (sys .modules ['rating_api.routes.exc_handlers' ])
61+ globals ()['app' ] = reloaded_module .app
62+ return dsn_mock
63+
64+
65+ @pytest .fixture (scope = "session" )
66+ def db_container (get_settings_mock ):
67+ """Фикстура настройки БД для тестов в Docker-контейнере."""
68+ container = (
69+ PostgresContainer (
70+ image = PostgresConfig .image , username = PostgresConfig .username , dbname = PostgresConfig .container_name
71+ )
72+ .with_bind_ports (5432 , PostgresConfig .external_port )
73+ .with_env ("POSTGRES_HOST_AUTH_METHOD" , PostgresConfig .ham )
74+ )
75+ container .start ()
76+ cfg = AlembicConfig (str (PostgresConfig .alembic_ini .resolve ()))
77+ cfg .set_main_option ("script_location" , "%(here)s/migrations" )
78+ command .upgrade (cfg , "head" )
79+ try :
80+ yield PostgresConfig .get_url ()
81+ finally :
82+ container .stop ()
83+
84+
85+ @pytest .fixture ()
86+ def dbsession (db_container ):
87+ """Фикстура настройки Session для работы с БД в тестах."""
88+ engine = create_engine (str (db_container ), pool_pre_ping = True )
89+ TestingSessionLocal = sessionmaker (bind = engine )
90+ session = TestingSessionLocal ()
91+ yield session
1192
1293
1394@pytest .fixture
@@ -25,15 +106,6 @@ def client(mocker):
25106 return client
26107
27108
28- @pytest .fixture
29- def dbsession () -> Session :
30- settings = Settings ()
31- engine = create_engine (str (settings .DB_DSN ), pool_pre_ping = True )
32- TestingSessionLocal = sessionmaker (bind = engine )
33- session = TestingSessionLocal ()
34- yield session
35-
36-
37109@pytest .fixture
38110def lecturer (dbsession ):
39111 _lecturer = Lecturer (first_name = "test_fname" , last_name = "test_lname" , middle_name = "test_mname" , timetable_id = 9900 )
0 commit comments