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.
Files changed (44) hide show
  1. strobengine-0.2.0/CHANGELOG.md +103 -0
  2. {strobengine-0.1.0 → strobengine-0.2.0}/Cargo.lock +106 -1
  3. {strobengine-0.1.0 → strobengine-0.2.0}/Cargo.toml +7 -3
  4. strobengine-0.1.0/README.md → strobengine-0.2.0/PKG-INFO +106 -7
  5. strobengine-0.1.0/PKG-INFO → strobengine-0.2.0/README.md +89 -19
  6. strobengine-0.2.0/docs/benchmarks.md +213 -0
  7. strobengine-0.2.0/docs/http_methods.md +165 -0
  8. strobengine-0.2.0/docs/infrastructure.md +238 -0
  9. strobengine-0.2.0/docs/roadmap.md +89 -0
  10. strobengine-0.2.0/examples/http_methods.py +67 -0
  11. strobengine-0.2.0/examples/load_test.py +14 -0
  12. strobengine-0.2.0/examples/spike_test.py +16 -0
  13. strobengine-0.2.0/examples/stress_test.py +15 -0
  14. {strobengine-0.1.0 → strobengine-0.2.0}/pyproject.toml +11 -1
  15. strobengine-0.2.0/src/chaos.rs +34 -0
  16. {strobengine-0.1.0 → strobengine-0.2.0}/src/config.rs +97 -16
  17. strobengine-0.2.0/src/lib.rs +425 -0
  18. {strobengine-0.1.0 → strobengine-0.2.0}/src/metrics.rs +9 -1
  19. strobengine-0.2.0/src/progress.rs +95 -0
  20. {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/__init__.py +2 -1
  21. {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/_strobengine.pyi +21 -1
  22. {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/cli.py +163 -22
  23. {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/engine.py +56 -16
  24. {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/reporter.py +14 -0
  25. strobengine-0.2.0/src/worker.rs +162 -0
  26. {strobengine-0.1.0 → strobengine-0.2.0}/tests/conftest.py +3 -1
  27. {strobengine-0.1.0 → strobengine-0.2.0}/tests/test_cli.py +8 -5
  28. {strobengine-0.1.0 → strobengine-0.2.0}/tests/test_engine.py +11 -5
  29. {strobengine-0.1.0 → strobengine-0.2.0}/uv.lock +4 -4
  30. strobengine-0.1.0/CHANGELOG.md +0 -25
  31. strobengine-0.1.0/src/lib.rs +0 -239
  32. strobengine-0.1.0/src/worker.rs +0 -58
  33. {strobengine-0.1.0 → strobengine-0.2.0}/.github/workflows/ci.yml +0 -0
  34. {strobengine-0.1.0 → strobengine-0.2.0}/.github/workflows/publish-test.yml +0 -0
  35. {strobengine-0.1.0 → strobengine-0.2.0}/.github/workflows/publish.yml +0 -0
  36. {strobengine-0.1.0 → strobengine-0.2.0}/.gitignore +0 -0
  37. {strobengine-0.1.0 → strobengine-0.2.0}/.python-version +0 -0
  38. {strobengine-0.1.0 → strobengine-0.2.0}/LICENSE +0 -0
  39. {strobengine-0.1.0 → strobengine-0.2.0}/cliff.toml +0 -0
  40. {strobengine-0.1.0 → strobengine-0.2.0}/src/logging.rs +0 -0
  41. {strobengine-0.1.0 → strobengine-0.2.0}/src/strobengine/py.typed +0 -0
  42. {strobengine-0.1.0 → strobengine-0.2.0}/tests/__init__.py +0 -0
  43. {strobengine-0.1.0 → strobengine-0.2.0}/tests/test_logging.py +0 -0
  44. {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.1.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.1.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
- 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.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 | 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
+
@@ -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 | Default | Description |
146
- |------|---------|-------------|
147
- | `-v`, `-vv`, `-vvv` | warn | Increase verbosity (INFO, DEBUG, TRACE) |
148
- | `-q`, `--quiet` | off | Suppress all output |
149
- | `--log-file <path>` | none | Write logs to file |
150
- | `-V`, `--version` | off | Show version and exit |
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 accepting `CancellationToken` for graceful shutdown. Workers finish in-flight requests before exiting.
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
-