pytest-httpchain 0.14.0__tar.gz → 0.14.2__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.14.0 → pytest_httpchain-0.14.2}/PKG-INFO +17 -9
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/README.md +13 -4
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/pyproject.toml +56 -41
- pytest_httpchain-0.14.2/pyproject.toml.orig +205 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/__init__.py +9 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/carrier.py +662 -0
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/cli.py +28 -17
- pytest_httpchain-0.14.2/src/pytest_httpchain/constants.py +23 -0
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/dataflow.py +32 -35
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/errors.py +11 -12
- pytest_httpchain-0.14.2/src/pytest_httpchain/factory.py +136 -0
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/har_writer.py +46 -96
- pytest_httpchain-0.14.2/src/pytest_httpchain/jsonref/__init__.py +18 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/jsonref/exceptions.py +10 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/jsonref/loader.py +23 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/jsonref/plumbing/circular.py +47 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/jsonref/plumbing/path.py +81 -0
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/jsonref/plumbing/reference.py +35 -89
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/models/__init__.py +2 -1
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/models/entities.py +41 -112
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/models/types.py +45 -102
- pytest_httpchain-0.14.2/src/pytest_httpchain/plugin.py +404 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/report_formatter.py +72 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/request_builder.py +131 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/response_steps.py +205 -0
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/schema.py +27 -45
- pytest_httpchain-0.14.2/src/pytest_httpchain/scoping.py +378 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/templates/__init__.py +23 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/templates/expressions.py +30 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/templates/substitution.py +192 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/userfunc.py +113 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/utils.py +118 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/validation/__init__.py +31 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/validation/deep.py +187 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/validation/diagnostics.py +92 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/validation/loader.py +99 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/validation/semantic.py +493 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/validation/validate.py +88 -0
- pytest_httpchain-0.14.2/src/pytest_httpchain/warnings.py +17 -0
- pytest_httpchain-0.14.0/src/pytest_httpchain/__init__.py +0 -30
- pytest_httpchain-0.14.0/src/pytest_httpchain/carrier.py +0 -988
- pytest_httpchain-0.14.0/src/pytest_httpchain/constants.py +0 -47
- pytest_httpchain-0.14.0/src/pytest_httpchain/factory.py +0 -152
- pytest_httpchain-0.14.0/src/pytest_httpchain/jsonref/__init__.py +0 -25
- pytest_httpchain-0.14.0/src/pytest_httpchain/jsonref/exceptions.py +0 -14
- pytest_httpchain-0.14.0/src/pytest_httpchain/jsonref/loader.py +0 -40
- pytest_httpchain-0.14.0/src/pytest_httpchain/jsonref/plumbing/circular.py +0 -86
- pytest_httpchain-0.14.0/src/pytest_httpchain/jsonref/plumbing/path.py +0 -115
- pytest_httpchain-0.14.0/src/pytest_httpchain/plugin.py +0 -433
- pytest_httpchain-0.14.0/src/pytest_httpchain/report_formatter.py +0 -92
- pytest_httpchain-0.14.0/src/pytest_httpchain/scoping.py +0 -441
- pytest_httpchain-0.14.0/src/pytest_httpchain/templates/__init__.py +0 -29
- pytest_httpchain-0.14.0/src/pytest_httpchain/templates/expressions.py +0 -37
- pytest_httpchain-0.14.0/src/pytest_httpchain/templates/substitution.py +0 -222
- pytest_httpchain-0.14.0/src/pytest_httpchain/userfunc.py +0 -170
- pytest_httpchain-0.14.0/src/pytest_httpchain/utils.py +0 -98
- pytest_httpchain-0.14.0/src/pytest_httpchain/validation.py +0 -1092
- pytest_httpchain-0.14.0/src/pytest_httpchain/warnings.py +0 -25
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/LICENSE +0 -0
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/jsonref/plumbing/__init__.py +0 -0
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/py.typed +0 -0
- {pytest_httpchain-0.14.0 → pytest_httpchain-0.14.2}/src/pytest_httpchain/templates/exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-httpchain
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.2
|
|
4
4
|
Summary: pytest plugin for HTTP testing using JSON files
|
|
5
5
|
Keywords: testing,pytest,httpx,http,api,integration-testing
|
|
6
6
|
Author: Alexander Eresov
|
|
@@ -10,11 +10,11 @@ License-File: LICENSE
|
|
|
10
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
11
|
Classifier: Framework :: Pytest
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
13
|
Classifier: Programming Language :: Python :: 3
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
17
16
|
Classifier: Topic :: Software Development :: Testing
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
18
|
Requires-Dist: deepmerge>=2.0
|
|
19
19
|
Requires-Dist: graphql-core>=3.0.0
|
|
20
20
|
Requires-Dist: httpx[http2]>=0.27.0
|
|
@@ -24,18 +24,19 @@ Requires-Dist: pydantic>=2.13.4
|
|
|
24
24
|
Requires-Dist: referencing>=0.28.4
|
|
25
25
|
Requires-Dist: pyrate-limiter>=4.2.0
|
|
26
26
|
Requires-Dist: pytest>=9.0
|
|
27
|
-
Requires-Dist: pytest-order>=1.3.0
|
|
28
27
|
Requires-Dist: simpleeval>=1.0.3
|
|
29
28
|
Requires-Dist: typer>=0.16.0
|
|
30
29
|
Requires-Python: >=3.13
|
|
31
|
-
Project-URL: Changelog, https://github.com/aeresov/pytest-httpchain/blob/main/CHANGELOG.md
|
|
32
|
-
Project-URL: Documentation, https://aeresov.github.io/pytest-httpchain/
|
|
33
30
|
Project-URL: Repository, https://github.com/aeresov/pytest-httpchain
|
|
31
|
+
Project-URL: Documentation, https://aeresov.github.io/pytest-httpchain/
|
|
32
|
+
Project-URL: Changelog, https://github.com/aeresov/pytest-httpchain/blob/main/CHANGELOG.md
|
|
34
33
|
Description-Content-Type: text/markdown
|
|
35
34
|
|
|
36
35
|
[](https://pypi.python.org/pypi/pytest-httpchain)
|
|
37
36
|
[](https://github.com/aeresov/pytest-httpchain/blob/main/LICENSE)
|
|
38
37
|
[](https://pypi.python.org/pypi/pytest-httpchain)
|
|
38
|
+
[](https://github.com/aeresov/pytest-httpchain/actions/workflows/test.yml)
|
|
39
|
+
[](https://aeresov.github.io/pytest-httpchain/)
|
|
39
40
|
|
|
40
41
|
# pytest-httpchain
|
|
41
42
|
|
|
@@ -73,7 +74,7 @@ Each scenario contains 1+ stages executed in order. One stage failure stops the
|
|
|
73
74
|
|
|
74
75
|
### Common data context
|
|
75
76
|
|
|
76
|
-
A key-value store persists throughout scenario execution. Variables, fixtures, and saved response data all live here. Use template expressions (`{{ var }}`)
|
|
77
|
+
A key-value store persists throughout scenario execution. Variables, fixtures, and saved response data all live here. Use template expressions (`{{ var }}`) in any request **value** — substitution happens dynamically before each stage. (Dict keys are not substituted; `HTTPCHAIN029` flags a template in a key.)
|
|
77
78
|
|
|
78
79
|
### Response processing
|
|
79
80
|
|
|
@@ -199,7 +200,7 @@ pip install 'git+https://github.com/aeresov/pytest-httpchain@main'
|
|
|
199
200
|
|
|
200
201
|
- Test file discovery is based on this name pattern: `test_<name>.<suffix>.json`.
|
|
201
202
|
The suffix is configurable via the `httpchain_suffix` pytest ini option, default value is **http**.
|
|
202
|
-
- `$ref` instructions can point to other files using relative paths; absolute paths are rejected for security.
|
|
203
|
+
- `$ref` instructions can point to other files using relative paths; absolute paths are rejected for security, and every reference must resolve inside the root path (pytest's `rootdir` when collecting; `--root-path` for the CLI).
|
|
203
204
|
You can limit the depth of relative path traversal using the `httpchain_ref_parent_traversal_depth` ini option, default value is **3**.
|
|
204
205
|
- Template expressions support list/dict comprehensions. You can limit the maximum comprehension length using the `httpchain_max_comprehension_length` ini option, default value is **50000**.
|
|
205
206
|
- Parallel stage iterations (repeat/foreach) have a safety limit configurable via the `httpchain_max_parallel_iterations` ini option, default value is **10000**.
|
|
@@ -250,7 +251,15 @@ A JSON Schema is published for as-you-type validation and autocomplete. Referenc
|
|
|
250
251
|
}
|
|
251
252
|
```
|
|
252
253
|
|
|
253
|
-
The hosted schema tracks the `main` branch (it is redeployed on every push, so it may describe unreleased changes)
|
|
254
|
+
The hosted schema at the unversioned URL tracks the `main` branch (it is redeployed on every push, so it may describe unreleased changes). To pin the schema for a release, use its versioned URL:
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"$schema": "https://aeresov.github.io/pytest-httpchain/schema/v0.14.0/scenario.schema.json"
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
or emit the schema matching your installed version locally:
|
|
254
263
|
|
|
255
264
|
```bash
|
|
256
265
|
uvx pytest-httpchain schema > scenario.schema.json
|
|
@@ -283,5 +292,4 @@ This project was inspired by [Tavern](https://github.com/taverntesting/tavern) a
|
|
|
283
292
|
[httpx](https://www.python-httpx.org) does comms.
|
|
284
293
|
[Pydantic](https://docs.pydantic.dev) keeps structure.
|
|
285
294
|
[simpleeval](https://github.com/danthedeckie/simpleeval) powers templates.
|
|
286
|
-
[pytest-order](https://github.com/pytest-dev/pytest-order) sorts chain.
|
|
287
295
|
[pytest-datadir](https://github.com/gabrielcnr/pytest-datadir) saved me a lot of elbow grease while testing.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
[](https://pypi.python.org/pypi/pytest-httpchain)
|
|
2
2
|
[](https://github.com/aeresov/pytest-httpchain/blob/main/LICENSE)
|
|
3
3
|
[](https://pypi.python.org/pypi/pytest-httpchain)
|
|
4
|
+
[](https://github.com/aeresov/pytest-httpchain/actions/workflows/test.yml)
|
|
5
|
+
[](https://aeresov.github.io/pytest-httpchain/)
|
|
4
6
|
|
|
5
7
|
# pytest-httpchain
|
|
6
8
|
|
|
@@ -38,7 +40,7 @@ Each scenario contains 1+ stages executed in order. One stage failure stops the
|
|
|
38
40
|
|
|
39
41
|
### Common data context
|
|
40
42
|
|
|
41
|
-
A key-value store persists throughout scenario execution. Variables, fixtures, and saved response data all live here. Use template expressions (`{{ var }}`)
|
|
43
|
+
A key-value store persists throughout scenario execution. Variables, fixtures, and saved response data all live here. Use template expressions (`{{ var }}`) in any request **value** — substitution happens dynamically before each stage. (Dict keys are not substituted; `HTTPCHAIN029` flags a template in a key.)
|
|
42
44
|
|
|
43
45
|
### Response processing
|
|
44
46
|
|
|
@@ -164,7 +166,7 @@ pip install 'git+https://github.com/aeresov/pytest-httpchain@main'
|
|
|
164
166
|
|
|
165
167
|
- Test file discovery is based on this name pattern: `test_<name>.<suffix>.json`.
|
|
166
168
|
The suffix is configurable via the `httpchain_suffix` pytest ini option, default value is **http**.
|
|
167
|
-
- `$ref` instructions can point to other files using relative paths; absolute paths are rejected for security.
|
|
169
|
+
- `$ref` instructions can point to other files using relative paths; absolute paths are rejected for security, and every reference must resolve inside the root path (pytest's `rootdir` when collecting; `--root-path` for the CLI).
|
|
168
170
|
You can limit the depth of relative path traversal using the `httpchain_ref_parent_traversal_depth` ini option, default value is **3**.
|
|
169
171
|
- Template expressions support list/dict comprehensions. You can limit the maximum comprehension length using the `httpchain_max_comprehension_length` ini option, default value is **50000**.
|
|
170
172
|
- Parallel stage iterations (repeat/foreach) have a safety limit configurable via the `httpchain_max_parallel_iterations` ini option, default value is **10000**.
|
|
@@ -215,7 +217,15 @@ A JSON Schema is published for as-you-type validation and autocomplete. Referenc
|
|
|
215
217
|
}
|
|
216
218
|
```
|
|
217
219
|
|
|
218
|
-
The hosted schema tracks the `main` branch (it is redeployed on every push, so it may describe unreleased changes)
|
|
220
|
+
The hosted schema at the unversioned URL tracks the `main` branch (it is redeployed on every push, so it may describe unreleased changes). To pin the schema for a release, use its versioned URL:
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"$schema": "https://aeresov.github.io/pytest-httpchain/schema/v0.14.0/scenario.schema.json"
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
or emit the schema matching your installed version locally:
|
|
219
229
|
|
|
220
230
|
```bash
|
|
221
231
|
uvx pytest-httpchain schema > scenario.schema.json
|
|
@@ -248,5 +258,4 @@ This project was inspired by [Tavern](https://github.com/taverntesting/tavern) a
|
|
|
248
258
|
[httpx](https://www.python-httpx.org) does comms.
|
|
249
259
|
[Pydantic](https://docs.pydantic.dev) keeps structure.
|
|
250
260
|
[simpleeval](https://github.com/danthedeckie/simpleeval) powers templates.
|
|
251
|
-
[pytest-order](https://github.com/pytest-dev/pytest-order) sorts chain.
|
|
252
261
|
[pytest-datadir](https://github.com/gabrielcnr/pytest-datadir) saved me a lot of elbow grease while testing.
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pytest-httpchain"
|
|
3
|
-
version = "0.14.
|
|
3
|
+
version = "0.14.2"
|
|
4
4
|
description = "pytest plugin for HTTP testing using JSON files"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
|
-
authors = [{ name = "Alexander Eresov", email = "aeresov@gmail.com" }]
|
|
8
7
|
dependencies = [
|
|
9
8
|
"deepmerge>=2.0",
|
|
10
9
|
"graphql-core>=3.0.0",
|
|
@@ -15,39 +14,56 @@ dependencies = [
|
|
|
15
14
|
"referencing>=0.28.4",
|
|
16
15
|
"pyrate-limiter>=4.2.0",
|
|
17
16
|
"pytest>=9.0",
|
|
18
|
-
"pytest-order>=1.3.0",
|
|
19
17
|
"simpleeval>=1.0.3",
|
|
20
18
|
"typer>=0.16.0",
|
|
21
19
|
]
|
|
22
|
-
keywords = [
|
|
20
|
+
keywords = [
|
|
21
|
+
"testing",
|
|
22
|
+
"pytest",
|
|
23
|
+
"httpx",
|
|
24
|
+
"http",
|
|
25
|
+
"api",
|
|
26
|
+
"integration-testing",
|
|
27
|
+
]
|
|
23
28
|
license = "MIT"
|
|
24
29
|
license-files = ["LICENSE"]
|
|
25
30
|
classifiers = [
|
|
26
31
|
"Development Status :: 5 - Production/Stable",
|
|
27
32
|
"Framework :: Pytest",
|
|
28
33
|
"Intended Audience :: Developers",
|
|
29
|
-
"License :: OSI Approved :: MIT License",
|
|
30
34
|
"Programming Language :: Python :: 3",
|
|
31
35
|
"Programming Language :: Python :: 3.13",
|
|
32
36
|
"Programming Language :: Python :: 3.14",
|
|
33
37
|
"Topic :: Software Development :: Testing",
|
|
38
|
+
"Typing :: Typed",
|
|
34
39
|
]
|
|
35
40
|
|
|
41
|
+
[[project.authors]]
|
|
42
|
+
name = "Alexander Eresov"
|
|
43
|
+
email = "aeresov@gmail.com"
|
|
44
|
+
|
|
36
45
|
[project.urls]
|
|
37
46
|
Repository = "https://github.com/aeresov/pytest-httpchain"
|
|
38
47
|
Documentation = "https://aeresov.github.io/pytest-httpchain/"
|
|
39
48
|
Changelog = "https://github.com/aeresov/pytest-httpchain/blob/main/CHANGELOG.md"
|
|
40
49
|
|
|
50
|
+
[project.scripts]
|
|
51
|
+
pytest-httpchain = "pytest_httpchain.cli:app"
|
|
52
|
+
|
|
53
|
+
[project.entry-points.pytest11]
|
|
54
|
+
pytest_httpchain = "pytest_httpchain.plugin"
|
|
55
|
+
|
|
41
56
|
[dependency-groups]
|
|
42
57
|
dev = [
|
|
58
|
+
"flask>=3.0",
|
|
43
59
|
"flask-httpauth>=4.8.0",
|
|
44
|
-
"http-server-mock>=1.7",
|
|
45
60
|
"import-linter>=2.0",
|
|
46
61
|
"ruff>=0.12.1",
|
|
47
|
-
"
|
|
48
|
-
"coverage[toml]>=7.0",
|
|
62
|
+
"coverage[toml]>=7.10",
|
|
49
63
|
"pytest-datadir>=1.7.2",
|
|
64
|
+
"pytest-order>=1.3.0",
|
|
50
65
|
"pytest-xdist>=3.8.0",
|
|
66
|
+
"trustme>=1.1.0",
|
|
51
67
|
]
|
|
52
68
|
docs = [
|
|
53
69
|
"mkdocs>=1.6.1",
|
|
@@ -55,21 +71,14 @@ docs = [
|
|
|
55
71
|
]
|
|
56
72
|
|
|
57
73
|
[tool.uv]
|
|
58
|
-
default-groups = [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
build-backend = "uv_build"
|
|
74
|
+
default-groups = [
|
|
75
|
+
"dev",
|
|
76
|
+
"docs",
|
|
77
|
+
]
|
|
63
78
|
|
|
64
79
|
[tool.uv.build-backend]
|
|
65
80
|
source-exclude = ["src/pytest_httpchain/**/CLAUDE.md"]
|
|
66
81
|
|
|
67
|
-
[project.scripts]
|
|
68
|
-
pytest-httpchain = "pytest_httpchain.cli:app"
|
|
69
|
-
|
|
70
|
-
[project.entry-points.pytest11]
|
|
71
|
-
pytest_httpchain = "pytest_httpchain.plugin"
|
|
72
|
-
|
|
73
82
|
[tool.ruff]
|
|
74
83
|
line-length = 180
|
|
75
84
|
target-version = "py313"
|
|
@@ -80,14 +89,15 @@ line-ending = "lf"
|
|
|
80
89
|
|
|
81
90
|
[tool.ruff.lint]
|
|
82
91
|
select = [
|
|
83
|
-
"E",
|
|
84
|
-
"W",
|
|
85
|
-
"F",
|
|
86
|
-
"I",
|
|
87
|
-
"B",
|
|
88
|
-
"C4",
|
|
89
|
-
"UP",
|
|
90
|
-
"
|
|
92
|
+
"E",
|
|
93
|
+
"W",
|
|
94
|
+
"F",
|
|
95
|
+
"I",
|
|
96
|
+
"B",
|
|
97
|
+
"C4",
|
|
98
|
+
"UP",
|
|
99
|
+
"PT",
|
|
100
|
+
"TID252",
|
|
91
101
|
]
|
|
92
102
|
ignore = []
|
|
93
103
|
|
|
@@ -103,10 +113,6 @@ python-version = "3.13"
|
|
|
103
113
|
[tool.ty.terminal]
|
|
104
114
|
error-on-warning = true
|
|
105
115
|
|
|
106
|
-
# Test suites (especially the models package) deliberately construct models with
|
|
107
|
-
# invalid types to assert ValidationError, and access attributes on unions/mocks
|
|
108
|
-
# the checker can't narrow. Relax those specific rules for test code only; src/
|
|
109
|
-
# stays strict and uses pinpoint `# ty: ignore[...]` comments instead.
|
|
110
116
|
[[tool.ty.overrides]]
|
|
111
117
|
include = ["tests/**"]
|
|
112
118
|
|
|
@@ -119,31 +125,38 @@ missing-argument = "ignore"
|
|
|
119
125
|
|
|
120
126
|
[tool.pytest]
|
|
121
127
|
minversion = "9.0"
|
|
122
|
-
|
|
128
|
+
strict = true
|
|
129
|
+
log_cli = false
|
|
123
130
|
log_cli_level = "INFO"
|
|
124
131
|
norecursedirs = ["examples"]
|
|
125
132
|
pytester_example_dir = "tests/integration/examples"
|
|
126
|
-
addopts = [
|
|
127
|
-
|
|
128
|
-
"
|
|
133
|
+
addopts = [
|
|
134
|
+
"--log-disable=werkzeug",
|
|
135
|
+
"--import-mode=importlib",
|
|
129
136
|
]
|
|
137
|
+
pythonpath = ["."]
|
|
130
138
|
testpaths = ["tests"]
|
|
139
|
+
markers = ["slow: subprocess-spawning or sleep-bound integration test; deselect with -m 'not slow'"]
|
|
140
|
+
filterwarnings = [
|
|
141
|
+
"error",
|
|
142
|
+
"default::pytest_httpchain.ScenarioValidationWarning",
|
|
143
|
+
]
|
|
131
144
|
|
|
132
145
|
[tool.coverage.run]
|
|
133
146
|
source = ["src"]
|
|
134
147
|
branch = true
|
|
148
|
+
parallel = true
|
|
149
|
+
patch = ["subprocess"]
|
|
135
150
|
|
|
136
151
|
[tool.coverage.report]
|
|
137
152
|
precision = 2
|
|
138
153
|
show_missing = true
|
|
139
154
|
skip_covered = false
|
|
155
|
+
fail_under = 88
|
|
140
156
|
|
|
141
157
|
[tool.importlinter]
|
|
142
158
|
root_package = "pytest_httpchain"
|
|
143
159
|
|
|
144
|
-
# Exhaustive vertical layering: every top-level module must appear in exactly one
|
|
145
|
-
# layer; a new module fails lint-imports until it is placed. Modules on the same
|
|
146
|
-
# layer (separated by ":") are additionally kept mutually independent.
|
|
147
160
|
[[tool.importlinter.contracts]]
|
|
148
161
|
name = "package layering"
|
|
149
162
|
type = "layers"
|
|
@@ -153,7 +166,7 @@ layers = [
|
|
|
153
166
|
"plugin : cli",
|
|
154
167
|
"factory",
|
|
155
168
|
"carrier : dataflow : schema",
|
|
156
|
-
"validation : har_writer : report_formatter",
|
|
169
|
+
"validation : har_writer : report_formatter : request_builder : response_steps",
|
|
157
170
|
"utils : scoping",
|
|
158
171
|
"userfunc",
|
|
159
172
|
"models",
|
|
@@ -161,10 +174,12 @@ layers = [
|
|
|
161
174
|
"errors : constants : warnings",
|
|
162
175
|
]
|
|
163
176
|
|
|
164
|
-
# Horizontal refinement the layers contract cannot express: models sits above the
|
|
165
|
-
# domain trio and may import templates/userfunc, but must never import jsonref.
|
|
166
177
|
[[tool.importlinter.contracts]]
|
|
167
178
|
name = "models does not import jsonref"
|
|
168
179
|
type = "forbidden"
|
|
169
180
|
source_modules = ["pytest_httpchain.models"]
|
|
170
181
|
forbidden_modules = ["pytest_httpchain.jsonref"]
|
|
182
|
+
|
|
183
|
+
[build-system]
|
|
184
|
+
requires = ["uv_build>=0.11,<0.12"]
|
|
185
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pytest-httpchain"
|
|
3
|
+
version = "0.14.2"
|
|
4
|
+
description = "pytest plugin for HTTP testing using JSON files"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
authors = [{ name = "Alexander Eresov", email = "aeresov@gmail.com" }]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"deepmerge>=2.0",
|
|
10
|
+
"graphql-core>=3.0.0",
|
|
11
|
+
"httpx[http2]>=0.27.0",
|
|
12
|
+
"jmespath>=1.0.0",
|
|
13
|
+
"jsonschema>=4.18.0",
|
|
14
|
+
"pydantic>=2.13.4",
|
|
15
|
+
"referencing>=0.28.4",
|
|
16
|
+
"pyrate-limiter>=4.2.0",
|
|
17
|
+
"pytest>=9.0",
|
|
18
|
+
"simpleeval>=1.0.3",
|
|
19
|
+
"typer>=0.16.0",
|
|
20
|
+
]
|
|
21
|
+
keywords = ["testing", "pytest", "httpx", "http", "api", "integration-testing"]
|
|
22
|
+
license = "MIT"
|
|
23
|
+
license-files = ["LICENSE"]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 5 - Production/Stable",
|
|
26
|
+
"Framework :: Pytest",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
"Programming Language :: Python :: 3.14",
|
|
31
|
+
"Topic :: Software Development :: Testing",
|
|
32
|
+
"Typing :: Typed",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Repository = "https://github.com/aeresov/pytest-httpchain"
|
|
37
|
+
Documentation = "https://aeresov.github.io/pytest-httpchain/"
|
|
38
|
+
Changelog = "https://github.com/aeresov/pytest-httpchain/blob/main/CHANGELOG.md"
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
dev = [
|
|
42
|
+
"flask>=3.0",
|
|
43
|
+
"flask-httpauth>=4.8.0",
|
|
44
|
+
"import-linter>=2.0",
|
|
45
|
+
"ruff>=0.12.1",
|
|
46
|
+
"coverage[toml]>=7.10", # `patch = ["subprocess"]` needs 7.10
|
|
47
|
+
|
|
48
|
+
"pytest-datadir>=1.7.2",
|
|
49
|
+
# Not a runtime dependency: the plugin's own collection hooks own stage
|
|
50
|
+
# ordering. Installed in dev so the ordering regression tests can exercise
|
|
51
|
+
# the regroup's defense against a user-installed pytest-order.
|
|
52
|
+
"pytest-order>=1.3.0",
|
|
53
|
+
"pytest-xdist>=3.8.0",
|
|
54
|
+
"trustme>=1.1.0",
|
|
55
|
+
]
|
|
56
|
+
docs = [
|
|
57
|
+
"mkdocs>=1.6.1",
|
|
58
|
+
"mkdocs-material>=9.6.16",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.uv]
|
|
62
|
+
default-groups = ["dev", "docs"]
|
|
63
|
+
|
|
64
|
+
[build-system]
|
|
65
|
+
requires = ["uv_build>=0.11,<0.12"]
|
|
66
|
+
build-backend = "uv_build"
|
|
67
|
+
|
|
68
|
+
[tool.uv.build-backend]
|
|
69
|
+
source-exclude = ["src/pytest_httpchain/**/CLAUDE.md"]
|
|
70
|
+
|
|
71
|
+
[project.scripts]
|
|
72
|
+
pytest-httpchain = "pytest_httpchain.cli:app"
|
|
73
|
+
|
|
74
|
+
[project.entry-points.pytest11]
|
|
75
|
+
pytest_httpchain = "pytest_httpchain.plugin"
|
|
76
|
+
|
|
77
|
+
[tool.ruff]
|
|
78
|
+
line-length = 180
|
|
79
|
+
target-version = "py313"
|
|
80
|
+
|
|
81
|
+
[tool.ruff.format]
|
|
82
|
+
quote-style = "double"
|
|
83
|
+
line-ending = "lf"
|
|
84
|
+
|
|
85
|
+
[tool.ruff.lint]
|
|
86
|
+
select = [
|
|
87
|
+
"E", # pycodestyle errors
|
|
88
|
+
"W", # pycodestyle warnings
|
|
89
|
+
"F", # pyflakes
|
|
90
|
+
"I", # isort
|
|
91
|
+
"B", # flake8-bugbear
|
|
92
|
+
"C4", # flake8-comprehensions
|
|
93
|
+
"UP", # pyupgrade
|
|
94
|
+
"PT", # flake8-pytest-style
|
|
95
|
+
"TID252", # flake8-tidy-imports: ban relative imports
|
|
96
|
+
]
|
|
97
|
+
ignore = []
|
|
98
|
+
|
|
99
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
100
|
+
ban-relative-imports = "all"
|
|
101
|
+
|
|
102
|
+
[tool.ruff.lint.isort]
|
|
103
|
+
known-first-party = ["tests"]
|
|
104
|
+
|
|
105
|
+
[tool.ty.environment]
|
|
106
|
+
python-version = "3.13"
|
|
107
|
+
|
|
108
|
+
[tool.ty.terminal]
|
|
109
|
+
error-on-warning = true
|
|
110
|
+
|
|
111
|
+
# Test suites (especially the models package) deliberately construct models with
|
|
112
|
+
# invalid types to assert ValidationError, and access attributes on unions/mocks
|
|
113
|
+
# the checker can't narrow. Relax those specific rules for test code only; src/
|
|
114
|
+
# stays strict and uses pinpoint `# ty: ignore[...]` comments instead.
|
|
115
|
+
[[tool.ty.overrides]]
|
|
116
|
+
include = ["tests/**"]
|
|
117
|
+
|
|
118
|
+
[tool.ty.overrides.rules]
|
|
119
|
+
invalid-argument-type = "ignore"
|
|
120
|
+
unknown-argument = "ignore"
|
|
121
|
+
unresolved-attribute = "ignore"
|
|
122
|
+
not-subscriptable = "ignore"
|
|
123
|
+
missing-argument = "ignore"
|
|
124
|
+
|
|
125
|
+
[tool.pytest]
|
|
126
|
+
minversion = "9.0"
|
|
127
|
+
# pytest 9 umbrella: strict_config, strict_markers, strict_xfail,
|
|
128
|
+
# strict_parametrization_ids — unknown ini keys and unregistered markers
|
|
129
|
+
# become errors instead of warnings.
|
|
130
|
+
strict = true
|
|
131
|
+
# Logs are captured and shown only for failing tests (pytest's default). Streaming
|
|
132
|
+
# live INFO for every passing test buried real output ~10-27x; opt back in per-run
|
|
133
|
+
# with `-o log_cli=true --log-cli-level=INFO` when debugging.
|
|
134
|
+
log_cli = false
|
|
135
|
+
log_cli_level = "INFO"
|
|
136
|
+
norecursedirs = ["examples"]
|
|
137
|
+
pytester_example_dir = "tests/integration/examples"
|
|
138
|
+
addopts = ["--log-disable=werkzeug", "--import-mode=importlib"]
|
|
139
|
+
pythonpath = [
|
|
140
|
+
".",
|
|
141
|
+
]
|
|
142
|
+
testpaths = ["tests"]
|
|
143
|
+
markers = [
|
|
144
|
+
"slow: subprocess-spawning or sleep-bound integration test; deselect with -m 'not slow'",
|
|
145
|
+
]
|
|
146
|
+
# Treat warnings as errors so dependency deprecations (e.g. across the 3.13/3.14
|
|
147
|
+
# and lowest-floor matrix) fail loudly instead of rotting. The plugin's own
|
|
148
|
+
# ScenarioValidationWarning is intentional behavior that many tests assert on,
|
|
149
|
+
# so it stays a warning; individual tests still escalate it via -W when needed.
|
|
150
|
+
filterwarnings = [
|
|
151
|
+
"error",
|
|
152
|
+
"default::pytest_httpchain.ScenarioValidationWarning",
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
[tool.coverage.run]
|
|
156
|
+
source = ["src"]
|
|
157
|
+
branch = true
|
|
158
|
+
# Measure pytester subprocess children too (runpytest_subprocess families):
|
|
159
|
+
# `patch` injects startup into every child with this config serialized —
|
|
160
|
+
# data_file/source absolutized, so children report into this project's data
|
|
161
|
+
# files regardless of their CWD. Requires the pid-suffixed parallel data
|
|
162
|
+
# files; run `coverage combine` before `coverage report`.
|
|
163
|
+
parallel = true
|
|
164
|
+
patch = ["subprocess"]
|
|
165
|
+
|
|
166
|
+
[tool.coverage.report]
|
|
167
|
+
precision = 2
|
|
168
|
+
show_missing = true
|
|
169
|
+
skip_covered = false
|
|
170
|
+
# Regression floor for the CI `coverage report` step (full suite under
|
|
171
|
+
# `coverage run -m pytest`, ~95%). Kept a few points below the real number to
|
|
172
|
+
# tolerate platform/Python-version branch differences; ratchet upward as
|
|
173
|
+
# coverage improves rather than letting it silently erode.
|
|
174
|
+
fail_under = 88
|
|
175
|
+
|
|
176
|
+
[tool.importlinter]
|
|
177
|
+
root_package = "pytest_httpchain"
|
|
178
|
+
|
|
179
|
+
# Exhaustive vertical layering: every top-level module must appear in exactly one
|
|
180
|
+
# layer; a new module fails lint-imports until it is placed. Modules on the same
|
|
181
|
+
# layer (separated by ":") are additionally kept mutually independent.
|
|
182
|
+
[[tool.importlinter.contracts]]
|
|
183
|
+
name = "package layering"
|
|
184
|
+
type = "layers"
|
|
185
|
+
containers = ["pytest_httpchain"]
|
|
186
|
+
exhaustive = true
|
|
187
|
+
layers = [
|
|
188
|
+
"plugin : cli",
|
|
189
|
+
"factory",
|
|
190
|
+
"carrier : dataflow : schema",
|
|
191
|
+
"validation : har_writer : report_formatter : request_builder : response_steps",
|
|
192
|
+
"utils : scoping",
|
|
193
|
+
"userfunc",
|
|
194
|
+
"models",
|
|
195
|
+
"templates : jsonref",
|
|
196
|
+
"errors : constants : warnings",
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
# Horizontal refinement the layers contract cannot express: models sits above the
|
|
200
|
+
# domain trio and may import templates/userfunc, but must never import jsonref.
|
|
201
|
+
[[tool.importlinter.contracts]]
|
|
202
|
+
name = "models does not import jsonref"
|
|
203
|
+
type = "forbidden"
|
|
204
|
+
source_modules = ["pytest_httpchain.models"]
|
|
205
|
+
forbidden_modules = ["pytest_httpchain.jsonref"]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""pytest-httpchain: declarative HTTP API integration testing.
|
|
2
|
+
|
|
3
|
+
Test scenarios are JSON files (``test_<name>.http.json``) with ``$ref`` support,
|
|
4
|
+
``{{ expr }}`` templates, and multi-stage request chaining.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pytest_httpchain.warnings import ScenarioValidationWarning
|
|
8
|
+
|
|
9
|
+
__all__ = ["ScenarioValidationWarning"]
|