pytest-resilience-agent 0.2.0__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.
- pytest_resilience_agent-0.2.0/.github/dependabot.yml +25 -0
- pytest_resilience_agent-0.2.0/.github/workflows/ci.yml +44 -0
- pytest_resilience_agent-0.2.0/.github/workflows/codeql.yml +38 -0
- pytest_resilience_agent-0.2.0/.github/workflows/dependabot-auto-merge.yml +29 -0
- pytest_resilience_agent-0.2.0/.github/workflows/publish.yml +33 -0
- pytest_resilience_agent-0.2.0/.github/workflows/scorecard.yml +42 -0
- pytest_resilience_agent-0.2.0/.gitignore +40 -0
- pytest_resilience_agent-0.2.0/.pre-commit-config.yaml +17 -0
- pytest_resilience_agent-0.2.0/CHANGELOG.md +113 -0
- pytest_resilience_agent-0.2.0/CONTRIBUTING.md +40 -0
- pytest_resilience_agent-0.2.0/LICENSE +21 -0
- pytest_resilience_agent-0.2.0/PKG-INFO +278 -0
- pytest_resilience_agent-0.2.0/README.md +243 -0
- pytest_resilience_agent-0.2.0/SECURITY.md +24 -0
- pytest_resilience_agent-0.2.0/assets/demo.gif +0 -0
- pytest_resilience_agent-0.2.0/demo/__init__.py +0 -0
- pytest_resilience_agent-0.2.0/demo/example_agent_tests/__init__.py +0 -0
- pytest_resilience_agent-0.2.0/demo/example_agent_tests/test_summarise_resilience.py +85 -0
- pytest_resilience_agent-0.2.0/demo/mock_lark.py +92 -0
- pytest_resilience_agent-0.2.0/demo/mock_truefoundry.py +99 -0
- pytest_resilience_agent-0.2.0/demo/run_demo.py +191 -0
- pytest_resilience_agent-0.2.0/demo/run_full_loop.py +207 -0
- pytest_resilience_agent-0.2.0/demo/sample_agent/__init__.py +0 -0
- pytest_resilience_agent-0.2.0/demo/sample_agent/app.py +119 -0
- pytest_resilience_agent-0.2.0/demo/scenarios/chaos_patterns.json +41 -0
- pytest_resilience_agent-0.2.0/docs/adr/0001-direction-reversal.md +55 -0
- pytest_resilience_agent-0.2.0/docs/adr/0002-respx-as-injection-layer.md +44 -0
- pytest_resilience_agent-0.2.0/docs/adr/0003-rule-based-generator.md +42 -0
- pytest_resilience_agent-0.2.0/docs/devpost_submission.md +116 -0
- pytest_resilience_agent-0.2.0/docs/screenshot/demo_full_loop.png +0 -0
- pytest_resilience_agent-0.2.0/docs/screenshot/render_demo_screenshot.py +109 -0
- pytest_resilience_agent-0.2.0/pyproject.toml +70 -0
- pytest_resilience_agent-0.2.0/scripts/smoke_live_integrations.py +128 -0
- pytest_resilience_agent-0.2.0/src/pytest_resilience_agent/__init__.py +8 -0
- pytest_resilience_agent-0.2.0/src/pytest_resilience_agent/chaos.py +174 -0
- pytest_resilience_agent-0.2.0/src/pytest_resilience_agent/cli.py +199 -0
- pytest_resilience_agent-0.2.0/src/pytest_resilience_agent/gateway.py +75 -0
- pytest_resilience_agent-0.2.0/src/pytest_resilience_agent/generator.py +118 -0
- pytest_resilience_agent-0.2.0/src/pytest_resilience_agent/lark_mcp.py +131 -0
- pytest_resilience_agent-0.2.0/src/pytest_resilience_agent/plugin.py +179 -0
- pytest_resilience_agent-0.2.0/src/pytest_resilience_agent/scenarios.py +471 -0
- pytest_resilience_agent-0.2.0/tests/__init__.py +0 -0
- pytest_resilience_agent-0.2.0/tests/conftest.py +5 -0
- pytest_resilience_agent-0.2.0/tests/test_chaos_scenarios.py +198 -0
- pytest_resilience_agent-0.2.0/tests/test_marker_and_fixtures.py +63 -0
- pytest_resilience_agent-0.2.0/tests/test_multi_turn_chaos.py +150 -0
- pytest_resilience_agent-0.2.0/tests/test_timeline_and_report.py +68 -0
- pytest_resilience_agent-0.2.0/videos/CREDITS.md +26 -0
- pytest_resilience_agent-0.2.0/videos/concat.txt +21 -0
- pytest_resilience_agent-0.2.0/videos/pytest-resilience-agent-demo.mp4 +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-01-title.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-02-problem.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-03-gap.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-04-lark.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-05-generator.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-06-pytest.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-07-loop.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-08-arch.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-09-built.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/beat-10-tagout.png +0 -0
- pytest_resilience_agent-0.2.0/videos/slides/make_slides.py +653 -0
- pytest_resilience_agent-0.2.0/videos/subtitles.ass +24 -0
- pytest_resilience_agent-0.2.0/videos/subtitles.srt +64 -0
- pytest_resilience_agent-0.2.0/videos/technology_dreams.mp3 +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: pip
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
day: monday
|
|
8
|
+
time: "06:00"
|
|
9
|
+
timezone: "Europe/London"
|
|
10
|
+
open-pull-requests-limit: 5
|
|
11
|
+
commit-message:
|
|
12
|
+
prefix: "deps"
|
|
13
|
+
include: scope
|
|
14
|
+
|
|
15
|
+
- package-ecosystem: github-actions
|
|
16
|
+
directory: "/"
|
|
17
|
+
schedule:
|
|
18
|
+
interval: weekly
|
|
19
|
+
day: monday
|
|
20
|
+
time: "06:00"
|
|
21
|
+
timezone: "Europe/London"
|
|
22
|
+
open-pull-requests-limit: 3
|
|
23
|
+
commit-message:
|
|
24
|
+
prefix: "ci"
|
|
25
|
+
include: scope
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
name: Lint (ruff)
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
18
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Install ruff
|
|
22
|
+
run: pip install "ruff==0.15.13"
|
|
23
|
+
- name: Ruff check
|
|
24
|
+
run: ruff check .
|
|
25
|
+
- name: Ruff format check
|
|
26
|
+
run: ruff format --check .
|
|
27
|
+
|
|
28
|
+
unit-tests:
|
|
29
|
+
name: Unit tests (Python ${{ matrix.python-version }})
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
strategy:
|
|
32
|
+
fail-fast: false
|
|
33
|
+
matrix:
|
|
34
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
37
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: ${{ matrix.python-version }}
|
|
40
|
+
cache: pip
|
|
41
|
+
- name: Install package with dev extras
|
|
42
|
+
run: pip install -e ".[dev]"
|
|
43
|
+
- name: Run tests (skip integration and slow markers)
|
|
44
|
+
run: pytest tests -m "not integration and not slow"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "30 5 * * 1"
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
analyze:
|
|
16
|
+
name: Analyze (Python)
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
actions: read
|
|
20
|
+
contents: read
|
|
21
|
+
security-events: write
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
25
|
+
|
|
26
|
+
- name: Initialize CodeQL
|
|
27
|
+
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
|
|
28
|
+
with:
|
|
29
|
+
languages: python
|
|
30
|
+
queries: security-and-quality
|
|
31
|
+
|
|
32
|
+
- name: Autobuild
|
|
33
|
+
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
|
|
34
|
+
|
|
35
|
+
- name: Perform CodeQL Analysis
|
|
36
|
+
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
|
|
37
|
+
with:
|
|
38
|
+
category: "/language:python"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Dependabot auto-merge
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize, reopened]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
auto-merge:
|
|
12
|
+
if: github.actor == 'dependabot[bot]'
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
contents: write
|
|
16
|
+
pull-requests: write
|
|
17
|
+
steps:
|
|
18
|
+
- name: Read PR metadata
|
|
19
|
+
id: meta
|
|
20
|
+
uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2
|
|
21
|
+
with:
|
|
22
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
23
|
+
|
|
24
|
+
- name: Enable auto-merge for patch + minor
|
|
25
|
+
if: steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor'
|
|
26
|
+
run: gh pr merge --auto --squash "$PR_URL"
|
|
27
|
+
env:
|
|
28
|
+
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
29
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment:
|
|
14
|
+
name: pypi
|
|
15
|
+
url: https://pypi.org/p/pytest-resilience-agent
|
|
16
|
+
permissions:
|
|
17
|
+
id-token: write
|
|
18
|
+
contents: read
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
|
|
27
|
+
- name: Build sdist and wheel
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade pip build
|
|
30
|
+
python -m build
|
|
31
|
+
|
|
32
|
+
- name: Publish to PyPI
|
|
33
|
+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Scorecard supply-chain security
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: "30 1 * * 6"
|
|
8
|
+
|
|
9
|
+
permissions: read-all
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
analysis:
|
|
13
|
+
name: Scorecard analysis
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
security-events: write
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: "Checkout code"
|
|
21
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
|
|
25
|
+
- name: "Run analysis"
|
|
26
|
+
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
|
27
|
+
with:
|
|
28
|
+
results_file: results.sarif
|
|
29
|
+
results_format: sarif
|
|
30
|
+
publish_results: true
|
|
31
|
+
|
|
32
|
+
- name: "Upload artifact"
|
|
33
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
34
|
+
with:
|
|
35
|
+
name: SARIF file
|
|
36
|
+
path: results.sarif
|
|
37
|
+
retention-days: 5
|
|
38
|
+
|
|
39
|
+
- name: "Upload to code-scanning"
|
|
40
|
+
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
|
|
41
|
+
with:
|
|
42
|
+
sarif_file: results.sarif
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
*.egg
|
|
12
|
+
|
|
13
|
+
# Virtual environments
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
|
|
18
|
+
# Editor
|
|
19
|
+
.vscode/
|
|
20
|
+
.idea/
|
|
21
|
+
*.swp
|
|
22
|
+
.DS_Store
|
|
23
|
+
|
|
24
|
+
# pytest
|
|
25
|
+
.pytest_cache/
|
|
26
|
+
.coverage
|
|
27
|
+
htmlcov/
|
|
28
|
+
|
|
29
|
+
# Local env files
|
|
30
|
+
.env
|
|
31
|
+
.env.local
|
|
32
|
+
|
|
33
|
+
# Build artefacts
|
|
34
|
+
*.whl
|
|
35
|
+
*.tar.gz
|
|
36
|
+
.secrets/
|
|
37
|
+
|
|
38
|
+
# Slide regeneration artefacts
|
|
39
|
+
videos/slides/*.orig.*
|
|
40
|
+
videos/*.orig.*
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.7.4
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
|
|
9
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
10
|
+
rev: v5.0.0
|
|
11
|
+
hooks:
|
|
12
|
+
- id: end-of-file-fixer
|
|
13
|
+
- id: trailing-whitespace
|
|
14
|
+
- id: check-yaml
|
|
15
|
+
- id: check-toml
|
|
16
|
+
- id: check-merge-conflict
|
|
17
|
+
- id: check-added-large-files
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Planned
|
|
11
|
+
- Semantic assertion hooks (composability with eval frameworks)
|
|
12
|
+
- LLM-driven scenario classifier behind the existing `pick_scenarios` interface
|
|
13
|
+
- Scenario composition primitives (e.g. `rate_limit` then `partial_outage`)
|
|
14
|
+
|
|
15
|
+
## [0.2.0] - 2026-06-11
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Multi-turn conversation chaos. The `resilience` marker now accepts
|
|
19
|
+
`turns=[[...], [...], ...]`, one scenario set per conversation turn, advanced
|
|
20
|
+
with `chaos.next_turn()`. Each turn is an independent chaos window: advancing
|
|
21
|
+
reverts the current turn's scenarios and applies the next turn's with fresh
|
|
22
|
+
call counters, so chaos can appear and clear mid-conversation (turn 1 clean,
|
|
23
|
+
turn 2 brownout, turn 3 recovered). `turns=` and `scenarios=` are mutually
|
|
24
|
+
exclusive; combining them, or advancing past the last turn, raises a clear
|
|
25
|
+
usage error. Each turn boundary emits a `chaos.turn.N` OpenTelemetry span.
|
|
26
|
+
- New `malformed_json` chaos scenario: the gateway returns HTTP 200 with an HTML error body instead of JSON, mirroring a proxy or CDN that swallows the upstream failure and serves its own page. Agent code that calls `response.json()` without guarding against decode errors surfaces this as an unhandled exception rather than a graceful fallback. Brings the built-in scenario count to ten.
|
|
27
|
+
|
|
28
|
+
## [0.1.0] - 2026-05-27
|
|
29
|
+
|
|
30
|
+
Initial release. Built for the DevNetwork [AI + ML] Hackathon 2026, Lark and
|
|
31
|
+
TrueFoundry sponsor tracks.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
**Plugin**
|
|
36
|
+
- `pytest_resilience_agent.plugin` registers the `resilience` pytest marker
|
|
37
|
+
with strict-marker support
|
|
38
|
+
- `ai_gateway` fixture: returns an `AIGatewayClient` configured from
|
|
39
|
+
`--resilience-gateway-url` CLI option or `TFY_GATEWAY_URL` env var
|
|
40
|
+
- `chaos` fixture: applies named scenarios from the marker for the test
|
|
41
|
+
duration, with automatic cleanup
|
|
42
|
+
- `--resilience-record PATH` option writes a JSON timeline of every chaos
|
|
43
|
+
event to disk at session finish
|
|
44
|
+
- `pytest_runtest_logreport` hook attaches a "chaos events" section to each
|
|
45
|
+
test report so judges and engineers see what was injected next to the
|
|
46
|
+
test outcome
|
|
47
|
+
|
|
48
|
+
**Chaos scenarios**
|
|
49
|
+
- `llm_timeout`: gateway sleeps past the request timeout
|
|
50
|
+
- `llm_5xx`: gateway returns 502 for the first N calls, then succeeds
|
|
51
|
+
- `rate_limit`: gateway returns 429 with `Retry-After`
|
|
52
|
+
- `mcp_error`: Lark MCP server raises a JSON-RPC error envelope
|
|
53
|
+
- `partial_outage`: first call 503, retry succeeds
|
|
54
|
+
- `cost_exceeded`: gateway returns 402 quota_exceeded
|
|
55
|
+
- `wrong_model_returned`: gateway silently routes to an unintended model
|
|
56
|
+
- `stream_stall`: gateway returns 200 with empty content (stream drop)
|
|
57
|
+
- `network_blip`: ConnectError on first N calls, recovery after
|
|
58
|
+
|
|
59
|
+
**Generator**
|
|
60
|
+
- `pytest_resilience_agent.generator.generate_test` writes a runnable
|
|
61
|
+
pytest file for any failure text, with chaos scenarios picked from a
|
|
62
|
+
deterministic regex rule set (ADR 0003)
|
|
63
|
+
- 9 regex rules cover the common failure-text patterns: 429, 502, 503,
|
|
64
|
+
504, 402, connection errors, empty/stream, MCP, model mismatch
|
|
65
|
+
|
|
66
|
+
**CLI**
|
|
67
|
+
- `pytest-resilience-agent` console script with five subcommands:
|
|
68
|
+
- `scenarios` lists the registered chaos scenarios
|
|
69
|
+
- `discover` lists failing tests via Lark MCP
|
|
70
|
+
- `generate` synthesises resilience tests from Lark failures
|
|
71
|
+
- `run` executes the generated tests through pytest
|
|
72
|
+
- `report` pushes resolution status back to Lark
|
|
73
|
+
|
|
74
|
+
**Demo entry points**
|
|
75
|
+
- `demo/run_demo.py`: drives the sample FastAPI agent through every chaos
|
|
76
|
+
scenario with a Rich-table summary
|
|
77
|
+
- `demo/run_full_loop.py`: full end-to-end loop - spins up mock Lark
|
|
78
|
+
server in a background thread, pulls failures, generates resilience
|
|
79
|
+
tests, runs pytest, reports resolutions back to Lark
|
|
80
|
+
- `demo/mock_truefoundry.py`: in-process mock of the TrueFoundry AI
|
|
81
|
+
Gateway with a Gemini → Claude → local fallback chain
|
|
82
|
+
- `demo/mock_lark.py`: in-process mock of the Lark MCP server with seeded
|
|
83
|
+
failing-test data
|
|
84
|
+
- `demo/sample_agent/app.py`: sample FastAPI agent that summarises
|
|
85
|
+
customer emails through the gateway with retry logic
|
|
86
|
+
|
|
87
|
+
**Tests and quality**
|
|
88
|
+
- 16 passing tests covering plugin registration, fixture wiring, every
|
|
89
|
+
chaos scenario, the timeline export, and the report hook
|
|
90
|
+
- 5 example end-user tests in `demo/example_agent_tests/` showing the
|
|
91
|
+
four patterns we expect adopters to copy
|
|
92
|
+
- pre-commit configuration (ruff format + lint, trailing-whitespace,
|
|
93
|
+
YAML/TOML/large-file checks)
|
|
94
|
+
- 3 ADRs in `docs/adr/`:
|
|
95
|
+
- 0001: resilience-first direction, not eval-first
|
|
96
|
+
- 0002: respx as the chaos injection layer
|
|
97
|
+
- 0003: rule-based scenario picker for v0.1, LLM-driven option for v0.2
|
|
98
|
+
|
|
99
|
+
**Observability**
|
|
100
|
+
- OpenTelemetry spans on every chaos scenario apply / revert, ready for
|
|
101
|
+
OTLP export to Cloud Trace or any compatible backend
|
|
102
|
+
|
|
103
|
+
### Known limitations
|
|
104
|
+
|
|
105
|
+
- Semantic-level paraphrased failures are out of scope (use phoenix2pytest
|
|
106
|
+
or DeepEval for those)
|
|
107
|
+
- Multi-turn conversation chaos is on the v0.2 roadmap
|
|
108
|
+
- Distributed-system chaos (network partitions across services) is out of
|
|
109
|
+
scope for v0.1; the HTTP-layer approach covers gateway, model, MCP, and
|
|
110
|
+
rate limiter in one mechanism
|
|
111
|
+
|
|
112
|
+
[Unreleased]: https://github.com/golikovichev/pytest-resilience-agent/compare/v0.1.0...HEAD
|
|
113
|
+
[0.1.0]: https://github.com/golikovichev/pytest-resilience-agent/releases/tag/v0.1.0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in pytest-resilience-agent. This is a small alpha project shipped during the DevNetwork AI+ML Hackathon, so the contribution flow is light.
|
|
4
|
+
|
|
5
|
+
## Reporting a bug
|
|
6
|
+
|
|
7
|
+
Open an issue with:
|
|
8
|
+
|
|
9
|
+
- What you ran (pytest invocation, marker selection, Python version)
|
|
10
|
+
- What you expected the resilience scenario to do
|
|
11
|
+
- What happened instead (timeline output on failure helps)
|
|
12
|
+
- A minimal scenario or fixture snippet that reproduces it (strip any real API keys or gateway tokens first)
|
|
13
|
+
|
|
14
|
+
## Suggesting a feature
|
|
15
|
+
|
|
16
|
+
Open an issue first so we can talk through the use case before you write code. The project scope is intentionally narrow: pytest-driven resilience testing for LLM applications via Lark MCP plus TrueFoundry AI Gateway, with rule-based assertions and explicit chaos markers. Feature requests that pull it elsewhere will get a polite redirect.
|
|
17
|
+
|
|
18
|
+
## Submitting a pull request
|
|
19
|
+
|
|
20
|
+
1. Fork the repo and create a branch from `main`.
|
|
21
|
+
2. Make your changes. Keep the diff focused on one thing.
|
|
22
|
+
3. Add or update tests in `tests/`. The CI runs `pytest -v` on Python 3.11, 3.12, and 3.13.
|
|
23
|
+
4. Run the tests locally before pushing:
|
|
24
|
+
```bash
|
|
25
|
+
pip install -e ".[dev]"
|
|
26
|
+
pytest tests -m "not integration"
|
|
27
|
+
```
|
|
28
|
+
5. New scenarios, fixtures, or markers need at least one happy-path test and one chaos-injection test that exercises the timeline output.
|
|
29
|
+
6. Open the PR with a short description of what changed and why.
|
|
30
|
+
|
|
31
|
+
## Code style
|
|
32
|
+
|
|
33
|
+
- Python 3.11+. Type hints on public functions.
|
|
34
|
+
- Function and variable names in English, snake_case (e.g., `inject_timeout`, `assert_recovery`).
|
|
35
|
+
- One responsibility per function. If a function grows past 30-40 lines, split it.
|
|
36
|
+
- Ruff handles lint and formatting. Run `ruff check . && ruff format .` before opening a PR.
|
|
37
|
+
|
|
38
|
+
## Security
|
|
39
|
+
|
|
40
|
+
If you find something that could leak gateway tokens, MCP server credentials, or PII from a real LLM transcript, please report privately. See `SECURITY.md` for the disclosure channel.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mikhail Golikov
|
|
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.
|