fixturama 0.1.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|