Autonomous API QA agent — define actions and invariants, explore every state path automatically.
Project description
VenomQA
Autonomous QA agent that exhaustively explores APIs — define actions and invariants, let VenomQA find every bug sequence your linear tests miss.
Install
python3 -m venv .venv
source .venv/bin/activate
pip install venomqa
How It Works
Instead of writing linear test scripts, you give VenomQA:
- Actions — things that can happen (create issue, close issue, create refund…)
- Invariants — rules that must always hold (open issues never contain closed ones, refund ≤ payment)
VenomQA explores every reachable state sequence using BFS, checkpointing and rolling back state between branches so each path starts clean.
Quickstart (v1 API)
from venomqa.v1 import Action, Invariant, Agent, World, BFS, Severity
from venomqa.v1.adapters.http import HttpClient
# 1. Define actions — signature is always (api, context)
def create_todo(api, context):
resp = api.post("/todos", json={"title": "Test"})
context.set("todo_id", resp.json()["id"])
return resp
def delete_todo(api, context):
return api.delete(f"/todos/{context.get('todo_id')}")
def list_todos(api, context):
resp = api.get("/todos")
context.set("todos", resp.json())
return resp
# 2. Define invariants — check() receives the World object
def count_is_non_negative(world):
todos = world.context.get("todos") or []
return len(todos) >= 0
invariant = Invariant(
name="count_non_negative",
check=count_is_non_negative,
message="Todo count must never be negative",
severity=Severity.CRITICAL,
)
# 3. Explore
api = HttpClient("http://localhost:8000")
world = World(api=api)
agent = Agent(
world=world,
actions=[
Action(name="create_todo", execute=create_todo),
Action(name="delete_todo", execute=delete_todo),
Action(name="list_todos", execute=list_todos),
],
invariants=[invariant],
strategy=BFS(),
max_steps=200,
)
result = agent.explore()
print(f"States: {result.states_visited}, Violations: {len(result.violations)}")
for v in result.violations:
print(f" [{v.severity.value.upper()}] {v.invariant_name}: {v.message}")
Core Concepts
| Concept | What it is |
|---|---|
Action |
A callable (api, context) -> response that mutates or reads API state |
Invariant |
A rule (world) -> bool checked after every action |
World |
Sandbox owning the HTTP client + rollbackable systems + shared context |
Agent |
Orchestrates exploration using a strategy (BFS, DFS, Random…) |
Context |
Key-value store shared across actions — use .set() / .get() |
Violation |
A recorded invariant failure with severity + reproduction path |
Action Signatures
Actions always receive (api, context) in that order:
# Minimal — no context needed
def health_check(api, context):
return api.get("/health")
# Read from context (set by a previous action)
def get_item(api, context):
item_id = context.get("item_id")
return api.get(f"/items/{item_id}")
# Write to context for downstream actions
def create_item(api, context):
resp = api.post("/items", json={"name": "Test"})
context.set("item_id", resp.json()["id"])
return resp
Note:
contextis aContextobject, not a dict. Usecontext.set(key, val)andcontext.get(key)— notcontext[key].
Invariant Signatures
Invariants receive a single World argument:
# Access shared context
def ids_are_set(world):
return world.context.has("user_id") and world.context.has("item_id")
# Access the API client directly
def api_is_reachable(world):
resp = world.api.get("/health")
return resp.status_code == 200
invariant = Invariant(
name="ids_set",
check=ids_are_set,
message="user_id and item_id must be set", # 'message', not 'description'
severity=Severity.HIGH,
)
Note: The field is
message=, notdescription=.
Rollback / Branching
VenomQA checkpoints and rolls back state between paths. Adapters that support rollback:
| System | Mechanism |
|---|---|
| PostgreSQL | SAVEPOINT / ROLLBACK TO SAVEPOINT |
| Redis | DUMP + FLUSHALL + RESTORE |
| In-memory (queue, mail, storage) | Copy + restore |
| Custom | Subclass MockHTTPServer (3-method interface) |
from venomqa.v1.adapters.postgres import PostgresAdapter
from venomqa.v1.adapters.redis import RedisAdapter
world = World(
api=HttpClient("http://localhost:8000"),
systems={
"db": PostgresAdapter("postgresql://localhost/mydb"),
"cache": RedisAdapter("redis://localhost:6379"),
},
)
Exploration Strategies
from venomqa.v1 import BFS, DFS, Random, CoverageGuided, Weighted
agent = Agent(..., strategy=BFS()) # breadth-first (default, best for bug finding)
agent = Agent(..., strategy=DFS()) # depth-first
agent = Agent(..., strategy=CoverageGuided()) # maximize state coverage
Reporters
from venomqa.v1 import ConsoleReporter, HTMLTraceReporter, JSONReporter
# Console output
ConsoleReporter().report(result)
# HTML — report() returns a string, write it yourself
html = HTMLTraceReporter()
with open("trace.html", "w") as f:
f.write(html.report(result)) # D3 force-graph of the state space
Working Example
examples/github_stripe_qa/ contains a full multi-API example with two deliberately planted bugs that VenomQA catches automatically:
cd examples/github_stripe_qa
python3 main.py
Development Setup
git clone https://github.com/namanag97/venomqa
cd venomqa
pip install -e ".[dev]"
make test # all unit tests
make lint # ruff
make typecheck # mypy --strict
make ci # lint + typecheck + coverage
CLI
venomqa run # run explorations
venomqa doctor # system diagnostics
venomqa llm-docs # print LLM context document (paste into any AI assistant)
venomqa --help
Using with an AI Assistant
Run venomqa llm-docs to get a complete context document you can paste into ChatGPT, Claude, Cursor, or any AI assistant. It includes all correct API signatures, patterns, and examples so the AI can help you write VenomQA tests accurately.
License
MIT — built by Naman Agarwal
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file venomqa-0.2.6.tar.gz.
File metadata
- Download URL: venomqa-0.2.6.tar.gz
- Upload date:
- Size: 2.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adf6a8e28e50dc59cd58bb19396baf26a2611cd98acd7719659491b7d6fc0f9e
|
|
| MD5 |
a4b5636c17184a00e59d964a5ba582f5
|
|
| BLAKE2b-256 |
fc84443b796fac259231672eef7243d29e1f2e1efb6e1df706d8c6e4176cd71b
|
Provenance
The following attestation bundles were made for venomqa-0.2.6.tar.gz:
Publisher:
publish.yml on namanag97/venomqa
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
venomqa-0.2.6.tar.gz -
Subject digest:
adf6a8e28e50dc59cd58bb19396baf26a2611cd98acd7719659491b7d6fc0f9e - Sigstore transparency entry: 956261962
- Sigstore integration time:
-
Permalink:
namanag97/venomqa@93b4bf68d750543d0ea33d1725cc126bd4eaa8d7 -
Branch / Tag:
refs/tags/v0.2.6 - Owner: https://github.com/namanag97
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@93b4bf68d750543d0ea33d1725cc126bd4eaa8d7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file venomqa-0.2.6-py3-none-any.whl.
File metadata
- Download URL: venomqa-0.2.6-py3-none-any.whl
- Upload date:
- Size: 1.0 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f625d34a5ff331c555172dc875c0047c6defd28c05454ba107aa95c075509a4
|
|
| MD5 |
1e2f0732642e5a6a497809f04ae86a76
|
|
| BLAKE2b-256 |
305a6973462bfa1d26b20d4a1a1ac2d431bbc241eed92175939b7b5a501f3b98
|
Provenance
The following attestation bundles were made for venomqa-0.2.6-py3-none-any.whl:
Publisher:
publish.yml on namanag97/venomqa
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
venomqa-0.2.6-py3-none-any.whl -
Subject digest:
4f625d34a5ff331c555172dc875c0047c6defd28c05454ba107aa95c075509a4 - Sigstore transparency entry: 956261965
- Sigstore integration time:
-
Permalink:
namanag97/venomqa@93b4bf68d750543d0ea33d1725cc126bd4eaa8d7 -
Branch / Tag:
refs/tags/v0.2.6 - Owner: https://github.com/namanag97
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@93b4bf68d750543d0ea33d1725cc126bd4eaa8d7 -
Trigger Event:
release
-
Statement type: