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.
@@ -0,0 +1,53 @@
1
+ # encoding: UTF-8
2
+ require 'hashie'
3
+ require_relative './enum'
4
+
5
+ #
6
+ # A class to model the number of times an expectation should be respected.
7
+ # @author:: Nayyara Samuel (mailto: nayyara.samuel@opower.com)
8
+ #
9
+ module MockServer::Model
10
+ # Enum for boolean values since Ruby does not have this by default
11
+ class Boolean < Enum
12
+ def allowed_values
13
+ [true, false]
14
+ end
15
+ end
16
+
17
+ # Model for times class
18
+ class Times < Hashie::Dash
19
+ include Hashie::Extensions::MethodAccess
20
+ include Hashie::Extensions::IgnoreUndeclared
21
+ include Hashie::Extensions::Coercion
22
+
23
+ property :remaining_times, default: 0
24
+ property :unlimited, default: false
25
+
26
+ coerce_key :unlimited, Boolean
27
+ end
28
+
29
+ # DSL methods related to times
30
+ module DSL
31
+ def unlimited
32
+ Times.new(unlimited: true)
33
+ end
34
+
35
+ def once
36
+ Times.new(remaining_times: 1)
37
+ end
38
+
39
+ def exactly(num)
40
+ Times.new(remaining_times: num)
41
+ end
42
+
43
+ def at_least(num)
44
+ Times.new(remaining_times: num, unlimited: true)
45
+ end
46
+
47
+ def times(&_)
48
+ obj = once
49
+ yield obj if block_given?
50
+ obj
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: UTF-8
2
+ require_relative './abstract_client'
3
+
4
+ #
5
+ # The client used to interact with the proxy server.
6
+ # @author:: Nayyara Samuel (mailto: nayyara.samuel@opower.com)
7
+ #
8
+ class MockServer::ProxyClient < MockServer::AbstractClient
9
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+ require 'active_support/inflector'
3
+ require 'json'
4
+
5
+ #
6
+ # A module that has common utility methods used by this project
7
+ # @author:: Nayyara Samuel (mailto: nayyara.samuel@opower.com)
8
+ #
9
+ module MockServer::UtilityMethods
10
+ # Does the following filter/transform operations on a hash
11
+ # - exclude null or empty valued keys from the hash
12
+ # - camelize the keys of the hash
13
+ # @param hash [Object] an object which will be used to create the hash. Must support :to_hash method
14
+ # @return [Hash] the transformed hash
15
+ def prepare_hash(obj)
16
+ obj = obj && obj.respond_to?(:to_hash) ? obj.to_hash : obj
17
+
18
+ if obj.is_a?(Hash)
19
+ obj.each_with_object({}) do |(k, v), acc|
20
+ is_empty = v.nil? || (v.respond_to?(:empty?) ? v.empty? : false)
21
+ acc[camelize(k)] = prepare_hash(v) unless is_empty
22
+ end
23
+ elsif obj.respond_to?(:map)
24
+ obj.map { |element| prepare_hash(element) }
25
+ else
26
+ obj
27
+ end
28
+ end
29
+
30
+ # @param [Object] an object to camelize the string representation of
31
+ # @return [String] the string converted to camelcase with first letter in lower case
32
+ def camelize(str)
33
+ str.to_s.camelize(:lower)
34
+ end
35
+
36
+ # Parse string response into JSON
37
+ # @param response [Response] from RestClient response
38
+ # @return [Hash] the parsed response or the object unmodified if parsing is not possible
39
+ def parse_response(response)
40
+ JSON.parse(response)
41
+ rescue JSON::ParserError
42
+ response
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: UTF-8
2
+ # Version for this gem
3
+ module MockServer
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mockserver/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'mockserver-client'
8
+ spec.version = MockServer::VERSION
9
+ spec.authors = ['Nayyara Samuel']
10
+ spec.email = ['nayyara.samuel@gmail.com']
11
+ spec.summary = %q(A Ruby client for MockServer)
12
+ spec.required_ruby_version = '~> 1.9.3'
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.add_development_dependency 'bundler', '~> 1.6'
20
+ spec.add_development_dependency 'rake', '~> 10.3.2'
21
+ spec.add_development_dependency 'rspec', '~> 3.0.0'
22
+ spec.add_development_dependency 'simplecov', '~> 0.8.2'
23
+ spec.add_development_dependency 'webmock', '~> 1.18'
24
+ spec.add_development_dependency 'rubocop', '~> 0.23.0'
25
+
26
+ spec.add_dependency 'hashie', '~> 3.0'
27
+ spec.add_dependency 'json', '~> 1.8.1'
28
+ spec.add_dependency 'activesupport', '~> 4.1.1'
29
+ spec.add_dependency 'rest-client', '~> 1.6.7'
30
+ spec.add_dependency 'logging_factory', '~> 0.0.2'
31
+ end
@@ -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
+ "queryParameters": [
6
+ {
7
+ "name": "returnUrl",
8
+ "values": ["/account"]
9
+ }
10
+ ],
11
+ "cookies": [
12
+ {
13
+ "name": "sessionId",
14
+ "values": ["2By8LOhBmaW5nZXJwcmludCIlMDAzMW"]
15
+ }
16
+ ],
17
+ "body": {
18
+ "type": "EXACT",
19
+ "value": "{username:'foo', password:'bar'}"
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "httpRequest": {
3
+ "method": "POST",
4
+ "path": "/login",
5
+ "queryParameters": [
6
+ {
7
+ "values": [
8
+ "/account"
9
+ ],
10
+ "name": "returnUrl"
11
+ }
12
+ ],
13
+ "cookies": [
14
+ {
15
+ "values": [
16
+ "2By8LOhBmaW5nZXJwcmludCIlMDAzMW"
17
+ ],
18
+ "name": "sessionId"
19
+ }
20
+ ],
21
+ "body": {
22
+ "type": "EXACT",
23
+ "value": "{\"username\":\"foo\",\"password\":\"bar\"}"
24
+ }
25
+ },
26
+ "httpResponse": {
27
+ "statusCode": 401,
28
+ "headers": [
29
+ {
30
+ "values": [
31
+ "application/json; charset=utf-8"
32
+ ],
33
+ "name": "Content-Type"
34
+ },
35
+ {
36
+ "values": [
37
+ "public, max-age=86400"
38
+ ],
39
+ "name": "Cache-Control"
40
+ }
41
+ ],
42
+ "body": "{\"message\":\"incorrect username and password combination\"}",
43
+ "delay": {
44
+ "timeUnit": "SECONDS",
45
+ "value": 1
46
+ }
47
+ },
48
+ "times": {
49
+ "remainingTimes": 2,
50
+ "unlimited": false
51
+ }
52
+ }
@@ -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,83 @@
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_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_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 body correctly' do
73
+ expect(to_camelized_hash(regex('*/login'))).to eq('type' => 'REGEX', 'value' => '*/login')
74
+ expect(to_camelized_hash(xpath('/login[1]'))).to eq('type' => 'XPATH', 'value' => '/login[1]')
75
+ expect(to_camelized_hash(parameterized(parameter('token', '4jy5hh')))).to eq('type' => 'PARAMETERS', 'parameters' => [{ 'name' => 'token', 'values' => ['4jy5hh'] }])
76
+ end
77
+
78
+ it 'generates times object correctly' do
79
+ expect(to_camelized_hash(unlimited)).to eq('unlimited' => 'true', 'remainingTimes' => 0)
80
+ expect(to_camelized_hash(at_least(2))).to eq('unlimited' => 'true', 'remainingTimes' => 2)
81
+ end
82
+
83
+ end
@@ -0,0 +1,77 @@
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).to_return(status: 201)
16
+ stub_request(:put, /.+\/clear/).with(body: search_request_json).to_return(status: 202)
17
+ stub_request(:put, /.+\/reset/).to_return(status: 202)
18
+ stub_request(:put, /.+\/retrieve/).with(body: search_request_json).to_return(body: '', status: 200)
19
+ end
20
+
21
+ it 'registers an expectation correctly' do
22
+ mock_expectation = expectation do |expectation|
23
+ expectation.request do |request|
24
+ request.method = 'POST'
25
+ request.path = '/login'
26
+ request.query_parameters << parameter('returnUrl', '/account')
27
+ request.cookies << cookie('sessionId', '2By8LOhBmaW5nZXJwcmludCIlMDAzMW')
28
+ request.body = exact({ username: 'foo', password: 'bar' }.to_json)
29
+ end
30
+
31
+ expectation.response do |response|
32
+ response.status_code = 401
33
+ response.headers << header('Content-Type', 'application/json; charset=utf-8')
34
+ response.headers << header('Cache-Control', 'public, max-age=86400')
35
+ response.body = body({ message: 'incorrect username and password combination' }.to_json)
36
+ response.delay = delay_by(:SECONDS, 1)
37
+ end
38
+
39
+ expectation.times = exactly(2)
40
+ end
41
+
42
+ response = client.register(mock_expectation)
43
+ expect(response.code).to eq(201)
44
+ end
45
+
46
+ it 'raises an error when trying to set both forward and response' do
47
+ mock_expectation = expectation do |expectation|
48
+ expectation.request do |request|
49
+ request.method = 'POST'
50
+ request.path = '/login'
51
+ end
52
+
53
+ expectation.response do |response|
54
+ response.status_code = 401
55
+ end
56
+
57
+ expectation.forward do |forward|
58
+ forward.scheme = :HTTP
59
+ end
60
+ end
61
+
62
+ expect { client.register(mock_expectation) }.to raise_error(RuntimeError, 'You can only set either of ["httpResponse", "httpForward"]. But not both')
63
+ end
64
+
65
+ it 'clears requests from the mock server' do
66
+ expect(client.clear(request(:POST, '/login')).code).to eq(202)
67
+ end
68
+
69
+ it 'resets the mock server' do
70
+ expect(client.reset.code).to eq(202)
71
+ end
72
+
73
+ it 'retrieves requests correctly' do
74
+ expect(client.retrieve(request(:POST, '/login')).code).to eq(200)
75
+ end
76
+
77
+ end
@@ -0,0 +1,37 @@
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(:register_expectation) { FIXTURES.read('register_expectation.json') }
8
+ let(:register_expectation_json) { register_expectation.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: "[#{register_expectation_json}, #{register_expectation_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
+ expect(response).to eq([register_expectation, register_expectation])
27
+ end
28
+
29
+ it 'raises an error when verification fails' do
30
+ expect { client.verify(request(:POST, '/login')) }.to raise_error(RuntimeError, 'Expected request to be present: [1] (exactly). But found: [2]')
31
+ end
32
+
33
+ it 'dumps to logs correctly do' do
34
+ expect(client.dump_log.code).to eq(202)
35
+ expect(client.dump_log({}, true).code).to eq(202)
36
+ end
37
+ end