strobengine 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- strobengine-0.3.0/.dockerignore +34 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/.github/workflows/ci.yml +4 -0
- strobengine-0.3.0/.github/workflows/docker-release.yml +53 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/.github/workflows/publish-test.yml +18 -3
- {strobengine-0.2.0 → strobengine-0.3.0}/.github/workflows/publish.yml +17 -3
- strobengine-0.3.0/.pre-commit-config.yaml +47 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/CHANGELOG.md +85 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/Cargo.lock +9 -2
- {strobengine-0.2.0 → strobengine-0.3.0}/Cargo.toml +2 -1
- strobengine-0.3.0/Dockerfile +28 -0
- strobengine-0.3.0/Makefile +11 -0
- strobengine-0.2.0/README.md → strobengine-0.3.0/PKG-INFO +58 -20
- strobengine-0.2.0/PKG-INFO → strobengine-0.3.0/README.md +41 -37
- strobengine-0.3.0/docs/docker.md +94 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/docs/roadmap.md +16 -14
- strobengine-0.3.0/docs/testing.md +129 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/examples/http_methods.py +7 -7
- strobengine-0.3.0/examples/load_test.py +27 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/examples/spike_test.py +1 -1
- {strobengine-0.2.0 → strobengine-0.3.0}/examples/stress_test.py +1 -1
- {strobengine-0.2.0 → strobengine-0.3.0}/pyproject.toml +9 -5
- {strobengine-0.2.0 → strobengine-0.3.0}/src/config.rs +8 -0
- strobengine-0.3.0/src/lib.rs +518 -0
- strobengine-0.3.0/src/logging.rs +44 -0
- strobengine-0.3.0/src/metrics.rs +343 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/src/progress.rs +16 -6
- {strobengine-0.2.0 → strobengine-0.3.0}/src/strobengine/_strobengine.pyi +32 -3
- {strobengine-0.2.0 → strobengine-0.3.0}/src/strobengine/cli.py +56 -24
- strobengine-0.3.0/src/strobengine/constants.py +14 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/src/strobengine/engine.py +45 -16
- {strobengine-0.2.0 → strobengine-0.3.0}/src/strobengine/reporter.py +70 -27
- {strobengine-0.2.0 → strobengine-0.3.0}/src/worker.rs +15 -12
- {strobengine-0.2.0 → strobengine-0.3.0}/tests/conftest.py +9 -0
- strobengine-0.3.0/tests/e2e/__init__.py +0 -0
- strobengine-0.3.0/tests/e2e/conftest.py +72 -0
- strobengine-0.3.0/tests/e2e/mock_server.py +69 -0
- strobengine-0.3.0/tests/e2e/test_cli_interface.py +149 -0
- strobengine-0.3.0/tests/e2e/test_http_scenarios.py +84 -0
- strobengine-0.3.0/tests/e2e/test_performance_sanity.py +61 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/tests/test_engine.py +18 -5
- {strobengine-0.2.0 → strobengine-0.3.0}/tests/test_reporter.py +35 -5
- strobengine-0.3.0/uv.lock +799 -0
- strobengine-0.2.0/examples/load_test.py +0 -14
- strobengine-0.2.0/src/lib.rs +0 -425
- strobengine-0.2.0/src/logging.rs +0 -34
- strobengine-0.2.0/src/metrics.rs +0 -143
- strobengine-0.2.0/uv.lock +0 -207
- {strobengine-0.2.0 → strobengine-0.3.0}/.gitignore +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/.python-version +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/LICENSE +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/cliff.toml +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/docs/benchmarks.md +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/docs/http_methods.md +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/docs/infrastructure.md +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/src/chaos.rs +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/src/strobengine/__init__.py +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/src/strobengine/py.typed +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/tests/__init__.py +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/tests/test_cli.py +0 -0
- {strobengine-0.2.0 → strobengine-0.3.0}/tests/test_logging.py +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Git metadata
|
|
2
|
+
.git/
|
|
3
|
+
.gitignore
|
|
4
|
+
|
|
5
|
+
# Rust build artifacts & cargo cache
|
|
6
|
+
target/
|
|
7
|
+
**/*.rs.bk
|
|
8
|
+
|
|
9
|
+
# Python cache & virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
__pycache__/
|
|
14
|
+
*.pyc
|
|
15
|
+
*.pyo
|
|
16
|
+
*.pyd
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
|
|
21
|
+
# IDE and OS metadata
|
|
22
|
+
.idea/
|
|
23
|
+
.vscode/
|
|
24
|
+
*.swp
|
|
25
|
+
.DS_Store
|
|
26
|
+
|
|
27
|
+
# Test & benchmark output artifacts
|
|
28
|
+
results/
|
|
29
|
+
dist/
|
|
30
|
+
*.egg-info/
|
|
31
|
+
|
|
32
|
+
# Docker files
|
|
33
|
+
Dockerfile
|
|
34
|
+
.dockerignore
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Build & Push Docker Image
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
release:
|
|
6
|
+
types: [published]
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
- 'v*.*.*'
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
docker-publish:
|
|
14
|
+
name: Build and Push Docker Image
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout Code
|
|
19
|
+
uses: actions/checkout@v7
|
|
20
|
+
|
|
21
|
+
- name: Set up QEMU (for multi-arch support)
|
|
22
|
+
uses: docker/setup-qemu-action@v4
|
|
23
|
+
|
|
24
|
+
- name: Set up Docker Buildx
|
|
25
|
+
uses: docker/setup-buildx-action@v4
|
|
26
|
+
|
|
27
|
+
- name: Log in to Docker Hub
|
|
28
|
+
uses: docker/login-action@v4
|
|
29
|
+
with:
|
|
30
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
31
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
32
|
+
|
|
33
|
+
- name: Extract Docker Metadata (Tags & Labels)
|
|
34
|
+
id: meta
|
|
35
|
+
uses: docker/metadata-action@v6
|
|
36
|
+
with:
|
|
37
|
+
images: ${{ secrets.DOCKERHUB_USERNAME }}/strobengine
|
|
38
|
+
tags: |
|
|
39
|
+
type=semver,pattern={{version}}
|
|
40
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
41
|
+
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
|
|
42
|
+
|
|
43
|
+
- name: Build and Push Docker Image
|
|
44
|
+
uses: docker/build-push-action@v7
|
|
45
|
+
with:
|
|
46
|
+
context: .
|
|
47
|
+
file: ./Dockerfile
|
|
48
|
+
push: true
|
|
49
|
+
platforms: linux/amd64,linux/arm64
|
|
50
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
51
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
52
|
+
cache-from: type=gha
|
|
53
|
+
cache-to: type=gha,mode=max
|
|
@@ -6,11 +6,24 @@ on:
|
|
|
6
6
|
|
|
7
7
|
jobs:
|
|
8
8
|
build-wheels:
|
|
9
|
-
name: Build wheels on ${{ matrix.os }}
|
|
9
|
+
name: Build wheels on ${{ matrix.os }} (${{ matrix.target || 'native' }})
|
|
10
10
|
runs-on: ${{ matrix.os }}
|
|
11
11
|
strategy:
|
|
12
|
+
fail-fast: false
|
|
12
13
|
matrix:
|
|
13
|
-
|
|
14
|
+
include:
|
|
15
|
+
# Linux
|
|
16
|
+
- os: ubuntu-latest
|
|
17
|
+
target: x86_64
|
|
18
|
+
# Windows
|
|
19
|
+
- os: windows-latest
|
|
20
|
+
target: x64
|
|
21
|
+
# macOS Apple Silicon (ARM64)
|
|
22
|
+
- os: macos-latest
|
|
23
|
+
target: aarch64
|
|
24
|
+
# macOS Intel (x86_64) cross-compiled on macos-latest
|
|
25
|
+
- os: macos-latest
|
|
26
|
+
target: x86_64
|
|
14
27
|
steps:
|
|
15
28
|
- uses: actions/checkout@v7
|
|
16
29
|
|
|
@@ -22,6 +35,7 @@ jobs:
|
|
|
22
35
|
- name: Build wheels via Maturin
|
|
23
36
|
uses: PyO3/maturin-action@v1
|
|
24
37
|
with:
|
|
38
|
+
target: ${{ matrix.target }}
|
|
25
39
|
args: --release --out dist --find-interpreter
|
|
26
40
|
sccache: 'true'
|
|
27
41
|
sdist: false
|
|
@@ -29,7 +43,7 @@ jobs:
|
|
|
29
43
|
- name: Upload wheel artifacts
|
|
30
44
|
uses: actions/upload-artifact@v7
|
|
31
45
|
with:
|
|
32
|
-
name: wheels-${{ matrix.os }}
|
|
46
|
+
name: wheels-${{ matrix.os }}-${{ matrix.target }}
|
|
33
47
|
path: dist/*.whl
|
|
34
48
|
|
|
35
49
|
build-sdist:
|
|
@@ -71,3 +85,4 @@ jobs:
|
|
|
71
85
|
with:
|
|
72
86
|
repository-url: https://test.pypi.org/legacy/
|
|
73
87
|
packages-dir: dist/
|
|
88
|
+
skip-existing: true # Skips 400 errors for already uploaded files
|
|
@@ -9,11 +9,24 @@ on:
|
|
|
9
9
|
|
|
10
10
|
jobs:
|
|
11
11
|
build-wheels:
|
|
12
|
-
name: Build wheels on ${{ matrix.os }}
|
|
12
|
+
name: Build wheels on ${{ matrix.os }} (${{ matrix.target || 'native' }})
|
|
13
13
|
runs-on: ${{ matrix.os }}
|
|
14
14
|
strategy:
|
|
15
|
+
fail-fast: false
|
|
15
16
|
matrix:
|
|
16
|
-
|
|
17
|
+
include:
|
|
18
|
+
# Linux
|
|
19
|
+
- os: ubuntu-latest
|
|
20
|
+
target: x86_64
|
|
21
|
+
# Windows
|
|
22
|
+
- os: windows-latest
|
|
23
|
+
target: x64
|
|
24
|
+
# macOS Apple Silicon (ARM64)
|
|
25
|
+
- os: macos-latest
|
|
26
|
+
target: aarch64
|
|
27
|
+
# macOS Intel (x86_64) cross-compiled on macos-latest
|
|
28
|
+
- os: macos-latest
|
|
29
|
+
target: x86_64
|
|
17
30
|
steps:
|
|
18
31
|
- uses: actions/checkout@v7
|
|
19
32
|
|
|
@@ -25,6 +38,7 @@ jobs:
|
|
|
25
38
|
- name: Build wheels via Maturin
|
|
26
39
|
uses: PyO3/maturin-action@v1
|
|
27
40
|
with:
|
|
41
|
+
target: ${{ matrix.target }}
|
|
28
42
|
args: --release --out dist --find-interpreter
|
|
29
43
|
sccache: 'true'
|
|
30
44
|
sdist: false
|
|
@@ -32,7 +46,7 @@ jobs:
|
|
|
32
46
|
- name: Upload wheel artifacts
|
|
33
47
|
uses: actions/upload-artifact@v7
|
|
34
48
|
with:
|
|
35
|
-
name: wheels-${{ matrix.os }}
|
|
49
|
+
name: wheels-${{ matrix.os }}-${{ matrix.target }}
|
|
36
50
|
path: dist/*.whl
|
|
37
51
|
|
|
38
52
|
build-sdist:
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
default_stages: [pre-push]
|
|
3
|
+
|
|
4
|
+
repos:
|
|
5
|
+
- repo: local
|
|
6
|
+
hooks:
|
|
7
|
+
- id: cargo-fmt
|
|
8
|
+
name: cargo fmt
|
|
9
|
+
entry: cargo fmt --check
|
|
10
|
+
language: system
|
|
11
|
+
types: [rust]
|
|
12
|
+
pass_filenames: false
|
|
13
|
+
|
|
14
|
+
- id: cargo-clippy
|
|
15
|
+
name: cargo clippy
|
|
16
|
+
entry: cargo clippy --all-targets --all-features -- -D warnings
|
|
17
|
+
language: system
|
|
18
|
+
types: [rust]
|
|
19
|
+
pass_filenames: false
|
|
20
|
+
|
|
21
|
+
- id: cargo-test
|
|
22
|
+
name: cargo test
|
|
23
|
+
entry: cargo test
|
|
24
|
+
language: system
|
|
25
|
+
types: [rust]
|
|
26
|
+
pass_filenames: false
|
|
27
|
+
|
|
28
|
+
- id: ruff-check
|
|
29
|
+
name: ruff check
|
|
30
|
+
entry: uv run ruff check .
|
|
31
|
+
language: system
|
|
32
|
+
types: [python]
|
|
33
|
+
pass_filenames: false
|
|
34
|
+
|
|
35
|
+
- id: ruff-format
|
|
36
|
+
name: ruff format
|
|
37
|
+
entry: uv run ruff format --check .
|
|
38
|
+
language: system
|
|
39
|
+
types: [python]
|
|
40
|
+
pass_filenames: false
|
|
41
|
+
|
|
42
|
+
- id: pytest
|
|
43
|
+
name: pytest
|
|
44
|
+
entry: uv run pytest -v
|
|
45
|
+
language: system
|
|
46
|
+
types: [python]
|
|
47
|
+
pass_filenames: false
|
|
@@ -4,6 +4,88 @@ All notable changes to `strobengine` will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.0] - 2026-08-13
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- *(logging)* Log warning on invalid EnvFilter string (#59)
|
|
12
|
+
- *(logging)* Warn when log file creation fails (#60)
|
|
13
|
+
- *(cli)* Auto-detect value-taking flags in positional arg detection (#61)
|
|
14
|
+
- *(cli)* Map trace log level to custom level 5 instead of DEBUG (#63)
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
- *(readme)* Add custom HTTP headers usage examples (#55)
|
|
19
|
+
- Add --form flag to README CLI reference
|
|
20
|
+
- Update roadmap to reflect recent project changes (#62)
|
|
21
|
+
- *(worker)* Document u64::MAX fallback for extreme latencies (#66)
|
|
22
|
+
- Consolidate testing guide into docs/testing.md
|
|
23
|
+
- *(docker)* Add Docker usage documentation
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
- *(rust)* Add form payload support to TestConfig and Content-Type logic
|
|
28
|
+
- *(python)* Add --form flag and type stubs
|
|
29
|
+
- *(cli)* Add --form payload support for URL-encoded request bodies (#56)
|
|
30
|
+
- *(metrics)* Add latency distribution, status code aggregation, and structured JSON repor (#72)
|
|
31
|
+
|
|
32
|
+
### Miscellaneous Tasks
|
|
33
|
+
|
|
34
|
+
- *(dev)* Setup pre-commit hooks and Makefile for local pre-push checks (#58)
|
|
35
|
+
- *(github)* Add Docker build and push workflow for releases
|
|
36
|
+
|
|
37
|
+
### Other
|
|
38
|
+
|
|
39
|
+
- *(deps)* Add urlencoding crate for form payload encoding
|
|
40
|
+
- *(deps)* Add aiohttp dev dependency for e2e test server
|
|
41
|
+
- *(docker)* Add multi-stage Dockerfile for Maturin package
|
|
42
|
+
- *(docker)* Add .dockerignore to optimize build context
|
|
43
|
+
- *(docker)* Switch runtime container execution to non-root user
|
|
44
|
+
|
|
45
|
+
### Refactoring
|
|
46
|
+
|
|
47
|
+
- *(engine)* Unify load test execution via ConcurrencyStrategy (#54)
|
|
48
|
+
- *(worker)* Remove unused status_code and is_error from RequestMetric (#57)
|
|
49
|
+
- *(progress)* Remove unused `_total_duration` param from `create_progress_bar` (#64)
|
|
50
|
+
|
|
51
|
+
### Testing
|
|
52
|
+
|
|
53
|
+
- Remove trivial allocator unit test (#65)
|
|
54
|
+
- *(e2e)* Add async mock server with status, delay, echo, and flaky endpoints
|
|
55
|
+
- Add e2e mock server (#67)
|
|
56
|
+
- *(e2e)* Add `cli_bin` fixture to detect strobengine CLI path (#68)
|
|
57
|
+
- *(e2e)* Harden mock server with AppKey types and header normalization
|
|
58
|
+
- *(e2e)* Switch mock_server fixture to threaded AppRunner
|
|
59
|
+
- *(e2e)* Add HTTP scenario tests against mock server
|
|
60
|
+
- *(e2e)* Harden mock server and consolidate testing docs
|
|
61
|
+
- *(e2e)* Add subprocess-based CLI interface tests (#70)
|
|
62
|
+
- *(e2e)* Configure --e2e pytest flag and CI pipeline step (#71)
|
|
63
|
+
## [0.2.1] - 2026-08-03
|
|
64
|
+
|
|
65
|
+
### Bug Fixes
|
|
66
|
+
|
|
67
|
+
- *(types)* Correct headers type in _strobengine.pyi from dict to list of tuples (#41)
|
|
68
|
+
- Replace build_client panic with proper error propagation (#42)
|
|
69
|
+
- Replace std::process::exit with safe PyKeyboardInterrupt on SIGINT (#43)
|
|
70
|
+
- *(progress)* Guard against zero total_duration in progress bar calculation (#44)
|
|
71
|
+
- Improve worker shutdown and panic handling (#45)
|
|
72
|
+
- *(metrics)* Normalize counter types to AtomicU64 for 32-bit safety (#46)
|
|
73
|
+
- *(cli)* Add type annotation to _output_results summary parameter (#48)
|
|
74
|
+
|
|
75
|
+
### Documentation
|
|
76
|
+
|
|
77
|
+
- Add comment explaining SystemExit re-raise pattern (#50)
|
|
78
|
+
|
|
79
|
+
### Miscellaneous Tasks
|
|
80
|
+
|
|
81
|
+
- Remove unnecessary clippy allow on run_load_test (#47)
|
|
82
|
+
- Add multi-architecture matrix for macOS wheel builds (#52)
|
|
83
|
+
- Prepare for release 0.2.1
|
|
84
|
+
|
|
85
|
+
### Refactoring
|
|
86
|
+
|
|
87
|
+
- Extract magic number 8192 to named constant METRIC_CHANNEL_BUFFER (#49)
|
|
88
|
+
- Extract magic numbers to named constants across Rust and Python (#51)
|
|
7
89
|
## [0.2.0] - 2026-08-02
|
|
8
90
|
|
|
9
91
|
### Bug Fixes
|
|
@@ -20,6 +102,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
20
102
|
- Update roadmap to reflect changes (#34)
|
|
21
103
|
- Add HTTP method, body, and header documentation
|
|
22
104
|
- Add code examples (#37)
|
|
105
|
+
- *(changelog)* Update CHANGELOG.md for v0.2.0 release
|
|
23
106
|
|
|
24
107
|
### Features
|
|
25
108
|
|
|
@@ -43,6 +126,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
43
126
|
|
|
44
127
|
- *(release)* Prepare v0.1.0 changelog and tag
|
|
45
128
|
- Add PyPI publish workflow with tag-based triggering (#26)
|
|
129
|
+
- *(pyproject)* Add license metadata and project URLs, bump version
|
|
130
|
+
- *(release)* Merge v0.2.0 release prep into main (#40)
|
|
46
131
|
|
|
47
132
|
### Performance
|
|
48
133
|
|
|
@@ -210,7 +210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
210
210
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
|
211
211
|
dependencies = [
|
|
212
212
|
"libc",
|
|
213
|
-
"windows-sys 0.
|
|
213
|
+
"windows-sys 0.61.2",
|
|
214
214
|
]
|
|
215
215
|
|
|
216
216
|
[[package]]
|
|
@@ -1273,7 +1273,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
|
|
1273
1273
|
|
|
1274
1274
|
[[package]]
|
|
1275
1275
|
name = "strobengine"
|
|
1276
|
-
version = "0.
|
|
1276
|
+
version = "0.3.0"
|
|
1277
1277
|
dependencies = [
|
|
1278
1278
|
"bytes",
|
|
1279
1279
|
"fastrand",
|
|
@@ -1286,6 +1286,7 @@ dependencies = [
|
|
|
1286
1286
|
"tokio-util",
|
|
1287
1287
|
"tracing",
|
|
1288
1288
|
"tracing-subscriber",
|
|
1289
|
+
"urlencoding",
|
|
1289
1290
|
]
|
|
1290
1291
|
|
|
1291
1292
|
[[package]]
|
|
@@ -1616,6 +1617,12 @@ dependencies = [
|
|
|
1616
1617
|
"serde",
|
|
1617
1618
|
]
|
|
1618
1619
|
|
|
1620
|
+
[[package]]
|
|
1621
|
+
name = "urlencoding"
|
|
1622
|
+
version = "2.1.3"
|
|
1623
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1624
|
+
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
|
1625
|
+
|
|
1619
1626
|
[[package]]
|
|
1620
1627
|
name = "utf8_iter"
|
|
1621
1628
|
version = "1.0.4"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "strobengine"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
|
|
@@ -23,5 +23,6 @@ tokio-util = { version = "0.7", features = ["rt"] }
|
|
|
23
23
|
tracing = "0.1"
|
|
24
24
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "registry"] }
|
|
25
25
|
mimalloc = "0.1.52"
|
|
26
|
+
urlencoding = "2"
|
|
26
27
|
fastrand = "2.5"
|
|
27
28
|
indicatif = "0.18.6"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Stage 1: Build wheel
|
|
2
|
+
FROM python:3.11-slim AS builder
|
|
3
|
+
RUN apt-get update && apt-get install -y curl build-essential
|
|
4
|
+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
5
|
+
ENV PATH="/root/.cargo/bin:${PATH}"
|
|
6
|
+
RUN pip install maturin
|
|
7
|
+
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
COPY . .
|
|
10
|
+
RUN maturin build --release --out dist
|
|
11
|
+
|
|
12
|
+
# Stage 2: Minimal Runtime Image
|
|
13
|
+
FROM python:3.11-slim
|
|
14
|
+
|
|
15
|
+
# Create a non-privileged system user and group
|
|
16
|
+
RUN groupadd -r appuser && useradd -r -g appuser appuser
|
|
17
|
+
|
|
18
|
+
WORKDIR /app
|
|
19
|
+
|
|
20
|
+
# Copy built wheels with proper ownership
|
|
21
|
+
COPY --from=builder /app/dist/*.whl .
|
|
22
|
+
RUN pip install --no-cache-dir *.whl && rm *.whl
|
|
23
|
+
|
|
24
|
+
# Switch execution context away from root
|
|
25
|
+
USER appuser
|
|
26
|
+
|
|
27
|
+
ENTRYPOINT ["strobengine"]
|
|
28
|
+
CMD ["--help"]
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strobengine
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Requires-Dist: rich>=13.0.0
|
|
5
|
+
Requires-Dist: typer>=0.27.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Summary: A load testing framework with a Python UI and a bare-metal Rust engine core.
|
|
8
|
+
Author-email: riccione <28346232+riccione@users.noreply.github.com>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
12
|
+
Project-URL: Changelog, https://github.com/strobeops/strobengine/blob/main/CHANGELOG.md
|
|
13
|
+
Project-URL: Documentation, https://github.com/strobeops/strobengine
|
|
14
|
+
Project-URL: Homepage, https://github.com/strobeops/strobengine
|
|
15
|
+
Project-URL: Source, https://github.com/strobeops/strobengine
|
|
16
|
+
|
|
1
17
|
# strobengine
|
|
2
18
|
|
|
3
19
|
A high-performance HTTP load testing engine with a Python API and a bare-metal Rust core.
|
|
@@ -36,6 +52,18 @@ uv sync
|
|
|
36
52
|
|
|
37
53
|
`uv sync` invokes [maturin](https://github.com/PyO3/maturin) under the hood, which compiles the Rust code into a native Python extension module and installs it into your virtual environment.
|
|
38
54
|
|
|
55
|
+
### Docker
|
|
56
|
+
|
|
57
|
+
Alternatively, pull and run strobengine directly from Docker Hub:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
docker pull strobeops/strobengine:latest
|
|
61
|
+
docker run --rm -it strobeops/strobengine load http://host.docker.internal:8080/api/health -c 50 -d 30
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
> See [docs/docker.md](docs/docker.md) for full Docker documentation including
|
|
65
|
+
> version tags, host networking, and building locally.
|
|
66
|
+
|
|
39
67
|
## Quick Start Usage
|
|
40
68
|
|
|
41
69
|
```python
|
|
@@ -46,27 +74,29 @@ from strobengine.reporter import print_summary
|
|
|
46
74
|
engine = StrobEngine(url="http://localhost:8080/api/health", concurrency=50, duration=30)
|
|
47
75
|
summary = engine.run()
|
|
48
76
|
|
|
49
|
-
print_summary(summary
|
|
77
|
+
print_summary(summary)
|
|
50
78
|
|
|
51
79
|
# Ramp/stress test (10 -> 200 workers over 60s, hold 30s)
|
|
52
80
|
engine = StrobEngine.stress_test(
|
|
53
81
|
"http://localhost:8080/api/health",
|
|
54
82
|
start_concurrency=10, max_concurrency=200,
|
|
55
83
|
ramp_duration=60, hold_duration=30,
|
|
84
|
+
headers=[("Authorization", "Bearer token123")],
|
|
56
85
|
)
|
|
57
86
|
summary = engine.run()
|
|
58
87
|
|
|
59
|
-
print_summary(summary
|
|
88
|
+
print_summary(summary)
|
|
60
89
|
|
|
61
90
|
# Spike test (baseline 5 -> peak 500 -> back to 5)
|
|
62
91
|
engine = StrobEngine.spike_test(
|
|
63
92
|
"http://localhost:8080/api/health",
|
|
64
93
|
baseline=5, peak_concurrency=500,
|
|
65
94
|
pre_spike_duration=5, spike_duration=10, post_spike_duration=5,
|
|
95
|
+
headers=[("X-Custom", "value")],
|
|
66
96
|
)
|
|
67
97
|
summary = engine.run()
|
|
68
98
|
|
|
69
|
-
print_summary(summary
|
|
99
|
+
print_summary(summary)
|
|
70
100
|
|
|
71
101
|
# POST request with JSON body and custom headers
|
|
72
102
|
engine = StrobEngine(
|
|
@@ -109,6 +139,16 @@ strobengine load http://localhost:8080/api/data --method POST --body '{"key": "v
|
|
|
109
139
|
strobengine load http://localhost:8080/api/resource/1 \
|
|
110
140
|
--method PUT --body '{"name": "updated"}' --header "Authorization: Bearer token"
|
|
111
141
|
|
|
142
|
+
# Multiple headers (repeatable -H flag)
|
|
143
|
+
strobengine load http://localhost:8080/api/data \
|
|
144
|
+
--method POST --body '{"key": "val"}' \
|
|
145
|
+
--header "Authorization: Bearer token" \
|
|
146
|
+
--header "X-Request-ID: abc-123"
|
|
147
|
+
|
|
148
|
+
# POST with URL-encoded form data
|
|
149
|
+
strobengine load http://localhost:8080/api/data \
|
|
150
|
+
--method POST --form "key1=value1&key2=value2"
|
|
151
|
+
|
|
112
152
|
# DELETE
|
|
113
153
|
strobengine load http://localhost:8080/api/resource/1 --method DELETE
|
|
114
154
|
|
|
@@ -119,7 +159,7 @@ strobengine load http://localhost:8080/api/health -vv
|
|
|
119
159
|
strobengine load http://localhost:8080/api/health -q
|
|
120
160
|
```
|
|
121
161
|
|
|
122
|
-
By default, this spawns **10 concurrent workers** for **10 seconds** with a **10-second request timeout**. Results are displayed as a formatted table with total requests, errors, requests/sec, and latency percentiles (avg, p95, p99).
|
|
162
|
+
By default, this spawns **10 concurrent workers** for **10 seconds** with a **10-second request timeout**. Results are displayed as a formatted table with total requests, errors, requests/sec, and latency percentiles (min, avg, p50, p90, p95, p99, max).
|
|
123
163
|
|
|
124
164
|
### Subcommands
|
|
125
165
|
|
|
@@ -138,6 +178,7 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
138
178
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
139
179
|
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
140
180
|
| `--body` | none | Request body (raw string) |
|
|
181
|
+
| `--form` | none | Form data body (e.g. key1=val1&key2=val2) |
|
|
141
182
|
| `--header` | none | Custom header key:value (repeatable) |
|
|
142
183
|
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
143
184
|
| `--no-progress` | off | Suppress live progress bar |
|
|
@@ -157,6 +198,7 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
157
198
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
158
199
|
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
159
200
|
| `--body` | none | Request body (raw string) |
|
|
201
|
+
| `--form` | none | Form data body (e.g. key1=val1&key2=val2) |
|
|
160
202
|
| `--header` | none | Custom header key:value (repeatable) |
|
|
161
203
|
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
162
204
|
| `--no-progress` | off | Suppress live progress bar |
|
|
@@ -177,6 +219,7 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
177
219
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
178
220
|
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
179
221
|
| `--body` | none | Request body (raw string) |
|
|
222
|
+
| `--form` | none | Form data body (e.g. key1=val1&key2=val2) |
|
|
180
223
|
| `--header` | none | Custom header key:value (repeatable) |
|
|
181
224
|
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
182
225
|
| `--no-progress` | off | Suppress live progress bar |
|
|
@@ -230,7 +273,7 @@ strobengine separates configuration, execution, and metrics into clean Rust modu
|
|
|
230
273
|
|
|
231
274
|
- **`config`** -- `TestConfig` for static load, `LoadProfile` enum for dynamic profiles (Constant, Ramp, Spike) with target concurrency interpolation.
|
|
232
275
|
- **`worker`** -- Async worker loops with method-aware request building, static payload reuse (Bytes), and zero-allocation header management via `ClientBuilder::default_headers()`.
|
|
233
|
-
- **`metrics`** -- Lock-free atomic counters (`AtomicUsize`) track total requests and errors without contention. An aggregator task collects raw latencies, then `calculate_summary` computes average, p95, and
|
|
276
|
+
- **`metrics`** -- Lock-free atomic counters (`AtomicUsize`) track total requests and errors without contention. An aggregator task collects raw latencies, then `calculate_summary` computes min, average, p50, p90, p95, p99, and max percentiles in Rust at bare-metal speed.
|
|
234
277
|
- **`chaos`** -- Protocol-agnostic fault injection engine with `ChaosEngine` evaluator and `ChaosFault` enum (LatencySpike, CorruptedPayload, MetadataCorruption, ConnectionDrop).
|
|
235
278
|
- **`progress`** -- Background Tokio render task sampling atomic metrics every 200ms, displaying live RPS, active VUs, and latency via indicatif.
|
|
236
279
|
- **Orchestrator** -- Supervisor task ticks every 200ms, calculates target concurrency from the active profile curve, spawns/aborts workers dynamically.
|
|
@@ -239,27 +282,21 @@ The Python GIL is released entirely via `py.detach()` during test execution, all
|
|
|
239
282
|
|
|
240
283
|
## Testing
|
|
241
284
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
```bash
|
|
245
|
-
# Rust unit tests
|
|
246
|
-
cargo test
|
|
247
|
-
|
|
248
|
-
# Python unit tests
|
|
249
|
-
uv run pytest -v
|
|
250
|
-
```
|
|
285
|
+
> See [docs/testing.md](docs/testing.md) for the full testing guide, including
|
|
286
|
+
> e2e tests, CI/CD checks, and running individual test suites.
|
|
251
287
|
|
|
252
288
|
## How to Contribute
|
|
253
289
|
|
|
254
290
|
1. Fork the repository
|
|
255
291
|
2. Create a feature branch (`git checkout -b feat/my-feature`)
|
|
256
|
-
3.
|
|
257
|
-
|
|
292
|
+
3. Install dependencies and set up pre-push Git hooks:
|
|
293
|
+
```bash
|
|
294
|
+
uv sync
|
|
295
|
+
uv run pre-commit install --hook-type pre-push
|
|
296
|
+
```
|
|
297
|
+
4. Run formatting, linting, and tests manually (or let git push run them automatically):
|
|
258
298
|
```bash
|
|
259
|
-
|
|
260
|
-
cargo clippy --all-targets -- -D warnings
|
|
261
|
-
cargo test
|
|
262
|
-
uv run pytest -v
|
|
299
|
+
make check
|
|
263
300
|
```
|
|
264
301
|
5. Commit your changes following [Conventional Commits](https://www.conventionalcommits.org/)
|
|
265
302
|
6. Push to your branch and open a Pull Request
|
|
@@ -267,3 +304,4 @@ uv run pytest -v
|
|
|
267
304
|
## License
|
|
268
305
|
|
|
269
306
|
This project is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full text.
|
|
307
|
+
|