mirage 2.2.3 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +11 -2
  3. data/HISTORY +1 -0
  4. data/VERSION +1 -1
  5. data/bin/mirage +6 -8
  6. data/features/client/clear.feature +8 -8
  7. data/features/client/{response.feature → preview_responses.feature} +0 -0
  8. data/features/client/put.feature +9 -1
  9. data/features/client/{request.feature → requests.feature} +0 -0
  10. data/features/client/running.feature +49 -0
  11. data/features/client/{command_line_interface.feature → start.feature} +0 -12
  12. data/features/client/stop.feature +85 -0
  13. data/features/server/commandline_interface/help.feature +16 -0
  14. data/features/server/commandline_interface/start.feature +30 -0
  15. data/features/server/commandline_interface/stop.feature +42 -0
  16. data/features/server/prime.feature +8 -7
  17. data/features/step_definitions/my_steps.rb +20 -16
  18. data/features/support/command_line.rb +22 -0
  19. data/features/support/env.rb +11 -121
  20. data/features/support/hooks.rb +30 -0
  21. data/features/support/mirage.rb +8 -0
  22. data/lib/mirage/client/client.rb +124 -0
  23. data/lib/mirage/client/error.rb +22 -0
  24. data/lib/mirage/client/response.rb +29 -0
  25. data/lib/mirage/client/runner.rb +142 -0
  26. data/lib/mirage/client.rb +4 -206
  27. data/mirage.gemspec +25 -8
  28. data/mirage_server.rb +15 -10
  29. data/rakefile +7 -1
  30. data/spec/running_via_api_spec.rb +147 -0
  31. data/spec/running_via_api_windows_spec.rb +187 -0
  32. data/test.rb +21 -4
  33. metadata +66 -33
  34. data/features/client/mirage_client.feature +0 -36
  35. data/features/server/command_line_iterface.feature +0 -45
  36. data/lib/mirage/cli.rb +0 -69
@@ -1,5 +1,8 @@
1
- $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../../lib")
2
- $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../../server")
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
- ENV['RUBYOPT'] =''
11
-
12
- module OsSupport
13
- def windows?
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,8 @@
1
+ module Mirage
2
+ def start_mirage_in_scratch_dir
3
+ Dir.chdir SCRATCH do
4
+ Mirage.start
5
+ end
6
+ end
7
+ end
8
+ include Mirage
@@ -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