Skip to content

Commit 3054f30

Browse files
authored
🔨 Adds more diagnostics info when pytest fails (#159)
* updates pylint options * adds diagnostics * cleanup * @bisgaard-itis review: configs * format * fix
1 parent c0f4957 commit 3054f30

File tree

6 files changed

+143
-7
lines changed

6 files changed

+143
-7
lines changed

‎.gitignore‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ target/
6464
#Ipython Notebook
6565
.ipynb_checkpoints
6666

67-
# vscode config
67+
# IDEs config (except templates)
6868
.vscode/*
69+
!.vscode/*.template.json
70+
6971

7072
# docs output
7173
site
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"pylint.args": [
3+
"--rcfile=clients/python/.pylintrc"
4+
]
5+
}

‎clients/python/.pylintrc‎

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,69 @@ confidence=HIGH,
147147
# --enable=similarities". If you want to run only the classes checker, but have
148148
# no Warning level messages displayed, use "--disable=all --enable=classes
149149
# --disable=W".
150-
disable=R,C,W
150+
disable=raw-checker-failed,
151+
bad-inline-option,
152+
locally-disabled,
153+
file-ignored,
154+
suppressed-message,
155+
useless-suppression,
156+
deprecated-pragma,
157+
use-symbolic-message-instead,
158+
too-few-public-methods,
159+
trailing-whitespace,
160+
duplicate-code,
161+
too-many-locals,
162+
bad-file-encoding,
163+
unnecessary-dunder-call,
164+
unneeded-not,
165+
consider-using-enumerate,
166+
consider-iterating-dictionary,
167+
consider-using-dict-items,
168+
use-maxsplit-arg,
169+
use-sequence-for-iteration,
170+
consider-using-f-string,
171+
use-implicit-booleaness-not-len,
172+
use-implicit-booleaness-not-comparison,
173+
wrong-spelling-in-comment,
174+
wrong-spelling-in-docstring,
175+
invalid-characters-in-docstring,
176+
line-too-long,
177+
too-many-lines,
178+
missing-final-newline,
179+
trailing-newlines,
180+
multiple-statements,
181+
superfluous-parens,
182+
mixed-line-endings,
183+
unexpected-line-ending-format,
184+
non-ascii-name,
185+
non-ascii-module-import,
186+
unnecessary-lambda-assignment,
187+
unnecessary-direct-lambda-call,
188+
multiple-imports,
189+
wrong-import-order,
190+
ungrouped-imports,
191+
wrong-import-position,
192+
useless-import-alias,
193+
import-outside-toplevel,
194+
invalid-name,
195+
disallowed-name,
196+
typevar-name-incorrect-variance,
197+
typevar-double-variance,
198+
typevar-name-mismatch,
199+
empty-docstring,
200+
missing-module-docstring,
201+
missing-class-docstring,
202+
missing-function-docstring,
203+
singleton-comparison,
204+
unidiomatic-typecheck,
205+
bad-classmethod-argument,
206+
bad-mcs-method-argument,
207+
bad-mcs-classmethod-argument,
208+
single-string-used-for-slots,
209+
fixme,
210+
import-error,
211+
unspecified-encoding,
212+
use-dict-literal
151213

152214
# Enable the message, report, category or checker with the given id(s). You can
153215
# either give multiple identifier separated by comma (,) or put this option

‎clients/python/client/osparc/_models.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ParentProjectInfo(BaseSettings):
1818

1919
@field_validator("x_simcore_parent_project_uuid", "x_simcore_parent_node_id")
2020
@classmethod
21-
def validate_uuids(cls, v: Optional[str]) -> str:
21+
def _validate_uuids(cls, v: Optional[str]) -> str:
2222
if v is not None:
2323
_ = UUID(v)
2424
return v

‎clients/python/test/e2e/conftest.py‎

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
import logging
23
import os
34
from pathlib import Path
@@ -14,6 +15,70 @@
1415
_GB: ByteSize = ByteSize(_MB * 1024) # in bytes
1516

1617

18+
# Dictionary to store start times of tests
19+
_test_start_times = {}
20+
21+
22+
def _utc_now():
23+
return datetime.datetime.now(tz=datetime.timezone.utc)
24+
25+
26+
def _construct_graylog_url(api_host, start_time, end_time):
27+
"""
28+
Construct a Graylog URL for the given time interval.
29+
"""
30+
base_url = api_host.replace("api.", "monitoring.", 1).rstrip("/")
31+
url = f"{base_url}/graylog/search"
32+
start_time_str = start_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
33+
end_time_str = end_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
34+
query = f"from={start_time_str}&to={end_time_str}"
35+
return f"{url}?{query}"
36+
37+
38+
def pytest_runtest_setup(item):
39+
"""
40+
Hook to capture the start time of each test.
41+
"""
42+
_test_start_times[item.name] = _utc_now()
43+
44+
45+
def pytest_runtest_makereport(item, call):
46+
"""
47+
Hook to add extra information when a test fails.
48+
"""
49+
if call.when == "call":
50+
51+
# Check if the test failed
52+
if call.excinfo is not None:
53+
test_name = item.name
54+
test_location = item.location
55+
api_host = os.environ.get("OSPARC_API_HOST", "")
56+
57+
diagnostics = {
58+
"test_name": test_name,
59+
"test_location": test_location,
60+
"api_host": api_host,
61+
}
62+
63+
# Get the start and end times of the test
64+
start_time = _test_start_times.get(test_name)
65+
end_time = _utc_now()
66+
67+
if start_time:
68+
diagnostics["graylog_url"] = _construct_graylog_url(api_host, start_time, end_time)
69+
70+
# Print the diagnostics
71+
print(f"\nDiagnostics for {test_name}:")
72+
for key, value in diagnostics.items():
73+
print(" ", key, ":", value)
74+
75+
76+
@pytest.hookimpl(tryfirst=True)
77+
def pytest_configure(config):
78+
config.pluginmanager.register(pytest_runtest_setup, "osparc_test_times_plugin")
79+
config.pluginmanager.register(pytest_runtest_makereport, "osparc_makereport_plugin")
80+
81+
1782
@pytest.fixture
1883
def configuration() -> Iterable[osparc.Configuration]:
1984
assert (host := os.environ.get("OSPARC_API_HOST"))
@@ -60,9 +125,9 @@ def tmp_file(tmp_path: Path, caplog) -> Path:
60125

61126

62127
@pytest.fixture
63-
def sleeper(api_client: osparc.ApiClient) -> Iterable[osparc.Solver]:
128+
def sleeper(api_client: osparc.ApiClient) -> osparc.Solver:
64129
solvers_api = osparc.SolversApi(api_client=api_client)
65130
sleeper: osparc.Solver = solvers_api.get_solver_release(
66131
"simcore/services/comp/itis/sleeper", "2.0.2"
67132
) # type: ignore
68-
yield sleeper
133+
return sleeper

‎clients/python/test/e2e/test_solvers_api.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ async def test_logstreaming(
6262
solvers_api.start_job(sleeper.id, sleeper.version, job.id)
6363

6464
nloglines: int = 0
65-
print("starting logstreaming...")
65+
url = f"/v0/solvers/{sleeper.id}/releases/{sleeper.version}/jobs/{job.id}/logstream"
66+
print(f"starting logstreaming from {url}...")
67+
6668
async with async_client.stream(
6769
"GET",
68-
f"/v0/solvers/{sleeper.id}/releases/{sleeper.version}/jobs/{job.id}/logstream",
70+
url,
6971
timeout=DEFAULT_TIMEOUT_SECONDS,
7072
) as response:
7173
async for line in response.aiter_lines():

0 commit comments

Comments
 (0)