Skip to content

Commit 63f80fc

Browse files
committed
python: Add unit tests for Svix API client (WIP)
1 parent a0942fb commit 63f80fc

File tree

5 files changed

+194
-0
lines changed

5 files changed

+194
-0
lines changed

.github/workflows/python-tests.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Python Tests
2+
on:
3+
push:
4+
paths:
5+
- "python/**"
6+
- "openapi.json"
7+
pull_request:
8+
paths:
9+
- "python/**"
10+
- "openapi.json"
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Build svix server image
18+
run: docker compose build
19+
working-directory: ./server
20+
21+
- uses: actions/setup-python@v2
22+
name: Install Python
23+
with:
24+
python-version: "3.11"
25+
26+
- name: Install deps
27+
run: |
28+
python -m pip install --upgrade pip
29+
python -m pip install -r requirements.txt .
30+
python -m pip install -r requirements-dev.txt .
31+
working-directory: ./python
32+
33+
- name: Regen openapi libs
34+
run: ./scripts/generate_openapi.sh
35+
working-directory: ./python
36+
37+
- name: Run Python tests
38+
run: pytest -sv
39+
working-directory: ./python

python/requirements-dev.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ attrs==21.4.0
1010
# via
1111
# openapi-python-client
1212
# pytest
13+
# pytest-docker
1314
autoflake==1.4
1415
# via openapi-python-client
1516
black==23.3.0
@@ -20,6 +21,9 @@ certifi==2024.7.4
2021
# via
2122
# httpcore
2223
# httpx
24+
# requests
25+
charset-normalizer==3.3.2
26+
# via requests
2327
click==8.0.1
2428
# via
2529
# black
@@ -36,6 +40,7 @@ httpx==0.23.0
3640
idna==3.3
3741
# via
3842
# anyio
43+
# requests
3944
# rfc3986
4045
iniconfig==1.1.1
4146
# via pytest
@@ -77,11 +82,17 @@ pyflakes==2.3.1
7782
pyproject-hooks==1.0.0
7883
# via build
7984
pytest==6.2.4
85+
# via
86+
# -r requirements.in/development.txt
87+
# pytest-docker
88+
pytest-docker==3.1.1
8089
# via -r requirements.in/development.txt
8190
python-dateutil==2.8.2
8291
# via openapi-python-client
8392
pyyaml==6.0.1
8493
# via openapi-python-client
94+
requests==2.32.3
95+
# via -r requirements.in/development.txt
8596
rfc3986[idna2008]==1.5.0
8697
# via httpx
8798
ruff==0.4.8
@@ -103,6 +114,8 @@ typing-extensions==4.6.3
103114
# via
104115
# mypy
105116
# pydantic
117+
urllib3==2.2.3
118+
# via requests
106119
wheel==0.40.0
107120
# via pip-tools
108121

python/requirements.in/development.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ pytest
55
httpx>=0.23.0
66
openapi-python-client>=0.14.1,<0.15 # I think version 0.15 is now dangerous for us? https://github.com/openapi-generators/openapi-python-client/pull/775#issuecomment-1646977834
77
jinja2>=3.1.3
8+
pytest-docker
9+
requests
10+

python/tests/conftest.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import os
2+
import shutil
3+
from subprocess import CalledProcessError, check_output
4+
5+
import pytest
6+
import requests
7+
from requests.adapters import HTTPAdapter
8+
from svix.api import Svix, SvixOptions
9+
from urllib3.util.retry import Retry
10+
11+
SVIX_ORG_ID = "org_svix_python_tests"
12+
13+
14+
def pytest_collection_modifyitems(config, items):
15+
"""Tests require docker compose (v2 or v1) so skip them if it is not
16+
installed on host."""
17+
skipper = None
18+
if shutil.which("docker") is None:
19+
skipper = pytest.mark.skip(reason="skipping test as docker command is missing")
20+
else:
21+
docker_compose_available = False
22+
try:
23+
# check if docker compose v2 if available
24+
check_output(["docker", "compose", "version"])
25+
docker_compose_available = True
26+
except CalledProcessError:
27+
# check if docker compose v1 if available
28+
docker_compose_available = shutil.which("docker-compose") is not None
29+
finally:
30+
if not docker_compose_available:
31+
skipper = pytest.mark.skip(
32+
reason="skipping test as docker compose is missing"
33+
)
34+
if skipper is not None:
35+
for item in items:
36+
item.add_marker(skipper)
37+
38+
39+
@pytest.fixture(scope="session")
40+
def docker_compose_command():
41+
try:
42+
# use docker compose v2 if available
43+
check_output(["docker", "compose", "version"])
44+
return "docker compose"
45+
except Exception:
46+
# fallback on v1 otherwise
47+
return "docker-compose"
48+
49+
50+
@pytest.fixture(scope="session")
51+
def docker_compose_file():
52+
return [
53+
os.path.join(os.path.dirname(__file__), "../../server/docker-compose.yml"),
54+
os.path.join(
55+
os.path.dirname(__file__), "../../server/docker-compose.override.yml"
56+
),
57+
]
58+
59+
60+
@pytest.fixture(scope="session")
61+
def docker_compose(docker_services):
62+
return docker_services._docker_compose
63+
64+
65+
@pytest.fixture(scope="session")
66+
def svix_server_url(docker_services):
67+
# svix server container exposes a free port to the docker host,
68+
# we use the docker network gateway IP in case the tests are also
69+
# executed in a container
70+
svix_server_port = docker_services.port_for("backend", 8071)
71+
return f"http://172.17.0.1:{svix_server_port}"
72+
73+
74+
@pytest.fixture(autouse=True, scope="session")
75+
def svix_server(svix_server_url):
76+
"""Spawn a Svix server for the tests session using docker compose"""
77+
# wait for the svix backend service to be up and responding
78+
request_session = requests.Session()
79+
retries = Retry(total=10, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504])
80+
request_session.mount("http://", HTTPAdapter(max_retries=retries))
81+
api_url = f"{svix_server_url}/api/v1/health/"
82+
response = request_session.get(api_url)
83+
assert response
84+
85+
86+
@pytest.fixture(autouse=True)
87+
def svix_wiper(docker_compose):
88+
"""Ensure stateless tests"""
89+
yield
90+
# wipe svix database after each test to ensure stateless tests
91+
docker_compose.execute(
92+
f"exec -T backend svix-server wipe --yes-i-know-what-im-doing {SVIX_ORG_ID}"
93+
)
94+
95+
96+
@pytest.fixture(scope="session")
97+
def svix_api(svix_server_url, docker_compose):
98+
# generate bearer token to authorize communication with the svix server
99+
exec_output = docker_compose.execute(
100+
f"exec -T backend svix-server jwt generate {SVIX_ORG_ID}"
101+
)
102+
svix_auth_token = (
103+
exec_output.decode()
104+
.replace("Token (Bearer): ", "")
105+
.replace("\r", "")
106+
.replace("\n", "")
107+
)
108+
return Svix(
109+
svix_auth_token,
110+
SvixOptions(server_url=svix_server_url),
111+
)

python/tests/test_client.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import uuid
2+
3+
import pytest
4+
from svix.api import ApplicationIn, ApplicationOut, Svix
5+
6+
7+
def _gen_uuid(name: str) -> str:
8+
return str(uuid.uuid5(uuid.NAMESPACE_DNS, name))
9+
10+
11+
@pytest.fixture
12+
def svix_app_name() -> str:
13+
return "svix_python_tests"
14+
15+
16+
def create_svix_app(
17+
svix_api: Svix, svix_app_name: str, svix_app_uid: str
18+
) -> ApplicationOut:
19+
return svix_api.application.get_or_create(
20+
ApplicationIn(name=svix_app_name, uid=svix_app_uid)
21+
)
22+
23+
24+
def test_svix_application_create(svix_api: Svix, svix_app_name: str):
25+
svix_app_uid = _gen_uuid(svix_app_name)
26+
app = create_svix_app(svix_api, svix_app_name, svix_app_uid)
27+
assert app.name == svix_app_name
28+
assert app.uid == svix_app_uid

0 commit comments

Comments
 (0)