mockserver-client 0.0.1
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 +15 -0
- data/.gitignore +23 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/README.md +200 -0
- data/Rakefile +11 -0
- data/lib/mockserver-client.rb +4 -0
- data/lib/mockserver/abstract_client.rb +112 -0
- data/lib/mockserver/mock_server_client.rb +50 -0
- data/lib/mockserver/model/array_of.rb +85 -0
- data/lib/mockserver/model/body.rb +56 -0
- data/lib/mockserver/model/delay.rb +34 -0
- data/lib/mockserver/model/enum.rb +47 -0
- data/lib/mockserver/model/expectation.rb +60 -0
- data/lib/mockserver/model/forward.rb +41 -0
- data/lib/mockserver/model/parameter.rb +46 -0
- data/lib/mockserver/model/request.rb +52 -0
- data/lib/mockserver/model/response.rb +39 -0
- data/lib/mockserver/model/times.rb +53 -0
- data/lib/mockserver/proxy_client.rb +9 -0
- data/lib/mockserver/utility_methods.rb +44 -0
- data/lib/mockserver/version.rb +5 -0
- data/mockserver-client.gemspec +31 -0
- data/spec/fixtures/forward_mockserver.json +7 -0
- data/spec/fixtures/incorrect_login_response.json +20 -0
- data/spec/fixtures/post_login_request.json +22 -0
- data/spec/fixtures/register_expectation.json +52 -0
- data/spec/fixtures/search_request.json +6 -0
- data/spec/fixtures/times_once.json +6 -0
- data/spec/mockserver/builder_spec.rb +83 -0
- data/spec/mockserver/mock_client_spec.rb +77 -0
- data/spec/mockserver/proxy_client_spec.rb +37 -0
- data/spec/spec_helper.rb +61 -0
- metadata +240 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
N2ZiZDY2MzcwMzQ5NGViNDc2YTQzYmY0NmYxMWI3ZjhkNGY5NzYyNQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OGVmMWM2ZWExZGVmZTk0YTU0MDI1MzU2ODM5YWNmMTNmYTY1M2NiMw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MzNkZDY5NWE2YTNlMGQ5YjQ1YWNiYjY0MGRlNTllYmZmMzRmN2YwNDYyOGM4
|
10
|
+
ODA2YzFiNzY0MGZjOWVlYTYyZGRhNzRmMzZlYmRkMzk4NjU3N2EyYzc5OGNk
|
11
|
+
NDBkMWFmMmRkNTk0YTI4NTFiOGM3MjMzMTc4NzQ4MTFiNzc2ZmQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MGIyZDEwOTUwMjllYTc4YTgzNWEyYTdlMjAzZDg4MWUyMmExMDA3Nzg3ZTI1
|
14
|
+
ODNlNmJmYzcxODI4NjVkZWMxOTgzZGQyNmMxOGRiZGQzYmEyZWMwYWNhYzIw
|
15
|
+
MTMyMTk3ZTk5M2MzOTcwNjg4ZjliOTFlZWQ2OWM4MmY5ZTQ1MjQ=
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
tmp.log
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Style/LineLength:
|
2
|
+
Max: 180
|
3
|
+
Style/ClassAndModuleChildren:
|
4
|
+
EnforcedStyle: compact
|
5
|
+
Style/CyclomaticComplexity:
|
6
|
+
Enabled: false
|
7
|
+
Lint/LiteralInInterpolation:
|
8
|
+
Enabled: false
|
9
|
+
Style/MethodLength:
|
10
|
+
Enabled: false
|
11
|
+
Style/FileName:
|
12
|
+
Exclude:
|
13
|
+
- '**/mockserver-client.rb'
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
# Mockserver Client
|
2
|
+
|
3
|
+
[](https://travis-ci.org/nayyara-samuel/mockserver-client)
|
4
|
+
|
5
|
+
A Ruby client for [MockServer](http://www.mock-server.com) project. This client follows the Java client's fluent style closely by using Ruby blocks.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'mockserver-client'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install mockserver-client
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
The usage notes here compare the Java syntax with the Ruby DSL for the various actions the MockServer client/proxy client supports. The Ruby code here assumes you have included `MockServer` and `MockServer::Model::DSL` modules. So these lines should typically be at the top of your code:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
include MockServer
|
27
|
+
include MockServer::Model::DSL
|
28
|
+
```
|
29
|
+
|
30
|
+
### Create Expectations
|
31
|
+
|
32
|
+
##### Java
|
33
|
+
```java
|
34
|
+
|
35
|
+
new MockServerClient("localhost", 9999)
|
36
|
+
.when(
|
37
|
+
request()
|
38
|
+
.withMethod("POST")
|
39
|
+
.withPath("/login")
|
40
|
+
.withQueryStringParameters(
|
41
|
+
new Parameter("returnUrl", "/account")
|
42
|
+
)
|
43
|
+
.withCookies(
|
44
|
+
new Cookie("sessionId", "2By8LOhBmaW5nZXJwcmludCIlMDAzMW")
|
45
|
+
)
|
46
|
+
.withBody("{username: 'foo', password: 'bar'}"),
|
47
|
+
Times.exactly(1)
|
48
|
+
)
|
49
|
+
.respond(
|
50
|
+
response()
|
51
|
+
.withStatusCode(401)
|
52
|
+
.withHeaders(
|
53
|
+
new Header("Content-Type", "application/json; charset=utf-8"),
|
54
|
+
new Header("Cache-Control", "public, max-age=86400")
|
55
|
+
)
|
56
|
+
.withBody("{ message: 'incorrect username and password combination' }")
|
57
|
+
.withDelay(new Delay(TimeUnit.SECONDS, 1))
|
58
|
+
);
|
59
|
+
```
|
60
|
+
|
61
|
+
##### Ruby
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
|
65
|
+
client = MockServerClient.new('localhost', 9999)
|
66
|
+
expectation = expectation do |expectation|
|
67
|
+
expectation.request do |request|
|
68
|
+
request.method = 'POST'
|
69
|
+
request.path = '/login'
|
70
|
+
request.query_parameters << parameter('returnUrl', '/account')
|
71
|
+
request.cookies = [cookie('sessionId', '2By8LOhBmaW5nZXJwcmludCIlMDAzMW')]
|
72
|
+
request.body = exact("{username: 'foo', password: 'bar'}")
|
73
|
+
end
|
74
|
+
|
75
|
+
expectation.response do |response|
|
76
|
+
response.status_code = 401
|
77
|
+
response.headers << header('Content-Type', 'application/json; charset=utf-8')
|
78
|
+
response.headers << header('Cache-Control', 'public, max-age=86400')
|
79
|
+
response.body = body("{ message: 'incorrect username and password combination' }")
|
80
|
+
response.delay = delay_by(:SECONDS, 1)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
client.register(expectation)
|
84
|
+
```
|
85
|
+
|
86
|
+
### Clear & Reset Server
|
87
|
+
|
88
|
+
##### Java
|
89
|
+
|
90
|
+
```java
|
91
|
+
mockServerClient.clear(
|
92
|
+
request()
|
93
|
+
.withMethod("POST")
|
94
|
+
.withPath("/login")
|
95
|
+
);
|
96
|
+
mockServerClient.reset();
|
97
|
+
```
|
98
|
+
|
99
|
+
##### Ruby
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
client.clear(request('POST', '/login'))
|
103
|
+
client.reset
|
104
|
+
```
|
105
|
+
### Verifying Behavior
|
106
|
+
|
107
|
+
##### Java
|
108
|
+
|
109
|
+
```java
|
110
|
+
new ProxyClient("localhost", 9090).verify(
|
111
|
+
request()
|
112
|
+
.withMethod("POST")
|
113
|
+
.withPath("/login")
|
114
|
+
.withBody(exact("{username: 'foo', password: 'bar'}"))
|
115
|
+
.withCookies(
|
116
|
+
new Cookie("sessionId", ".*")
|
117
|
+
),
|
118
|
+
Times.exactly(1)
|
119
|
+
);
|
120
|
+
```
|
121
|
+
|
122
|
+
##### Ruby
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
|
126
|
+
# Not providing times here because the default is exactly(1) i.e. the second argument to verify method
|
127
|
+
ProxyClient.new('localhost', 9090).verify(request(:POST, '/login') do |request|
|
128
|
+
request.body = exact("{username: 'foo', password: 'bar'}")
|
129
|
+
request.cookies = [cookie("sessionId", ".*")]
|
130
|
+
end)
|
131
|
+
```
|
132
|
+
|
133
|
+
|
134
|
+
### Analyzing Behavior
|
135
|
+
|
136
|
+
##### Java
|
137
|
+
|
138
|
+
```java
|
139
|
+
new ProxyClient("localhost", 9090).dumpToLogAsJava(
|
140
|
+
request()
|
141
|
+
.withMethod("POST")
|
142
|
+
.withPath("/login")
|
143
|
+
);
|
144
|
+
```
|
145
|
+
|
146
|
+
##### Ruby
|
147
|
+
```ruby
|
148
|
+
# Second argument is true to set output to Java; false to set output to default JSON (default)
|
149
|
+
ProxyClient.new('localhost', 9090).dump_log(request(:POST, '/login'), true)
|
150
|
+
```
|
151
|
+
|
152
|
+
## Complete Ruby DSL
|
153
|
+
The DSL is provided via the `MockServer::Model::DSL` module. Include this module in your code to make the DSL available.
|
154
|
+
|
155
|
+
Request
|
156
|
+
* **request**: Used to build a request object. If a block is passed, will configure request first and then return the configured request. Example: `request(:POST, '/login') {|r| r.headers << header("Content-Type", "application/json")}`.
|
157
|
+
* **http_request**: Alias for `request`.
|
158
|
+
|
159
|
+
Body
|
160
|
+
* **body**: Create a string body object (use in a response). Example: `body("unaccepted")`.
|
161
|
+
* **exact**: Create a body of type `EXACT`. Example: `exact('{"reason": "unauthorized"}')`.
|
162
|
+
* **regex**: Create a body of type `REGEX`. Example: `regex('username[a-z]{4}')`.
|
163
|
+
* **xpath**: Used to create a body of type `XPATH`. Example: `xpath("/element[key = 'some_key' and value = 'some_value']")`.
|
164
|
+
* **parameterized**; Create a body to type `PARAMETERS`. Example `parameterized(parameter('someValue', 1, 2), parameter('otherValue', 4, 5))`.
|
165
|
+
|
166
|
+
Parameters
|
167
|
+
* **parameter**: Create a generic parameter. Example: `parameter('key', 'value1' , 'value2')`.
|
168
|
+
* **cookie**: Create a cookie (same as `parameter` above but exists as syntactic sugar). Example: `cookie('sessionId', 'sessionid1ldj')`.
|
169
|
+
* **header**: Create a header (same as `parameter` above but exists as syntactic sugar). Example: `header('Content-Type', 'application/json')`.
|
170
|
+
|
171
|
+
Forward
|
172
|
+
* **forward**: Create a forwarding response. If a block is passed, will configure forward response first and then return the configured object. Example: `forward {|f| f.scheme = 'HTTPS' }`.
|
173
|
+
* **http_forward**: Alias for `forward`.
|
174
|
+
|
175
|
+
Response
|
176
|
+
* **response**: Create a response object. If a block is passed, will configure response first and then return the configured response. Example: `response {|r| r.status_code = 201 }`.
|
177
|
+
* **http_response**: Alias for `response`.
|
178
|
+
|
179
|
+
Delay
|
180
|
+
* **delay_by**. Create a delay object in a response. Example : `delay_by(:MICROSECONDS, 20)`.
|
181
|
+
|
182
|
+
Times (used in Expectation)
|
183
|
+
* **times**: Create an 'times' object. If a block is passed, will configure the object first before returning it. Example: `times {|t| t.unlimited = false }`.
|
184
|
+
* **unlimited**: Create an object with unlimited repeats. Example: `unlimited()`. (No parameters).
|
185
|
+
* **once**. Create an object that repeats only once. Example: `once()`. (No parameters).
|
186
|
+
* **exactly**. Create an object that repeats exactly the number of times specified. Example: `exactly(2)`.
|
187
|
+
* **at_least**: Create an object that repeats at least the given number of times. (Use in verify). Example: `at_least(2)`.
|
188
|
+
|
189
|
+
Expectation (use in register)
|
190
|
+
* **expectation**: Create an expectation object. If a block is passed, will configure the object first before returning it. Example: `expectation {|e| e.request {|r| r.path = "index.html} }`.
|
191
|
+
Getter methods for `request`, `response` and `forward` methods will optionally accept a block. If block is passed object is configured before it is returned. The attribute `times` has conventional getter and setter methods.
|
192
|
+
|
193
|
+
## Contributing
|
194
|
+
|
195
|
+
1. Fork it ( https://github.com/[my-github-username]/mockserver-client/fork )
|
196
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
197
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
198
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
199
|
+
5. Create a new Pull Request
|
200
|
+
6. **IMPORTANT**: Keep code coverage high. Preferably above 95%. This project uses SimpleCov for code coverage which reports percentage coverage.
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
require 'rspec/mocks/version'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
RuboCop::RakeTask.new(:rubocop)
|
9
|
+
|
10
|
+
desc 'Main task for this project to ensure the project passes build'
|
11
|
+
task default: [:rubocop, :spec, :build]
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rest-client'
|
3
|
+
require 'logging_factory'
|
4
|
+
require_relative './model/times'
|
5
|
+
require_relative './model/request'
|
6
|
+
|
7
|
+
# An abstract client for making requests supported by mock and proxy client.
|
8
|
+
# @author Nayyara Samuel(mailto: nayyara.samuel@opower.com)
|
9
|
+
#
|
10
|
+
module MockServer
|
11
|
+
HTTP_REQUEST = 'httpRequest'
|
12
|
+
HTTP_RESPONSE = 'httpResponse'
|
13
|
+
HTTP_FORWARD = 'httpForward'
|
14
|
+
HTTP_TIMES = 'times'
|
15
|
+
|
16
|
+
RESET_ENDPOINT = '/reset'
|
17
|
+
CLEAR_ENDPOINT = '/clear'
|
18
|
+
RETRIEVE_ENDPOINT = '/retrieve'
|
19
|
+
DUMP_LOG_ENDPOINT = '/dumpToLog'
|
20
|
+
|
21
|
+
# An abstract client for making requests supported by mock and proxy client.
|
22
|
+
class AbstractClient
|
23
|
+
include Model::DSL
|
24
|
+
include Model
|
25
|
+
|
26
|
+
attr_accessor :logger
|
27
|
+
|
28
|
+
def initialize(host, port)
|
29
|
+
fail 'Cannot instantiate AbstractClient class. You must subclass it.' if self.class == AbstractClient
|
30
|
+
fail 'Host/port must not be nil' unless host && port
|
31
|
+
@base = RestClient::Resource.new("http://#{host}:#{port}")
|
32
|
+
@logger = ::LoggingFactory::DEFAULT_FACTORY.log(self.class)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Clear all expectations with the given request
|
36
|
+
# @param request [Request] the request to use to clear an expectation
|
37
|
+
# @return [Object] the response from the clear action
|
38
|
+
def clear(request)
|
39
|
+
request = prepare_hash(HTTP_REQUEST => Request.new(request))
|
40
|
+
url = CLEAR_ENDPOINT
|
41
|
+
|
42
|
+
logger.debug("Clearing expectation with request: #{request}")
|
43
|
+
logger.debug("URL: #{url}. Payload: #{request.to_hash}")
|
44
|
+
|
45
|
+
response = @base[url].put(request.to_json, content_type: :json)
|
46
|
+
logger.debug("Got clear response: #{response.code}")
|
47
|
+
parse_response(response)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Reset the mock server clearing all expectations previously registered
|
51
|
+
# @return [Object] the response from the reset action
|
52
|
+
def reset
|
53
|
+
request = {}
|
54
|
+
url = RESET_ENDPOINT
|
55
|
+
|
56
|
+
logger.debug('Resetting mockserver')
|
57
|
+
logger.debug("URL: #{url}. Payload: #{request.to_hash}")
|
58
|
+
|
59
|
+
response = @base[url].put(request.to_json)
|
60
|
+
logger.debug("Got reset response: #{response.code}")
|
61
|
+
parse_response(response)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Retrieve the list of requests that have been processed by the server
|
65
|
+
# @param request [Request] to filter requests
|
66
|
+
# @return [Object] the list of responses processed by the server
|
67
|
+
def retrieve(request = nil)
|
68
|
+
request = request ? prepare_hash(HTTP_REQUEST => Request.new(request)) : {}
|
69
|
+
url = RETRIEVE_ENDPOINT
|
70
|
+
|
71
|
+
logger.debug('Retrieving request list from mockserver')
|
72
|
+
logger.debug("URL: #{url}. Payload: #{request.to_hash}")
|
73
|
+
|
74
|
+
response = @base[RETRIEVE_ENDPOINT].put(request.to_json)
|
75
|
+
logger.debug("Got retrieve response: #{response.code}")
|
76
|
+
parse_response(response)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Request to dump logs to file
|
80
|
+
# @param java [Boolean] true to dump as Java code; false to dump as JSON
|
81
|
+
# @return [Object] the list of responses processed by the server
|
82
|
+
def dump_log(request = nil, java = false)
|
83
|
+
type_params = java ? '?type=java' : ''
|
84
|
+
url = "#{DUMP_LOG_ENDPOINT}#{type_params}"
|
85
|
+
request = Request.new(request) || {}
|
86
|
+
|
87
|
+
logger.debug('Sending dump log request to mockserver')
|
88
|
+
logger.debug("URL: #{url}. Payload: #{request.to_hash}")
|
89
|
+
|
90
|
+
response = @base[url].put(request.to_json)
|
91
|
+
logger.debug("Got dump to log response: #{response.code}")
|
92
|
+
parse_response(response)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Verify that the given request is called the number of times expected
|
96
|
+
# @param request [Request] to filter requests
|
97
|
+
# @param times [Times] expected number of times
|
98
|
+
# @return [Object] the list of responses processed by the server that match the request
|
99
|
+
def verify(request, times = exactly(1))
|
100
|
+
logger.debug('Sending query for verify to mockserver')
|
101
|
+
results = retrieve(request)
|
102
|
+
|
103
|
+
# Reusing the times model here so interpreting values here
|
104
|
+
num_times = times.remaining_times
|
105
|
+
is_exact = !times.unlimited
|
106
|
+
|
107
|
+
fulfilled = is_exact ? (num_times == results.size) : (num_times <= results.size)
|
108
|
+
fail "Expected request to be present: [#{num_times}] (#{is_exact ? 'exactly' : 'at least'}). But found: [#{results.size}]" unless fulfilled
|
109
|
+
results
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative './model/expectation'
|
3
|
+
require_relative './abstract_client'
|
4
|
+
require_relative './utility_methods'
|
5
|
+
|
6
|
+
#
|
7
|
+
# The client used to interact with the mock server.
|
8
|
+
# @author:: Nayyara Samuel (mailto: nayyara.samuel@opower.com)
|
9
|
+
#
|
10
|
+
module MockServer
|
11
|
+
EXPECTATION_ENDPOINT = '/expectation'
|
12
|
+
|
13
|
+
# The client used to interact with the mock server.
|
14
|
+
class MockServerClient < AbstractClient
|
15
|
+
include Model
|
16
|
+
include UtilityMethods
|
17
|
+
|
18
|
+
# Registers an expectation with the mockserver
|
19
|
+
# @param expectation [Expectation] the expectation to create the request from
|
20
|
+
# @return [Object] the response from the register action
|
21
|
+
def register(expectation)
|
22
|
+
fail 'Expectation passed in is not valid type' unless expectation.is_a?(Expectation)
|
23
|
+
url = EXPECTATION_ENDPOINT
|
24
|
+
request = create_expectation_request(expectation)
|
25
|
+
|
26
|
+
logger.debug('Registering new expectation')
|
27
|
+
logger.debug("URL: #{url} Payload: #{request.to_hash}")
|
28
|
+
|
29
|
+
response = @base[url].put(request.to_json, content_type: :json)
|
30
|
+
logger.debug("Got register response: #{response.code}")
|
31
|
+
parse_response(response)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Create an expecation request to send to the expectation endpoint of
|
37
|
+
# @param expectation [Expectation] the expectation to create the request from
|
38
|
+
# @return [Hash] a hash representing the request to use in registering an expectation with the mock server
|
39
|
+
def create_expectation_request(expectation)
|
40
|
+
expectation_request = prepare_hash(HTTP_REQUEST => expectation.request,
|
41
|
+
HTTP_RESPONSE => expectation.response,
|
42
|
+
HTTP_FORWARD => expectation.forward,
|
43
|
+
HTTP_TIMES => expectation.times)
|
44
|
+
|
45
|
+
logger.debug("Expectation JSON: #{expectation_request.to_json}")
|
46
|
+
fail "You can only set either of #{[HTTP_RESPONSE, HTTP_FORWARD]}. But not both" if expectation_request[HTTP_RESPONSE] && expectation_request[HTTP_FORWARD]
|
47
|
+
expectation_request
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|