mirage 2.2.3 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/Gemfile.lock +11 -2
- data/HISTORY +1 -0
- data/VERSION +1 -1
- data/bin/mirage +6 -8
- data/features/client/clear.feature +8 -8
- data/features/client/{response.feature → preview_responses.feature} +0 -0
- data/features/client/put.feature +9 -1
- data/features/client/{request.feature → requests.feature} +0 -0
- data/features/client/running.feature +49 -0
- data/features/client/{command_line_interface.feature → start.feature} +0 -12
- data/features/client/stop.feature +85 -0
- data/features/server/commandline_interface/help.feature +16 -0
- data/features/server/commandline_interface/start.feature +30 -0
- data/features/server/commandline_interface/stop.feature +42 -0
- data/features/server/prime.feature +8 -7
- data/features/step_definitions/my_steps.rb +20 -16
- data/features/support/command_line.rb +22 -0
- data/features/support/env.rb +11 -121
- data/features/support/hooks.rb +30 -0
- data/features/support/mirage.rb +8 -0
- data/lib/mirage/client/client.rb +124 -0
- data/lib/mirage/client/error.rb +22 -0
- data/lib/mirage/client/response.rb +29 -0
- data/lib/mirage/client/runner.rb +142 -0
- data/lib/mirage/client.rb +4 -206
- data/mirage.gemspec +25 -8
- data/mirage_server.rb +15 -10
- data/rakefile +7 -1
- data/spec/running_via_api_spec.rb +147 -0
- data/spec/running_via_api_windows_spec.rb +187 -0
- data/test.rb +21 -4
- metadata +66 -33
- data/features/client/mirage_client.feature +0 -36
- data/features/server/command_line_iterface.feature +0 -45
- data/lib/mirage/cli.rb +0 -69
data/features/support/env.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
ROOT_DIR = File.expand_path("#{File.dirname(__FILE__)}/../..")
|
2
|
+
SOURCE_PATH = "#{ROOT_DIR}/lib"
|
3
|
+
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(SOURCE_PATH)
|
3
6
|
require 'rubygems'
|
4
7
|
require 'mirage/client'
|
5
8
|
require 'cucumber'
|
@@ -7,130 +10,17 @@ require 'rspec'
|
|
7
10
|
require 'mechanize'
|
8
11
|
require 'childprocess'
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
ENV['OS'] == 'Windows_NT'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
World OsSupport
|
18
|
-
include OsSupport
|
19
|
-
|
20
|
-
SCRATCH = './scratch'
|
21
|
-
RUBY_CMD = RUBY_PLATFORM == 'JAVA' ? 'jruby' : 'ruby'
|
13
|
+
SCRATCH = "#{ROOT_DIR}/scratch"
|
14
|
+
RUBY_CMD = ChildProcess.jruby? ? 'jruby' : 'ruby'
|
15
|
+
BLANK_RUBYOPT_CMD = ChildProcess.windows? ? 'set RUBYOPT=' : "export RUBYOPT=''"
|
16
|
+
ENV['RUBYOPT'] = ''
|
22
17
|
|
23
18
|
|
24
|
-
BLANK_RUBYOPT_CMD = windows? ? 'set RUBYOPT=' : "export RUBYOPT=''"
|
25
|
-
|
26
19
|
if 'regression' == ENV['mode']
|
27
|
-
MIRAGE_CMD = windows? ? `where mirage.bat`.chomp : 'mirage'
|
20
|
+
MIRAGE_CMD = ChildProcess.windows? ? `where mirage.bat`.chomp : 'mirage'
|
28
21
|
else
|
29
22
|
MIRAGE_CMD = "#{RUBY_CMD} ../bin/mirage"
|
30
23
|
end
|
31
24
|
|
25
|
+
World(Mirage::Web)
|
32
26
|
|
33
|
-
|
34
|
-
module CommandLine
|
35
|
-
COMAND_LINE_OUTPUT_PATH = "#{File.dirname(__FILE__)}/../../#{SCRATCH}/commandline_output.txt"
|
36
|
-
module Windows
|
37
|
-
def run command
|
38
|
-
command = "#{MIRAGE_CMD} #{command.split(' ').drop(1).join(' ')}" if command =~ /^mirage/
|
39
|
-
command = "#{command} > #{COMAND_LINE_OUTPUT_PATH}"
|
40
|
-
Dir.chdir(SCRATCH)
|
41
|
-
`#{BLANK_RUBYOPT_CMD}`
|
42
|
-
process = ChildProcess.build(*(command.split(' ')))
|
43
|
-
process.start
|
44
|
-
sleep 0.5 until process.exited?
|
45
|
-
Dir.chdir('../')
|
46
|
-
File.read(COMAND_LINE_OUTPUT_PATH)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
module Linux
|
51
|
-
def run command
|
52
|
-
`#{BLANK_RUBYOPT_CMD} && cd #{SCRATCH} && #{command}`
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
module Web
|
59
|
-
include Mirage::Web
|
60
|
-
|
61
|
-
def normalise text
|
62
|
-
text.gsub(/[\n]/, ' ').gsub(/\s+/, ' ')
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
module Regression
|
68
|
-
include CommandLine
|
69
|
-
|
70
|
-
def run command
|
71
|
-
execute(command)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
module Mirage
|
76
|
-
module Runner
|
77
|
-
def stop_mirage
|
78
|
-
system "cd #{SCRATCH} && #{MIRAGE_CMD} stop"
|
79
|
-
end
|
80
|
-
|
81
|
-
def start_mirage
|
82
|
-
if windows?
|
83
|
-
|
84
|
-
puts "starting mirage"
|
85
|
-
Dir.chdir(SCRATCH)
|
86
|
-
process = ChildProcess.build(MIRAGE_CMD, "start")
|
87
|
-
process.start
|
88
|
-
sleep 0.5 until process.exited?
|
89
|
-
Dir.chdir '../'
|
90
|
-
puts "finished starting mirage"
|
91
|
-
else
|
92
|
-
system "cd #{SCRATCH} && #{MIRAGE_CMD} start"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
module IntelliJ
|
100
|
-
include CommandLine
|
101
|
-
def run command
|
102
|
-
execute "#{RUBY_CMD} #{command}"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
include Mirage::Runner
|
107
|
-
|
108
|
-
World(Web)
|
109
|
-
World(Mirage::Runner)
|
110
|
-
windows? ? World(CommandLine::Windows) : World(CommandLine::Linux)
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
Before do
|
115
|
-
FileUtils.mkdir_p(SCRATCH)
|
116
|
-
$mirage = Mirage::Client.new
|
117
|
-
if $mirage.running?
|
118
|
-
$mirage.clear
|
119
|
-
else
|
120
|
-
start_mirage
|
121
|
-
end
|
122
|
-
|
123
|
-
Dir["#{SCRATCH}/*"].each do |file|
|
124
|
-
FileUtils.rm_rf(file) unless file == "#{SCRATCH}/mirage.log"
|
125
|
-
end
|
126
|
-
|
127
|
-
if File.exists? "#{SCRATCH}/mirage.log"
|
128
|
-
@mirage_log_file = File.open("#{SCRATCH}/mirage.log")
|
129
|
-
@mirage_log_file.seek(0, IO::SEEK_END)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
|
134
|
-
at_exit do
|
135
|
-
stop_mirage if $mirage.running?
|
136
|
-
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Before do
|
2
|
+
FileUtils.mkdir_p(SCRATCH)
|
3
|
+
|
4
|
+
if Mirage.running?
|
5
|
+
$mirage.clear
|
6
|
+
else
|
7
|
+
$mirage = start_mirage_in_scratch_dir
|
8
|
+
end
|
9
|
+
|
10
|
+
Dir["#{SCRATCH}/*"].each do |file|
|
11
|
+
FileUtils.rm_rf(file) unless file == "#{SCRATCH}/mirage.log"
|
12
|
+
end
|
13
|
+
|
14
|
+
if File.exists? "#{SCRATCH}/mirage.log"
|
15
|
+
@mirage_log_file = File.open("#{SCRATCH}/mirage.log")
|
16
|
+
@mirage_log_file.seek(0, IO::SEEK_END)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Before ('@command_line') do
|
21
|
+
Mirage.stop :all
|
22
|
+
end
|
23
|
+
|
24
|
+
After('@command_line') do
|
25
|
+
Mirage.stop :all
|
26
|
+
end
|
27
|
+
|
28
|
+
at_exit do
|
29
|
+
Mirage.stop :all
|
30
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'uri'
|
2
|
+
module Mirage
|
3
|
+
class Client
|
4
|
+
include Mirage::Web
|
5
|
+
attr_reader :url
|
6
|
+
|
7
|
+
# Creates an instance of the Mirage client that can be used to interact with the Mirage Server
|
8
|
+
#
|
9
|
+
# Client.new => a client that is configured to connect to Mirage on http://localhost:7001/mirage (the default settings for Mirage)
|
10
|
+
# Client.new(URL) => a client that is configured to connect to an instance of Mirage running on the specified url.
|
11
|
+
def initialize url="http://localhost:7001/mirage"
|
12
|
+
@url = url
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop
|
16
|
+
Mirage.stop :port => URI.parse(@url).port
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Set a text or file based response template, to be hosted at a given end point. A block can be specified to configure the template
|
21
|
+
# client.set(endpoint, response, &block) => unique id that can be used to call back to the server
|
22
|
+
#
|
23
|
+
# Examples:
|
24
|
+
# client.put('greeting', 'hello')
|
25
|
+
#
|
26
|
+
# client.put('greeting', 'hello') do |response|
|
27
|
+
# response.pattern = 'pattern' #regex or string literal applied against the request querystring and body
|
28
|
+
# response.method = :post #By default templates will respond to get requests
|
29
|
+
# response.content_type = 'text/html' #defaults text/plain
|
30
|
+
# response.default = true # defaults to false. setting to true will allow this template to respond to request made to sub resources should it match.
|
31
|
+
# end
|
32
|
+
def put endpoint, response_value, &block
|
33
|
+
response = Mirage::Response.new response_value
|
34
|
+
|
35
|
+
yield response if block_given?
|
36
|
+
|
37
|
+
build_response(http_put("#{@url}/templates/#{endpoint}", response.value, response.headers))
|
38
|
+
end
|
39
|
+
|
40
|
+
# Use to look to preview the content of a response template would return to a client without actually triggering.
|
41
|
+
# client.response(response_id) => response held on the server as a String
|
42
|
+
def response response_id
|
43
|
+
response = build_response(http_get("#{@url}/templates/#{response_id}"))
|
44
|
+
case response
|
45
|
+
when String then
|
46
|
+
return response
|
47
|
+
when Mirage::Web::FileResponse then
|
48
|
+
return response.response.body
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
# Clear Content from Mirage
|
54
|
+
#
|
55
|
+
# If a response id is not valid, a ResponseNotFound exception will be thrown
|
56
|
+
#
|
57
|
+
# Example Usage:
|
58
|
+
# client.clear -> clear all responses and associated requests
|
59
|
+
# client.clear(response_id) -> Clear the response and tracked request for a given response id
|
60
|
+
# client.clear(:requests) -> Clear all tracked request information
|
61
|
+
# client.clear(:request => response_id) -> Clear the tracked request for a given response id
|
62
|
+
def clear thing=nil
|
63
|
+
|
64
|
+
case thing
|
65
|
+
when :requests
|
66
|
+
http_delete("#{@url}/requests")
|
67
|
+
when Numeric then
|
68
|
+
http_delete("#{@url}/templates/#{thing}")
|
69
|
+
when Hash then
|
70
|
+
puts "deleteing request #{thing[:request]}"
|
71
|
+
http_delete("#{@url}/requests/#{thing[:request]}") if thing[:request]
|
72
|
+
else
|
73
|
+
NilClass
|
74
|
+
http_delete("#{@url}/templates")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Retrieve the last request that triggered a response to be returned. If the request contained content in its body, this is returned. If the
|
81
|
+
# request did not have any content in its body then what ever was in the request query string is returned instead
|
82
|
+
#
|
83
|
+
# Example Usage
|
84
|
+
# client.request(response_id) -> Tracked request as a String
|
85
|
+
def request response_id
|
86
|
+
build_response(http_get("#{@url}/requests/#{response_id}"))
|
87
|
+
end
|
88
|
+
|
89
|
+
# Save the state of the Mirage server so that it can be reverted back to that exact state at a later time.
|
90
|
+
def save
|
91
|
+
http_put("#{@url}/backup", '').code == 200
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Revert the state of Mirage back to the state that was last saved
|
96
|
+
# If there is no snapshot to rollback to, nothing happens
|
97
|
+
def revert
|
98
|
+
http_put(@url, '').code == 200
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# Check to see if mirage is running on the url that the client is pointing to
|
103
|
+
def running?
|
104
|
+
Mirage.running?(@url)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Clear down the Mirage Server and load any defaults that are in Mirages default responses directory.
|
108
|
+
def prime
|
109
|
+
build_response(http_put("#{@url}/defaults", ''))
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def build_response response
|
114
|
+
case response.code.to_i
|
115
|
+
when 500 then
|
116
|
+
raise ::Mirage::InternalServerException.new(response.body, response.code.to_i)
|
117
|
+
when 404 then
|
118
|
+
raise ::Mirage::ResponseNotFound.new(response.body, response.code.to_i)
|
119
|
+
else
|
120
|
+
response.body
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -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 ResponseNotFound < MirageError;
|
15
|
+
end
|
16
|
+
|
17
|
+
class ClientError < ::Exception
|
18
|
+
def initialize message
|
19
|
+
super message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
module Mirage
|
3
|
+
class Response
|
4
|
+
|
5
|
+
attr_accessor :content_type,:method, :response_code, :pattern, :default, :status, :delay
|
6
|
+
attr_reader :value
|
7
|
+
|
8
|
+
def initialize response
|
9
|
+
@content_type = 'text/plain'
|
10
|
+
@value = response
|
11
|
+
@method = :get
|
12
|
+
@status = 200
|
13
|
+
@delay = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def headers
|
17
|
+
headers = {}
|
18
|
+
headers['Content-Type']=@content_type
|
19
|
+
headers['X-mirage-file'] = 'true' if @response.kind_of?(IO)
|
20
|
+
headers['X-mirage-method'] = @method
|
21
|
+
headers['X-mirage-pattern'] = @pattern if @pattern
|
22
|
+
headers['X-mirage-default'] = @default if @default == true
|
23
|
+
headers['X-mirage-status'] = @status
|
24
|
+
headers['X-mirage-delay'] = @delay
|
25
|
+
headers
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'waitforit'
|
3
|
+
require 'childprocess'
|
4
|
+
require 'uri'
|
5
|
+
module Mirage
|
6
|
+
class << self
|
7
|
+
include Web
|
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={:port => 7001}
|
14
|
+
Runner.new.invoke(:start, [], options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Stop locally running instance(s) of Mirage
|
18
|
+
#
|
19
|
+
# Example Usage:
|
20
|
+
# Mirage.stop -> Will stop mirage if there is only instance running. Can be running on any port.
|
21
|
+
# Mirage.stop :port => port -> stop mirage on a given port
|
22
|
+
# Mirage.stop :port => [port1, port2...] -> stops multiple running instances of Mirage
|
23
|
+
def stop options={}
|
24
|
+
options = {:port => :all} if options == :all
|
25
|
+
|
26
|
+
if options[:port]
|
27
|
+
options[:port] = [options[:port]] unless options[:port].is_a?(Array)
|
28
|
+
end
|
29
|
+
|
30
|
+
Runner.new.invoke(:stop, [], options)
|
31
|
+
rescue ClientError => e
|
32
|
+
raise ClientError.new("Mirage is running multiple ports, please specify the port(s) see api/tests for details")
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Detect if Mirage is running on a URL or a local port
|
37
|
+
#
|
38
|
+
# Example Usage:
|
39
|
+
# Mirage.running? -> boolean indicating whether Mirage is running on *locally* on port 7001
|
40
|
+
# Mirage.running? :port => port -> boolean indicating whether Mirage is running on *locally* on the given port
|
41
|
+
# Mirage.running? url -> boolean indicating whether Mirage is running on the given URL
|
42
|
+
def running? options_or_url = {:port => 7001}
|
43
|
+
url = options_or_url.is_a?(Hash) ? "http://localhost:#{options_or_url[:port]}/mirage" : options_or_url
|
44
|
+
http_get(url) and return true
|
45
|
+
rescue Errno::ECONNREFUSED
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
class Runner < Thor
|
52
|
+
include ::Mirage::Web
|
53
|
+
RUBY_CMD = ChildProcess.jruby? ? 'jruby' : 'ruby'
|
54
|
+
|
55
|
+
desc "start", "Starts mirage"
|
56
|
+
method_option :port, :aliases => "-p", :type => :numeric, :default => 7001, :desc => "port that mirage should be started on"
|
57
|
+
method_option :defaults, :aliases => "-d", :type => :string, :default => 'responses', :desc => "location to load default responses from"
|
58
|
+
method_option :debug, :type => :boolean, :default => false, :desc => "run in debug mode"
|
59
|
+
|
60
|
+
def start
|
61
|
+
unless mirage_process_ids([options[:port]]).empty?
|
62
|
+
puts "Mirage is already running: #{mirage_process_ids([options[:port]]).values.join(",")}"
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
mirage_server_file = "#{File.dirname(__FILE__)}/../../../mirage_server.rb"
|
67
|
+
|
68
|
+
if ChildProcess.windows?
|
69
|
+
command = ["cmd", "/C", "start", "mirage server port #{options[:port]}", RUBY_CMD, mirage_server_file]
|
70
|
+
else
|
71
|
+
command = [RUBY_CMD, mirage_server_file]
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
command = command.concat(options.to_a).flatten.collect { |arg| arg.to_s }
|
76
|
+
ChildProcess.build(*command).start
|
77
|
+
|
78
|
+
mirage_client = Mirage::Client.new "http://localhost:#{options[:port]}/mirage"
|
79
|
+
wait_until(:timeout_after => 30.seconds) { mirage_client.running? }
|
80
|
+
|
81
|
+
begin
|
82
|
+
mirage_client.prime
|
83
|
+
rescue Mirage::InternalServerException => e
|
84
|
+
puts "WARN: #{e.message}"
|
85
|
+
end
|
86
|
+
mirage_client
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "stop", "Stops mirage"
|
90
|
+
method_option :port, :aliases => "-p", :type => :array, :banner => "[port_1 port_2|all]", :desc => "port(s) of mirage instance(s). ALL stops all running instances"
|
91
|
+
|
92
|
+
def stop
|
93
|
+
ports = options[:port] || []
|
94
|
+
if ports.empty?
|
95
|
+
mirage_process_ids = mirage_process_ids([:all])
|
96
|
+
raise ClientError.new("Mirage is running on ports #{mirage_process_ids.keys.sort.join(", ")}. Please run mirage stop -p [PORT(s)] instead") if mirage_process_ids.size > 1
|
97
|
+
end
|
98
|
+
|
99
|
+
ports = case ports
|
100
|
+
when %w(all), [:all], []
|
101
|
+
[:all]
|
102
|
+
else
|
103
|
+
ports.collect { |port| port.to_i }
|
104
|
+
end
|
105
|
+
|
106
|
+
mirage_process_ids(ports).values.each do |process_id|
|
107
|
+
ChildProcess.windows? ? `taskkill /F /T /PID #{process_id}` : IO.popen("kill -9 #{process_id}")
|
108
|
+
end
|
109
|
+
|
110
|
+
wait_until do
|
111
|
+
mirage_process_ids(ports).empty?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def processes_with_name name
|
118
|
+
if ChildProcess.windows?
|
119
|
+
|
120
|
+
`tasklist /V | findstr "#{name.gsub(" ", '\\ ')}"`
|
121
|
+
else
|
122
|
+
IO.popen("ps aux | grep '#{name}' | grep -v grep | grep -v #{$$}")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def mirage_process_ids *ports
|
127
|
+
ports.flatten!
|
128
|
+
mirage_instances = {}
|
129
|
+
["Mirage Server", "mirage_server", "mirage server"].each do |process_name|
|
130
|
+
processes_with_name(process_name).lines.collect { |line| line.chomp }.each do |process_line|
|
131
|
+
pid = process_line.split(' ')[1]
|
132
|
+
port = process_line[/port (\d+)/, 1]
|
133
|
+
mirage_instances[port] = pid
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
return mirage_instances if ports.first.to_s.downcase == "all"
|
138
|
+
Hash[mirage_instances.find_all { |port, pid| ports.include?(port.to_i) }]
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|