flipp-mockserver-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rubocop.yml +7 -0
  4. data/Gemfile +4 -0
  5. data/README.md +182 -0
  6. data/Rakefile +10 -0
  7. data/bin/mockserver +9 -0
  8. data/lib/cli.rb +146 -0
  9. data/lib/mockserver-client.rb +17 -0
  10. data/lib/mockserver/abstract_client.rb +111 -0
  11. data/lib/mockserver/mock_server_client.rb +59 -0
  12. data/lib/mockserver/model/array_of.rb +85 -0
  13. data/lib/mockserver/model/body.rb +56 -0
  14. data/lib/mockserver/model/cookie.rb +36 -0
  15. data/lib/mockserver/model/delay.rb +34 -0
  16. data/lib/mockserver/model/enum.rb +47 -0
  17. data/lib/mockserver/model/expectation.rb +158 -0
  18. data/lib/mockserver/model/forward.rb +41 -0
  19. data/lib/mockserver/model/header.rb +43 -0
  20. data/lib/mockserver/model/parameter.rb +43 -0
  21. data/lib/mockserver/model/request.rb +77 -0
  22. data/lib/mockserver/model/response.rb +45 -0
  23. data/lib/mockserver/model/times.rb +61 -0
  24. data/lib/mockserver/proxy_client.rb +9 -0
  25. data/lib/mockserver/utility_methods.rb +59 -0
  26. data/lib/mockserver/version.rb +5 -0
  27. data/mockserver-client.gemspec +36 -0
  28. data/pom.xml +116 -0
  29. data/spec/fixtures/forward_mockserver.json +7 -0
  30. data/spec/fixtures/incorrect_login_response.json +20 -0
  31. data/spec/fixtures/post_login_request.json +22 -0
  32. data/spec/fixtures/register_expectation.json +50 -0
  33. data/spec/fixtures/retrieved_request.json +22 -0
  34. data/spec/fixtures/search_request.json +6 -0
  35. data/spec/fixtures/times_once.json +6 -0
  36. data/spec/integration/mock_client_integration_spec.rb +82 -0
  37. data/spec/mockserver/builder_spec.rb +136 -0
  38. data/spec/mockserver/mock_client_spec.rb +80 -0
  39. data/spec/mockserver/proxy_client_spec.rb +38 -0
  40. data/spec/spec_helper.rb +61 -0
  41. metadata +293 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "httpForward": {
3
+ "host": "www.mock-server.com",
4
+ "port": 80,
5
+ "scheme": "HTTP"
6
+ }
7
+ }
@@ -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,6 @@
1
+ {
2
+ "httpRequest": {
3
+ "method": "POST",
4
+ "path": "/login"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "times": {
3
+ "remainingTimes": 1,
4
+ "unlimited": "false"
5
+ }
6
+ }
@@ -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