strobengine 0.5.2__tar.gz → 0.6.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 (98) hide show
  1. {strobengine-0.5.2 → strobengine-0.6.1}/CHANGELOG.md +66 -0
  2. {strobengine-0.5.2 → strobengine-0.6.1}/Cargo.lock +1 -1
  3. {strobengine-0.5.2 → strobengine-0.6.1}/Cargo.toml +1 -1
  4. {strobengine-0.5.2 → strobengine-0.6.1}/PKG-INFO +1 -1
  5. {strobengine-0.5.2 → strobengine-0.6.1}/docs/http3.md +10 -1
  6. {strobengine-0.5.2 → strobengine-0.6.1}/docs/reports.md +18 -2
  7. {strobengine-0.5.2 → strobengine-0.6.1}/docs/roadmap.md +12 -12
  8. {strobengine-0.5.2 → strobengine-0.6.1}/docs/sse.md +8 -1
  9. {strobengine-0.5.2 → strobengine-0.6.1}/pyproject.toml +2 -1
  10. {strobengine-0.5.2 → strobengine-0.6.1}/src/chaos.rs +33 -0
  11. {strobengine-0.5.2 → strobengine-0.6.1}/src/config.rs +97 -0
  12. {strobengine-0.5.2 → strobengine-0.6.1}/src/lib.rs +172 -144
  13. {strobengine-0.5.2 → strobengine-0.6.1}/src/metrics.rs +334 -33
  14. {strobengine-0.5.2 → strobengine-0.6.1}/src/protocols/grpc.rs +1 -0
  15. {strobengine-0.5.2 → strobengine-0.6.1}/src/protocols/http.rs +3 -1
  16. {strobengine-0.5.2 → strobengine-0.6.1}/src/protocols/http3.rs +2 -0
  17. {strobengine-0.5.2 → strobengine-0.6.1}/src/protocols/sse.rs +6 -0
  18. {strobengine-0.5.2 → strobengine-0.6.1}/src/protocols/websocket.rs +5 -0
  19. {strobengine-0.5.2 → strobengine-0.6.1}/src/report/schema.rs +63 -9
  20. {strobengine-0.5.2 → strobengine-0.6.1}/src/report/writer.rs +6 -0
  21. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/_strobengine.pyi +31 -0
  22. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/cli.py +135 -179
  23. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/engine.py +14 -10
  24. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/reporter.py +90 -1
  25. strobengine-0.5.2/src/strobengine/reporting/html_report.py → strobengine-0.6.1/src/strobengine/reporting/assets/report_template.html +0 -72
  26. strobengine-0.6.1/src/strobengine/reporting/html_report.py +71 -0
  27. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/test_grpc_e2e.py +4 -0
  28. strobengine-0.6.1/tests/e2e/test_latency_stats_e2e.py +52 -0
  29. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/test_sse_e2e.py +27 -0
  30. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/test_websocket_e2e.py +8 -4
  31. {strobengine-0.5.2 → strobengine-0.6.1}/tests/test_engine.py +8 -6
  32. {strobengine-0.5.2 → strobengine-0.6.1}/tests/test_reporter.py +7 -0
  33. {strobengine-0.5.2 → strobengine-0.6.1}/tests/test_reporting.py +28 -0
  34. {strobengine-0.5.2 → strobengine-0.6.1}/uv.lock +1 -1
  35. {strobengine-0.5.2 → strobengine-0.6.1}/.dockerignore +0 -0
  36. {strobengine-0.5.2 → strobengine-0.6.1}/.github/workflows/ci.yml +0 -0
  37. {strobengine-0.5.2 → strobengine-0.6.1}/.github/workflows/docker-release.yml +0 -0
  38. {strobengine-0.5.2 → strobengine-0.6.1}/.github/workflows/publish-test.yml +0 -0
  39. {strobengine-0.5.2 → strobengine-0.6.1}/.github/workflows/publish.yml +0 -0
  40. {strobengine-0.5.2 → strobengine-0.6.1}/.gitignore +0 -0
  41. {strobengine-0.5.2 → strobengine-0.6.1}/.pre-commit-config.yaml +0 -0
  42. {strobengine-0.5.2 → strobengine-0.6.1}/.python-version +0 -0
  43. {strobengine-0.5.2 → strobengine-0.6.1}/Dockerfile +0 -0
  44. {strobengine-0.5.2 → strobengine-0.6.1}/LICENSE +0 -0
  45. {strobengine-0.5.2 → strobengine-0.6.1}/Makefile +0 -0
  46. {strobengine-0.5.2 → strobengine-0.6.1}/README.md +0 -0
  47. {strobengine-0.5.2 → strobengine-0.6.1}/cliff.toml +0 -0
  48. {strobengine-0.5.2 → strobengine-0.6.1}/docs/benchmarks.md +0 -0
  49. {strobengine-0.5.2 → strobengine-0.6.1}/docs/cli.md +0 -0
  50. {strobengine-0.5.2 → strobengine-0.6.1}/docs/dependencies.md +0 -0
  51. {strobengine-0.5.2 → strobengine-0.6.1}/docs/docker.md +0 -0
  52. {strobengine-0.5.2 → strobengine-0.6.1}/docs/grpc.md +0 -0
  53. {strobengine-0.5.2 → strobengine-0.6.1}/docs/http_methods.md +0 -0
  54. {strobengine-0.5.2 → strobengine-0.6.1}/docs/infrastructure.md +0 -0
  55. {strobengine-0.5.2 → strobengine-0.6.1}/docs/install.md +0 -0
  56. {strobengine-0.5.2 → strobengine-0.6.1}/docs/quickstart.md +0 -0
  57. {strobengine-0.5.2 → strobengine-0.6.1}/docs/release.md +0 -0
  58. {strobengine-0.5.2 → strobengine-0.6.1}/docs/table_of_contents.md +0 -0
  59. {strobengine-0.5.2 → strobengine-0.6.1}/docs/testing.md +0 -0
  60. {strobengine-0.5.2 → strobengine-0.6.1}/docs/websockets.md +0 -0
  61. {strobengine-0.5.2 → strobengine-0.6.1}/examples/grpc.py +0 -0
  62. {strobengine-0.5.2 → strobengine-0.6.1}/examples/http3.py +0 -0
  63. {strobengine-0.5.2 → strobengine-0.6.1}/examples/http_methods.py +0 -0
  64. {strobengine-0.5.2 → strobengine-0.6.1}/examples/load_test.py +0 -0
  65. {strobengine-0.5.2 → strobengine-0.6.1}/examples/spike_test.py +0 -0
  66. {strobengine-0.5.2 → strobengine-0.6.1}/examples/sse.py +0 -0
  67. {strobengine-0.5.2 → strobengine-0.6.1}/examples/stress_test.py +0 -0
  68. {strobengine-0.5.2 → strobengine-0.6.1}/examples/websocket.py +0 -0
  69. {strobengine-0.5.2 → strobengine-0.6.1}/rust-toolchain.toml +0 -0
  70. {strobengine-0.5.2 → strobengine-0.6.1}/scripts/release.py +0 -0
  71. {strobengine-0.5.2 → strobengine-0.6.1}/src/logging.rs +0 -0
  72. {strobengine-0.5.2 → strobengine-0.6.1}/src/progress.rs +0 -0
  73. {strobengine-0.5.2 → strobengine-0.6.1}/src/protocols/grpc_parser.rs +0 -0
  74. {strobengine-0.5.2 → strobengine-0.6.1}/src/protocols/grpc_reflection.rs +0 -0
  75. {strobengine-0.5.2 → strobengine-0.6.1}/src/protocols/mod.rs +0 -0
  76. {strobengine-0.5.2 → strobengine-0.6.1}/src/report/mod.rs +0 -0
  77. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/__init__.py +0 -0
  78. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/constants.py +0 -0
  79. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/py.typed +0 -0
  80. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/reporting/__init__.py +0 -0
  81. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/reporting/assets/chart.min.js +0 -0
  82. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/reporting/baseline.py +0 -0
  83. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/reporting/csv_report.py +0 -0
  84. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/reporting/junit_report.py +0 -0
  85. {strobengine-0.5.2 → strobengine-0.6.1}/src/strobengine/reporting/markdown_report.py +0 -0
  86. {strobengine-0.5.2 → strobengine-0.6.1}/tests/__init__.py +0 -0
  87. {strobengine-0.5.2 → strobengine-0.6.1}/tests/conftest.py +0 -0
  88. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/__init__.py +0 -0
  89. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/conftest.py +0 -0
  90. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/mock_server.py +0 -0
  91. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/test_cli_interface.py +0 -0
  92. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/test_http3_e2e.py +0 -0
  93. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/test_http_scenarios.py +0 -0
  94. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/test_performance_sanity.py +0 -0
  95. {strobengine-0.5.2 → strobengine-0.6.1}/tests/e2e/test_persistence_e2e.py +0 -0
  96. {strobengine-0.5.2 → strobengine-0.6.1}/tests/fixtures/service.proto +0 -0
  97. {strobengine-0.5.2 → strobengine-0.6.1}/tests/test_cli.py +0 -0
  98. {strobengine-0.5.2 → strobengine-0.6.1}/tests/test_logging.py +0 -0
@@ -4,6 +4,68 @@ All notable changes to `strobengine` will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.1] - 2026-09-13
8
+
9
+ ### Bug Fixes
10
+
11
+ - *(lib)* Remove unnecessary clippy::too_many_arguments suppression
12
+
13
+ ### Features
14
+
15
+ - *(reporter)* Use dynamic terminal width for histogram bar rendering
16
+
17
+ ### Refactoring
18
+
19
+ - *(schema)* Derive LatencyPercentiles from TestSummary via From impl
20
+ - *(engine)* Clone TestSummary before enrichment to prevent in-place mutation
21
+ ## [0.6.0] - 2026-09-09
22
+
23
+ ### Bug Fixes
24
+
25
+ - *(pyi)* Add QuicMetrics, SseMetrics stubs and missing TestSummary properties
26
+ - *(metrics)* Use serde_json for to_json and add protocol fields to schema
27
+ - *(reporter)* Include protocol sections in JSON report artifact
28
+ - *(reporter)* Use correct chaos_faults structure in JSON artifact
29
+
30
+ ### Documentation
31
+
32
+ - *(cli)* Document known CLI option duplication across subcommands
33
+ - Update documentation for QuicMetrics and SseMetrics
34
+ - Update roadmap and report schema for chaos fault tracking
35
+ - Update roadmap and report schema for extended latency statistics
36
+
37
+ ### Features
38
+
39
+ - *(metrics)* Add QuicMetrics, SseMetrics, and connection latency to TestSummary
40
+ - *(aggregator)* Aggregate connection latency, QUIC, and SSE metrics in execute_test
41
+ - *(chaos)* Add ChaosMetrics struct and ChaosFault name helper
42
+ - *(metrics)* Add chaos fields to RequestMetric and TestSummary
43
+ - *(protocols)* Set chaos_fault on RequestMetric in all engines
44
+ - *(aggregator)* Collect chaos metrics in existing Tokio loop
45
+ - *(reports)* Include chaos metrics in JSON report artifact
46
+ - *(metrics)* Add latency jitter, p99.99, and histogram bucketing
47
+ - *(reporter)* Expose extended latency stats in terminal and artifact output
48
+
49
+ ### Miscellaneous Tasks
50
+
51
+ - *(release)* Bump version to 0.6.0
52
+
53
+ ### Refactoring
54
+
55
+ - *(lib)* Extract spawn_worker helper from execute_test
56
+ - *(config)* Add protocol sub-structs and for_protocol_detection
57
+ - *(lib)* Use for_protocol_detection in run_load_profiles
58
+ - *(cli)* Extract ExportOptions dataclass and deduplicate _output_results
59
+ - *(cli)* Replace 27-param _build_request_options with **kwargs
60
+ - *(reporting)* Extract inline HTML template to separate file
61
+ - *(engine)* Replace nested ternary with dictionary WsMode mapping
62
+
63
+ ### Testing
64
+
65
+ - *(report)* Update TestSummary test fixture for new metric fields
66
+ - *(e2e)* Add test for SSE metrics JSON export
67
+ - *(e2e)* Add chaos fault tracking verification tests
68
+ - *(e2e)* Add latency stats JSON and terminal output E2E tests
7
69
  ## [0.5.2] - 2026-09-02
8
70
 
9
71
  ### Bug Fixes
@@ -13,6 +75,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
75
  - *(reporter)* Replace hardcoded version string with dynamic resolution
14
76
  - *(reporting)* Validate baseline artifact structure in compute_comparison
15
77
 
78
+ ### Miscellaneous Tasks
79
+
80
+ - *(release)* Bump version to 0.5.2
81
+
16
82
  ### Refactoring
17
83
 
18
84
  - *(lib)* Remove unnecessary clones on config.body, config.form, config.headers
@@ -1785,7 +1785,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
1785
1785
 
1786
1786
  [[package]]
1787
1787
  name = "strobengine"
1788
- version = "0.5.2"
1788
+ version = "0.6.1"
1789
1789
  dependencies = [
1790
1790
  "async-trait",
1791
1791
  "base64",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "strobengine"
3
- version = "0.5.2"
3
+ version = "0.6.1"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: strobengine
3
- Version: 0.5.2
3
+ Version: 0.6.1
4
4
  Requires-Dist: rich>=13.0.0
5
5
  Requires-Dist: typer>=0.27.0
6
6
  Requires-Dist: jinja2>=3.1
@@ -123,7 +123,15 @@ The `--http3` flag enables HTTP/3 mode explicitly.
123
123
  ## Metrics
124
124
 
125
125
  HTTP/3 load tests produce the same `TestSummary` metrics as HTTP, plus
126
- QUIC-specific fields:
126
+ aggregated QUIC metrics in `TestSummary.quic`:
127
+
128
+ | Field | Description |
129
+ |-------|-------------|
130
+ | `zero_rtt_accepted_count` | Number of successful 0-RTT connections |
131
+ | `retransmissions` | Total QUIC packet retransmissions |
132
+ | `avg_handshake_ms` | Average QUIC handshake duration in milliseconds |
133
+
134
+ Per-iteration fields available in `RequestMetric`:
127
135
 
128
136
  | Metric | Description |
129
137
  |--------|-------------|
@@ -138,6 +146,7 @@ QUIC-specific fields:
138
146
  | `max_latency_ms` | Maximum latency |
139
147
  | `total_bytes_received` | Total bytes received |
140
148
  | `status_codes` | Status code distribution (200=success, 0=network error) |
149
+ | `avg_connection_latency_us` | Average connection establishment latency in microseconds |
141
150
  | `quic_handshake_us` | QUIC handshake duration (persistent sessions) |
142
151
  | `quic_0rtt_used` | Whether 0-RTT was accepted (persistent sessions) |
143
152
  | `quic_retransmits` | Packet retransmission count since last iteration |
@@ -40,8 +40,13 @@ These flags appear on all three subcommands (`load`, `stress`, `spike`):
40
40
  | `cli_options` | `method`, `concurrency`, `timeout_secs`, `chaos`, `chaos_rate`, `body`, `headers` |
41
41
  | `system_info` | `hostname`, `platform`, `version` |
42
42
  | `summary` | `total_requests`, `successful_requests`, `failed_requests`, `rps`, `bytes_transferred` |
43
- | `latency_percentiles` | `p50_us`, `p90_us`, `p95_us`, `p99_us`, `min_us`, `max_us`, `mean_us` |
43
+ | `latency_percentiles` | `p50_us`, `p90_us`, `p95_us`, `p99_us`, `p99_99_us`, `min_us`, `max_us`, `mean_us`, `std_dev_us` |
44
+ | `latency_histogram` | Bucket distribution (e.g., `"<1ms": 1240`, `"1-5ms": 750`, ...) |
44
45
  | `error_breakdown` | Status code string → count (e.g., `"200": 950, "500": 50`) |
46
+ | `avg_connection_latency_us` | Average connection establishment latency in microseconds |
47
+ | `quic` | `zero_rtt_accepted_count`, `retransmissions`, `avg_handshake_ms` *(HTTP/3 only)* |
48
+ | `sse` | `total_events_received`, `avg_ttfb_ms` *(SSE only)* |
49
+ | `chaos_faults` | `injected_total`, `by_type` *(chaos-enabled runs only)* |
45
50
 
46
51
  All latency values are stored in **microseconds**.
47
52
 
@@ -58,7 +63,18 @@ All latency values are stored in **microseconds**.
58
63
  },
59
64
  "summary": { "total_requests": 1500, "successful_requests": 1485, "failed_requests": 15, "rps": 50.0, "bytes_transferred": 524288 },
60
65
  "latency_percentiles": { "p50_us": 1200.0, "p90_us": 3500.0, "p95_us": 5200.0, "p99_us": 9800.0, "min_us": 200.0, "max_us": 15000.0, "mean_us": 2800.0 },
61
- "error_breakdown": { "200": 1485, "500": 15 }
66
+ "error_breakdown": { "200": 1485, "500": 15 },
67
+ "avg_connection_latency_us": 850.0,
68
+ "quic": null,
69
+ "sse": null,
70
+ "chaos_faults": {
71
+ "injected_total": 12,
72
+ "by_type": {
73
+ "ConnectionDrop": 5,
74
+ "LatencySpike": 4,
75
+ "CorruptedPayload": 3
76
+ }
77
+ }
62
78
  }
63
79
  ```
64
80
 
@@ -87,18 +87,18 @@ This document outlines the planned trajectory and upcoming feature epics for **s
87
87
  ### Epic: Advanced Metrics & Observability
88
88
  *Target Focus: Deep Performance Insights & System Monitoring*
89
89
 
90
- - [ ] **Aggregate Existing Protocol Metrics** `[v0.6.0]`
91
- - [ ] Forward `connection_latency_us`, `quic_*`, `sse_*` fields through aggregator to `TestSummary`
92
- - [ ] Add protocol-specific sections to JSON report artifact
93
-
94
- - [ ] **Chaos Fault Tracking** `[v0.6.0]`
95
- - [ ] Count injected faults by type (ConnectionDrop, LatencySpike, etc.)
96
- - [ ] Include fault breakdown in JSON report artifact
97
-
98
- - [ ] **Extended Latency Statistics** `[v0.6.0]`
99
- - [ ] Compute latency jitter (standard deviation)
100
- - [ ] Add p99.99 percentile
101
- - [ ] Add latency histogram buckets for distribution visualization
90
+ - [x] **Aggregate Existing Protocol Metrics** `[v0.6.0]`
91
+ - [x] Forward `connection_latency_us`, `quic_*`, `sse_*` fields through aggregator to `TestSummary`
92
+ - [x] Add protocol-specific sections to JSON report artifact
93
+
94
+ - [x] **Chaos Fault Tracking** `[v0.6.0]`
95
+ - [x] Count injected faults by type (ConnectionDrop, LatencySpike, etc.)
96
+ - [x] Include fault breakdown in JSON report artifact
97
+
98
+ - [x] **Extended Latency Statistics** `[v0.6.0]`
99
+ - [x] Compute latency jitter (standard deviation)
100
+ - [x] Add p99.99 percentile
101
+ - [x] Add latency histogram buckets for distribution visualization
102
102
 
103
103
  - [ ] **Client System Resource Monitoring** `[v0.7.0]`
104
104
  - [ ] Track engine CPU %, memory (RSS), thread count during test
@@ -141,7 +141,14 @@ connection.
141
141
  ## Metrics
142
142
 
143
143
  SSE load tests produce the same `TestSummary` metrics as HTTP, plus
144
- SSE-specific fields:
144
+ aggregated SSE metrics in `TestSummary.sse`:
145
+
146
+ | Field | Description |
147
+ |-------|-------------|
148
+ | `total_events_received` | Total SSE events received across all iterations |
149
+ | `avg_ttfb_ms` | Average time to first event in milliseconds |
150
+
151
+ Per-iteration fields available in `RequestMetric`:
145
152
 
146
153
  | Metric | Description |
147
154
  |--------|-------------|
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "strobengine"
3
- version = "0.5.2"
3
+ version = "0.6.1"
4
4
  description = "A load testing framework with a Python UI and a bare-metal Rust engine core."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -31,6 +31,7 @@ features = ["pyo3/extension-module"]
31
31
  module-name = "strobengine._strobengine"
32
32
  python-packages = ["strobengine"]
33
33
  python-source = "src"
34
+ include = ["src/strobengine/reporting/assets/*"]
34
35
 
35
36
  [tool.uv]
36
37
  cache-keys = [{ file = "pyproject.toml" }, { file = "src/**/*.rs" }, { file = "Cargo.toml" }, { file = "Cargo.lock" }]
@@ -1,3 +1,5 @@
1
+ use std::collections::HashMap;
2
+
1
3
  pub const DEFAULT_CHAOS_RATE: f32 = 0.1;
2
4
 
3
5
  #[derive(Debug, Clone, Copy)]
@@ -8,6 +10,37 @@ pub enum ChaosFault {
8
10
  ConnectionDrop,
9
11
  }
10
12
 
13
+ impl ChaosFault {
14
+ pub fn name(&self) -> &'static str {
15
+ match self {
16
+ ChaosFault::LatencySpike { .. } => "LatencySpike",
17
+ ChaosFault::CorruptedPayload => "CorruptedPayload",
18
+ ChaosFault::MetadataCorruption => "MetadataCorruption",
19
+ ChaosFault::ConnectionDrop => "ConnectionDrop",
20
+ }
21
+ }
22
+ }
23
+
24
+ #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
25
+ pub struct ChaosMetrics {
26
+ pub total_injected: u64,
27
+ pub faults_by_type: HashMap<String, u64>,
28
+ }
29
+
30
+ impl ChaosMetrics {
31
+ pub fn new() -> Self {
32
+ Self::default()
33
+ }
34
+
35
+ pub fn record_fault(&mut self, fault: &ChaosFault) {
36
+ self.total_injected += 1;
37
+ *self
38
+ .faults_by_type
39
+ .entry(fault.name().to_string())
40
+ .or_insert(0) += 1;
41
+ }
42
+ }
43
+
11
44
  #[derive(Debug, Clone, Copy, Default)]
12
45
  pub struct ChaosEngine {
13
46
  pub enabled: bool,
@@ -3,6 +3,57 @@ use std::time::Duration;
3
3
  use crate::chaos::DEFAULT_CHAOS_RATE;
4
4
  use pyo3::prelude::*;
5
5
 
6
+ /// WebSocket protocol options (internal, not exposed to Python).
7
+ #[derive(Debug, Clone, Default)]
8
+ #[allow(dead_code)]
9
+ pub(crate) struct WsOptions {
10
+ pub mode: WsMode,
11
+ pub payload: Option<String>,
12
+ pub persistent: bool,
13
+ pub keepalive_secs: Option<u64>,
14
+ pub max_messages: Option<u64>,
15
+ pub role: Option<String>,
16
+ pub publish_interval_ms: Option<u64>,
17
+ pub subscribers: Option<usize>,
18
+ }
19
+
20
+ /// gRPC protocol options (internal, not exposed to Python).
21
+ #[derive(Debug, Clone, Default)]
22
+ #[allow(dead_code)]
23
+ pub(crate) struct GrpcOptions {
24
+ pub service: Option<String>,
25
+ pub method: Option<String>,
26
+ pub payload: Option<String>,
27
+ pub deadline_ms: Option<u64>,
28
+ pub proto_path: Option<String>,
29
+ pub use_reflection: bool,
30
+ }
31
+
32
+ /// HTTP/3 + QUIC options (internal, not exposed to Python).
33
+ #[derive(Debug, Clone, Default)]
34
+ #[allow(dead_code)]
35
+ pub(crate) struct Http3Options {
36
+ pub enabled: bool,
37
+ pub zero_rtt: bool,
38
+ pub max_idle_timeout_ms: Option<u64>,
39
+ }
40
+
41
+ /// SSE streaming options (internal, not exposed to Python).
42
+ #[derive(Debug, Clone, Default)]
43
+ #[allow(dead_code)]
44
+ pub(crate) struct SseOptions {
45
+ pub enabled: bool,
46
+ pub max_events: Option<u64>,
47
+ }
48
+
49
+ /// Report output options (internal, not exposed to Python).
50
+ #[derive(Debug, Clone, Default)]
51
+ #[allow(dead_code)]
52
+ pub(crate) struct OutputOptions {
53
+ pub output_dir: Option<String>,
54
+ pub no_save: bool,
55
+ }
56
+
6
57
  #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7
58
  #[pyclass(from_py_object)]
8
59
  pub enum WsMode {
@@ -208,6 +259,52 @@ impl TestConfig {
208
259
  }
209
260
  }
210
261
 
262
+ impl TestConfig {
263
+ /// Minimal config for protocol detection (used by `run_load_profiles`).
264
+ /// Replaces the 32-line positional call with a concise factory.
265
+ pub fn for_protocol_detection(
266
+ url: String,
267
+ concurrency: usize,
268
+ duration_secs: u64,
269
+ timeout_secs: u64,
270
+ ) -> Self {
271
+ Self::new(
272
+ url,
273
+ concurrency,
274
+ duration_secs,
275
+ timeout_secs,
276
+ false,
277
+ DEFAULT_CHAOS_RATE,
278
+ false,
279
+ "GET",
280
+ None,
281
+ None,
282
+ None,
283
+ WsMode::Handshake,
284
+ None,
285
+ None,
286
+ None,
287
+ None,
288
+ None,
289
+ None,
290
+ false,
291
+ false,
292
+ None,
293
+ None,
294
+ None,
295
+ None,
296
+ None,
297
+ false,
298
+ false,
299
+ None,
300
+ false,
301
+ None,
302
+ None,
303
+ false,
304
+ )
305
+ }
306
+ }
307
+
211
308
  #[derive(Debug, Clone)]
212
309
  #[pyclass(from_py_object)]
213
310
  pub enum LoadProfile {