Metadata-Version: 2.4
Name: venomqa
Version: 0.4.3
Summary: Autonomous API QA agent — define actions and invariants, explore every state path automatically.
Project-URL: Homepage, https://namanag97.github.io/venomqa
Project-URL: Documentation, https://namanag97.github.io/venomqa
Project-URL: Repository, https://github.com/namanag97/venomqa
Project-URL: Source Code, https://github.com/namanag97/venomqa
Project-URL: Bug Tracker, https://github.com/namanag97/venomqa/issues
Project-URL: Changelog, https://github.com/namanag97/venomqa/blob/main/CHANGELOG.md
Project-URL: Getting Started, https://github.com/namanag97/venomqa/blob/main/SETUP.md
Author-email: Naman Agarwal <naman@venomqa.dev>
Maintainer-email: Naman Agarwal <naman@venomqa.dev>
License-Expression: MIT
License-File: LICENSE
Keywords: api-testing,branch-exploration,checkpoint,e2e-testing,end-to-end,graphql-testing,http-testing,integration-testing,journey-testing,qa,rest-api,state-management,stateful-testing,test-automation,testing
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Testing :: Acceptance
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: click>=8.0.0
Requires-Dist: faker>=18.0.0
Requires-Dist: httpx>=0.25.0
Requires-Dist: mkdocstrings-python>=2.0.2
Requires-Dist: mkdocstrings>=1.0.3
Requires-Dist: psycopg[binary]>=3.1.0
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0.0
Requires-Dist: watchdog>=3.0.0
Provides-Extra: all
Requires-Dist: boto3>=1.26.0; extra == 'all'
Requires-Dist: docker>=6.0.0; extra == 'all'
Requires-Dist: gql>=3.4.0; extra == 'all'
Requires-Dist: graphql-core>=3.2.0; extra == 'all'
Requires-Dist: psycopg[binary]>=3.1.0; extra == 'all'
Requires-Dist: redis>=4.0.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: hypothesis>=6.0.0; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: docker
Requires-Dist: docker>=6.0.0; extra == 'docker'
Provides-Extra: docs
Requires-Dist: mkdocs-autorefs>=0.5.0; extra == 'docs'
Requires-Dist: mkdocs-gen-files>=0.5.0; extra == 'docs'
Requires-Dist: mkdocs-literate-nav>=0.6.0; extra == 'docs'
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
Requires-Dist: mkdocs-section-index>=0.3.0; extra == 'docs'
Requires-Dist: mkdocs>=1.5.0; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.24.0; extra == 'docs'
Provides-Extra: graphql
Requires-Dist: gql>=3.4.0; extra == 'graphql'
Requires-Dist: graphql-core>=3.2.0; extra == 'graphql'
Provides-Extra: postgres
Requires-Dist: psycopg[binary]>=3.1.0; extra == 'postgres'
Provides-Extra: redis
Requires-Dist: redis>=4.0.0; extra == 'redis'
Provides-Extra: s3
Requires-Dist: boto3>=1.26.0; extra == 's3'
Description-Content-Type: text/markdown

# VenomQA

**Autonomous QA agent that exhaustively explores APIs** — define actions and invariants, let VenomQA find every bug sequence your linear tests miss.

[![PyPI version](https://badge.fury.io/py/venomqa.svg)](https://pypi.org/project/venomqa/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://pypi.org/project/venomqa/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

---

## Install

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install venomqa
```

---

## How It Works

Instead of writing linear test scripts, you give VenomQA:

1. **Actions** — things that can happen (create issue, close issue, create refund…)
2. **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)

```python
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:

```python
# 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:** `context` is a `Context` object, not a dict. Use `context.set(key, val)` and `context.get(key)` — not `context[key]`.

---

## Invariant Signatures

Invariants receive a single `World` argument:

```python
# 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=`, not `description=`.

---

## 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) |

```python
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

```python
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

```python
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:

```bash
cd examples/github_stripe_qa
python3 main.py
```

---

## Development Setup

```bash
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

```bash
# V1 stateful exploration (recommended)
venomqa explore journey.py --base-url http://localhost:8000   # run exploration
venomqa validate journey.py                                   # check journey syntax
venomqa record   journey.py --base-url http://localhost:8000  # record + generate skeleton

# General
venomqa run        # run V0 journeys
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](https://github.com/namanag97)
