-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathconftest.py
177 lines (138 loc) · 5.82 KB
/
conftest.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
"""Pytest config and fixtures for all test suites"""
import os
import platform
import subprocess
import pytest
import requests
from requests.adapters import HTTPAdapter, Retry
from retry import retry
from django.core.management import call_command
from django.test import override_settings
def pytest_configure(config):
# Bootstrap Django config
from nav.bootstrap import bootstrap_django
bootstrap_django('pytest')
# Setup test environment for Django
from django.test.utils import setup_test_environment
setup_test_environment()
if platform.system() == 'Linux':
# Install custom reactor for Twisted tests
from nav.ipdevpoll.epollreactor2 import install
install()
def is_running_in_github_actions():
"""Returns True if running under GitHub Actions"""
return os.getenv("GITHUB_ACTIONS")
def _get_preferred_database_name():
if is_running_in_github_actions():
if not os.getenv("PGDATABASE") and os.getenv("TOX_ENV_NAME"):
# Generate an appropriately unique database name for this test run
return "{prefix}_{suffix}".format(
prefix=os.getenv("GITHUB_RUN_ID", "tox"),
suffix=os.getenv("TOX_ENV_NAME").replace("-", "_"),
)
return "nav"
@pytest.fixture(scope='session')
def postgresql(request, admin_username, admin_password):
"""Fixture for all tests that depend on a running PostgreSQL server. This fixture
will try to detect and use an existing PostgreSQL instance (like if running in a
GitHub action), otherwise it will set up a temporary PostgreSQL server for the test
session.
If your test needs to write to the database, it should ask for the `db` fixture
instead, as this ensures changes are rolled back when the test is done. However,
if your test makes db changes that need to be visible from another process, you
must make your own data fixture to ensure the data is removed when the test is
done.
"""
if not is_running_in_github_actions():
request.getfixturevalue("docker_services")
dbname = _get_preferred_database_name()
_update_db_conf_for_test_run(dbname)
_populate_test_database(dbname, admin_username, admin_password)
yield dbname
print("postgres fixture is done")
def _update_db_conf_for_test_run(database_name):
db_conf_path = os.path.join(os.getenv("BUILDDIR"), "etc/db.conf")
pghost = os.getenv('PGHOST', 'localhost')
pgport = os.getenv('PGPORT', '5432')
pguser = os.getenv('PGUSER', 'nav')
pgpassword = os.getenv('PGPASSWORD', 'nav')
with open(db_conf_path, "w") as output:
output.writelines(
[
f"dbhost={pghost}\n",
f"dbport={pgport}\n",
f"db_nav={database_name}\n",
f"script_default={pguser}\n",
f"userpw_{pguser}={pgpassword}\n",
"\n",
]
)
return db_conf_path
@retry(Exception, tries=3, delay=2, backoff=2)
def _populate_test_database(database_name, admin_username, admin_password):
# Init/sync db schema
env = {
'PGHOST': 'localhost',
'PGUSER': 'nav',
'PGDATABASE': database_name,
'PGPORT': '5432',
'PGPASSWORD': 'nav',
'PATH': os.getenv("PATH"),
}
navsyncdb_path = os.path.join(os.getenv("BUILDDIR"), 'bin', 'navsyncdb')
subprocess.check_call([navsyncdb_path], env=env) # , '-c'], #, '--drop-database'],
# reset password of NAV admin account if indicated by environment
if admin_password:
sql = f"UPDATE account SET password = {admin_password!r} WHERE login={admin_username!r}"
subprocess.check_call(["psql", "-c", sql, database_name], env=env)
# Add generic test data set
test_data_path = './tests/docker/scripts/test-data.sql'
subprocess.check_call(["psql", "-f", test_data_path, database_name], env=env)
@pytest.fixture(scope='session')
def admin_username():
return os.environ.get('ADMINUSERNAME', 'admin')
@pytest.fixture(scope='session')
def admin_password():
return os.environ.get('ADMINPASSWORD', 'admin')
@pytest.fixture(scope='session')
def build_sass():
"""Builds the NAV SASS files into CSS files that can be installed as static files"""
subprocess.check_call(["make", "sassbuild"])
@pytest.fixture(scope='session')
def staticfiles(build_sass, tmp_path_factory):
"""Collects Django static files into a temporary directory and return the web root
directory path that can be served by a web server.
"""
webroot = tmp_path_factory.mktemp("webroot")
static = webroot / "static"
with override_settings(STATIC_ROOT=static):
print(f"Collecting static files in {static!r}")
call_command('collectstatic', interactive=False)
yield webroot
@pytest.fixture(scope='session')
def gunicorn(postgresql, staticfiles):
"""Sets up NAV to be served by a gunicorn instance.
Useful for tests that need to make external HTTP requests to NAV.
Returns the (expected) base URL of the gunicorn instance.
"""
workspace = os.path.join(os.environ.get('WORKSPACE', ''), 'reports')
errorlog = os.path.join(workspace, 'gunicorn-error.log')
accesslog = os.path.join(workspace, 'gunicorn-access.log')
gunicorn = subprocess.Popen(
[
'gunicorn',
'--error-logfile',
errorlog,
'--access-logfile',
accesslog,
f'navtest_wsgi:nav_test_app(root={str(staticfiles)!r})',
]
)
# Allow for gunicorn to become ready to serve requests before handing off to a test
session = requests.Session()
retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504])
base_url = "http://localhost:8000"
session.mount(base_url, HTTPAdapter(max_retries=retries))
session.get(base_url)
yield base_url
gunicorn.terminate()