specforge-api 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.
- specforge_api-0.1.0/.gitignore +40 -0
- specforge_api-0.1.0/LICENSE +21 -0
- specforge_api-0.1.0/PKG-INFO +260 -0
- specforge_api-0.1.0/README.md +193 -0
- specforge_api-0.1.0/pyproject.toml +100 -0
- specforge_api-0.1.0/src/specforge/__init__.py +23 -0
- specforge_api-0.1.0/src/specforge/adapters/fastapi.py +101 -0
- specforge_api-0.1.0/src/specforge/adapters/flask.py +78 -0
- specforge_api-0.1.0/src/specforge/assets/index.html +2995 -0
- specforge_api-0.1.0/src/specforge/cli/main.py +153 -0
- specforge_api-0.1.0/src/specforge/config.py +42 -0
- specforge_api-0.1.0/src/specforge/parser/models.py +131 -0
- specforge_api-0.1.0/src/specforge/parser/openapi3.py +285 -0
- specforge_api-0.1.0/src/specforge/parser/resolver.py +101 -0
- specforge_api-0.1.0/src/specforge/testing/exporters/pytest_export.py +89 -0
- specforge_api-0.1.0/src/specforge/testing/fuzzers/array_fuzzer.py +56 -0
- specforge_api-0.1.0/src/specforge/testing/fuzzers/boolean_fuzzer.py +38 -0
- specforge_api-0.1.0/src/specforge/testing/fuzzers/numeric_fuzzer.py +92 -0
- specforge_api-0.1.0/src/specforge/testing/fuzzers/object_fuzzer.py +99 -0
- specforge_api-0.1.0/src/specforge/testing/fuzzers/string_fuzzer.py +157 -0
- specforge_api-0.1.0/src/specforge/testing/invariants.py +170 -0
- specforge_api-0.1.0/src/specforge/testing/planner.py +155 -0
- specforge_api-0.1.0/src/specforge/utils/html_injector.py +43 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Virtual Environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
ENV/
|
|
17
|
+
|
|
18
|
+
# Testing & Coverage
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
.mypy_cache/
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
|
|
25
|
+
# Frontend / Node
|
|
26
|
+
frontend/node_modules/
|
|
27
|
+
frontend/dist/
|
|
28
|
+
frontend/.turbo/
|
|
29
|
+
npm-debug.log*
|
|
30
|
+
yarn-debug.log*
|
|
31
|
+
yarn-error.log*
|
|
32
|
+
pnpm-debug.log*
|
|
33
|
+
|
|
34
|
+
# IDE & OS
|
|
35
|
+
.vscode/
|
|
36
|
+
.idea/
|
|
37
|
+
*.swp
|
|
38
|
+
*.swo
|
|
39
|
+
.DS_Store
|
|
40
|
+
Thumbs.db
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SpecForge Contributors
|
|
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,260 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: specforge-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The interactive API client and automated contract testing engine for Python.
|
|
5
|
+
Project-URL: Homepage, https://github.com/specforge-api/specforge
|
|
6
|
+
Project-URL: Documentation, https://github.com/specforge-api/specforge#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/specforge-api/specforge
|
|
8
|
+
Project-URL: Issues, https://github.com/specforge-api/specforge/issues
|
|
9
|
+
Author: SpecForge Team
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 SpecForge Contributors
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Keywords: api-client,contract-testing,fastapi,fuzzing,openapi,pytest,swagger,testing
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
42
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
43
|
+
Classifier: Topic :: Software Development :: Testing
|
|
44
|
+
Requires-Python: >=3.9
|
|
45
|
+
Requires-Dist: httpx>=0.24.0
|
|
46
|
+
Requires-Dist: pydantic>=2.0.0
|
|
47
|
+
Requires-Dist: pyyaml>=6.0
|
|
48
|
+
Provides-Extra: all
|
|
49
|
+
Requires-Dist: fastapi>=0.100.0; extra == 'all'
|
|
50
|
+
Requires-Dist: flask>=2.0.0; extra == 'all'
|
|
51
|
+
Requires-Dist: rich>=13.0.0; extra == 'all'
|
|
52
|
+
Requires-Dist: typer>=0.9.0; extra == 'all'
|
|
53
|
+
Provides-Extra: cli
|
|
54
|
+
Requires-Dist: rich>=13.0.0; extra == 'cli'
|
|
55
|
+
Requires-Dist: typer>=0.9.0; extra == 'cli'
|
|
56
|
+
Provides-Extra: dev
|
|
57
|
+
Requires-Dist: mypy>=1.5.0; extra == 'dev'
|
|
58
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
59
|
+
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
60
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
61
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
62
|
+
Provides-Extra: fastapi
|
|
63
|
+
Requires-Dist: fastapi>=0.100.0; extra == 'fastapi'
|
|
64
|
+
Provides-Extra: flask
|
|
65
|
+
Requires-Dist: flask>=2.0.0; extra == 'flask'
|
|
66
|
+
Description-Content-Type: text/markdown
|
|
67
|
+
|
|
68
|
+
# SpecForge ⚡
|
|
69
|
+
|
|
70
|
+
[](https://pypi.org/project/specforge-api/)
|
|
71
|
+
[](https://pypi.org/project/specforge-api/)
|
|
72
|
+
[](https://opensource.org/licenses/MIT)
|
|
73
|
+
|
|
74
|
+
**The Interactive API Client & Automated Contract Testing Engine for Python.**
|
|
75
|
+
|
|
76
|
+
A modern, drop-in Python library that replaces Swagger UI with a beautiful, responsive API workspace **and** automatically discovers boundary vulnerabilities, missing whitespace stripping, and unhandled 500 server crashes directly against your endpoints.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 🚀 Why SpecForge?
|
|
81
|
+
|
|
82
|
+
1. **Active Quality Gate, Not Just Passive Docs**: Traditional tools like Swagger UI only show the "happy path". SpecForge algorithmically analyzes your schema constraints and runs targeted boundary fuzzing right in your browser.
|
|
83
|
+
2. **"Zero 500s" Invariant**: Verifies that edge-case inputs (e.g. `" "`, empty strings, off-by-one numbers, malformed emails) return clean client errors (`422/400`), never unhandled server exceptions (`500`).
|
|
84
|
+
3. **One-Click Pytest Exporter**: Discovered edge cases can be instantly exported into commit-ready `test_api_contracts.py` files with `@pytest.mark.parametrize`.
|
|
85
|
+
4. **Zero Frontend Build Dependencies**: The interactive workspace is fully compiled and inlined into the Python package. No Node.js or npm needed on your machine.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 📦 Installation
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install specforge-api
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
With framework extras:
|
|
96
|
+
```bash
|
|
97
|
+
pip install "specforge-api[fastapi]" # FastAPI
|
|
98
|
+
pip install "specforge-api[flask]" # Flask
|
|
99
|
+
pip install "specforge-api[all]" # Full CLI & all framework adapters
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## ⚡ Quickstart
|
|
105
|
+
|
|
106
|
+
### FastAPI
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from fastapi import FastAPI
|
|
110
|
+
from specforge import SpecForge
|
|
111
|
+
|
|
112
|
+
app = FastAPI(title="My API", version="1.0.0")
|
|
113
|
+
|
|
114
|
+
# Mount SpecForge (auto-detects /openapi.json)
|
|
115
|
+
SpecForge(app)
|
|
116
|
+
|
|
117
|
+
@app.post("/users")
|
|
118
|
+
def create_user(username: str, age: int):
|
|
119
|
+
return {"status": "created", "username": username}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Now open `http://localhost:8000/docs` in your browser. You get:
|
|
123
|
+
- A modern, interactive dark/light mode API client (Try-It-Out, request history, multi-language code snippets).
|
|
124
|
+
- An integrated **Boundary & Contract Testing Tab** to run automated fuzzing on all endpoints with one click.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### Flask
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from flask import Flask
|
|
132
|
+
from specforge.flask import SpecForge
|
|
133
|
+
|
|
134
|
+
app = Flask(__name__)
|
|
135
|
+
|
|
136
|
+
# Register Blueprint
|
|
137
|
+
SpecForge(app, spec_url="/api/spec.json", path="/docs")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 🧪 Schema-Aware Boundary Fuzzing
|
|
143
|
+
|
|
144
|
+
SpecForge generates targeted test cases according to field types:
|
|
145
|
+
|
|
146
|
+
| Field Type | Automated Test Coverage |
|
|
147
|
+
|---|---|
|
|
148
|
+
| **String** | Rejecting whitespace-only (`" "`), empty strings (`""`), min/max bounds, Unicode 4-byte emojis, null bytes (`\x00`), format violations (email, uuid, datetime), and injection probes. |
|
|
149
|
+
| **Number / Integer** | Boundary checks (`min - 1`, `max + 1`), negative values on unsigned fields, zero checks, float-into-integer pollution, and integer overflow. |
|
|
150
|
+
| **Boolean** | Strict type testing vs loose coercion (`"true"`, `"yes"`, `1`, `0`, `null`). |
|
|
151
|
+
| **Array** | Empty lists (`[]`), `minItems` / `maxItems` boundaries, and `uniqueItems: true` duplicates. |
|
|
152
|
+
| **Object** | Combinatorial required field omission, extra unexpected keys (`additionalProperties: false`), and empty bodies. |
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 📁 Integrating Existing Project Tests
|
|
157
|
+
|
|
158
|
+
If your repository already contains test suites (written in `pytest` or `unittest`), SpecForge can automatically index and display them in a dedicated **Project Tests** tab and link them directly to their corresponding API routes.
|
|
159
|
+
|
|
160
|
+
### Quick Setup
|
|
161
|
+
|
|
162
|
+
Pass `test_paths` when mounting SpecForge in your application:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from fastapi import FastAPI
|
|
166
|
+
from specforge import SpecForge
|
|
167
|
+
|
|
168
|
+
app = FastAPI()
|
|
169
|
+
|
|
170
|
+
# Mount SpecForge with existing project tests
|
|
171
|
+
SpecForge(
|
|
172
|
+
app,
|
|
173
|
+
test_paths=["tests"], # Can be directories or individual test files
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Now open `/docs` to see:
|
|
178
|
+
- **📁 Project Tests Tab**: Browse all repository tests, file paths, line numbers, docstrings, and parameter matrices.
|
|
179
|
+
- **Route Coverage Linking**: View your existing tests directly inside the API Explorer alongside SpecForge's automated boundary fuzzers.
|
|
180
|
+
|
|
181
|
+
### Linking Tests to API Routes
|
|
182
|
+
|
|
183
|
+
SpecForge associates existing tests with OpenAPI routes using two mechanisms:
|
|
184
|
+
|
|
185
|
+
#### 1. Automatic HTTP Client Detection (Zero Configuration)
|
|
186
|
+
If your test uses `TestClient`, `httpx`, or `requests` to call an API path, SpecForge automatically recognizes the route:
|
|
187
|
+
```python
|
|
188
|
+
def test_user_creation_underage(client):
|
|
189
|
+
"""Rejects underage registrations."""
|
|
190
|
+
response = client.post("/api/v1/users", json={"username": "bob", "age": 16})
|
|
191
|
+
assert response.status_code == 422
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### 2. Explicit Pytest Marker (Recommended for Complex Tests)
|
|
195
|
+
For tests with dynamic URLs or mocks, declare the endpoint explicitly:
|
|
196
|
+
```python
|
|
197
|
+
import pytest
|
|
198
|
+
|
|
199
|
+
@pytest.mark.endpoint("POST", "/api/v1/users")
|
|
200
|
+
def test_user_signup_pipeline():
|
|
201
|
+
"""End-to-end user registration and event dispatch."""
|
|
202
|
+
...
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 🔒 Security & Zero-Execution Guarantee
|
|
206
|
+
|
|
207
|
+
SpecForge uses **Python Static Abstract Syntax Tree (AST) parsing** to discover tests:
|
|
208
|
+
- **Zero Code Execution**: SpecForge **never** imports or executes your test files. Tests are analyzed purely as static syntax trees.
|
|
209
|
+
- **No Side Effects**: Discovering tests will never trigger database operations, background tasks, or network calls.
|
|
210
|
+
- **Path Sandboxing**: Scanned paths are strictly verified to remain within the project root directory, preventing any path traversal.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 🛠️ CLI & CI/CD Pipeline
|
|
215
|
+
|
|
216
|
+
Run headless contract fuzzing in your continuous integration pipeline:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
specforge test http://localhost:8000/openapi.json --fail-on-500
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 💻 Local Development & Testing Guide
|
|
225
|
+
|
|
226
|
+
### 1. Run the Interactive Live Demo (Zero Dependencies)
|
|
227
|
+
You can launch and explore SpecForge immediately using Python's standard library:
|
|
228
|
+
```bash
|
|
229
|
+
python examples/standalone_demo.py
|
|
230
|
+
```
|
|
231
|
+
Open **`http://localhost:8000/docs`** in your browser to:
|
|
232
|
+
- Test API endpoints and inspect formatted JSON responses.
|
|
233
|
+
- Click **"⚡ Run Boundary Test Suite"** to watch automated fuzzing of whitespace strings, negative numbers, and missing fields.
|
|
234
|
+
- Click **"🐍 Export to Pytest"** to view and copy auto-generated test code.
|
|
235
|
+
|
|
236
|
+
### 2. Run the Automated Unit Test Suite
|
|
237
|
+
```bash
|
|
238
|
+
pytest -v
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 3. Install in Another Project Locally (Editable Mode)
|
|
242
|
+
To use this local version inside any other Python app on your machine without publishing to PyPI:
|
|
243
|
+
```bash
|
|
244
|
+
# In your other project's directory and virtualenv:
|
|
245
|
+
pip install -e c:\Research-projects\specforge
|
|
246
|
+
```
|
|
247
|
+
*Any change you make in SpecForge source code will instantly reflect in your other application without reinstalling!*
|
|
248
|
+
|
|
249
|
+
### 4. Build a Distribution Wheel (.whl)
|
|
250
|
+
```bash
|
|
251
|
+
pip install build
|
|
252
|
+
python -m build
|
|
253
|
+
# Outputs wheel file in dist/specforge-0.1.0-py3-none-any.whl
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 📄 License
|
|
259
|
+
|
|
260
|
+
SpecForge is licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# SpecForge ⚡
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/specforge-api/)
|
|
4
|
+
[](https://pypi.org/project/specforge-api/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**The Interactive API Client & Automated Contract Testing Engine for Python.**
|
|
8
|
+
|
|
9
|
+
A modern, drop-in Python library that replaces Swagger UI with a beautiful, responsive API workspace **and** automatically discovers boundary vulnerabilities, missing whitespace stripping, and unhandled 500 server crashes directly against your endpoints.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 🚀 Why SpecForge?
|
|
14
|
+
|
|
15
|
+
1. **Active Quality Gate, Not Just Passive Docs**: Traditional tools like Swagger UI only show the "happy path". SpecForge algorithmically analyzes your schema constraints and runs targeted boundary fuzzing right in your browser.
|
|
16
|
+
2. **"Zero 500s" Invariant**: Verifies that edge-case inputs (e.g. `" "`, empty strings, off-by-one numbers, malformed emails) return clean client errors (`422/400`), never unhandled server exceptions (`500`).
|
|
17
|
+
3. **One-Click Pytest Exporter**: Discovered edge cases can be instantly exported into commit-ready `test_api_contracts.py` files with `@pytest.mark.parametrize`.
|
|
18
|
+
4. **Zero Frontend Build Dependencies**: The interactive workspace is fully compiled and inlined into the Python package. No Node.js or npm needed on your machine.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install specforge-api
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
With framework extras:
|
|
29
|
+
```bash
|
|
30
|
+
pip install "specforge-api[fastapi]" # FastAPI
|
|
31
|
+
pip install "specforge-api[flask]" # Flask
|
|
32
|
+
pip install "specforge-api[all]" # Full CLI & all framework adapters
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## ⚡ Quickstart
|
|
38
|
+
|
|
39
|
+
### FastAPI
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from fastapi import FastAPI
|
|
43
|
+
from specforge import SpecForge
|
|
44
|
+
|
|
45
|
+
app = FastAPI(title="My API", version="1.0.0")
|
|
46
|
+
|
|
47
|
+
# Mount SpecForge (auto-detects /openapi.json)
|
|
48
|
+
SpecForge(app)
|
|
49
|
+
|
|
50
|
+
@app.post("/users")
|
|
51
|
+
def create_user(username: str, age: int):
|
|
52
|
+
return {"status": "created", "username": username}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Now open `http://localhost:8000/docs` in your browser. You get:
|
|
56
|
+
- A modern, interactive dark/light mode API client (Try-It-Out, request history, multi-language code snippets).
|
|
57
|
+
- An integrated **Boundary & Contract Testing Tab** to run automated fuzzing on all endpoints with one click.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Flask
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from flask import Flask
|
|
65
|
+
from specforge.flask import SpecForge
|
|
66
|
+
|
|
67
|
+
app = Flask(__name__)
|
|
68
|
+
|
|
69
|
+
# Register Blueprint
|
|
70
|
+
SpecForge(app, spec_url="/api/spec.json", path="/docs")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 🧪 Schema-Aware Boundary Fuzzing
|
|
76
|
+
|
|
77
|
+
SpecForge generates targeted test cases according to field types:
|
|
78
|
+
|
|
79
|
+
| Field Type | Automated Test Coverage |
|
|
80
|
+
|---|---|
|
|
81
|
+
| **String** | Rejecting whitespace-only (`" "`), empty strings (`""`), min/max bounds, Unicode 4-byte emojis, null bytes (`\x00`), format violations (email, uuid, datetime), and injection probes. |
|
|
82
|
+
| **Number / Integer** | Boundary checks (`min - 1`, `max + 1`), negative values on unsigned fields, zero checks, float-into-integer pollution, and integer overflow. |
|
|
83
|
+
| **Boolean** | Strict type testing vs loose coercion (`"true"`, `"yes"`, `1`, `0`, `null`). |
|
|
84
|
+
| **Array** | Empty lists (`[]`), `minItems` / `maxItems` boundaries, and `uniqueItems: true` duplicates. |
|
|
85
|
+
| **Object** | Combinatorial required field omission, extra unexpected keys (`additionalProperties: false`), and empty bodies. |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 📁 Integrating Existing Project Tests
|
|
90
|
+
|
|
91
|
+
If your repository already contains test suites (written in `pytest` or `unittest`), SpecForge can automatically index and display them in a dedicated **Project Tests** tab and link them directly to their corresponding API routes.
|
|
92
|
+
|
|
93
|
+
### Quick Setup
|
|
94
|
+
|
|
95
|
+
Pass `test_paths` when mounting SpecForge in your application:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from fastapi import FastAPI
|
|
99
|
+
from specforge import SpecForge
|
|
100
|
+
|
|
101
|
+
app = FastAPI()
|
|
102
|
+
|
|
103
|
+
# Mount SpecForge with existing project tests
|
|
104
|
+
SpecForge(
|
|
105
|
+
app,
|
|
106
|
+
test_paths=["tests"], # Can be directories or individual test files
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Now open `/docs` to see:
|
|
111
|
+
- **📁 Project Tests Tab**: Browse all repository tests, file paths, line numbers, docstrings, and parameter matrices.
|
|
112
|
+
- **Route Coverage Linking**: View your existing tests directly inside the API Explorer alongside SpecForge's automated boundary fuzzers.
|
|
113
|
+
|
|
114
|
+
### Linking Tests to API Routes
|
|
115
|
+
|
|
116
|
+
SpecForge associates existing tests with OpenAPI routes using two mechanisms:
|
|
117
|
+
|
|
118
|
+
#### 1. Automatic HTTP Client Detection (Zero Configuration)
|
|
119
|
+
If your test uses `TestClient`, `httpx`, or `requests` to call an API path, SpecForge automatically recognizes the route:
|
|
120
|
+
```python
|
|
121
|
+
def test_user_creation_underage(client):
|
|
122
|
+
"""Rejects underage registrations."""
|
|
123
|
+
response = client.post("/api/v1/users", json={"username": "bob", "age": 16})
|
|
124
|
+
assert response.status_code == 422
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### 2. Explicit Pytest Marker (Recommended for Complex Tests)
|
|
128
|
+
For tests with dynamic URLs or mocks, declare the endpoint explicitly:
|
|
129
|
+
```python
|
|
130
|
+
import pytest
|
|
131
|
+
|
|
132
|
+
@pytest.mark.endpoint("POST", "/api/v1/users")
|
|
133
|
+
def test_user_signup_pipeline():
|
|
134
|
+
"""End-to-end user registration and event dispatch."""
|
|
135
|
+
...
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 🔒 Security & Zero-Execution Guarantee
|
|
139
|
+
|
|
140
|
+
SpecForge uses **Python Static Abstract Syntax Tree (AST) parsing** to discover tests:
|
|
141
|
+
- **Zero Code Execution**: SpecForge **never** imports or executes your test files. Tests are analyzed purely as static syntax trees.
|
|
142
|
+
- **No Side Effects**: Discovering tests will never trigger database operations, background tasks, or network calls.
|
|
143
|
+
- **Path Sandboxing**: Scanned paths are strictly verified to remain within the project root directory, preventing any path traversal.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 🛠️ CLI & CI/CD Pipeline
|
|
148
|
+
|
|
149
|
+
Run headless contract fuzzing in your continuous integration pipeline:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
specforge test http://localhost:8000/openapi.json --fail-on-500
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 💻 Local Development & Testing Guide
|
|
158
|
+
|
|
159
|
+
### 1. Run the Interactive Live Demo (Zero Dependencies)
|
|
160
|
+
You can launch and explore SpecForge immediately using Python's standard library:
|
|
161
|
+
```bash
|
|
162
|
+
python examples/standalone_demo.py
|
|
163
|
+
```
|
|
164
|
+
Open **`http://localhost:8000/docs`** in your browser to:
|
|
165
|
+
- Test API endpoints and inspect formatted JSON responses.
|
|
166
|
+
- Click **"⚡ Run Boundary Test Suite"** to watch automated fuzzing of whitespace strings, negative numbers, and missing fields.
|
|
167
|
+
- Click **"🐍 Export to Pytest"** to view and copy auto-generated test code.
|
|
168
|
+
|
|
169
|
+
### 2. Run the Automated Unit Test Suite
|
|
170
|
+
```bash
|
|
171
|
+
pytest -v
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 3. Install in Another Project Locally (Editable Mode)
|
|
175
|
+
To use this local version inside any other Python app on your machine without publishing to PyPI:
|
|
176
|
+
```bash
|
|
177
|
+
# In your other project's directory and virtualenv:
|
|
178
|
+
pip install -e c:\Research-projects\specforge
|
|
179
|
+
```
|
|
180
|
+
*Any change you make in SpecForge source code will instantly reflect in your other application without reinstalling!*
|
|
181
|
+
|
|
182
|
+
### 4. Build a Distribution Wheel (.whl)
|
|
183
|
+
```bash
|
|
184
|
+
pip install build
|
|
185
|
+
python -m build
|
|
186
|
+
# Outputs wheel file in dist/specforge-0.1.0-py3-none-any.whl
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 📄 License
|
|
192
|
+
|
|
193
|
+
SpecForge is licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "specforge-api"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "The interactive API client and automated contract testing engine for Python."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "SpecForge Team" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"openapi",
|
|
17
|
+
"swagger",
|
|
18
|
+
"fastapi",
|
|
19
|
+
"testing",
|
|
20
|
+
"contract-testing",
|
|
21
|
+
"fuzzing",
|
|
22
|
+
"api-client",
|
|
23
|
+
"pytest"
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 3 - Alpha",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"License :: OSI Approved :: MIT License",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.9",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Programming Language :: Python :: 3.13",
|
|
35
|
+
"Topic :: Software Development :: Testing",
|
|
36
|
+
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
|
|
37
|
+
]
|
|
38
|
+
dependencies = [
|
|
39
|
+
"httpx>=0.24.0",
|
|
40
|
+
"pydantic>=2.0.0",
|
|
41
|
+
"pyyaml>=6.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
fastapi = ["fastapi>=0.100.0"]
|
|
46
|
+
flask = ["flask>=2.0.0"]
|
|
47
|
+
cli = ["typer>=0.9.0", "rich>=13.0.0"]
|
|
48
|
+
all = [
|
|
49
|
+
"fastapi>=0.100.0",
|
|
50
|
+
"flask>=2.0.0",
|
|
51
|
+
"typer>=0.9.0",
|
|
52
|
+
"rich>=13.0.0"
|
|
53
|
+
]
|
|
54
|
+
dev = [
|
|
55
|
+
"pytest>=7.4.0",
|
|
56
|
+
"pytest-asyncio>=0.21.0",
|
|
57
|
+
"ruff>=0.1.0",
|
|
58
|
+
"mypy>=1.5.0",
|
|
59
|
+
"types-pyyaml",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[project.scripts]
|
|
63
|
+
specforge = "specforge.cli.main:cli_entrypoint"
|
|
64
|
+
|
|
65
|
+
[project.urls]
|
|
66
|
+
Homepage = "https://github.com/specforge-api/specforge"
|
|
67
|
+
Documentation = "https://github.com/specforge-api/specforge#readme"
|
|
68
|
+
Repository = "https://github.com/specforge-api/specforge"
|
|
69
|
+
Issues = "https://github.com/specforge-api/specforge/issues"
|
|
70
|
+
|
|
71
|
+
[tool.hatch.build.targets.wheel]
|
|
72
|
+
packages = ["src/specforge"]
|
|
73
|
+
|
|
74
|
+
[tool.hatch.build.targets.sdist]
|
|
75
|
+
include = [
|
|
76
|
+
"/src",
|
|
77
|
+
"/README.md",
|
|
78
|
+
"/LICENSE",
|
|
79
|
+
"/pyproject.toml"
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[tool.ruff]
|
|
83
|
+
line-length = 100
|
|
84
|
+
target-version = "py39"
|
|
85
|
+
|
|
86
|
+
[tool.ruff.lint]
|
|
87
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
88
|
+
ignore = ["E501"]
|
|
89
|
+
|
|
90
|
+
[tool.mypy]
|
|
91
|
+
python_version = "3.10"
|
|
92
|
+
warn_return_any = false
|
|
93
|
+
warn_unused_configs = true
|
|
94
|
+
disallow_untyped_defs = false
|
|
95
|
+
ignore_missing_imports = true
|
|
96
|
+
|
|
97
|
+
[tool.pytest.ini_options]
|
|
98
|
+
testpaths = ["tests"]
|
|
99
|
+
pythonpath = ["src"]
|
|
100
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""SpecForge: The Interactive API Client & Automated Contract Testing Engine for Python."""
|
|
2
|
+
|
|
3
|
+
from specforge.config import SpecForgeConfig
|
|
4
|
+
from specforge.parser.openapi3 import OpenAPIParser
|
|
5
|
+
from specforge.testing.exporters.pytest_export import PytestExporter
|
|
6
|
+
from specforge.testing.planner import TestPlanner
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
# Conditionally import FastAPI adapter if available
|
|
11
|
+
try:
|
|
12
|
+
from specforge.adapters.fastapi import SpecForge
|
|
13
|
+
except ImportError:
|
|
14
|
+
SpecForge = None # type: ignore
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"SpecForge",
|
|
18
|
+
"SpecForgeConfig",
|
|
19
|
+
"OpenAPIParser",
|
|
20
|
+
"TestPlanner",
|
|
21
|
+
"PytestExporter",
|
|
22
|
+
"__version__",
|
|
23
|
+
]
|