spec_forge 0.1.0 → 0.2.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 +51 -2
- data/README.md +366 -143
- data/lib/spec_forge/attribute/chainable.rb +19 -16
- data/lib/spec_forge/attribute/factory.rb +6 -18
- data/lib/spec_forge/attribute/faker.rb +56 -20
- data/lib/spec_forge/attribute/literal.rb +4 -0
- data/lib/spec_forge/attribute/resolvable.rb +4 -6
- data/lib/spec_forge/attribute/resolvable_array.rb +4 -0
- data/lib/spec_forge/attribute/resolvable_hash.rb +4 -0
- data/lib/spec_forge/attribute/variable.rb +6 -13
- data/lib/spec_forge/attribute.rb +3 -12
- data/lib/spec_forge/cli/init.rb +2 -9
- data/lib/spec_forge/configuration.rb +58 -0
- data/lib/spec_forge/factory.rb +4 -4
- data/lib/spec_forge/http/backend.rb +42 -8
- data/lib/spec_forge/http/client.rb +2 -2
- data/lib/spec_forge/http/request.rb +11 -14
- data/lib/spec_forge/normalizer/configuration.rb +77 -0
- data/lib/spec_forge/normalizer/expectation.rb +1 -0
- data/lib/spec_forge/normalizer/spec.rb +1 -0
- data/lib/spec_forge/normalizer.rb +14 -11
- data/lib/spec_forge/runner.rb +98 -72
- data/lib/spec_forge/spec/expectation/constraint.rb +2 -5
- data/lib/spec_forge/spec/expectation.rb +32 -13
- data/lib/spec_forge/spec.rb +20 -14
- data/lib/spec_forge/version.rb +1 -1
- data/lib/spec_forge.rb +20 -11
- data/lib/templates/forge_helper.tt +48 -0
- data/spec_forge/forge_helper.rb +37 -0
- data/spec_forge/specs/users.yml +6 -4
- metadata +6 -7
- data/lib/spec_forge/config.rb +0 -84
- data/lib/spec_forge/environment.rb +0 -71
- data/lib/spec_forge/normalizer/config.rb +0 -104
- data/lib/templates/config.tt +0 -19
- data/spec_forge/config.yml +0 -19
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SpecForge
|
4
|
+
class Normalizer
|
5
|
+
class Configuration < Normalizer
|
6
|
+
STRUCTURE = {
|
7
|
+
base_url: SHARED_ATTRIBUTES[:base_url].except(:default), # Make it required
|
8
|
+
headers: SHARED_ATTRIBUTES[:headers],
|
9
|
+
query: SHARED_ATTRIBUTES[:query],
|
10
|
+
factories: {
|
11
|
+
type: Hash,
|
12
|
+
default: {},
|
13
|
+
structure: {
|
14
|
+
auto_discover: {
|
15
|
+
type: [TrueClass, FalseClass],
|
16
|
+
default: true
|
17
|
+
},
|
18
|
+
paths: {
|
19
|
+
type: Array,
|
20
|
+
default: []
|
21
|
+
}
|
22
|
+
}
|
23
|
+
},
|
24
|
+
on_debug: {
|
25
|
+
type: Proc
|
26
|
+
}
|
27
|
+
}.freeze
|
28
|
+
end
|
29
|
+
|
30
|
+
# On Normalizer
|
31
|
+
class << self
|
32
|
+
#
|
33
|
+
# Generates an empty configuration hash
|
34
|
+
#
|
35
|
+
# @return [Hash]
|
36
|
+
#
|
37
|
+
def default_configuration
|
38
|
+
Configuration.default
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Normalizes a configuration hash by standardizing its keys while ensuring the required data
|
43
|
+
# is provided or defaulted.
|
44
|
+
# Raises InvalidStructureError if anything is missing/invalid type
|
45
|
+
#
|
46
|
+
# @param input [Hash] The hash to normalize
|
47
|
+
#
|
48
|
+
# @return [Hash] A normalized hash as a new instance
|
49
|
+
#
|
50
|
+
def normalize_configuration!(input)
|
51
|
+
raise_errors! do
|
52
|
+
normalize_configuration(input)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Normalize a configuration hash
|
58
|
+
# Used internally by .normalize_configuration!, but is available for utility
|
59
|
+
#
|
60
|
+
# @param configuration [Hash] Configuration representation as a Hash
|
61
|
+
#
|
62
|
+
# @return [Array] Two item array
|
63
|
+
# First - The normalized hash
|
64
|
+
# Second - Array of errors, if any
|
65
|
+
#
|
66
|
+
# @private
|
67
|
+
#
|
68
|
+
def normalize_configuration(configuration)
|
69
|
+
if !Type.hash?(configuration)
|
70
|
+
raise InvalidTypeError.new(configuration, Hash, for: "configuration")
|
71
|
+
end
|
72
|
+
|
73
|
+
Normalizer::Configuration.new("configuration", configuration).normalize
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -11,6 +11,7 @@ module SpecForge
|
|
11
11
|
query: Normalizer::SHARED_ATTRIBUTES[:query],
|
12
12
|
body: Normalizer::SHARED_ATTRIBUTES[:body],
|
13
13
|
variables: Normalizer::SHARED_ATTRIBUTES[:variables],
|
14
|
+
debug: Normalizer::SHARED_ATTRIBUTES[:debug],
|
14
15
|
expectations: {type: Array}
|
15
16
|
}.freeze
|
16
17
|
end
|
@@ -34,6 +34,11 @@ module SpecForge
|
|
34
34
|
variables: {
|
35
35
|
type: Hash,
|
36
36
|
default: {}
|
37
|
+
},
|
38
|
+
debug: {
|
39
|
+
type: [TrueClass, FalseClass],
|
40
|
+
default: false,
|
41
|
+
aliases: %i[pry breakpoint]
|
37
42
|
}
|
38
43
|
}.freeze
|
39
44
|
|
@@ -103,10 +108,12 @@ module SpecForge
|
|
103
108
|
#
|
104
109
|
def default
|
105
110
|
structure.transform_values do |value|
|
106
|
-
if (
|
107
|
-
default.dup
|
111
|
+
if value.key?(:default)
|
112
|
+
value[:default].dup
|
108
113
|
elsif value[:type] == Integer # Can't call new on int
|
109
114
|
0
|
115
|
+
elsif value[:type] == Proc # Sameeee
|
116
|
+
-> {}
|
110
117
|
else
|
111
118
|
value[:type].new
|
112
119
|
end
|
@@ -172,12 +179,8 @@ module SpecForge
|
|
172
179
|
end
|
173
180
|
end
|
174
181
|
|
175
|
-
|
176
|
-
# These need to be required after the base class due to them requiring
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
require_relative "normalizer/expectation"
|
181
|
-
require_relative "normalizer/factory_reference"
|
182
|
-
require_relative "normalizer/factory"
|
183
|
-
require_relative "normalizer/spec"
|
182
|
+
####################################################################################################
|
183
|
+
# These need to be required after the base class due to them requiring constants on Normalizer
|
184
|
+
Dir[File.expand_path("normalizer/*.rb", __dir__)].sort.each do |path|
|
185
|
+
require path
|
186
|
+
end
|
data/lib/spec_forge/runner.rb
CHANGED
@@ -2,89 +2,115 @@
|
|
2
2
|
|
3
3
|
module SpecForge
|
4
4
|
class Runner
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
class << self
|
6
|
+
#
|
7
|
+
# Runs any specs
|
8
|
+
#
|
9
|
+
def run
|
10
|
+
RSpec::Core::Runner.disable_autorun!
|
11
|
+
RSpec::Core::Runner.run([], $stderr, $stdout)
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
#
|
15
|
+
# Defines a spec with RSpec
|
16
|
+
#
|
17
|
+
# @param spec_forge [Spec] The spec to define
|
18
|
+
#
|
19
|
+
def define_spec(spec_forge)
|
20
|
+
runner_forge = self
|
21
|
+
|
22
|
+
RSpec.describe(spec_forge.name) do
|
23
|
+
spec_forge.expectations.each do |expectation|
|
24
|
+
# Define the example group
|
25
|
+
describe(expectation.name) do
|
26
|
+
constraints = expectation.constraints
|
27
|
+
|
28
|
+
let!(:expected_status) { constraints.status.resolve }
|
29
|
+
let!(:expected_json) { constraints.json.resolve.deep_stringify_keys }
|
30
|
+
|
31
|
+
before do
|
32
|
+
# Ensure all variables are called and resolved, in case they are not referenced
|
33
|
+
expectation.variables.resolve
|
34
|
+
end
|
35
|
+
|
36
|
+
subject(:response) { expectation.http_client.call }
|
37
|
+
|
38
|
+
it do
|
39
|
+
if spec_forge.debug? || expectation.debug?
|
40
|
+
runner_forge.handle_debug(expectation, self)
|
41
|
+
end
|
21
42
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
describe(expectation_forge.name) do
|
33
|
-
runner_forge.define_variables(self, expectation_forge)
|
34
|
-
runner_forge.define_examples(self, expectation_forge)
|
43
|
+
# Status check
|
44
|
+
expect(response.status).to eq(expected_status)
|
45
|
+
|
46
|
+
# JSON check
|
47
|
+
if constraints.json.size > 0
|
48
|
+
expect(response.body).to be_kind_of(Hash)
|
49
|
+
expect(response.body).to include(expected_json)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
35
53
|
end
|
36
54
|
end
|
37
55
|
end
|
38
|
-
end
|
39
56
|
|
40
|
-
|
41
|
-
|
42
|
-
#
|
43
|
-
# @param context [RSpec::ExampleGroup] The rspec example group for this spec
|
44
|
-
# @param expectation [Expectation] The expectation that holds the variables
|
45
|
-
#
|
46
|
-
def define_variables(context, expectation)
|
47
|
-
expectation.variables.each do |variable_name, attribute|
|
48
|
-
context.let(variable_name, &attribute.to_proc)
|
57
|
+
def handle_debug(...)
|
58
|
+
DebugProxy.new(...).call
|
49
59
|
end
|
50
60
|
end
|
51
61
|
|
52
|
-
|
53
|
-
# Defines the expectation itself using the constraint
|
54
|
-
#
|
55
|
-
# @param context [RSpec::ExampleGroup] The RSpec example group for this spec
|
56
|
-
# @param expectation [Expectation] The expectation that holds the constraint
|
57
|
-
#
|
58
|
-
def define_examples(context, expectation)
|
59
|
-
context.instance_exec(expectation) do |expectation|
|
60
|
-
# Ensures the only one API call occurs per expectation
|
61
|
-
before(:all) { @response = expectation.http_client.call }
|
62
|
-
|
63
|
-
constraints = expectation.constraints.resolve
|
64
|
-
request = expectation.http_client.request
|
65
|
-
|
66
|
-
# Define the example group
|
67
|
-
context "#{request.http_method} #{request.url}" do
|
68
|
-
subject(:response) { @response }
|
69
|
-
|
70
|
-
# Status check
|
71
|
-
expected_status = constraints[:status]
|
72
|
-
it "expects the response to return a status code of #{expected_status}" do
|
73
|
-
expect(response.status).to eq(expected_status)
|
74
|
-
end
|
62
|
+
################################################################################################
|
75
63
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
expect(response.body).to be_kind_of(Hash)
|
81
|
-
end
|
64
|
+
class DebugProxy
|
65
|
+
def self.default
|
66
|
+
-> { puts inspect }
|
67
|
+
end
|
82
68
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
69
|
+
attr_reader :expectation, :variables, :expected_status, :expected_json, :request, :response
|
70
|
+
|
71
|
+
def initialize(expectation, spec_context)
|
72
|
+
@callback = SpecForge.configuration.on_debug
|
73
|
+
|
74
|
+
@expected_status = spec_context.expected_status
|
75
|
+
@expected_json = spec_context.expected_json
|
76
|
+
|
77
|
+
@request = expectation.http_client.request
|
78
|
+
@response = spec_context.response
|
79
|
+
|
80
|
+
@variables = expectation.variables
|
81
|
+
@expectation = expectation
|
82
|
+
end
|
83
|
+
|
84
|
+
def call
|
85
|
+
puts <<~STRING
|
86
|
+
|
87
|
+
Debug triggered for: #{expectation.name}
|
88
|
+
|
89
|
+
Available methods:
|
90
|
+
- expectation: Full expectation context
|
91
|
+
- variables: Current variable definitions
|
92
|
+
- expected_status: Expected HTTP status code (#{expected_status})
|
93
|
+
- expected_json: Expected response body
|
94
|
+
- request: HTTP request details (method, url, headers, body)
|
95
|
+
- response: HTTP response
|
96
|
+
|
97
|
+
Tip: Type 'self' for a JSON overview of the current state
|
98
|
+
Individual methods return full object details for advanced debugging
|
99
|
+
STRING
|
100
|
+
|
101
|
+
instance_exec(&@callback)
|
102
|
+
end
|
103
|
+
|
104
|
+
def inspect
|
105
|
+
hash = expectation.to_h
|
106
|
+
|
107
|
+
hash[:response] = {
|
108
|
+
headers: response.headers,
|
109
|
+
status: response.status,
|
110
|
+
body: response.body
|
111
|
+
}
|
112
|
+
|
113
|
+
JSON.pretty_generate(hash)
|
88
114
|
end
|
89
115
|
end
|
90
116
|
end
|
@@ -5,6 +5,8 @@ require_relative "expectation/constraint"
|
|
5
5
|
module SpecForge
|
6
6
|
class Spec
|
7
7
|
class Expectation
|
8
|
+
attr_predicate :debug
|
9
|
+
|
8
10
|
attr_reader :name, :variables, :constraints, :http_client
|
9
11
|
|
10
12
|
#
|
@@ -13,37 +15,54 @@ module SpecForge
|
|
13
15
|
# @param input [Hash] A hash containing the various attributes to control the expectation
|
14
16
|
# @param name [String] The name of the expectation
|
15
17
|
#
|
16
|
-
def initialize(
|
17
|
-
load_name(name, input)
|
18
|
-
|
18
|
+
def initialize(input, global_options: {})
|
19
19
|
# This allows defining spec level attributes that can be overwritten by the expectation
|
20
|
-
input = Attribute.from(overlay_options(global_options, input))
|
20
|
+
input = Attribute.from(Configuration.overlay_options(global_options, input))
|
21
21
|
|
22
|
+
load_debug(input)
|
22
23
|
load_variables(input)
|
23
24
|
|
24
25
|
# Must be after load_variables
|
25
26
|
load_constraints(input)
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
@http_client = HTTP::Client.new(
|
29
|
+
variables:, **input.except(:name, :variables, :expect, :debug)
|
30
|
+
)
|
31
|
+
|
32
|
+
# Must be after http_client
|
33
|
+
load_name(input)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_h
|
37
|
+
{
|
38
|
+
name:,
|
39
|
+
debug: debug?,
|
40
|
+
variables: variables.resolve,
|
41
|
+
request: http_client.request.to_h,
|
42
|
+
constraints: constraints.to_h
|
43
|
+
}
|
29
44
|
end
|
30
45
|
|
31
46
|
private
|
32
47
|
|
33
|
-
def
|
34
|
-
#
|
35
|
-
|
36
|
-
source.deep_merge(overlay)
|
37
|
-
end
|
48
|
+
def load_name(input)
|
49
|
+
# GET /users
|
50
|
+
@name = "#{http_client.request.http_verb.upcase} #{http_client.request.url}"
|
38
51
|
|
39
|
-
|
40
|
-
|
52
|
+
# GET /users - Returns a 404
|
53
|
+
if (name = input[:name].resolve.presence)
|
54
|
+
@name += " - #{name}"
|
55
|
+
end
|
41
56
|
end
|
42
57
|
|
43
58
|
def load_variables(input)
|
44
59
|
@variables = Attribute.bind_variables(input[:variables], input[:variables])
|
45
60
|
end
|
46
61
|
|
62
|
+
def load_debug(input)
|
63
|
+
@debug = input[:debug].resolve
|
64
|
+
end
|
65
|
+
|
47
66
|
def load_constraints(input)
|
48
67
|
constraints = Attribute.bind_variables(input[:expect], variables)
|
49
68
|
@constraints = Constraint.new(**constraints)
|
data/lib/spec_forge/spec.rb
CHANGED
@@ -5,13 +5,13 @@ require_relative "spec/expectation"
|
|
5
5
|
module SpecForge
|
6
6
|
class Spec
|
7
7
|
#
|
8
|
-
# Loads the specs from their yml files and
|
8
|
+
# Loads the specs from their yml files and defines them with the test runner
|
9
9
|
#
|
10
10
|
# @param path [String, Path] The base path where the specs directory is located
|
11
11
|
#
|
12
|
-
def self.
|
12
|
+
def self.load_and_define(base_path)
|
13
13
|
specs = load_from_path(base_path.join("specs", "**/*.yml"))
|
14
|
-
specs.each(&:
|
14
|
+
specs.each(&:define)
|
15
15
|
end
|
16
16
|
|
17
17
|
#
|
@@ -40,6 +40,8 @@ module SpecForge
|
|
40
40
|
|
41
41
|
############################################################################
|
42
42
|
|
43
|
+
attr_predicate :debug
|
44
|
+
|
43
45
|
attr_reader :name, :file_path, :expectations
|
44
46
|
|
45
47
|
#
|
@@ -55,23 +57,27 @@ module SpecForge
|
|
55
57
|
@file_path = file_path
|
56
58
|
|
57
59
|
input = Normalizer.normalize_spec!(input)
|
58
|
-
|
60
|
+
|
61
|
+
# Don't pass this down to the expectations
|
62
|
+
@debug = input.delete(:debug) || false
|
63
|
+
|
64
|
+
global_options = normalize_global_options(input)
|
59
65
|
|
60
66
|
@expectations =
|
61
67
|
input[:expectations].map.with_index do |expectation_input, index|
|
62
|
-
Expectation.new(
|
63
|
-
"expectations (item #{index})",
|
64
|
-
expectation_input,
|
65
|
-
global_options:
|
66
|
-
)
|
68
|
+
Expectation.new(expectation_input, global_options:)
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
72
|
+
def define
|
73
|
+
Runner.define_spec(self)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def normalize_global_options(input)
|
79
|
+
config = SpecForge.configuration.to_h.slice(:base_url, :headers, :query)
|
80
|
+
Configuration.overlay_options(config, input.except(:expectations))
|
75
81
|
end
|
76
82
|
end
|
77
83
|
end
|
data/lib/spec_forge/version.rb
CHANGED
data/lib/spec_forge.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "logger"
|
4
|
+
|
4
5
|
require "active_support"
|
5
6
|
require "active_support/core_ext"
|
6
7
|
require "commander"
|
@@ -17,8 +18,7 @@ require "yaml"
|
|
17
18
|
|
18
19
|
require_relative "spec_forge/attribute"
|
19
20
|
require_relative "spec_forge/cli"
|
20
|
-
require_relative "spec_forge/
|
21
|
-
require_relative "spec_forge/environment"
|
21
|
+
require_relative "spec_forge/configuration"
|
22
22
|
require_relative "spec_forge/error"
|
23
23
|
require_relative "spec_forge/factory"
|
24
24
|
require_relative "spec_forge/http"
|
@@ -35,10 +35,15 @@ module SpecForge
|
|
35
35
|
# @param path [String] The file path that contains factories and specs
|
36
36
|
#
|
37
37
|
def self.run(path = SpecForge.forge)
|
38
|
-
|
38
|
+
forge_helper = path.join("forge_helper.rb")
|
39
|
+
require_relative forge_helper if File.exist?(forge_helper)
|
40
|
+
|
41
|
+
configuration.validate
|
39
42
|
|
40
43
|
Factory.load_and_register(path)
|
41
|
-
Spec.
|
44
|
+
Spec.load_and_define(path)
|
45
|
+
|
46
|
+
Runner.run
|
42
47
|
end
|
43
48
|
|
44
49
|
#
|
@@ -60,12 +65,20 @@ module SpecForge
|
|
60
65
|
end
|
61
66
|
|
62
67
|
#
|
63
|
-
# Returns SpecForge's
|
68
|
+
# Returns SpecForge's configuration
|
64
69
|
#
|
65
70
|
# @return [Config]
|
66
71
|
#
|
67
|
-
def self.
|
68
|
-
@
|
72
|
+
def self.configuration
|
73
|
+
@configuration ||= Configuration.new
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Yields SpecForge's configuration to a block
|
78
|
+
#
|
79
|
+
def self.configure(&block)
|
80
|
+
block&.call(configuration)
|
81
|
+
configuration
|
69
82
|
end
|
70
83
|
|
71
84
|
#
|
@@ -83,8 +96,4 @@ module SpecForge
|
|
83
96
|
cleaner
|
84
97
|
end
|
85
98
|
end
|
86
|
-
|
87
|
-
def self.environment
|
88
|
-
@environment ||= Environment.new
|
89
|
-
end
|
90
99
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##########################################
|
4
|
+
# Framework Integration
|
5
|
+
##########################################
|
6
|
+
|
7
|
+
# Rails Integration
|
8
|
+
# require_relative "../config/environment"
|
9
|
+
|
10
|
+
# RSpec Integration (includes your existing configurations)
|
11
|
+
# require_relative "../spec/spec_helper"
|
12
|
+
|
13
|
+
# Custom requires (models, libraries, etc)
|
14
|
+
# Dir[File.join(__dir__, "..", "lib", "**", "*.rb")].sort.each { |f| require f }
|
15
|
+
|
16
|
+
##########################################
|
17
|
+
# Configuration
|
18
|
+
##########################################
|
19
|
+
|
20
|
+
SpecForge.configure do |config|
|
21
|
+
# Base configuration
|
22
|
+
config.base_url = "http://localhost:3000"
|
23
|
+
|
24
|
+
# Default request headers
|
25
|
+
config.headers = {
|
26
|
+
"Authorization" => "Bearer #{ENV.fetch("API_TOKEN", "")}"
|
27
|
+
}
|
28
|
+
|
29
|
+
# Optional: Default query parameters
|
30
|
+
# config.query = {api_key: ENV['API_KEY']}
|
31
|
+
|
32
|
+
# Factory configuration
|
33
|
+
# config.factories.auto_discover = false # Default: true
|
34
|
+
# config.factories.paths += ["lib/factories"] # Adds to default paths
|
35
|
+
|
36
|
+
# Debug configuration
|
37
|
+
# Available in specs with debug: true (aliases: breakpoint, pry)
|
38
|
+
# Defaults to printing state overview (-> { puts inspect })
|
39
|
+
# Available context: expectation, variables, request, response,
|
40
|
+
# expected_status, expected_json
|
41
|
+
# config.on_debug { binding.pry }
|
42
|
+
|
43
|
+
# Test Framework Configuration
|
44
|
+
# Useful for database cleaners, test data setup, etc
|
45
|
+
# config.specs.before(:suite) { }
|
46
|
+
# config.specs.around { |example| example.run }
|
47
|
+
# config.specs.formatter = :documentation
|
48
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
## Using Rails? Uncomment to load your app
|
4
|
+
# ENV["RAILS_ENV"] ||= "test"
|
5
|
+
# require_relative "../config/environment"
|
6
|
+
|
7
|
+
## Not using Rails? Load anything you need here
|
8
|
+
# Dir[SpecForge.root.join("lib", "my_api", "models", "**/*.rb")].sort.each { |path| require path }
|
9
|
+
|
10
|
+
## Using RSpec? Uncomment to use your existing configurations
|
11
|
+
# require_relative "../spec/spec_helper"
|
12
|
+
|
13
|
+
SpecForge.configure do |config|
|
14
|
+
## Base URL prefix for all API requests. All test paths will be appended to this URL
|
15
|
+
config.base_url = "http://localhost:3000"
|
16
|
+
|
17
|
+
## Default request headers - commonly used for authentication and content negotiation
|
18
|
+
api_token = ENV.fetch("API_TOKEN", "")
|
19
|
+
config.headers = {
|
20
|
+
"Authorization" => "Bearer #{api_token}"
|
21
|
+
}
|
22
|
+
|
23
|
+
## Default query parameters - useful for API keys or additional request context
|
24
|
+
# config.query = {api_token:}
|
25
|
+
|
26
|
+
## Factory configuration options
|
27
|
+
##
|
28
|
+
## Enable/disable automatic factory discovery. When enabled, SpecForge will automatically
|
29
|
+
## load factories from FactoryBot's default paths. Note: Factories defined in
|
30
|
+
## "spec_forge/factories" are always loaded regardless of this setting.
|
31
|
+
# config.factories.auto_discover = false # Default: true
|
32
|
+
|
33
|
+
##
|
34
|
+
## Additional paths, relative to the project folder, for discovering FactoryBot factories
|
35
|
+
## By default, FactoryBot looks in "spec/factories" and "test/factories"
|
36
|
+
# config.factories.paths += ["custom/factories/path"]
|
37
|
+
end
|
data/spec_forge/specs/users.yml
CHANGED
@@ -7,15 +7,17 @@ index_users:
|
|
7
7
|
show_user:
|
8
8
|
url: /users/{id}
|
9
9
|
expectations:
|
10
|
-
-
|
10
|
+
- query:
|
11
|
+
id: -1
|
12
|
+
expect:
|
11
13
|
status: 404
|
12
|
-
-
|
14
|
+
- query:
|
15
|
+
id: 1
|
16
|
+
expect:
|
13
17
|
status: 200
|
14
18
|
json:
|
15
19
|
name: kind_of.string
|
16
20
|
email: /\w+@example\.com/i
|
17
|
-
query:
|
18
|
-
id: 1
|
19
21
|
|
20
22
|
create_user:
|
21
23
|
url: /users
|