strobengine 0.1.0__tar.gz → 0.2.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.2.0/CHANGELOG.md +103 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/Cargo.lock +106 -1
- {strobengine-0.1.0 → strobengine-0.2.0}/Cargo.toml +7 -3
- strobengine-0.1.0/README.md → strobengine-0.2.0/PKG-INFO +106 -7
- strobengine-0.1.0/PKG-INFO → strobengine-0.2.0/README.md +89 -19
- strobengine-0.2.0/docs/benchmarks.md +213 -0
- strobengine-0.2.0/docs/http_methods.md +165 -0
- strobengine-0.2.0/docs/infrastructure.md +238 -0
- strobengine-0.2.0/docs/roadmap.md +89 -0
- strobengine-0.2.0/examples/http_methods.py +67 -0
- strobengine-0.2.0/examples/load_test.py +14 -0
- strobengine-0.2.0/examples/spike_test.py +16 -0
- strobengine-0.2.0/examples/stress_test.py +15 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/pyproject.toml +11 -1
- strobengine-0.2.0/src/chaos.rs +34 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/src/config.rs +97 -16
- strobengine-0.2.0/src/lib.rs +425 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/src/metrics.rs +9 -1
- strobengine-0.2.0/src/progress.rs +95 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/__init__.py +2 -1
- {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/_strobengine.pyi +21 -1
- {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/cli.py +163 -22
- {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/engine.py +56 -16
- {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/reporter.py +14 -0
- strobengine-0.2.0/src/worker.rs +162 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/tests/conftest.py +3 -1
- {strobengine-0.1.0 → strobengine-0.2.0}/tests/test_cli.py +8 -5
- {strobengine-0.1.0 → strobengine-0.2.0}/tests/test_engine.py +11 -5
- {strobengine-0.1.0 → strobengine-0.2.0}/uv.lock +4 -4
- strobengine-0.1.0/CHANGELOG.md +0 -25
- strobengine-0.1.0/src/lib.rs +0 -239
- strobengine-0.1.0/src/worker.rs +0 -58
- {strobengine-0.1.0 → strobengine-0.2.0}/.github/workflows/ci.yml +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/.github/workflows/publish-test.yml +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/.github/workflows/publish.yml +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/.gitignore +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/.python-version +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/LICENSE +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/cliff.toml +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/src/logging.rs +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/py.typed +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/tests/__init__.py +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/tests/test_logging.py +0 -0
- {strobengine-0.1.0 → strobengine-0.2.0}/tests/test_reporter.py +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `strobengine` will be documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.2.0] - 2026-08-02
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- *(progress)* Downgrade expected HTTP errors from warn to debug
|
|
12
|
+
- *(cli)* Move logging flags to subcommands for natural syntax
|
|
13
|
+
- *(engine)* Resolve null options and attribute access bugs (#38)
|
|
14
|
+
|
|
15
|
+
### Documentation
|
|
16
|
+
|
|
17
|
+
- Add benchmark methodology, infrastructure setup, and project roadmap
|
|
18
|
+
- Add tool versions to benchmark methodology (#29)
|
|
19
|
+
- Document verbosity, progress bar, and CLI flags
|
|
20
|
+
- Update roadmap to reflect changes (#34)
|
|
21
|
+
- Add HTTP method, body, and header documentation
|
|
22
|
+
- Add code examples (#37)
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
- *(reporter)* Add metric descriptions to CLI summary output (#30)
|
|
27
|
+
- Add chaos testing engine (#32)
|
|
28
|
+
- Add indicatif dependency for progress bar rendering
|
|
29
|
+
- Add progress bar module and live render loop
|
|
30
|
+
- *(metrics)* Add atomic fields to LiveCounters for active workers and latency tracking
|
|
31
|
+
- *(worker)* Track active workers and update live request metrics
|
|
32
|
+
- *(config)* Support no_progress flag in TestConfig
|
|
33
|
+
- *(engine)* Spawn live progress bar during load test runs
|
|
34
|
+
- *(cli)* Add --no-progress option to suppress live progress bar
|
|
35
|
+
- Progress indicators (#33)
|
|
36
|
+
- Add HTTP request configuration
|
|
37
|
+
- Support custom HTTP requests
|
|
38
|
+
- *(cli)* Add HTTP request options
|
|
39
|
+
- Add support for HTTP methods, request bodies, and custom headers (#35)
|
|
40
|
+
- Implement graceful shutdown on SIGINT (#39)
|
|
41
|
+
|
|
42
|
+
### Miscellaneous Tasks
|
|
43
|
+
|
|
44
|
+
- *(release)* Prepare v0.1.0 changelog and tag
|
|
45
|
+
- Add PyPI publish workflow with tag-based triggering (#26)
|
|
46
|
+
|
|
47
|
+
### Performance
|
|
48
|
+
|
|
49
|
+
- Switch global allocator to mimalloc for reduced lock contention (#28)
|
|
50
|
+
- Optimize connection pooling with tcp_nodelay, pre-warming, and body consumption (#31)
|
|
51
|
+
|
|
52
|
+
### Refactoring
|
|
53
|
+
|
|
54
|
+
- *(engine)* Encapsulate request options into RequestOptions dataclass (#36)
|
|
55
|
+
|
|
56
|
+
### Testing
|
|
57
|
+
|
|
58
|
+
- *(config)* Simplify header setup in custom config test
|
|
59
|
+
## [0.1.0] - 2026-07-23
|
|
60
|
+
|
|
61
|
+
### Bug Fixes
|
|
62
|
+
|
|
63
|
+
- *(tests)* Fix backward-compat tests to go through main()
|
|
64
|
+
- *(docs)* Correct print_summary import and add results display to examples
|
|
65
|
+
- *(docs)* Correct print_summary import and add results display to examples (#23)
|
|
66
|
+
|
|
67
|
+
### Documentation
|
|
68
|
+
|
|
69
|
+
- *(changelog)* Configure git-cliff and generate initial CHANGELOG.md (#16)
|
|
70
|
+
- Add logging flags and tracing crates to README
|
|
71
|
+
|
|
72
|
+
### Features
|
|
73
|
+
|
|
74
|
+
- *(config)* Add TestConfig pyclass with Python default arguments (#5)
|
|
75
|
+
- *(metrics)* Add TestSummary pyclass and calculate_summary (#6)
|
|
76
|
+
- Add StrobEngine class with sync and async interfaces (#11)
|
|
77
|
+
- Dynamic load profiling (#17)
|
|
78
|
+
- *(cli)* Add -V/--version flag with importlib.metadata
|
|
79
|
+
- *(rust)* Add tracing instrumentation and init_logging binding
|
|
80
|
+
- *(cli)* Add -v/-q/--log-file flags with stderr logging
|
|
81
|
+
- *(logging)* Unify system logging (#20)
|
|
82
|
+
|
|
83
|
+
### Miscellaneous Tasks
|
|
84
|
+
|
|
85
|
+
- Add Python environment and build artifacts to .gitignore
|
|
86
|
+
- Initialize strobengine hybrid workspace architecture
|
|
87
|
+
- *(cargo)* Fix formatting and wrap comment for abi3-py38 feature (#1)
|
|
88
|
+
- *(python)* Add __all__ to package init for explicit public API (#8)
|
|
89
|
+
- Add GitHub Actions workflow for Rust and Python checks (#10)
|
|
90
|
+
- Update minimum Python version to 3.13 for abi3-py313
|
|
91
|
+
- *(lint)* Add ruff rules and auto-fix pyupgrade suggestions
|
|
92
|
+
- Bump minimum python version to 3.13 and expand ruff rules (#21)
|
|
93
|
+
- Add TestPyPI publish workflow with OIDC trusted publishing
|
|
94
|
+
- Lower minimum Python version to 3.11 for broader compatibility
|
|
95
|
+
- Lower minimum Python version to 3.11 for broader compatibility (#24)
|
|
96
|
+
- Add testpypi environment to publish workflow
|
|
97
|
+
- Add testpypi environment to publish workflow (#25)
|
|
98
|
+
- Add PyPI publish workflow with tag-based triggering
|
|
99
|
+
|
|
100
|
+
### Refactoring
|
|
101
|
+
|
|
102
|
+
- Clean up __init__.py public API exports (#14)
|
|
103
|
+
- *(cli)* Migrate CLI from argparse to typer (#18)
|
|
@@ -118,6 +118,18 @@ dependencies = [
|
|
|
118
118
|
"memchr",
|
|
119
119
|
]
|
|
120
120
|
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "console"
|
|
123
|
+
version = "0.16.4"
|
|
124
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
+
checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c"
|
|
126
|
+
dependencies = [
|
|
127
|
+
"encode_unicode",
|
|
128
|
+
"libc",
|
|
129
|
+
"unicode-width",
|
|
130
|
+
"windows-sys 0.61.2",
|
|
131
|
+
]
|
|
132
|
+
|
|
121
133
|
[[package]]
|
|
122
134
|
name = "core-foundation"
|
|
123
135
|
version = "0.9.4"
|
|
@@ -170,6 +182,12 @@ version = "1.0.5"
|
|
|
170
182
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
171
183
|
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
|
172
184
|
|
|
185
|
+
[[package]]
|
|
186
|
+
name = "encode_unicode"
|
|
187
|
+
version = "1.0.0"
|
|
188
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
189
|
+
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
|
190
|
+
|
|
173
191
|
[[package]]
|
|
174
192
|
name = "encoding_rs"
|
|
175
193
|
version = "0.8.35"
|
|
@@ -185,6 +203,22 @@ version = "1.0.2"
|
|
|
185
203
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
186
204
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|
187
205
|
|
|
206
|
+
[[package]]
|
|
207
|
+
name = "errno"
|
|
208
|
+
version = "0.3.14"
|
|
209
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
210
|
+
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
|
211
|
+
dependencies = [
|
|
212
|
+
"libc",
|
|
213
|
+
"windows-sys 0.52.0",
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
[[package]]
|
|
217
|
+
name = "fastrand"
|
|
218
|
+
version = "2.5.0"
|
|
219
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
220
|
+
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
|
221
|
+
|
|
188
222
|
[[package]]
|
|
189
223
|
name = "find-msvc-tools"
|
|
190
224
|
version = "0.1.9"
|
|
@@ -534,6 +568,19 @@ dependencies = [
|
|
|
534
568
|
"hashbrown",
|
|
535
569
|
]
|
|
536
570
|
|
|
571
|
+
[[package]]
|
|
572
|
+
name = "indicatif"
|
|
573
|
+
version = "0.18.6"
|
|
574
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
575
|
+
checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c"
|
|
576
|
+
dependencies = [
|
|
577
|
+
"console",
|
|
578
|
+
"portable-atomic",
|
|
579
|
+
"unicode-width",
|
|
580
|
+
"unit-prefix",
|
|
581
|
+
"web-time",
|
|
582
|
+
]
|
|
583
|
+
|
|
537
584
|
[[package]]
|
|
538
585
|
name = "ipnet"
|
|
539
586
|
version = "2.12.0"
|
|
@@ -628,6 +675,15 @@ version = "0.2.186"
|
|
|
628
675
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
629
676
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
|
630
677
|
|
|
678
|
+
[[package]]
|
|
679
|
+
name = "libmimalloc-sys"
|
|
680
|
+
version = "0.1.49"
|
|
681
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
682
|
+
checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9"
|
|
683
|
+
dependencies = [
|
|
684
|
+
"cc",
|
|
685
|
+
]
|
|
686
|
+
|
|
631
687
|
[[package]]
|
|
632
688
|
name = "litemap"
|
|
633
689
|
version = "0.8.2"
|
|
@@ -661,6 +717,15 @@ version = "2.8.3"
|
|
|
661
717
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
662
718
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
663
719
|
|
|
720
|
+
[[package]]
|
|
721
|
+
name = "mimalloc"
|
|
722
|
+
version = "0.1.52"
|
|
723
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
724
|
+
checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862"
|
|
725
|
+
dependencies = [
|
|
726
|
+
"libmimalloc-sys",
|
|
727
|
+
]
|
|
728
|
+
|
|
664
729
|
[[package]]
|
|
665
730
|
name = "mime"
|
|
666
731
|
version = "0.3.17"
|
|
@@ -1152,6 +1217,16 @@ version = "2.0.1"
|
|
|
1152
1217
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1153
1218
|
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
|
1154
1219
|
|
|
1220
|
+
[[package]]
|
|
1221
|
+
name = "signal-hook-registry"
|
|
1222
|
+
version = "1.4.8"
|
|
1223
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1224
|
+
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
|
1225
|
+
dependencies = [
|
|
1226
|
+
"errno",
|
|
1227
|
+
"libc",
|
|
1228
|
+
]
|
|
1229
|
+
|
|
1155
1230
|
[[package]]
|
|
1156
1231
|
name = "simd_cesu8"
|
|
1157
1232
|
version = "1.2.0"
|
|
@@ -1198,8 +1273,13 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
|
|
1198
1273
|
|
|
1199
1274
|
[[package]]
|
|
1200
1275
|
name = "strobengine"
|
|
1201
|
-
version = "0.
|
|
1276
|
+
version = "0.2.0"
|
|
1202
1277
|
dependencies = [
|
|
1278
|
+
"bytes",
|
|
1279
|
+
"fastrand",
|
|
1280
|
+
"http",
|
|
1281
|
+
"indicatif",
|
|
1282
|
+
"mimalloc",
|
|
1203
1283
|
"pyo3",
|
|
1204
1284
|
"reqwest",
|
|
1205
1285
|
"tokio",
|
|
@@ -1347,10 +1427,23 @@ dependencies = [
|
|
|
1347
1427
|
"libc",
|
|
1348
1428
|
"mio",
|
|
1349
1429
|
"pin-project-lite",
|
|
1430
|
+
"signal-hook-registry",
|
|
1350
1431
|
"socket2",
|
|
1432
|
+
"tokio-macros",
|
|
1351
1433
|
"windows-sys 0.61.2",
|
|
1352
1434
|
]
|
|
1353
1435
|
|
|
1436
|
+
[[package]]
|
|
1437
|
+
name = "tokio-macros"
|
|
1438
|
+
version = "2.7.1"
|
|
1439
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1440
|
+
checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba"
|
|
1441
|
+
dependencies = [
|
|
1442
|
+
"proc-macro2",
|
|
1443
|
+
"quote",
|
|
1444
|
+
"syn 2.0.119",
|
|
1445
|
+
]
|
|
1446
|
+
|
|
1354
1447
|
[[package]]
|
|
1355
1448
|
name = "tokio-rustls"
|
|
1356
1449
|
version = "0.26.4"
|
|
@@ -1493,6 +1586,18 @@ version = "1.0.24"
|
|
|
1493
1586
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1494
1587
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
1495
1588
|
|
|
1589
|
+
[[package]]
|
|
1590
|
+
name = "unicode-width"
|
|
1591
|
+
version = "0.2.2"
|
|
1592
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1593
|
+
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
|
1594
|
+
|
|
1595
|
+
[[package]]
|
|
1596
|
+
name = "unit-prefix"
|
|
1597
|
+
version = "0.5.2"
|
|
1598
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1599
|
+
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
|
|
1600
|
+
|
|
1496
1601
|
[[package]]
|
|
1497
1602
|
name = "untrusted"
|
|
1498
1603
|
version = "0.9.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "strobengine"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
|
|
@@ -16,8 +16,12 @@ crate-type = ["cdylib", "rlib"]
|
|
|
16
16
|
# minimum Python version 3.11
|
|
17
17
|
pyo3 = { version = "0.29", features = ["extension-module", "abi3-py311"] }
|
|
18
18
|
reqwest = { version = "0.13.4" }
|
|
19
|
-
|
|
19
|
+
http = "1"
|
|
20
|
+
bytes = "1"
|
|
21
|
+
tokio = { version = "1.53.0", features = ["sync", "time", "rt", "rt-multi-thread", "macros", "signal"] }
|
|
20
22
|
tokio-util = { version = "0.7", features = ["rt"] }
|
|
21
23
|
tracing = "0.1"
|
|
22
24
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "registry"] }
|
|
23
|
-
|
|
25
|
+
mimalloc = "0.1.52"
|
|
26
|
+
fastrand = "2.5"
|
|
27
|
+
indicatif = "0.18.6"
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strobengine
|
|
3
|
+
Version: 0.2.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/strobe-ops/strobengine/blob/main/CHANGELOG.md
|
|
13
|
+
Project-URL: Documentation, https://github.com/strobe-ops/strobengine
|
|
14
|
+
Project-URL: Homepage, https://github.com/strobe-ops/strobengine
|
|
15
|
+
Project-URL: Source, https://github.com/strobe-ops/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.
|
|
@@ -18,6 +34,10 @@ A high-performance HTTP load testing engine with a Python API and a bare-metal R
|
|
|
18
34
|
| tokio-util | 0.7 | CancellationToken for graceful worker shutdown |
|
|
19
35
|
| tracing | 0.1 | Structured logging instrumentation |
|
|
20
36
|
| tracing-subscriber | 0.3 | Log formatting and output (stderr/file) |
|
|
37
|
+
| indicatif | 0.17 | Terminal progress bar rendering |
|
|
38
|
+
| fastrand | 2 | Fast random number generation for chaos injection |
|
|
39
|
+
| http | 1 | HTTP method types and header primitives |
|
|
40
|
+
| bytes | 1 | Zero-copy byte buffer for request payloads |
|
|
21
41
|
|
|
22
42
|
## Installation & Compilation
|
|
23
43
|
|
|
@@ -63,6 +83,15 @@ engine = StrobEngine.spike_test(
|
|
|
63
83
|
summary = engine.run()
|
|
64
84
|
|
|
65
85
|
print_summary(summary, url=engine._url, duration_secs=30)
|
|
86
|
+
|
|
87
|
+
# POST request with JSON body and custom headers
|
|
88
|
+
engine = StrobEngine(
|
|
89
|
+
url="http://localhost:8080/api/data",
|
|
90
|
+
method="POST",
|
|
91
|
+
body='{"name": "test", "value": 42}',
|
|
92
|
+
headers=[("Authorization", "Bearer token123")],
|
|
93
|
+
)
|
|
94
|
+
summary = engine.run()
|
|
66
95
|
```
|
|
67
96
|
|
|
68
97
|
For async contexts (FastAPI, Typer, etc.):
|
|
@@ -85,6 +114,25 @@ strobengine spike http://localhost:8080/api/health --baseline 5 --peak 1000 --pr
|
|
|
85
114
|
|
|
86
115
|
# JSON output for CI/CD
|
|
87
116
|
strobengine load http://localhost:8080/api/health --json
|
|
117
|
+
|
|
118
|
+
# Chaos/fault injection test (~10% of requests get faults)
|
|
119
|
+
strobengine load http://localhost:8080/api/health --chaos
|
|
120
|
+
|
|
121
|
+
# POST with JSON body
|
|
122
|
+
strobengine load http://localhost:8080/api/data --method POST --body '{"key": "val"}'
|
|
123
|
+
|
|
124
|
+
# PUT with custom headers
|
|
125
|
+
strobengine load http://localhost:8080/api/resource/1 \
|
|
126
|
+
--method PUT --body '{"name": "updated"}' --header "Authorization: Bearer token"
|
|
127
|
+
|
|
128
|
+
# DELETE
|
|
129
|
+
strobengine load http://localhost:8080/api/resource/1 --method DELETE
|
|
130
|
+
|
|
131
|
+
# Verbose debug output
|
|
132
|
+
strobengine load http://localhost:8080/api/health -vv
|
|
133
|
+
|
|
134
|
+
# Quiet mode (suppress logs, keep progress bar)
|
|
135
|
+
strobengine load http://localhost:8080/api/health -q
|
|
88
136
|
```
|
|
89
137
|
|
|
90
138
|
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).
|
|
@@ -104,6 +152,14 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
104
152
|
| `-c`, `--concurrency` | `10` | Number of concurrent workers |
|
|
105
153
|
| `-d`, `--duration` | `10` | Duration in seconds |
|
|
106
154
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
155
|
+
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
156
|
+
| `--body` | none | Request body (raw string) |
|
|
157
|
+
| `--header` | none | Custom header key:value (repeatable) |
|
|
158
|
+
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
159
|
+
| `--no-progress` | off | Suppress live progress bar |
|
|
160
|
+
| `-v`, `-vv`, `-vvv` | warn | Increase verbosity (INFO, DEBUG, TRACE) |
|
|
161
|
+
| `-q`, `--quiet` | off | Suppress all output |
|
|
162
|
+
| `--log-file <path>` | none | Write logs to file |
|
|
107
163
|
| `--json` | off | Output raw JSON instead of formatted table |
|
|
108
164
|
|
|
109
165
|
### Stress Subcommand Options
|
|
@@ -115,6 +171,14 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
115
171
|
| `--ramp` | `60` | Ramp duration in seconds |
|
|
116
172
|
| `--hold` | `30` | Hold duration at target concurrency |
|
|
117
173
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
174
|
+
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
175
|
+
| `--body` | none | Request body (raw string) |
|
|
176
|
+
| `--header` | none | Custom header key:value (repeatable) |
|
|
177
|
+
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
178
|
+
| `--no-progress` | off | Suppress live progress bar |
|
|
179
|
+
| `-v`, `-vv`, `-vvv` | warn | Increase verbosity (INFO, DEBUG, TRACE) |
|
|
180
|
+
| `-q`, `--quiet` | off | Suppress all output |
|
|
181
|
+
| `--log-file <path>` | none | Write logs to file |
|
|
118
182
|
| `--json` | off | Output raw JSON |
|
|
119
183
|
|
|
120
184
|
### Spike Subcommand Options
|
|
@@ -127,16 +191,48 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
127
191
|
| `--spike-duration` | `10` | Spike duration in seconds |
|
|
128
192
|
| `--post-spike` | `5` | Post-spike duration in seconds |
|
|
129
193
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
194
|
+
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
195
|
+
| `--body` | none | Request body (raw string) |
|
|
196
|
+
| `--header` | none | Custom header key:value (repeatable) |
|
|
197
|
+
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
198
|
+
| `--no-progress` | off | Suppress live progress bar |
|
|
199
|
+
| `-v`, `-vv`, `-vvv` | warn | Increase verbosity (INFO, DEBUG, TRACE) |
|
|
200
|
+
| `-q`, `--quiet` | off | Suppress all output |
|
|
201
|
+
| `--log-file <path>` | none | Write logs to file |
|
|
130
202
|
| `--json` | off | Output raw JSON |
|
|
131
203
|
|
|
132
204
|
### Global Options
|
|
133
205
|
|
|
134
|
-
| Flag |
|
|
135
|
-
|
|
136
|
-
| `-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
206
|
+
| Flag | Description |
|
|
207
|
+
|------|-------------|
|
|
208
|
+
| `-V`, `--version` | Show version and exit |
|
|
209
|
+
|
|
210
|
+
> **HTTP Methods, Bodies & Headers:** See [docs/http_methods.md](docs/http_methods.md) for detailed documentation on supported HTTP methods, request body handling, custom headers, and performance characteristics.
|
|
211
|
+
|
|
212
|
+
### Live Progress Bar
|
|
213
|
+
|
|
214
|
+
During test execution, a live progress bar displays on stderr with real-time telemetry:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
⠋ [00:00:05] [==============>-------------] 40% | 1250 req/s | 20 VUs | 12 err | avg 4.2ms
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- **RPS**: Instantaneous requests per second (sampled every 200ms)
|
|
221
|
+
- **VUs**: Active virtual users / concurrent workers
|
|
222
|
+
- **Errors**: Total error count
|
|
223
|
+
- **Avg latency**: Running average across all completed requests
|
|
224
|
+
|
|
225
|
+
The progress bar auto-detects non-TTY environments (CI/CD, piped output) and suppresses itself. Use `--no-progress` to explicitly disable it on interactive terminals.
|
|
226
|
+
|
|
227
|
+
### Verbosity Levels
|
|
228
|
+
|
|
229
|
+
| Flag | Level | Shows |
|
|
230
|
+
|------|-------|-------|
|
|
231
|
+
| (default) | `warn` | Errors and warnings only |
|
|
232
|
+
| `-v` | `info` | Engine start/stop, test configuration |
|
|
233
|
+
| `-vv` | `debug` | Worker spawn, HTTP errors, connection events |
|
|
234
|
+
| `-vvv` | `trace` | Per-request latency, status codes, chaos injection |
|
|
235
|
+
| `-q` | off | Suppress all log output (progress bar remains) |
|
|
140
236
|
|
|
141
237
|
Logs stream to **stderr** by default, keeping stdout clean for JSON output piping:
|
|
142
238
|
|
|
@@ -149,8 +245,10 @@ strobengine load http://localhost:8080/api/health -vv --json > results.json
|
|
|
149
245
|
strobengine separates configuration, execution, and metrics into clean Rust modules, exposed to Python via PyO3:
|
|
150
246
|
|
|
151
247
|
- **`config`** -- `TestConfig` for static load, `LoadProfile` enum for dynamic profiles (Constant, Ramp, Spike) with target concurrency interpolation.
|
|
152
|
-
- **`worker`** -- Async worker loops
|
|
248
|
+
- **`worker`** -- Async worker loops with method-aware request building, static payload reuse (Bytes), and zero-allocation header management via `ClientBuilder::default_headers()`.
|
|
153
249
|
- **`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 p99 percentiles in Rust at bare-metal speed.
|
|
250
|
+
- **`chaos`** -- Protocol-agnostic fault injection engine with `ChaosEngine` evaluator and `ChaosFault` enum (LatencySpike, CorruptedPayload, MetadataCorruption, ConnectionDrop).
|
|
251
|
+
- **`progress`** -- Background Tokio render task sampling atomic metrics every 200ms, displaying live RPS, active VUs, and latency via indicatif.
|
|
154
252
|
- **Orchestrator** -- Supervisor task ticks every 200ms, calculates target concurrency from the active profile curve, spawns/aborts workers dynamically.
|
|
155
253
|
|
|
156
254
|
The Python GIL is released entirely via `py.detach()` during test execution, allowing the full Tokio thread pool to run concurrently without throttling Python.
|
|
@@ -185,3 +283,4 @@ uv run pytest -v
|
|
|
185
283
|
## License
|
|
186
284
|
|
|
187
285
|
This project is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full text.
|
|
286
|
+
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: strobengine
|
|
3
|
-
Version: 0.1.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
|
-
Requires-Python: >=3.11
|
|
10
|
-
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
11
|
-
|
|
12
1
|
# strobengine
|
|
13
2
|
|
|
14
3
|
A high-performance HTTP load testing engine with a Python API and a bare-metal Rust core.
|
|
@@ -29,6 +18,10 @@ A high-performance HTTP load testing engine with a Python API and a bare-metal R
|
|
|
29
18
|
| tokio-util | 0.7 | CancellationToken for graceful worker shutdown |
|
|
30
19
|
| tracing | 0.1 | Structured logging instrumentation |
|
|
31
20
|
| tracing-subscriber | 0.3 | Log formatting and output (stderr/file) |
|
|
21
|
+
| indicatif | 0.17 | Terminal progress bar rendering |
|
|
22
|
+
| fastrand | 2 | Fast random number generation for chaos injection |
|
|
23
|
+
| http | 1 | HTTP method types and header primitives |
|
|
24
|
+
| bytes | 1 | Zero-copy byte buffer for request payloads |
|
|
32
25
|
|
|
33
26
|
## Installation & Compilation
|
|
34
27
|
|
|
@@ -74,6 +67,15 @@ engine = StrobEngine.spike_test(
|
|
|
74
67
|
summary = engine.run()
|
|
75
68
|
|
|
76
69
|
print_summary(summary, url=engine._url, duration_secs=30)
|
|
70
|
+
|
|
71
|
+
# POST request with JSON body and custom headers
|
|
72
|
+
engine = StrobEngine(
|
|
73
|
+
url="http://localhost:8080/api/data",
|
|
74
|
+
method="POST",
|
|
75
|
+
body='{"name": "test", "value": 42}',
|
|
76
|
+
headers=[("Authorization", "Bearer token123")],
|
|
77
|
+
)
|
|
78
|
+
summary = engine.run()
|
|
77
79
|
```
|
|
78
80
|
|
|
79
81
|
For async contexts (FastAPI, Typer, etc.):
|
|
@@ -96,6 +98,25 @@ strobengine spike http://localhost:8080/api/health --baseline 5 --peak 1000 --pr
|
|
|
96
98
|
|
|
97
99
|
# JSON output for CI/CD
|
|
98
100
|
strobengine load http://localhost:8080/api/health --json
|
|
101
|
+
|
|
102
|
+
# Chaos/fault injection test (~10% of requests get faults)
|
|
103
|
+
strobengine load http://localhost:8080/api/health --chaos
|
|
104
|
+
|
|
105
|
+
# POST with JSON body
|
|
106
|
+
strobengine load http://localhost:8080/api/data --method POST --body '{"key": "val"}'
|
|
107
|
+
|
|
108
|
+
# PUT with custom headers
|
|
109
|
+
strobengine load http://localhost:8080/api/resource/1 \
|
|
110
|
+
--method PUT --body '{"name": "updated"}' --header "Authorization: Bearer token"
|
|
111
|
+
|
|
112
|
+
# DELETE
|
|
113
|
+
strobengine load http://localhost:8080/api/resource/1 --method DELETE
|
|
114
|
+
|
|
115
|
+
# Verbose debug output
|
|
116
|
+
strobengine load http://localhost:8080/api/health -vv
|
|
117
|
+
|
|
118
|
+
# Quiet mode (suppress logs, keep progress bar)
|
|
119
|
+
strobengine load http://localhost:8080/api/health -q
|
|
99
120
|
```
|
|
100
121
|
|
|
101
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).
|
|
@@ -115,6 +136,14 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
115
136
|
| `-c`, `--concurrency` | `10` | Number of concurrent workers |
|
|
116
137
|
| `-d`, `--duration` | `10` | Duration in seconds |
|
|
117
138
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
139
|
+
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
140
|
+
| `--body` | none | Request body (raw string) |
|
|
141
|
+
| `--header` | none | Custom header key:value (repeatable) |
|
|
142
|
+
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
143
|
+
| `--no-progress` | off | Suppress live progress bar |
|
|
144
|
+
| `-v`, `-vv`, `-vvv` | warn | Increase verbosity (INFO, DEBUG, TRACE) |
|
|
145
|
+
| `-q`, `--quiet` | off | Suppress all output |
|
|
146
|
+
| `--log-file <path>` | none | Write logs to file |
|
|
118
147
|
| `--json` | off | Output raw JSON instead of formatted table |
|
|
119
148
|
|
|
120
149
|
### Stress Subcommand Options
|
|
@@ -126,6 +155,14 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
126
155
|
| `--ramp` | `60` | Ramp duration in seconds |
|
|
127
156
|
| `--hold` | `30` | Hold duration at target concurrency |
|
|
128
157
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
158
|
+
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
159
|
+
| `--body` | none | Request body (raw string) |
|
|
160
|
+
| `--header` | none | Custom header key:value (repeatable) |
|
|
161
|
+
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
162
|
+
| `--no-progress` | off | Suppress live progress bar |
|
|
163
|
+
| `-v`, `-vv`, `-vvv` | warn | Increase verbosity (INFO, DEBUG, TRACE) |
|
|
164
|
+
| `-q`, `--quiet` | off | Suppress all output |
|
|
165
|
+
| `--log-file <path>` | none | Write logs to file |
|
|
129
166
|
| `--json` | off | Output raw JSON |
|
|
130
167
|
|
|
131
168
|
### Spike Subcommand Options
|
|
@@ -138,16 +175,48 @@ By default, this spawns **10 concurrent workers** for **10 seconds** with a **10
|
|
|
138
175
|
| `--spike-duration` | `10` | Spike duration in seconds |
|
|
139
176
|
| `--post-spike` | `5` | Post-spike duration in seconds |
|
|
140
177
|
| `-t`, `--timeout` | `10` | Per-request timeout in seconds |
|
|
178
|
+
| `--method` | `GET` | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
179
|
+
| `--body` | none | Request body (raw string) |
|
|
180
|
+
| `--header` | none | Custom header key:value (repeatable) |
|
|
181
|
+
| `--chaos` | off | Enable fault injection (~10% of requests) |
|
|
182
|
+
| `--no-progress` | off | Suppress live progress bar |
|
|
183
|
+
| `-v`, `-vv`, `-vvv` | warn | Increase verbosity (INFO, DEBUG, TRACE) |
|
|
184
|
+
| `-q`, `--quiet` | off | Suppress all output |
|
|
185
|
+
| `--log-file <path>` | none | Write logs to file |
|
|
141
186
|
| `--json` | off | Output raw JSON |
|
|
142
187
|
|
|
143
188
|
### Global Options
|
|
144
189
|
|
|
145
|
-
| Flag |
|
|
146
|
-
|
|
147
|
-
| `-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
190
|
+
| Flag | Description |
|
|
191
|
+
|------|-------------|
|
|
192
|
+
| `-V`, `--version` | Show version and exit |
|
|
193
|
+
|
|
194
|
+
> **HTTP Methods, Bodies & Headers:** See [docs/http_methods.md](docs/http_methods.md) for detailed documentation on supported HTTP methods, request body handling, custom headers, and performance characteristics.
|
|
195
|
+
|
|
196
|
+
### Live Progress Bar
|
|
197
|
+
|
|
198
|
+
During test execution, a live progress bar displays on stderr with real-time telemetry:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
⠋ [00:00:05] [==============>-------------] 40% | 1250 req/s | 20 VUs | 12 err | avg 4.2ms
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
- **RPS**: Instantaneous requests per second (sampled every 200ms)
|
|
205
|
+
- **VUs**: Active virtual users / concurrent workers
|
|
206
|
+
- **Errors**: Total error count
|
|
207
|
+
- **Avg latency**: Running average across all completed requests
|
|
208
|
+
|
|
209
|
+
The progress bar auto-detects non-TTY environments (CI/CD, piped output) and suppresses itself. Use `--no-progress` to explicitly disable it on interactive terminals.
|
|
210
|
+
|
|
211
|
+
### Verbosity Levels
|
|
212
|
+
|
|
213
|
+
| Flag | Level | Shows |
|
|
214
|
+
|------|-------|-------|
|
|
215
|
+
| (default) | `warn` | Errors and warnings only |
|
|
216
|
+
| `-v` | `info` | Engine start/stop, test configuration |
|
|
217
|
+
| `-vv` | `debug` | Worker spawn, HTTP errors, connection events |
|
|
218
|
+
| `-vvv` | `trace` | Per-request latency, status codes, chaos injection |
|
|
219
|
+
| `-q` | off | Suppress all log output (progress bar remains) |
|
|
151
220
|
|
|
152
221
|
Logs stream to **stderr** by default, keeping stdout clean for JSON output piping:
|
|
153
222
|
|
|
@@ -160,8 +229,10 @@ strobengine load http://localhost:8080/api/health -vv --json > results.json
|
|
|
160
229
|
strobengine separates configuration, execution, and metrics into clean Rust modules, exposed to Python via PyO3:
|
|
161
230
|
|
|
162
231
|
- **`config`** -- `TestConfig` for static load, `LoadProfile` enum for dynamic profiles (Constant, Ramp, Spike) with target concurrency interpolation.
|
|
163
|
-
- **`worker`** -- Async worker loops
|
|
232
|
+
- **`worker`** -- Async worker loops with method-aware request building, static payload reuse (Bytes), and zero-allocation header management via `ClientBuilder::default_headers()`.
|
|
164
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 p99 percentiles in Rust at bare-metal speed.
|
|
234
|
+
- **`chaos`** -- Protocol-agnostic fault injection engine with `ChaosEngine` evaluator and `ChaosFault` enum (LatencySpike, CorruptedPayload, MetadataCorruption, ConnectionDrop).
|
|
235
|
+
- **`progress`** -- Background Tokio render task sampling atomic metrics every 200ms, displaying live RPS, active VUs, and latency via indicatif.
|
|
165
236
|
- **Orchestrator** -- Supervisor task ticks every 200ms, calculates target concurrency from the active profile curve, spawns/aborts workers dynamically.
|
|
166
237
|
|
|
167
238
|
The Python GIL is released entirely via `py.detach()` during test execution, allowing the full Tokio thread pool to run concurrently without throttling Python.
|
|
@@ -196,4 +267,3 @@ uv run pytest -v
|
|
|
196
267
|
## License
|
|
197
268
|
|
|
198
269
|
This project is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full text.
|
|
199
|
-
|