mimic 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,19 @@
1
+ ## 0.4.0
2
+
3
+ * Mimic can now be run in it's own process and configured externally using a REST API.
4
+ * Mimic can be run using the Daemons gem safely (pass :fork => false to disable built-in forking).
5
+ * All existing stubs can be cleared.
6
+
7
+ ## 0.3.0
8
+
9
+ * All verb methods (get, post etc.) can take blocks
10
+
11
+ ## 0.2.0
12
+
13
+ * Added support for using Rack middleware
14
+ * Removed host file modification feature
15
+ * Refactor code top build on top of Sinatra::Base
16
+
17
+ ## 0.1.0
18
+
19
+ * Initial release
data/README.md CHANGED
@@ -18,7 +18,7 @@ Registering to a single request stub:
18
18
 
19
19
  And the result, using RestClient:
20
20
 
21
- $ RestClient.get("http://www.example.com:10090/some/path") # => 200 | hello world
21
+ $ RestClient.get("http://www.example.com:11988/some/path") # => 200 | hello world
22
22
 
23
23
  Registering multiple request stubs; note that you can stub the same path with different HTTP methods separately.
24
24
 
@@ -46,6 +46,37 @@ Finally, because Mimic is built on top of Sinatra for the core request handling,
46
46
  end
47
47
  end
48
48
 
49
+ ## Using Mimic with non-Ruby processes
50
+
51
+ Mimic has a built-in REST API that lets you configure your request stubs over HTTP. This makes it possible to use Mimic from other processes that can perform HTTP requests.
52
+
53
+ First of all, you'll need to run Mimic as a daemon. You can do this with a simple Ruby script:
54
+
55
+ #!/usr/bin/env ruby
56
+ Mimic.daemonize({:port => 11988, :remote_configuration_path => '/api'})
57
+
58
+ Give the script executable permissions and then start it:
59
+
60
+ $ your_mimic_script.rb start (or run)
61
+
62
+ The remote configuration path is where the API endpoints will be mounted - this is configurable as you will not be able this path or any paths below it in your stubs, so choose one that doesn't conflict with the paths you need to stub.
63
+
64
+ The API supports both JSON and Plist payloads, defaulting to JSON. Set the request Content-Type header to application/plist for Plist requests.
65
+
66
+ For the following Mimic configuration (using the Ruby DSL):
67
+
68
+ Mimic.mimic.get("/some/path").returning("hello world")
69
+
70
+ The equivalent stub can be configured using the REST API as follows:
71
+
72
+ $ curl -d'{"path":"/some/path", "body":"hello world"}' http://localhost:11988/api/get
73
+
74
+ Likewise, a POST request to the same path could be stubbed like so:
75
+
76
+ $ curl -d'{"path":"/some/path", "body":"hello world"}' http://localhost:11988/api/post
77
+
78
+ The end-point of the API is the HTTP verb you are stubbing, the path, response body, code and headers are specified in the POST data (a hash in JSON or Plist format). See the HTTP API Cucumber features for more examples.
79
+
49
80
  ## Contributors
50
81
 
51
82
  * [James Fairbairn](http://github.com/jfairbairn)
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'cucumber'
3
3
  require 'cucumber/rake/task'
4
- require 'spec/rake/spectask'
4
+ require 'rspec/core/rake_task'
5
5
 
6
6
  desc "Run all Cucumber features"
7
7
  Cucumber::Rake::Task.new(:features) do |t|
@@ -9,12 +9,11 @@ Cucumber::Rake::Task.new(:features) do |t|
9
9
  end
10
10
 
11
11
  desc "Run all specs"
12
- Spec::Rake::SpecTask.new('specs') do |t|
13
- t.spec_files = FileList['spec/**/*.rb']
12
+ RSpec::Core::RakeTask.new(:spec) do |t|
14
13
  end
15
14
 
16
- task :default => :specs
17
- task :all => [:specs, :features]
15
+ task :default => :spec
16
+ task :all => [:spec, :features]
18
17
 
19
18
  require "rake/gempackagetask"
20
19
  require "rake/rdoctask"
@@ -28,7 +27,7 @@ spec = Gem::Specification.new do |s|
28
27
 
29
28
  # Change these as appropriate
30
29
  s.name = "mimic"
31
- s.version = "0.3.0"
30
+ s.version = "0.4.0"
32
31
  s.summary = "A Ruby gem for faking external web services for testing"
33
32
  s.authors = "Luke Redpath"
34
33
  s.email = "luke@lukeredpath.co.uk"
@@ -39,18 +38,21 @@ spec = Gem::Specification.new do |s|
39
38
  s.rdoc_options = %w(--main README.md)
40
39
 
41
40
  # Add any extra files to include in the gem
42
- s.files = %w(LICENSE Rakefile README.md) + Dir.glob("{spec,lib/**/*}")
41
+ s.files = %w(LICENSE CHANGES Rakefile README.md) + Dir.glob("{spec,lib/**/*}")
43
42
  s.require_paths = ["lib"]
44
43
 
45
44
  # If you want to depend on other gems, add them here, along with any
46
45
  # relevant versions
47
46
  s.add_dependency("rack")
48
47
  s.add_dependency("sinatra")
48
+ s.add_dependency("json")
49
+ s.add_dependency("plist")
49
50
 
50
51
  # If your tests use any gems, include them here
51
- s.add_development_dependency("rspec")
52
+ s.add_development_dependency("rspec", "~> 2.4.0")
52
53
  s.add_development_dependency("cucumber")
53
54
  s.add_development_dependency("mocha")
55
+ s.add_development_dependency("rest-client")
54
56
  end
55
57
 
56
58
  # This task actually builds the gem. We also regenerate a static
@@ -85,6 +87,11 @@ task :clean => [:clobber_rdoc, :clobber_package] do
85
87
  end
86
88
 
87
89
  task 'Release if all specs pass'
88
- task :release => [:specs, :features, :package] do
90
+ task :release => [:clean, :bundle, :spec, :features, :package] do
89
91
  system("gem push pkg/#{spec.name}-#{spec.version}.gem")
90
92
  end
93
+
94
+ desc 'Install all gem dependencies'
95
+ task :bundle => :gemspec do
96
+ system("bundle")
97
+ end
@@ -5,19 +5,37 @@ require 'logger'
5
5
 
6
6
  module Mimic
7
7
  MIMIC_DEFAULT_PORT = 11988
8
-
8
+
9
+ MIMIC_DEFAULT_OPTIONS = {
10
+ :hostname => 'localhost',
11
+ :port => MIMIC_DEFAULT_PORT,
12
+ :remote_configuration_path => nil,
13
+ :fork => true
14
+ }
15
+
9
16
  def self.mimic(options = {}, &block)
10
- options = {:hostname => 'localhost', :port => MIMIC_DEFAULT_PORT}.merge(options)
11
-
12
- FakeHost.new(options[:hostname]).tap do |host|
17
+ options = MIMIC_DEFAULT_OPTIONS.merge(options)
18
+
19
+ host = FakeHost.new(options[:hostname], options[:remote_configuration_path]).tap do |host|
13
20
  host.instance_eval(&block) if block_given?
14
- Server.instance.serve(host, options[:port])
21
+ Server.instance.serve(host, options[:port], options[:fork])
15
22
  end
23
+ add_host(host)
16
24
  end
17
-
25
+
18
26
  def self.cleanup!
19
27
  Mimic::Server.instance.shutdown
20
28
  end
29
+
30
+ def self.reset_all!
31
+ @hosts.each { |h| h.clear }
32
+ end
33
+
34
+ private
35
+
36
+ def self.add_host(host)
37
+ host.tap { |h| (@hosts ||= []) << h }
38
+ end
21
39
 
22
40
  class Server
23
41
  include Singleton
@@ -26,28 +44,40 @@ module Mimic
26
44
  @logger ||= Logger.new(StringIO.new)
27
45
  end
28
46
 
29
- def serve(host_app, port)
30
- @thread = Thread.fork do
31
- Rack::Handler::WEBrick.run(host_app,
32
- :Port => port,
33
- :Logger => logger,
34
- :AccessLog => logger
35
-
36
- ) { |server| @server = server }
47
+ def serve(host_app, port, should_fork)
48
+ if should_fork
49
+ @thread = Thread.fork do
50
+ start_service(host_app, port)
51
+ end
52
+
53
+ wait_for_service(host_app.hostname, port)
54
+
55
+ else
56
+ start_service(host_app, port)
37
57
  end
38
-
39
- wait_for_service(host_app.hostname, port)
40
58
  end
41
-
42
- def shutdown
43
- if @thread
44
- Thread.kill(@thread)
45
- @server.shutdown
59
+
60
+ def start_service(host_app, port)
61
+ Rack::Handler::WEBrick.run(host_app.url_map, {
62
+ :Port => port,
63
+ :Logger => logger,
64
+ :AccessLog => logger,
65
+
66
+ }) do |server|
67
+ @server = server
68
+
69
+ trap("TERM") { @server.shutdown }
70
+ trap("INT") { @server.shutdown }
46
71
  end
47
72
  end
48
-
73
+
74
+ def shutdown
75
+ Thread.kill(@thread) if @thread
76
+ @server.shutdown if @server
77
+ end
78
+
49
79
  # courtesy of http://is.gd/eoYho
50
-
80
+
51
81
  def listening?(host, port)
52
82
  begin
53
83
  socket = TCPSocket.new(host, port)
@@ -0,0 +1,91 @@
1
+ require 'json'
2
+ require 'plist'
3
+
4
+ module Mimic
5
+ class API < Sinatra::Base
6
+ class << self
7
+ attr_accessor :host
8
+ end
9
+
10
+ def host
11
+ self.class.host
12
+ end
13
+
14
+ %w{get post put delete head}.each do |verb|
15
+ post "/#{verb}" do
16
+ api_request = APIRequest.from_request(request, verb)
17
+ api_request.setup_stubs_on(host)
18
+ [201, {}, api_request.to_s]
19
+ end
20
+ end
21
+
22
+ post "/multi" do
23
+ api_request = APIRequest.from_request(request)
24
+ api_request.setup_stubs_on(host)
25
+ [201, {}, api_request.to_s]
26
+ end
27
+
28
+ post "/clear" do
29
+ self.host.clear
30
+ [200, {}, ""]
31
+ end
32
+
33
+ private
34
+
35
+ class APIRequest
36
+ def initialize(data, method = nil)
37
+ @data = data
38
+ @method = (method || "GET")
39
+ end
40
+
41
+ def to_s
42
+ @data.inspect
43
+ end
44
+
45
+ def self.from_request(request, method = nil)
46
+ case request.content_type
47
+ when /json/
48
+ data = JSON.parse(request.body.string)
49
+ when /plist/
50
+ data = Plist.parse_xml(request.body.string)
51
+ else
52
+ data = JSON.parse(request.body.string)
53
+ end
54
+ new(data, method)
55
+ end
56
+
57
+ def setup_stubs_on(host)
58
+ (@data["stubs"] || [@data]).each do |stub_data|
59
+ Stub.new(stub_data, stub_data['method'] || @method).on(host)
60
+ end
61
+ end
62
+
63
+ class Stub
64
+ def initialize(data, method = nil)
65
+ @data = data
66
+ @method = method
67
+ end
68
+
69
+ def on(host)
70
+ host.send(@method.downcase.to_sym, path).returning(body, code, headers)
71
+ end
72
+
73
+ def path
74
+ @data['path'] || '/'
75
+ end
76
+
77
+ def body
78
+ @data['body'] || ''
79
+ end
80
+
81
+ def code
82
+ @data['code'] || 200
83
+ end
84
+
85
+ def headers
86
+ @data['headers'] || {}
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,17 +1,21 @@
1
1
  require 'sinatra/base'
2
+ require 'mimic/api'
2
3
 
3
4
  module Mimic
4
5
  class FakeHost
5
- attr_reader :hostname
6
+ attr_reader :hostname, :url_map
6
7
 
7
- def initialize(hostname)
8
+ def initialize(hostname, remote_configuration_path = nil)
8
9
  @hostname = hostname
9
10
  @stubs = []
10
11
  @app = Class.new(Sinatra::Base)
11
-
12
+ @remote_configuration_path = remote_configuration_path
13
+
12
14
  @app.not_found do
13
15
  [404, {}, ""]
14
16
  end
17
+
18
+ build_url_map!
15
19
  end
16
20
 
17
21
  def get(path, &block)
@@ -34,17 +38,27 @@ module Mimic
34
38
  request("HEAD", path, &block)
35
39
  end
36
40
 
41
+ def import(path)
42
+ if File.exists?(path)
43
+ instance_eval(File.read(path))
44
+ end
45
+ end
46
+
37
47
  def call(env)
38
48
  @stubs.each(&:build)
39
49
  @app.call(env)
40
50
  end
41
51
 
42
- def method_missing(method, *args, &block)
43
- @app.send(method, *args, &block)
52
+ def clear
53
+ @stubs.clear
44
54
  end
45
55
 
46
56
  private
47
57
 
58
+ def method_missing(method, *args, &block)
59
+ @app.send(method, *args, &block)
60
+ end
61
+
48
62
  def request(method, path, &block)
49
63
  if block_given?
50
64
  @app.send(method.downcase, path, &block)
@@ -54,6 +68,17 @@ module Mimic
54
68
  end
55
69
  end
56
70
 
71
+ def build_url_map!
72
+ routes = {'/' => self}
73
+
74
+ if @remote_configuration_path
75
+ API.host = self
76
+ routes[@remote_configuration_path] = API
77
+ end
78
+
79
+ @url_map = Rack::URLMap.new(routes)
80
+ end
81
+
57
82
  class StubbedRequest
58
83
  def initialize(app, method, path)
59
84
  @method, @path = method, path
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mimic
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
5
- prerelease: false
4
+ hash: 15
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Luke Redpath
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-20 00:00:00 +01:00
18
+ date: 2011-01-13 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -47,7 +47,7 @@ dependencies:
47
47
  type: :runtime
48
48
  version_requirements: *id002
49
49
  - !ruby/object:Gem::Dependency
50
- name: rspec
50
+ name: json
51
51
  prerelease: false
52
52
  requirement: &id003 !ruby/object:Gem::Requirement
53
53
  none: false
@@ -58,10 +58,10 @@ dependencies:
58
58
  segments:
59
59
  - 0
60
60
  version: "0"
61
- type: :development
61
+ type: :runtime
62
62
  version_requirements: *id003
63
63
  - !ruby/object:Gem::Dependency
64
- name: cucumber
64
+ name: plist
65
65
  prerelease: false
66
66
  requirement: &id004 !ruby/object:Gem::Requirement
67
67
  none: false
@@ -72,12 +72,28 @@ dependencies:
72
72
  segments:
73
73
  - 0
74
74
  version: "0"
75
- type: :development
75
+ type: :runtime
76
76
  version_requirements: *id004
77
77
  - !ruby/object:Gem::Dependency
78
- name: mocha
78
+ name: rspec
79
79
  prerelease: false
80
80
  requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ hash: 31
86
+ segments:
87
+ - 2
88
+ - 4
89
+ - 0
90
+ version: 2.4.0
91
+ type: :development
92
+ version_requirements: *id005
93
+ - !ruby/object:Gem::Dependency
94
+ name: cucumber
95
+ prerelease: false
96
+ requirement: &id006 !ruby/object:Gem::Requirement
81
97
  none: false
82
98
  requirements:
83
99
  - - ">="
@@ -87,7 +103,35 @@ dependencies:
87
103
  - 0
88
104
  version: "0"
89
105
  type: :development
90
- version_requirements: *id005
106
+ version_requirements: *id006
107
+ - !ruby/object:Gem::Dependency
108
+ name: mocha
109
+ prerelease: false
110
+ requirement: &id007 !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ type: :development
120
+ version_requirements: *id007
121
+ - !ruby/object:Gem::Dependency
122
+ name: rest-client
123
+ prerelease: false
124
+ requirement: &id008 !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ hash: 3
130
+ segments:
131
+ - 0
132
+ version: "0"
133
+ type: :development
134
+ version_requirements: *id008
91
135
  description:
92
136
  email: luke@lukeredpath.co.uk
93
137
  executables: []
@@ -98,8 +142,10 @@ extra_rdoc_files:
98
142
  - README.md
99
143
  files:
100
144
  - LICENSE
145
+ - CHANGES
101
146
  - Rakefile
102
147
  - README.md
148
+ - lib/mimic/api.rb
103
149
  - lib/mimic/fake_host.rb
104
150
  - lib/mimic.rb
105
151
  has_rdoc: true
@@ -133,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
179
  requirements: []
134
180
 
135
181
  rubyforge_project:
136
- rubygems_version: 1.3.7
182
+ rubygems_version: 1.4.1
137
183
  signing_key:
138
184
  specification_version: 3
139
185
  summary: A Ruby gem for faking external web services for testing