specsentinel 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.
- specsentinel-0.1.0/LICENSE +21 -0
- specsentinel-0.1.0/PKG-INFO +216 -0
- specsentinel-0.1.0/README.md +196 -0
- specsentinel-0.1.0/pyproject.toml +35 -0
- specsentinel-0.1.0/setup.cfg +4 -0
- specsentinel-0.1.0/src/specsentinel/__init__.py +1 -0
- specsentinel-0.1.0/src/specsentinel/__main__.py +5 -0
- specsentinel-0.1.0/src/specsentinel/checker.py +272 -0
- specsentinel-0.1.0/src/specsentinel/cli.py +119 -0
- specsentinel-0.1.0/src/specsentinel/report.py +75 -0
- specsentinel-0.1.0/src/specsentinel/runner.py +198 -0
- specsentinel-0.1.0/src/specsentinel/spec.py +77 -0
- specsentinel-0.1.0/src/specsentinel.egg-info/PKG-INFO +216 -0
- specsentinel-0.1.0/src/specsentinel.egg-info/SOURCES.txt +19 -0
- specsentinel-0.1.0/src/specsentinel.egg-info/dependency_links.txt +1 -0
- specsentinel-0.1.0/src/specsentinel.egg-info/entry_points.txt +2 -0
- specsentinel-0.1.0/src/specsentinel.egg-info/requires.txt +4 -0
- specsentinel-0.1.0/src/specsentinel.egg-info/top_level.txt +1 -0
- specsentinel-0.1.0/tests/test_checker.py +161 -0
- specsentinel-0.1.0/tests/test_e2e.py +152 -0
- specsentinel-0.1.0/tests/test_smoke.py +28 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 b6bs62fhys-jpg
|
|
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,216 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: specsentinel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Detects when a live API drifts from its OpenAPI spec. Exit code 0 on match, 1 on drift, 2 if the check could not run.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/b6bs62fhys-jpg/specsentinel
|
|
7
|
+
Project-URL: Issues, https://github.com/b6bs62fhys-jpg/specsentinel/issues
|
|
8
|
+
Keywords: openapi,api,contract,drift,testing,cli
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Topic :: Software Development :: Testing
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: PyYAML>=6
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# SpecSentinel
|
|
22
|
+
|
|
23
|
+
Your OpenAPI spec says one thing. Your API does another. SpecSentinel calls the running API, compares every answer with the spec and tells you where they disagree.
|
|
24
|
+
|
|
25
|
+
It reports three kinds of drift:
|
|
26
|
+
|
|
27
|
+
* missing fields
|
|
28
|
+
* wrong data types
|
|
29
|
+
* undocumented status codes
|
|
30
|
+
|
|
31
|
+
Exit code 0 means spec and API match. Exit code 1 means drift. Exit code 2 means the check could not be completed. That makes it a one line gate in a CI/CD pipeline.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
Python 3.9 or newer.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
pip install git+https://github.com/b6bs62fhys-jpg/specsentinel
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or from a clone of this repository:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
pip install .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Try it in one minute
|
|
48
|
+
|
|
49
|
+
The repository ships a demo API that drifts from its spec on purpose.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
python examples/demo_server.py &
|
|
53
|
+
specsentinel examples/petstore.yaml --url http://127.0.0.1:8099
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Real output:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
SpecSentinel 0.1.0
|
|
60
|
+
Spec: examples/petstore.yaml
|
|
61
|
+
Target: http://127.0.0.1:8099
|
|
62
|
+
|
|
63
|
+
GET /health 200 OK
|
|
64
|
+
GET /pets 200 OK
|
|
65
|
+
GET /pets/{petId} 200 DRIFT
|
|
66
|
+
error MISSING_FIELD body.name
|
|
67
|
+
required field is missing in the response
|
|
68
|
+
error TYPE_MISMATCH body.id
|
|
69
|
+
expected integer, got string
|
|
70
|
+
warning UNDOCUMENTED_FIELD body.breed
|
|
71
|
+
field is returned but not described in the spec
|
|
72
|
+
GET /stats 503 DRIFT
|
|
73
|
+
error UNDOCUMENTED_STATUS status
|
|
74
|
+
status 503 is not documented (documented: 200)
|
|
75
|
+
GET /owners/{ownerId} - SKIPPED
|
|
76
|
+
no example value for path parameter 'ownerId' (pass --param ownerId=VALUE)
|
|
77
|
+
|
|
78
|
+
4 checked, 2 with drift, 1 skipped, 0 failed
|
|
79
|
+
Result: DRIFT (exit code 1)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Start the demo with `--conform` and the same command ends with `Result: MATCH (exit code 0)`.
|
|
83
|
+
|
|
84
|
+
## Exit codes
|
|
85
|
+
|
|
86
|
+
| Code | Meaning |
|
|
87
|
+
|------|---------|
|
|
88
|
+
| 0 | Every checked operation matches the spec |
|
|
89
|
+
| 1 | At least one operation drifts from the spec |
|
|
90
|
+
| 2 | The check could not be completed: bad spec, bad arguments, API unreachable, or nothing could be checked |
|
|
91
|
+
|
|
92
|
+
Code 2 is deliberately not 0. A pipeline should never turn green because nothing was actually checked.
|
|
93
|
+
|
|
94
|
+
## What counts as drift
|
|
95
|
+
|
|
96
|
+
| Finding | Severity | Meaning |
|
|
97
|
+
|---------|----------|---------|
|
|
98
|
+
| UNDOCUMENTED_STATUS | error | The API answered with a status code the spec does not list. Ranges like 4XX and `default` are honored. |
|
|
99
|
+
| MISSING_FIELD | error | A field marked required in the spec is not in the response. |
|
|
100
|
+
| TYPE_MISMATCH | error | A value has a different JSON type than the spec says. `nullable` and type lists are understood. |
|
|
101
|
+
| ENUM_MISMATCH | error | A value is not one of the enum values in the spec. |
|
|
102
|
+
| UNDOCUMENTED_CONTENT_TYPE | error | The response content type is not listed for that status. |
|
|
103
|
+
| INVALID_JSON | error | The spec promises JSON but the body is not valid JSON. |
|
|
104
|
+
| EMPTY_BODY | error | The spec documents a body but the response is empty. |
|
|
105
|
+
| NO_SCHEMA_MATCH | error | A value fits none of the oneOf or anyOf alternatives. |
|
|
106
|
+
| UNDOCUMENTED_FIELD | warning | A field is returned that the spec does not describe. An error when the schema sets `additionalProperties: false`. |
|
|
107
|
+
|
|
108
|
+
Warnings do not fail the run. Add `--strict` and they do.
|
|
109
|
+
|
|
110
|
+
## Options
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
specsentinel SPEC --url BASE_URL [options]
|
|
114
|
+
|
|
115
|
+
SPEC path or URL of the OpenAPI 3.x document, YAML or JSON
|
|
116
|
+
--url BASE_URL base URL of the running API
|
|
117
|
+
-H, --header 'N: v' header sent with every request, repeatable
|
|
118
|
+
--param NAME=VALUE value for a path or query parameter, repeatable
|
|
119
|
+
--params-file FILE YAML or JSON file with parameter values
|
|
120
|
+
--timeout SECONDS wait per request, default 10
|
|
121
|
+
--strict treat warnings as drift
|
|
122
|
+
--format text|json output format, default text
|
|
123
|
+
--version
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Use `--format json` when another tool should read the result.
|
|
127
|
+
|
|
128
|
+
## How requests are built
|
|
129
|
+
|
|
130
|
+
SpecSentinel sends GET requests only. Methods that change data could damage the API under test, so they are out of scope by design.
|
|
131
|
+
|
|
132
|
+
Parameter values come from the spec: `example`, `examples`, the schema `example`, `default`, or the first `enum` value. A path parameter without any of these cannot be filled in automatically. That operation is reported as SKIPPED with the exact `--param` to pass. Skipped operations do not fail the run.
|
|
133
|
+
|
|
134
|
+
### Params file
|
|
135
|
+
|
|
136
|
+
Large APIs have many operations with path parameters. Put the values in a file instead of a long command line:
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
# params.yaml
|
|
140
|
+
petId: 1 # used by every operation with a parameter called petId
|
|
141
|
+
ownerId: 7
|
|
142
|
+
GET /pets/{petId}: # applies to this one operation only
|
|
143
|
+
petId: 42
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
specsentinel openapi.yaml --url https://staging.example.com --params-file params.yaml
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Order of precedence, later wins: spec example, params file, params file entry for one operation, `--param` on the command line.
|
|
151
|
+
|
|
152
|
+
For authentication pass the header yourself:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
specsentinel openapi.yaml --url https://staging.example.com \
|
|
156
|
+
--header "Authorization: Bearer $API_TOKEN"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## GitHub Actions
|
|
160
|
+
|
|
161
|
+
```yaml
|
|
162
|
+
name: API contract
|
|
163
|
+
on: [push, pull_request]
|
|
164
|
+
|
|
165
|
+
jobs:
|
|
166
|
+
drift:
|
|
167
|
+
runs-on: ubuntu-latest
|
|
168
|
+
steps:
|
|
169
|
+
- uses: actions/checkout@v4
|
|
170
|
+
- uses: actions/setup-python@v5
|
|
171
|
+
with:
|
|
172
|
+
python-version: "3.12"
|
|
173
|
+
- run: pip install git+https://github.com/b6bs62fhys-jpg/specsentinel
|
|
174
|
+
- run: >
|
|
175
|
+
specsentinel openapi.yaml
|
|
176
|
+
--url https://staging.example.com
|
|
177
|
+
--header "Authorization: Bearer ${{ secrets.API_TOKEN }}"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
When the API drifts, exit code 1 fails the step and the build goes red.
|
|
181
|
+
|
|
182
|
+
## Tested against real specifications
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
The schema checks were run against the public OpenAPI descriptions of Swagger Petstore, GitHub and Stripe: 920 GET operations and 2032 documented JSON responses in total. For every response a conforming example was generated from its schema and fed through the checker. It produced no findings and no crashes.
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
You can repeat the run yourself with `python tools/spec_smoke.py <spec file or URL>`. The exact output is in `docs/smoke_results.md`.
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
The generator and the checker share the same reading of the schema, so this run shows that the checker raises no false alarms on large real specifications. It does not show that every kind of drift is caught, and it does not replace running SpecSentinel against your own live API.
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
## Limits of this version
|
|
195
|
+
|
|
196
|
+
This is an early release. Known limits:
|
|
197
|
+
|
|
198
|
+
* GET operations only
|
|
199
|
+
* JSON response bodies only, other content types are not compared
|
|
200
|
+
* response headers are not compared
|
|
201
|
+
* only local `$ref` references, no references to other files
|
|
202
|
+
* allOf is merged, oneOf and anyOf pass when any alternative fits
|
|
203
|
+
* no string formats, lengths or numeric ranges yet
|
|
204
|
+
|
|
205
|
+
Feedback on which check should come next is very welcome. Open an issue.
|
|
206
|
+
|
|
207
|
+
## Development
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
pip install -e ".[dev]"
|
|
211
|
+
pytest
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT, see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# SpecSentinel
|
|
2
|
+
|
|
3
|
+
Your OpenAPI spec says one thing. Your API does another. SpecSentinel calls the running API, compares every answer with the spec and tells you where they disagree.
|
|
4
|
+
|
|
5
|
+
It reports three kinds of drift:
|
|
6
|
+
|
|
7
|
+
* missing fields
|
|
8
|
+
* wrong data types
|
|
9
|
+
* undocumented status codes
|
|
10
|
+
|
|
11
|
+
Exit code 0 means spec and API match. Exit code 1 means drift. Exit code 2 means the check could not be completed. That makes it a one line gate in a CI/CD pipeline.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
Python 3.9 or newer.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
pip install git+https://github.com/b6bs62fhys-jpg/specsentinel
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or from a clone of this repository:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
pip install .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Try it in one minute
|
|
28
|
+
|
|
29
|
+
The repository ships a demo API that drifts from its spec on purpose.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
python examples/demo_server.py &
|
|
33
|
+
specsentinel examples/petstore.yaml --url http://127.0.0.1:8099
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Real output:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
SpecSentinel 0.1.0
|
|
40
|
+
Spec: examples/petstore.yaml
|
|
41
|
+
Target: http://127.0.0.1:8099
|
|
42
|
+
|
|
43
|
+
GET /health 200 OK
|
|
44
|
+
GET /pets 200 OK
|
|
45
|
+
GET /pets/{petId} 200 DRIFT
|
|
46
|
+
error MISSING_FIELD body.name
|
|
47
|
+
required field is missing in the response
|
|
48
|
+
error TYPE_MISMATCH body.id
|
|
49
|
+
expected integer, got string
|
|
50
|
+
warning UNDOCUMENTED_FIELD body.breed
|
|
51
|
+
field is returned but not described in the spec
|
|
52
|
+
GET /stats 503 DRIFT
|
|
53
|
+
error UNDOCUMENTED_STATUS status
|
|
54
|
+
status 503 is not documented (documented: 200)
|
|
55
|
+
GET /owners/{ownerId} - SKIPPED
|
|
56
|
+
no example value for path parameter 'ownerId' (pass --param ownerId=VALUE)
|
|
57
|
+
|
|
58
|
+
4 checked, 2 with drift, 1 skipped, 0 failed
|
|
59
|
+
Result: DRIFT (exit code 1)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Start the demo with `--conform` and the same command ends with `Result: MATCH (exit code 0)`.
|
|
63
|
+
|
|
64
|
+
## Exit codes
|
|
65
|
+
|
|
66
|
+
| Code | Meaning |
|
|
67
|
+
|------|---------|
|
|
68
|
+
| 0 | Every checked operation matches the spec |
|
|
69
|
+
| 1 | At least one operation drifts from the spec |
|
|
70
|
+
| 2 | The check could not be completed: bad spec, bad arguments, API unreachable, or nothing could be checked |
|
|
71
|
+
|
|
72
|
+
Code 2 is deliberately not 0. A pipeline should never turn green because nothing was actually checked.
|
|
73
|
+
|
|
74
|
+
## What counts as drift
|
|
75
|
+
|
|
76
|
+
| Finding | Severity | Meaning |
|
|
77
|
+
|---------|----------|---------|
|
|
78
|
+
| UNDOCUMENTED_STATUS | error | The API answered with a status code the spec does not list. Ranges like 4XX and `default` are honored. |
|
|
79
|
+
| MISSING_FIELD | error | A field marked required in the spec is not in the response. |
|
|
80
|
+
| TYPE_MISMATCH | error | A value has a different JSON type than the spec says. `nullable` and type lists are understood. |
|
|
81
|
+
| ENUM_MISMATCH | error | A value is not one of the enum values in the spec. |
|
|
82
|
+
| UNDOCUMENTED_CONTENT_TYPE | error | The response content type is not listed for that status. |
|
|
83
|
+
| INVALID_JSON | error | The spec promises JSON but the body is not valid JSON. |
|
|
84
|
+
| EMPTY_BODY | error | The spec documents a body but the response is empty. |
|
|
85
|
+
| NO_SCHEMA_MATCH | error | A value fits none of the oneOf or anyOf alternatives. |
|
|
86
|
+
| UNDOCUMENTED_FIELD | warning | A field is returned that the spec does not describe. An error when the schema sets `additionalProperties: false`. |
|
|
87
|
+
|
|
88
|
+
Warnings do not fail the run. Add `--strict` and they do.
|
|
89
|
+
|
|
90
|
+
## Options
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
specsentinel SPEC --url BASE_URL [options]
|
|
94
|
+
|
|
95
|
+
SPEC path or URL of the OpenAPI 3.x document, YAML or JSON
|
|
96
|
+
--url BASE_URL base URL of the running API
|
|
97
|
+
-H, --header 'N: v' header sent with every request, repeatable
|
|
98
|
+
--param NAME=VALUE value for a path or query parameter, repeatable
|
|
99
|
+
--params-file FILE YAML or JSON file with parameter values
|
|
100
|
+
--timeout SECONDS wait per request, default 10
|
|
101
|
+
--strict treat warnings as drift
|
|
102
|
+
--format text|json output format, default text
|
|
103
|
+
--version
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Use `--format json` when another tool should read the result.
|
|
107
|
+
|
|
108
|
+
## How requests are built
|
|
109
|
+
|
|
110
|
+
SpecSentinel sends GET requests only. Methods that change data could damage the API under test, so they are out of scope by design.
|
|
111
|
+
|
|
112
|
+
Parameter values come from the spec: `example`, `examples`, the schema `example`, `default`, or the first `enum` value. A path parameter without any of these cannot be filled in automatically. That operation is reported as SKIPPED with the exact `--param` to pass. Skipped operations do not fail the run.
|
|
113
|
+
|
|
114
|
+
### Params file
|
|
115
|
+
|
|
116
|
+
Large APIs have many operations with path parameters. Put the values in a file instead of a long command line:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
# params.yaml
|
|
120
|
+
petId: 1 # used by every operation with a parameter called petId
|
|
121
|
+
ownerId: 7
|
|
122
|
+
GET /pets/{petId}: # applies to this one operation only
|
|
123
|
+
petId: 42
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
specsentinel openapi.yaml --url https://staging.example.com --params-file params.yaml
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Order of precedence, later wins: spec example, params file, params file entry for one operation, `--param` on the command line.
|
|
131
|
+
|
|
132
|
+
For authentication pass the header yourself:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
specsentinel openapi.yaml --url https://staging.example.com \
|
|
136
|
+
--header "Authorization: Bearer $API_TOKEN"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## GitHub Actions
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
name: API contract
|
|
143
|
+
on: [push, pull_request]
|
|
144
|
+
|
|
145
|
+
jobs:
|
|
146
|
+
drift:
|
|
147
|
+
runs-on: ubuntu-latest
|
|
148
|
+
steps:
|
|
149
|
+
- uses: actions/checkout@v4
|
|
150
|
+
- uses: actions/setup-python@v5
|
|
151
|
+
with:
|
|
152
|
+
python-version: "3.12"
|
|
153
|
+
- run: pip install git+https://github.com/b6bs62fhys-jpg/specsentinel
|
|
154
|
+
- run: >
|
|
155
|
+
specsentinel openapi.yaml
|
|
156
|
+
--url https://staging.example.com
|
|
157
|
+
--header "Authorization: Bearer ${{ secrets.API_TOKEN }}"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
When the API drifts, exit code 1 fails the step and the build goes red.
|
|
161
|
+
|
|
162
|
+
## Tested against real specifications
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
The schema checks were run against the public OpenAPI descriptions of Swagger Petstore, GitHub and Stripe: 920 GET operations and 2032 documented JSON responses in total. For every response a conforming example was generated from its schema and fed through the checker. It produced no findings and no crashes.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
You can repeat the run yourself with `python tools/spec_smoke.py <spec file or URL>`. The exact output is in `docs/smoke_results.md`.
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
The generator and the checker share the same reading of the schema, so this run shows that the checker raises no false alarms on large real specifications. It does not show that every kind of drift is caught, and it does not replace running SpecSentinel against your own live API.
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
## Limits of this version
|
|
175
|
+
|
|
176
|
+
This is an early release. Known limits:
|
|
177
|
+
|
|
178
|
+
* GET operations only
|
|
179
|
+
* JSON response bodies only, other content types are not compared
|
|
180
|
+
* response headers are not compared
|
|
181
|
+
* only local `$ref` references, no references to other files
|
|
182
|
+
* allOf is merged, oneOf and anyOf pass when any alternative fits
|
|
183
|
+
* no string formats, lengths or numeric ranges yet
|
|
184
|
+
|
|
185
|
+
Feedback on which check should come next is very welcome. Open an issue.
|
|
186
|
+
|
|
187
|
+
## Development
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
pip install -e ".[dev]"
|
|
191
|
+
pytest
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT, see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "specsentinel"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Detects when a live API drifts from its OpenAPI spec. Exit code 0 on match, 1 on drift, 2 if the check could not run."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["openapi", "api", "contract", "drift", "testing", "cli"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Topic :: Software Development :: Testing",
|
|
18
|
+
]
|
|
19
|
+
dependencies = ["PyYAML>=6"]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://github.com/b6bs62fhys-jpg/specsentinel"
|
|
23
|
+
Issues = "https://github.com/b6bs62fhys-jpg/specsentinel/issues"
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = ["pytest>=7"]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
specsentinel = "specsentinel.cli:main"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|