mirage-on-thin 3.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.simplecov +6 -0
- data/.travis.yml +3 -0
- data/Gemfile +29 -0
- data/Gemfile.lock +151 -0
- data/HISTORY +22 -0
- data/README.md +156 -0
- data/Rakefile +10 -0
- data/VERSION +1 -0
- data/bin/mirage +14 -0
- data/features/.nav +29 -0
- data/features/client/clear.feature +78 -0
- data/features/client/configure.feature +72 -0
- data/features/client/model.feature +95 -0
- data/features/client/preview_responses.feature +33 -0
- data/features/client/prime.feature +32 -0
- data/features/client/put.feature +111 -0
- data/features/client/readme.md +3 -0
- data/features/client/requests.feature +20 -0
- data/features/client/running.feature +51 -0
- data/features/client/save_and_revert.feature +39 -0
- data/features/client/start.feature +46 -0
- data/features/client/stop.feature +53 -0
- data/features/commandline_interface/help.feature +17 -0
- data/features/commandline_interface/readme.md +1 -0
- data/features/commandline_interface/start.feature +18 -0
- data/features/commandline_interface/stop.feature +42 -0
- data/features/logging.feature +6 -0
- data/features/prime.feature +35 -0
- data/features/readme.md +7 -0
- data/features/requests/delete.feature +48 -0
- data/features/requests/get.feature +36 -0
- data/features/save_and_revert.feature +35 -0
- data/features/step_definitions/my_steps.rb +98 -0
- data/features/step_definitions/observation_steps.rb +103 -0
- data/features/support/command_line.rb +33 -0
- data/features/support/env.rb +22 -0
- data/features/support/hooks.rb +26 -0
- data/features/support/mirage.rb +12 -0
- data/features/support/web.rb +20 -0
- data/features/templates/delete.feature +45 -0
- data/features/templates/get.feature +54 -0
- data/features/templates/path_wildcards.feature +10 -0
- data/features/templates/preview.feature +18 -0
- data/features/templates/put.feature +77 -0
- data/features/templates/put_with_substitutions.feature +22 -0
- data/features/templates/readme.md +4 -0
- data/features/templates/required_content.feature +113 -0
- data/features/web_user_interface.feature +44 -0
- data/full_build.sh +100 -0
- data/lib/mirage/client.rb +10 -0
- data/lib/mirage/client/cli_bridge.rb +30 -0
- data/lib/mirage/client/client.rb +73 -0
- data/lib/mirage/client/error.rb +22 -0
- data/lib/mirage/client/helpers/method_builder.rb +19 -0
- data/lib/mirage/client/request.rb +26 -0
- data/lib/mirage/client/requests.rb +13 -0
- data/lib/mirage/client/runner.rb +103 -0
- data/lib/mirage/client/template.rb +56 -0
- data/lib/mirage/client/template/configuration.rb +44 -0
- data/lib/mirage/client/template/model.rb +48 -0
- data/lib/mirage/client/template/model/common_methods.rb +24 -0
- data/lib/mirage/client/template/model/instance_methods.rb +95 -0
- data/lib/mirage/client/templates.rb +50 -0
- data/mirage-on-thin.gemspec +175 -0
- data/mirage_server.rb +35 -0
- data/server/app.rb +4 -0
- data/server/binary_data_checker.rb +15 -0
- data/server/extensions/hash.rb +10 -0
- data/server/extensions/object.rb +5 -0
- data/server/helpers.rb +3 -0
- data/server/helpers/http_headers.rb +31 -0
- data/server/helpers/template_requirements.rb +33 -0
- data/server/mock_response.rb +242 -0
- data/server/server.rb +184 -0
- data/spec/client/cli_bridge_spec.rb +63 -0
- data/spec/client/client_spec.rb +179 -0
- data/spec/client/helpers/method_builder_spec.rb +40 -0
- data/spec/client/request_spec.rb +39 -0
- data/spec/client/requests_spec.rb +9 -0
- data/spec/client/runner_spec.rb +138 -0
- data/spec/client/template/configuration_spec.rb +32 -0
- data/spec/client/template/model/common_methods_spec.rb +25 -0
- data/spec/client/template/model/instance_methods_spec.rb +169 -0
- data/spec/client/template/model_spec.rb +119 -0
- data/spec/client/template_spec.rb +146 -0
- data/spec/client/templates_spec.rb +197 -0
- data/spec/resources/binary.file +0 -0
- data/spec/server/binary_data_checker_spec.rb +21 -0
- data/spec/server/helpers/http_headers_spec.rb +20 -0
- data/spec/server/helpers/template_requirements_spec.rb +34 -0
- data/spec/server/mock_response_spec.rb +577 -0
- data/spec/server/server_spec.rb +156 -0
- data/spec/spec_helper.rb +85 -0
- data/tasks/application.rake +7 -0
- data/tasks/packaging.rake +28 -0
- data/tasks/tests.rake +25 -0
- data/views/index.haml +16 -0
- data/views/response.haml +46 -0
- metadata +337 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module Mirage
|
2
|
+
class MirageError < ::Exception
|
3
|
+
attr_reader :code
|
4
|
+
|
5
|
+
def initialize message, code
|
6
|
+
super message
|
7
|
+
@code = message, code
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class InternalServerException < MirageError
|
12
|
+
end
|
13
|
+
|
14
|
+
class TemplateNotFound < ::Exception
|
15
|
+
end
|
16
|
+
|
17
|
+
class ClientError < ::Exception
|
18
|
+
def initialize message
|
19
|
+
super message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Mirage
|
2
|
+
module Helpers
|
3
|
+
module MethodBuilder
|
4
|
+
def builder_methods *method_names
|
5
|
+
|
6
|
+
method_names.each do |method_name|
|
7
|
+
method_name = method_name.to_sym
|
8
|
+
define_method method_name do |arg=nil|
|
9
|
+
return instance_variable_get("@#{method_name}".to_sym) if arg.nil?
|
10
|
+
instance_variable_set("@#{method_name}".to_sym, arg)
|
11
|
+
self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
alias builder_method builder_methods
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'hashie/mash'
|
2
|
+
module Mirage
|
3
|
+
class Request
|
4
|
+
include HTTParty
|
5
|
+
|
6
|
+
class << self
|
7
|
+
alias_method :backedup_get, :get
|
8
|
+
def get url
|
9
|
+
result = Hashie::Mash.new(backedup_get(url, format: :json))
|
10
|
+
request = new
|
11
|
+
request.parameters = result.parameters
|
12
|
+
request.headers = result.headers
|
13
|
+
request.request_url = result.request_url
|
14
|
+
request.body = result.body
|
15
|
+
request.id = result.id
|
16
|
+
request
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :parameters, :headers, :body, :request_url, :id
|
21
|
+
|
22
|
+
def delete
|
23
|
+
self.class.delete(id)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'waitforit'
|
3
|
+
require 'childprocess'
|
4
|
+
require 'uri'
|
5
|
+
require 'httparty'
|
6
|
+
module Mirage
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# Start Mirage locally on a given port
|
10
|
+
# Example Usage:
|
11
|
+
#
|
12
|
+
# Mirage.start :port => 9001 -> Configured MirageClient ready to use.
|
13
|
+
def start options={}
|
14
|
+
options={:port => 7001}.merge(options)
|
15
|
+
Runner.new.invoke(:start, [], options)
|
16
|
+
Mirage::Client.new(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Stop locally running instance(s) of Mirage
|
20
|
+
#
|
21
|
+
# Example Usage:
|
22
|
+
# Mirage.stop -> Will stop mirage if there is only instance running. Can be running on any port.
|
23
|
+
# Mirage.stop :port => port -> stop mirage on a given port
|
24
|
+
# Mirage.stop :port => [port1, port2...] -> stops multiple running instances of Mirage
|
25
|
+
def stop options={:port => []}
|
26
|
+
options = {:port => :all} if options == :all
|
27
|
+
|
28
|
+
if options[:port]
|
29
|
+
options[:port] = [options[:port]] unless options[:port].is_a?(Array)
|
30
|
+
end
|
31
|
+
|
32
|
+
Runner.new.invoke(:stop, [], options)
|
33
|
+
rescue ClientError => e
|
34
|
+
raise ClientError.new("Mirage is running multiple ports, please specify the port(s) see api/tests for details")
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Detect if Mirage is running on a URL or a local port
|
39
|
+
#
|
40
|
+
# Example Usage:
|
41
|
+
# Mirage.running? -> boolean indicating whether Mirage is running on *locally* on port 7001
|
42
|
+
# Mirage.running? :port => port -> boolean indicating whether Mirage is running on *locally* on the given port
|
43
|
+
# Mirage.running? url -> boolean indicating whether Mirage is running on the given URL
|
44
|
+
def running? options_or_url = {:port => 7001}
|
45
|
+
url = options_or_url.kind_of?(Hash) ? "http://localhost:#{options_or_url[:port]}" : options_or_url
|
46
|
+
HTTParty.get(url) and return true
|
47
|
+
rescue Errno::ECONNREFUSED
|
48
|
+
return false
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class Runner < Thor
|
54
|
+
include CLIBridge
|
55
|
+
RUBY_CMD = ChildProcess.jruby? ? 'jruby' : 'ruby'
|
56
|
+
|
57
|
+
desc "start", "Starts mirage"
|
58
|
+
method_option :port, :aliases => "-p", :type => :numeric, :default => 7001, :desc => "port that mirage should be started on"
|
59
|
+
method_option :defaults, :aliases => "-d", :type => :string, :default => 'mirage', :desc => "location to load default responses from"
|
60
|
+
method_option :debug, :type => :boolean, :default => false, :desc => "run in debug mode"
|
61
|
+
|
62
|
+
def start
|
63
|
+
port = options[:port]
|
64
|
+
process_ids = mirage_process_ids([port])
|
65
|
+
unless process_ids.empty?
|
66
|
+
warn "Mirage is already running: #{process_ids.values.join(",")}"
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
mirage_server_file = "#{File.dirname(__FILE__)}/../../../mirage_server.rb"
|
71
|
+
|
72
|
+
if ChildProcess.windows?
|
73
|
+
command = ["cmd", "/C", "start", "mirage server port #{port}", RUBY_CMD, mirage_server_file]
|
74
|
+
else
|
75
|
+
command = [RUBY_CMD, mirage_server_file]
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
command = command.concat(options.to_a).flatten.collect { |arg| arg.to_s }
|
80
|
+
ChildProcess.build(*command).start
|
81
|
+
|
82
|
+
wait_until(:timeout_after => 30.seconds) { Mirage.running?(options) }
|
83
|
+
|
84
|
+
begin
|
85
|
+
Mirage::Client.new(options).prime
|
86
|
+
rescue Mirage::InternalServerException => e
|
87
|
+
puts "WARN: #{e.message}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "stop", "Stops mirage"
|
92
|
+
method_option :port, :aliases => "-p", :type => :array, :default => [], :banner => "[port_1 port_2|all]", :desc => "port(s) of mirage instance(s). ALL stops all running instances"
|
93
|
+
|
94
|
+
def stop
|
95
|
+
ports = options[:port].collect{|port| port=~/\d+/ ? port.to_i : port}
|
96
|
+
process_ids = mirage_process_ids(ports)
|
97
|
+
raise ClientError.new("Mirage is running on ports #{process_ids.keys.sort.join(", ")}. Please run mirage stop -p [PORT(s)] instead") if (process_ids.size > 1 && ports.empty?)
|
98
|
+
process_ids.values.each { |process_id| kill process_id }
|
99
|
+
wait_until { mirage_process_ids(options[:port]).empty? }
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'json'
|
3
|
+
require 'httparty'
|
4
|
+
require 'hashie/mash'
|
5
|
+
|
6
|
+
require 'client/template/configuration'
|
7
|
+
require 'client/template/model'
|
8
|
+
|
9
|
+
module Mirage
|
10
|
+
|
11
|
+
class Template
|
12
|
+
|
13
|
+
include HTTParty
|
14
|
+
include Model::InstanceMethods
|
15
|
+
include Model::CommonMethods
|
16
|
+
|
17
|
+
class << self
|
18
|
+
alias_method :backedup_get, :get
|
19
|
+
|
20
|
+
def get url
|
21
|
+
response = backedup_get(url, :format => :json)
|
22
|
+
raise TemplateNotFound if response.code == 404
|
23
|
+
response_hashie = Hashie::Mash.new response
|
24
|
+
|
25
|
+
response_config = response_hashie.response
|
26
|
+
request_config = response_hashie.request
|
27
|
+
|
28
|
+
template = new(response_hashie.endpoint, Base64.decode64(response_config.body))
|
29
|
+
|
30
|
+
template.id response_hashie.id
|
31
|
+
template.default response_config['default']
|
32
|
+
template.delay response_config.delay
|
33
|
+
template.content_type response_config.content_type
|
34
|
+
template.status response_config.status
|
35
|
+
template.headers response_config.headers
|
36
|
+
|
37
|
+
template.required_parameters request_config.parameters
|
38
|
+
template.required_body_content request_config.body_content
|
39
|
+
template.http_method request_config.http_method
|
40
|
+
template.url url
|
41
|
+
template.requests_url response_hashie.requests_url
|
42
|
+
template.required_headers request_config.headers
|
43
|
+
|
44
|
+
template
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize *args
|
49
|
+
endpoint = args.first
|
50
|
+
|
51
|
+
raise ArgumentError, "You must specify a string endpoint as the first argument" unless endpoint && endpoint.is_a?(String)
|
52
|
+
super *args
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'client/helpers/method_builder'
|
2
|
+
|
3
|
+
module Mirage
|
4
|
+
class Template
|
5
|
+
class Configuration
|
6
|
+
extend Helpers::MethodBuilder
|
7
|
+
builder_methods :http_method, :status, :delay, :content_type, :default
|
8
|
+
attr_accessor :caller_binding
|
9
|
+
DEFAULT_HTTP_METHOD=:get
|
10
|
+
DEFAULT_STATUS=200
|
11
|
+
DEFAULT_DELAY=0
|
12
|
+
DEFAULT_CONTENT_TYPE="text/plain"
|
13
|
+
DEFAULT_DEFAULT=false
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
reset
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset
|
20
|
+
@http_method = DEFAULT_HTTP_METHOD
|
21
|
+
@status = DEFAULT_STATUS
|
22
|
+
@delay = DEFAULT_DELAY
|
23
|
+
@content_type = DEFAULT_CONTENT_TYPE
|
24
|
+
@default = DEFAULT_DEFAULT
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def method_missing(method, *args, &block)
|
29
|
+
@caller_binding.send method, *args, &block if @caller_binding
|
30
|
+
end
|
31
|
+
|
32
|
+
def == config
|
33
|
+
config.is_a?(Configuration) &&
|
34
|
+
http_method == config.http_method &&
|
35
|
+
status == config.status &&
|
36
|
+
delay == config.delay &&
|
37
|
+
content_type == config.content_type &&
|
38
|
+
default == config.default
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'client/helpers/method_builder'
|
2
|
+
require 'client/template/model/common_methods'
|
3
|
+
require 'client/template/model/instance_methods'
|
4
|
+
|
5
|
+
module Mirage
|
6
|
+
class Template
|
7
|
+
module Model
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def extended clazz
|
11
|
+
clazz.extend(CommonMethods)
|
12
|
+
clazz.extend(Helpers::MethodBuilder)
|
13
|
+
clazz.send(:include, HTTParty)
|
14
|
+
clazz.send(:include, CommonMethods)
|
15
|
+
clazz.send(:include, InstanceMethods)
|
16
|
+
|
17
|
+
|
18
|
+
mod = Module.new do
|
19
|
+
def initialize *args
|
20
|
+
|
21
|
+
super *args
|
22
|
+
[:content_type,
|
23
|
+
:http_method,
|
24
|
+
:default,
|
25
|
+
:status,
|
26
|
+
:delay,
|
27
|
+
:required_parameters,
|
28
|
+
:required_body_content,
|
29
|
+
:required_headers,
|
30
|
+
:headers,
|
31
|
+
:endpoint, :delay].each do |attribute|
|
32
|
+
eval("#{attribute} self.class.#{attribute} if self.class.#{attribute}")
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
clazz.send(:include, mod)
|
40
|
+
|
41
|
+
clazz.format :json
|
42
|
+
clazz
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Mirage
|
2
|
+
|
3
|
+
class Template
|
4
|
+
module Model
|
5
|
+
module CommonMethods
|
6
|
+
extend Helpers::MethodBuilder
|
7
|
+
|
8
|
+
builder_methods :content_type,
|
9
|
+
:http_method,
|
10
|
+
:default,
|
11
|
+
:status,
|
12
|
+
:delay,
|
13
|
+
:required_parameters,
|
14
|
+
:required_body_content,
|
15
|
+
:required_headers,
|
16
|
+
:headers,
|
17
|
+
:endpoint,
|
18
|
+
:body
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Mirage
|
2
|
+
class Template
|
3
|
+
module Model
|
4
|
+
module InstanceMethods
|
5
|
+
extend Helpers::MethodBuilder
|
6
|
+
include CommonMethods
|
7
|
+
|
8
|
+
attr_accessor :caller_binding
|
9
|
+
|
10
|
+
def initialize *args
|
11
|
+
if args.last.is_a?(Template::Configuration)
|
12
|
+
default_config = args.delete_at(-1)
|
13
|
+
else
|
14
|
+
default_config = Template::Configuration.new
|
15
|
+
end
|
16
|
+
|
17
|
+
@endpoint, @body = *args
|
18
|
+
@content_type = default_config.content_type
|
19
|
+
@http_method = default_config.http_method
|
20
|
+
@status = default_config.status
|
21
|
+
@delay = default_config.delay
|
22
|
+
@required_parameters = {}
|
23
|
+
@required_headers = {}
|
24
|
+
@required_body_content = []
|
25
|
+
@headers = {}
|
26
|
+
@default = default_config.default
|
27
|
+
end
|
28
|
+
|
29
|
+
builder_method :id,
|
30
|
+
:url,
|
31
|
+
:requests_url
|
32
|
+
|
33
|
+
|
34
|
+
def create
|
35
|
+
@id = self.class.put("#{@endpoint}", :body => self.to_json, :headers => {'content-type' => 'application/json'})['id']
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete
|
40
|
+
self.class.delete(url)
|
41
|
+
Request.delete requests_url
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def to_json
|
46
|
+
{
|
47
|
+
:response => {
|
48
|
+
:body => Base64.encode64(body),
|
49
|
+
:status => status,
|
50
|
+
:default => default,
|
51
|
+
:content_type => content_type,
|
52
|
+
:headers => headers,
|
53
|
+
:delay => delay
|
54
|
+
},
|
55
|
+
:request => {
|
56
|
+
:parameters => encode_regexs(required_parameters),
|
57
|
+
:headers => encode_regexs(required_headers),
|
58
|
+
:body_content => encode_regexs(required_body_content),
|
59
|
+
:http_method => http_method,
|
60
|
+
}
|
61
|
+
}.to_json
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def encode_regexs hash_or_array
|
66
|
+
case hash_or_array
|
67
|
+
when Array
|
68
|
+
hash_or_array.collect { |value| encode(value) }
|
69
|
+
else
|
70
|
+
encoded = {}
|
71
|
+
hash_or_array.each do |key, value|
|
72
|
+
encoded[key] = encode(value)
|
73
|
+
end
|
74
|
+
encoded
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def encode(value)
|
79
|
+
value.is_a?(Regexp) ? "%r{#{value.source}}" : value
|
80
|
+
end
|
81
|
+
|
82
|
+
def method_missing(method, *args, &block)
|
83
|
+
|
84
|
+
if @caller_binding
|
85
|
+
@caller_binding.send method, *args, &block
|
86
|
+
else
|
87
|
+
super method, *args, &block
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|