Skip to content

Commit 15db3c3

Browse files
committed
Adding smoketests for the API examples
This runs every API example in a minimal environment where the dependencies for the other API frameworks are missing. This test guarantees that json-logging works for each individual framework.
1 parent 9eda892 commit 15db3c3

File tree

12 files changed

+197
-3
lines changed

12 files changed

+197
-3
lines changed

.github/workflows/code_quality.yml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ jobs:
3535
runs-on: ubuntu-latest
3636

3737
strategy:
38-
max-parallel: 4
3938
matrix:
4039
python-version: ["3.7", "3.8", "3.9"]
4140

@@ -52,4 +51,28 @@ jobs:
5251
pip install -r test-requirements.txt
5352
- name: Run tests
5453
run: |
55-
pytest
54+
pytest --ignore tests/smoketests
55+
56+
backendSmoketests:
57+
name: Individual Backend Smoketests
58+
runs-on: ubuntu-latest
59+
60+
strategy:
61+
matrix:
62+
backend: ["fastapi", "flask", "quart", "sanic"]
63+
64+
steps:
65+
- name: Set up Python ${{ matrix.python-version }}
66+
uses: actions/setup-python@v2
67+
with:
68+
python-version: 3.9
69+
- name: "Git checkout"
70+
uses: actions/checkout@v2
71+
- name: Install dependencies
72+
run: |
73+
python -m pip install --upgrade pip
74+
pip install -r tests/smoketests/${{ matrix.backend }}/requirements.txt
75+
- name: Run tests
76+
run: |
77+
export backend=${{ matrix.backend }}
78+
pytest tests/smoketests/test_run_smoketest.py

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ async def home(request):
125125
return sanic.response.text("hello world")
126126

127127
if __name__ == "__main__":
128-
app.run(host="0.0.0.0", port=8000)
128+
app.run(host="0.0.0.0", port=5000)
129129
```
130130

131131
### Quart

tests/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Organization of the test folder
2+
3+
```
4+
├───helpers Shared functionality for all tests
5+
├───smoketests A test script to run all API examples and see if they work
6+
│ ├───fastapi
7+
│ ├───flask
8+
│ ├───quart
9+
│ └───sanic
10+
└───test_*.py Unit tests
11+
```

tests/smoketests/fastapi/api.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import datetime, logging, sys, json_logging, fastapi, uvicorn
2+
3+
app = fastapi.FastAPI()
4+
json_logging.init_fastapi(enable_json=True)
5+
json_logging.init_request_instrument(app)
6+
7+
# init the logger as usual
8+
logger = logging.getLogger("test-logger")
9+
logger.setLevel(logging.DEBUG)
10+
logger.addHandler(logging.StreamHandler(sys.stdout))
11+
12+
@app.get('/')
13+
def home():
14+
logger.info("test log statement")
15+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
16+
correlation_id = json_logging.get_correlation_id()
17+
return "Hello world : " + str(datetime.datetime.now())
18+
19+
if __name__ == "__main__":
20+
uvicorn.run(app, host='0.0.0.0', port=5000)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fastapi
2+
uvicorn
3+
requests
4+
pytest
5+
-e .

tests/smoketests/flask/api.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import datetime, logging, sys, json_logging, flask
2+
3+
app = flask.Flask(__name__)
4+
json_logging.init_flask(enable_json=True)
5+
json_logging.init_request_instrument(app)
6+
7+
# init the logger as usual
8+
logger = logging.getLogger("test-logger")
9+
logger.setLevel(logging.DEBUG)
10+
logger.addHandler(logging.StreamHandler(sys.stdout))
11+
12+
@app.route('/')
13+
def home():
14+
logger.info("test log statement")
15+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
16+
correlation_id = json_logging.get_correlation_id()
17+
return "Hello world : " + str(datetime.datetime.now())
18+
19+
if __name__ == "__main__":
20+
app.run(host='0.0.0.0', port=int(5000), use_reloader=False)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
flask
2+
requests
3+
pytest
4+
-e .

tests/smoketests/quart/api.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import asyncio, logging, sys, json_logging, quart
2+
3+
app = quart.Quart(__name__)
4+
json_logging.init_quart(enable_json=True)
5+
json_logging.init_request_instrument(app)
6+
7+
# init the logger as usual
8+
logger = logging.getLogger("test logger")
9+
logger.setLevel(logging.DEBUG)
10+
logger.addHandler(logging.StreamHandler(sys.stdout))
11+
12+
@app.route('/')
13+
async def home():
14+
logger.info("test log statement")
15+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
16+
correlation_id = json_logging.get_correlation_id()
17+
return "Hello world"
18+
19+
if __name__ == "__main__":
20+
loop = asyncio.get_event_loop()
21+
app.run(host='0.0.0.0', port=int(5000), use_reloader=False, loop=loop)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
quart
2+
requests
3+
pytest
4+
-e .

tests/smoketests/sanic/api.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import logging, sys, json_logging, sanic
2+
3+
app = sanic.Sanic(name="sanic-web-app")
4+
json_logging.init_sanic(enable_json=True)
5+
json_logging.init_request_instrument(app)
6+
7+
# init the logger as usual
8+
logger = logging.getLogger("sanic-integration-test-app")
9+
logger.setLevel(logging.DEBUG)
10+
logger.addHandler(logging.StreamHandler(sys.stdout))
11+
12+
@app.route("/")
13+
async def home(request):
14+
logger.info("test log statement")
15+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
16+
# this will be faster
17+
correlation_id = json_logging.get_correlation_id(request=request)
18+
# this will be slower, but will work in context you cant get a reference of request object
19+
correlation_id_without_request_obj = json_logging.get_correlation_id()
20+
21+
return sanic.response.text("hello world")
22+
23+
if __name__ == "__main__":
24+
app.run(host="0.0.0.0", port=5000)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
sanic
2+
requests
3+
pytest
4+
-e .
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Test module to run all example APIs and see if they work independently of each other
2+
3+
These tests are meant to be executed independently with only the minimal environment.
4+
This way it becomes clear if json_logging works without the dependencies of API frameworks one
5+
doesn't intend to use.
6+
"""
7+
8+
import os
9+
import signal
10+
import subprocess
11+
import sys
12+
import time
13+
from pathlib import Path
14+
15+
import pytest
16+
import requests
17+
18+
19+
def collect_backends():
20+
"""Generator function which returns all test backends one by one
21+
22+
If the environment variable "backend" is set, only this one backend is returned
23+
"""
24+
preset_backend = os.environ.get('backend')
25+
if preset_backend:
26+
yield preset_backend
27+
else:
28+
for folder in Path(__file__).parent.iterdir():
29+
if folder.is_dir():
30+
yield str(folder.name)
31+
32+
33+
@pytest.mark.parametrize('backend', collect_backends(), ids=collect_backends())
34+
def test_api_example(backend):
35+
"""For each of the examples start the API and see if the root endpoint is reachable"""
36+
api_process = subprocess.Popen(
37+
[sys.executable, Path(__file__).parent / backend / "api.py"],
38+
stdout=sys.stdout,
39+
stderr=subprocess.STDOUT,
40+
)
41+
try:
42+
deadline = time.perf_counter() + 10.0
43+
while time.perf_counter() < deadline:
44+
time.sleep(1)
45+
session = requests.Session()
46+
session.trust_env = False
47+
os.environ['NO_PROXY'] = 'localhost'
48+
os.environ['no_proxy'] = 'localhost'
49+
try:
50+
response = requests.get("http://localhost:5000/", timeout=1)
51+
assert response.status_code == 200
52+
return
53+
except requests.exceptions.Timeout:
54+
pass
55+
assert False, "API did not respond"
56+
finally:
57+
api_process.send_signal(signal.SIGTERM)
58+
api_process.wait(timeout=5.0)

0 commit comments

Comments
 (0)