fixturama 0.1.0 → 0.5.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 +99 -0
- data/README.md +76 -4
- data/lib/fixturama.rb +48 -20
- data/lib/fixturama/changes.rb +72 -0
- data/lib/fixturama/changes/base.rb +28 -0
- data/lib/fixturama/changes/chain.rb +64 -0
- data/lib/fixturama/changes/chain/actions.rb +31 -0
- data/lib/fixturama/changes/chain/arguments.rb +59 -0
- data/lib/fixturama/changes/chain/raise_action.rb +43 -0
- data/lib/fixturama/changes/chain/return_action.rb +33 -0
- data/lib/fixturama/changes/const.rb +30 -0
- data/lib/fixturama/changes/env.rb +35 -0
- data/lib/fixturama/changes/request.rb +91 -0
- data/lib/fixturama/changes/request/response.rb +62 -0
- data/lib/fixturama/changes/request/responses.rb +22 -0
- data/lib/fixturama/changes/seed.rb +39 -0
- data/lib/fixturama/config.rb +5 -0
- data/lib/fixturama/fixture_error.rb +31 -0
- data/lib/fixturama/loader.rb +6 -2
- data/lib/fixturama/loader/context.rb +14 -5
- data/lib/fixturama/loader/value.rb +1 -0
- data/lib/fixturama/version.rb +4 -0
- metadata +85 -73
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.rubocop.yml +0 -22
- data/.travis.yml +0 -17
- data/Gemfile +0 -9
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -6
- data/fixturama.gemspec +0 -24
- data/lib/fixturama/seed.rb +0 -15
- data/lib/fixturama/stubs.rb +0 -79
- data/lib/fixturama/stubs/chain.rb +0 -90
- data/lib/fixturama/stubs/chain/actions.rb +0 -39
- data/lib/fixturama/stubs/chain/actions/raise.rb +0 -21
- data/lib/fixturama/stubs/chain/actions/return.rb +0 -23
- data/lib/fixturama/stubs/chain/arguments.rb +0 -86
- data/lib/fixturama/stubs/const.rb +0 -43
- data/lib/fixturama/stubs/request.rb +0 -98
- data/lib/fixturama/stubs/request/response.rb +0 -43
- data/lib/fixturama/stubs/request/responses.rb +0 -20
- data/lib/fixturama/utils.rb +0 -39
- data/spec/fixturama/load_fixture/_spec.rb +0 -53
- data/spec/fixturama/load_fixture/data.json +0 -5
- data/spec/fixturama/load_fixture/data.yaml +0 -3
- data/spec/fixturama/load_fixture/data.yml +0 -3
- data/spec/fixturama/seed_fixture/_spec.rb +0 -36
- data/spec/fixturama/seed_fixture/seed.yml +0 -16
- data/spec/fixturama/stub_fixture/_spec.rb +0 -120
- data/spec/fixturama/stub_fixture/stub.yml +0 -73
- data/spec/spec_helper.rb +0 -26
@@ -0,0 +1,64 @@
|
|
1
|
+
class Fixturama::Changes
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Stub a chain of messages
|
5
|
+
#
|
6
|
+
class Chain < Base
|
7
|
+
require_relative "chain/raise_action"
|
8
|
+
require_relative "chain/return_action"
|
9
|
+
require_relative "chain/actions"
|
10
|
+
require_relative "chain/arguments"
|
11
|
+
|
12
|
+
def key
|
13
|
+
@key ||= ["chain", @receiver.name, *@messages].join(".")
|
14
|
+
end
|
15
|
+
|
16
|
+
def merge(other)
|
17
|
+
return self unless other.instance_of?(self.class) && other.key == key
|
18
|
+
|
19
|
+
tap { @arguments = (other.arguments | arguments).sort_by(&:order) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(example)
|
23
|
+
call_action = example.send(:receive_message_chain, *@messages) do |*real|
|
24
|
+
action = arguments.find { |expected| expected.match?(*real) }
|
25
|
+
action ? action.call : raise("Unexpected arguments: #{real}")
|
26
|
+
end
|
27
|
+
|
28
|
+
example.send(:allow, @receiver).to call_action
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
attr_reader :arguments
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def initialize(**options)
|
38
|
+
@receiver = receiver_from(options)
|
39
|
+
@messages = messages_from(options)
|
40
|
+
@arguments = [Arguments.new(options)]
|
41
|
+
end
|
42
|
+
|
43
|
+
def receiver_from(options)
|
44
|
+
case options.slice(:class, :object).keys
|
45
|
+
when %i[class] then Kernel.const_get(options[:class])
|
46
|
+
when %i[object] then Object.send(:eval, options[:object])
|
47
|
+
else raise
|
48
|
+
end
|
49
|
+
rescue StandardError => err
|
50
|
+
raise Fixturama::FixtureError.new("a stabbed object", options, err)
|
51
|
+
end
|
52
|
+
|
53
|
+
def messages_from(options)
|
54
|
+
case value = options[:chain]
|
55
|
+
when Array then value.map(&:to_sym)
|
56
|
+
when String then [value.to_sym]
|
57
|
+
when Symbol then value
|
58
|
+
else raise
|
59
|
+
end
|
60
|
+
rescue StandardError => err
|
61
|
+
raise Fixturama::FixtureError.new("a messages chain", options, err)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Fixturama::Changes::Chain
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Keep arguments of a message chain along with the corresponding actions
|
5
|
+
#
|
6
|
+
class Actions
|
7
|
+
def next
|
8
|
+
@list.count > 1 ? @list.pop : @list.first
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def initialize(*list)
|
14
|
+
list = [{ return: nil }] if list.empty?
|
15
|
+
|
16
|
+
@list = list.flatten.reverse.flat_map do |item|
|
17
|
+
action = build(item)
|
18
|
+
[action] * action.repeat
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def build(item)
|
23
|
+
item = Hash(item).transform_keys(&:to_sym)
|
24
|
+
case item.slice(:return, :raise).keys
|
25
|
+
when %i[return] then ReturnAction.new(item)
|
26
|
+
when %i[raise] then RaiseAction.new(item)
|
27
|
+
else raise Fixturama::FixtureError.new("an action", item)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Fixturama::Changes::Chain
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Keep a set of arguments along with the corresponding actions to be done
|
5
|
+
#
|
6
|
+
class Arguments
|
7
|
+
# @return [Array<Object>] the collection of arguments
|
8
|
+
attr_reader :arguments
|
9
|
+
|
10
|
+
# Order of comparing this set of arguments with the actual ones
|
11
|
+
# @return [Integer]
|
12
|
+
def order
|
13
|
+
-arguments.count
|
14
|
+
end
|
15
|
+
|
16
|
+
# Compare definitions by sets of arguments
|
17
|
+
# @param [Fixturama::Changes::Chain::Arguments] other
|
18
|
+
# @return [Boolean]
|
19
|
+
def ==(other)
|
20
|
+
other.arguments == arguments
|
21
|
+
end
|
22
|
+
|
23
|
+
# If actual arguments are covered by the current ones
|
24
|
+
def match?(*args, **opts)
|
25
|
+
return false if arguments.count > args.count + 1
|
26
|
+
|
27
|
+
arguments.first(args.count).zip(args).each do |(expected, actual)|
|
28
|
+
return false unless actual == expected
|
29
|
+
end
|
30
|
+
|
31
|
+
if arguments.count > args.count
|
32
|
+
Hash(arguments.last).transform_keys(&:to_sym).each do |k, v|
|
33
|
+
return false unless opts[k] == v
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
# Call the corresponding action if actual arguments are matched
|
41
|
+
def call
|
42
|
+
@actions.next.call
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def initialize(options)
|
48
|
+
@arguments = extract(options, :arguments)
|
49
|
+
@actions = Actions.new(*extract(options, :actions))
|
50
|
+
end
|
51
|
+
|
52
|
+
def extract(options, key)
|
53
|
+
return [] unless options.key?(key)
|
54
|
+
|
55
|
+
source = options[key]
|
56
|
+
source.is_a?(Array) ? source : [source]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Fixturama::Changes::Chain
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Raise a specified exception as a result of stubbing
|
5
|
+
#
|
6
|
+
class RaiseAction
|
7
|
+
attr_reader :repeat
|
8
|
+
|
9
|
+
def call
|
10
|
+
raise @error
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def initialize(**options)
|
16
|
+
@error = error_from options
|
17
|
+
@repeat = repeat_from options
|
18
|
+
rescue StandardError => err
|
19
|
+
raise Fixturama::FixtureError.new("an exception class", options, err)
|
20
|
+
end
|
21
|
+
|
22
|
+
def error_from(options)
|
23
|
+
klass = klass_from(options)
|
24
|
+
params = options[:arguments]
|
25
|
+
params.is_a?(Array) ? klass.new(*params) : klass
|
26
|
+
end
|
27
|
+
|
28
|
+
def klass_from(options)
|
29
|
+
klass = case value = options[:raise]
|
30
|
+
when NilClass, TrueClass, "true" then StandardError
|
31
|
+
when Class then value
|
32
|
+
else Kernel.const_get(value)
|
33
|
+
end
|
34
|
+
|
35
|
+
klass < Exception ? klass : raise("#{klass} is not an exception")
|
36
|
+
end
|
37
|
+
|
38
|
+
def repeat_from(options)
|
39
|
+
options.fetch(:repeat, 1).to_i.tap { |val| return val if val.positive? }
|
40
|
+
raise Fixturama::FixtureError.new("a number of repeats", options)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Fixturama::Changes::Chain
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Return a specified value as a result of stubbing
|
5
|
+
#
|
6
|
+
class ReturnAction
|
7
|
+
attr_reader :repeat
|
8
|
+
|
9
|
+
def call
|
10
|
+
@value
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def initialize(**options)
|
16
|
+
@value = value_from(options)
|
17
|
+
@repeat = repeat_from(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def value_from(options)
|
21
|
+
value = options[:return]
|
22
|
+
value.respond_to?(:dup) ? value.dup : value
|
23
|
+
rescue TypeError
|
24
|
+
# in the Ruby 2.3.0 Fixnum#dup is defined, but raises TypeError
|
25
|
+
value
|
26
|
+
end
|
27
|
+
|
28
|
+
def repeat_from(options)
|
29
|
+
options.fetch(:repeat, 1).to_i.tap { |val| return val if val.positive? }
|
30
|
+
raise Fixturama::FixtureError.new("a number of repeats", options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Fixturama::Changes
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Stub a constant
|
5
|
+
#
|
6
|
+
class Const < Base
|
7
|
+
def key
|
8
|
+
name
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(example)
|
12
|
+
example.send(:stub_const, name, value)
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def initialize(**options)
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def name
|
23
|
+
@options[:const]
|
24
|
+
end
|
25
|
+
|
26
|
+
def value
|
27
|
+
@options[:value]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Fixturama::Changes
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Stub an environment variable
|
5
|
+
#
|
6
|
+
class Env < Base
|
7
|
+
# All changes has the same +key+
|
8
|
+
# They will be merged before stubbing (see +call+)
|
9
|
+
def key
|
10
|
+
"ENV"
|
11
|
+
end
|
12
|
+
|
13
|
+
# When we merge 2 env-s, we just merge their options
|
14
|
+
def merge(other)
|
15
|
+
return self unless other.is_a?(self.class)
|
16
|
+
dup.tap { |env| env.options.update(other.options) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(example)
|
20
|
+
original = Hash ENV
|
21
|
+
example.send(:stub_const, "ENV", original.merge(options))
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
attr_reader :options
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def initialize(**options)
|
32
|
+
@options = { options[:env].to_s => options[:value].to_s }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Fixturama::Changes
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Stub an HTTP(S) request using +Webmock+
|
5
|
+
#
|
6
|
+
class Request < Base
|
7
|
+
require_relative "request/response"
|
8
|
+
require_relative "request/responses"
|
9
|
+
|
10
|
+
def call(example)
|
11
|
+
stub = example.stub_request(http_method, uri)
|
12
|
+
stub = stub.with(request) if request.any?
|
13
|
+
stub.to_return { |_| responses.next }
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :options
|
20
|
+
|
21
|
+
def initialize(options)
|
22
|
+
@options = options
|
23
|
+
with_error { @options = Hash(@options).transform_keys(&:to_sym) }
|
24
|
+
end
|
25
|
+
|
26
|
+
HTTP_METHODS = %i[get post put patch delete head options any].freeze
|
27
|
+
|
28
|
+
def http_method
|
29
|
+
with_error("http method") do
|
30
|
+
value = with_error("method") { options[:method]&.to_sym&.downcase }
|
31
|
+
value ||= :any
|
32
|
+
raise("Invalid HTTP method") unless HTTP_METHODS.include?(value)
|
33
|
+
value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def uri
|
38
|
+
with_error("uri") { maybe_regexp(options[:uri] || options[:url]) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def headers
|
42
|
+
with_error("headers") do
|
43
|
+
Hash(options[:headers]).transform_keys(&:to_s) if options.key? :headers
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def query
|
48
|
+
with_error("query") do
|
49
|
+
Hash(options[:query]).transform_keys(&:to_s) if options.key?(:query)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def body
|
54
|
+
with_error("body") { maybe_regexp options[:body] }
|
55
|
+
end
|
56
|
+
|
57
|
+
def basic_auth
|
58
|
+
with_error("basic auth") do
|
59
|
+
value = options[:auth] || options[:basic_auth]
|
60
|
+
Hash(value).transform_keys(&:to_s).values_at("user", "pass") if value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def request
|
65
|
+
@request ||= {
|
66
|
+
headers: headers,
|
67
|
+
body: body,
|
68
|
+
query: query,
|
69
|
+
basic_auth: basic_auth
|
70
|
+
}.select { |_, val| val }
|
71
|
+
end
|
72
|
+
|
73
|
+
def responses
|
74
|
+
@responses ||= Responses.new(options[:response] || options[:responses])
|
75
|
+
end
|
76
|
+
|
77
|
+
def with_error(part = nil)
|
78
|
+
yield
|
79
|
+
rescue StandardError => err
|
80
|
+
part = ["a valid request", part].compact.join(" ")
|
81
|
+
raise Fixturama::FixtureError.new(part, options, err)
|
82
|
+
end
|
83
|
+
|
84
|
+
def maybe_regexp(str)
|
85
|
+
return unless str
|
86
|
+
|
87
|
+
str = str.to_s
|
88
|
+
str[%r{\A/.*/\z}] ? Regexp.new(str[1..-2]) : str
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Fixturama::Changes::Request
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Store data for a response to the corresponding request
|
5
|
+
#
|
6
|
+
class Response
|
7
|
+
# @!attribute [r] repeat A number of times the response to be repeated
|
8
|
+
# @return [Integer]
|
9
|
+
attr_reader :repeat
|
10
|
+
|
11
|
+
# @!attribute [r] to_h A hash for the +to_respond(...)+ part of the stub
|
12
|
+
# @return [Hash<Symbol, Object>]
|
13
|
+
attr_reader :to_h
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def initialize(options)
|
18
|
+
@options = options
|
19
|
+
@options = with_error { Hash(options).transform_keys(&:to_sym) }
|
20
|
+
@to_h = build_hash
|
21
|
+
@repeat = build_repeat
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_repeat
|
25
|
+
with_error("number of repeats") do
|
26
|
+
value = @options.fetch(:repeat, 1).to_i
|
27
|
+
value.positive? ? value : raise("Wrong value")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_hash
|
32
|
+
{ status: status, body: body, headers: headers }.select { |_, v| v }
|
33
|
+
end
|
34
|
+
|
35
|
+
def status
|
36
|
+
with_error("status") { @options[:status]&.to_i } || 200
|
37
|
+
end
|
38
|
+
|
39
|
+
def body
|
40
|
+
with_error("body") do
|
41
|
+
case @options[:body]
|
42
|
+
when NilClass then nil
|
43
|
+
when Hash then JSON.dump(@options[:body])
|
44
|
+
else @options[:body].to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def headers
|
50
|
+
with_error("headers") do
|
51
|
+
Hash(@options[:headers]).map { |k, v| [k.to_s, v.to_s] }.to_h
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def with_error(part = nil)
|
56
|
+
yield
|
57
|
+
rescue StandardError => err
|
58
|
+
object = ["a response", part].compact.join(" ")
|
59
|
+
raise Fixturama::FixtureError.new(object, options, err)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|