probeflow
Pytest for HTTP APIs: a local CLI that runs Git-diffable .http request files
as an enforceable test suite. No accounts, cloud sync, GUI, or team workspace service.
Overview
probeflow reads IntelliJ / VS Code REST Client request files, executes them, checks explicit response assertions, and fails CI when the contract breaks.
Installation
# From source (recommended for development)
pip install -e ".[dev]"
# Or install from PyPI
pip install probeflow
After installation the probeflow command is available in your PATH.
Quick Start
Create a .http file:
### @name = getUsers
GET https://api.example.com/users
Accept: application/json
###
### @name = createUser
POST https://api.example.com/users
Content-Type: application/json
{
"name": "Alice",
"email": "alice@example.com"
}
Run it:
probeflow run requests.http
probeflow test requests.http # Execute as an enforceable test suite
Commands
probeflow run
Execute requests from a .http file interactively.
probeflow run requests.http # Run all requests
probeflow run requests.http --index 0 # Run one request by 0-based index
probeflow run requests.http --env dev # Use .env.dev for variable substitution
probeflow run requests.http --headers # Show response headers
probeflow run requests.http --timeout 60 # Set timeout in seconds (default: 30)
probeflow run requests.http --quiet # Suppress output, show errors only
probeflow run requests.http --check-assertions # Evaluate assertions after each request
Response chaining works in run too — a named request's response is available
as {{name.response.body.$.field}} in subsequent requests within the same file.
Use --check-assertions to evaluate assertions during development (exit code 1
on failure). For CI-enforced assertions, use test.
probeflow test
Execute requests in declaration order and evaluate every ### @assert block.
Exits 0 only when every request completes and every assertion passes.
probeflow test tests/api.http
probeflow test tests/api.http --env staging
probeflow test tests/api.http --json results.json --junit-xml results.xml
probeflow validate
Check a .http file for syntax errors without executing any requests.
Request names (### @name = ...) must be unique within a file. Duplicate names
produce an error. In test mode, unresolved variables (variables not found in
.env files, system environment, or response chains) are treated as errors.
probeflow validate requests.http
probeflow format
Normalize a .http file for consistent style (method casing, header formatting,
separator spacing).
probeflow format requests.http # Format in-place
probeflow format requests.http --check # Check only — exit 0 if already formatted
probeflow format requests.http -o out.http # Write to a different file
probeflow version
Show the installed version and implemented grammar version.
probeflow version
GitHub Actions
The test command's non-zero exit status makes a broken API produce a red check:
name: API tests
on: [push, pull_request]
jobs:
api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install -e ".[dev]"
- name: Run API contract tests
run: probeflow test tests/api.http --junit-xml api-results.xml
If tests/api.http contains # status == 200 and the API returns 500, the
step exits 1 and GitHub marks the workflow with a red X.
.http File Format
probeflow follows the IntelliJ / VS Code REST Client .http file format.
All probeflow-specific extensions (### @assert, ### @name, etc.) use constructs
that non-probeflow tools treat as comments or separators, so files remain
compatible with both editors.
Basic Request
GET https://api.example.com/users
Request with Headers and Body
POST https://api.example.com/users
Content-Type: application/json
Accept: application/json
Authorization: Bearer token123
{
"name": "Alice",
"role": "admin"
}
Multiple Requests
Separate requests with ###:
### @name = listUsers
GET https://api.example.com/users
###
### @name = createUser
POST https://api.example.com/users
Content-Type: application/json
{"name": "Bob"}
Named Requests
### @name = healthCheck
GET https://api.example.com/health
Names are used in test output and for response chaining.
Comments
// This is a comment
# This is also a comment
GET https://api.example.com/data
HTTP Version
GET https://api.example.com/data HTTP/1.1
Script Hooks
### @before and ### @after directives point to Python hook functions:
### @name = createResource
### @before = hooks.py:sign_request
POST https://api.example.com/resources
Content-Type: application/json
{"name": "test-resource"}
### @after = hooks.py:verify_signature
Hook references are parsed and validated, but not executed in this release.
The test runner refuses files containing hooks rather than running arbitrary code
implicitly. A future executor will require an explicit --allow-scripts flag.
Supported Methods
GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
Assertions
Add a ### @assert block after any request to make probeflow test enforce
the response contract:
### @name = getUser
GET https://api.example.com/users/1
### @assert
# status == 200
# body.$.name == "Alice"
# body.$.email is string
# body.$.roles is array
# body.$.address exists
# duration < 500ms
# header.Content-Type contains "application/json"
Assertion Syntax
| Target | Example |
|---|---|
| Status code | status == 200 · status in [200, 201] · status < 300 |
| Body field | body.$.name == "Alice" · body.$.count > 0 |
| Body type | body.$.id is number · body.$.tags is array |
| Body existence | body.$.token exists · body.$.error not exists |
| Body pattern | body.$.email matches ".*@example\\.com" |
| Header | header.Content-Type contains "json" · header.X-Id exists |
| Duration | duration < 500ms |
Supported Operators
== != < > <= >= in contains matches exists not exists is
Type Names for is
string · number · boolean · array · object
Response Chaining
Use a preceding named request's response as a variable in later requests:
### @name = login
POST https://api.example.com/auth
Content-Type: application/json
{"username": "alice", "password": "{{auth_password}}"}
###
### @name = getProfile
GET https://api.example.com/me
Authorization: Bearer {{login.response.body.$.token}}
### @assert
# status == 200
# body.$.username == "alice"
Chaining Reference Syntax
| Reference | Resolves to |
|---|---|
{{name.response.status}} |
HTTP status code as a string |
{{name.response.body.$.field}} |
JSONPath value from the response body |
{{name.response.headers.X-Request-Id}} |
Response header value |
Chaining references resolve in declaration order. Referencing a request that hasn't run yet, or one that failed, is a hard error with a clear message.
Environment Variables
.env Files
Place a .env file (or .env.<name>) next to your .http file, or in any
parent directory. ProbeFlow searches upward from the .http file's directory
to find the nearest .env file:
# .env.dev
base_url=https://api.example.com
auth_token=my-secret-token
Variable Substitution
Use {{variable}} syntax in URLs, headers, and bodies:
### @env = dev
GET {{base_url}}/users
Authorization: Bearer {{auth_token}}
Resolution Order
- Chaining references (
{{name.response.*}}) — resolved from prior responses .env.<name>file variables (when@envis set)- Default
.env/.env.localfile variables - System environment variables (
os.environ) - Left as-is (
{{name}}) — visible in output so you know what's missing
Output
probeflow run displays:
- Status line color-coded by range — green 2xx, yellow 3xx, red 4xx, bold red 5xx
- Timing in milliseconds and response size in human-readable bytes
- JSON syntax highlighting for
application/jsonresponses - Response headers when
--headersis passed
probeflow test displays per-request PASS / FAIL with the first failing
assertion message. Use --json or --junit-xml for machine-readable output.
Development
# Install in editable mode with dev dependencies
pip install -e ".[dev]"
# Run the full test suite (coverage included)
python -m pytest
# Lint
ruff check .
# Format
ruff format .
# Check formatting without modifying
ruff format --check .
Project Structure
probeflow/
├── probeflow/
│ ├── __init__.py # Package metadata and version
│ ├── models.py # Pydantic data models (AST)
│ ├── parser.py # .http file lexer + grammar-driven parser
│ ├── client.py # HTTP client (httpx wrapper)
│ ├── environment.py # Variable substitution, .env loading, auth helpers
│ ├── evaluator.py # Assertion evaluation engine
│ ├── formatter.py # Rich terminal output
│ ├── test_runner.py # Test suite loop, JUnit/JSON report writers
│ └── cli.py # Typer CLI — run, test, validate, format, version
├── tests/
│ ├── conftest.py
│ ├── fixtures/ # .http fixture files + golden JSON snapshots
│ ├── test_parser.py
│ ├── test_parser_golden.py
│ ├── test_environment.py
│ ├── test_evaluator.py
│ ├── test_client.py
│ ├── test_cli.py
│ └── test_phase2.py # Auth, multipart, response chaining
├── examples/
│ ├── simple.http
│ ├── requests.http
│ └── .env.dev
├── docs/
│ └── spec.md # Formal .http grammar (EBNF)
├── pyproject.toml
└── README.md
License
MIT
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 probeflow-0.2.0.tar.gz.
File metadata
- Download URL: probeflow-0.2.0.tar.gz
- Upload date:
- Size: 53.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0faa75d3cb3c6cdb020a6cc08ee59d06c19288fe88515d3a36af500156e8119
|
|
| MD5 |
2f764f18c45969d8f941ac458f3f34dd
|
|
| BLAKE2b-256 |
5d2bb9373cb654b7512a295af0bdfe47d7daad6d471c27a88631ed8629165f08
|
Provenance
The following attestation bundles were made for probeflow-0.2.0.tar.gz:
Publisher:
release.yml on Kunal241207/probeflow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
probeflow-0.2.0.tar.gz -
Subject digest:
b0faa75d3cb3c6cdb020a6cc08ee59d06c19288fe88515d3a36af500156e8119 - Sigstore transparency entry: 2633106002
- Sigstore integration time:
-
Permalink:
Kunal241207/probeflow@d203301c17e206852ff5b9d9183e566cc83dbf7c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Kunal241207
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d203301c17e206852ff5b9d9183e566cc83dbf7c -
Trigger Event:
push
-
Statement type:
File details
Details for the file probeflow-0.2.0-py3-none-any.whl.
File metadata
- Download URL: probeflow-0.2.0-py3-none-any.whl
- Upload date:
- Size: 31.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7031fd2915e3a2a9c39f4478e96d5daf92de0d3cf0794c3913cc887728a94c4a
|
|
| MD5 |
547f922edf87a824b8136541494a9d30
|
|
| BLAKE2b-256 |
223ada28086a348fba074a3eab071207f01db198672a48d4e0a2357252e1fd1c
|
Provenance
The following attestation bundles were made for probeflow-0.2.0-py3-none-any.whl:
Publisher:
release.yml on Kunal241207/probeflow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
probeflow-0.2.0-py3-none-any.whl -
Subject digest:
7031fd2915e3a2a9c39f4478e96d5daf92de0d3cf0794c3913cc887728a94c4a - Sigstore transparency entry: 2633106036
- Sigstore integration time:
-
Permalink:
Kunal241207/probeflow@d203301c17e206852ff5b9d9183e566cc83dbf7c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Kunal241207
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d203301c17e206852ff5b9d9183e566cc83dbf7c -
Trigger Event:
push
-
Statement type: