mirage 2.2.3 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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