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.
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