flipp-mockserver-client 0.1.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 +7 -0
- data/.gitignore +21 -0
- data/.rubocop.yml +7 -0
- data/Gemfile +4 -0
- data/README.md +182 -0
- data/Rakefile +10 -0
- data/bin/mockserver +9 -0
- data/lib/cli.rb +146 -0
- data/lib/mockserver-client.rb +17 -0
- data/lib/mockserver/abstract_client.rb +111 -0
- data/lib/mockserver/mock_server_client.rb +59 -0
- data/lib/mockserver/model/array_of.rb +85 -0
- data/lib/mockserver/model/body.rb +56 -0
- data/lib/mockserver/model/cookie.rb +36 -0
- data/lib/mockserver/model/delay.rb +34 -0
- data/lib/mockserver/model/enum.rb +47 -0
- data/lib/mockserver/model/expectation.rb +158 -0
- data/lib/mockserver/model/forward.rb +41 -0
- data/lib/mockserver/model/header.rb +43 -0
- data/lib/mockserver/model/parameter.rb +43 -0
- data/lib/mockserver/model/request.rb +77 -0
- data/lib/mockserver/model/response.rb +45 -0
- data/lib/mockserver/model/times.rb +61 -0
- data/lib/mockserver/proxy_client.rb +9 -0
- data/lib/mockserver/utility_methods.rb +59 -0
- data/lib/mockserver/version.rb +5 -0
- data/mockserver-client.gemspec +36 -0
- data/pom.xml +116 -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 +50 -0
- data/spec/fixtures/retrieved_request.json +22 -0
- data/spec/fixtures/search_request.json +6 -0
- data/spec/fixtures/times_once.json +6 -0
- data/spec/integration/mock_client_integration_spec.rb +82 -0
- data/spec/mockserver/builder_spec.rb +136 -0
- data/spec/mockserver/mock_client_spec.rb +80 -0
- data/spec/mockserver/proxy_client_spec.rb +38 -0
- data/spec/spec_helper.rb +61 -0
- metadata +293 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"httpResponse": {
|
3
|
+
"statusCode": 401,
|
4
|
+
"headers": [
|
5
|
+
{
|
6
|
+
"name": "Content-Type",
|
7
|
+
"values": ["application/json; charset=utf-8"]
|
8
|
+
},
|
9
|
+
{
|
10
|
+
"name": "Cache-Control",
|
11
|
+
"values": ["public, max-age=86400"]
|
12
|
+
}
|
13
|
+
],
|
14
|
+
"body": "incorrect username and password combination",
|
15
|
+
"delay": {
|
16
|
+
"timeUnit": "SECONDS",
|
17
|
+
"value": 1
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"httpRequest": {
|
3
|
+
"method": "POST",
|
4
|
+
"path": "/login",
|
5
|
+
"queryStringParameters": [
|
6
|
+
{
|
7
|
+
"name": "returnUrl",
|
8
|
+
"values": ["/account"]
|
9
|
+
}
|
10
|
+
],
|
11
|
+
"cookies": [
|
12
|
+
{
|
13
|
+
"name": "sessionId",
|
14
|
+
"value": "2By8LOhBmaW5nZXJwcmludCIlMDAzMW"
|
15
|
+
}
|
16
|
+
],
|
17
|
+
"body": {
|
18
|
+
"type": "STRING",
|
19
|
+
"value": "{username:'foo', password:'bar'}"
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
{
|
2
|
+
"httpRequest": {
|
3
|
+
"method": "POST",
|
4
|
+
"path": "/login",
|
5
|
+
"queryStringParameters": [
|
6
|
+
{
|
7
|
+
"values": [
|
8
|
+
"/account"
|
9
|
+
],
|
10
|
+
"name": "returnUrl"
|
11
|
+
}
|
12
|
+
],
|
13
|
+
"cookies": [
|
14
|
+
{
|
15
|
+
"name": "sessionId",
|
16
|
+
"value": "2By8LOhBmaW5nZXJwcmludCIlMDAzMW"
|
17
|
+
}
|
18
|
+
],
|
19
|
+
"body": {
|
20
|
+
"type": "STRING",
|
21
|
+
"value": "{\"username\":\"foo\",\"password\":\"bar\"}"
|
22
|
+
}
|
23
|
+
},
|
24
|
+
"httpResponse": {
|
25
|
+
"statusCode": 401,
|
26
|
+
"headers": [
|
27
|
+
{
|
28
|
+
"values": [
|
29
|
+
"application/json; charset=utf-8"
|
30
|
+
],
|
31
|
+
"name": "Content-Type"
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"values": [
|
35
|
+
"public, max-age=86400"
|
36
|
+
],
|
37
|
+
"name": "Cache-Control"
|
38
|
+
}
|
39
|
+
],
|
40
|
+
"body": "{\"message\":\"incorrect username and password combination\"}",
|
41
|
+
"delay": {
|
42
|
+
"timeUnit": "SECONDS",
|
43
|
+
"value": 1
|
44
|
+
}
|
45
|
+
},
|
46
|
+
"times": {
|
47
|
+
"remainingTimes": 2,
|
48
|
+
"unlimited": "false"
|
49
|
+
}
|
50
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"method": "POST",
|
3
|
+
"path": "/login",
|
4
|
+
"queryStringParameters": [
|
5
|
+
{
|
6
|
+
"values": [
|
7
|
+
"/account"
|
8
|
+
],
|
9
|
+
"name": "returnUrl"
|
10
|
+
}
|
11
|
+
],
|
12
|
+
"cookies": [
|
13
|
+
{
|
14
|
+
"value": "2By8LOhBmaW5nZXJwcmludCIlMDAzMW",
|
15
|
+
"name": "sessionId"
|
16
|
+
}
|
17
|
+
],
|
18
|
+
"body": {
|
19
|
+
"type": "STRING",
|
20
|
+
"value": "{\"username\":\"foo\",\"password\":\"bar\"}"
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rspec'
|
3
|
+
require 'net/http'
|
4
|
+
require 'webmock/rspec'
|
5
|
+
require_relative '../../lib/mockserver-client'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
include WebMock::API
|
9
|
+
include MockServer
|
10
|
+
include MockServer::UtilityMethods
|
11
|
+
include MockServer::Model::DSL
|
12
|
+
|
13
|
+
# Only accept expect syntax do not allow old should syntax
|
14
|
+
config.expect_with :rspec do |c|
|
15
|
+
c.syntax = :expect
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe MockServer::MockServerClient do
|
20
|
+
|
21
|
+
let(:client) { MockServer::MockServerClient.new('localhost', 8098) }
|
22
|
+
|
23
|
+
before do
|
24
|
+
# To suppress logging output to standard output, write to a temporary file
|
25
|
+
client.logger = LoggingFactory::DEFAULT_FACTORY.log('test', output: 'tmp.log', truncate: true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_agent
|
29
|
+
uri = URI('http://api.nsa.gov:1337/agent')
|
30
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
31
|
+
req = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
|
32
|
+
req.body = { name: 'John Doe', role: 'agent' }.to_json
|
33
|
+
res = http.request(req)
|
34
|
+
puts "response #{res.body}"
|
35
|
+
rescue => e
|
36
|
+
puts "failed #{e}"
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'setup complex expectation' do
|
40
|
+
WebMock.allow_net_connect!
|
41
|
+
|
42
|
+
# given
|
43
|
+
mock_expectation = expectation do |expectation|
|
44
|
+
expectation.request do |request|
|
45
|
+
request.method = 'POST'
|
46
|
+
request.path = '/somePath'
|
47
|
+
request.query_string_parameters << parameter('param', 'someQueryStringValue')
|
48
|
+
request.headers << header('Header', 'someHeaderValue')
|
49
|
+
request.cookies << cookie('cookie', 'someCookieValue')
|
50
|
+
request.body = exact('someBody')
|
51
|
+
end
|
52
|
+
|
53
|
+
expectation.response do |response|
|
54
|
+
response.status_code = 201
|
55
|
+
response.headers << header('header', 'someHeaderValue')
|
56
|
+
response.body = body('someBody')
|
57
|
+
response.delay = delay_by(:SECONDS, 1)
|
58
|
+
end
|
59
|
+
|
60
|
+
expectation.times = exactly(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
# and
|
64
|
+
expect(client.register(mock_expectation).code).to eq(201)
|
65
|
+
|
66
|
+
# when
|
67
|
+
uri = URI('http://localhost:8098/somePath')
|
68
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
69
|
+
req = Net::HTTP::Post.new('/somePath?param=someQueryStringValue')
|
70
|
+
req['Header'] = 'someHeaderValue'
|
71
|
+
req['Cookie'] = 'cookie=someCookieValue'
|
72
|
+
req.body = 'someBody'
|
73
|
+
res = http.request(req)
|
74
|
+
|
75
|
+
# then
|
76
|
+
expect(res.code).to eq('201')
|
77
|
+
expect(res.body).to eq('someBody')
|
78
|
+
|
79
|
+
WebMock.disable_net_connect!
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative '../spec_helper'
|
3
|
+
|
4
|
+
describe MockServer::Model::DSL do
|
5
|
+
|
6
|
+
it 'generates http requests correctly' do
|
7
|
+
mock_request = http_request(:POST, '/login')
|
8
|
+
mock_request.query_string_parameters = [parameter('returnUrl', '/account')]
|
9
|
+
mock_request.cookies = [cookie('sessionId', '2By8LOhBmaW5nZXJwcmludCIlMDAzMW')]
|
10
|
+
mock_request.body = exact("{username:'foo', password:'bar'}")
|
11
|
+
|
12
|
+
expect(to_camelized_hash(HTTP_REQUEST => mock_request)).to eq(FIXTURES.read('post_login_request.json'))
|
13
|
+
|
14
|
+
# Block style
|
15
|
+
mock_request = request(:POST, '/login') do |request|
|
16
|
+
request.query_string_parameters = [parameter('returnUrl', '/account')]
|
17
|
+
request.cookies = [cookie('sessionId', '2By8LOhBmaW5nZXJwcmludCIlMDAzMW')]
|
18
|
+
request.body = exact("{username:'foo', password:'bar'}")
|
19
|
+
end
|
20
|
+
|
21
|
+
expect(to_camelized_hash(HTTP_REQUEST => mock_request)).to eq(FIXTURES.read('post_login_request.json'))
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'generates http responses correctly' do
|
25
|
+
mock_response = http_response
|
26
|
+
mock_response.status_code = 401
|
27
|
+
mock_response.headers = [header('Content-Type', 'application/json; charset=utf-8')]
|
28
|
+
mock_response.headers << header('Cache-Control', 'public, max-age=86400')
|
29
|
+
mock_response.body = body('incorrect username and password combination')
|
30
|
+
mock_response.delay = delay_by(:SECONDS, 1)
|
31
|
+
|
32
|
+
expect(to_camelized_hash(HTTP_RESPONSE => mock_response)).to eq(FIXTURES.read('incorrect_login_response.json'))
|
33
|
+
|
34
|
+
# Block style
|
35
|
+
mock_response = response do |response|
|
36
|
+
response.status_code = 401
|
37
|
+
response.headers = [header('Content-Type', 'application/json; charset=utf-8')]
|
38
|
+
response.headers << header('Cache-Control', 'public, max-age=86400')
|
39
|
+
response.body = body('incorrect username and password combination')
|
40
|
+
response.delay = delay_by(:SECONDS, 1)
|
41
|
+
end
|
42
|
+
|
43
|
+
expect(to_camelized_hash(HTTP_RESPONSE => mock_response)).to eq(FIXTURES.read('incorrect_login_response.json'))
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'generates http forwards correctly' do
|
47
|
+
mock_forward = http_forward
|
48
|
+
mock_forward.host = 'www.mock-server.com'
|
49
|
+
mock_forward.port = 80
|
50
|
+
|
51
|
+
expect(to_camelized_hash(HTTP_FORWARD => mock_forward)).to eq(FIXTURES.read('forward_mockserver.json'))
|
52
|
+
|
53
|
+
# Block style
|
54
|
+
mock_forward = forward do |forward|
|
55
|
+
forward.host = 'www.mock-server.com'
|
56
|
+
forward.port = 80
|
57
|
+
end
|
58
|
+
|
59
|
+
expect(to_camelized_hash(HTTP_FORWARD => mock_forward)).to eq(FIXTURES.read('forward_mockserver.json'))
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'generates times correctly' do
|
63
|
+
expect(to_camelized_hash(HTTP_TIMES => exactly(1))).to eq(FIXTURES.read('times_once.json'))
|
64
|
+
|
65
|
+
# Block style
|
66
|
+
mock_times = times do |times|
|
67
|
+
times.remaining_times = 1
|
68
|
+
end
|
69
|
+
expect(to_camelized_hash(HTTP_TIMES => mock_times)).to eq(FIXTURES.read('times_once.json'))
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'generates expectation correctly from file' do
|
73
|
+
payload = FIXTURES.read('register_expectation.json')
|
74
|
+
mock_expectation = expectation_from_json(payload)
|
75
|
+
|
76
|
+
expect(to_camelized_hash(mock_expectation.to_hash)).to eq(payload)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'generates body correctly' do
|
80
|
+
expect(to_camelized_hash(regex('*/login'))).to eq('type' => 'REGEX', 'value' => '*/login')
|
81
|
+
expect(to_camelized_hash(xpath('/login[1]'))).to eq('type' => 'XPATH', 'value' => '/login[1]')
|
82
|
+
expect(to_camelized_hash(parameterized(parameter('token', '4jy5hh')))).to eq('type' => 'PARAMETERS', 'parameters' => [{ 'name' => 'token', 'values' => ['4jy5hh'] }])
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'generates times object correctly' do
|
86
|
+
expect(to_camelized_hash(unlimited)).to eq('unlimited' => 'true', 'remainingTimes' => 0)
|
87
|
+
expect(to_camelized_hash(at_least(2))).to eq('unlimited' => 'true', 'remainingTimes' => 2)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
describe 'transforming request body' do
|
92
|
+
let(:expectation) { MockServer::Model::Expectation.new }
|
93
|
+
let(:request) { MockServer::Model::Request.new }
|
94
|
+
|
95
|
+
[{type: :XPATH, value: '/mock/instance'},
|
96
|
+
{type: :REGEX, value: '\d+.\d+'},
|
97
|
+
{type: :STRING, value: 'Mockserver is great'}].each do |request_hash|
|
98
|
+
|
99
|
+
it "generates a request body of type #{request_hash[:type]}" do
|
100
|
+
request.body = MockServer::Model::Body.new(request_hash)
|
101
|
+
expectation.request = request
|
102
|
+
|
103
|
+
expect(to_camelized_hash(expectation)).to eq({
|
104
|
+
"httpRequest" => {
|
105
|
+
"method" => "GET",
|
106
|
+
"body" => {
|
107
|
+
"type" => request_hash[:type].to_s,
|
108
|
+
"value" => request_hash[:value].to_s
|
109
|
+
}
|
110
|
+
}
|
111
|
+
})
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when request body type binary' do
|
116
|
+
let(:body) do
|
117
|
+
MockServer::Model::Body.new(type: :BINARY, value: 'TW9ja3NlcnZlciBpcyBncmVhdAo=')
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'correctly transforms to a string and updates the object' do
|
121
|
+
request.body = body
|
122
|
+
expectation.request = request
|
123
|
+
|
124
|
+
expect(to_camelized_hash(expectation)).to eq({
|
125
|
+
"httpRequest" => {
|
126
|
+
"method" => "GET",
|
127
|
+
"body" => {
|
128
|
+
"type" => "STRING",
|
129
|
+
"value" => "Mockserver is great\n"
|
130
|
+
}
|
131
|
+
}
|
132
|
+
})
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative '../spec_helper'
|
3
|
+
|
4
|
+
describe MockServer::MockServerClient do
|
5
|
+
|
6
|
+
let(:client) { MockServer::MockServerClient.new('localhost', 8080) }
|
7
|
+
let(:register_expectation_json) { FIXTURES.read('register_expectation.json').to_json }
|
8
|
+
let(:search_request_json) { FIXTURES.read('search_request.json').to_json }
|
9
|
+
|
10
|
+
before do
|
11
|
+
# To suppress logging output to standard output, write to a temporary file
|
12
|
+
client.logger = LoggingFactory::DEFAULT_FACTORY.log('test', output: 'tmp.log', truncate: true)
|
13
|
+
|
14
|
+
# Stub requests
|
15
|
+
stub_request(:put, /.+\/expectation/).with(body: register_expectation_json, headers: { 'Content-Type' => 'application/json' }).to_return(status: 201)
|
16
|
+
stub_request(:put, /.+\/clear/).with(body: search_request_json, headers: { 'Content-Type' => 'application/json' }).to_return(status: 202)
|
17
|
+
stub_request(:put, /.+\/reset/).with(headers: { 'Content-Type' => 'application/json' }).to_return(status: 202)
|
18
|
+
stub_request(:put, /.+\/retrieve/).with(body: search_request_json, headers: { 'Content-Type' => 'application/json' }).to_return(
|
19
|
+
body: '[]',
|
20
|
+
status: 200
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'registers an expectation correctly' do
|
25
|
+
mock_expectation = expectation do |expectation|
|
26
|
+
expectation.request do |request|
|
27
|
+
request.method = 'POST'
|
28
|
+
request.path = '/login'
|
29
|
+
request.query_string_parameters << parameter('returnUrl', '/account')
|
30
|
+
request.cookies << cookie('sessionId', '2By8LOhBmaW5nZXJwcmludCIlMDAzMW')
|
31
|
+
request.body = exact({ username: 'foo', password: 'bar' }.to_json)
|
32
|
+
end
|
33
|
+
|
34
|
+
expectation.response do |response|
|
35
|
+
response.status_code = 401
|
36
|
+
response.headers << header('Content-Type', 'application/json; charset=utf-8')
|
37
|
+
response.headers << header('Cache-Control', 'public, max-age=86400')
|
38
|
+
response.body = body({ message: 'incorrect username and password combination' }.to_json)
|
39
|
+
response.delay = delay_by(:SECONDS, 1)
|
40
|
+
end
|
41
|
+
|
42
|
+
expectation.times = exactly(2)
|
43
|
+
end
|
44
|
+
|
45
|
+
response = client.register(mock_expectation)
|
46
|
+
expect(response.code).to eq(201)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'raises an error when trying to set both forward and response' do
|
50
|
+
mock_expectation = expectation do |expectation|
|
51
|
+
expectation.request do |request|
|
52
|
+
request.method = 'POST'
|
53
|
+
request.path = '/login'
|
54
|
+
end
|
55
|
+
|
56
|
+
expectation.response do |response|
|
57
|
+
response.status_code = 401
|
58
|
+
end
|
59
|
+
|
60
|
+
expectation.forward do |forward|
|
61
|
+
forward.scheme = :HTTP
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
expect { client.register(mock_expectation) }.to raise_error(RuntimeError, 'You can only set either of ["httpResponse", "httpForward"]. But not both')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'clears requests from the mock server' do
|
69
|
+
expect(client.clear(request(:POST, '/login')).code).to eq(202)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'resets the mock server' do
|
73
|
+
expect(client.reset.code).to eq(202)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'retrieves requests correctly' do
|
77
|
+
expect(client.retrieve(request(:POST, '/login')).code).to eq(200)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative '../spec_helper'
|
3
|
+
|
4
|
+
describe MockServer::ProxyClient do
|
5
|
+
|
6
|
+
let(:client) { MockServer::ProxyClient.new('localhost', 8080) }
|
7
|
+
let(:retrieved_request) { FIXTURES.read('retrieved_request.json') }
|
8
|
+
let(:retrieved_request_json) { retrieved_request.to_json }
|
9
|
+
let(:search_request_json) { FIXTURES.read('search_request.json').to_json }
|
10
|
+
|
11
|
+
before do
|
12
|
+
# To suppress logging output to standard output, write to a temporary file
|
13
|
+
client.logger = LoggingFactory::DEFAULT_FACTORY.log('test', output: 'tmp.log', truncate: true)
|
14
|
+
|
15
|
+
# Stub requests
|
16
|
+
stub_request(:put, /.+\/retrieve/).with(body: search_request_json).to_return(
|
17
|
+
body: "[#{retrieved_request_json}, #{retrieved_request_json}]",
|
18
|
+
status: 200
|
19
|
+
)
|
20
|
+
stub_request(:put, /.+\/dumpToLog$/).to_return(status: 202).once
|
21
|
+
stub_request(:put, /.+\/dumpToLog\?type=java$/).to_return(status: 202).once
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'verifies requests correctly' do
|
25
|
+
response = client.verify(request(:POST, '/login'), exactly(2))
|
26
|
+
response = response.map { |mock| to_camelized_hash(mock.to_hash) }
|
27
|
+
expect(response).to eq([retrieved_request, retrieved_request])
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises an error when verification fails' do
|
31
|
+
expect { client.verify(request(:POST, '/login')) }.to raise_error(RuntimeError, 'Expected request to be present: [1] (exactly). But found: [2]')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'dumps to logs correctly do' do
|
35
|
+
expect(client.dump_log.code).to eq(202)
|
36
|
+
expect(client.dump_log({}, true).code).to eq(202)
|
37
|
+
end
|
38
|
+
end
|