proofrun 0.1.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.
- proofrun-0.1.1/.github/workflows/ci.yaml +86 -0
- proofrun-0.1.1/.github/workflows/release.yaml +113 -0
- proofrun-0.1.1/.gitignore +84 -0
- proofrun-0.1.1/.pre-commit-config.yaml +26 -0
- proofrun-0.1.1/LICENSE +21 -0
- proofrun-0.1.1/Makefile +67 -0
- proofrun-0.1.1/PKG-INFO +268 -0
- proofrun-0.1.1/README.md +250 -0
- proofrun-0.1.1/docs/demo-fail.svg +119 -0
- proofrun-0.1.1/docs/demo-pass.svg +118 -0
- proofrun-0.1.1/pyproject.toml +80 -0
- proofrun-0.1.1/scenarios/auth.yaml +45 -0
- proofrun-0.1.1/scenarios/chain.yaml +89 -0
- proofrun-0.1.1/scenarios/crud.yaml +163 -0
- proofrun-0.1.1/scenarios/profile.yaml +39 -0
- proofrun-0.1.1/scenarios/server.py +83 -0
- proofrun-0.1.1/src/proofrun/__init__.py +10 -0
- proofrun-0.1.1/src/proofrun/__main__.py +9 -0
- proofrun-0.1.1/src/proofrun/cli.py +325 -0
- proofrun-0.1.1/src/proofrun/core/__init__.py +40 -0
- proofrun-0.1.1/src/proofrun/core/compiler.py +136 -0
- proofrun-0.1.1/src/proofrun/core/config.py +150 -0
- proofrun-0.1.1/src/proofrun/core/context.py +185 -0
- proofrun-0.1.1/src/proofrun/core/engine.py +231 -0
- proofrun-0.1.1/src/proofrun/core/notifier.py +75 -0
- proofrun-0.1.1/src/proofrun/core/provider.py +55 -0
- proofrun-0.1.1/src/proofrun/core/state.py +81 -0
- proofrun-0.1.1/tests/__init__.py +3 -0
- proofrun-0.1.1/tests/test_cli.py +912 -0
- proofrun-0.1.1/tests/test_compiler.py +598 -0
- proofrun-0.1.1/tests/test_config.py +451 -0
- proofrun-0.1.1/tests/test_context.py +262 -0
- proofrun-0.1.1/tests/test_engine.py +527 -0
- proofrun-0.1.1/tests/test_main.py +28 -0
- proofrun-0.1.1/tests/test_notifier.py +196 -0
- proofrun-0.1.1/tests/test_provider.py +184 -0
- proofrun-0.1.1/tests/test_state.py +138 -0
- proofrun-0.1.1/uv.lock +917 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
- master
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
quality:
|
|
18
|
+
name: Quality & Tests
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
|
|
21
|
+
strategy:
|
|
22
|
+
fail-fast: false
|
|
23
|
+
matrix:
|
|
24
|
+
python-version:
|
|
25
|
+
- "3.11"
|
|
26
|
+
- "3.12"
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout repository
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Set up Python
|
|
33
|
+
uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: ${{ matrix.python-version }}
|
|
36
|
+
|
|
37
|
+
- name: Set up uv
|
|
38
|
+
uses: astral-sh/setup-uv@v5
|
|
39
|
+
with:
|
|
40
|
+
enable-cache: true
|
|
41
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
42
|
+
|
|
43
|
+
- name: Install dependencies
|
|
44
|
+
run: make install
|
|
45
|
+
|
|
46
|
+
- name: Run code quality & coverage checks
|
|
47
|
+
run: make check
|
|
48
|
+
|
|
49
|
+
- name: Run integration scenarios
|
|
50
|
+
run: |
|
|
51
|
+
make run-profile
|
|
52
|
+
make run-auth
|
|
53
|
+
make run-chain
|
|
54
|
+
make run-crud
|
|
55
|
+
|
|
56
|
+
build:
|
|
57
|
+
name: Build Package
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
needs: quality
|
|
60
|
+
|
|
61
|
+
steps:
|
|
62
|
+
- name: Checkout repository
|
|
63
|
+
uses: actions/checkout@v4
|
|
64
|
+
|
|
65
|
+
- name: Set up Python
|
|
66
|
+
uses: actions/setup-python@v5
|
|
67
|
+
with:
|
|
68
|
+
python-version: "3.11"
|
|
69
|
+
|
|
70
|
+
- name: Set up uv
|
|
71
|
+
uses: astral-sh/setup-uv@v5
|
|
72
|
+
with:
|
|
73
|
+
enable-cache: true
|
|
74
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
75
|
+
|
|
76
|
+
- name: Install dependencies
|
|
77
|
+
run: make install
|
|
78
|
+
|
|
79
|
+
- name: Build distributions
|
|
80
|
+
run: make build
|
|
81
|
+
|
|
82
|
+
- name: Verify distributions
|
|
83
|
+
run: |
|
|
84
|
+
ls -lh dist/
|
|
85
|
+
test -f dist/*.whl
|
|
86
|
+
test -f dist/*.tar.gz
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
quality:
|
|
13
|
+
name: Quality & Tests
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout repository
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.11"
|
|
23
|
+
|
|
24
|
+
- name: Set up uv
|
|
25
|
+
uses: astral-sh/setup-uv@v5
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
29
|
+
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: make install
|
|
32
|
+
|
|
33
|
+
- name: Run code quality checks
|
|
34
|
+
run: make check
|
|
35
|
+
|
|
36
|
+
- name: Run integration scenarios
|
|
37
|
+
run: |
|
|
38
|
+
make run-profile
|
|
39
|
+
make run-chain
|
|
40
|
+
|
|
41
|
+
build:
|
|
42
|
+
name: Build Package
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
needs: quality
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout repository
|
|
47
|
+
uses: actions/checkout@v4
|
|
48
|
+
|
|
49
|
+
- name: Set up Python
|
|
50
|
+
uses: actions/setup-python@v5
|
|
51
|
+
with:
|
|
52
|
+
python-version: "3.11"
|
|
53
|
+
|
|
54
|
+
- name: Set up uv
|
|
55
|
+
uses: astral-sh/setup-uv@v5
|
|
56
|
+
with:
|
|
57
|
+
enable-cache: true
|
|
58
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
59
|
+
|
|
60
|
+
- name: Verify tag matches package version
|
|
61
|
+
run: |
|
|
62
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
63
|
+
PKG_VERSION="$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)"
|
|
64
|
+
echo "tag: ${TAG_VERSION} pyproject: ${PKG_VERSION}"
|
|
65
|
+
if [ "${TAG_VERSION}" != "${PKG_VERSION}" ]; then
|
|
66
|
+
echo "::error::Tag v${TAG_VERSION} does not match pyproject version ${PKG_VERSION}"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
- name: Build distributions
|
|
71
|
+
run: make build
|
|
72
|
+
|
|
73
|
+
- name: Store distributions
|
|
74
|
+
uses: actions/upload-artifact@v4
|
|
75
|
+
with:
|
|
76
|
+
name: package-distributions
|
|
77
|
+
path: dist/
|
|
78
|
+
|
|
79
|
+
publish-to-pypi:
|
|
80
|
+
name: Publish to PyPI
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
needs: build
|
|
83
|
+
environment: release
|
|
84
|
+
permissions:
|
|
85
|
+
id-token: write
|
|
86
|
+
steps:
|
|
87
|
+
- name: Download distributions
|
|
88
|
+
uses: actions/download-artifact@v4
|
|
89
|
+
with:
|
|
90
|
+
name: package-distributions
|
|
91
|
+
path: dist/
|
|
92
|
+
|
|
93
|
+
- name: Publish to PyPI
|
|
94
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
95
|
+
|
|
96
|
+
github-release:
|
|
97
|
+
name: Create GitHub Release
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
needs: publish-to-pypi
|
|
100
|
+
permissions:
|
|
101
|
+
contents: write
|
|
102
|
+
steps:
|
|
103
|
+
- name: Download distributions
|
|
104
|
+
uses: actions/download-artifact@v4
|
|
105
|
+
with:
|
|
106
|
+
name: package-distributions
|
|
107
|
+
path: dist/
|
|
108
|
+
|
|
109
|
+
- name: Create GitHub Release
|
|
110
|
+
uses: softprops/action-gh-release@v2
|
|
111
|
+
with:
|
|
112
|
+
files: dist/*
|
|
113
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Virtual environments
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
env/
|
|
11
|
+
ENV/
|
|
12
|
+
env.bak/
|
|
13
|
+
venv.bak/
|
|
14
|
+
|
|
15
|
+
# Environment / secrets
|
|
16
|
+
.env
|
|
17
|
+
.env.*
|
|
18
|
+
!.env.example
|
|
19
|
+
|
|
20
|
+
# Distribution / packaging
|
|
21
|
+
build/
|
|
22
|
+
dist/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.eggs/
|
|
25
|
+
*.egg
|
|
26
|
+
MANIFEST
|
|
27
|
+
|
|
28
|
+
# Test / coverage
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.coverage
|
|
31
|
+
.coverage.*
|
|
32
|
+
htmlcov/
|
|
33
|
+
coverage.xml
|
|
34
|
+
*.cover
|
|
35
|
+
*.lcov
|
|
36
|
+
.hypothesis/
|
|
37
|
+
.tox/
|
|
38
|
+
.nox/
|
|
39
|
+
|
|
40
|
+
# Tool caches
|
|
41
|
+
.ruff_cache/
|
|
42
|
+
.pyright_cache/
|
|
43
|
+
.mypy_cache/
|
|
44
|
+
.pyre/
|
|
45
|
+
.pytype/
|
|
46
|
+
.dmypy.json
|
|
47
|
+
dmypy.json
|
|
48
|
+
|
|
49
|
+
# IDE / editors
|
|
50
|
+
.idea/
|
|
51
|
+
.vscode/
|
|
52
|
+
.spyderproject
|
|
53
|
+
.spyproject
|
|
54
|
+
.ropeproject
|
|
55
|
+
|
|
56
|
+
# Jupyter
|
|
57
|
+
.ipynb_checkpoints/
|
|
58
|
+
|
|
59
|
+
# OS
|
|
60
|
+
.DS_Store
|
|
61
|
+
Thumbs.db
|
|
62
|
+
|
|
63
|
+
# Temporary files
|
|
64
|
+
*.tmp
|
|
65
|
+
*.temp
|
|
66
|
+
*.swp
|
|
67
|
+
*.swo
|
|
68
|
+
*~
|
|
69
|
+
tempCodeRunnerFile.py
|
|
70
|
+
|
|
71
|
+
# Build / local tooling
|
|
72
|
+
.pdm-python
|
|
73
|
+
.pdm-build/
|
|
74
|
+
.pixi/
|
|
75
|
+
__pypackages__/
|
|
76
|
+
|
|
77
|
+
# Local package/build metadata
|
|
78
|
+
.python-version
|
|
79
|
+
|
|
80
|
+
# PyPI credentials
|
|
81
|
+
.pypirc
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
FUTURE.md
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.8.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
stages: [pre-commit]
|
|
8
|
+
|
|
9
|
+
- id: ruff-format
|
|
10
|
+
stages: [pre-commit]
|
|
11
|
+
|
|
12
|
+
- repo: local
|
|
13
|
+
hooks:
|
|
14
|
+
- id: pyright
|
|
15
|
+
name: pyright
|
|
16
|
+
entry: uv run pyright
|
|
17
|
+
language: system
|
|
18
|
+
pass_filenames: false
|
|
19
|
+
stages: [pre-push]
|
|
20
|
+
|
|
21
|
+
- id: pytest
|
|
22
|
+
name: pytest
|
|
23
|
+
entry: uv run pytest -q
|
|
24
|
+
language: system
|
|
25
|
+
pass_filenames: false
|
|
26
|
+
stages: [pre-push]
|
proofrun-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kishan Thanki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
proofrun-0.1.1/Makefile
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
.PHONY: install test coverage coverage-html lint format format-check typecheck check \
|
|
2
|
+
run-profile run-auth run-chain run-crud build clean full ci
|
|
3
|
+
|
|
4
|
+
full: clean install check build
|
|
5
|
+
|
|
6
|
+
install:
|
|
7
|
+
uv sync --group dev
|
|
8
|
+
|
|
9
|
+
test:
|
|
10
|
+
uv run pytest -q
|
|
11
|
+
|
|
12
|
+
coverage:
|
|
13
|
+
uv run pytest -q --cov=proofrun --cov-branch \
|
|
14
|
+
--cov-report=term-missing \
|
|
15
|
+
--cov-fail-under=100
|
|
16
|
+
|
|
17
|
+
coverage-html:
|
|
18
|
+
uv run pytest -q --cov=proofrun --cov-branch \
|
|
19
|
+
--cov-report=html
|
|
20
|
+
@echo "Coverage report generated: htmlcov/index.html"
|
|
21
|
+
|
|
22
|
+
lint:
|
|
23
|
+
uv run ruff check src/ tests/
|
|
24
|
+
|
|
25
|
+
format:
|
|
26
|
+
uv run ruff format src/ tests/
|
|
27
|
+
|
|
28
|
+
format-check:
|
|
29
|
+
uv run ruff format --check src/ tests/
|
|
30
|
+
uv run ruff check src/ tests/
|
|
31
|
+
|
|
32
|
+
typecheck:
|
|
33
|
+
uv run pyright
|
|
34
|
+
|
|
35
|
+
check: lint format-check typecheck coverage
|
|
36
|
+
|
|
37
|
+
run-profile:
|
|
38
|
+
uv run proofrun scenarios/profile.yaml --once
|
|
39
|
+
|
|
40
|
+
run-auth:
|
|
41
|
+
python scenarios/server.py & \
|
|
42
|
+
SERVER_PID=$$!; \
|
|
43
|
+
sleep 1; \
|
|
44
|
+
uv run proofrun scenarios/auth.yaml --once; \
|
|
45
|
+
EXIT_CODE=$$?; \
|
|
46
|
+
kill $$SERVER_PID 2>/dev/null || true; \
|
|
47
|
+
exit $$EXIT_CODE
|
|
48
|
+
|
|
49
|
+
run-chain:
|
|
50
|
+
uv run proofrun scenarios/chain.yaml --once
|
|
51
|
+
|
|
52
|
+
run-crud:
|
|
53
|
+
uv run proofrun scenarios/crud.yaml --once
|
|
54
|
+
|
|
55
|
+
build:
|
|
56
|
+
uv build
|
|
57
|
+
|
|
58
|
+
clean:
|
|
59
|
+
rm -rf dist/ \
|
|
60
|
+
.ruff_cache/ \
|
|
61
|
+
.pytest_cache/ \
|
|
62
|
+
.pyright_cache/ \
|
|
63
|
+
.coverage \
|
|
64
|
+
.coverage.* \
|
|
65
|
+
htmlcov/ \
|
|
66
|
+
__pycache__/ \
|
|
67
|
+
*.egg-info/
|
proofrun-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: proofrun
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Synthetic API monitoring as code daemon.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: fastjsonschema>=2.19.1
|
|
9
|
+
Requires-Dist: httpx>=0.27.0
|
|
10
|
+
Requires-Dist: jsonpath-ng>=1.6.1
|
|
11
|
+
Requires-Dist: orjson>=3.9.15
|
|
12
|
+
Requires-Dist: pydantic>=2.6.0
|
|
13
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
14
|
+
Requires-Dist: rich>=13.7.0
|
|
15
|
+
Requires-Dist: typer>=0.11.0
|
|
16
|
+
Requires-Dist: uvloop>=0.19.0
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
<div align="center">
|
|
20
|
+
|
|
21
|
+
# Proofrun
|
|
22
|
+
|
|
23
|
+
**Write an API workflow in YAML. Run it locally, in CI, or as a monitor that only alerts when health changes.**
|
|
24
|
+
|
|
25
|
+
[](https://www.python.org/)
|
|
26
|
+
[](https://github.com/Kishan-Thanki/proof/actions/workflows/ci.yaml)
|
|
27
|
+
[](https://github.com/Kishan-Thanki/proof/releases)
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
Most monitors tell you an endpoint returned `200`. Proofrun tells you whether the
|
|
35
|
+
*journey* works: log in, take the token from the response, use it on the next
|
|
36
|
+
request, and check what comes back.
|
|
37
|
+
|
|
38
|
+
When something breaks, you see exactly which step failed and why:
|
|
39
|
+
|
|
40
|
+
<div align="center">
|
|
41
|
+
<img src="docs/demo-fail.svg" alt="Proofrun reporting a failed step in a login-then-fetch scenario" width="860">
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
And when everything is fine:
|
|
45
|
+
|
|
46
|
+
<div align="center">
|
|
47
|
+
<img src="docs/demo-pass.svg" alt="Proofrun reporting a passing scenario" width="860">
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
**YAML in. HTTP checks out. No database, queue, or external service required.**
|
|
51
|
+
|
|
52
|
+
- **One file describes the workflow.** Scenarios live in Git next to your code.
|
|
53
|
+
- **Multi-step.** Values extracted from one response feed the next request.
|
|
54
|
+
- **Runs anywhere.** Same file locally, in CI (exit code `0` / `1`), or as a daemon.
|
|
55
|
+
- **Quiet by design.** In daemon mode you are alerted when health *changes*, not on every failed check.
|
|
56
|
+
|
|
57
|
+
## Quickstart (30 seconds)
|
|
58
|
+
|
|
59
|
+
**1. Install** (Python 3.11+):
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install proofrun
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or with pip: `pipx install proofrun`
|
|
66
|
+
|
|
67
|
+
**2. Create `health.yaml`:**
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
version: "1.0"
|
|
71
|
+
|
|
72
|
+
global:
|
|
73
|
+
base_url: "https://jsonplaceholder.typicode.com"
|
|
74
|
+
|
|
75
|
+
scenarios:
|
|
76
|
+
- name: "Public API is up"
|
|
77
|
+
steps:
|
|
78
|
+
- name: "GET /posts/1"
|
|
79
|
+
request:
|
|
80
|
+
method: GET
|
|
81
|
+
path: /posts/1
|
|
82
|
+
expect:
|
|
83
|
+
status: 200
|
|
84
|
+
max_latency_ms: 2000
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**3. Run it:**
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
proofrun health.yaml
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
That's it. Swap in your own `base_url` and paths. Add `--daemon` to keep it
|
|
94
|
+
running.
|
|
95
|
+
|
|
96
|
+
## A realistic example: log in, then use the token
|
|
97
|
+
|
|
98
|
+
This is the scenario from the screenshots above. It logs in, extracts the
|
|
99
|
+
access token with JSONPath, sends it as a `Bearer` header on the next request,
|
|
100
|
+
extracts the user id from *that* response, and uses it in the URL of the third.
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
version: "1.0"
|
|
104
|
+
|
|
105
|
+
global:
|
|
106
|
+
base_url: "http://127.0.0.1:8099"
|
|
107
|
+
timeout_seconds: 5
|
|
108
|
+
|
|
109
|
+
scenarios:
|
|
110
|
+
- name: "Login and fetch orders"
|
|
111
|
+
steps:
|
|
112
|
+
- name: "Log in"
|
|
113
|
+
request:
|
|
114
|
+
method: POST
|
|
115
|
+
path: /login
|
|
116
|
+
json:
|
|
117
|
+
email: "demo@example.com"
|
|
118
|
+
password: "demo-password"
|
|
119
|
+
expect:
|
|
120
|
+
status: 200
|
|
121
|
+
max_latency_ms: 1000
|
|
122
|
+
extract:
|
|
123
|
+
token: "$.access_token" # saved as ${token}
|
|
124
|
+
|
|
125
|
+
- name: "Fetch profile"
|
|
126
|
+
request:
|
|
127
|
+
method: GET
|
|
128
|
+
path: /me
|
|
129
|
+
headers:
|
|
130
|
+
Authorization: "Bearer ${token}" # used here
|
|
131
|
+
expect:
|
|
132
|
+
status: 200
|
|
133
|
+
extract:
|
|
134
|
+
user_id: "$.id" # saved as ${user_id}
|
|
135
|
+
|
|
136
|
+
- name: "Fetch orders"
|
|
137
|
+
request:
|
|
138
|
+
method: GET
|
|
139
|
+
path: "/users/${user_id}/orders"
|
|
140
|
+
headers:
|
|
141
|
+
Authorization: "Bearer ${token}"
|
|
142
|
+
expect:
|
|
143
|
+
status: 200
|
|
144
|
+
schema: # JSON Schema validation
|
|
145
|
+
type: array
|
|
146
|
+
minItems: 1
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Steps run in order and stop at the first failure, so the report points at the
|
|
150
|
+
step that actually broke. You can try this without any external service. The
|
|
151
|
+
repo includes a tiny local API:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# terminal 1: start the demo API
|
|
155
|
+
python scenarios/server.py
|
|
156
|
+
|
|
157
|
+
# terminal 2: run the scenario (passes, exit code 0)
|
|
158
|
+
proofrun scenarios/auth.yaml
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Now break it on purpose. Restart the demo API with `--break`, which makes
|
|
162
|
+
`GET /me` return `500`, and run the same scenario again:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
python scenarios/server.py --break
|
|
166
|
+
proofrun scenarios/auth.yaml # fails, exit code 1
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## What you can assert
|
|
170
|
+
|
|
171
|
+
Each step's `expect` block supports:
|
|
172
|
+
|
|
173
|
+
| Assertion | Example | Notes |
|
|
174
|
+
| --- | --- | --- |
|
|
175
|
+
| `status` | `status: 200` | Required. |
|
|
176
|
+
| `max_latency_ms` | `max_latency_ms: 500` | Fails if the response is slower. |
|
|
177
|
+
| `headers` | `headers: { Content-Type: "application/json" }` | Exact match. |
|
|
178
|
+
| `schema` | inline JSON Schema, or a path to a file | Validates the JSON response body. |
|
|
179
|
+
|
|
180
|
+
A step can also `extract` values with JSONPath for later steps. Besides your own
|
|
181
|
+
variables, built-in dynamic values are available anywhere you can write `${...}`:
|
|
182
|
+
`${$uuid}`, `${$timestamp}`, `${$timestamp_ms}`, `${$iso_timestamp}`,
|
|
183
|
+
`${$random_int}`.
|
|
184
|
+
|
|
185
|
+
Defaults you set under `global` (`base_url`, `timeout_seconds`, `headers`) apply
|
|
186
|
+
to every step, and any step can override its own `headers` and `timeout`.
|
|
187
|
+
|
|
188
|
+
## Run it continuously
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
proofrun monitors/api.yaml --daemon
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
In daemon mode Proofrun re-runs your scenarios on a loop and tracks each one as
|
|
195
|
+
`HEALTHY` or `FAILING`.
|
|
196
|
+
|
|
197
|
+
* **Alerts on change, not on repetition.** A webhook fires when a scenario starts
|
|
198
|
+
failing and again when it recovers. A scenario that stays down does not
|
|
199
|
+
re-alert on every cycle.
|
|
200
|
+
* **Edit configs live.** Changes to the YAML are picked up without a restart. If
|
|
201
|
+
an edit is invalid, Proofrun keeps running the last working configuration.
|
|
202
|
+
* **Jittered schedule.** Sleep time varies by ±15% so checks don't all land on
|
|
203
|
+
the same second.
|
|
204
|
+
|
|
205
|
+
Send alerts to any HTTP endpoint:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
proofrun monitors/api.yaml --daemon --webhook-url https://<your_domain>/hooks/proofrun
|
|
209
|
+
|
|
210
|
+
# or
|
|
211
|
+
|
|
212
|
+
export PROOFRUN_WEBHOOK_URL=https://<your_domain>/hooks/proofrun
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The webhook receives plain JSON:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"event": "scenario_failed",
|
|
220
|
+
"scenario": "Login and fetch orders",
|
|
221
|
+
"previous_status": "HEALTHY",
|
|
222
|
+
"status": "FAILING",
|
|
223
|
+
"details": "- Fetch profile: Status code mismatch: expected 200, got 500"
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`event` is `scenario_failed` or `scenario_recovered`.
|
|
228
|
+
|
|
229
|
+
## Use it in CI
|
|
230
|
+
|
|
231
|
+
Proofrun exits with `0` when every scenario passes and `1` when any fails, so it
|
|
232
|
+
works as a smoke test or a scheduled check. For example, with GitHub Actions:
|
|
233
|
+
|
|
234
|
+
```yaml
|
|
235
|
+
name: API check
|
|
236
|
+
on:
|
|
237
|
+
schedule:
|
|
238
|
+
- cron: "*/15 * * * *"
|
|
239
|
+
workflow_dispatch:
|
|
240
|
+
|
|
241
|
+
jobs:
|
|
242
|
+
proofrun:
|
|
243
|
+
runs-on: ubuntu-latest
|
|
244
|
+
steps:
|
|
245
|
+
- uses: actions/checkout@v4
|
|
246
|
+
- uses: actions/setup-python@v5
|
|
247
|
+
with:
|
|
248
|
+
python-version: "3.12"
|
|
249
|
+
- run: pip install proofrun
|
|
250
|
+
- run: proofrun monitors/api.yaml
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
A failing check fails the workflow run, and GitHub notifies you the way it
|
|
254
|
+
notifies you about any failed workflow.
|
|
255
|
+
|
|
256
|
+
## CLI
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
proofrun <config.yaml> # run once and exit (default)
|
|
260
|
+
proofrun <config.yaml> --daemon # run continuously
|
|
261
|
+
proofrun <config.yaml> --interval 30 # override the daemon interval (seconds)
|
|
262
|
+
proofrun <config.yaml> --daemon --webhook-url <url>
|
|
263
|
+
proofrun --version
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
[MIT](LICENSE)
|