Skip to content

Commit 701e8e5

Browse files
emyllerclaude
andauthored
feat(CI): Set up CodSpeed (#272)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent b1a41c5 commit 701e8e5

7 files changed

Lines changed: 269 additions & 10 deletions

File tree

‎.github/workflows/codspeed.yml‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: CodSpeed
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read # Check out the repository and its submodules
12+
id-token: write # Authenticate result uploads to CodSpeed via OIDC
13+
14+
jobs:
15+
benchmarks:
16+
name: Run benchmarks
17+
runs-on: codspeed-macro
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22+
with:
23+
submodules: recursive
24+
persist-credentials: false
25+
26+
- name: Set up Python
27+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
28+
with:
29+
python-version: '3.13'
30+
31+
- name: Install Poetry
32+
run: pip install poetry
33+
34+
- name: Run benchmarks
35+
uses: CodSpeedHQ/action@78102a468f96d8e0ccb92d0ec49425753fc42062 # v5.6.0
36+
with:
37+
mode: walltime
38+
run: make benchmark

‎.gitmodules‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[submodule "tests/data/sdk-benchmarks"]
2+
path = tests/data/sdk-benchmarks
3+
url = https://github.com/Flagsmith/sdk-benchmarks.git
4+
branch = 0.0.2

‎Makefile‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
SDK_BENCHMARKS := tests/data/sdk-benchmarks
2+
3+
$(SDK_BENCHMARKS)/environments:
4+
git submodule update --init -- $(SDK_BENCHMARKS)
5+
6+
.PHONY: benchmark
7+
benchmark: | $(SDK_BENCHMARKS)/environments
8+
poetry install --with dev,benchmark
9+
SDK_BENCHMARKS_ENVIRONMENTS_DIR=$(SDK_BENCHMARKS)/environments poetry run pytest benchmarks --codspeed

‎benchmarks/test_sdk.py‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""
2+
Benchmarks a locally evaluating SDK client against environment documents tiered by size.
3+
"""
4+
5+
import os
6+
import typing
7+
from pathlib import Path
8+
9+
import pytest
10+
import responses
11+
from flag_engine.engine import ContextValue
12+
13+
from flagsmith import Flagsmith
14+
15+
API_URL = "http://flagsmith.benchmark/api/v1/"
16+
ENVIRONMENTS_DIR = Path(os.environ["SDK_BENCHMARKS_ENVIRONMENTS_DIR"])
17+
IDENTITY_OVERRIDES_IDENTIFIER = "identity-override-1"
18+
IDENTIFIER = "user-1"
19+
TRAITS: typing.Mapping[str, ContextValue] = {
20+
"plan": "plan-7",
21+
"age": 30,
22+
"country": "GB",
23+
"version": "2.5.1",
24+
"email": "jane@example.com",
25+
"user_id": 12,
26+
"beta": True,
27+
"score": 95,
28+
"tier": "pro",
29+
"device": "iPhone 15",
30+
}
31+
32+
33+
@pytest.fixture(scope="module", params=["small", "medium", "large"])
34+
def client(request: pytest.FixtureRequest) -> Flagsmith:
35+
"""
36+
Build a locally evaluating client serving the environment document from memory.
37+
"""
38+
environment_document = (ENVIRONMENTS_DIR / f"{request.param}.json").read_bytes()
39+
40+
with responses.RequestsMock() as mocked_responses:
41+
mocked_responses.get(
42+
f"{API_URL}environment-document/",
43+
body=environment_document,
44+
content_type="application/json",
45+
)
46+
client = Flagsmith(
47+
environment_key="ser.benchmark",
48+
api_url=API_URL,
49+
enable_local_evaluation=True,
50+
)
51+
client.environment_data_polling_manager_thread.stop() # Refreshed once, at startup only
52+
client.environment_data_polling_manager_thread.join()
53+
54+
return client
55+
56+
57+
@pytest.mark.benchmark
58+
def test_get_environment_flags(client: Flagsmith) -> None:
59+
"""
60+
Get a feature value from the environment flags.
61+
"""
62+
client.get_environment_flags().get_feature_value("feature_1")
63+
64+
65+
@pytest.mark.benchmark
66+
def test_get_identity_flags_with_identity_overrides(client: Flagsmith) -> None:
67+
"""
68+
Check a feature for an identity with identity overrides.
69+
"""
70+
client.get_identity_flags(IDENTITY_OVERRIDES_IDENTIFIER).is_feature_enabled(
71+
"feature_8"
72+
)
73+
74+
75+
@pytest.mark.benchmark
76+
def test_get_identity_flags_no_traits(client: Flagsmith) -> None:
77+
"""
78+
Get a feature value for an identity without traits.
79+
"""
80+
client.get_identity_flags(IDENTIFIER).get_feature_value("feature_1")
81+
82+
83+
@pytest.mark.benchmark
84+
def test_get_identity_flags_with_traits(client: Flagsmith) -> None:
85+
"""
86+
Get a feature value for an identity with traits.
87+
"""
88+
client.get_identity_flags(IDENTIFIER, TRAITS).get_feature_value("feature_1")
89+
90+
91+
@pytest.mark.benchmark
92+
def test_get_identity_segments_with_traits(client: Flagsmith) -> None:
93+
"""
94+
List the segments of an identity with traits.
95+
"""
96+
client.get_identity_segments(IDENTIFIER, TRAITS)

‎poetry.lock‎

Lines changed: 112 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pyproject.toml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ responses = "^0.24.1"
3131
types-requests = "^2.32"
3232
pyfakefs = "^5.9.2"
3333

34+
[tool.poetry.group.benchmark]
35+
optional = true
36+
37+
[tool.poetry.group.benchmark.dependencies]
38+
pytest-codspeed = "^5.0.3"
39+
40+
[tool.pytest.ini_options]
41+
testpaths = ["tests"]
42+
3443
[tool.mypy]
3544
exclude = ["example/*"]
3645

‎tests/data/sdk-benchmarks‎

Submodule sdk-benchmarks added at 7da2be4

0 commit comments

Comments
 (0)