feat: add load testing infrastructure with Locust#164
Conversation
|
@aanyacloud is attempting to deploy a commit to the s3dfx-cyber's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughThis PR establishes load testing infrastructure by introducing Locust as a development dependency, documenting test scenarios and acceptance criteria, and implementing a TENETLoadUser class with weighted task methods to test health, stats, events, and LLM event endpoints. ChangesLoad Testing Infrastructure Setup
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
tests/load/README.md (2)
14-18: ⚡ Quick winClarify installation instructions to align with development workflow.
The installation section instructs users to manually install Locust via
pip install locust, but the dependency is already declared inrequirements-dev.txt. This could confuse developers about the preferred installation method.📝 Suggested documentation update
## Installation ```bash -pip install locust +pip install -r requirements-dev.txt
+Or if you only need Locust:
+
+bash +pip install locust>=2.31.0 +</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@tests/load/README.mdaround lines 14 - 18, Update the Installation section
in tests/load/README.md to instruct developers to install dependencies via the
project's dev requirements file instead of a direct pip install of Locust;
replace the single-line instructionpip install locustwith guidance to run
pip install -r requirements-dev.txtand add an optional note showing how to
install only Locust (e.g.,pip install locust>=2.31.0) for users who don't
want all dev dependencies.</details> <!-- cr-comment:v1:1eedec5f3d1221f27478cc59 --> --- `30-46`: _⚡ Quick win_ **Provide concrete guidance for executing each test scenario.** The test scenarios describe the goals (sustained load, spike load, concurrent connections) but don't explain how to configure Locust to achieve them. Users need specific parameter values (users, spawn rate, duration) for each scenario. <details> <summary>📝 Suggested documentation addition</summary> ```diff ## Test Scenarios ### Sustained Load Test - Target: 100 requests/second - Duration: 10 minutes + +**Locust configuration:** +```bash +locust -f tests/load/locustfile.py --host=http://localhost:8000 \ + --users 50 --spawn-rate 10 --run-time 10m --headless +``` ### Spike Load Test - Simulate sudden bursts of traffic - Increase users rapidly + +**Locust configuration:** +```bash +locust -f tests/load/locustfile.py --host=http://localhost:8000 \ + --users 200 --spawn-rate 50 --run-time 5m --headless +``` ### Concurrent Connection Test - Run multiple concurrent users - Observe latency and error rates + +**Locust configuration:** +```bash +locust -f tests/load/locustfile.py --host=http://localhost:8000 \ + --users 100 --spawn-rate 20 --run-time 5m --headless +```🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/load/README.md` around lines 30 - 46, Add concrete Locust CLI configurations for each scenario so users can reproduce the tests: update the Sustained Load, Spike Load, and Concurrent Connection sections to include example locust command invocations (e.g., the existing locust -f tests/load/locustfile.py --host=... command) with explicit --users, --spawn-rate, and --run-time values (suggested: Sustained: --users 50 --spawn-rate 10 --run-time 10m --headless; Spike: --users 200 --spawn-rate 50 --run-time 5m --headless; Concurrent: --users 100 --spawn-rate 20 --run-time 5m --headless), and add a brief note on what each parameter controls so readers know how to tune them.tests/load/locustfile.py (4)
6-7: ⚡ Quick winAdd host attribute to support default target configuration.
The
TENETLoadUserclass lacks ahostattribute, requiring users to always specify--hoston the command line. Providing a sensible default improves developer experience while still allowing override.🔧 Proposed addition
class TENETLoadUser(HttpUser): + host = os.getenv("TENET_HOST", "http://localhost:8000") wait_time = between(1, 3)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/load/locustfile.py` around lines 6 - 7, TENETLoadUser lacks a default target; add a host attribute on the TENETLoadUser class (e.g., host = "http://localhost:8000" or another sensible default) so locust has a default target while still allowing CLI --host to override it; update the TENETLoadUser class definition to include this host attribute (reference: TENETLoadUser, HttpUser).
14-20: 💤 Low valueRemove unnecessary authentication from health check endpoint.
The
health_checktask sends thex-api-keyheader to/health, but health endpoints typically don't require authentication. This adds unnecessary overhead and may not reflect realistic production traffic patterns.⚡ Optional optimization
`@task`(3) def health_check(self): - self.client.get( - "/health", - headers=self.headers, - name="GET /health" - ) + self.client.get("/health", name="GET /health")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/load/locustfile.py` around lines 14 - 20, The health_check task is sending authentication via self.headers when calling self.client.get("/health") which is unnecessary; modify the health_check method (the health_check task that invokes self.client.get) to omit the headers/x-api-key for the "/health" request so the call uses no authentication headers (remove passing self.headers or pass headers=None) and keep the request name "GET /health" intact.
38-56: ⚡ Quick winVary test payload data to exercise realistic code paths.
The
submit_llm_eventtask always submits identical prompts, which doesn't test how the system handles diverse inputs. From services/ingest/app.py:308-358, the endpoint runsquick_heuristic_check(request.prompt)that returnsblocked,risk_score, andverdict—testing with varied prompts ensures realistic load distribution across these code paths.🎲 Proposed enhancement with varied prompts
+import random + +SAMPLE_PROMPTS = [ + "What is machine learning?", + "Explain quantum computing", + "How does blockchain work?", + "Describe neural networks", + "What are the benefits of cloud computing?", +] + # ... `@task`(1) def submit_llm_event(self): payload = { "source_type": "chat", "source_id": "load-test-user", "model": "gpt-4", - "prompt": "This is a load testing request", + "prompt": random.choice(SAMPLE_PROMPTS), "system_prompt": "You are a helpful assistant", "metadata": { "environment": "load-test" } } - self.client.post( + with self.client.post( "/v1/events/llm", json=payload, headers=self.headers, - name="POST /v1/events/llm" - ) + name="POST /v1/events/llm", + catch_response=True + ) as response: + if response.status_code not in (200, 201): + response.failure(f"Got status code {response.status_code}") + elif "event_id" not in response.json(): + response.failure("Missing expected field: event_id") + else: + response.success()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/load/locustfile.py` around lines 38 - 56, The submit_llm_event task always posts the same payload; modify submit_llm_event to generate varied payloads (randomize prompt text from a small list of benign, risky, and blocked examples, vary source_id and model occasionally, and tweak metadata) so the /v1/events/llm endpoint (exercise quick_heuristic_check) sees diverse inputs and triggers different code paths; update the function submit_llm_event to pick a prompt at random and adjust other fields per request to spread load across blocked/risk_score/verdict branches.
30-36: ⚡ Quick winTest query parameters to validate pagination logic.
The
list_eventsendpoint supportslimitandoffsetquery parameters (from services/ingest/app.py:418-455), but the load test always calls it without parameters. Testing pagination validates that the endpoint scales correctly under load.🔄 Suggested enhancement
+import random + # ... `@task`(2) def list_events(self): + limit = random.choice([10, 50, 100]) + offset = random.randint(0, 50) with self.client.get( "/v1/events", + params={"limit": limit, "offset": offset}, headers=self.headers, name="GET /v1/events",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/load/locustfile.py` around lines 30 - 36, The load test's list_events method always calls "/v1/events" with no query params, so it doesn't exercise pagination; update the list_events task in locustfile.py to send randomized limit and offset query parameters (e.g., limit in a sensible range like 1-100 and offset across expected dataset size) on each request to simulate paginated access; construct the request using either a params dict passed to self.client.get or append a query string to "/v1/events", and update the request name to reflect the templated query (so metrics show which pagination values were used). Include a reference comment or tie to services/ingest/app.py pagination behavior for maintainers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/load/locustfile.py`:
- Around line 22-36: Wrap the GET calls in get_stats and list_events with
client.get(..., catch_response=True) and validate the response inside that
context: check resp.status_code (expect 200) and call resp.success() on valid
responses or resp.failure(...) with an informative message on non-200 or
unexpected body; also catch exceptions around the request and call
resp.failure(...) (or mark failure) so Locust records errors for those
authenticated endpoints (functions get_stats and list_events, and the
self.client.get calls using self.headers).
- Line 3: Replace the hardcoded API_KEY constant in tests/load/locustfile.py
with a value read from an environment variable (e.g., TENET_LOAD_TEST_API_KEY);
update the code that references API_KEY so it raises a clear error or exits if
the env var is missing, and ensure any locust task functions (where API_KEY is
used) continue to reference the same symbol (API_KEY) so no other changes are
required; also add a brief README note or comment showing how to export
TENET_LOAD_TEST_API_KEY before running locust.
- Around line 9-12: The class-level mutable dict headers should be converted to
an instance attribute or property to avoid sharing it across Locust users;
update the Locust user class (where headers is declared) to initialize
self.headers in an on_start method (or implement a `@property` that returns a
fresh dict) using API_KEY and "Content-Type": "application/json", and update any
references from headers to self.headers so each user gets its own dict instance
and concurrent modifications won't affect other users.
In `@tests/load/README.md`:
- Around line 20-28: Update the tests/load/README.md to include required host
and auth setup: instruct users to pass --host (example:
--host=http://localhost:8000) when running locust with tests/load/locustfile.py
and add an "Authentication" section telling them to set the API_KEY constant in
locustfile.py to a valid test API key with the needed permissions (ingest for
POST /v1/events/llm and read for GET /v1/stats and GET /v1/events); reference
locustfile.py and the API_KEY constant so it's clear where to configure these
values.
---
Nitpick comments:
In `@tests/load/locustfile.py`:
- Around line 6-7: TENETLoadUser lacks a default target; add a host attribute on
the TENETLoadUser class (e.g., host = "http://localhost:8000" or another
sensible default) so locust has a default target while still allowing CLI --host
to override it; update the TENETLoadUser class definition to include this host
attribute (reference: TENETLoadUser, HttpUser).
- Around line 14-20: The health_check task is sending authentication via
self.headers when calling self.client.get("/health") which is unnecessary;
modify the health_check method (the health_check task that invokes
self.client.get) to omit the headers/x-api-key for the "/health" request so the
call uses no authentication headers (remove passing self.headers or pass
headers=None) and keep the request name "GET /health" intact.
- Around line 38-56: The submit_llm_event task always posts the same payload;
modify submit_llm_event to generate varied payloads (randomize prompt text from
a small list of benign, risky, and blocked examples, vary source_id and model
occasionally, and tweak metadata) so the /v1/events/llm endpoint (exercise
quick_heuristic_check) sees diverse inputs and triggers different code paths;
update the function submit_llm_event to pick a prompt at random and adjust other
fields per request to spread load across blocked/risk_score/verdict branches.
- Around line 30-36: The load test's list_events method always calls
"/v1/events" with no query params, so it doesn't exercise pagination; update the
list_events task in locustfile.py to send randomized limit and offset query
parameters (e.g., limit in a sensible range like 1-100 and offset across
expected dataset size) on each request to simulate paginated access; construct
the request using either a params dict passed to self.client.get or append a
query string to "/v1/events", and update the request name to reflect the
templated query (so metrics show which pagination values were used). Include a
reference comment or tie to services/ingest/app.py pagination behavior for
maintainers.
In `@tests/load/README.md`:
- Around line 14-18: Update the Installation section in tests/load/README.md to
instruct developers to install dependencies via the project's dev requirements
file instead of a direct pip install of Locust; replace the single-line
instruction `pip install locust` with guidance to run `pip install -r
requirements-dev.txt` and add an optional note showing how to install only
Locust (e.g., `pip install locust>=2.31.0`) for users who don't want all dev
dependencies.
- Around line 30-46: Add concrete Locust CLI configurations for each scenario so
users can reproduce the tests: update the Sustained Load, Spike Load, and
Concurrent Connection sections to include example locust command invocations
(e.g., the existing locust -f tests/load/locustfile.py --host=... command) with
explicit --users, --spawn-rate, and --run-time values (suggested: Sustained:
--users 50 --spawn-rate 10 --run-time 10m --headless; Spike: --users 200
--spawn-rate 50 --run-time 5m --headless; Concurrent: --users 100 --spawn-rate
20 --run-time 5m --headless), and add a brief note on what each parameter
controls so readers know how to tune them.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: bf3eaf50-a609-4756-95a1-165960ca1801
📒 Files selected for processing (3)
requirements-dev.txttests/load/README.mdtests/load/locustfile.py
| @@ -0,0 +1,56 @@ | |||
| from locust import HttpUser, task, between | |||
|
|
|||
| API_KEY = "test-api-key" | |||
There was a problem hiding this comment.
Replace hardcoded API key with environment variable.
The hardcoded test-api-key constant poses security and configuration risks: it may be committed to version control and cannot be changed per environment without modifying code. From the relevant code snippets (services/ingest/app.py), the API requires valid authenticated keys with specific permissions (ingest for POST /v1/events/llm, read for GET endpoints).
🔐 Proposed fix using environment variable
+import os
from locust import HttpUser, task, between
-API_KEY = "test-api-key"
+API_KEY = os.getenv("TENET_LOAD_TEST_API_KEY", "test-api-key")Then document in README.md:
export TENET_LOAD_TEST_API_KEY="your-valid-test-key"
locust -f tests/load/locustfile.py --host=http://localhost:8000📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| API_KEY = "test-api-key" | |
| import os | |
| from locust import HttpUser, task, between | |
| API_KEY = os.getenv("TENET_LOAD_TEST_API_KEY", "test-api-key") |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/load/locustfile.py` at line 3, Replace the hardcoded API_KEY constant
in tests/load/locustfile.py with a value read from an environment variable
(e.g., TENET_LOAD_TEST_API_KEY); update the code that references API_KEY so it
raises a clear error or exits if the env var is missing, and ensure any locust
task functions (where API_KEY is used) continue to reference the same symbol
(API_KEY) so no other changes are required; also add a brief README note or
comment showing how to export TENET_LOAD_TEST_API_KEY before running locust.
| headers = { | ||
| "x-api-key": API_KEY, | ||
| "Content-Type": "application/json", | ||
| } |
There was a problem hiding this comment.
Convert mutable class attribute to property or instance attribute.
Ruff correctly flags that defining headers as a mutable dict at class level means all instances share the same dict object. If any task modifies self.headers, it affects all concurrent users.
🛡️ Recommended fix using on_start
- headers = {
- "x-api-key": API_KEY,
- "Content-Type": "application/json",
- }
+
+ def on_start(self):
+ """Initialize headers per user instance."""
+ self.headers = {
+ "x-api-key": API_KEY,
+ "Content-Type": "application/json",
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| headers = { | |
| "x-api-key": API_KEY, | |
| "Content-Type": "application/json", | |
| } | |
| def on_start(self): | |
| """Initialize headers per user instance.""" | |
| self.headers = { | |
| "x-api-key": API_KEY, | |
| "Content-Type": "application/json", | |
| } |
🧰 Tools
🪛 Ruff (0.15.15)
[warning] 9-12: Mutable default value for class attribute
(RUF012)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/load/locustfile.py` around lines 9 - 12, The class-level mutable dict
headers should be converted to an instance attribute or property to avoid
sharing it across Locust users; update the Locust user class (where headers is
declared) to initialize self.headers in an on_start method (or implement a
`@property` that returns a fresh dict) using API_KEY and "Content-Type":
"application/json", and update any references from headers to self.headers so
each user gets its own dict instance and concurrent modifications won't affect
other users.
Source: Linters/SAST tools
| @task(2) | ||
| def get_stats(self): | ||
| self.client.get( | ||
| "/v1/stats", | ||
| headers=self.headers, | ||
| name="GET /v1/stats" | ||
| ) | ||
|
|
||
| @task(2) | ||
| def list_events(self): | ||
| self.client.get( | ||
| "/v1/events", | ||
| headers=self.headers, | ||
| name="GET /v1/events" | ||
| ) |
There was a problem hiding this comment.
Add response validation and error handling to authenticated endpoints.
The get_stats and list_events tasks don't validate response status codes or catch exceptions. For load testing, tracking success/failure rates is essential to meet the acceptance criteria (error rate < 0.1%).
✅ Proposed enhancement with response validation
`@task`(2)
def get_stats(self):
- self.client.get(
+ with self.client.get(
"/v1/stats",
headers=self.headers,
- name="GET /v1/stats"
- )
+ name="GET /v1/stats",
+ catch_response=True
+ ) as response:
+ if response.status_code != 200:
+ response.failure(f"Got status code {response.status_code}")
+ elif "total_events" not in response.json():
+ response.failure("Missing expected field: total_events")
+ else:
+ response.success()
`@task`(2)
def list_events(self):
- self.client.get(
+ with self.client.get(
"/v1/events",
headers=self.headers,
- name="GET /v1/events"
- )
+ name="GET /v1/events",
+ catch_response=True
+ ) as response:
+ if response.status_code != 200:
+ response.failure(f"Got status code {response.status_code}")
+ elif "events" not in response.json():
+ response.failure("Missing expected field: events")
+ else:
+ response.success()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/load/locustfile.py` around lines 22 - 36, Wrap the GET calls in
get_stats and list_events with client.get(..., catch_response=True) and validate
the response inside that context: check resp.status_code (expect 200) and call
resp.success() on valid responses or resp.failure(...) with an informative
message on non-200 or unexpected body; also catch exceptions around the request
and call resp.failure(...) (or mark failure) so Locust records errors for those
authenticated endpoints (functions get_stats and list_events, and the
self.client.get calls using self.headers).
| ## Running Tests | ||
|
|
||
| ```bash | ||
| locust -f tests/load/locustfile.py | ||
| ``` | ||
|
|
||
| Then open: | ||
|
|
||
| http://localhost:8089 |
There was a problem hiding this comment.
Document required host configuration and API key setup.
The running instructions omit critical setup details: users must specify the target host URL via --host and ensure a valid API key is configured. Without these, the load tests will fail.
📝 Suggested documentation addition
## Running Tests
+### Prerequisites
+
+1. Ensure the TENET AI ingest service is running
+2. Configure a valid API key with `ingest` and `read` permissions (see Authentication section below)
+
+### Execute
+
```bash
-locust -f tests/load/locustfile.py
+locust -f tests/load/locustfile.py --host=http://localhost:8000Then open:
http://localhost:8089
+
+### Authentication
+
+Update the API_KEY constant in locustfile.py with a valid test API key that has:
+- ingest permission for POST /v1/events/llm
+- read permission for GET /v1/stats and GET /v1/events
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @tests/load/README.md around lines 20 - 28, Update the tests/load/README.md
to include required host and auth setup: instruct users to pass --host (example:
--host=http://localhost:8000) when running locust with tests/load/locustfile.py
and add an "Authentication" section telling them to set the API_KEY constant in
locustfile.py to a valid test API key with the needed permissions (ingest for
POST /v1/events/llm and read for GET /v1/stats and GET /v1/events); reference
locustfile.py and the API_KEY constant so it's clear where to configure these
values.
</details>
<!-- fingerprinting:phantom:poseidon:puma -->
<!-- cr-comment:v1:fc7fe9e7eef0cf670bb72c46 -->
<!-- This is an auto-generated comment by CodeRabbit -->
There was a problem hiding this comment.
1 issue found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/load/locustfile.py">
<violation number="1" location="tests/load/locustfile.py:3">
P1: Hardcoding `test-api-key` here makes the load test brittle and likely unauthorized under the project’s expected API-key configuration, so the script may only measure 401/403 responses instead of actual ingest performance.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| @@ -0,0 +1,56 @@ | |||
| from locust import HttpUser, task, between | |||
|
|
|||
| API_KEY = "test-api-key" | |||
There was a problem hiding this comment.
P1: Hardcoding test-api-key here makes the load test brittle and likely unauthorized under the project’s expected API-key configuration, so the script may only measure 401/403 responses instead of actual ingest performance.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/load/locustfile.py, line 3:
<comment>Hardcoding `test-api-key` here makes the load test brittle and likely unauthorized under the project’s expected API-key configuration, so the script may only measure 401/403 responses instead of actual ingest performance.</comment>
<file context>
@@ -0,0 +1,56 @@
+from locust import HttpUser, task, between
+
+API_KEY = "test-api-key"
+
+
</file context>
|
Implemented Issue #131. CI/test and CodeQL checks are passing. |
Summary
Added load testing infrastructure for the TENET AI ingest service using Locust.
Key Changes
tests/load/locustfile.pyfor Locust-based load testingtests/load/README.mdwith load testing instructions and scenariosrequirements-dev.txtRelated Issue
Fixes #131
Type of Change
Screenshots / Logs (Optional)
N/A
How Has This Been Tested?
Added Locust load-testing configuration
Verified Python syntax for
locustfile.pyVerified project structure and test documentation
Unit tests
Integration tests
Manual testing
Checklist
Additional Notes (Optional)
This PR introduces the initial load-testing framework for the ingest service. Future performance testing can be executed using Locust to validate latency, throughput, and error-rate targets under different traffic patterns.
Summary by cubic
Add load testing for the ingest service using
locust. Includes scripts and docs to run sustained, spike, and concurrent tests against core endpoints to measure latency, throughput, and errors (addresses #131).New Features
tests/load/locustfile.pywith tasks for GET/health, GET/v1/stats, GET/v1/events, POST/v1/events/llm.tests/load/README.mdwith run instructions, scenarios, and metrics (P50/P95/P99, throughput, error rate) plus acceptance criteria (P95 < 500 ms, error rate < 0.1%).Dependencies
locust>=2.31.0torequirements-dev.txt.Written for commit 6a6442e. Summary will update on new commits.
Summary by CodeRabbit
Tests
Documentation
Chores