Skip to content

Commit 6b365d0

Browse files
Add CodSpeed benchmark integration (#657)
Co-authored-by: codspeed-hq[bot] <117304815+codspeed-hq[bot]@users.noreply.github.com>
1 parent 1f6c455 commit 6b365d0

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

.github/workflows/codspeed.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CodSpeed Benchmarks
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
pull_request:
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
id-token: write
13+
14+
jobs:
15+
benchmarks:
16+
name: Run benchmarks
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- uses: actions/checkout@v6
21+
22+
- uses: actions/setup-python@v6
23+
with:
24+
python-version: "3.13"
25+
26+
- name: Install uv
27+
uses: astral-sh/setup-uv@v7
28+
29+
- name: Install dependencies
30+
run: uv pip install --system -e ".[test]" pytest-codspeed==4.3.0
31+
32+
- name: Run the benchmarks
33+
uses: CodSpeedHQ/action@v4
34+
with:
35+
mode: simulation
36+
run: pytest tests/benchmarks/ --codspeed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
<a href="https://pypi.org/project/ravyn" target="_blank">
1919
<img src="https://img.shields.io/pypi/pyversions/ravyn.svg?color=%2334D058" alt="Supported Python versions">
2020
</a>
21+
22+
<a href="https://codspeed.io/dymmond/ravyn?utm_source=badge" target="_blank">
23+
<img src="https://img.shields.io/endpoint?url=https://codspeed.io/badge/dymmond/ravyn" alt="CodSpeed">
24+
</a>
2125
</p>
2226

2327
---

tests/benchmarks/__init__.py

Whitespace-only changes.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Performance benchmarks for the Ravyn framework."""
2+
3+
import pytest
4+
from lilya import status
5+
from pydantic import BaseModel
6+
7+
from ravyn.routing.gateways import Gateway
8+
from ravyn.routing.handlers import get, post
9+
from ravyn.testclient import create_client
10+
11+
# ---------------------------------------------------------------------------
12+
# Handlers used across benchmarks
13+
# ---------------------------------------------------------------------------
14+
15+
16+
@get("/")
17+
def homepage() -> dict:
18+
return {"message": "welcome"}
19+
20+
21+
@get("/hello/{name}")
22+
def greet(name: str) -> dict:
23+
return {"message": f"Hello, {name}!"}
24+
25+
26+
class Item(BaseModel):
27+
name: str
28+
price: float
29+
quantity: int = 1
30+
31+
32+
@post("/items")
33+
def create_item(data: Item) -> dict:
34+
return {"name": data.name, "total": data.price * data.quantity}
35+
36+
37+
@get("/users/{user_id}")
38+
def get_user(user_id: int) -> dict:
39+
return {"id": user_id, "name": "test-user"}
40+
41+
42+
# ---------------------------------------------------------------------------
43+
# Benchmarks
44+
# ---------------------------------------------------------------------------
45+
46+
47+
@pytest.mark.benchmark
48+
def test_bench_app_creation():
49+
"""Measure the cost of creating a Ravyn application with routes."""
50+
client = create_client(
51+
routes=[
52+
Gateway(handler=homepage),
53+
Gateway(handler=greet),
54+
Gateway(handler=create_item),
55+
Gateway(handler=get_user),
56+
],
57+
enable_openapi=False,
58+
)
59+
# Ensure the app is valid
60+
assert client.app is not None
61+
62+
63+
@pytest.mark.benchmark
64+
def test_bench_simple_get_request():
65+
"""Measure a plain GET request through the full stack."""
66+
with create_client(
67+
routes=[Gateway(handler=homepage)],
68+
enable_openapi=False,
69+
) as client:
70+
response = client.get("/")
71+
assert response.status_code == status.HTTP_200_OK
72+
73+
74+
@pytest.mark.benchmark
75+
def test_bench_parametrized_get_request():
76+
"""Measure a GET request with a path parameter."""
77+
with create_client(
78+
routes=[Gateway(handler=greet)],
79+
enable_openapi=False,
80+
) as client:
81+
response = client.get("/hello/world")
82+
assert response.status_code == status.HTTP_200_OK
83+
84+
85+
@pytest.mark.benchmark
86+
def test_bench_post_request_with_validation():
87+
"""Measure a POST request with Pydantic model validation."""
88+
with create_client(
89+
routes=[Gateway(handler=create_item)],
90+
enable_openapi=False,
91+
) as client:
92+
response = client.post(
93+
"/items",
94+
json={"name": "widget", "price": 9.99, "quantity": 3},
95+
)
96+
assert response.status_code == 201
97+
98+
99+
@pytest.mark.benchmark
100+
def test_bench_multiple_routes():
101+
"""Measure routing resolution across several registered routes."""
102+
with create_client(
103+
routes=[
104+
Gateway(handler=homepage),
105+
Gateway(handler=greet),
106+
Gateway(handler=create_item),
107+
Gateway(handler=get_user),
108+
],
109+
enable_openapi=False,
110+
) as client:
111+
response = client.get("/users/42")
112+
assert response.status_code == status.HTTP_200_OK
113+
assert response.json() == {"id": 42, "name": "test-user"}

0 commit comments

Comments
 (0)