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
@@ -0,0 +1,74 @@
|
|
1
|
+
id: string
|
2
|
+
|
3
|
+
name: string
|
4
|
+
|
5
|
+
line_number: integer
|
6
|
+
|
7
|
+
base_url:
|
8
|
+
type: string
|
9
|
+
default: null
|
10
|
+
required: false
|
11
|
+
|
12
|
+
url:
|
13
|
+
type: string
|
14
|
+
default: null
|
15
|
+
required: false
|
16
|
+
aliases:
|
17
|
+
- path
|
18
|
+
|
19
|
+
http_verb:
|
20
|
+
type: string
|
21
|
+
default: null # Do not default this to "GET". Leave it null. Seriously.
|
22
|
+
required: false
|
23
|
+
aliases:
|
24
|
+
- method
|
25
|
+
- http_method
|
26
|
+
validator: http_verb
|
27
|
+
|
28
|
+
headers:
|
29
|
+
type: hash
|
30
|
+
default: {}
|
31
|
+
required: false
|
32
|
+
|
33
|
+
query:
|
34
|
+
type:
|
35
|
+
- hash
|
36
|
+
- string
|
37
|
+
aliases:
|
38
|
+
- params
|
39
|
+
default: {}
|
40
|
+
required: false
|
41
|
+
|
42
|
+
body:
|
43
|
+
type:
|
44
|
+
- hash
|
45
|
+
- string
|
46
|
+
aliases:
|
47
|
+
- data
|
48
|
+
default: {}
|
49
|
+
required: false
|
50
|
+
|
51
|
+
variables:
|
52
|
+
type:
|
53
|
+
- hash
|
54
|
+
- string
|
55
|
+
default: {}
|
56
|
+
required: false
|
57
|
+
|
58
|
+
debug:
|
59
|
+
type: boolean
|
60
|
+
aliases:
|
61
|
+
- pry
|
62
|
+
- breakpoint
|
63
|
+
default: false
|
64
|
+
required: false
|
65
|
+
|
66
|
+
callback:
|
67
|
+
type: string
|
68
|
+
required: false
|
69
|
+
validator: callback
|
70
|
+
|
71
|
+
documentation:
|
72
|
+
type: boolean
|
73
|
+
required: false
|
74
|
+
default: true
|
@@ -0,0 +1,23 @@
|
|
1
|
+
base_url: string
|
2
|
+
|
3
|
+
headers:
|
4
|
+
reference: headers
|
5
|
+
|
6
|
+
query:
|
7
|
+
reference: query
|
8
|
+
|
9
|
+
factories:
|
10
|
+
type: hash
|
11
|
+
default: {}
|
12
|
+
structure:
|
13
|
+
###########################################
|
14
|
+
auto_discover:
|
15
|
+
type: boolean
|
16
|
+
default: true
|
17
|
+
|
18
|
+
paths:
|
19
|
+
type: array
|
20
|
+
default: []
|
21
|
+
|
22
|
+
on_debug:
|
23
|
+
type: proc
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Internal
|
2
|
+
id:
|
3
|
+
reference: id
|
4
|
+
|
5
|
+
line_number:
|
6
|
+
reference: line_number
|
7
|
+
|
8
|
+
# User defined
|
9
|
+
name:
|
10
|
+
reference: name
|
11
|
+
|
12
|
+
base_url:
|
13
|
+
reference: base_url
|
14
|
+
|
15
|
+
url:
|
16
|
+
reference: url
|
17
|
+
|
18
|
+
http_verb:
|
19
|
+
reference: http_verb
|
20
|
+
|
21
|
+
headers:
|
22
|
+
reference: headers
|
23
|
+
|
24
|
+
query:
|
25
|
+
reference: query
|
26
|
+
|
27
|
+
body:
|
28
|
+
reference: body
|
29
|
+
|
30
|
+
variables:
|
31
|
+
reference: variables
|
32
|
+
|
33
|
+
debug:
|
34
|
+
reference: debug
|
35
|
+
|
36
|
+
store_as:
|
37
|
+
type: string
|
38
|
+
default: ""
|
39
|
+
|
40
|
+
documentation:
|
41
|
+
reference: documentation
|
42
|
+
|
43
|
+
expect:
|
44
|
+
type: hash
|
45
|
+
structure:
|
46
|
+
###########################################
|
47
|
+
reference: constraint
|
@@ -0,0 +1,28 @@
|
|
1
|
+
variables:
|
2
|
+
reference: variables
|
3
|
+
|
4
|
+
callbacks:
|
5
|
+
type: array
|
6
|
+
default: []
|
7
|
+
structure:
|
8
|
+
###########################################
|
9
|
+
type: hash
|
10
|
+
default: {}
|
11
|
+
structure:
|
12
|
+
###########################################
|
13
|
+
before_file:
|
14
|
+
reference: callback
|
15
|
+
before_spec:
|
16
|
+
reference: callback
|
17
|
+
before_each:
|
18
|
+
reference: callback
|
19
|
+
aliases:
|
20
|
+
- before
|
21
|
+
after_each:
|
22
|
+
reference: callback
|
23
|
+
aliases:
|
24
|
+
- after
|
25
|
+
after_spec:
|
26
|
+
reference: callback
|
27
|
+
after_file:
|
28
|
+
reference: callback
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Internal
|
2
|
+
id:
|
3
|
+
reference: id
|
4
|
+
|
5
|
+
name:
|
6
|
+
reference: name
|
7
|
+
|
8
|
+
file_name: string
|
9
|
+
|
10
|
+
file_path: string
|
11
|
+
|
12
|
+
line_number:
|
13
|
+
reference: line_number
|
14
|
+
|
15
|
+
# User defined
|
16
|
+
base_url:
|
17
|
+
reference: base_url
|
18
|
+
|
19
|
+
url:
|
20
|
+
reference: url
|
21
|
+
|
22
|
+
http_verb:
|
23
|
+
reference: http_verb
|
24
|
+
|
25
|
+
headers:
|
26
|
+
reference: headers
|
27
|
+
|
28
|
+
query:
|
29
|
+
reference: query
|
30
|
+
|
31
|
+
body:
|
32
|
+
reference: body
|
33
|
+
|
34
|
+
variables:
|
35
|
+
reference: variables
|
36
|
+
|
37
|
+
debug:
|
38
|
+
reference: debug
|
39
|
+
|
40
|
+
documentation:
|
41
|
+
reference: documentation
|
42
|
+
|
43
|
+
expectations:
|
44
|
+
type: array
|
45
|
+
structure:
|
46
|
+
###########################################
|
47
|
+
type: hash
|
48
|
+
structure:
|
49
|
+
###########################################
|
50
|
+
reference: expectation
|
@@ -0,0 +1,183 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SpecForge
|
4
|
+
class Runner
|
5
|
+
#
|
6
|
+
# Bridges SpecForge specs with RSpec execution
|
7
|
+
#
|
8
|
+
# Converts SpecForge forge objects into RSpec test structures
|
9
|
+
# and manages the test execution lifecycle.
|
10
|
+
#
|
11
|
+
class Adapter
|
12
|
+
include Singleton
|
13
|
+
|
14
|
+
#
|
15
|
+
# Configures RSpec with forge definitions
|
16
|
+
#
|
17
|
+
# Sets up RSpec and prepares everything for running tests
|
18
|
+
#
|
19
|
+
# @param forges [Array<Forge>] The forges to set up for testing
|
20
|
+
#
|
21
|
+
def self.setup(forges)
|
22
|
+
# Defines the forges with RSpec
|
23
|
+
forges.each { |forge| instance.describe(forge) }
|
24
|
+
|
25
|
+
# Disable autorun because RSpec does it
|
26
|
+
RSpec::Core::Runner.disable_autorun!
|
27
|
+
|
28
|
+
# Allows modifying the error backtrace reporting within rspec
|
29
|
+
RSpec.configuration.instance_variable_set(:@backtrace_formatter, BacktraceFormatter)
|
30
|
+
|
31
|
+
# Listen for passed/failed events to trigger the "after_each" callback
|
32
|
+
RSpec.configuration.reporter.register_listener(
|
33
|
+
Listener.instance,
|
34
|
+
:example_passed, :example_failed
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Executes the configured RSpec tests
|
40
|
+
#
|
41
|
+
# Runs all configured tests through RSpec with optional exit behavior.
|
42
|
+
#
|
43
|
+
# @param exit_on_finish [Boolean] Whether to exit the process when done
|
44
|
+
# @param exit_on_failure [Boolean] Whether to exit the process if any test fails
|
45
|
+
#
|
46
|
+
# @return [Integer, nil] Exit status if exit_on_finish is false
|
47
|
+
#
|
48
|
+
def self.run(exit_on_finish: false, exit_on_failure: false)
|
49
|
+
status = RSpec::Core::Runner.run([]).to_i
|
50
|
+
|
51
|
+
exit(status) if exit_on_finish || (exit_on_failure && status != 0)
|
52
|
+
|
53
|
+
status
|
54
|
+
end
|
55
|
+
|
56
|
+
##########################################################################
|
57
|
+
|
58
|
+
#
|
59
|
+
# Defines RSpec examples for a specific forge
|
60
|
+
# Creates the test structure for a single forge file
|
61
|
+
#
|
62
|
+
# @param forge [Forge] The forge to define
|
63
|
+
#
|
64
|
+
def describe(forge)
|
65
|
+
# This is just like writing a normal RSpec test, except with loops ;)
|
66
|
+
RSpec.describe(forge.name) do
|
67
|
+
# Callback for the file
|
68
|
+
before(:context) { Callbacks.before_file(forge) }
|
69
|
+
after(:context) { Callbacks.after_file(forge) }
|
70
|
+
|
71
|
+
# Specs
|
72
|
+
forge.specs.each do |spec|
|
73
|
+
# Describe the spec
|
74
|
+
describe(spec.name) do
|
75
|
+
# Request data is for the spec and contains the base and overlays
|
76
|
+
let!(:request_data) { forge.request[spec.id] }
|
77
|
+
|
78
|
+
# The HTTP client for the spec
|
79
|
+
let!(:http_client) { HTTP::Client.new(**request_data[:base]) }
|
80
|
+
|
81
|
+
# Callback for the spec
|
82
|
+
before(:context) { Callbacks.before_spec(forge, spec) }
|
83
|
+
after(:context) { Callbacks.after_spec(forge, spec) }
|
84
|
+
|
85
|
+
# Expectations
|
86
|
+
spec.expectations.each do |expectation|
|
87
|
+
# Onto the actual expectation itself
|
88
|
+
describe(expectation.name) do
|
89
|
+
# Set metadata for the example group for error reporting
|
90
|
+
Metadata.set_for_group(spec, expectation, self)
|
91
|
+
|
92
|
+
# Lazily load the constraints
|
93
|
+
let(:constraints) { expectation.constraints.as_matchers }
|
94
|
+
|
95
|
+
let(:match_status) { constraints[:status] }
|
96
|
+
let(:match_json) { constraints[:json] }
|
97
|
+
let(:match_json_class) { be_kind_of(match_json.class) }
|
98
|
+
let(:match_headers) { constraints[:headers] }
|
99
|
+
|
100
|
+
# The request for the test itself. Overlays the expectation's data if it exists
|
101
|
+
let(:request) do
|
102
|
+
request = request_data[:base]
|
103
|
+
|
104
|
+
if (overlay = request_data[:overlay][expectation.id])
|
105
|
+
request = request.deep_merge(overlay)
|
106
|
+
end
|
107
|
+
|
108
|
+
HTTP::Request.new(**request)
|
109
|
+
end
|
110
|
+
|
111
|
+
# The Faraday response
|
112
|
+
subject(:response) { http_client.call(request) }
|
113
|
+
|
114
|
+
# Callbacks for the expectation
|
115
|
+
before :each do
|
116
|
+
Callbacks.before_expectation(
|
117
|
+
forge, spec, expectation, self, RSpec.current_example
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
# The 'after_expectation' callback is handled by Listener due to RSpec not
|
122
|
+
# reporting the example's status until after the describe block has finished.
|
123
|
+
after :each do
|
124
|
+
# However, the downside about having the callback triggered later is that RSpec
|
125
|
+
# will have reset the memoized let variables back to nil.
|
126
|
+
# This causes an issue when an expectation goes to store the state, it will end
|
127
|
+
# up re-calling the various variables and triggering another HTTP request.
|
128
|
+
# Since the variables are still memoized in this hook, it is the perfect
|
129
|
+
# time to store the referenced to them.
|
130
|
+
State.set(response:)
|
131
|
+
end
|
132
|
+
|
133
|
+
# The test itself
|
134
|
+
it(expectation.constraints.description) do
|
135
|
+
# Debugging
|
136
|
+
if spec.debug? || expectation.debug?
|
137
|
+
Callbacks.on_debug(forge, spec, expectation, self)
|
138
|
+
end
|
139
|
+
|
140
|
+
############################################################
|
141
|
+
# Status check
|
142
|
+
expect(response.status).to match_status
|
143
|
+
|
144
|
+
############################################################
|
145
|
+
# Headers check
|
146
|
+
if match_headers.present?
|
147
|
+
match_headers.each do |key, matcher|
|
148
|
+
expect(response.headers).to include(key.downcase => matcher)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
############################################################
|
153
|
+
# JSON check
|
154
|
+
if match_json.present?
|
155
|
+
expect(response.body).to match_json_class
|
156
|
+
|
157
|
+
case match_json
|
158
|
+
when Hash
|
159
|
+
match_json.each do |key, matcher|
|
160
|
+
expect(response.body).to include(key)
|
161
|
+
|
162
|
+
begin
|
163
|
+
expect(response.body[key]).to matcher
|
164
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
165
|
+
# Add the key that failed to the front of the error message
|
166
|
+
e.message.insert(0, "Key: #{key.in_quotes}\n")
|
167
|
+
raise e
|
168
|
+
end
|
169
|
+
end
|
170
|
+
else
|
171
|
+
expect(response.body).to match_json
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -179,14 +179,14 @@ module SpecForge
|
|
179
179
|
expectation_hash[:expect][:json] = matchers_to_description(expectation_hash[:expect][:json])
|
180
180
|
|
181
181
|
{
|
182
|
+
global:,
|
183
|
+
variables:,
|
184
|
+
request: request.to_h,
|
182
185
|
response: {
|
183
186
|
status: response.status,
|
184
187
|
body: response.body,
|
185
188
|
headers: response.headers
|
186
189
|
},
|
187
|
-
global:,
|
188
|
-
variables:,
|
189
|
-
request: request.to_h,
|
190
190
|
expectation: expectation_hash,
|
191
191
|
spec: spec_hash
|
192
192
|
}
|
@@ -87,11 +87,10 @@ module SpecForge
|
|
87
87
|
scope:,
|
88
88
|
request: request&.to_h,
|
89
89
|
variables: SpecForge.context.variables.deep_dup,
|
90
|
-
response
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
}
|
90
|
+
response:,
|
91
|
+
headers: response&.headers,
|
92
|
+
status: response&.status,
|
93
|
+
body: response&.body
|
95
94
|
)
|
96
95
|
end
|
97
96
|
end
|
data/lib/spec_forge/runner.rb
CHANGED
@@ -8,150 +8,66 @@ module SpecForge
|
|
8
8
|
class Runner
|
9
9
|
class << self
|
10
10
|
#
|
11
|
-
#
|
12
|
-
# Creates the test structure that will be executed
|
11
|
+
# Prepares forge objects for test execution
|
13
12
|
#
|
14
|
-
#
|
13
|
+
# Loads the forge helper, registers factories, loads specs from files,
|
14
|
+
# applies filtering, and returns ready-to-run forge objects.
|
15
15
|
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
#
|
23
|
-
# Runs the defined RSpec examples
|
24
|
-
# Executes the tests after they've been defined
|
25
|
-
#
|
26
|
-
def run
|
27
|
-
prepare_for_run
|
28
|
-
|
29
|
-
ARGV.clear
|
30
|
-
RSpec::Core::Runner.invoke
|
31
|
-
end
|
32
|
-
|
33
|
-
#
|
34
|
-
# Defines RSpec examples for a specific forge
|
35
|
-
# Creates the test structure for a single forge file
|
16
|
+
# @param file_name [String, nil] Optional file name filter
|
17
|
+
# @param spec_name [String, nil] Optional spec name filter
|
18
|
+
# @param expectation_name [String, nil] Optional expectation name filter
|
36
19
|
#
|
37
|
-
# @
|
20
|
+
# @return [Array<Forge>] Array of prepared forge objects
|
38
21
|
#
|
39
|
-
def
|
40
|
-
|
41
|
-
RSpec.describe(forge.name) do
|
42
|
-
# Callback for the file
|
43
|
-
before(:context) { Callbacks.before_file(forge) }
|
44
|
-
after(:context) { Callbacks.after_file(forge) }
|
45
|
-
|
46
|
-
# Specs
|
47
|
-
forge.specs.each do |spec|
|
48
|
-
# Describe the spec
|
49
|
-
describe(spec.name) do
|
50
|
-
# Request data is for the spec and contains the base and overlays
|
51
|
-
let!(:request_data) { forge.request[spec.id] }
|
52
|
-
|
53
|
-
# The HTTP client for the spec
|
54
|
-
let!(:http_client) { HTTP::Client.new(**request_data[:base]) }
|
55
|
-
|
56
|
-
# Callback for the spec
|
57
|
-
before(:context) { Callbacks.before_spec(forge, spec) }
|
58
|
-
after(:context) { Callbacks.after_spec(forge, spec) }
|
59
|
-
|
60
|
-
# Expectations
|
61
|
-
spec.expectations.each do |expectation|
|
62
|
-
# Onto the actual expectation itself
|
63
|
-
describe(expectation.name) do
|
64
|
-
# Set metadata for the example group for error reporting
|
65
|
-
Metadata.set_for_group(spec, expectation, self)
|
22
|
+
def prepare(file_name: nil, spec_name: nil, expectation_name: nil)
|
23
|
+
load_forge_helper
|
66
24
|
|
67
|
-
|
68
|
-
|
25
|
+
# Load factories
|
26
|
+
Factory.load_and_register
|
69
27
|
|
70
|
-
|
71
|
-
|
72
|
-
let(:match_json_class) { be_kind_of(match_json.class) }
|
28
|
+
# Load the specs from their files and create forges from them
|
29
|
+
forges = Loader.load_from_files.map { |f| Forge.new(*f) }
|
73
30
|
|
74
|
-
|
75
|
-
|
76
|
-
request = request_data[:base]
|
31
|
+
# Filter out the specs and expectations
|
32
|
+
forges = Filter.apply(forges, file_name:, spec_name:, expectation_name:)
|
77
33
|
|
78
|
-
|
79
|
-
|
80
|
-
end
|
34
|
+
# Tell the user that we filtered if we did
|
35
|
+
Filter.announce(forges, file_name:, spec_name:, expectation_name:)
|
81
36
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
# The Faraday response
|
86
|
-
subject(:response) { http_client.call(request) }
|
87
|
-
|
88
|
-
# Callbacks for the expectation
|
89
|
-
before :each do
|
90
|
-
Callbacks.before_expectation(
|
91
|
-
forge, spec, expectation, self, RSpec.current_example
|
92
|
-
)
|
93
|
-
end
|
94
|
-
|
95
|
-
# The 'after_expectation' callback is handled by Listener due to RSpec not
|
96
|
-
# reporting the example's status until after the describe block has finished.
|
97
|
-
after :each do
|
98
|
-
# However, the downside about having the callback triggered later is that RSpec
|
99
|
-
# will have reset the memoized let variables back to nil.
|
100
|
-
# This causes an issue when an expectation goes to store the state, it will end
|
101
|
-
# up re-calling the various variables and triggering another HTTP request.
|
102
|
-
# Since the variables are still memoized in this hook, it is the perfect
|
103
|
-
# time to store the referenced to them.
|
104
|
-
State.set(response:)
|
105
|
-
end
|
106
|
-
|
107
|
-
# The test itself
|
108
|
-
it(expectation.constraints.description) do
|
109
|
-
if spec.debug? || expectation.debug?
|
110
|
-
Callbacks.on_debug(forge, spec, expectation, self)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Status check
|
114
|
-
expect(response.status).to match_status
|
115
|
-
|
116
|
-
# JSON check
|
117
|
-
if match_json.present?
|
118
|
-
expect(response.body).to match_json_class
|
37
|
+
forges
|
38
|
+
end
|
119
39
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
end
|
136
|
-
end
|
40
|
+
#
|
41
|
+
# Runs the prepared forges through RSpec
|
42
|
+
#
|
43
|
+
# Sets up the RSpec adapter and executes all tests, with optional
|
44
|
+
# exit behavior for CLI usage.
|
45
|
+
#
|
46
|
+
# @param forges [Array<Forge>] The forge objects to run
|
47
|
+
# @param exit_on_finish [Boolean] Whether to exit the process when complete
|
48
|
+
# @param exit_on_failure [Boolean] Whether to exit the process if any test fails
|
49
|
+
#
|
50
|
+
# @return [Integer, nil] Exit status if exit_on_finish is false
|
51
|
+
#
|
52
|
+
def run(forges, **)
|
53
|
+
Adapter.setup(forges)
|
54
|
+
Adapter.run(**)
|
137
55
|
end
|
138
56
|
|
139
57
|
private
|
140
58
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
59
|
+
def load_forge_helper
|
60
|
+
forge_helper = SpecForge.forge_path.join("forge_helper.rb")
|
61
|
+
require_relative forge_helper if File.exist?(forge_helper)
|
144
62
|
|
145
|
-
#
|
146
|
-
|
147
|
-
Listener.instance,
|
148
|
-
:example_passed, :example_failed
|
149
|
-
)
|
63
|
+
# Validate in case anything was changed
|
64
|
+
SpecForge.configuration.validate
|
150
65
|
end
|
151
66
|
end
|
152
67
|
end
|
153
68
|
end
|
154
69
|
|
70
|
+
require_relative "runner/adapter"
|
155
71
|
require_relative "runner/callbacks"
|
156
72
|
require_relative "runner/debug_proxy"
|
157
73
|
require_relative "runner/listener"
|