├── .rspec ├── .env ├── lib ├── mountebank │ ├── version.rb │ ├── stub │ │ ├── https_response.rb │ │ ├── tcp_response.rb │ │ ├── proxy_response.rb │ │ ├── http_response.rb │ │ ├── response.rb │ │ └── predicate.rb │ ├── helper.rb │ ├── stub.rb │ ├── network.rb │ └── imposter.rb └── mountebank.rb ├── Gemfile ├── .gitignore ├── Rakefile ├── spec ├── spec_helper.rb ├── mountebank │ ├── stub │ │ ├── tcp_response_spec.rb │ │ ├── https_response_spec.rb │ │ ├── predicate_spec.rb │ │ ├── proxy_response_spec.rb │ │ ├── response_spec.rb │ │ └── http_response_spec.rb │ ├── helper_spec.rb │ ├── network_spec.rb │ ├── stub_spec.rb │ └── imposter_spec.rb ├── mountebank_spec.rb └── examples_spec.rb ├── LICENSE.txt ├── mountebank.gemspec └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | MOUNTEBANK_SERVER=127.0.0.1 2 | MOUNTEBANK_PORT=2525 3 | -------------------------------------------------------------------------------- /lib/mountebank/version.rb: -------------------------------------------------------------------------------- 1 | module Mountebank 2 | VERSION = "0.0.1" 3 | end 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in mountebank.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/mountebank/stub/https_response.rb: -------------------------------------------------------------------------------- 1 | class Mountebank::Stub::HttpsResponse < Mountebank::Stub::HttpResponse 2 | end 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | *.bundle 11 | *.so 12 | *.o 13 | *.a 14 | mkmf.log 15 | mb.log 16 | mb.pid 17 | tags 18 | .idea/ 19 | .ruby-version 20 | .ruby-gemset 21 | -------------------------------------------------------------------------------- /lib/mountebank/stub/tcp_response.rb: -------------------------------------------------------------------------------- 1 | class Mountebank::Stub::TcpResponse < Mountebank::Stub::Response 2 | def self.create(data='') 3 | payload = {} 4 | payload[:data] = data unless data.empty? 5 | 6 | data = {is: payload} 7 | new(data) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require 'dotenv/tasks' 3 | 4 | require 'rspec/core/rake_task' 5 | 6 | desc "Run all specs" 7 | RSpec::Core::RakeTask.new(:spec) do |t| 8 | t.rspec_opts = %w[--color] 9 | t.verbose = false 10 | end 11 | 12 | task :default => [:dotenv, :spec] 13 | 14 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'mountebank' 2 | require 'dotenv' 3 | require 'pry' 4 | require 'open-uri' 5 | Dotenv.load 6 | 7 | def reset_mountebank 8 | Mountebank.reset 9 | end 10 | 11 | def test_url(uri) 12 | open(uri).read 13 | end 14 | 15 | RSpec.configure do |config| 16 | end 17 | 18 | -------------------------------------------------------------------------------- /lib/mountebank/stub/proxy_response.rb: -------------------------------------------------------------------------------- 1 | class Mountebank::Stub::ProxyResponse < Mountebank::Stub::Response 2 | PROXY_MODE_ONCE = 'proxyOnce' 3 | PROXY_MODE_ALWAYS = 'proxyAlways' 4 | 5 | def self.create(to, mode=PROXY_MODE_ONCE, predicateGenerators=[]) 6 | data = {proxy: { 7 | to: to, 8 | mode: mode, 9 | predicateGenerators: predicateGenerators 10 | } 11 | } 12 | new(data) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/mountebank/stub/tcp_response_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Stub::TcpResponse do 4 | let(:data) { 'blah' } 5 | let!(:response) { Mountebank::Stub::TcpResponse.create(data) } 6 | 7 | describe '.create' do 8 | it 'returns response object' do 9 | expect(response).to be_a Mountebank::Stub::TcpResponse 10 | expect(response.to_json).to eq '{"is":{"data":"blah"}}' 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/mountebank/stub/http_response.rb: -------------------------------------------------------------------------------- 1 | class Mountebank::Stub::HttpResponse < Mountebank::Stub::Response 2 | def self.create(statusCode=200, headers={}, body='', behaviors={}) 3 | payload = {} 4 | payload[:statusCode] = statusCode 5 | payload[:headers] = headers unless headers.empty? 6 | payload[:body] = body unless body.empty? 7 | 8 | data = {is: payload} 9 | data.merge!(_behaviors: behaviors) unless behaviors.empty? 10 | new(data) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/mountebank/helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Helper do 4 | describe '.symbolize' do 5 | let(:hash) { { 6 | "foo" => {"bar" => "Baz"}, 7 | "goo" => "balls" 8 | } 9 | } 10 | let(:expected_hash) { { 11 | :foo => {:bar => "Baz"}, 12 | :goo => "balls" 13 | } 14 | } 15 | 16 | it 'symbolizes' do 17 | expect(Mountebank::Helper.symbolize(hash)).to eq expected_hash 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/mountebank/stub/https_response_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Stub::HttpsResponse do 4 | let(:statusCode) { 200 } 5 | let(:headers) { {"Content-Type" => "application/json"} } 6 | let(:body) { {foo:"bar"}.to_json } 7 | let!(:response) { Mountebank::Stub::HttpsResponse.create(statusCode, headers, body) } 8 | 9 | describe '.create' do 10 | it 'returns response object' do 11 | expect(response).to be_a Mountebank::Stub::HttpsResponse 12 | expect(response.to_json).to eq '{"is":{"statusCode":200,"headers":{"Content-Type":"application/json"},"body":"{\"foo\":\"bar\"}"}}' 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/mountebank/network_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'mountebank/network' 3 | 4 | RSpec.describe Mountebank::Network do 5 | subject{ Mountebank::Network } 6 | describe '.mountebank_server_uri' do 7 | it 'returns correct uri' do 8 | expect(subject.mountebank_server_uri).to eq 'http://127.0.0.1:2525' 9 | end 10 | end 11 | 12 | describe '.connection' do 13 | it 'returns a Faraday instance' do 14 | expect(subject.connection).to be_a(::Faraday::Connection) 15 | end 16 | end 17 | 18 | describe '.get' do 19 | it 'calls a URL with GET' do 20 | expect(subject.get('/').status).to eq 200 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/mountebank/stub/response.rb: -------------------------------------------------------------------------------- 1 | class Mountebank::Stub::Response 2 | attr_accessor :is, :proxy, :inject 3 | 4 | def initialize(data={}) 5 | @is = data[:is] || nil 6 | @proxy = data[:proxy] || nil 7 | @inject = data[:inject] || nil 8 | @behaviors = data[:_behaviors] 9 | end 10 | 11 | def self.with_injection(injection='') 12 | return false if injection.empty? 13 | 14 | data = {inject:injection} 15 | new(data) 16 | end 17 | 18 | def to_json(*args) 19 | data = {} 20 | data[:is] = @is unless @is.nil? 21 | data[:proxy] = @proxy unless @proxy.nil? 22 | data[:inject] = @inject unless @inject.nil? 23 | data[:_behaviors] = @behaviors unless @behaviors.nil? 24 | data.to_json(*args) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/mountebank/stub/predicate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Stub::Predicate do 4 | let(:data) { {} } 5 | let(:predicate) { Mountebank::Stub::Predicate.new(data) } 6 | 7 | describe '#initialize' do 8 | it 'creates a new object' do 9 | expect(predicate.equals).to be_nil 10 | expect(predicate.caseSensitive).to be_nil 11 | expect(predicate.except).to be_nil 12 | expect(predicate.to_json).to eq '{}' 13 | end 14 | 15 | context 'sets value' do 16 | let(:data) { {equals: {path:'/test'}} } 17 | 18 | it 'has path' do 19 | expect(predicate.equals).to eq({path:'/test'}) 20 | expect(predicate.to_json).to eq '{"equals":{"path":"/test"}}' 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/mountebank_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank do 4 | before:each do 5 | reset_mountebank 6 | end 7 | 8 | describe '.reset' do 9 | it 'returns' do 10 | expect(Mountebank.reset).to be 11 | end 12 | end 13 | 14 | describe '.imposters' do 15 | context 'no imposters' do 16 | it 'blank' do 17 | expect(Mountebank.imposters).to be_empty 18 | end 19 | end 20 | 21 | context 'has imposters' do 22 | before do 23 | Mountebank::Imposter.create(4545) 24 | end 25 | 26 | it 'not empty' do 27 | expect(Mountebank.imposters).to_not be_empty 28 | end 29 | 30 | it 'returns valid imposter' do 31 | expect(Mountebank.imposters.first).to be_a Mountebank::Imposter 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/mountebank/stub/proxy_response_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Stub::ProxyResponse do 4 | let(:to) { 'http://example.com' } 5 | let(:mode) { Mountebank::Stub::ProxyResponse::PROXY_MODE_ONCE } 6 | let(:predicateGenerators) { [ 7 | { matches: { 8 | method:true, 9 | path:true, 10 | query:true 11 | } 12 | } 13 | ] 14 | } 15 | let!(:response) { Mountebank::Stub::ProxyResponse.create(to, mode, predicateGenerators) } 16 | 17 | describe '.create' do 18 | it 'returns response object' do 19 | expect(response).to be_a Mountebank::Stub::ProxyResponse 20 | expect(response.to_json).to eq '{"proxy":{"to":"http://example.com","mode":"proxyOnce","predicateGenerators":[{"matches":{"method":true,"path":true,"query":true}}]}}' 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/mountebank.rb: -------------------------------------------------------------------------------- 1 | require "mountebank/version" 2 | require "mountebank/helper" 3 | require "mountebank/network" 4 | require "mountebank/imposter" 5 | require "mountebank/stub" 6 | require "mountebank/stub/response" 7 | require "mountebank/stub/http_response" 8 | require "mountebank/stub/https_response" 9 | require "mountebank/stub/tcp_response" 10 | require "mountebank/stub/proxy_response" 11 | require "mountebank/stub/predicate" 12 | require "json" 13 | 14 | module Mountebank 15 | extend self 16 | 17 | def self.imposters 18 | imposters = [] 19 | 20 | response = Network.get('/imposters') 21 | if response.success? 22 | response.body[:imposters].each do |imposter| 23 | imposters << Mountebank::Imposter.new(imposter) 24 | end 25 | end 26 | 27 | imposters 28 | end 29 | 30 | def self.reset 31 | response = Network.delete('/imposters') 32 | response.success? 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/mountebank/stub/predicate.rb: -------------------------------------------------------------------------------- 1 | class Mountebank::Stub::Predicate 2 | attr_accessor :equals, :deepEquals, :contains, :startsWith, :endsWith, :matches, :exists, :not, :or, :and, :inject, :caseSensitive, :except 3 | 4 | VALID_OPERATORS = [ 5 | :equals, :deepEquals, :contains, :startsWith, :endsWith, :matches, :exists, :not, 6 | :or, :and, :inject 7 | ] 8 | 9 | def initialize(data={}) 10 | VALID_OPERATORS.each do |key| 11 | send("#{key}=", data[key]) if data.key?(key) 12 | end 13 | @caseSensitive = data[:caseSensitive] || nil 14 | @except = data[:except] || nil 15 | end 16 | 17 | def to_json(*args) 18 | data = {} 19 | VALID_OPERATORS.each do |key| 20 | data[key] = send("#{key}") if instance_variable_defined?("@#{key}") 21 | end 22 | data[:caseSensitive] = @caseSensitive unless @caseSensitive.nil? 23 | data[:except] = @except unless @except.nil? 24 | data.to_json(*args) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/mountebank/stub/response_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Stub::Response do 4 | let(:data) { {} } 5 | let(:response) { Mountebank::Stub::Response.new(data) } 6 | 7 | describe '#initialize' do 8 | it 'creates a new object' do 9 | expect(response.is).to be_nil 10 | expect(response.proxy).to be_nil 11 | expect(response.inject).to be_nil 12 | expect(response.to_json).to eq '{}' 13 | end 14 | end 15 | 16 | context 'add dummy response' do 17 | let(:data) { { 18 | :is => {statusCode: 200, body:"ohai"} 19 | } 20 | } 21 | it 'is able to response' do 22 | expect(response.to_json).to eq '{"is":{"statusCode":200,"body":"ohai"}}' 23 | end 24 | end 25 | 26 | context '.with_injection' do 27 | let(:data) { 'function (request, state, logger){}' } 28 | let(:response) { Mountebank::Stub::Response.with_injection(data) } 29 | 30 | it 'is valid' do 31 | expect(response.inject).to eq(data) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/mountebank/stub/http_response_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Stub::HttpResponse do 4 | let(:statusCode) { 200 } 5 | let(:headers) { {"Content-Type" => "application/json"} } 6 | let(:body) { {foo:"bar"}.to_json } 7 | let!(:response) { Mountebank::Stub::HttpResponse.create(statusCode, headers, body) } 8 | let!(:response_with_behaviors) { Mountebank::Stub::HttpResponse.create(statusCode, headers, body, {wait: 10000}) } 9 | 10 | describe '.create' do 11 | it 'returns response object' do 12 | expect(response).to be_a Mountebank::Stub::HttpResponse 13 | expect(response.to_json).to eq '{"is":{"statusCode":200,"headers":{"Content-Type":"application/json"},"body":"{\"foo\":\"bar\"}"}}' 14 | end 15 | 16 | it 'returns a response object' do 17 | expect(response_with_behaviors).to be_a Mountebank::Stub::HttpResponse 18 | expect(response_with_behaviors.to_json).to eq '{"is":{"statusCode":200,"headers":{"Content-Type":"application/json"},"body":"{\"foo\":\"bar\"}"},"_behaviors":{"wait":10000}}' 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Michael Cheng 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /lib/mountebank/helper.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | 3 | module Mountebank 4 | class Helper 5 | # Convert Ruby Hash keys into symbols 6 | # Source: https://gist.github.com/Integralist/9503099 7 | def self.symbolize(obj) 8 | return obj.reduce({}) do |memo, (k, v)| 9 | memo.tap { |m| m[k.to_sym] = symbolize(v) } 10 | end if obj.is_a? Hash 11 | 12 | return obj.reduce([]) do |memo, v| 13 | memo << symbolize(v); memo 14 | end if obj.is_a? Array 15 | 16 | obj 17 | end 18 | end 19 | 20 | class SymbolizeKeys < ::Faraday::Middleware 21 | def initialize(app = nil, options = {}) 22 | super(app) 23 | @options = options 24 | @content_types = Array(options[:content_type]) 25 | end 26 | 27 | def call(environment) 28 | @app.call(environment).on_complete do |env| 29 | if env[:body].is_a? Hash 30 | env[:body] = Helper.symbolize(env[:body]) 31 | end 32 | end 33 | end 34 | end 35 | 36 | if ::Faraday::Middleware.respond_to? :register_middleware 37 | ::Faraday::Response.register_middleware :symbolize_keys => lambda { Mountebank::SymbolizeKeys } 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/mountebank/stub.rb: -------------------------------------------------------------------------------- 1 | class Mountebank::Stub 2 | attr_reader :responses, :predicates 3 | 4 | def initialize(data={}) 5 | set_attributes(data) 6 | end 7 | 8 | def self.create(responses=[], predicates=[]) 9 | data = { 10 | :responses => responses, 11 | :predicates => predicates 12 | } 13 | new(data) 14 | end 15 | 16 | def to_json(*args) 17 | data = {} 18 | data[:responses] = @responses unless @responses.empty? 19 | data[:predicates] = @predicates unless @predicates.empty? 20 | data.to_json(*args) 21 | end 22 | 23 | private 24 | 25 | def set_attributes(data={}) 26 | @responses, @predicates = [], [] 27 | 28 | if data[:responses] 29 | data[:responses].each do |response| 30 | unless response.is_a? Mountebank::Stub::Response 31 | response = Mountebank::Stub::Response.new(response) 32 | end 33 | @responses << response 34 | end 35 | end 36 | 37 | if data[:predicates] 38 | data[:predicates].each do |predicate| 39 | unless predicate.is_a? Mountebank::Stub::Predicate 40 | predicate = Mountebank::Stub::Predicate.new(predicate) 41 | end 42 | @predicates << predicate 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/mountebank/network.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | require 'faraday_middleware' 3 | 4 | module Mountebank 5 | class Network 6 | def self.connection 7 | @conn ||= Faraday.new(url: mountebank_server_uri) do |conn| 8 | conn.request :json 9 | conn.response :symbolize_keys, :content_type => /\bjson$/ 10 | conn.response :json, :content_type => /\bjson$/ 11 | conn.adapter Faraday.default_adapter 12 | end 13 | end 14 | 15 | def self.get(uri) 16 | connection.get(uri) 17 | end 18 | 19 | def self.post(uri, data) 20 | connection.post do |req| 21 | req.url uri 22 | req.body = data 23 | end 24 | end 25 | 26 | def self.put(uri, data) 27 | connection.put do |req| 28 | req.url uri 29 | req.body = data 30 | end 31 | end 32 | 33 | def self.delete(uri) 34 | connection.delete do |req| 35 | req.url uri 36 | end 37 | end 38 | 39 | def self.mountebank_server 40 | ENV['MOUNTEBANK_SERVER'] || 'localhost' 41 | end 42 | 43 | def self.mountebank_server_port 44 | ENV['MOUNTEBANK_PORT'] || '2525' 45 | end 46 | 47 | def self.mountebank_server_uri 48 | "http://#{mountebank_server}:#{mountebank_server_port}" 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /mountebank.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'mountebank/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "mountebank" 8 | spec.version = Mountebank::VERSION 9 | spec.authors = ["Michael Cheng"] 10 | spec.email = ["mcheng.work@gmail.com"] 11 | spec.summary = %q{Ruby GEM to manage a Mountebank Test Server} 12 | spec.description = %q{A simple Ruby library that lets you manage your Mountebank test server.} 13 | spec.homepage = "https://github.com/CoderKungfu/mountebank-gem" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0") 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_runtime_dependency "faraday", "~>0.9.0" 22 | spec.add_runtime_dependency "faraday_middleware" 23 | spec.add_development_dependency "bundler", "~> 1.7" 24 | spec.add_development_dependency "rake", "~> 10.0" 25 | spec.add_development_dependency "dotenv", "~> 1.0.0" 26 | spec.add_development_dependency "rspec" 27 | spec.add_development_dependency "pry" 28 | end 29 | -------------------------------------------------------------------------------- /spec/mountebank/stub_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Stub do 4 | let(:responses) { [] } 5 | let(:predicates) { [] } 6 | let(:stub) { Mountebank::Stub.create(responses, predicates) } 7 | 8 | describe '#initialize' do 9 | it 'creates a new object' do 10 | expect(stub).to be_a Mountebank::Stub 11 | expect(stub.responses).to eq [] 12 | expect(stub.predicates).to eq [] 13 | expect(stub.to_json).to eq '{}' 14 | end 15 | end 16 | 17 | context 'has responses' do 18 | let(:responses) { [ 19 | { 20 | is: {statusCode: 200, body:"ohai"} 21 | } 22 | ] 23 | } 24 | 25 | it 'is not empty' do 26 | expect(stub.responses).to_not be_empty 27 | end 28 | 29 | it 'is a response' do 30 | expect(stub.responses.first).to be_a Mountebank::Stub::Response 31 | expect(stub.responses.first.is[:statusCode]).to eq 200 32 | end 33 | 34 | it 'renders correct JSON' do 35 | expect(stub.to_json).to eq '{"responses":[{"is":{"statusCode":200,"body":"ohai"}}]}' 36 | end 37 | end 38 | 39 | context 'has predicates' do 40 | let(:predicates) { [ 41 | { 42 | equals: {path:'/test'} 43 | } 44 | ] 45 | } 46 | 47 | it 'is not empty' do 48 | expect(stub.predicates).to_not be_empty 49 | end 50 | 51 | it 'is a predicate' do 52 | expect(stub.predicates.first).to be_a Mountebank::Stub::Predicate 53 | expect(stub.predicates.first.equals[:path]).to eq('/test') 54 | end 55 | 56 | it 'renders correct JSON' do 57 | expect(stub.to_json).to eq '{"predicates":[{"equals":{"path":"/test"}}]}' 58 | end 59 | end 60 | end 61 | 62 | -------------------------------------------------------------------------------- /spec/examples_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe 'Examples' do 4 | before:each do 5 | reset_mountebank 6 | end 7 | 8 | describe 'get all imposters' do 9 | it 'should be empty' do 10 | expect(Mountebank.imposters).to be_empty 11 | end 12 | end 13 | 14 | describe 'create imposter' do 15 | it 'should create' do 16 | port = 4545 17 | protocol = Mountebank::Imposter::PROTOCOL_HTTP 18 | imposter = Mountebank::Imposter.create(port, protocol) 19 | 20 | expect(imposter.reload.requests).to be_empty 21 | test_url('http://127.0.0.1:4545') 22 | expect(imposter.reload.requests).to_not be_empty 23 | end 24 | end 25 | 26 | describe 'create imposter with stub' do 27 | it 'should have stub' do 28 | port = 4545 29 | protocol = Mountebank::Imposter::PROTOCOL_HTTP 30 | imposter = Mountebank::Imposter.build(port, protocol) 31 | 32 | # Create a response 33 | status_code = 200 34 | headers = {"Content-Type" => "application/json"} 35 | body = {foo:"bar"}.to_json 36 | response = Mountebank::Stub::HttpResponse.create(status_code, headers, body) 37 | 38 | imposter.add_stub(response) 39 | imposter.save! 40 | 41 | expect(imposter.reload.requests).to be_empty 42 | expect(test_url('http://127.0.0.1:4545')).to eq('{"foo":"bar"}') 43 | expect(imposter.reload.requests).to_not be_empty 44 | end 45 | end 46 | 47 | describe 'create imposter with stub & predicate' do 48 | it 'should have stub & predicate' do 49 | port = 4545 50 | protocol = Mountebank::Imposter::PROTOCOL_HTTP 51 | imposter = Mountebank::Imposter.build(port, protocol) 52 | 53 | # Create a response 54 | status_code = 200 55 | headers = {"Content-Type" => "application/json"} 56 | body = {foo:"bar2"}.to_json 57 | response = Mountebank::Stub::HttpResponse.create(status_code, headers, body) 58 | 59 | # Create a predicate 60 | data = {equals: {path:"/test"}} 61 | predicate = Mountebank::Stub::Predicate.new(data) 62 | 63 | imposter.add_stub(response, predicate) 64 | imposter.save! 65 | 66 | expect(imposter.reload.requests).to be_empty 67 | expect(test_url('http://127.0.0.1:4545/test')).to eq('{"foo":"bar2"}') 68 | expect(test_url('http://127.0.0.1:4545')).to eq('') 69 | expect(imposter.reload.requests).to_not be_empty 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mountebank 2 | 3 | A simple Ruby library that lets you manage your [Mountebank test server](http://www.mbtest.org/). 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | ```ruby 10 | gem 'mountebank' 11 | ``` 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install mountebank 20 | 21 | ## Usage 22 | 23 | ### Pre-Requisite 24 | 25 | Install Mountebank: 26 | 27 | ``` 28 | npm install -g mountebank --production 29 | ``` 30 | 31 | Start Mountebank: 32 | 33 | ``` 34 | mb --allowInjection 35 | ``` 36 | 37 | I recommend reading the [Mountebank documentation](http://www.mbtest.org/docs/api/overview) for a deeper understanding of their API. 38 | 39 | ### Initialization 40 | 41 | 1. Add these to you environment hash (eg. add to your `.env` file) 42 | 43 | ``` 44 | MOUNTEBANK_SERVER=127.0.0.1 45 | MOUNTEBANK_PORT=2525 46 | ``` 47 | 48 | 2. Require the lib in your `spec_helper`. 49 | 50 | ```ruby 51 | require 'mountebank' 52 | ``` 53 | 54 | ### Get all available imposters 55 | 56 | ```ruby 57 | Mountebank.imposters 58 | ``` 59 | 60 | ### Create Imposter 61 | 62 | ```ruby 63 | port = 4545 64 | protocol = Mountebank::Imposter::PROTOCOL_HTTP 65 | imposter = Mountebank::Imposter.create(port, protocol) 66 | ``` 67 | 68 | ### Create Imposter with Stub 69 | 70 | ```ruby 71 | port = 4545 72 | protocol = Mountebank::Imposter::PROTOCOL_HTTP 73 | imposter = Mountebank::Imposter.build(port, protocol) 74 | 75 | # Create a response 76 | status_code = 200 77 | headers = {"Content-Type" => "application/json"} 78 | body = {foo:"bar"}.to_json 79 | response = Mountebank::Stub::HttpResponse.create(status_code, headers, body) 80 | 81 | imposter.add_stub(response) 82 | imposter.save! 83 | ``` 84 | 85 | Check the URL: 86 | ``` 87 | curl http://127.0.0.1:4545 88 | ``` 89 | 90 | ### Get all stubs 91 | 92 | ```ruby 93 | imposter = Mountbank.imposters.first 94 | puts imposter.stubs 95 | ``` 96 | 97 | ### Create Imposter with Stub & Predicate 98 | 99 | ```ruby 100 | port = 4545 101 | protocol = Mountebank::Imposter::PROTOCOL_HTTP 102 | imposter = Mountebank::Imposter.build(port, protocol) 103 | 104 | # Create a response 105 | status_code = 200 106 | headers = {"Content-Type" => "application/json"} 107 | body = {foo:"bar2"}.to_json 108 | response = Mountebank::Stub::HttpResponse.create(status_code, headers, body) 109 | 110 | # Create a predicate 111 | data = {equals: {path:"/test"}} 112 | predicate = Mountebank::Stub::Predicate.new(data) 113 | 114 | imposter.add_stub(response, predicate) 115 | imposter.save! 116 | ``` 117 | 118 | Check the URL: 119 | ``` 120 | curl http://127.0.0.1:4545/test 121 | ``` 122 | 123 | ### Create a response with behaviors 124 | [Behaviors](http://www.mbtest.org/docs/api/behaviors) can be passed through when creating a stub http response. 125 | ```ruby 126 | response = Mountebank::Stub::HttpResponse.create(status_code, headers, body, {wait: 1000}) # Wait 1 second before responding 127 | ``` 128 | 129 | ## Running Specs 130 | 131 | The current set of specs require the Mountebank instance to be started with the `--mock` flag. 132 | 133 | ## Contributing 134 | 135 | 1. Fork it ( https://github.com/CoderKungfu/mountebank/fork ) 136 | 2. Create your feature branch (`git checkout -b my-new-feature`) 137 | 3. Commit your changes (`git commit -am 'Add some feature'`) 138 | 4. Push to the branch (`git push origin my-new-feature`) 139 | 5. Create a new Pull Request 140 | -------------------------------------------------------------------------------- /lib/mountebank/imposter.rb: -------------------------------------------------------------------------------- 1 | module Mountebank 2 | class Imposter 3 | attr_reader :port, :protocol, :name, :stubs, :requests, :matches, :mode 4 | 5 | PROTOCOL_HTTP = 'http' 6 | PROTOCOL_HTTPS = 'https' 7 | PROTOCOL_SMTP = 'smtp' 8 | PROTOCOL_TCP = 'tcp' 9 | 10 | PROTOCOLS = [ 11 | PROTOCOL_HTTP, 12 | PROTOCOL_HTTPS, 13 | PROTOCOL_SMTP, 14 | PROTOCOL_TCP 15 | ] 16 | 17 | CREATE_PARAMS_HTTP = [:protocol, :port, :name, :stubs] 18 | CREATE_PARAMS_HTTPS = [:protocol, :port, :name, :stubs] 19 | CREATE_PARAMS_TCP = [:protocol, :mode, :mode, :name, :stubs] 20 | CREATE_PARAMS_SMTP = [:protocol, :port, :name] 21 | 22 | def initialize(data={}) 23 | set_attributes(data) 24 | end 25 | 26 | def self.build(port, protocol=PROTOCOL_HTTP, options={}) 27 | raise 'Invalid port number' unless port.is_a? Integer 28 | raise 'Invalid protocol' unless PROTOCOLS.include?(protocol) 29 | 30 | data = {port: port, protocol: protocol}.merge(options) 31 | Mountebank::Imposter.new(data) 32 | end 33 | 34 | def save! 35 | delete! 36 | response = Network.post('/imposters', replayable_data) 37 | return reload if response.success? 38 | 39 | false 40 | end 41 | 42 | def self.create(port, protocol=PROTOCOL_HTTP, options={}) 43 | self.build(port, protocol, options).save! 44 | end 45 | 46 | def self.find(port) 47 | imposter_data = Imposter.get_imposter_config(port) 48 | return Mountebank::Imposter.new(imposter_data) unless imposter_data.empty? 49 | 50 | false 51 | end 52 | 53 | def self.delete(port) 54 | response = Network.delete("/imposters/#{port}") 55 | response.success? && !response.body.empty? 56 | end 57 | 58 | def delete! 59 | Imposter.delete(@port) 60 | end 61 | 62 | def reload 63 | data = Imposter.get_imposter_config(@port) 64 | set_attributes(data) unless data.empty? 65 | 66 | self 67 | end 68 | 69 | def add_stub(response=nil, predicate=nil) 70 | responses, predicates = [], [] 71 | 72 | if response.is_a? Array 73 | responses = response 74 | elsif response.is_a? Mountebank::Stub::Response 75 | responses << response 76 | end 77 | 78 | if predicate.is_a? Array 79 | predicates = predicate 80 | elsif predicate.is_a? Mountebank::Stub::Predicate 81 | predicates << predicate 82 | end 83 | 84 | @stubs << Mountebank::Stub.create(responses, predicates) 85 | end 86 | 87 | def replayable_data 88 | data = serializable_hash 89 | data.delete(:requests) 90 | data.delete(:matches) 91 | 92 | data 93 | end 94 | 95 | def to_json(*args) 96 | serializable_hash.to_json(*args) 97 | end 98 | 99 | private 100 | 101 | def serializable_hash 102 | data = {port: @port, protocol: @protocol, name: @name} 103 | data[:stubs] = @stubs unless @stubs.empty? 104 | data[:requests] = @requests unless @requests.empty? 105 | data[:matches] = @matches unless @matches.empty? 106 | data[:mode] = @mode unless @mode.nil? 107 | 108 | data 109 | end 110 | 111 | def self.get_imposter_config(port) 112 | response = Network.get("/imposters/#{port}") 113 | response.success? ? response.body : [] 114 | end 115 | 116 | def set_attributes(data) 117 | @port = data[:port] 118 | @protocol = data[:protocol] 119 | @name = data[:name] || "imposter_#{@port}" 120 | @stubs = [] 121 | if data[:stubs].respond_to?(:each) 122 | data[:stubs].each do |stub| 123 | stub = Mountebank::Stub.new(stub) unless stub.is_a? Mountebank::Stub 124 | @stubs << stub 125 | end 126 | end 127 | @requests = data[:requests] || [] 128 | @matches = data[:matches] || [] 129 | @mode = data[:mode] || nil 130 | end 131 | end 132 | end 133 | -------------------------------------------------------------------------------- /spec/mountebank/imposter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Mountebank::Imposter do 4 | before:each do 5 | reset_mountebank 6 | end 7 | 8 | let(:port) { 4545 } 9 | let(:protocol) { Mountebank::Imposter::PROTOCOL_HTTP } 10 | 11 | shared_examples 'blank imposter' do 12 | it 'valid imposter' do 13 | expect(imposter).to be_a Mountebank::Imposter 14 | expect(imposter.port).to eq port 15 | expect(imposter.protocol).to eq protocol 16 | expect(imposter.name).to eq "imposter_#{port}" 17 | expect(imposter.stubs).to be_empty 18 | expect(imposter.requests).to be_empty 19 | expect(imposter.mode).to be_nil 20 | end 21 | end 22 | 23 | shared_examples 'persists imposter' do 24 | it 'persist to server' do 25 | imposter 26 | expect(Mountebank.imposters).to_not be_empty 27 | end 28 | end 29 | 30 | describe '.build' do 31 | let(:imposter) { Mountebank::Imposter.build(port, protocol) } 32 | 33 | it_should_behave_like 'blank imposter' 34 | end 35 | 36 | describe '.create' do 37 | context 'new imposter' do 38 | let(:imposter) { Mountebank::Imposter.create(port, protocol) } 39 | 40 | it_should_behave_like 'blank imposter' 41 | it_should_behave_like 'persists imposter' 42 | end 43 | 44 | context 'assumes 2nd argument to be `http`' do 45 | let(:imposter) { Mountebank::Imposter.create(port) } 46 | 47 | it_should_behave_like 'blank imposter' 48 | it_should_behave_like 'persists imposter' 49 | end 50 | 51 | context 'other creation options' do 52 | let(:imposter) { Mountebank::Imposter.create(port, protocol, name:'meow_server') } 53 | 54 | it 'uses a different name' do 55 | expect(imposter.name).to eq 'meow_server' 56 | end 57 | end 58 | 59 | context 'invalid arguments' do 60 | it 'raises invalid port' do 61 | expect{ Mountebank::Imposter.create('abcd') }.to raise_error 'Invalid port number' 62 | end 63 | 64 | it 'raises invalid protocol' do 65 | expect{ Mountebank::Imposter.create(port, 'seattle') }.to raise_error 'Invalid protocol' 66 | end 67 | end 68 | 69 | context 'creates stub response' do 70 | let(:responses) { [ 71 | {is: {statusCode: 200, body:"ohai"}} 72 | ] 73 | } 74 | let(:predicates) { [] } 75 | let(:stubs) { [ 76 | Mountebank::Stub.create(responses, predicates) 77 | ] 78 | } 79 | let!(:imposter) { Mountebank::Imposter.create(port, protocol, stubs:stubs) } 80 | 81 | it 'is valid' do 82 | expect(test_url('http://127.0.0.1:4545')).to eq 'ohai' 83 | expect(imposter.reload.requests).to_not be_empty 84 | expect(imposter.stubs.first).to be_a Mountebank::Stub 85 | end 86 | 87 | context 'with predicates' do 88 | let(:response_body) { "Its a real test" } 89 | let(:responses) { [ 90 | {is: {statusCode: 200, body:response_body}} 91 | ] 92 | } 93 | let(:predicates) { [ 94 | {equals: {path:'/test'}} 95 | ] 96 | } 97 | 98 | it 'is valid' do 99 | expect(test_url('http://127.0.0.1:4545/test')).to eq response_body 100 | end 101 | end 102 | end 103 | end 104 | 105 | describe '.get' do 106 | before do 107 | Mountebank::Imposter.create(port) 108 | end 109 | 110 | context 'valid imposter' do 111 | let(:imposter) { Mountebank::Imposter.find(port) } 112 | 113 | it_should_behave_like 'blank imposter' 114 | it_should_behave_like 'persists imposter' 115 | end 116 | 117 | context 'unknown imposter' do 118 | it 'returns false' do 119 | expect(Mountebank::Imposter.find(4546)).to_not be 120 | end 121 | end 122 | end 123 | 124 | describe '.delete' do 125 | before do 126 | Mountebank::Imposter.create(port) 127 | end 128 | 129 | context 'has imposter' do 130 | it 'returns true' do 131 | expect(Mountebank::Imposter.delete(port)).to be 132 | end 133 | end 134 | 135 | context 'no imposter' do 136 | it 'returns false' do 137 | expect(Mountebank::Imposter.delete(4546)).to_not be 138 | end 139 | end 140 | end 141 | 142 | describe '#reload' do 143 | before do 144 | Mountebank::Imposter.create(port) 145 | end 146 | 147 | let!(:imposter) { Mountebank::Imposter.find(port) } 148 | 149 | context 'no change' do 150 | it 'returns imposter' do 151 | expect(imposter.reload).to be_a Mountebank::Imposter 152 | end 153 | 154 | it_should_behave_like 'blank imposter' 155 | it_should_behave_like 'persists imposter' 156 | end 157 | 158 | context 'has requests' do 159 | it 'returns imposter with requests' do 160 | test_url('http://127.0.0.1:4545') 161 | expect(imposter.reload.requests).to_not be_empty 162 | end 163 | end 164 | end 165 | 166 | describe '#add_stub' do 167 | let(:imposter) { Mountebank::Imposter.build(port, protocol) } 168 | 169 | context 'with response' do 170 | before do 171 | response = Mountebank::Stub::HttpResponse.create(200, {}, 'ohai you') 172 | imposter.add_stub(response) 173 | end 174 | 175 | it 'adds new stub' do 176 | expect(imposter.to_json).to eq("{\"port\":#{port},\"protocol\":\"#{protocol}\",\"name\":\"imposter_#{port}\",\"stubs\":[{\"responses\":[{\"is\":{\"statusCode\":200,\"body\":\"ohai you\"}}]}]}") 177 | end 178 | 179 | it 'is valid imposter' do 180 | imposter.save! 181 | expect(test_url('http://127.0.0.1:4545')).to eq('ohai you') 182 | end 183 | end 184 | 185 | context 'with predicate' do 186 | before do 187 | response = Mountebank::Stub::HttpResponse.create(200, {}, 'ohai test2') 188 | data = {equals: {path:'/test2'}} 189 | predicate = Mountebank::Stub::Predicate.new(data) 190 | imposter.add_stub(response, predicate) 191 | end 192 | 193 | it 'is valid imposter' do 194 | imposter.save! 195 | expect(test_url('http://127.0.0.1:4545/test2')).to eq('ohai test2') 196 | end 197 | end 198 | 199 | context 'with array of responses' do 200 | before do 201 | response1 = Mountebank::Stub::HttpResponse.create(200, {}, 'hello mother') 202 | response2 = Mountebank::Stub::HttpResponse.create(200, {}, 'hello father') 203 | data = {equals: {path:'/test3'}} 204 | predicate = Mountebank::Stub::Predicate.new(data) 205 | imposter.add_stub([response1, response2], predicate) 206 | imposter.save! 207 | end 208 | 209 | it 'should save stub in memory with 2 responses' do 210 | expect(imposter.stubs.first.responses.length).to eq(2) 211 | end 212 | 213 | it 'should save stub to server with 2 responses' do 214 | stub = Mountebank::Imposter.get_imposter_config(port)[:stubs].first 215 | expect(stub[:responses].length).to eq(2) 216 | end 217 | 218 | it 'is a valid imposter with 2 responses' do 219 | expect(test_url('http://127.0.0.1:4545/test3')).to eq('hello mother') 220 | expect(test_url('http://127.0.0.1:4545/test3')).to eq('hello father') 221 | end 222 | end 223 | 224 | context 'with array of predicates' do 225 | before do 226 | response1 = Mountebank::Stub::HttpResponse.create(200, {}, 'hello mother') 227 | data1 = {equals: {path:'/test3'}} 228 | data2 = {equals: {method:'GET'}} 229 | predicate1 = Mountebank::Stub::Predicate.new(data1) 230 | predicate2 = Mountebank::Stub::Predicate.new(data2) 231 | imposter.add_stub(response1, [predicate1, predicate2]) 232 | imposter.save! 233 | end 234 | 235 | it 'should save stub in memory with 2 predicates' do 236 | expect(imposter.stubs.first.predicates.length).to eq(2) 237 | end 238 | 239 | it 'should save stub to server with 2 predicates' do 240 | stub = Mountebank::Imposter.get_imposter_config(port)[:stubs].first 241 | expect(stub[:predicates].length).to eq(2) 242 | end 243 | 244 | it 'is a valid imposter' do 245 | expect(test_url('http://127.0.0.1:4545/test3')).to eq('hello mother') 246 | end 247 | 248 | end 249 | 250 | end 251 | 252 | describe '#replayable_data' do 253 | let(:imposter) { Mountebank::Imposter.build(port, protocol) } 254 | 255 | it 'returns valid data' do 256 | expect(imposter.replayable_data).to eq({port:port, protocol:protocol, name:"imposter_#{port}"}) 257 | end 258 | end 259 | end --------------------------------------------------------------------------------