brine-dsl 0.11.0 → 0.12.0

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