strobengine 0.1.0__tar.gz → 0.2.1__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.
Files changed (46) hide show
  1. {strobengine-0.1.0 → strobengine-0.2.1}/.github/workflows/publish-test.yml +18 -3
  2. {strobengine-0.1.0 → strobengine-0.2.1}/.github/workflows/publish.yml +17 -3
  3. strobengine-0.2.1/CHANGELOG.md +131 -0
  4. {strobengine-0.1.0 → strobengine-0.2.1}/Cargo.lock +106 -1
  5. {strobengine-0.1.0 → strobengine-0.2.1}/Cargo.toml +7 -3
  6. strobengine-0.1.0/README.md → strobengine-0.2.1/PKG-INFO +106 -7
  7. strobengine-0.1.0/PKG-INFO → strobengine-0.2.1/README.md +89 -19
  8. strobengine-0.2.1/docs/benchmarks.md +213 -0
  9. strobengine-0.2.1/docs/http_methods.md +165 -0
  10. strobengine-0.2.1/docs/infrastructure.md +238 -0
  11. strobengine-0.2.1/docs/roadmap.md +89 -0
  12. strobengine-0.2.1/examples/http_methods.py +67 -0
  13. strobengine-0.2.1/examples/load_test.py +14 -0
  14. strobengine-0.2.1/examples/spike_test.py +16 -0
  15. strobengine-0.2.1/examples/stress_test.py +15 -0
  16. {strobengine-0.1.0 → strobengine-0.2.1}/pyproject.toml +11 -1
  17. strobengine-0.2.1/src/chaos.rs +34 -0
  18. {strobengine-0.1.0 → strobengine-0.2.1}/src/config.rs +97 -16
  19. strobengine-0.2.1/src/lib.rs +493 -0
  20. {strobengine-0.1.0 → strobengine-0.2.1}/src/metrics.rs +28 -13
  21. strobengine-0.2.1/src/progress.rs +105 -0
  22. {strobengine-0.1.0 → strobengine-0.2.1}/src/strobengine/__init__.py +2 -1
  23. {strobengine-0.1.0 → strobengine-0.2.1}/src/strobengine/_strobengine.pyi +21 -1
  24. {strobengine-0.1.0 → strobengine-0.2.1}/src/strobengine/cli.py +168 -24
  25. strobengine-0.2.1/src/strobengine/constants.py +14 -0
  26. strobengine-0.2.1/src/strobengine/engine.py +176 -0
  27. {strobengine-0.1.0 → strobengine-0.2.1}/src/strobengine/reporter.py +14 -0
  28. strobengine-0.2.1/src/worker.rs +162 -0
  29. {strobengine-0.1.0 → strobengine-0.2.1}/tests/conftest.py +3 -1
  30. {strobengine-0.1.0 → strobengine-0.2.1}/tests/test_cli.py +8 -5
  31. {strobengine-0.1.0 → strobengine-0.2.1}/tests/test_engine.py +11 -5
  32. {strobengine-0.1.0 → strobengine-0.2.1}/uv.lock +4 -4
  33. strobengine-0.1.0/CHANGELOG.md +0 -25
  34. strobengine-0.1.0/src/lib.rs +0 -239
  35. strobengine-0.1.0/src/strobengine/engine.py +0 -122
  36. strobengine-0.1.0/src/worker.rs +0 -58
  37. {strobengine-0.1.0 → strobengine-0.2.1}/.github/workflows/ci.yml +0 -0
  38. {strobengine-0.1.0 → strobengine-0.2.1}/.gitignore +0 -0
  39. {strobengine-0.1.0 → strobengine-0.2.1}/.python-version +0 -0
  40. {strobengine-0.1.0 → strobengine-0.2.1}/LICENSE +0 -0
  41. {strobengine-0.1.0 → strobengine-0.2.1}/cliff.toml +0 -0
  42. {strobengine-0.1.0 → strobengine-0.2.1}/src/logging.rs +0 -0
  43. {strobengine-0.1.0 → strobengine-0.2.1}/src/strobengine/py.typed +0 -0
  44. {strobengine-0.1.0 → strobengine-0.2.1}/tests/__init__.py +0 -0
  45. {strobengine-0.1.0 → strobengine-0.2.1}/tests/test_logging.py +0 -0
  46. {strobengine-0.1.0 → strobengine-0.2.1}/tests/test_reporter.py +0 -0
@@ -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
- os: [ubuntu-latest, macos-latest, windows-latest]
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
- os: [ubuntu-latest, macos-latest, windows-latest]
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,131 @@
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.1] - 2026-08-03
8
+
9
+ ### Bug Fixes
10
+
11
+ - *(types)* Correct headers type in _strobengine.pyi from dict to list of tuples (#41)
12
+ - Replace build_client panic with proper error propagation (#42)
13
+ - Replace std::process::exit with safe PyKeyboardInterrupt on SIGINT (#43)
14
+ - *(progress)* Guard against zero total_duration in progress bar calculation (#44)
15
+ - Improve worker shutdown and panic handling (#45)
16
+ - *(metrics)* Normalize counter types to AtomicU64 for 32-bit safety (#46)
17
+ - *(cli)* Add type annotation to _output_results summary parameter (#48)
18
+
19
+ ### Documentation
20
+
21
+ - Add comment explaining SystemExit re-raise pattern (#50)
22
+
23
+ ### Miscellaneous Tasks
24
+
25
+ - Remove unnecessary clippy allow on run_load_test (#47)
26
+ - Add multi-architecture matrix for macOS wheel builds (#52)
27
+
28
+ ### Refactoring
29
+
30
+ - Extract magic number 8192 to named constant METRIC_CHANNEL_BUFFER (#49)
31
+ - Extract magic numbers to named constants across Rust and Python (#51)
32
+ ## [0.2.0] - 2026-08-02
33
+
34
+ ### Bug Fixes
35
+
36
+ - *(progress)* Downgrade expected HTTP errors from warn to debug
37
+ - *(cli)* Move logging flags to subcommands for natural syntax
38
+ - *(engine)* Resolve null options and attribute access bugs (#38)
39
+
40
+ ### Documentation
41
+
42
+ - Add benchmark methodology, infrastructure setup, and project roadmap
43
+ - Add tool versions to benchmark methodology (#29)
44
+ - Document verbosity, progress bar, and CLI flags
45
+ - Update roadmap to reflect changes (#34)
46
+ - Add HTTP method, body, and header documentation
47
+ - Add code examples (#37)
48
+ - *(changelog)* Update CHANGELOG.md for v0.2.0 release
49
+
50
+ ### Features
51
+
52
+ - *(reporter)* Add metric descriptions to CLI summary output (#30)
53
+ - Add chaos testing engine (#32)
54
+ - Add indicatif dependency for progress bar rendering
55
+ - Add progress bar module and live render loop
56
+ - *(metrics)* Add atomic fields to LiveCounters for active workers and latency tracking
57
+ - *(worker)* Track active workers and update live request metrics
58
+ - *(config)* Support no_progress flag in TestConfig
59
+ - *(engine)* Spawn live progress bar during load test runs
60
+ - *(cli)* Add --no-progress option to suppress live progress bar
61
+ - Progress indicators (#33)
62
+ - Add HTTP request configuration
63
+ - Support custom HTTP requests
64
+ - *(cli)* Add HTTP request options
65
+ - Add support for HTTP methods, request bodies, and custom headers (#35)
66
+ - Implement graceful shutdown on SIGINT (#39)
67
+
68
+ ### Miscellaneous Tasks
69
+
70
+ - *(release)* Prepare v0.1.0 changelog and tag
71
+ - Add PyPI publish workflow with tag-based triggering (#26)
72
+ - *(pyproject)* Add license metadata and project URLs, bump version
73
+ - *(release)* Merge v0.2.0 release prep into main (#40)
74
+
75
+ ### Performance
76
+
77
+ - Switch global allocator to mimalloc for reduced lock contention (#28)
78
+ - Optimize connection pooling with tcp_nodelay, pre-warming, and body consumption (#31)
79
+
80
+ ### Refactoring
81
+
82
+ - *(engine)* Encapsulate request options into RequestOptions dataclass (#36)
83
+
84
+ ### Testing
85
+
86
+ - *(config)* Simplify header setup in custom config test
87
+ ## [0.1.0] - 2026-07-23
88
+
89
+ ### Bug Fixes
90
+
91
+ - *(tests)* Fix backward-compat tests to go through main()
92
+ - *(docs)* Correct print_summary import and add results display to examples
93
+ - *(docs)* Correct print_summary import and add results display to examples (#23)
94
+
95
+ ### Documentation
96
+
97
+ - *(changelog)* Configure git-cliff and generate initial CHANGELOG.md (#16)
98
+ - Add logging flags and tracing crates to README
99
+
100
+ ### Features
101
+
102
+ - *(config)* Add TestConfig pyclass with Python default arguments (#5)
103
+ - *(metrics)* Add TestSummary pyclass and calculate_summary (#6)
104
+ - Add StrobEngine class with sync and async interfaces (#11)
105
+ - Dynamic load profiling (#17)
106
+ - *(cli)* Add -V/--version flag with importlib.metadata
107
+ - *(rust)* Add tracing instrumentation and init_logging binding
108
+ - *(cli)* Add -v/-q/--log-file flags with stderr logging
109
+ - *(logging)* Unify system logging (#20)
110
+
111
+ ### Miscellaneous Tasks
112
+
113
+ - Add Python environment and build artifacts to .gitignore
114
+ - Initialize strobengine hybrid workspace architecture
115
+ - *(cargo)* Fix formatting and wrap comment for abi3-py38 feature (#1)
116
+ - *(python)* Add __all__ to package init for explicit public API (#8)
117
+ - Add GitHub Actions workflow for Rust and Python checks (#10)
118
+ - Update minimum Python version to 3.13 for abi3-py313
119
+ - *(lint)* Add ruff rules and auto-fix pyupgrade suggestions
120
+ - Bump minimum python version to 3.13 and expand ruff rules (#21)
121
+ - Add TestPyPI publish workflow with OIDC trusted publishing
122
+ - Lower minimum Python version to 3.11 for broader compatibility
123
+ - Lower minimum Python version to 3.11 for broader compatibility (#24)
124
+ - Add testpypi environment to publish workflow
125
+ - Add testpypi environment to publish workflow (#25)
126
+ - Add PyPI publish workflow with tag-based triggering
127
+
128
+ ### Refactoring
129
+
130
+ - Clean up __init__.py public API exports (#14)
131
+ - *(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.1.0"
1276
+ version = "0.2.1"
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.1.0"
3
+ version = "0.2.1"
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
- tokio = { version = "1.53.0", features = ["sync", "time", "rt", "rt-multi-thread"] }
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
- # pyo3-async-runtimes = { version = "0.29.0", features = ["tokio-runtime"] }
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.1
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 | Default | Description |
135
- |------|---------|-------------|
136
- | `-v`, `-vv`, `-vvv` | warn | Increase verbosity (INFO, DEBUG, TRACE) |
137
- | `-q`, `--quiet` | off | Suppress all output |
138
- | `--log-file <path>` | none | Write logs to file |
139
- | `-V`, `--version` | off | Show version and exit |
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 accepting `CancellationToken` for graceful shutdown. Workers finish in-flight requests before exiting.
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
+