brine-dsl 0.11.0 → 0.12.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.
@@ -1,35 +1,35 @@
1
1
  ##
2
2
  # @file coering.rb
3
- # Type coercion to support assertions.
3
+ # Coerce types to support assertions.
4
4
  ##
5
5
 
6
6
  module Brine
7
7
 
8
8
  ##
9
- # A module which allows mixing in Corcer behavior.
9
+ # Provide type coercion.
10
10
  ##
11
11
  module Coercing
12
12
 
13
13
  ##
14
- # Coerces the types of provided objects to support desired assertions.
14
+ # Coerces the types of provided objects to support assertion evaluation.
15
15
  #
16
16
  # Coercion is used to support handling richer data types in Brine without
17
17
  # introducing noisy type information to the language.
18
- # Argument Transforms can be used to convert input provided directly
18
+ # Transformation code can be used to convert input provided directly
19
19
  # to a Brine step, however data extracted from JSON will by default be
20
20
  # limited to the small amount of JSON supported data types. As normally
21
21
  # the data extracted from JSON will only be directly present on one side
22
22
  # of the assertion (most likely the left), the simpler JSON data types can
23
23
  # be coerced to a richer type based on the right hand side.
24
24
  #
25
- # A standard example (and that which is defined at the moment here) is date/time
25
+ # A standard example (and one which is supported in this file) is date/time
26
26
  # values. When testing an API that returns such values it is likely desirable to
27
27
  # perform assertions with proper date/time semantics, and the coercer allows
28
28
  # such assertions against a value retrieved out of a JSON structure.
29
29
  #
30
- # The combination of Argument Transforms and the Coercer can also effectively
31
- # allow for user defined types to seemlessly exist within the Brine tests.
32
- # The date/time implementation is an example of this.
30
+ # The combination of transforming of ParameterType inputs and the Coercer can
31
+ # also effectively allow for user defined types to seemlessly exist within the
32
+ # Brine tests.
33
33
  #
34
34
  # Implementation (Most of the non-implementation info should later be moved).
35
35
  # ---
@@ -62,9 +62,9 @@ module Brine
62
62
  #
63
63
  # Looks up and invokes the associated coercion function.
64
64
  #
65
- # @param [Object] first The first operand.
66
- # @param [Object] second The second operand.
67
- # @return [Array] A pair of the potentially coerced [first, second] values.
65
+ # @param first [Object] Provide the first operand.
66
+ # @param second [Object] Provide the second operand.
67
+ # @return [Array] Return the coerced [first, second] values.
68
68
  ##
69
69
  def coerce(first, second)
70
70
  @map[[first.class, second.class]].call(first, second)
@@ -73,11 +73,11 @@ module Brine
73
73
  end
74
74
 
75
75
  ##
76
- # The coercer instance mixed in as a property.
76
+ # Expose a Coercer as a property.
77
77
  #
78
- # Will instantiate a Coercer if needed on first access.
78
+ # A Coercer will be instantiated as needed on first access.
79
79
  #
80
- # @return [Coercer] The object which will be used to handle coercions.
80
+ # @return [Coercer] Return the object which will be used to handle coercions.
81
81
  ##
82
82
  def coercer
83
83
  @coercer ||= Coercer.new
@@ -86,7 +86,7 @@ module Brine
86
86
  end
87
87
 
88
88
  ##
89
- # Include the Coercing module functionality into the main Brine module.
89
+ # Mix the Coercing module functionality into the main Brine module.
90
90
  ##
91
91
  include Coercing
92
92
  end
@@ -1,6 +1,6 @@
1
1
  ##
2
2
  # @file hooks.rb
3
- # Cucumber hook callbacks used by Brine.
3
+ # Register Brine callbacks for Cucumber hooks.
4
4
  ##
5
5
 
6
6
  ##
@@ -1,19 +1,19 @@
1
1
  ##
2
2
  # @file mustache_expanding.rb
3
- # Support for expanding Mustache templates with a defined binding.
3
+ # Support expanding Mustache templates with a provided binding.
4
4
  ##
5
5
  module Brine
6
6
 
7
7
  ##
8
- # Provides a binding environment and template expansion that uses that environment.
8
+ # Provide a binding environment and template expansion that can use such an environment.
9
9
  ##
10
10
  module MustacheExpanding
11
11
  require 'mustache'
12
12
 
13
13
  ##
14
- # Mutable hash which serves as binding environment.
14
+ # Furnish a mutable hash to serve as a binding environment.
15
15
  #
16
- # @return [Hash<String, Object>] The active binding environment.
16
+ # @return [Hash<String, Object>] Return the active binding environment.
17
17
  ##
18
18
  def binding
19
19
  @binding ||= {}
@@ -22,8 +22,8 @@ module Brine
22
22
  ##
23
23
  # Assign `value' to `key' within binding,
24
24
  #
25
- # @param [String] key The key in the binding to which `value` will be assigned.
26
- # @param [Object] value The value to assign to ``key` within the binding.
25
+ # @param key [String] Specify the key in the binding to which `value` will be assigned.
26
+ # @param value [Object] Specify the value to assign to ``key` within the binding.
27
27
  ##
28
28
  def bind(key, value)
29
29
  STDERR.puts "Assigning #{value} to #{key}" if ENV['BRINE_LOG_BINDING']
@@ -31,32 +31,30 @@ module Brine
31
31
  end
32
32
 
33
33
  ##
34
- # Expanded Mustache template using binding environment.
34
+ # Provide an expandable Mustache template using binding environment.
35
35
  #
36
36
  # This exists to support latent expansion and template retrieval.
37
- #
38
- # @param [String] template Template content to expand with binding.
39
- # @return [String] The contents of `template` with any expansions done using `binding`.
40
37
  ##
41
38
  class BrineTemplate < Mustache
42
39
 
43
40
  ##
44
- # Instantiate a Brine template.
41
+ # Instantiate a BrineTemplate.
45
42
  #
46
- # @param tmpl [String] The string content of the template.
47
- # @param binding [Hash] A reference to the (mutable) binding.
43
+ # @param tmpl [String] Define the string content of the template.
48
44
  ##
49
- def initialize(tmpl, binding)
45
+ def initialize(tmpl)
50
46
  @template = tmpl
51
- @binding = binding
52
47
  end
53
48
 
54
49
  ##
55
- # Expand the template using the current bindings.
50
+ # Expand the template using the provided bindings.
51
+ #
52
+ # @param binding [Hash] Provide bound values to use within the template.
53
+ # @return [String] Return the contents of this template applied against `binding`.
56
54
  ##
57
- def expand
55
+ def expand(binding)
58
56
  begin
59
- context.push(@binding)
57
+ context.push(binding)
60
58
  render
61
59
  ensure
62
60
  context.pop
@@ -66,22 +64,55 @@ module Brine
66
64
  ##
67
65
  # Stringify as template contents.
68
66
  #
69
- # This supports cases such as dynamic steps
70
- # where a string is expected but the template should not yet
71
- # be expanded.
67
+ # This supports cases such as dynamic steps where a string is expected
68
+ # but the template should not yet be expanded.
72
69
  ##
73
70
  def to_s
74
71
  @template
75
72
  end
76
73
  end
77
74
 
75
+ ##
76
+ # Allow retrieval of BrineTemplates for provided template content.
77
+ #
78
+ # @param str [String] Define the string contents for the returned template.
79
+ # @return [BrineTemplate] Return a BrineTemplate with the provided content.
80
+ ##
78
81
  def as_template(str)
79
- BrineTemplate.new(str, binding)
82
+ BrineTemplate.new(str)
80
83
  end
81
84
  end
82
85
 
83
86
  ##
84
- # Include the MustacheExpanding module functionality into the main Brine module.
87
+ # Mix the MustacheExpanding module functionality into the main Brine module.
85
88
  ##
86
89
  include MustacheExpanding
87
90
  end
91
+
92
+ ##
93
+ # Assign the provided value to the specified identifier.
94
+ #
95
+ # @param name [Object] Specify the identifier to which the value will be bound.
96
+ # @param value [Object} Provide the value to bind to the identifier.
97
+ ##
98
+ When('{grave_param} is assigned {grave_param}') do |name, value|
99
+ perform { bind(expand(name, binding), value) }
100
+ end
101
+
102
+ ##
103
+ # Assign a random string (UUID) to the specified identifier.
104
+ #
105
+ # @param name [Object] Specify the identifier to which a random string will be bound.
106
+ ##
107
+ When('{grave_param} is assigned a random string') do |name|
108
+ perform { bind(expand(name, binding), SecureRandom.uuid) }
109
+ end
110
+
111
+ ##
112
+ # Assign a current timestamp to the specified identifier.
113
+ #
114
+ # @param name [Object] Specify the identifier to which the timestamp will be bound.
115
+ ##
116
+ When('{grave_param} is assigned a timestamp') do |name|
117
+ perform { bind(expand(name, binding), DateTime.now) }
118
+ end
@@ -1,27 +1,26 @@
1
1
  ##
2
2
  # @file performing.rb
3
- # Performing of of potentially deferred actions.
3
+ # Perform potentially deferred actions.
4
4
  ##
5
-
6
5
  module Brine
7
6
 
8
7
  ##
9
- # A module supporting either immediate or defered evaluation of logic.
8
+ # Support either immediate or defered evaluation of logic.
10
9
  ##
11
10
  module Performing
12
11
 
13
12
  ##
14
- # A passthrough performer which immediately invokes provided actions.
13
+ # Immediately invoke provided actions.
15
14
  #
16
15
  # This has no instance state and therefore a Flyweight could be used,
17
- # but too few instances are expected to warrant even the minor divergence.
16
+ # but too few instances are expected to warrant even the minor specialization.
18
17
  ##
19
18
  class ImmediatePerformer
20
19
 
21
20
  ##
22
21
  # Perform the provided actions immediately.
23
22
  #
24
- # @param [Proc] A thunk of actions to be performed.
23
+ # @param [Proc] Provide actions to be performed.
25
24
  ##
26
25
  def perform(actions)
27
26
  actions.call
@@ -29,7 +28,7 @@ module Brine
29
28
  end
30
29
 
31
30
  ##
32
- # A Peformer which collects rather than evaluating actions.
31
+ # Collect actions to be evaluated later.
33
32
  ##
34
33
  class CollectingPerformer
35
34
 
@@ -41,9 +40,9 @@ module Brine
41
40
  end
42
41
 
43
42
  ##
44
- # Collect provided actions for later evaluation.
43
+ # Collect provided actions.
45
44
  #
46
- # @param [Proc] A thunk of actions to be performed.
45
+ # @param actions [Proc] Provide actions to collect.
47
46
  ##
48
47
  def perform(actions)
49
48
  @actions << actions
@@ -59,11 +58,11 @@ module Brine
59
58
  end
60
59
 
61
60
  ##
62
- # The currently active Performer instance exposed as a property.
61
+ # Expose the currently active Performer as a property.
63
62
  #
64
63
  # The default implementation will be wired as needed upon first access.
65
64
  #
66
- # @return [Performer, #perform] The Performer to which actions will be sent.
65
+ # @return [Performer, #perform] Return the Performer to which actions will be sent.
67
66
  ##
68
67
  def performer
69
68
  @performer || reset_performer
@@ -72,16 +71,16 @@ module Brine
72
71
  ##
73
72
  # Reset the Performer instance to the default implementation.
74
73
  #
75
- # @return [Performer, #perform] The default implementation which will now be the `performer`.
74
+ # @return [Performer, #perform] Return the default implementation which will now be the active Performer.
76
75
  ##
77
76
  def reset_performer
78
77
  @performer = ImmediatePerformer.new
79
78
  end
80
79
 
81
80
  ##
82
- # Pass the actions to the current Performer instance.
81
+ # Pass the actions to the active Performer instance.
83
82
  #
84
- # @param [Proc] The thunk of the actions to be performed.
83
+ # @param actions [Proc] The actions to pass to the Performer.
85
84
  ##
86
85
  def perform(&actions)
87
86
  performer.perform(actions)
@@ -95,12 +94,12 @@ module Brine
95
94
  end
96
95
 
97
96
  ##
98
- # The number of seconds between polling attempts.
97
+ # Determine the number of seconds between polling attempts.
99
98
  #
100
- # Can be provided by the `BRINE_POLL_INTERVAL_SECONDS` environment variable.
101
- # Defaults to `0.5`.
99
+ # This can be provided by the `BRINE_POLL_INTERVAL_SECONDS` environment variable
100
+ # (defaults to `0.5`).
102
101
  #
103
- # @return [Number] The number of seconds to sleep between poll attempts.
102
+ # @return [Number] Return the number of seconds to sleep between poll attempts.
104
103
  ##
105
104
  def poll_interval_seconds
106
105
  ENV['BRINE_POLL_INTERVAL_SECONDS'] || 0.25
@@ -113,11 +112,11 @@ module Brine
113
112
  # will be returned, if any exception is thrown it will be
114
113
  # retried until the period elapses.
115
114
  #
116
- # @param [Number] seconds The period (in seconds) during which the block will be retried.
117
- # @param [Number] interval How long to sleep between polling attempts, defaults to `#poll_interval_seconds`.
118
- # @param [Block] The logic to retry within the defined period.
115
+ # @param seconds [Number] Define the period (in seconds) for which the block will be retried.
116
+ # @param interval [Number] Define how long to sleep between polling attempts (defaults to `#poll_interval_seconds`).
117
+ # @param [Block] Provide the logic to retry within the defined period.
119
118
  # @return [Object] The result of the block if successfully evaluated.
120
- # @throws [Exception] The most recent exception thrown from the block if never successfully evaluated.
119
+ # @throws [Exception] Re-throws the most recent exception thrown from the block if never successfully evaluated.
121
120
  ##
122
121
  def poll_for(seconds, interval=poll_interval_seconds)
123
122
  failure = nil
@@ -134,13 +133,13 @@ module Brine
134
133
  end
135
134
 
136
135
  ##
137
- # The duration in seconds for the given handle.
136
+ # Retrieve the duration in seconds for the given handle.
138
137
  #
139
138
  # Currently this only supports values provided through environment variables
140
139
  # of the format BRINE_DURATION_SECONDS_{handle}.
141
140
  #
142
- # @param[String] duration The handle/name of the duration whose length should be returned.
143
- # @return [Number] The number of seconds to poll for the requested duration.
141
+ # @param duration [String] Identify the duration whose length should be returned.
142
+ # @return [Number] Return the number of seconds defined for the requested duration.
144
143
  ##
145
144
  def retrieve_duration(duration)
146
145
  if ENV["BRINE_DURATION_SECONDS_#{duration}"]
@@ -157,3 +156,30 @@ module Brine
157
156
  ##
158
157
  include Performing
159
158
  end
159
+
160
+ require 'brine/selecting.rb'
161
+
162
+ ##
163
+ # Collect actions rather than immediately evaluating them.
164
+ ##
165
+ When('actions are defined such that') do
166
+ collect_actions
167
+ end
168
+
169
+ ##
170
+ # Evaluate collected actions for the provided period.
171
+ #
172
+ # This will pass if any evaluation is successful within the specified time.
173
+ #
174
+ # @param negated [Boolean] Specify whether the assertion should be expected to fail.
175
+ # @param period [Object] Specify the period length as returned by #retrieve_duration.
176
+ ##
177
+ Then('the actions are{maybe_not} successful within a {grave_param} period') do |negated, period|
178
+ method = negated ? :to : :to_not
179
+ expect do
180
+ poll_for(retrieve_duration(expand(period, binding))) do
181
+ performer.evaluate
182
+ end
183
+ end.send(method, raise_error)
184
+ reset_performer
185
+ end
@@ -1,19 +1,18 @@
1
1
  ##
2
2
  # @file requesting.rb
3
- # Request construction and response storage.
3
+ # Provide request construction and response storage.
4
4
  ##
5
-
6
5
  module Brine
7
6
 
8
7
  ##
9
- # Module in charge of constructing requests and saving responses.
8
+ # Support constructing requests and saving responses.
10
9
  ##
11
10
  module Requesting
12
11
  require 'faraday_middleware'
13
12
  require 'brine/client_building'
14
13
 
15
14
  ##
16
- # The root url to which Brine will send requests.
15
+ # Retrieve the root url to which Brine will send requests.
17
16
  #
18
17
  # This will normally be the value of ENV['BRINE_ROOT_URL'],
19
18
  # and that value should be directly usable after older
@@ -21,7 +20,7 @@ module Brine
21
20
  #
22
21
  # @brine_root_url is used if set to allow setting this value more programmatically.
23
22
  #
24
- # @return [String] The root URL to use or nil if none is provided.
23
+ # @return [String] Return the root URL to use or nil if none is provided.
25
24
  ##
26
25
  def brine_root_url
27
26
  if @brine_root_url
@@ -37,8 +36,8 @@ module Brine
37
36
  ##
38
37
  # Normalize an HTTP method for the HTTP client library (to a lowercased symbol).
39
38
  #
40
- # @param [String] method A text representation of the HTTP method.
41
- # @return [Symbol] A representation of `method` usable by the HTTP client library.
39
+ # @param method [String] Provide a text representation of the HTTP method.
40
+ # @return [Symbol] Return `method` in a form potentially usable by the HTTP client library.
42
41
  ##
43
42
  def parse_method(method)
44
43
  method.downcase.to_sym
@@ -49,19 +48,19 @@ module Brine
49
48
  #
50
49
  # This will generally be a connection as created by the ClientBuilding module.
51
50
  #
52
- # @param [Faraday::Connection, #run_request] client The client which will be used to issue HTTP requests.
51
+ # @param client [Faraday::Connection, #run_request] Provide the client which will be used to issue HTTP requests.
53
52
  ##
54
53
  def set_client(client)
55
54
  @client = client
56
55
  end
57
56
 
58
57
  ##
59
- # The currently active client which will be used to issue HTTP calls.
58
+ # Return the currently active client which will be used to issue HTTP calls.
60
59
  #
61
60
  # This will be initialized as neded on first access
62
61
  # to a default client constructed by the ClientBuilding module.
63
62
  #
64
- # @return [Faraday::Connection, #run_request] The currently active client object.
63
+ # @return [Faraday::Connection, #run_request] Return the active HTTP client.
65
64
  ##
66
65
  def client
67
66
  @client ||= client_for_host(brine_root_url)
@@ -82,7 +81,7 @@ module Brine
82
81
  #
83
82
  # This will override any previous body value.
84
83
  #
85
- # @param [Object] The new data to be placed in the request body.
84
+ # @param obj [Object] Provide the data to place in the request body.
86
85
  ##
87
86
  def set_request_body(obj)
88
87
  @body = obj
@@ -99,9 +98,8 @@ module Brine
99
98
  # methods will always be inclued in the request and therefore such data should
100
99
  # be cleared using `#reset_request` if it is not desired.
101
100
  #
102
- # @param [Symbol] method The client friendly representation of the HTTP method for the request
103
- # (@see #parse_method).
104
- # @param [String] url The url to which the request will be sent.
101
+ # @param method [Symbol] Provide the HTTP method for the request such as returned by #parse_method.
102
+ # @param url [String] Specify the url to which the request will be sent.
105
103
  ##
106
104
  def send_request(method, url)
107
105
  @response = client.run_request(method, url, @body, headers) do |req|
@@ -110,21 +108,21 @@ module Brine
110
108
  end
111
109
 
112
110
  ##
113
- # The response for the last sent request.
111
+ # Return the response for the last sent request.
114
112
  #
115
- # @return [Faraday::Response] The most recent response.
113
+ # @return [Faraday::Response] Return the most recent response.
116
114
  ##
117
115
  def response
118
116
  @response
119
117
  end
120
118
 
121
119
  ##
122
- # The headers for the request currently being built.
120
+ # Expose the headers for the request currently being built.
123
121
  #
124
- # Will be initialized as needed on first access,
122
+ # This will be initialized as needed on first access,
125
123
  # with a default specifying JSON content-type.
126
124
  #
127
- # @return [Hash] The headers to use for the constructed request.
125
+ # @return [Hash] Return the headers to use for the constructed request.
128
126
  ##
129
127
  def headers
130
128
  @headers ||= {content_type: 'application/json'}
@@ -133,8 +131,8 @@ module Brine
133
131
  ##
134
132
  # Set the specified header to the provided value.
135
133
  #
136
- # @param [String] k The name of the header whose value will be set.
137
- # @param [Object] v The value to set for the specified header.
134
+ # @param k [String] Specify the name of the header whose value will be set.
135
+ # @param v [Object] Provide the value to set for the specified header.
138
136
  # This should normally be a String, but some other types may work.
139
137
  ##
140
138
  def set_header(k, v)
@@ -142,11 +140,11 @@ module Brine
142
140
  end
143
141
 
144
142
  ##
145
- # The query parameters which will be attached to the constructeed request.
143
+ # Expose the query parameters which will be attached to the constructeed request.
146
144
  #
147
- # Will be initialized to an empty hash as needed upon first access.
145
+ # This will be initialized to an empty hash as needed upon first access.
148
146
  #
149
- # @return [Hash] The query parameters to use for request construction.
147
+ # @return [Hash] Return the query parameters to use for the constructed request.
150
148
  ##
151
149
  def request_params
152
150
  @request_params ||= {}
@@ -155,8 +153,8 @@ module Brine
155
153
  ##
156
154
  # Assign the provided value to the specified request query parameter.
157
155
  #
158
- # @param [String] k The name of the query parameter whose value is being assigned.
159
- # @param [Object] v The value to assign the query parameter (normally a String).
156
+ # @param k [String] Specify the name of the query parameter whose value is being assigned.
157
+ # @param v [Object] Provide the value to assign the query parameter (normally a String).
160
158
  ##
161
159
  def set_request_param(k, v)
162
160
  request_params[k] = v
@@ -169,3 +167,76 @@ module Brine
169
167
  ##
170
168
  include Requesting
171
169
  end
170
+
171
+ require 'brine/selecting'
172
+ require 'brine/transforming'
173
+
174
+ ##
175
+ # Define a body for the request currently under construction.
176
+ #
177
+ # @param input [String] Define the body contents to use in the request.
178
+ ##
179
+ When('the request body is assigned:') do |input|
180
+ perform { set_request_body(input) }
181
+ end
182
+
183
+ ##
184
+ # Define the indicated query parameter value for the request currently under construction.
185
+ #
186
+ # @param param [Object] Specify the name of the query parameter whose value will be set.
187
+ # @param value [Object] Specify the value to set for the named query parameter.
188
+ ##
189
+ When('the request query parameter {grave_param} is assigned {grave_param}') do |param, value|
190
+ perform { set_request_param(param, value) }
191
+ end
192
+
193
+ ##
194
+ # Define the indicated header value for the request currently under construction.
195
+ #
196
+ # @param header [Object] Specify the name of the header whose value will be set.
197
+ # @param value [Object] Specify the value to set for the named header.
198
+ ##
199
+ When('the request header {grave_param} is assigned {grave_param}') do |header, value|
200
+ perform { set_header(header, value) }
201
+ end
202
+
203
+ ##
204
+ # Issue a request with the provided method to the specified url.
205
+ #
206
+ # Bind the returned response.
207
+ #
208
+ # @param method [String] Specify the HTTP method to use for the request.
209
+ # @param url [Object] Specify the URL to which the request will be sent.
210
+ ##
211
+ When('a(n) {http_method} is sent to {grave_param}') do |method, url|
212
+ perform do
213
+ send_request(parse_method(method), URI.escape(expand(url, binding)))
214
+ bind('response', response)
215
+ reset_request
216
+ end
217
+ end
218
+
219
+ ##
220
+ # Attach a HTTP Basic Auth header to the request currently under construction.
221
+ #
222
+ # @param user [Object] Specify the user with which to generate the header.
223
+ # @param password [Object] Specify the password with which to generate the header.
224
+ ##
225
+ When('the request credentials are set for basic auth user {grave_param} and password {grave_param}') do |user, password|
226
+ perform do
227
+ base64_combined = Base64.strict_encode64("#{expand(user, binding)}:#{expand(password, binding)}")
228
+ set_header('Authorization', "Basic #{base64_combined}")
229
+ end
230
+ end
231
+
232
+ ##
233
+ # Assign a value extracted from a response attribute to the specified identifier.
234
+ #
235
+ # @param name [Object] Specify the identifier to which the value will be bound.
236
+ # @param attribute [String] Specify the attribute from which the value will be retrieved.
237
+ # @param traversal [Traversal] Provide a traversal to fetch the value out of the attribute.
238
+ ##
239
+ When('{grave_param} is assigned the response {response_attribute}{traversal}') do
240
+ |name, attribute, traversal|
241
+ perform { bind(expand(name, binding), traversal.visit(response_attribute(attribute))) }
242
+ end