pytest-httpchain 0.1.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_httpchain-0.1.0/LICENSE +21 -0
- pytest_httpchain-0.1.0/PKG-INFO +245 -0
- pytest_httpchain-0.1.0/README.md +220 -0
- pytest_httpchain-0.1.0/pyproject.toml +112 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/__init__.py +0 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/carrier.py +139 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/carrier_factory.py +92 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/constants.py +10 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/context.py +77 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/exceptions.py +46 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/helpers.py +43 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/plugin.py +192 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/request.py +102 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/response.py +165 -0
- pytest_httpchain-0.1.0/src/pytest_httpchain/stage_executor.py +111 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 aeresov
|
|
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.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytest-httpchain
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: pytest plugin for HTTP testing using JSON files
|
|
5
|
+
Keywords: testing,pytest,requests
|
|
6
|
+
Author: Alexander Eresov
|
|
7
|
+
Author-email: Alexander Eresov <aeresov@gmail.com>
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Framework :: Pytest
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Software Development :: Testing
|
|
16
|
+
Requires-Dist: pydantic>=2.11.7
|
|
17
|
+
Requires-Dist: pytest-httpchain-jsonref
|
|
18
|
+
Requires-Dist: pytest-httpchain-models
|
|
19
|
+
Requires-Dist: pytest-order>=1.3.0
|
|
20
|
+
Requires-Dist: rich>=13.7.0
|
|
21
|
+
Requires-Dist: pytest-httpchain-mcp ; extra == 'mcp'
|
|
22
|
+
Requires-Python: >=3.13, <4.0
|
|
23
|
+
Provides-Extra: mcp
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
[](https://pypi.python.org/pypi/pytest-httpchain)
|
|
27
|
+
[](https://github.com/aeresov/pytest-httpchain/blob/main/LICENSE)
|
|
28
|
+
[](https://pypi.python.org/pypi/pytest-httpchain)
|
|
29
|
+
|
|
30
|
+
# pytest-httpchain
|
|
31
|
+
|
|
32
|
+
A pytest plugin for testing HTTP endpoints.
|
|
33
|
+
|
|
34
|
+
## Overview
|
|
35
|
+
|
|
36
|
+
`pytest-httpchain` is an integration testing framework for HTTP APIs based on battle-hardened [requests](https://requests.readthedocs.io) lib.\
|
|
37
|
+
It aims at helping with common HTTP API testing scenarios, where user needs to make several calls in specific order using data obtained along the way, like auth tokens or resource ids.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Install normally via package manager of your choice from PyPi:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install pytest-httpchain
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
or directly from Github, in case you need a particular ref:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install 'git+https://github.com/aeresov/pytest-httpchain@main'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Optional dependencies
|
|
54
|
+
|
|
55
|
+
The following optional dependencies are available:
|
|
56
|
+
|
|
57
|
+
- `mcp`: installs MCP server package and its starting script. Details in [MCP Server](#mcp-server).
|
|
58
|
+
|
|
59
|
+
## Features
|
|
60
|
+
|
|
61
|
+
### Pytest integration
|
|
62
|
+
|
|
63
|
+
Most of pytest magic can be used: markers, fixtures, other plugins.\
|
|
64
|
+
|
|
65
|
+
> NOTE: parametrization is not yet implemented, therefore `parametrize` marker won't have any effect.
|
|
66
|
+
|
|
67
|
+
### Declarative format
|
|
68
|
+
|
|
69
|
+
Test scenarios are written declaratively in JSON files.\
|
|
70
|
+
`pytest-httpchain` supports JSONRef, so use can reuse arbitrary parts of your scenarios with `$ref` directive.\
|
|
71
|
+
Properties are merged in a greedy way with type checking.
|
|
72
|
+
|
|
73
|
+
### Multi-stage tests
|
|
74
|
+
|
|
75
|
+
Each test scenario contains 1+ stages; each stage is a single HTTP call.\
|
|
76
|
+
`pytest-httpchain` executes stages in the order they are listed in scenario file; one stage failure stops the execution chain.
|
|
77
|
+
|
|
78
|
+
### Common data context and variable substitution
|
|
79
|
+
|
|
80
|
+
`pytest-httpchain` maintains key-value data storage throughout the execution.\
|
|
81
|
+
This storage ("common data context") is populated with declared variables, fixtures and data saved by stages. The data remains there throughout the scenario execution.\
|
|
82
|
+
Writing scenarios, you can use Jinja-style expressions like `"{{ var }}"` for JSON values. `pytest-httpchain` does variable substitution dynamically right before executing a stage, and uses common data context keys as variables in these expressions.\
|
|
83
|
+
Values from common data context also might be verified during verified/asserted.
|
|
84
|
+
|
|
85
|
+
### User functions
|
|
86
|
+
|
|
87
|
+
`pytest-httpchain` can import and call regular python functions:
|
|
88
|
+
|
|
89
|
+
- to extract data from HTTP response
|
|
90
|
+
- to verify HTTP response and values in common data context
|
|
91
|
+
- to provide [custom authentication for requests](https://requests.readthedocs.io/en/latest/user/advanced/#custom-authentication)
|
|
92
|
+
|
|
93
|
+
### JMESPath support
|
|
94
|
+
|
|
95
|
+
`pytest-httpchain` can extract values from JSON responses using JMESPath expressions directly.
|
|
96
|
+
|
|
97
|
+
### JSON schema support
|
|
98
|
+
|
|
99
|
+
`pytest-httpchain` can verify JSON reponses against user-defined JSON schema.
|
|
100
|
+
|
|
101
|
+
## Quick Start
|
|
102
|
+
|
|
103
|
+
Create a JSON test file named like `test_<name>.<suffix>.json` (default suffix is `http`):
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
# conftest.py
|
|
107
|
+
import pytest
|
|
108
|
+
from datetime import datetime
|
|
109
|
+
|
|
110
|
+
@pytest.fixture
|
|
111
|
+
def now_utc():
|
|
112
|
+
return datetime.now()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"vars": {
|
|
118
|
+
"user_id": 1
|
|
119
|
+
},
|
|
120
|
+
"stages": [
|
|
121
|
+
{
|
|
122
|
+
"name": "get_user",
|
|
123
|
+
"request": {
|
|
124
|
+
"url": "https://api.example.com/users/{{ user_id }}"
|
|
125
|
+
},
|
|
126
|
+
"response": [
|
|
127
|
+
{
|
|
128
|
+
"verify": {
|
|
129
|
+
"status": 200
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"save": {
|
|
134
|
+
"vars": {
|
|
135
|
+
"user_name": "user.name"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"name": "update_user",
|
|
143
|
+
"fixtures": ["now_utc"],
|
|
144
|
+
"request": {
|
|
145
|
+
"url": "https://api.example.com/users/{{ user_id }}",
|
|
146
|
+
"method": "PUT",
|
|
147
|
+
"body": {
|
|
148
|
+
"json": {
|
|
149
|
+
"user": {
|
|
150
|
+
"name": "{{ user_name }}_updated",
|
|
151
|
+
"timestamp": "{{ str(now_utc) }}"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"response": [
|
|
157
|
+
{
|
|
158
|
+
"verify": {
|
|
159
|
+
"status": 200
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "cleanup",
|
|
166
|
+
"always_run": true,
|
|
167
|
+
"request": {
|
|
168
|
+
"url": "https://api.example.com/cleanup",
|
|
169
|
+
"method": "POST"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Scenario we created:
|
|
177
|
+
|
|
178
|
+
- common data context is seeded with the first variable `user_id`
|
|
179
|
+
- **get_user**\
|
|
180
|
+
url is assembled using `user_id` variable from common data context\
|
|
181
|
+
HTTP GET call is made\
|
|
182
|
+
we verify the call returned code 200\
|
|
183
|
+
assuming JSON body is returned, we extract a value by JMESPath expression `user.name` and save it to common data context under `user_name` key
|
|
184
|
+
- **update_user**\
|
|
185
|
+
`now_utc` fixture value is injected into common data context\
|
|
186
|
+
url is assembled using `user_id` variable from common data context\
|
|
187
|
+
we create JSON body in place using values from common data context, note that `now_utc` is converted to string in place\
|
|
188
|
+
HTTP PUT call with body is made\
|
|
189
|
+
we verify the call returned code 200\
|
|
190
|
+
- **cleanup**\
|
|
191
|
+
finalizing call meant for graceful exit\
|
|
192
|
+
`always_run` parameter means this stage will be executed regardless of errors in previous stages
|
|
193
|
+
|
|
194
|
+
For detailed examples see [USAGE.md](USAGE.md).
|
|
195
|
+
|
|
196
|
+
## Configuration
|
|
197
|
+
|
|
198
|
+
- Test file discovery is based on this name pattern: `test_<name>.<suffix>.json`.\
|
|
199
|
+
The `suffix` is configurable as pytest ini option, default value is **http**.
|
|
200
|
+
- `$ref` instructions can point to other files; absolute and relative paths are supported.\
|
|
201
|
+
You can limit the depth of relative path traversal using `ref_parent_traversal_depth` ini option, default value is **3**.
|
|
202
|
+
|
|
203
|
+
## MCP Server
|
|
204
|
+
|
|
205
|
+
`pytest-httpchain` includes an MCP (Model Context Protocol) server to aid AI code assistants.
|
|
206
|
+
|
|
207
|
+
### Installation
|
|
208
|
+
|
|
209
|
+
The optional dependency `mcp` installs MCP server's package and `pytest-httpchain-mcp` script.\
|
|
210
|
+
Use this script as call target for your MCP configuration.
|
|
211
|
+
|
|
212
|
+
Claude Code `.mcp.json` example:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"mcpServers": {
|
|
217
|
+
"pytest-httpchain": {
|
|
218
|
+
"type": "stdio",
|
|
219
|
+
"command": "uv",
|
|
220
|
+
"args": ["run", "pytest-httpchain-mcp"],
|
|
221
|
+
"env": {}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Features
|
|
228
|
+
|
|
229
|
+
The MCP server provides:
|
|
230
|
+
|
|
231
|
+
- **Scenario validation** - validate test scenario and scan for possible problems
|
|
232
|
+
|
|
233
|
+
## Documentation
|
|
234
|
+
|
|
235
|
+
- [Usage Examples](USAGE.md) - Practical code examples
|
|
236
|
+
- [Full Documentation](https://aeresov.github.io/pytest-httpchain) - Complete guide
|
|
237
|
+
- [Changelog](CHANGELOG.md) - Release notes
|
|
238
|
+
|
|
239
|
+
## Thanks
|
|
240
|
+
|
|
241
|
+
`pytest-httpchain` was heavily inspired by [Tavern](https://github.com/taverntesting/tavern) and [pytest-play](https://github.com/davidemoro/pytest-play).
|
|
242
|
+
[requests](https://requests.readthedocs.io) does the comms.
|
|
243
|
+
[Pydantic](https://docs.pydantic.dev) keeps the structure.
|
|
244
|
+
[pytest-order](https://github.com/pytest-dev/pytest-order) powers the chaining.
|
|
245
|
+
[pytest-datadir](https://github.com/gabrielcnr/pytest-datadir) saved me a lot of elbow grease.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
[](https://pypi.python.org/pypi/pytest-httpchain)
|
|
2
|
+
[](https://github.com/aeresov/pytest-httpchain/blob/main/LICENSE)
|
|
3
|
+
[](https://pypi.python.org/pypi/pytest-httpchain)
|
|
4
|
+
|
|
5
|
+
# pytest-httpchain
|
|
6
|
+
|
|
7
|
+
A pytest plugin for testing HTTP endpoints.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
`pytest-httpchain` is an integration testing framework for HTTP APIs based on battle-hardened [requests](https://requests.readthedocs.io) lib.\
|
|
12
|
+
It aims at helping with common HTTP API testing scenarios, where user needs to make several calls in specific order using data obtained along the way, like auth tokens or resource ids.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Install normally via package manager of your choice from PyPi:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install pytest-httpchain
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
or directly from Github, in case you need a particular ref:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install 'git+https://github.com/aeresov/pytest-httpchain@main'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Optional dependencies
|
|
29
|
+
|
|
30
|
+
The following optional dependencies are available:
|
|
31
|
+
|
|
32
|
+
- `mcp`: installs MCP server package and its starting script. Details in [MCP Server](#mcp-server).
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
### Pytest integration
|
|
37
|
+
|
|
38
|
+
Most of pytest magic can be used: markers, fixtures, other plugins.\
|
|
39
|
+
|
|
40
|
+
> NOTE: parametrization is not yet implemented, therefore `parametrize` marker won't have any effect.
|
|
41
|
+
|
|
42
|
+
### Declarative format
|
|
43
|
+
|
|
44
|
+
Test scenarios are written declaratively in JSON files.\
|
|
45
|
+
`pytest-httpchain` supports JSONRef, so use can reuse arbitrary parts of your scenarios with `$ref` directive.\
|
|
46
|
+
Properties are merged in a greedy way with type checking.
|
|
47
|
+
|
|
48
|
+
### Multi-stage tests
|
|
49
|
+
|
|
50
|
+
Each test scenario contains 1+ stages; each stage is a single HTTP call.\
|
|
51
|
+
`pytest-httpchain` executes stages in the order they are listed in scenario file; one stage failure stops the execution chain.
|
|
52
|
+
|
|
53
|
+
### Common data context and variable substitution
|
|
54
|
+
|
|
55
|
+
`pytest-httpchain` maintains key-value data storage throughout the execution.\
|
|
56
|
+
This storage ("common data context") is populated with declared variables, fixtures and data saved by stages. The data remains there throughout the scenario execution.\
|
|
57
|
+
Writing scenarios, you can use Jinja-style expressions like `"{{ var }}"` for JSON values. `pytest-httpchain` does variable substitution dynamically right before executing a stage, and uses common data context keys as variables in these expressions.\
|
|
58
|
+
Values from common data context also might be verified during verified/asserted.
|
|
59
|
+
|
|
60
|
+
### User functions
|
|
61
|
+
|
|
62
|
+
`pytest-httpchain` can import and call regular python functions:
|
|
63
|
+
|
|
64
|
+
- to extract data from HTTP response
|
|
65
|
+
- to verify HTTP response and values in common data context
|
|
66
|
+
- to provide [custom authentication for requests](https://requests.readthedocs.io/en/latest/user/advanced/#custom-authentication)
|
|
67
|
+
|
|
68
|
+
### JMESPath support
|
|
69
|
+
|
|
70
|
+
`pytest-httpchain` can extract values from JSON responses using JMESPath expressions directly.
|
|
71
|
+
|
|
72
|
+
### JSON schema support
|
|
73
|
+
|
|
74
|
+
`pytest-httpchain` can verify JSON reponses against user-defined JSON schema.
|
|
75
|
+
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
Create a JSON test file named like `test_<name>.<suffix>.json` (default suffix is `http`):
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
# conftest.py
|
|
82
|
+
import pytest
|
|
83
|
+
from datetime import datetime
|
|
84
|
+
|
|
85
|
+
@pytest.fixture
|
|
86
|
+
def now_utc():
|
|
87
|
+
return datetime.now()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"vars": {
|
|
93
|
+
"user_id": 1
|
|
94
|
+
},
|
|
95
|
+
"stages": [
|
|
96
|
+
{
|
|
97
|
+
"name": "get_user",
|
|
98
|
+
"request": {
|
|
99
|
+
"url": "https://api.example.com/users/{{ user_id }}"
|
|
100
|
+
},
|
|
101
|
+
"response": [
|
|
102
|
+
{
|
|
103
|
+
"verify": {
|
|
104
|
+
"status": 200
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"save": {
|
|
109
|
+
"vars": {
|
|
110
|
+
"user_name": "user.name"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"name": "update_user",
|
|
118
|
+
"fixtures": ["now_utc"],
|
|
119
|
+
"request": {
|
|
120
|
+
"url": "https://api.example.com/users/{{ user_id }}",
|
|
121
|
+
"method": "PUT",
|
|
122
|
+
"body": {
|
|
123
|
+
"json": {
|
|
124
|
+
"user": {
|
|
125
|
+
"name": "{{ user_name }}_updated",
|
|
126
|
+
"timestamp": "{{ str(now_utc) }}"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"response": [
|
|
132
|
+
{
|
|
133
|
+
"verify": {
|
|
134
|
+
"status": 200
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"name": "cleanup",
|
|
141
|
+
"always_run": true,
|
|
142
|
+
"request": {
|
|
143
|
+
"url": "https://api.example.com/cleanup",
|
|
144
|
+
"method": "POST"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Scenario we created:
|
|
152
|
+
|
|
153
|
+
- common data context is seeded with the first variable `user_id`
|
|
154
|
+
- **get_user**\
|
|
155
|
+
url is assembled using `user_id` variable from common data context\
|
|
156
|
+
HTTP GET call is made\
|
|
157
|
+
we verify the call returned code 200\
|
|
158
|
+
assuming JSON body is returned, we extract a value by JMESPath expression `user.name` and save it to common data context under `user_name` key
|
|
159
|
+
- **update_user**\
|
|
160
|
+
`now_utc` fixture value is injected into common data context\
|
|
161
|
+
url is assembled using `user_id` variable from common data context\
|
|
162
|
+
we create JSON body in place using values from common data context, note that `now_utc` is converted to string in place\
|
|
163
|
+
HTTP PUT call with body is made\
|
|
164
|
+
we verify the call returned code 200\
|
|
165
|
+
- **cleanup**\
|
|
166
|
+
finalizing call meant for graceful exit\
|
|
167
|
+
`always_run` parameter means this stage will be executed regardless of errors in previous stages
|
|
168
|
+
|
|
169
|
+
For detailed examples see [USAGE.md](USAGE.md).
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
- Test file discovery is based on this name pattern: `test_<name>.<suffix>.json`.\
|
|
174
|
+
The `suffix` is configurable as pytest ini option, default value is **http**.
|
|
175
|
+
- `$ref` instructions can point to other files; absolute and relative paths are supported.\
|
|
176
|
+
You can limit the depth of relative path traversal using `ref_parent_traversal_depth` ini option, default value is **3**.
|
|
177
|
+
|
|
178
|
+
## MCP Server
|
|
179
|
+
|
|
180
|
+
`pytest-httpchain` includes an MCP (Model Context Protocol) server to aid AI code assistants.
|
|
181
|
+
|
|
182
|
+
### Installation
|
|
183
|
+
|
|
184
|
+
The optional dependency `mcp` installs MCP server's package and `pytest-httpchain-mcp` script.\
|
|
185
|
+
Use this script as call target for your MCP configuration.
|
|
186
|
+
|
|
187
|
+
Claude Code `.mcp.json` example:
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"mcpServers": {
|
|
192
|
+
"pytest-httpchain": {
|
|
193
|
+
"type": "stdio",
|
|
194
|
+
"command": "uv",
|
|
195
|
+
"args": ["run", "pytest-httpchain-mcp"],
|
|
196
|
+
"env": {}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Features
|
|
203
|
+
|
|
204
|
+
The MCP server provides:
|
|
205
|
+
|
|
206
|
+
- **Scenario validation** - validate test scenario and scan for possible problems
|
|
207
|
+
|
|
208
|
+
## Documentation
|
|
209
|
+
|
|
210
|
+
- [Usage Examples](USAGE.md) - Practical code examples
|
|
211
|
+
- [Full Documentation](https://aeresov.github.io/pytest-httpchain) - Complete guide
|
|
212
|
+
- [Changelog](CHANGELOG.md) - Release notes
|
|
213
|
+
|
|
214
|
+
## Thanks
|
|
215
|
+
|
|
216
|
+
`pytest-httpchain` was heavily inspired by [Tavern](https://github.com/taverntesting/tavern) and [pytest-play](https://github.com/davidemoro/pytest-play).
|
|
217
|
+
[requests](https://requests.readthedocs.io) does the comms.
|
|
218
|
+
[Pydantic](https://docs.pydantic.dev) keeps the structure.
|
|
219
|
+
[pytest-order](https://github.com/pytest-dev/pytest-order) powers the chaining.
|
|
220
|
+
[pytest-datadir](https://github.com/gabrielcnr/pytest-datadir) saved me a lot of elbow grease.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pytest-httpchain"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "pytest plugin for HTTP testing using JSON files"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13,<4.0"
|
|
7
|
+
authors = [{ name = "Alexander Eresov", email = "aeresov@gmail.com" }]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"pydantic>=2.11.7",
|
|
10
|
+
"pytest-httpchain-jsonref",
|
|
11
|
+
"pytest-httpchain-models",
|
|
12
|
+
"pytest-order>=1.3.0",
|
|
13
|
+
"rich>=13.7.0",
|
|
14
|
+
]
|
|
15
|
+
keywords = ["testing", "pytest", "requests"]
|
|
16
|
+
license-files = ["LICENSE"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 5 - Production/Stable",
|
|
19
|
+
"Framework :: Pytest",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Testing",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
mcp = ["pytest-httpchain-mcp"]
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"flask-httpauth>=4.8.0",
|
|
33
|
+
"http-server-mock>=1.7",
|
|
34
|
+
"pytest-xdist>=3.8.0",
|
|
35
|
+
"responses>=0.25.7",
|
|
36
|
+
"ruff>=0.12.1",
|
|
37
|
+
]
|
|
38
|
+
docs = [
|
|
39
|
+
"mkdocs>=1.6.1",
|
|
40
|
+
"mkdocs-material>=9.6.16",
|
|
41
|
+
"mkdocstrings>=0.30.0",
|
|
42
|
+
"mkdocstrings-python>=1.16.12",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[tool.uv]
|
|
46
|
+
default-groups = ["docs"]
|
|
47
|
+
|
|
48
|
+
[tool.uv.workspace]
|
|
49
|
+
members = ["packages/*"]
|
|
50
|
+
|
|
51
|
+
[tool.uv.sources]
|
|
52
|
+
pytest-httpchain-jsonref = { workspace = true }
|
|
53
|
+
pytest-httpchain-templates = { workspace = true }
|
|
54
|
+
pytest-httpchain-mcp = { workspace = true }
|
|
55
|
+
pytest-httpchain-models = { workspace = true }
|
|
56
|
+
pytest-httpchain-userfunc = { workspace = true }
|
|
57
|
+
|
|
58
|
+
[build-system]
|
|
59
|
+
requires = ["uv_build>=0.7.21,<0.8.0"]
|
|
60
|
+
build-backend = "uv_build"
|
|
61
|
+
|
|
62
|
+
[project.entry-points.pytest11]
|
|
63
|
+
pytest_httpchain = "pytest_httpchain.plugin"
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 180
|
|
67
|
+
target-version = "py313"
|
|
68
|
+
|
|
69
|
+
[tool.ruff.format]
|
|
70
|
+
quote-style = "double"
|
|
71
|
+
line-ending = "lf"
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint]
|
|
74
|
+
select = [
|
|
75
|
+
"E", # pycodestyle errors
|
|
76
|
+
"W", # pycodestyle warnings
|
|
77
|
+
"F", # pyflakes
|
|
78
|
+
"I", # isort
|
|
79
|
+
"B", # flake8-bugbear
|
|
80
|
+
"C4", # flake8-comprehensions
|
|
81
|
+
"UP", # pyupgrade
|
|
82
|
+
]
|
|
83
|
+
ignore = []
|
|
84
|
+
|
|
85
|
+
[tool.ruff.lint.isort]
|
|
86
|
+
known-first-party = ["tests", "utils"]
|
|
87
|
+
|
|
88
|
+
[tool.pytest.ini_options]
|
|
89
|
+
log_cli = true
|
|
90
|
+
log_cli_level = "INFO"
|
|
91
|
+
# testpaths = ["tests"]
|
|
92
|
+
norecursedirs = ["examples"]
|
|
93
|
+
pytester_example_dir = "tests/integration/examples"
|
|
94
|
+
addopts = ["--log-disable=werkzeug", "--import-mode=importlib"]
|
|
95
|
+
pythonpath = [
|
|
96
|
+
".",
|
|
97
|
+
"packages/pytest-httpchain-jsonref/src",
|
|
98
|
+
"packages/pytest-httpchain-templates/src",
|
|
99
|
+
"packages/pytest-httpchain-mcp/src",
|
|
100
|
+
"packages/pytest-httpchain-models/src",
|
|
101
|
+
"packages/pytest-httpchain-userfunc/src",
|
|
102
|
+
]
|
|
103
|
+
testpaths = ["tests", "packages/*/tests"]
|
|
104
|
+
|
|
105
|
+
[tool.pyright]
|
|
106
|
+
exclude = [".tox/"]
|
|
107
|
+
ignore = [".venv/"]
|
|
108
|
+
reportMissingImports = "error"
|
|
109
|
+
reportMissingTypeStubs = false
|
|
110
|
+
reportExplicitAny = false
|
|
111
|
+
typeCheckingMode = "off"
|
|
112
|
+
pythonPlatform = "Linux"
|
|
File without changes
|