spec_forge 0.6.0 → 0.7.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +112 -2
- data/README.md +133 -8
- data/flake.lock +3 -3
- data/flake.nix +3 -3
- data/lib/spec_forge/attribute/factory.rb +1 -1
- data/lib/spec_forge/callbacks.rb +9 -0
- data/lib/spec_forge/cli/docs/generate.rb +72 -0
- data/lib/spec_forge/cli/docs.rb +92 -0
- data/lib/spec_forge/cli/init.rb +39 -7
- data/lib/spec_forge/cli/new.rb +13 -3
- data/lib/spec_forge/cli/run.rb +12 -4
- data/lib/spec_forge/cli/serve.rb +155 -0
- data/lib/spec_forge/cli.rb +14 -6
- data/lib/spec_forge/configuration.rb +2 -2
- data/lib/spec_forge/context/store.rb +23 -40
- data/lib/spec_forge/core_ext/array.rb +27 -0
- data/lib/spec_forge/documentation/builder.rb +383 -0
- data/lib/spec_forge/documentation/document/operation.rb +47 -0
- data/lib/spec_forge/documentation/document/parameter.rb +22 -0
- data/lib/spec_forge/documentation/document/request_body.rb +24 -0
- data/lib/spec_forge/documentation/document/response.rb +39 -0
- data/lib/spec_forge/documentation/document/response_body.rb +27 -0
- data/lib/spec_forge/documentation/document.rb +48 -0
- data/lib/spec_forge/documentation/generators/base.rb +81 -0
- data/lib/spec_forge/documentation/generators/openapi/base.rb +100 -0
- data/lib/spec_forge/documentation/generators/openapi/error_formatter.rb +149 -0
- data/lib/spec_forge/documentation/generators/openapi/v3_0.rb +65 -0
- data/lib/spec_forge/documentation/generators/openapi.rb +59 -0
- data/lib/spec_forge/documentation/generators.rb +17 -0
- data/lib/spec_forge/documentation/loader/cache.rb +138 -0
- data/lib/spec_forge/documentation/loader.rb +159 -0
- data/lib/spec_forge/documentation/openapi/base.rb +33 -0
- data/lib/spec_forge/documentation/openapi/v3_0/example.rb +44 -0
- data/lib/spec_forge/documentation/openapi/v3_0/media_type.rb +42 -0
- data/lib/spec_forge/documentation/openapi/v3_0/operation.rb +175 -0
- data/lib/spec_forge/documentation/openapi/v3_0/response.rb +65 -0
- data/lib/spec_forge/documentation/openapi/v3_0/schema.rb +80 -0
- data/lib/spec_forge/documentation/openapi/v3_0/tag.rb +71 -0
- data/lib/spec_forge/documentation/openapi.rb +23 -0
- data/lib/spec_forge/documentation.rb +27 -0
- data/lib/spec_forge/error.rb +17 -0
- data/lib/spec_forge/factory.rb +2 -2
- data/lib/spec_forge/filter.rb +3 -4
- data/lib/spec_forge/forge.rb +5 -4
- data/lib/spec_forge/http/backend.rb +2 -0
- data/lib/spec_forge/http/request.rb +14 -3
- data/lib/spec_forge/loader.rb +14 -24
- data/lib/spec_forge/normalizer/default.rb +51 -0
- data/lib/spec_forge/normalizer/definition.rb +248 -0
- data/lib/spec_forge/normalizer/validators.rb +99 -0
- data/lib/spec_forge/normalizer.rb +356 -199
- data/lib/spec_forge/normalizers/_shared.yml +74 -0
- data/lib/spec_forge/normalizers/configuration.yml +23 -0
- data/lib/spec_forge/normalizers/constraint.yml +8 -0
- data/lib/spec_forge/normalizers/expectation.yml +47 -0
- data/lib/spec_forge/normalizers/factory.yml +12 -0
- data/lib/spec_forge/normalizers/factory_reference.yml +15 -0
- data/lib/spec_forge/normalizers/global_context.yml +28 -0
- data/lib/spec_forge/normalizers/spec.yml +50 -0
- data/lib/spec_forge/runner/adapter.rb +183 -0
- data/lib/spec_forge/runner/debug_proxy.rb +3 -3
- data/lib/spec_forge/runner/state.rb +4 -5
- data/lib/spec_forge/runner.rb +40 -124
- data/lib/spec_forge/spec/expectation/constraint.rb +13 -5
- data/lib/spec_forge/spec/expectation.rb +7 -3
- data/lib/spec_forge/spec.rb +13 -58
- data/lib/spec_forge/version.rb +1 -1
- data/lib/spec_forge.rb +30 -23
- data/lib/templates/openapi.yml.tt +22 -0
- data/lib/templates/redoc.html.tt +28 -0
- data/lib/templates/swagger.html.tt +59 -0
- metadata +92 -14
- data/lib/spec_forge/normalizer/configuration.rb +0 -90
- data/lib/spec_forge/normalizer/constraint.rb +0 -60
- data/lib/spec_forge/normalizer/expectation.rb +0 -105
- data/lib/spec_forge/normalizer/factory.rb +0 -78
- data/lib/spec_forge/normalizer/factory_reference.rb +0 -85
- data/lib/spec_forge/normalizer/global_context.rb +0 -88
- data/lib/spec_forge/normalizer/spec.rb +0 -97
- /data/lib/templates/{forge_helper.tt → forge_helper.rb.tt} +0 -0
- /data/lib/templates/{new_factory.tt → new_factory.yml.tt} +0 -0
- /data/lib/templates/{new_spec.tt → new_spec.yml.tt} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f8c38cb9ff50cb02ee74171a4d571ac411807b1dcce43edab0f28a757269b2f
|
4
|
+
data.tar.gz: 1595911a7a84d7712ddcc5bf0e2a08c0c30afce995ac02bb1372c1ae29ca7db0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 731ff8f0cc847010f38caab823c7d30c0d3d7083d4ea53de6ac46c19929c2c31ce698db787303c706ae6ee517dea396ef5ceb220732dd172050967404dd102af
|
7
|
+
data.tar.gz: 1a24dd8f84528427e30d105c4e358ca226b699e79a68a9ac2eb5385a974abb2acb27f8c8a3751e8c69a70001ef8d2ef3938ab06f9c81f1f041325b1b038923e1
|
data/CHANGELOG.md
CHANGED
@@ -15,7 +15,115 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
15
15
|
### Removed
|
16
16
|
-->
|
17
17
|
|
18
|
-
## [
|
18
|
+
## [0.7.0] - 12025-06-22
|
19
|
+
|
20
|
+
### Added
|
21
|
+
|
22
|
+
#### 🚀 Documentation-First Architecture
|
23
|
+
**The Big Picture**: SpecForge now generates OpenAPI documentation from your tests automatically!
|
24
|
+
|
25
|
+
- **Primary Documentation Workflow**: New `docs` command (now the default!) generates OpenAPI specs from test execution
|
26
|
+
- Smart caching system with `--fresh` flag for forced regeneration
|
27
|
+
- Multiple output formats: YAML (default) or JSON via `--format`
|
28
|
+
- Custom output paths with `--output` option
|
29
|
+
- Built-in OpenAPI 3.0.4 validation with detailed, helpful error messages
|
30
|
+
- Optional validation skip with `--skip-validation` for faster iterations
|
31
|
+
|
32
|
+
- **Live Documentation Server**: `spec_forge serve` command for immediate feedback
|
33
|
+
- Local web server with Swagger UI (default) or Redoc (`--ui redoc`)
|
34
|
+
- Configurable port with `--port` (defaults to 8080)
|
35
|
+
- Auto-generated HTML templates for both UI options
|
36
|
+
- Perfect for development and API review workflows
|
37
|
+
|
38
|
+
- **Flexible Configuration System**:
|
39
|
+
- Directory-based config: `config/components/`, `config/paths/`, etc.
|
40
|
+
- Template-based initialization with sensible defaults
|
41
|
+
- Enhanced YAML merging with `$ref` support
|
42
|
+
- Full OpenAPI customization through configuration files
|
43
|
+
|
44
|
+
#### 🧪 Enhanced Testing Capabilities
|
45
|
+
|
46
|
+
- **HTTP Header Testing**: Comprehensive header validation
|
47
|
+
```yaml
|
48
|
+
headers:
|
49
|
+
Content-Type: "application/json"
|
50
|
+
X-Request-ID: /^[0-9a-f-]{36}$/
|
51
|
+
Cache-Control:
|
52
|
+
matcher.and:
|
53
|
+
- matcher.include: "max-age="
|
54
|
+
- matcher.include: "private"
|
55
|
+
```
|
56
|
+
|
57
|
+
- **Flexible Store System**: Store anything, access everything
|
58
|
+
- OpenStruct-based entries for maximum flexibility
|
59
|
+
- Custom data via callbacks (config, metadata, computed values)
|
60
|
+
- Same familiar `store.id.attribute` syntax
|
61
|
+
- Perfect for complex test scenarios and feature flags
|
62
|
+
|
63
|
+
- **Documentation Control**: Fine-grained control over what gets documented
|
64
|
+
- New `documentation: true/false` attribute for specs and expectations
|
65
|
+
- Exclude test-only scenarios from API docs while keeping functionality
|
66
|
+
|
67
|
+
#### ⚙️ Architecture Improvements
|
68
|
+
|
69
|
+
- **YAML-Driven Normalizers**: Configuration over code
|
70
|
+
- Structure definitions in `lib/spec_forge/normalizers/*.yml`
|
71
|
+
- Powerful `reference:` system for reusable components
|
72
|
+
- Wildcard support (`*`) for catch-all schemas
|
73
|
+
- Centralized validation logic in dedicated module
|
74
|
+
|
75
|
+
- **Enhanced CLI Experience**:
|
76
|
+
- Improved `init` command with `--skip-openapi` and `--skip-factories` flags
|
77
|
+
- Better help text and examples throughout
|
78
|
+
- Clearer error messages with actionable context
|
79
|
+
|
80
|
+
- **Developer Utilities**:
|
81
|
+
- `Array#to_merged_h` for cleaner hash merging
|
82
|
+
- Unified `.normalize!(input, using:)` API across normalizers
|
83
|
+
- Separated test preparation (`Runner.prepare`) from execution
|
84
|
+
|
85
|
+
### Changed
|
86
|
+
|
87
|
+
#### 🎯 User Experience Overhaul
|
88
|
+
|
89
|
+
- **New Default Behavior**: `spec_forge` without arguments now shows help instead of running tests
|
90
|
+
- **Breaking Change**: Use `spec_forge docs` for documentation or `spec_forge run` for test-only execution
|
91
|
+
- Safer default that guides users to the right command for their needs
|
92
|
+
|
93
|
+
- **Streamlined Commands**:
|
94
|
+
- Better command organization and help text
|
95
|
+
- Consistent flag naming across commands
|
96
|
+
- Enhanced error handling with helpful suggestions
|
97
|
+
|
98
|
+
#### 🏗️ Internal Refactoring
|
99
|
+
|
100
|
+
- **Normalizer Architecture**: YAML-based instead of class-heavy approach
|
101
|
+
- Consolidated shared definitions in `_shared.yml`
|
102
|
+
- Easier maintenance and extension
|
103
|
+
- Better error context with attribute path tracking
|
104
|
+
|
105
|
+
- **Test Execution Pipeline**:
|
106
|
+
- Clean separation between test preparation and execution
|
107
|
+
- Enhanced RSpec adapter pattern
|
108
|
+
- Better reusability for documentation generation
|
109
|
+
|
110
|
+
- **HTTP & Store Improvements**:
|
111
|
+
- Automatic header value string conversion
|
112
|
+
- Simplified store entry structure with OpenStruct flexibility
|
113
|
+
- Enhanced request/response handling
|
114
|
+
|
115
|
+
### Removed
|
116
|
+
|
117
|
+
- **Legacy Architecture**: Individual normalizer class files (replaced with YAML config)
|
118
|
+
|
119
|
+
---
|
120
|
+
|
121
|
+
**Migration Notes**:
|
122
|
+
- Update any scripts using bare `spec_forge` - now shows help instead of running tests
|
123
|
+
- Use `spec_forge docs` for documentation generation or `spec_forge run` for testing
|
124
|
+
- Store access patterns remain the same, but internal structure is more flexible
|
125
|
+
|
126
|
+
## [0.6.0] - 12025-03-25
|
19
127
|
|
20
128
|
### Added
|
21
129
|
|
@@ -231,7 +339,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
231
339
|
|
232
340
|
- Initial commit
|
233
341
|
|
234
|
-
[unreleased]: https://github.com/itsthedevman/spec_forge/compare/v0.
|
342
|
+
[unreleased]: https://github.com/itsthedevman/spec_forge/compare/v0.7.0...HEAD
|
343
|
+
[0.7.0]: https://github.com/itsthedevman/spec_forge/compare/v0.6.0...v0.7.0
|
344
|
+
[0.6.0]: https://github.com/itsthedevman/spec_forge/compare/v0.5.0...v0.6.0
|
235
345
|
[0.5.0]: https://github.com/itsthedevman/spec_forge/compare/v0.4.0...v0.5.0
|
236
346
|
[0.4.0]: https://github.com/itsthedevman/spec_forge/compare/v0.3.2...v0.4.0
|
237
347
|
[0.3.2]: https://github.com/itsthedevman/spec_forge/compare/v0.3.0...v0.3.2
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
> Note: The code in this repository represents the latest development version with new features and improvements that are being prepared for future releases. For the current stable version, check out [v0.6.0](https://github.com/itsthedevman/spec_forge/releases/tag/v0.6.0) on GitHub releases.
|
8
8
|
|
9
|
-
Write API tests in YAML that read like documentation:
|
9
|
+
Write API tests in YAML that read like documentation and generate OpenAPI specifications:
|
10
10
|
|
11
11
|
```yaml
|
12
12
|
show_user:
|
@@ -25,22 +25,39 @@ show_user:
|
|
25
25
|
matcher.and:
|
26
26
|
- kind_of.string
|
27
27
|
- /@/
|
28
|
+
headers:
|
29
|
+
Content-Type: "application/json"
|
30
|
+
X-Request-ID: /^[0-9a-f-]{36}$/
|
28
31
|
```
|
29
32
|
|
30
|
-
That's a complete test. No Ruby code, no configuration files, no HTTP client setup - just
|
33
|
+
That's a complete test that validates your API and creates OpenAPI documentation. No Ruby code, no configuration files, no HTTP client setup - just clear, executable specifications.
|
31
34
|
|
32
35
|
## Why SpecForge?
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
**For Testing:**
|
38
|
+
|
39
|
+
- **Reduce Boilerplate**: Write tests without repetitive setup code and HTTP configuration
|
40
|
+
- **Quick Setup**: Start testing APIs in minutes instead of spending hours on test infrastructure
|
41
|
+
- **Clear Syntax**: Tests that everyone can read and understand, regardless of Ruby expertise
|
42
|
+
|
43
|
+
**For Documentation:**
|
44
|
+
|
45
|
+
- **OpenAPI Generation**: Generate OpenAPI specifications from your test structure, with full customization through configuration files
|
46
|
+
- **Living Documentation**: Your tests ensure the documentation always matches your actual API behavior
|
47
|
+
- **Professional Output**: View your API docs in Swagger UI or Redoc with minimal setup
|
48
|
+
|
49
|
+
**For Teams:**
|
50
|
+
|
51
|
+
- **Developer & QA Collaboration**: Create specifications that both developers and QA can maintain
|
52
|
+
- **Gradual Adoption**: Use alongside your existing test suite, introducing it incrementally where it makes sense
|
39
53
|
|
40
54
|
## Key Features
|
41
55
|
|
56
|
+
- **Automatic Documentation Generation**: Transform tests into OpenAPI specifications with customizable configuration
|
57
|
+
- **Live Documentation Server**: Local development server for viewing generated documentation
|
42
58
|
- **YAML-Based Tests**: Write clear, declarative tests that read like documentation
|
43
59
|
- **RSpec Integration**: Leverage all the power of RSpec matchers and expectations
|
60
|
+
- **Header Testing**: Comprehensive HTTP header validation with compound matchers
|
44
61
|
- **FactoryBot Integration**: Generate test data with FactoryBot integration
|
45
62
|
- **Faker Integration**: Create realistic test data with Faker
|
46
63
|
- **Variable System**: Define and reference variables for dynamic test data
|
@@ -49,6 +66,23 @@ That's a complete test. No Ruby code, no configuration files, no HTTP client set
|
|
49
66
|
- **Global Variables**: Define shared configuration at the file level
|
50
67
|
- **Callback System**: Hook into the test lifecycle using Ruby for setup, teardown, and much more!
|
51
68
|
|
69
|
+
## Quick Start
|
70
|
+
|
71
|
+
Get started with SpecForge in 3 commands:
|
72
|
+
|
73
|
+
```bash
|
74
|
+
# 1. Initialize SpecForge
|
75
|
+
spec_forge init
|
76
|
+
|
77
|
+
# 2. Create your first test
|
78
|
+
spec_forge new spec users
|
79
|
+
|
80
|
+
# 3. View your documentation
|
81
|
+
spec_forge serve
|
82
|
+
```
|
83
|
+
|
84
|
+
Then visit `http://localhost:8080` to see your API documentation!
|
85
|
+
|
52
86
|
## When Not to Use SpecForge
|
53
87
|
|
54
88
|
Consider alternatives when you need:
|
@@ -92,12 +126,103 @@ Create your first test:
|
|
92
126
|
spec_forge new spec users
|
93
127
|
```
|
94
128
|
|
95
|
-
|
129
|
+
Generate documentation (default command):
|
130
|
+
|
131
|
+
```bash
|
132
|
+
spec_forge
|
133
|
+
```
|
134
|
+
|
135
|
+
Or start the live documentation server:
|
136
|
+
|
137
|
+
```bash
|
138
|
+
spec_forge serve
|
139
|
+
```
|
140
|
+
|
141
|
+
Run tests only (no documentation):
|
96
142
|
|
97
143
|
```bash
|
98
144
|
spec_forge run
|
99
145
|
```
|
100
146
|
|
147
|
+
## Documentation Workflow
|
148
|
+
|
149
|
+
SpecForge provides multiple ways to work with your API documentation:
|
150
|
+
|
151
|
+
```bash
|
152
|
+
# Generate OpenAPI specifications
|
153
|
+
spec_forge docs # Smart caching
|
154
|
+
spec_forge docs --fresh # Force regeneration
|
155
|
+
spec_forge docs --format json # Output as JSON instead of YAML
|
156
|
+
|
157
|
+
# View documentation in browser
|
158
|
+
spec_forge serve # Generate if needed + serve
|
159
|
+
spec_forge serve --fresh # Force regeneration + serve
|
160
|
+
spec_forge serve --ui redoc # Use Redoc instead
|
161
|
+
spec_forge serve --port 3001 # Custom port
|
162
|
+
|
163
|
+
# Traditional testing
|
164
|
+
spec_forge run # Pure testing mode
|
165
|
+
spec_forge run users:show_user # Run specific tests
|
166
|
+
```
|
167
|
+
|
168
|
+
## Example: Complete User API
|
169
|
+
|
170
|
+
```yaml
|
171
|
+
# spec_forge/specs/users.yml
|
172
|
+
global:
|
173
|
+
variables:
|
174
|
+
admin_role: "admin"
|
175
|
+
|
176
|
+
list_users:
|
177
|
+
path: /users
|
178
|
+
expectations:
|
179
|
+
- expect:
|
180
|
+
status: 200
|
181
|
+
headers:
|
182
|
+
Content-Type: "application/json"
|
183
|
+
json:
|
184
|
+
users:
|
185
|
+
matcher.have_size:
|
186
|
+
be.greater_than: 0
|
187
|
+
|
188
|
+
create_user:
|
189
|
+
path: /users
|
190
|
+
method: POST
|
191
|
+
variables:
|
192
|
+
username: faker.internet.username
|
193
|
+
email: faker.internet.email
|
194
|
+
body:
|
195
|
+
name: variables.username
|
196
|
+
email: variables.email
|
197
|
+
role: global.variables.admin_role
|
198
|
+
store_as: new_user
|
199
|
+
expectations:
|
200
|
+
- expect:
|
201
|
+
status: 201
|
202
|
+
headers:
|
203
|
+
Location: /\/users\/\d+/
|
204
|
+
json:
|
205
|
+
id: kind_of.integer
|
206
|
+
name: variables.username
|
207
|
+
email: variables.email
|
208
|
+
role: global.variables.admin_role
|
209
|
+
|
210
|
+
show_user:
|
211
|
+
path: /users/{id}
|
212
|
+
query:
|
213
|
+
id: store.new_user.body.id
|
214
|
+
expectations:
|
215
|
+
- expect:
|
216
|
+
status: 200
|
217
|
+
json:
|
218
|
+
id: store.new_user.body.id
|
219
|
+
name: store.new_user.body.name
|
220
|
+
email: store.new_user.body.email
|
221
|
+
role: global.variables.admin_role
|
222
|
+
```
|
223
|
+
|
224
|
+
This automatically generates a complete OpenAPI specification with all endpoints, request/response schemas, and examples!
|
225
|
+
|
101
226
|
## Documentation
|
102
227
|
|
103
228
|
For comprehensive documentation, visit the [SpecForge Wiki](https://github.com/itsthedevman/spec_forge/wiki) which includes:
|
data/flake.lock
CHANGED
@@ -20,11 +20,11 @@
|
|
20
20
|
},
|
21
21
|
"nixpkgs": {
|
22
22
|
"locked": {
|
23
|
-
"lastModified":
|
24
|
-
"narHash": "sha256-
|
23
|
+
"lastModified": 1750365781,
|
24
|
+
"narHash": "sha256-XE/lFNhz5lsriMm/yjXkvSZz5DfvKJLUjsS6pP8EC50=",
|
25
25
|
"owner": "NixOS",
|
26
26
|
"repo": "nixpkgs",
|
27
|
-
"rev": "
|
27
|
+
"rev": "08f22084e6085d19bcfb4be30d1ca76ecb96fe54",
|
28
28
|
"type": "github"
|
29
29
|
},
|
30
30
|
"original": {
|
data/flake.nix
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
{
|
2
|
-
description = "Ruby 3.
|
2
|
+
description = "Ruby 3.2 development environment";
|
3
3
|
|
4
4
|
inputs = {
|
5
5
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
@@ -20,8 +20,8 @@
|
|
20
20
|
{
|
21
21
|
devShells.default = pkgs.mkShell {
|
22
22
|
buildInputs = with pkgs; [
|
23
|
-
(
|
24
|
-
jemallocSupport =
|
23
|
+
(ruby_3_2.override {
|
24
|
+
jemallocSupport = false;
|
25
25
|
docSupport = false;
|
26
26
|
})
|
27
27
|
|
@@ -80,7 +80,7 @@ module SpecForge
|
|
80
80
|
super
|
81
81
|
|
82
82
|
# Check the arguments before preparing them
|
83
|
-
arguments[:keyword] = Normalizer.
|
83
|
+
arguments[:keyword] = Normalizer.normalize!(arguments[:keyword], using: :factory_reference)
|
84
84
|
|
85
85
|
prepare_arguments!
|
86
86
|
end
|
data/lib/spec_forge/callbacks.rb
CHANGED
@@ -36,6 +36,15 @@ module SpecForge
|
|
36
36
|
instance[name.to_s] = block
|
37
37
|
end
|
38
38
|
|
39
|
+
#
|
40
|
+
# Deregisters a callback
|
41
|
+
#
|
42
|
+
# @param name [String, Symbol] The name of the callback
|
43
|
+
#
|
44
|
+
def deregister(name)
|
45
|
+
instance.delete(name.to_s)
|
46
|
+
end
|
47
|
+
|
39
48
|
#
|
40
49
|
# Checks if a callback is registered for the given event
|
41
50
|
#
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SpecForge
|
4
|
+
class CLI
|
5
|
+
class Docs < Command
|
6
|
+
#
|
7
|
+
# Shared functionality for generating OpenAPI documentation
|
8
|
+
#
|
9
|
+
# This module contains the core logic for running tests, extracting endpoint
|
10
|
+
# data, and generating OpenAPI specifications. It's used by both the Docs
|
11
|
+
# and Serve commands to avoid duplication.
|
12
|
+
#
|
13
|
+
module Generate
|
14
|
+
#
|
15
|
+
# Generates OpenAPI documentation and writes it to disk
|
16
|
+
#
|
17
|
+
# Runs the documentation generation pipeline: executes tests, extracts
|
18
|
+
# endpoint data, generates OpenAPI spec, validates it, and writes the
|
19
|
+
# output file in the specified format.
|
20
|
+
#
|
21
|
+
# @return [Pathname] The path to the generated documentation file
|
22
|
+
#
|
23
|
+
def generate_documentation
|
24
|
+
generator = Documentation::Generators::OpenAPI["3.0"]
|
25
|
+
output = generator.generate(use_cache: !options.fresh)
|
26
|
+
|
27
|
+
generator.validate!(output) unless options.skip_validation
|
28
|
+
|
29
|
+
# Determine output format and path
|
30
|
+
file_format = determine_file_format
|
31
|
+
file_path = determine_output_path(file_format)
|
32
|
+
|
33
|
+
content =
|
34
|
+
if file_format == "json"
|
35
|
+
JSON.pretty_generate(output)
|
36
|
+
else
|
37
|
+
output.to_yaml(stringify_names: true)
|
38
|
+
end
|
39
|
+
|
40
|
+
::File.write(file_path, content)
|
41
|
+
|
42
|
+
file_path
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def determine_file_format
|
48
|
+
file_format = options.format&.downcase || "yml"
|
49
|
+
validate_format!(file_format)
|
50
|
+
|
51
|
+
file_format
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_format!(format)
|
55
|
+
return if VALID_FORMATS.include?(format)
|
56
|
+
|
57
|
+
raise ArgumentError,
|
58
|
+
"Invalid format #{format.in_quotes}. Valid formats: #{VALID_FORMATS.join_map(", ", &:in_quotes)}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def determine_output_path(format)
|
62
|
+
if options.output
|
63
|
+
Pathname.new(options.output)
|
64
|
+
else
|
65
|
+
extension = (format == "json") ? "json" : "yml"
|
66
|
+
SpecForge.openapi_path.join("generated", "openapi.#{extension}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "docs/generate"
|
4
|
+
|
5
|
+
module SpecForge
|
6
|
+
class CLI
|
7
|
+
#
|
8
|
+
# Command for generating OpenAPI documentation from SpecForge tests
|
9
|
+
#
|
10
|
+
# Runs tests and extracts endpoint data to create OpenAPI specifications.
|
11
|
+
# Uses intelligent caching to avoid unnecessary test re-execution when
|
12
|
+
# specs haven't changed.
|
13
|
+
#
|
14
|
+
# @example Generate documentation
|
15
|
+
# spec_forge docs
|
16
|
+
#
|
17
|
+
# @example Generate with fresh test run
|
18
|
+
# spec_forge docs --fresh
|
19
|
+
#
|
20
|
+
class Docs < Command
|
21
|
+
include Docs::Generate
|
22
|
+
|
23
|
+
#
|
24
|
+
# Valid file formats for documentation output
|
25
|
+
#
|
26
|
+
# Supported formats include YAML variants (yml, yaml) and JSON.
|
27
|
+
# Used for validation when users specify the --format option.
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
#
|
31
|
+
VALID_FORMATS = %w[yml yaml json].freeze
|
32
|
+
|
33
|
+
command_name "docs"
|
34
|
+
syntax "docs"
|
35
|
+
summary "Generate OpenAPI documentation from test results"
|
36
|
+
|
37
|
+
description <<~DESC
|
38
|
+
Generate OpenAPI documentation from test results.
|
39
|
+
|
40
|
+
Uses caching to avoid re-running tests unless specs
|
41
|
+
have changed. Output format can be YAML or JSON.
|
42
|
+
DESC
|
43
|
+
|
44
|
+
example "docs",
|
45
|
+
"Generates OpenAPI specifications from your tests using smart caching"
|
46
|
+
|
47
|
+
example "docs --fresh",
|
48
|
+
"Forces test re-execution and regenerates OpenAPI specs ignoring cache"
|
49
|
+
|
50
|
+
example "docs --format json",
|
51
|
+
"Generates OpenAPI specifications in JSON format instead of YAML"
|
52
|
+
|
53
|
+
example "docs --output ./build/api.yml",
|
54
|
+
"Generates OpenAPI specs to a custom file path"
|
55
|
+
|
56
|
+
example "docs --skip-validation",
|
57
|
+
"Generates documentation without validating the OpenAPI specification"
|
58
|
+
|
59
|
+
option "--fresh", "Re-run all tests ignoring cache"
|
60
|
+
option "--format=FORMAT", "Output format: yml/yaml or json (default: yml)"
|
61
|
+
option "--output=PATH", "Full file path for generated documentation"
|
62
|
+
option "--skip-validation", "Skip OpenAPI specification validation during generation"
|
63
|
+
|
64
|
+
#
|
65
|
+
# Generates OpenAPI documentation from tests
|
66
|
+
#
|
67
|
+
# Runs all SpecForge tests and creates OpenAPI specifications from the
|
68
|
+
# successful test results. This is the main entry point for the docs workflow.
|
69
|
+
#
|
70
|
+
# @return [void]
|
71
|
+
#
|
72
|
+
def call
|
73
|
+
# spec_forge/openapi/generated
|
74
|
+
generated_path = SpecForge.openapi_path.join("generated")
|
75
|
+
actions.empty_directory(generated_path, verbose: false)
|
76
|
+
actions.empty_directory(generated_path.join(".cache"), verbose: false)
|
77
|
+
|
78
|
+
file_path = generate_documentation
|
79
|
+
|
80
|
+
puts <<~STRING
|
81
|
+
|
82
|
+
========================================
|
83
|
+
🎉 Success!
|
84
|
+
========================================
|
85
|
+
|
86
|
+
Your OpenAPI specification is valid and ready to use.
|
87
|
+
Output written to: #{file_path.relative_path_from(SpecForge.forge_path)}
|
88
|
+
STRING
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/spec_forge/cli/init.rb
CHANGED
@@ -11,21 +11,53 @@ module SpecForge
|
|
11
11
|
class Init < Command
|
12
12
|
command_name "init"
|
13
13
|
syntax "init"
|
14
|
-
summary "
|
14
|
+
summary "Set up your SpecForge project (creates folders and config files)"
|
15
|
+
|
16
|
+
description <<~DESC
|
17
|
+
Creates the SpecForge project structure.
|
18
|
+
|
19
|
+
Sets up:
|
20
|
+
• spec_forge/specs/ for test files
|
21
|
+
• spec_forge/factories/ for test data (optional)
|
22
|
+
• spec_forge/openapi/ for documentation config (optional)
|
23
|
+
• forge_helper.rb for configuration
|
24
|
+
DESC
|
25
|
+
|
26
|
+
option "--skip-openapi", "Skip generating the \"openapi\" directory"
|
27
|
+
option "--skip-factories", "Skip generating the \"factories\" directory"
|
15
28
|
|
16
29
|
#
|
17
30
|
# Creates the "spec_forge", "spec_forge/factories", and "spec_forge/specs" directories
|
18
31
|
# Also creates the "spec_forge.rb" initialization file
|
19
32
|
#
|
20
33
|
def call
|
34
|
+
initialize_forge
|
35
|
+
initialize_openapi unless options.skip_openapi
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def initialize_forge
|
21
41
|
base_path = SpecForge.forge_path
|
22
|
-
actions.empty_directory
|
23
|
-
actions.empty_directory
|
42
|
+
actions.empty_directory(base_path.join("specs"))
|
43
|
+
actions.empty_directory(base_path.join("factories")) unless options.skip_factories
|
44
|
+
actions.template("forge_helper.rb.tt", base_path.join("forge_helper.rb"))
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize_openapi
|
48
|
+
# spec_forge/openapi
|
49
|
+
openapi_path = SpecForge.openapi_path
|
50
|
+
actions.empty_directory(openapi_path)
|
51
|
+
|
52
|
+
# spec_forge/openapi/config
|
53
|
+
config_path = openapi_path.join("config")
|
54
|
+
|
55
|
+
actions.empty_directory(config_path)
|
56
|
+
actions.empty_directory(config_path.join("paths")) # openapi/config/paths
|
57
|
+
actions.empty_directory(config_path.join("components")) # openapi/config/components
|
24
58
|
|
25
|
-
|
26
|
-
|
27
|
-
SpecForge.root.join(base_path, "forge_helper.rb")
|
28
|
-
)
|
59
|
+
# openapi/config/openapi.yml
|
60
|
+
actions.template("openapi.yml.tt", config_path.join("openapi.yml"))
|
29
61
|
end
|
30
62
|
end
|
31
63
|
end
|
data/lib/spec_forge/cli/new.rb
CHANGED
@@ -13,7 +13,17 @@ module SpecForge
|
|
13
13
|
#
|
14
14
|
class New < Command
|
15
15
|
command_name "new"
|
16
|
-
summary "Create
|
16
|
+
summary "Create new test specs or data factories"
|
17
|
+
|
18
|
+
description <<~DESC
|
19
|
+
Generate new files from templates.
|
20
|
+
|
21
|
+
Types:
|
22
|
+
• spec - Creates YAML test files with common patterns
|
23
|
+
• factory - Creates FactoryBot factories for test data
|
24
|
+
|
25
|
+
Files are created in the appropriate spec_forge/ subdirectory.
|
26
|
+
DESC
|
17
27
|
|
18
28
|
syntax "new <type> <name>"
|
19
29
|
|
@@ -51,7 +61,7 @@ module SpecForge
|
|
51
61
|
|
52
62
|
def create_new_spec(name)
|
53
63
|
actions.template(
|
54
|
-
"new_spec.tt",
|
64
|
+
"new_spec.yml.tt",
|
55
65
|
SpecForge.forge_path.join("specs", "#{name}.yml"),
|
56
66
|
context: Proxy.new(name).call
|
57
67
|
)
|
@@ -59,7 +69,7 @@ module SpecForge
|
|
59
69
|
|
60
70
|
def create_new_factory(name)
|
61
71
|
actions.template(
|
62
|
-
"new_factory.tt",
|
72
|
+
"new_factory.yml.tt",
|
63
73
|
SpecForge.forge_path.join("factories", "#{name}.yml"),
|
64
74
|
context: Proxy.new(name).call
|
65
75
|
)
|
data/lib/spec_forge/cli/run.rb
CHANGED
@@ -21,8 +21,18 @@ module SpecForge
|
|
21
21
|
command_name "run"
|
22
22
|
syntax "run [target]"
|
23
23
|
|
24
|
-
summary "
|
25
|
-
|
24
|
+
summary "Execute your API tests with smart filtering options"
|
25
|
+
|
26
|
+
description <<~DESC
|
27
|
+
Execute API tests with filtering options.
|
28
|
+
|
29
|
+
Target formats:
|
30
|
+
• file_name - Run all specs in a file
|
31
|
+
• file:spec - Run specific spec
|
32
|
+
• file:spec:"expectation" - Run individual expectation
|
33
|
+
|
34
|
+
Uses RSpec for execution with detailed error reporting.
|
35
|
+
DESC
|
26
36
|
|
27
37
|
example "spec_forge run",
|
28
38
|
"Run all specs in spec_forge/specs/"
|
@@ -39,8 +49,6 @@ module SpecForge
|
|
39
49
|
example "spec_forge run users:create_user:\"POST /users - Create Admin\"",
|
40
50
|
"Run the specific expectation named \"Create Admin\""
|
41
51
|
|
42
|
-
# option "-n", "--no-docs", "Do not generate OpenAPI documentation on completion"
|
43
|
-
|
44
52
|
#
|
45
53
|
# Loads and runs all specs, or a subset of specs based on the provided arguments
|
46
54
|
#
|