summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-04-11 15:21:50 -0500
committerLuke Kanies <luke@madstop.com>2008-04-11 15:21:50 -0500
commitd834242db13a827a34340c5f2e51c8df532d5196 (patch)
treecdd98ee63103082a81dac7e91e13b60458685780
parentfb05ef3c96038d67a46eb142202af186ad6cb0b3 (diff)
parentd9846fc3f06f61fcb4b8806740f77747a7f6939e (diff)
downloadpuppet-d834242db13a827a34340c5f2e51c8df532d5196.tar.gz
puppet-d834242db13a827a34340c5f2e51c8df532d5196.tar.xz
puppet-d834242db13a827a34340c5f2e51c8df532d5196.zip
Merge branch '0.24.x'
Conflicts: spec/unit/network/server.rb
-rw-r--r--lib/puppet/indirector/indirection.rb5
-rw-r--r--lib/puppet/indirector/node/rest.rb2
-rw-r--r--lib/puppet/indirector/rest.rb54
-rw-r--r--lib/puppet/network/http/handler.rb32
-rw-r--r--lib/puppet/network/http/mongrel.rb17
-rw-r--r--lib/puppet/network/http/mongrel/rest.rb22
-rw-r--r--lib/puppet/network/http/mongrel/xmlrpc.rb4
-rw-r--r--lib/puppet/network/http/webrick.rb43
-rw-r--r--lib/puppet/network/http/webrick/rest.rb17
-rw-r--r--lib/puppet/network/http/webrick/xmlrpc.rb4
-rw-r--r--lib/puppet/network/server.rb44
-rw-r--r--spec/integration/indirector/rest.rb460
-rw-r--r--spec/integration/network/server/mongrel.rb46
-rw-r--r--spec/integration/network/server/webrick.rb46
-rw-r--r--spec/spec.opts2
-rwxr-xr-xspec/unit/indirector/indirection.rb6
-rwxr-xr-xspec/unit/indirector/node/rest.rb4
-rwxr-xr-xspec/unit/indirector/rest.rb362
-rw-r--r--spec/unit/network/http/mongrel.rb25
-rw-r--r--spec/unit/network/http/mongrel/rest.rb260
-rw-r--r--spec/unit/network/http/webrick.rb41
-rw-r--r--spec/unit/network/http/webrick/rest.rb303
-rw-r--r--spec/unit/network/server.rb57
23 files changed, 1407 insertions, 449 deletions
diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb
index 15358a801..05464f8c9 100644
--- a/lib/puppet/indirector/indirection.rb
+++ b/lib/puppet/indirector/indirection.rb
@@ -225,14 +225,14 @@ class Puppet::Indirector::Indirection
request = request(:destroy, key, *args)
terminus = prepare(request)
- terminus.destroy(request)
+ result = terminus.destroy(request)
if cache? and cached = cache.find(request(:find, key, *args))
# Reuse the existing request, since it's equivalent.
cache.destroy(request)
end
- nil
+ result
end
# Search for more than one instance. Should always return an array.
@@ -242,7 +242,6 @@ class Puppet::Indirector::Indirection
if result = terminus.search(request)
raise Puppet::DevError, "Search results from terminus %s are not an array" % terminus.name unless result.is_a?(Array)
-
result.each do |instance|
instance.expiration ||= self.expiration
end
diff --git a/lib/puppet/indirector/node/rest.rb b/lib/puppet/indirector/node/rest.rb
index c5d2f97fb..d8b75f6e7 100644
--- a/lib/puppet/indirector/node/rest.rb
+++ b/lib/puppet/indirector/node/rest.rb
@@ -1,7 +1,7 @@
require 'puppet/node'
require 'puppet/indirector/rest'
-class Puppet::Node::REST < Puppet::Indirector::REST
+class Puppet::Node::Rest < Puppet::Indirector::REST
desc "This will eventually be a REST-based mechanism for finding nodes. It is currently non-functional."
# TODO/FIXME
end
diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb
index 7b7c932c4..d33150fc2 100644
--- a/lib/puppet/indirector/rest.rb
+++ b/lib/puppet/indirector/rest.rb
@@ -1,8 +1,56 @@
-require 'puppet/indirector/rest'
+require 'net/http'
+require 'uri'
# Access objects via REST
class Puppet::Indirector::REST < Puppet::Indirector::Terminus
- def find(name, options = {})
- indirection.model.new(name)
+
+ def rest_connection_details
+ { :host => Puppet[:server], :port => Puppet[:masterport].to_i }
+ end
+
+ def network_fetch(path)
+ network {|conn| conn.get("/#{path}").body }
+ end
+
+ def network_delete(path)
+ network {|conn| conn.delete("/#{path}").body }
+ end
+
+ def network_put(path, data)
+ network {|conn| conn.put("/#{path}", data).body }
+ end
+
+ def find(request)
+ network_result = network_fetch("#{indirection.name}/#{request.key}")
+ raise YAML.load(network_result) if exception?(network_result)
+ indirection.model.from_yaml(network_result)
+ end
+
+ def search(request)
+ network_results = network_fetch("#{indirection.name}s/#{request.key}")
+ raise YAML.load(network_results) if exception?(network_results)
+ YAML.load(network_results.to_s).collect {|result| indirection.model.from_yaml(result) }
+ end
+
+ def destroy(request)
+ network_result = network_delete("#{indirection.name}/#{request.key}")
+ raise YAML.load(network_result) if exception?(network_result)
+ YAML.load(network_result.to_s)
+ end
+
+ def save(request)
+ network_result = network_put("#{indirection.name}/", request.instance.to_yaml)
+ raise YAML.load(network_result) if exception?(network_result)
+ indirection.model.from_yaml(network_result)
+ end
+
+ private
+
+ def network(&block)
+ Net::HTTP.start(rest_connection_details[:host], rest_connection_details[:port]) {|conn| yield(conn) }
+ end
+
+ def exception?(yaml_string)
+ yaml_string =~ %r{--- !ruby/exception}
end
end
diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb
index 773381c8d..7113c92d3 100644
--- a/lib/puppet/network/http/handler.rb
+++ b/lib/puppet/network/http/handler.rb
@@ -1,9 +1,9 @@
-class Puppet::Network::HTTP::Handler
- def initialize(args = {})
+module Puppet::Network::HTTP::Handler
+
+ def initialize_for_puppet(args = {})
raise ArgumentError unless @server = args[:server]
raise ArgumentError unless @handler = args[:handler]
@model = find_model_for_handler(@handler)
- register_handler
end
# handle an HTTP request
@@ -18,38 +18,46 @@ class Puppet::Network::HTTP::Handler
end
private
+
+ def model
+ @model
+ end
def do_find(request, response)
key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]")
args = params(request)
- result = @model.find(key, args).to_yaml
+ result = model.find(key, args).to_yaml
encode_result(request, response, result)
end
def do_search(request, response)
args = params(request)
- result = @model.search(args).collect {|obj| obj.to_yaml }
+ result = model.search(args).collect {|result| result.to_yaml }.to_yaml
encode_result(request, response, result)
end
def do_destroy(request, response)
key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]")
args = params(request)
- result = @model.destroy(key, args)
+ result = model.destroy(key, args)
encode_result(request, response, YAML.dump(result))
end
def do_save(request, response)
- data = body(request)
+ data = body(request).to_s
raise ArgumentError, "No data to save" if !data or data.empty?
- args = params(request)
- obj = @model.new
- result = obj.save(args.merge(:data => data)).to_yaml
+ # args = params(request)
+ obj = model.from_yaml(data)
+ result = save_object(obj).to_yaml
encode_result(request, response, result)
end
+
+ def save_object(obj)
+ obj.save
+ end
def do_exception(request, response, exception, status=404)
- encode_result(request, response, exception.to_s, status)
+ encode_result(request, response, exception.to_yaml, status)
end
def find_model_for_handler(handler)
@@ -77,7 +85,7 @@ class Puppet::Network::HTTP::Handler
%r{/#{@handler.to_s}s$}.match(path(request))
end
- # methods specific to a given web server
+ # methods to be overridden by the including web server class
def register_handler
raise NotImplementedError
diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb
index 8ea669531..9a4531c7a 100644
--- a/lib/puppet/network/http/mongrel.rb
+++ b/lib/puppet/network/http/mongrel.rb
@@ -1,7 +1,6 @@
require 'mongrel' if Puppet.features.mongrel?
require 'puppet/network/http/mongrel/rest'
-require 'puppet/network/http/mongrel/xmlrpc'
class Puppet::Network::HTTP::Mongrel
def initialize(args = {})
@@ -14,20 +13,20 @@ class Puppet::Network::HTTP::Mongrel
raise ArgumentError, ":address must be specified." unless args[:address]
raise ArgumentError, ":port must be specified." unless args[:port]
raise "Mongrel server is already listening" if listening?
-
+
@protocols = args[:protocols]
@handlers = args[:handlers]
- @server = Mongrel::HttpServer.new(args[:address], args[:port])
-
+ @server = Mongrel::HttpServer.new(args[:address], args[:port])
setup_handlers
- @server.run
@listening = true
+ @server.run
end
def unlisten
raise "Mongrel server is not listening" unless listening?
- @server.graceful_shutdown
+ @server.stop
+ @server = nil
@listening = false
end
@@ -39,16 +38,16 @@ class Puppet::Network::HTTP::Mongrel
def setup_handlers
@protocols.each do |protocol|
+ klass = class_for_protocol(protocol)
@handlers.each do |handler|
- class_for_protocol(protocol).new(:server => @server, :handler => handler)
+ @server.register('/' + handler.to_s, klass.new(:server => @server, :handler => handler))
+ @server.register('/' + handler.to_s + 's', klass.new(:server => @server, :handler => handler))
end
end
end
- # TODO/FIXME: need a spec which forces delegation to the real class
def class_for_protocol(protocol)
return Puppet::Network::HTTP::MongrelREST if protocol.to_sym == :rest
- return Puppet::Network::HTTP::MongrelXMLRPC if protocol.to_sym == :xmlrpc
raise ArgumentError, "Unknown protocol [#{protocol}]."
end
end
diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb
index 6c24e360c..2a3d4f143 100644
--- a/lib/puppet/network/http/mongrel/rest.rb
+++ b/lib/puppet/network/http/mongrel/rest.rb
@@ -1,36 +1,44 @@
require 'puppet/network/http/handler'
-class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler
+class Puppet::Network::HTTP::MongrelREST < Mongrel::HttpHandler
+
+ include Puppet::Network::HTTP::Handler
+
+ def initialize(args={})
+ super()
+ initialize_for_puppet(args)
+ end
private
-
- def register_handler
- @server.register('/' + @handler.to_s, self)
- @server.register('/' + @handler.to_s + 's', self)
- end
-
+
+ # which HTTP verb was used in this request
def http_method(request)
request.params[Mongrel::Const::REQUEST_METHOD]
end
+ # what path was requested?
def path(request)
# LAK:NOTE See http://snurl.com/21zf8 [groups_google_com]
x = '/' + request.params[Mongrel::Const::REQUEST_PATH].split('/')[1]
end
+ # return the key included in the request path
def request_key(request)
# LAK:NOTE See http://snurl.com/21zf8 [groups_google_com]
x = request.params[Mongrel::Const::REQUEST_PATH].split('/')[2]
end
+ # return the request body
def body(request)
request.body
end
+ # return the query params for this request
def params(request)
Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"])
end
+ # produce the body of the response
def encode_result(request, response, result, status = 200)
response.start(status) do |head, body|
body.write(result)
diff --git a/lib/puppet/network/http/mongrel/xmlrpc.rb b/lib/puppet/network/http/mongrel/xmlrpc.rb
deleted file mode 100644
index 92acd4f0e..000000000
--- a/lib/puppet/network/http/mongrel/xmlrpc.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-class Puppet::Network::HTTP::MongrelXMLRPC
- def initialize(args = {})
- end
-end
diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb
index c4b2ed3c6..3a37e2071 100644
--- a/lib/puppet/network/http/webrick.rb
+++ b/lib/puppet/network/http/webrick.rb
@@ -1,11 +1,17 @@
require 'webrick'
require 'webrick/https'
require 'puppet/network/http/webrick/rest'
-require 'puppet/network/http/webrick/xmlrpc'
+require 'thread'
class Puppet::Network::HTTP::WEBrick
def initialize(args = {})
@listening = false
+ @mutex = Mutex.new
+ end
+
+ def self.class_for_protocol(protocol)
+ return Puppet::Network::HTTP::WEBrickREST if protocol.to_sym == :rest
+ raise "Unknown protocol [#{protocol}]."
end
def listen(args = {})
@@ -13,39 +19,44 @@ class Puppet::Network::HTTP::WEBrick
raise ArgumentError, ":protocols must be specified." if !args[:protocols] or args[:protocols].empty?
raise ArgumentError, ":address must be specified." unless args[:address]
raise ArgumentError, ":port must be specified." unless args[:port]
- raise "WEBrick server is already listening" if listening?
@protocols = args[:protocols]
@handlers = args[:handlers]
@server = WEBrick::HTTPServer.new(:BindAddress => args[:address], :Port => args[:port])
setup_handlers
- @server.start
- @listening = true
+
+ @mutex.synchronize do
+ raise "WEBrick server is already listening" if @listening
+ @listening = true
+ @thread = Thread.new { @server.start }
+ end
end
def unlisten
- raise "WEBrick server is not listening" unless listening?
- @server.shutdown
- @listening = false
+ @mutex.synchronize do
+ raise "WEBrick server is not listening" unless @listening
+ @server.shutdown
+ @thread.join
+ @server = nil
+ @listening = false
+ end
end
def listening?
- @listening
+ @mutex.synchronize do
+ @listening
+ end
end
-
+
private
def setup_handlers
@protocols.each do |protocol|
+ klass = self.class.class_for_protocol(protocol)
@handlers.each do |handler|
- class_for_protocol(protocol).new(:server => @server, :handler => handler)
+ @server.mount('/' + handler.to_s, klass, handler)
+ @server.mount('/' + handler.to_s + 's', klass, handler)
end
end
end
-
- def class_for_protocol(protocol)
- return Puppet::Network::HTTP::WEBrickREST if protocol.to_sym == :rest
- return Puppet::Network::HTTP::WEBrickXMLRPC if protocol.to_sym == :xmlrpc
- raise ArgumentError, "Unknown protocol [#{protocol}]."
- end
end
diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb
index 8cda079e2..b43912196 100644
--- a/lib/puppet/network/http/webrick/rest.rb
+++ b/lib/puppet/network/http/webrick/rest.rb
@@ -1,19 +1,22 @@
require 'puppet/network/http/handler'
-class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler
+class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet
+
+ include Puppet::Network::HTTP::Handler
+
+ def initialize(server, handler)
+ raise ArgumentError, "server is required" unless server
+ super(server)
+ initialize_for_puppet(:server => server, :handler => handler)
+ end
# WEBrick uses a service() method to respond to requests. Simply delegate to the handler response() method.
def service(request, response)
process(request, response)
end
-
+
private
- def register_handler
- @server.mount('/' + @handler.to_s, self)
- @server.mount('/' + @handler.to_s + 's', self)
- end
-
def http_method(request)
request.request_method
end
diff --git a/lib/puppet/network/http/webrick/xmlrpc.rb b/lib/puppet/network/http/webrick/xmlrpc.rb
deleted file mode 100644
index 793708f8a..000000000
--- a/lib/puppet/network/http/webrick/xmlrpc.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-class Puppet::Network::HTTP::WEBrickXMLRPC
- def initialize(args = {})
- end
-end
diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb
index f2c8dc18c..cab14519b 100644
--- a/lib/puppet/network/server.rb
+++ b/lib/puppet/network/server.rb
@@ -10,44 +10,44 @@ class Puppet::Network::Server
raise(ArgumentError, "Must specify :address or configure Puppet :bindaddress.")
@port = args[:port] || Puppet[:masterport] ||
raise(ArgumentError, "Must specify :port or configure Puppet :masterport")
- @protocols = []
- @listening = false
- @routes = {}
- self.register(args[:handlers]) if args[:handlers]
+ @protocols = [ :rest ]
+ @listening = false
+ @routes = {}
+ self.register(args[:handlers]) if args[:handlers]
end
def register(*indirections)
- raise ArgumentError, "Indirection names are required." if indirections.empty?
- indirections.flatten.each { |i| @routes[i.to_sym] = true }
+ raise ArgumentError, "Indirection names are required." if indirections.empty?
+ indirections.flatten.each { |i| @routes[i.to_sym] = true }
end
def unregister(*indirections)
- raise "Cannot unregister indirections while server is listening." if listening?
- indirections = @routes.keys if indirections.empty?
-
- indirections.flatten.each do |i|
- raise(ArgumentError, "Indirection [%s] is unknown." % i) unless @routes[i.to_sym]
- end
+ raise "Cannot unregister indirections while server is listening." if listening?
+ indirections = @routes.keys if indirections.empty?
+
+ indirections.flatten.each do |i|
+ raise(ArgumentError, "Indirection [%s] is unknown." % i) unless @routes[i.to_sym]
+ end
- indirections.flatten.each do |i|
- @routes.delete(i.to_sym)
- end
+ indirections.flatten.each do |i|
+ @routes.delete(i.to_sym)
+ end
end
def listening?
- @listening
+ @listening
end
def listen
- raise "Cannot listen -- already listening." if listening?
- http_server.listen(@routes.dup)
- @listening = true
+ raise "Cannot listen -- already listening." if listening?
+ @listening = true
+ http_server.listen(:address => address, :port => port, :handlers => @routes.keys, :protocols => protocols)
end
def unlisten
- raise "Cannot unlisten -- not currently listening." unless listening?
- http_server.unlisten
- @listening = false
+ raise "Cannot unlisten -- not currently listening." unless listening?
+ http_server.unlisten
+ @listening = false
end
def http_server_class
diff --git a/spec/integration/indirector/rest.rb b/spec/integration/indirector/rest.rb
new file mode 100644
index 000000000..cafcce713
--- /dev/null
+++ b/spec/integration/indirector/rest.rb
@@ -0,0 +1,460 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'puppet/network/server'
+require 'puppet/indirector'
+require 'puppet/indirector/rest'
+
+# a fake class that will be indirected via REST
+class Puppet::TestIndirectedFoo
+ extend Puppet::Indirector
+ indirects :test_indirected_foo, :terminus_setting => :test_indirected_foo_terminus
+
+ attr_reader :value
+
+ def initialize(value = 0)
+ @value = value
+ end
+
+ def self.from_yaml(yaml)
+ YAML.load(yaml)
+ end
+
+ def name
+ "bob"
+ end
+end
+
+# empty Terminus class -- this would normally have to be in a directory findable by the autoloader, but we short-circuit that below
+class Puppet::TestIndirectedFoo::Rest < Puppet::Indirector::REST
+end
+
+
+describe Puppet::Indirector::REST do
+ describe "when using webrick" do
+ before :each do
+ Puppet[:servertype] = 'webrick'
+ @params = { :address => "127.0.0.1", :port => 34343, :handlers => [ :test_indirected_foo ] }
+ @server = Puppet::Network::Server.new(@params)
+ @server.listen
+
+ # the autoloader was clearly not written test-first. We subvert the integration test to get around its bullshit.
+ Puppet::Indirector::Terminus.stubs(:terminus_class).returns(Puppet::TestIndirectedFoo::Rest)
+ Puppet::TestIndirectedFoo.indirection.stubs(:terminus_class).returns :rest
+
+ # Stub the connection information.
+ Puppet::TestIndirectedFoo.indirection.terminus(:rest).stubs(:rest_connection_details).returns(:host => "localhost", :port => 34343)
+ end
+
+ describe "when finding a model instance over REST" do
+ describe "when a matching model instance can be found" do
+ before :each do
+ @model_instance = Puppet::TestIndirectedFoo.new(23)
+ @mock_model = stub('faked model', :find => @model_instance)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should not fail" do
+ Puppet::TestIndirectedFoo.find('bar')
+ lambda { Puppet::TestIndirectedFoo.find('bar') }.should_not raise_error
+ end
+
+ it 'should return an instance of the model class' do
+ Puppet::TestIndirectedFoo.find('bar').class.should == Puppet::TestIndirectedFoo
+ end
+
+ it 'should return the instance of the model class associated with the provided lookup key' do
+ Puppet::TestIndirectedFoo.find('bar').value.should == @model_instance.value
+ end
+
+ it 'should set an expiration on model instance' do
+ Puppet::TestIndirectedFoo.find('bar').expiration.should_not be_nil
+ end
+ end
+
+ describe "when no matching model instance can be found" do
+ before :each do
+ @mock_model = stub('faked model', :find => nil)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should return nil" do
+ Puppet::TestIndirectedFoo.find('bar').should be_nil
+ end
+ end
+
+ describe "when an exception is encountered in looking up a model instance" do
+ before :each do
+ @mock_model = stub('faked model')
+ @mock_model.stubs(:find).raises(RuntimeError)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should raise an exception" do
+ lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe "when searching for model instances over REST" do
+ describe "when matching model instances can be found" do
+ before :each do
+ @model_instances = [ Puppet::TestIndirectedFoo.new(23), Puppet::TestIndirectedFoo.new(24) ]
+ @mock_model = stub('faked model', :search => @model_instances)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should not fail" do
+ lambda { Puppet::TestIndirectedFoo.search('bar') }.should_not raise_error
+ end
+
+ it 'should return all matching results' do
+ Puppet::TestIndirectedFoo.search('bar').length.should == @model_instances.length
+ end
+
+ it 'should return model instances' do
+ Puppet::TestIndirectedFoo.search('bar').each do |result|
+ result.class.should == Puppet::TestIndirectedFoo
+ end
+ end
+
+ it 'should return the instance of the model class associated with the provided lookup key' do
+ Puppet::TestIndirectedFoo.search('bar').collect(&:value).should == @model_instances.collect(&:value)
+ end
+
+ it 'should set a version timestamp on model instances' do
+ pending("Luke looking at why this version magic might not be working") do
+ Puppet::TestIndirectedFoo.search('bar').each do |result|
+ result.version.should_not be_nil
+ end
+ end
+ end
+ end
+
+ describe "when no matching model instance can be found" do
+ before :each do
+ @mock_model = stub('faked model', :find => nil)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should return nil" do
+ Puppet::TestIndirectedFoo.find('bar').should be_nil
+ end
+ end
+
+ describe "when an exception is encountered in looking up a model instance" do
+ before :each do
+ @mock_model = stub('faked model')
+ @mock_model.stubs(:find).raises(RuntimeError)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should raise an exception" do
+ lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe "when destroying a model instance over REST" do
+ describe "when a matching model instance can be found" do
+ before :each do
+ @mock_model = stub('faked model', :destroy => true)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should not fail" do
+ lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should_not raise_error
+ end
+
+ it 'should return success' do
+ Puppet::TestIndirectedFoo.destroy('bar').should == true
+ end
+ end
+
+ describe "when no matching model instance can be found" do
+ before :each do
+ @mock_model = stub('faked model', :destroy => false)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should return failure" do
+ Puppet::TestIndirectedFoo.destroy('bar').should == false
+ end
+ end
+
+ describe "when an exception is encountered in destroying a model instance" do
+ before :each do
+ @mock_model = stub('faked model')
+ @mock_model.stubs(:destroy).raises(RuntimeError)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should raise an exception" do
+ lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe "when saving a model instance over REST" do
+ before :each do
+ @instance = Puppet::TestIndirectedFoo.new(42)
+ @mock_model = stub('faked model', :from_yaml => @instance)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:model).returns(@mock_model)
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).returns(@instance)
+ end
+
+ describe "when a successful save can be performed" do
+ before :each do
+ end
+
+ it "should not fail" do
+ lambda { @instance.save }.should_not raise_error
+ end
+
+ it 'should return an instance of the model class' do
+ @instance.save.class.should == Puppet::TestIndirectedFoo
+ end
+
+ it 'should return a matching instance of the model class' do
+ @instance.save.value.should == @instance.value
+ end
+ end
+
+ describe "when a save cannot be completed" do
+ before :each do
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).returns(false)
+ end
+
+ it "should return failure" do
+ @instance.save.should == false
+ end
+ end
+
+ describe "when an exception is encountered in performing a save" do
+ before :each do
+ Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).raises(RuntimeError)
+ end
+
+ it "should raise an exception" do
+ lambda { @instance.save }.should raise_error(RuntimeError)
+ end
+ end
+ end
+
+ after :each do
+ @server.unlisten
+ end
+ end
+
+ describe "when using mongrel" do
+ confine "Mongrel is not available" => Puppet.features.mongrel?
+
+ before :each do
+ Puppet[:servertype] = 'mongrel'
+ @params = { :address => "127.0.0.1", :port => 34343, :handlers => [ :test_indirected_foo ] }
+ @server = Puppet::Network::Server.new(@params)
+ @server.listen
+
+ # the autoloader was clearly not written test-first. We subvert the integration test to get around its bullshit.
+ Puppet::Indirector::Terminus.stubs(:terminus_class).returns(Puppet::TestIndirectedFoo::Rest)
+ Puppet::TestIndirectedFoo.indirection.stubs(:terminus_class).returns :rest
+
+ # Stub the connection information.
+ Puppet::TestIndirectedFoo.indirection.terminus(:rest).stubs(:rest_connection_details).returns(:host => "localhost", :port => 34343)
+ end
+
+ describe "when finding a model instance over REST" do
+ describe "when a matching model instance can be found" do
+ before :each do
+ @model_instance = Puppet::TestIndirectedFoo.new(23)
+ @mock_model = stub('faked model', :find => @model_instance)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should not fail" do
+ lambda { Puppet::TestIndirectedFoo.find('bar') }.should_not raise_error
+ end
+
+ it 'should return an instance of the model class' do
+ Puppet::TestIndirectedFoo.find('bar').class.should == Puppet::TestIndirectedFoo
+ end
+
+ it 'should return the instance of the model class associated with the provided lookup key' do
+ Puppet::TestIndirectedFoo.find('bar').value.should == @model_instance.value
+ end
+
+ it 'should set an expiration on model instance' do
+ Puppet::TestIndirectedFoo.find('bar').expiration.should_not be_nil
+ end
+ end
+
+ describe "when no matching model instance can be found" do
+ before :each do
+ @mock_model = stub('faked model', :find => nil)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should return nil" do
+ Puppet::TestIndirectedFoo.find('bar').should be_nil
+ end
+ end
+
+ describe "when an exception is encountered in looking up a model instance" do
+ before :each do
+ @mock_model = stub('faked model')
+ @mock_model.stubs(:find).raises(RuntimeError)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should raise an exception" do
+ lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe "when searching for model instances over REST" do
+ describe "when matching model instances can be found" do
+ before :each do
+ @model_instances = [ Puppet::TestIndirectedFoo.new(23), Puppet::TestIndirectedFoo.new(24) ]
+ @mock_model = stub('faked model', :search => @model_instances)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should not fail" do
+ lambda { Puppet::TestIndirectedFoo.search('bar') }.should_not raise_error
+ end
+
+ it 'should return all matching results' do
+ Puppet::TestIndirectedFoo.search('bar').length.should == @model_instances.length
+ end
+
+ it 'should return model instances' do
+ Puppet::TestIndirectedFoo.search('bar').each do |result|
+ result.class.should == Puppet::TestIndirectedFoo
+ end
+ end
+
+ it 'should return the instance of the model class associated with the provided lookup key' do
+ Puppet::TestIndirectedFoo.search('bar').collect(&:value).should == @model_instances.collect(&:value)
+ end
+
+ it 'should set an expiration on model instances' do
+ Puppet::TestIndirectedFoo.search('bar').each do |result|
+ result.expiration.should_not be_nil
+ end
+ end
+ end
+
+ describe "when no matching model instance can be found" do
+ before :each do
+ @mock_model = stub('faked model', :find => nil)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should return nil" do
+ Puppet::TestIndirectedFoo.find('bar').should be_nil
+ end
+ end
+
+ describe "when an exception is encountered in looking up a model instance" do
+ before :each do
+ @mock_model = stub('faked model')
+ @mock_model.stubs(:find).raises(RuntimeError)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should raise an exception" do
+ lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe "when destroying a model instance over REST" do
+ describe "when a matching model instance can be found" do
+ before :each do
+ @mock_model = stub('faked model', :destroy => true)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should not fail" do
+ lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should_not raise_error
+ end
+
+ it 'should return success' do
+ Puppet::TestIndirectedFoo.destroy('bar').should == true
+ end
+ end
+
+ describe "when no matching model instance can be found" do
+ before :each do
+ @mock_model = stub('faked model', :destroy => false)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should return failure" do
+ Puppet::TestIndirectedFoo.destroy('bar').should == false
+ end
+ end
+
+ describe "when an exception is encountered in destroying a model instance" do
+ before :each do
+ @mock_model = stub('faked model')
+ @mock_model.stubs(:destroy).raises(RuntimeError)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ end
+
+ it "should raise an exception" do
+ lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe "when saving a model instance over REST" do
+ before :each do
+ @instance = Puppet::TestIndirectedFoo.new(42)
+ @mock_model = stub('faked model', :from_yaml => @instance)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:model).returns(@mock_model)
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).returns(@instance)
+ end
+
+ describe "when a successful save can be performed" do
+ before :each do
+ end
+
+ it "should not fail" do
+ lambda { @instance.save }.should_not raise_error
+ end
+
+ it 'should return an instance of the model class' do
+ @instance.save.class.should == Puppet::TestIndirectedFoo
+ end
+
+ it 'should return a matching instance of the model class' do
+ @instance.save.value.should == @instance.value
+ end
+ end
+
+ describe "when a save cannot be completed" do
+ before :each do
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).returns(false)
+ end
+
+ it "should return failure" do
+ @instance.save.should == false
+ end
+ end
+
+ describe "when an exception is encountered in performing a save" do
+ before :each do
+ Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).raises(RuntimeError)
+ end
+
+ it "should raise an exception" do
+ lambda { @instance.save }.should raise_error(RuntimeError)
+ end
+ end
+ end
+
+ after :each do
+ @server.unlisten
+ end
+ end
+end
diff --git a/spec/integration/network/server/mongrel.rb b/spec/integration/network/server/mongrel.rb
new file mode 100644
index 000000000..65caf78c9
--- /dev/null
+++ b/spec/integration/network/server/mongrel.rb
@@ -0,0 +1,46 @@
+require File.dirname(__FILE__) + '/../../../spec_helper'
+require 'puppet/network/server'
+require 'socket'
+
+describe Puppet::Network::Server do
+ describe "when using mongrel" do
+ confine "Mongrel is not available" => Puppet.features.mongrel?
+
+ before :each do
+ Puppet[:servertype] = 'mongrel'
+ @params = { :address => "127.0.0.1", :port => 34346, :handlers => [ :node ] }
+ @server = Puppet::Network::Server.new(@params)
+ end
+
+ describe "before listening" do
+ it "should not be reachable at the specified address and port" do
+ lambda { TCPSocket.new('127.0.0.1', 34346) }.should raise_error(Errno::ECONNREFUSED)
+ end
+ end
+
+ describe "when listening" do
+ it "should be reachable on the specified address and port" do
+ @server.listen
+ lambda { TCPSocket.new('127.0.0.1', 34346) }.should_not raise_error
+ end
+
+ it "should not allow multiple servers to listen on the same address and port" do
+ @server.listen
+ @server2 = Puppet::Network::Server.new(@params)
+ lambda { @server2.listen }.should raise_error
+ end
+ end
+
+ describe "after unlistening" do
+ it "should not be reachable on the port and address assigned" do
+ @server.listen
+ @server.unlisten
+ lambda { TCPSocket.new('127.0.0.1', 34346) }.should raise_error(Errno::ECONNREFUSED)
+ end
+ end
+
+ after :each do
+ @server.unlisten if @server.listening?
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/integration/network/server/webrick.rb b/spec/integration/network/server/webrick.rb
new file mode 100644
index 000000000..0ab8d89e4
--- /dev/null
+++ b/spec/integration/network/server/webrick.rb
@@ -0,0 +1,46 @@
+require File.dirname(__FILE__) + '/../../../spec_helper'
+require 'puppet/network/server'
+require 'socket'
+
+describe Puppet::Network::Server do
+ describe "when using webrick" do
+ before :each do
+ Puppet[:servertype] = 'webrick'
+ @params = { :address => "127.0.0.1", :port => 34343, :handlers => [ :node ] }
+ end
+
+ describe "before listening" do
+ it "should not be reachable at the specified address and port" do
+ lambda { TCPSocket.new('127.0.0.1', 34343) }.should raise_error
+ end
+ end
+
+ describe "when listening" do
+ it "should be reachable on the specified address and port" do
+ @server = Puppet::Network::Server.new(@params.merge(:port => 34343))
+ @server.listen
+ lambda { TCPSocket.new('127.0.0.1', 34343) }.should_not raise_error
+ end
+
+ it "should not allow multiple servers to listen on the same address and port" do
+ @server = Puppet::Network::Server.new(@params.merge(:port => 34343))
+ @server.listen
+ @server2 = Puppet::Network::Server.new(@params.merge(:port => 34343))
+ lambda { @server2.listen }.should raise_error
+ end
+
+ after :each do
+ @server.unlisten if @server.listening?
+ end
+ end
+
+ describe "after unlistening" do
+ it "should not be reachable on the port and address assigned" do
+ @server = Puppet::Network::Server.new(@params.merge(:port => 34343))
+ @server.listen
+ @server.unlisten
+ lambda { TCPSocket.new('127.0.0.1', 34343) }.should raise_error(Errno::ECONNREFUSED)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/spec.opts b/spec/spec.opts
index 2f9bf0d0a..695852c2f 100644
--- a/spec/spec.opts
+++ b/spec/spec.opts
@@ -1,3 +1,5 @@
+--format
+s
--colour
--loadby
mtime
diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb
index e8ab9633b..cefd0557e 100755
--- a/spec/unit/indirector/indirection.rb
+++ b/spec/unit/indirector/indirection.rb
@@ -356,9 +356,9 @@ describe Puppet::Indirector::Indirection do
it_should_behave_like "Indirection Delegator"
it_should_behave_like "Delegation Authorizer"
- it "should return nil" do
- @terminus.stubs(:destroy)
- @indirection.destroy("/my/key").should be_nil
+ it "should return the result of removing the instance" do
+ @terminus.stubs(:destroy).returns "yayness"
+ @indirection.destroy("/my/key").should == "yayness"
end
describe "when caching is enabled" do
diff --git a/spec/unit/indirector/node/rest.rb b/spec/unit/indirector/node/rest.rb
index 33cfda426..3b99e33c4 100755
--- a/spec/unit/indirector/node/rest.rb
+++ b/spec/unit/indirector/node/rest.rb
@@ -4,9 +4,9 @@ require File.dirname(__FILE__) + '/../../../spec_helper'
require 'puppet/indirector/node/rest'
-describe Puppet::Node::REST do
+describe Puppet::Node::Rest do
before do
- @searcher = Puppet::Node::REST.new
+ @searcher = Puppet::Node::Rest.new
end
diff --git a/spec/unit/indirector/rest.rb b/spec/unit/indirector/rest.rb
index 3c4716dc8..1a1064491 100755
--- a/spec/unit/indirector/rest.rb
+++ b/spec/unit/indirector/rest.rb
@@ -4,11 +4,11 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/indirector/rest'
describe Puppet::Indirector::REST do
- # FIXME : TODO / look through this, does this make sense?
before do
Puppet::Indirector::Terminus.stubs(:register_terminus_class)
- @model = mock 'model'
- @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model
+ @model = stub('model')
+ @instance = stub('model instance')
+ @indirection = stub('indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model)
Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection)
@rest_class = Class.new(Puppet::Indirector::REST) do
@@ -19,12 +19,354 @@ describe Puppet::Indirector::REST do
@searcher = @rest_class.new
end
+
+ describe "when locating the REST connection" do
+ before do
+ Puppet.settings.stubs(:value).returns("whatever")
+ end
- it "should return an instance of the indirected model"
- it "should deserialize result data after a call into a Model instance for find"
- it "should deserialize result data after a call into a list of Model instances for search"
- it "should deserialize result data after a call into a boolean for save"
- it "should deserialize result data after a call into a boolean for destroy"
- it "should generate an error when result data deserializes improperly"
- it "should generate an error when result data specifies an error"
+ it "should return the :server setting as the host" do
+ Puppet.settings.expects(:value).with(:server).returns "myserver"
+ @searcher.rest_connection_details[:host].should == "myserver"
+ end
+
+ it "should return the :masterport (as an Integer) as the port" do
+ Puppet.settings.expects(:value).with(:masterport).returns "1234"
+ @searcher.rest_connection_details[:port].should == 1234
+ end
+ end
+
+ describe "when doing a network fetch" do
+ before :each do
+ Net::HTTP.stubs(:start).returns('result')
+ @details = { :host => '127.0.0.1', :port => 34343 }
+ @searcher.stubs(:rest_connection_details).returns(@details)
+ end
+
+ it "should accept a path" do
+ lambda { @search.network_fetch('foo') }.should_not raise_error(ArgumentError)
+ end
+
+ it "should require a path" do
+ lambda { @searcher.network_fetch }.should raise_error(ArgumentError)
+ end
+
+ it "should look up connection details" do
+ @searcher.expects(:rest_connection_details).returns(@details)
+ @searcher.network_fetch('foo')
+ end
+
+ it "should use the GET http method" do
+ @mock_result = stub('mock result', :body => 'result')
+ @mock_connection = mock('mock http connection', :get => @mock_result)
+ @searcher.stubs(:network).yields(@mock_connection)
+ @searcher.network_fetch('foo')
+ end
+
+ it "should use the appropriate remote server" do
+ Net::HTTP.expects(:start).with {|host, port| host == @details[:host] }
+ @searcher.network_fetch('foo')
+ end
+
+ it "should use the appropriate remote port" do
+ Net::HTTP.expects(:start).with {|host, port| port == @details[:port] }
+ @searcher.network_fetch('foo')
+ end
+
+ it "should use the provided path" do
+ @mock_result = stub('mock result', :body => 'result')
+ @mock_connection = stub('mock http connection')
+ @mock_connection.expects(:get).with('/foo').returns(@mock_result)
+ @searcher.stubs(:network).yields(@mock_connection)
+ @searcher.network_fetch('foo')
+ end
+
+ it "should return the results of the GET request" do
+ @searcher.network_fetch('foo').should == 'result'
+ end
+ end
+
+ describe "when doing a network delete" do
+ before :each do
+ Net::HTTP.stubs(:start).returns('result')
+ @details = { :host => '127.0.0.1', :port => 34343 }
+ @searcher.stubs(:rest_connection_details).returns(@details)
+ end
+
+ it "should accept a path" do
+ lambda { @search.network_delete('foo') }.should_not raise_error(ArgumentError)
+ end
+
+ it "should require a path" do
+ lambda { @searcher.network_delete }.should raise_error(ArgumentError)
+ end
+
+ it "should look up connection details" do
+ @searcher.expects(:rest_connection_details).returns(@details)
+ @searcher.network_delete('foo')
+ end
+
+ it "should use the DELETE http method" do
+ @mock_result = stub('mock result', :body => 'result')
+ @mock_connection = mock('mock http connection', :delete => @mock_result)
+ @searcher.stubs(:network).yields(@mock_connection)
+ @searcher.network_delete('foo')
+ end
+
+ it "should use the appropriate remote server" do
+ Net::HTTP.expects(:start).with {|host, port| host == @details[:host] }
+ @searcher.network_delete('foo')
+ end
+
+ it "should use the appropriate remote port" do
+ Net::HTTP.expects(:start).with {|host, port| port == @details[:port] }
+ @searcher.network_delete('foo')
+ end
+
+ it "should use the provided path" do
+ @mock_result = stub('mock result', :body => 'result')
+ @mock_connection = stub('mock http connection')
+ @mock_connection.expects(:delete).with('/foo').returns(@mock_result)
+ @searcher.stubs(:network).yields(@mock_connection)
+ @searcher.network_delete('foo')
+ end
+
+ it "should return the results of the DELETE request" do
+ @searcher.network_delete('foo').should == 'result'
+ end
+ end
+
+ describe "when doing a network put" do
+ before :each do
+ Net::HTTP.stubs(:start).returns('result')
+ @details = { :host => '127.0.0.1', :port => 34343 }
+ @data = { :foo => 'bar' }
+ @searcher.stubs(:rest_connection_details).returns(@details)
+ end
+
+ it "should accept a path and data" do
+ lambda { @search.network_put('foo', @data) }.should_not raise_error(ArgumentError)
+ end
+
+ it "should require a path and data" do
+ lambda { @searcher.network_put('foo') }.should raise_error(ArgumentError)
+ end
+
+ it "should look up connection details" do
+ @searcher.expects(:rest_connection_details).returns(@details)
+ @searcher.network_put('foo', @data)
+ end
+
+ it "should use the appropriate remote server" do
+ Net::HTTP.expects(:start).with {|host, port| host == @details[:host] }
+ @searcher.network_put('foo', @data)
+ end
+
+ it "should use the appropriate remote port" do
+ Net::HTTP.expects(:start).with {|host, port| port == @details[:port] }
+ @searcher.network_put('foo', @data)
+ end
+
+ it "should use the PUT http method" do
+ @mock_result = stub('mock result', :body => 'result')
+ @mock_connection = mock('mock http connection', :put => @mock_result)
+ @searcher.stubs(:network).yields(@mock_connection)
+ @searcher.network_put('foo', @data)
+ end
+
+ it "should use the provided path" do
+ @mock_result = stub('mock result', :body => 'result')
+ @mock_connection = stub('mock http connection')
+ @mock_connection.expects(:put).with {|path, data| path == '/foo' }.returns(@mock_result)
+ @searcher.stubs(:network).yields(@mock_connection)
+ @searcher.network_put('foo', @data)
+ end
+
+ it "should use the provided data" do
+ @mock_result = stub('mock result', :body => 'result')
+ @mock_connection = stub('mock http connection')
+ @mock_connection.expects(:put).with {|path, data| data == @data }.returns(@mock_result)
+ @searcher.stubs(:network).yields(@mock_connection)
+ @searcher.network_put('foo', @data)
+ end
+
+ it "should return the results of the PUT request" do
+ @searcher.network_put('foo', @data).should == 'result'
+ end
+ end
+
+ describe "when doing a find" do
+ before :each do
+ @result = { :foo => 'bar'}.to_yaml
+ @searcher.stubs(:network_fetch).returns(@result) # neuter the network connection
+ @model.stubs(:from_yaml).returns(@instance)
+
+ @request = stub 'request', :key => 'foo'
+ end
+
+ it "should look up the model instance over the network" do
+ @searcher.expects(:network_fetch).returns(@result)
+ @searcher.find(@request)
+ end
+
+ it "should look up the model instance using the named indirection" do
+ @searcher.expects(:network_fetch).with {|path| path =~ %r{^#{@indirection.name.to_s}/} }.returns(@result)
+ @searcher.find(@request)
+ end
+
+ it "should look up the model instance using the provided key" do
+ @searcher.expects(:network_fetch).with {|path| path =~ %r{/foo$} }.returns(@result)
+ @searcher.find(@request)
+ end
+
+ it "should deserialize result data to a Model instance" do
+ @model.expects(:from_yaml)
+ @searcher.find(@request)
+ end
+
+ it "should return the deserialized Model instance" do
+ @searcher.find(@request).should == @instance
+ end
+
+ it "should return nil when deserialized model instance is nil" do
+ @model.stubs(:from_yaml).returns(nil)
+ @searcher.find(@request).should be_nil
+ end
+
+ it "should generate an error when result data deserializes improperly" do
+ @model.stubs(:from_yaml).raises(ArgumentError)
+ lambda { @searcher.find(@request) }.should raise_error(ArgumentError)
+ end
+
+ it "should generate an error when result data specifies an error" do
+ @searcher.stubs(:network_fetch).returns(RuntimeError.new("bogus").to_yaml)
+ lambda { @searcher.find(@request) }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "when doing a search" do
+ before :each do
+ @result = [1, 2].to_yaml
+ @searcher.stubs(:network_fetch).returns(@result)
+ @model.stubs(:from_yaml).returns(@instance)
+
+ @request = stub 'request', :key => 'foo'
+ end
+
+ it "should look up the model data over the network" do
+ @searcher.expects(:network_fetch).returns(@result)
+ @searcher.search(@request)
+ end
+
+ it "should look up the model instance using the named indirection" do
+ @searcher.expects(:network_fetch).with {|path| path =~ %r{^#{@indirection.name.to_s}s/} }.returns(@result)
+ @searcher.search(@request)
+ end
+
+ it "should look up the model instance using the provided key" do
+ @searcher.expects(:network_fetch).with {|path| path =~ %r{/foo$} }.returns(@result)
+ @searcher.search(@request)
+ end
+
+ it "should deserialize result data into a list of Model instances" do
+ @model.expects(:from_yaml).at_least(2)
+ @searcher.search(@request)
+ end
+
+ it "should generate an error when result data deserializes improperly" do
+ @model.stubs(:from_yaml).raises(ArgumentError)
+ lambda { @searcher.search(@request) }.should raise_error(ArgumentError)
+ end
+
+ it "should generate an error when result data specifies an error" do
+ @searcher.stubs(:network_fetch).returns(RuntimeError.new("bogus").to_yaml)
+ lambda { @searcher.search(@request) }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "when doing a destroy" do
+ before :each do
+ @result = true.to_yaml
+ @searcher.stubs(:network_delete).returns(@result) # neuter the network connection
+ @model.stubs(:from_yaml).returns(@instance)
+
+ @request = stub 'request', :key => 'foo'
+ end
+
+ it "should look up the model instance over the network" do
+ @searcher.expects(:network_delete).returns(@result)
+ @searcher.destroy(@request)
+ end
+
+ it "should look up the model instance using the named indirection" do
+ @searcher.expects(:network_delete).with {|path| path =~ %r{^#{@indirection.name.to_s}/} }.returns(@result)
+ @searcher.destroy(@request)
+ end
+
+ it "should look up the model instance using the provided key" do
+ @searcher.expects(:network_delete).with {|path| path =~ %r{/foo$} }.returns(@result)
+ @searcher.destroy(@request)
+ end
+
+ it "should deserialize result data" do
+ YAML.expects(:load).with(@result)
+ @searcher.destroy(@request)
+ end
+
+ it "should return deserialized result data" do
+ @searcher.destroy(@request).should == true
+ end
+
+ it "should generate an error when result data specifies an error" do
+ @searcher.stubs(:network_delete).returns(RuntimeError.new("bogus").to_yaml)
+ lambda { @searcher.destroy(@request) }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "when doing a save" do
+ before :each do
+ @result = { :foo => 'bar'}.to_yaml
+ @searcher.stubs(:network_put).returns(@result) # neuter the network connection
+ @model.stubs(:from_yaml).returns(@instance)
+
+ @request = stub 'request', :instance => @instance
+ end
+
+ it "should save the model instance over the network" do
+ @searcher.expects(:network_put).returns(@result)
+ @searcher.save(@request)
+ end
+
+ it "should save the model instance using the named indirection" do
+ @searcher.expects(:network_put).with do |path, data|
+ path =~ %r{^#{@indirection.name.to_s}/} and
+ data == @instance.to_yaml
+ end.returns(@result)
+ @searcher.save(@request)
+ end
+
+ it "should deserialize result data to a Model instance" do
+ @model.expects(:from_yaml)
+ @searcher.save(@request)
+ end
+
+ it "should return the resulting deserialized Model instance" do
+ @searcher.save(@request).should == @instance
+ end
+
+ it "should return nil when deserialized model instance is nil" do
+ @model.stubs(:from_yaml).returns(nil)
+ @searcher.save(@request).should be_nil
+ end
+
+ it "should generate an error when result data deserializes improperly" do
+ @model.stubs(:from_yaml).raises(ArgumentError)
+ lambda { @searcher.save(@request) }.should raise_error(ArgumentError)
+ end
+
+ it "should generate an error when result data specifies an error" do
+ @searcher.stubs(:network_put).returns(RuntimeError.new("bogus").to_yaml)
+ lambda { @searcher.save(@request) }.should raise_error(RuntimeError)
+ end
+ end
end
diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb
index a39e7354a..ccfca2f55 100644
--- a/spec/unit/network/http/mongrel.rb
+++ b/spec/unit/network/http/mongrel.rb
@@ -23,7 +23,7 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do
@mock_mongrel.stubs(:run)
@mock_mongrel.stubs(:register)
Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel)
- @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest, :xmlrpc ] }
+ @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] }
end
it "should fail if already listening" do
@@ -65,32 +65,21 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do
it "should instantiate a handler for each protocol+handler pair to configure web server routing" do
@listen_params[:protocols].each do |protocol|
- mock_handler = mock("handler instance for [#{protocol}]")
- mock_handler_class = mock("handler class for [#{protocol}]")
@listen_params[:handlers].each do |handler|
- mock_handler_class.expects(:new).with {|args|
- args[:server] == @mock_mongrel and args[:handler] == handler
- }.returns(mock_handler)
+ @mock_mongrel.expects(:register)
end
- @server.expects(:class_for_protocol).with(protocol).at_least_once.returns(mock_handler_class)
end
@server.listen(@listen_params)
end
it "should use a Mongrel + REST class to configure Mongrel when REST services are requested" do
- Puppet::Network::HTTP::MongrelREST.expects(:new).at_least_once
- @server.listen(@listen_params.merge(:protocols => [:rest]))
- end
-
- it "should use a Mongrel + XMLRPC class to configure Mongrel when XMLRPC services are requested" do
- Puppet::Network::HTTP::MongrelXMLRPC.expects(:new).at_least_once
- @server.listen(@listen_params.merge(:protocols => [:xmlrpc]))
+ @server.expects(:class_for_protocol).with(:rest).at_least_once.returns(Puppet::Network::HTTP::MongrelREST)
+ @server.listen(@listen_params)
end
it "should fail if services from an unknown protocol are requested" do
Proc.new { @server.listen(@listen_params.merge(:protocols => [ :foo ]))}.should raise_error(ArgumentError)
end
-
end
describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do
@@ -102,7 +91,7 @@ describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do
@mock_mongrel.stubs(:register)
Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel)
@server = Puppet::Network::HTTP::Mongrel.new
- @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest, :xmlrpc ] }
+ @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] }
end
it "should fail unless listening" do
@@ -111,13 +100,13 @@ describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do
it "should order mongrel server to stop" do
@server.listen(@listen_params)
- @mock_mongrel.expects(:graceful_shutdown)
+ @mock_mongrel.expects(:stop)
@server.unlisten
end
it "should not be listening" do
@server.listen(@listen_params)
- @mock_mongrel.stubs(:graceful_shutdown)
+ @mock_mongrel.stubs(:stop)
@server.unlisten
@server.should_not be_listening
end
diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb
index 9b2feb6ee..b483bbd46 100644
--- a/spec/unit/network/http/mongrel/rest.rb
+++ b/spec/unit/network/http/mongrel/rest.rb
@@ -1,8 +1,3 @@
-#!/usr/bin/env ruby
-#
-# Created by Rick Bradley on 2007-10-16.
-# Copyright (c) 2007. All rights reserved.
-
require File.dirname(__FILE__) + '/../../../../spec_helper'
require 'puppet/network/http'
@@ -34,20 +29,6 @@ describe Puppet::Network::HTTP::MongrelREST, "when initializing" do
Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(nil)
Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params) }.should raise_error(ArgumentError)
end
-
- it "should register itself with the mongrel server for the singular HTTP methods" do
- @mock_mongrel.expects(:register).with do |*args|
- args.first == '/foo' and args.last.is_a? Puppet::Network::HTTP::MongrelREST
- end
- Puppet::Network::HTTP::MongrelREST.new(@params)
- end
-
- it "should register itself with the mongrel server for the plural GET method" do
- @mock_mongrel.expects(:register).with do |*args|
- args.first == '/foos' and args.last.is_a? Puppet::Network::HTTP::MongrelREST
- end
- Puppet::Network::HTTP::MongrelREST.new(@params)
- end
end
describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do
@@ -92,7 +73,7 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do
'QUERY_STRING' => '' }.merge(params))
@mock_request.stubs(:body).returns('this is a fake request body')
@mock_model_instance = stub('indirected model instance', :save => true)
- @mock_model_class.stubs(:new).returns(@mock_model_instance)
+ @mock_model_class.stubs(:from_yaml).returns(@mock_model_instance)
end
def setup_bad_request
@@ -119,7 +100,7 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do
it "should call the model save method if the request represents an HTTP PUT" do
setup_save_request
- @mock_model_instance.expects(:save).with(:data => 'this is a fake request body')
+ @mock_model_instance.expects(:save)
@handler.process(@mock_request, @mock_response)
end
@@ -147,137 +128,138 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do
@handler.process(@mock_request, @mock_response)
end
- it "should fail to find model if key is not specified" do
- @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo'})
- @mock_response.expects(:start).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should fail to destroy model if key is not specified" do
- @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foo'})
- @mock_response.expects(:start).with(404)
- @handler.process(@mock_request, @mock_response)
- end
- it "should fail to save model if data is not specified" do
- @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foo'})
- @mock_request.stubs(:body).returns('')
- @mock_response.expects(:start).with(404)
- @handler.process(@mock_request, @mock_response)
- end
+ describe "when finding a model instance" do |variable|
+ it "should fail to find model if key is not specified" do
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo'})
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
- it "should pass HTTP request parameters to model find" do
- setup_find_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- @mock_model_class.expects(:find).with do |key, args|
- key == 'key' and args['foo'] == 'baz' and args['bar'] == 'xyzzy'
+ it "should pass HTTP request parameters to model find" do
+ setup_find_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
+ @mock_model_class.expects(:find).with do |key, args|
+ key == 'key' and args['foo'] == 'baz' and args['bar'] == 'xyzzy'
+ end
+ @handler.process(@mock_request, @mock_response)
end
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should pass HTTP request parameters to model search" do
- setup_search_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- @mock_model_class.expects(:search).with do |args|
- args['foo'] == 'baz' and args['bar'] == 'xyzzy'
- end.returns([])
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should pass HTTP request parameters to model delete" do
- setup_destroy_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- @mock_model_class.expects(:destroy).with do |key, args|
- key == 'key' and args['foo'] == 'baz' and args['bar'] == 'xyzzy'
+
+ it "should generate a 200 response when a model find call succeeds" do
+ setup_find_request
+ @mock_response.expects(:start).with(200)
+ @handler.process(@mock_request, @mock_response)
end
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should pass HTTP request parameters to model save" do
- setup_save_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- @mock_model_instance.expects(:save).with do |args|
- args[:data] == 'this is a fake request body' and args['foo'] == 'baz' and args['bar'] == 'xyzzy'
+
+ it "should return a serialized object when a model find call succeeds" do
+ setup_find_request
+ @mock_model_instance = stub('model instance')
+ @mock_model_instance.expects(:to_yaml)
+ @mock_model_class.stubs(:find).returns(@mock_model_instance)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should serialize a controller exception when an exception is thrown by find" do
+ setup_find_request
+ @mock_model_class.expects(:find).raises(ArgumentError)
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
end
- @handler.process(@mock_request, @mock_response)
end
- it "should generate a 200 response when a model find call succeeds" do
- setup_find_request
- @mock_response.expects(:start).with(200)
- @handler.process(@mock_request, @mock_response)
+ describe "when destroying a model instance" do |variable|
+ it "should fail to destroy model if key is not specified" do
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foo'})
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should pass HTTP request parameters to model destroy" do
+ setup_destroy_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
+ @mock_model_class.expects(:destroy).with do |key, args|
+ key == 'key' and args['foo'] == 'baz' and args['bar'] == 'xyzzy'
+ end
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should generate a 200 response when a model destroy call succeeds" do
+ setup_destroy_request
+ @mock_response.expects(:start).with(200)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should return a serialized success result when a model destroy call succeeds" do
+ setup_destroy_request
+ @mock_model_class.stubs(:destroy).returns(true)
+ @mock_body.expects(:write).with("--- true\n")
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should serialize a controller exception when an exception is thrown by destroy" do
+ setup_destroy_request
+ @mock_model_class.expects(:destroy).raises(ArgumentError)
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
end
- it "should generate a 200 response when a model search call succeeds" do
- setup_search_request
- @mock_response.expects(:start).with(200)
- @handler.process(@mock_request, @mock_response)
+ describe "when saving a model instance" do |variable|
+ it "should fail to save model if data is not specified" do
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foo'})
+ @mock_request.stubs(:body).returns('')
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should generate a 200 response when a model save call succeeds" do
+ setup_save_request
+ @mock_response.expects(:start).with(200)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should return a serialized object when a model save call succeeds" do
+ setup_save_request
+ @mock_model_instance.stubs(:save).returns(@mock_model_instance)
+ @mock_model_instance.expects(:to_yaml).returns('foo')
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should serialize a controller exception when an exception is thrown by save" do
+ setup_save_request
+ @mock_model_instance.expects(:save).raises(ArgumentError)
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
end
- it "should generate a 200 response when a model destroy call succeeds" do
- setup_destroy_request
- @mock_response.expects(:start).with(200)
- @handler.process(@mock_request, @mock_response)
- end
+ describe "when searching for model instances" do |variable|
+ it "should pass HTTP request parameters to model search" do
+ setup_search_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
+ @mock_model_class.expects(:search).with do |args|
+ args['foo'] == 'baz' and args['bar'] == 'xyzzy'
+ end.returns([])
+ @handler.process(@mock_request, @mock_response)
+ end
- it "should generate a 200 response when a model save call succeeds" do
- setup_save_request
- @mock_response.expects(:start).with(200)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should return a serialized object when a model find call succeeds" do
- setup_find_request
- @mock_model_instance = stub('model instance')
- @mock_model_instance.expects(:to_yaml)
- @mock_model_class.stubs(:find).returns(@mock_model_instance)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should return a list of serialized objects when a model search call succeeds" do
- setup_search_request
- mock_matches = [1..5].collect {|i| mock("model instance #{i}", :to_yaml => "model instance #{i}") }
- @mock_model_class.stubs(:search).returns(mock_matches)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should return a serialized success result when a model destroy call succeeds" do
- setup_destroy_request
- @mock_model_class.stubs(:destroy).returns(true)
- @mock_body.expects(:write).with("--- true\n")
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should return a serialized object when a model save call succeeds" do
- setup_save_request
- @mock_model_instance.stubs(:save).returns(@mock_model_instance)
- @mock_model_instance.expects(:to_yaml).returns('foo')
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should serialize a controller exception when an exception is thrown by find" do
- setup_find_request
- @mock_model_class.expects(:find).raises(ArgumentError)
- @mock_response.expects(:start).with(404)
- @handler.process(@mock_request, @mock_response)
- end
+ it "should generate a 200 response when a model search call succeeds" do
+ setup_search_request
+ @mock_response.expects(:start).with(200)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should return a list of serialized objects when a model search call succeeds" do
+ setup_search_request
+ mock_matches = [1..5].collect {|i| mock("model instance #{i}", :to_yaml => "model instance #{i}") }
+ @mock_model_class.stubs(:search).returns(mock_matches)
+ @handler.process(@mock_request, @mock_response)
+ end
- it "should serialize a controller exception when an exception is thrown by search" do
- setup_search_request
- @mock_model_class.expects(:search).raises(ArgumentError)
- @mock_response.expects(:start).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should serialize a controller exception when an exception is thrown by destroy" do
- setup_destroy_request
- @mock_model_class.expects(:destroy).raises(ArgumentError)
- @mock_response.expects(:start).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should serialize a controller exception when an exception is thrown by save" do
- setup_save_request
- @mock_model_instance.expects(:save).raises(ArgumentError)
- @mock_response.expects(:start).with(404)
- @handler.process(@mock_request, @mock_response)
- end
+ it "should serialize a controller exception when an exception is thrown by search" do
+ setup_search_request
+ @mock_model_class.expects(:search).raises(ArgumentError)
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+ end
it "should serialize a controller exception if the request fails" do
setup_bad_request
diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb
index 0689b1b6b..05ed2f0e2 100644
--- a/spec/unit/network/http/webrick.rb
+++ b/spec/unit/network/http/webrick.rb
@@ -14,11 +14,11 @@ end
describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do
before do
- @mock_webrick = mock('webrick')
+ @mock_webrick = stub('webrick', :[] => {})
[:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)}
WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick)
@server = Puppet::Network::HTTP::WEBrick.new
- @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest, :xmlrpc ] }
+ @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] }
end
it "should fail if already listening" do
@@ -64,37 +64,48 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do
mock_handler = mock("handler instance for [#{protocol}]")
mock_handler_class = mock("handler class for [#{protocol}]")
@listen_params[:handlers].each do |handler|
- mock_handler_class.expects(:new).with {|args|
- args[:server] == @mock_webrick and args[:handler] == handler
- }.returns(mock_handler)
+ @mock_webrick.expects(:mount)
end
- @server.expects(:class_for_protocol).with(protocol).at_least_once.returns(mock_handler_class)
end
@server.listen(@listen_params)
end
it "should use a WEBrick + REST class to configure WEBrick when REST services are requested" do
- Puppet::Network::HTTP::WEBrickREST.expects(:new).at_least_once
+ Puppet::Network::HTTP::WEBrick.expects(:class_for_protocol).with(:rest).at_least_once
@server.listen(@listen_params.merge(:protocols => [:rest]))
end
- it "should use a WEBrick + XMLRPC class to configure WEBrick when XMLRPC services are requested" do
- Puppet::Network::HTTP::WEBrickXMLRPC.expects(:new).at_least_once
- @server.listen(@listen_params.merge(:protocols => [:xmlrpc]))
- end
-
it "should fail if services from an unknown protocol are requested" do
- Proc.new { @server.listen(@listen_params.merge(:protocols => [ :foo ]))}.should raise_error(ArgumentError)
+ Proc.new { @server.listen(@listen_params.merge(:protocols => [ :foo ]))}.should raise_error
end
end
+
+describe Puppet::Network::HTTP::WEBrick, "when looking up the class to handle a protocol" do
+ it "should require a protocol" do
+ lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol }.should raise_error(ArgumentError)
+ end
+
+ it "should accept a protocol" do
+ lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol("bob") }.should_not raise_error(ArgumentError)
+ end
+
+ it "should use a WEBrick + REST class when a REST protocol is specified" do
+ Puppet::Network::HTTP::WEBrick.class_for_protocol("rest").should == Puppet::Network::HTTP::WEBrickREST
+ end
+
+ it "should fail when an unknown protocol is specified" do
+ lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol("abcdefg") }.should raise_error
+ end
+end
+
describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do
before do
- @mock_webrick = mock('webrick')
+ @mock_webrick = stub('webrick', :[] => {})
[:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)}
WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick)
@server = Puppet::Network::HTTP::WEBrick.new
- @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest, :xmlrpc ] }
+ @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] }
end
it "should fail unless listening" do
diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb
index aa7a3d53a..b7bd33880 100644
--- a/spec/unit/network/http/webrick/rest.rb
+++ b/spec/unit/network/http/webrick/rest.rb
@@ -1,49 +1,33 @@
-#!/usr/bin/env ruby
-#
-# Created by Rick Bradley on 2007-10-16.
-# Copyright (c) 2007. All rights reserved.
-
require File.dirname(__FILE__) + '/../../../../spec_helper'
require 'puppet/network/http'
describe Puppet::Network::HTTP::WEBrickREST, "when initializing" do
before do
- @mock_webrick = stub('WEBrick server', :mount => true)
+ @mock_webrick = stub('WEBrick server', :mount => true, :[] => {})
@mock_model = mock('indirected model')
Puppet::Indirector::Indirection.stubs(:model).returns(@mock_model)
- @params = { :server => @mock_webrick, :handler => :foo }
+ @params = [ @mock_webrick, :foo ]
end
it "should require access to a WEBrick server" do
- Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params.delete_if {|k,v| :server == k })}.should raise_error(ArgumentError)
+ Proc.new {
+ @params[0] = nil
+ Puppet::Network::HTTP::WEBrickREST.new(*@params)
+ }.should raise_error(ArgumentError)
end
it "should require an indirection name" do
- Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params.delete_if {|k,v| :handler == k })}.should raise_error(ArgumentError)
+ Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params.first) }.should raise_error(ArgumentError)
end
it "should look up the indirection model from the indirection name" do
Puppet::Indirector::Indirection.expects(:model).returns(@mock_model)
- Puppet::Network::HTTP::WEBrickREST.new(@params)
+ Puppet::Network::HTTP::WEBrickREST.new(*@params)
end
it "should fail if the indirection is not known" do
Puppet::Indirector::Indirection.expects(:model).returns(nil)
- Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params) }.should raise_error(ArgumentError)
- end
-
- it "should register itself with the WEBrick server for the singular HTTP methods" do
- @mock_webrick.expects(:mount).with do |*args|
- args.first == '/foo' and args.last.is_a?(Puppet::Network::HTTP::WEBrickREST)
- end
- Puppet::Network::HTTP::WEBrickREST.new(@params)
- end
-
- it "should register itself with the WEBrick server for the plural GET method" do
- @mock_webrick.expects(:mount).with do |*args|
- args.first == '/foos' and args.last.is_a?(Puppet::Network::HTTP::WEBrickREST)
- end
- Puppet::Network::HTTP::WEBrickREST.new(@params)
+ Proc.new { Puppet::Network::HTTP::WEBrickREST.new(*@params) }.should raise_error(ArgumentError)
end
end
@@ -52,9 +36,9 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do
@mock_request = stub('webrick http request', :query => {})
@mock_response = stub('webrick http response', :status= => true, :body= => true)
@mock_model_class = stub('indirected model class')
- @mock_webrick = stub('mongrel http server', :mount => true)
+ @mock_webrick = stub('webrick http server', :mount => true, :[] => {})
Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class)
- @handler = Puppet::Network::HTTP::WEBrickREST.new(:server => @mock_webrick, :handler => :foo)
+ @handler = Puppet::Network::HTTP::WEBrickREST.new(@mock_webrick, :foo)
end
def setup_find_request
@@ -80,7 +64,7 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do
@mock_request.stubs(:path).returns('/foo')
@mock_request.stubs(:body).returns('This is a fake request body')
@mock_model_instance = stub('indirected model instance', :save => true)
- @mock_model_class.stubs(:new).returns(@mock_model_instance)
+ @mock_model_class.stubs(:from_yaml).returns(@mock_model_instance)
end
def setup_bad_request
@@ -88,6 +72,7 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do
@mock_request.stubs(:path).returns('/foos')
end
+
it "should call the model find method if the request represents a singular HTTP GET" do
setup_find_request
@mock_model_class.expects(:find).with('key', {})
@@ -108,8 +93,7 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do
it "should call the model save method if the request represents an HTTP PUT" do
setup_save_request
- @mock_model_instance.expects(:save).with(:data => 'This is a fake request body')
- @mock_model_class.expects(:new).returns(@mock_model_instance)
+ @mock_model_instance.expects(:save)
@handler.service(@mock_request, @mock_response)
end
@@ -120,12 +104,14 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do
@handler.process(@mock_request, @mock_response)
end
- it "should fail if the request's pluralization is wrong" do
+ it "should fail if delete request's pluralization is wrong" do
@mock_request.stubs(:request_method).returns('DELETE')
@mock_request.stubs(:path).returns('/foos/key')
@mock_response.expects(:status=).with(404)
@handler.process(@mock_request, @mock_response)
-
+ end
+
+ it "should fail if put request's pluralization is wrong" do
@mock_request.stubs(:request_method).returns('PUT')
@mock_request.stubs(:path).returns('/foos/key')
@mock_response.expects(:status=).with(404)
@@ -139,145 +125,144 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do
@handler.process(@mock_request, @mock_response)
end
- it "should fail to find model if key is not specified" do
- @mock_request.stubs(:request_method).returns('GET')
- @mock_request.stubs(:path).returns('/foo')
- @mock_response.expects(:status=).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should fail to destroy model if key is not specified" do
- @mock_request.stubs(:request_method).returns('DELETE')
- @mock_request.stubs(:path).returns('/foo')
- @mock_response.expects(:status=).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should fail to save model if data is not specified" do
- @mock_request.stubs(:request_method).returns('PUT')
- @mock_request.stubs(:path).returns('/foo')
- @mock_request.stubs(:body).returns('')
- @mock_response.expects(:status=).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should pass HTTP request parameters to model find" do
- setup_find_request
- @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:find).with do |key, args|
- key == 'key' and args[:foo] == :baz and args[:bar] == :xyzzy
+ describe "when finding a model instance" do |variable|
+ it "should fail to find model if key is not specified" do
+ @mock_request.stubs(:request_method).returns('GET')
+ @mock_request.stubs(:path).returns('/foo')
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should pass HTTP request parameters to model find" do
+ setup_find_request
+ @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
+ @mock_model_class.expects(:find).with do |key, args|
+ key == 'key' and args[:foo] == :baz and args[:bar] == :xyzzy
+ end
+ @handler.service(@mock_request, @mock_response)
+ end
+
+ it "should generate a 200 response when a model find call succeeds" do
+ setup_find_request
+ @mock_response.expects(:status=).with(200)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should return a serialized object when a model find call succeeds" do
+ setup_find_request
+ @mock_model_instance = stub('model instance')
+ @mock_model_instance.expects(:to_yaml)
+ @mock_model_class.stubs(:find).returns(@mock_model_instance)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should serialize a controller exception when an exception is thrown by find" do
+ setup_find_request
+ @mock_model_class.expects(:find).raises(ArgumentError)
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
end
- @handler.service(@mock_request, @mock_response)
- end
-
- it "should pass HTTP request parameters to model search" do
- setup_search_request
- @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:search).with do |args|
- args[:foo] == :baz and args[:bar] == :xyzzy
- end.returns([])
- @handler.service(@mock_request, @mock_response)
end
- it "should pass HTTP request parameters to model destroy" do
- setup_destroy_request
- @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:destroy).with do |key, args|
- key == 'key' and args[:foo] == :baz and args[:bar] == :xyzzy
+ describe "when destroying a model instance" do |variable|
+ it "should fail to destroy model if key is not specified" do
+ @mock_request.stubs(:request_method).returns('DELETE')
+ @mock_request.stubs(:path).returns('/foo')
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should pass HTTP request parameters to model destroy" do
+ setup_destroy_request
+ @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
+ @mock_model_class.expects(:destroy).with do |key, args|
+ key == 'key' and args[:foo] == :baz and args[:bar] == :xyzzy
+ end
+ @handler.service(@mock_request, @mock_response)
+ end
+
+ it "should generate a 200 response when a model destroy call succeeds" do
+ setup_destroy_request
+ @mock_response.expects(:status=).with(200)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should return a serialized success result when a model destroy call succeeds" do
+ setup_destroy_request
+ @mock_model_class.stubs(:destroy).returns(true)
+ @mock_response.expects(:body=).with("--- true\n")
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should serialize a controller exception when an exception is thrown by search" do
+ setup_search_request
+ @mock_model_class.expects(:search).raises(ArgumentError)
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
end
- @handler.service(@mock_request, @mock_response)
end
- it "should pass HTTP request parameters to model save" do
- setup_save_request
- @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_instance.expects(:save).with do |args|
- args[:data] == 'This is a fake request body' and args[:foo] == :baz and args[:bar] == :xyzzy
+ describe "when saving a model instance" do
+ it "should fail to save model if data is not specified" do
+ @mock_request.stubs(:request_method).returns('PUT')
+ @mock_request.stubs(:path).returns('/foo')
+ @mock_request.stubs(:body).returns('')
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should generate a 200 response when a model save call succeeds" do
+ setup_save_request
+ @mock_response.expects(:status=).with(200)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should return a serialized object when a model save call succeeds" do
+ setup_save_request
+ @mock_model_instance.stubs(:save).returns(@mock_model_instance)
+ @mock_model_instance.expects(:to_yaml).returns('foo')
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should serialize a controller exception when an exception is thrown by save" do
+ setup_save_request
+ @mock_model_instance.expects(:save).raises(ArgumentError)
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
end
- @handler.service(@mock_request, @mock_response)
- end
-
- it "should generate a 200 response when a model find call succeeds" do
- setup_find_request
- @mock_response.expects(:status=).with(200)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should generate a 200 response when a model search call succeeds" do
- setup_search_request
- @mock_response.expects(:status=).with(200)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should generate a 200 response when a model destroy call succeeds" do
- setup_destroy_request
- @mock_response.expects(:status=).with(200)
- @handler.process(@mock_request, @mock_response)
end
- it "should generate a 200 response when a model save call succeeds" do
- setup_save_request
- @mock_response.expects(:status=).with(200)
- @handler.process(@mock_request, @mock_response)
- end
+ describe "when searching for model instances" do
+ it "should pass HTTP request parameters to model search" do
+ setup_search_request
+ @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
+ @mock_model_class.expects(:search).with do |args|
+ args[:foo] == :baz and args[:bar] == :xyzzy
+ end.returns([])
+ @handler.service(@mock_request, @mock_response)
+ end
- it "should return a serialized object when a model find call succeeds" do
- setup_find_request
- @mock_model_instance = stub('model instance')
- @mock_model_instance.expects(:to_yaml)
- @mock_model_class.stubs(:find).returns(@mock_model_instance)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should return a list of serialized objects when a model search call succeeds" do
- setup_search_request
- mock_matches = [1..5].collect {|i| mock("model instance #{i}", :to_yaml => "model instance #{i}") }
- @mock_model_class.stubs(:search).returns(mock_matches)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should return a serialized success result when a model destroy call succeeds" do
- setup_destroy_request
- @mock_model_class.stubs(:destroy).returns(true)
- @mock_response.expects(:body=).with("--- true\n")
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should return a serialized object when a model save call succeeds" do
- setup_save_request
- @mock_model_instance.stubs(:save).returns(@mock_model_instance)
- @mock_model_instance.expects(:to_yaml).returns('foo')
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should serialize a controller exception when an exception is thrown by find" do
- setup_find_request
- @mock_model_class.expects(:find).raises(ArgumentError)
- @mock_response.expects(:status=).with(404)
- @handler.process(@mock_request, @mock_response)
+ it "should generate a 200 response when a model search call succeeds" do
+ setup_search_request
+ @mock_response.expects(:status=).with(200)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should return a list of serialized objects when a model search call succeeds" do
+ setup_search_request
+ mock_matches = [1..5].collect {|i| mock("model instance #{i}", :to_yaml => "model instance #{i}") }
+ @mock_model_class.stubs(:search).returns(mock_matches)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should serialize a controller exception when an exception is thrown by destroy" do
+ setup_destroy_request
+ @mock_model_class.expects(:destroy).raises(ArgumentError)
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
end
- it "should serialize a controller exception when an exception is thrown by search" do
- setup_search_request
- @mock_model_class.expects(:search).raises(ArgumentError)
- @mock_response.expects(:status=).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should serialize a controller exception when an exception is thrown by destroy" do
- setup_destroy_request
- @mock_model_class.expects(:destroy).raises(ArgumentError)
- @mock_response.expects(:status=).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should serialize a controller exception when an exception is thrown by save" do
- setup_save_request
- @mock_model_instance.expects(:save).raises(ArgumentError)
- @mock_response.expects(:status=).with(404)
- @handler.process(@mock_request, @mock_response)
- end
-
it "should serialize a controller exception if the request fails" do
setup_bad_request
@mock_response.expects(:status=).with(404)
diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb
index 846b5471d..ef9e85a73 100644
--- a/spec/unit/network/server.rb
+++ b/spec/unit/network/server.rb
@@ -161,8 +161,12 @@ describe Puppet::Network::Server, "in general" do
Proc.new { @server2.unregister(:bar) }.should raise_error(ArgumentError)
end
- it "should provide a means of determining which style of service is being offered to clients" do
- @server.protocols.should == []
+ it "should provide a means of determining which protocols are in use" do
+ @server.should respond_to(:protocols)
+ end
+
+ it "should only support the REST protocol at this time" do
+ @server.protocols.should == [ :rest ]
end
it "should provide a means of determining the listening address" do
@@ -230,23 +234,54 @@ describe Puppet::Network::Server, "when listening is being turned on" do
@mock_http_server_class = mock('http server class')
Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class)
Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337)
+ @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337, :handlers => [:node])
@mock_http_server = mock('http server')
@mock_http_server.stubs(:listen)
end
it "should fetch an instance of an HTTP server" do
- mock_http_server_class = mock('http server class')
- mock_http_server_class.expects(:new).returns(@mock_http_server)
- @server.expects(:http_server_class).returns(mock_http_server_class)
+ @server.stubs(:http_server_class).returns(@mock_http_server_class)
+ @mock_http_server_class.expects(:new).returns(@mock_http_server)
@server.listen
end
it "should cause the HTTP server to listen" do
+ @server.stubs(:http_server).returns(@mock_http_server)
@mock_http_server.expects(:listen)
- @server.expects(:http_server).returns(@mock_http_server)
@server.listen
end
+
+ it "should pass the listening address to the HTTP server" do
+ @server.stubs(:http_server).returns(@mock_http_server)
+ @mock_http_server.expects(:listen).with do |args|
+ args[:address] == '127.0.0.1'
+ end
+ @server.listen
+ end
+
+ it "should pass the listening port to the HTTP server" do
+ @server.stubs(:http_server).returns(@mock_http_server)
+ @mock_http_server.expects(:listen).with do |args|
+ args[:port] == 31337
+ end
+ @server.listen
+ end
+
+ it "should pass a list of handlers to the HTTP server" do
+ @server.stubs(:http_server).returns(@mock_http_server)
+ @mock_http_server.expects(:listen).with do |args|
+ args[:handlers] == [ :node ]
+ end
+ @server.listen
+ end
+
+ it "should pass a list of protocols to the HTTP server" do
+ @server.stubs(:http_server).returns(@mock_http_server)
+ @mock_http_server.expects(:listen).with do |args|
+ args[:protocols] == [ :rest ]
+ end
+ @server.listen
+ end
end
describe Puppet::Network::Server, "when listening is being turned off" do
@@ -274,16 +309,8 @@ end
describe Class.new, "put these somewhere" do
- it "should allow indirections to deny access to services based upon which client is connecting, or whether the client is authorized"
- it "should deny access to clients based upon rules"
it "should have the ability to use a class-level from_ hook (from_yaml, from_text, etc.) that can be called, based on content-type header, to allow for different deserializations of an object"
it "should allow from_* on the inbound :data packet (look at its content_type) when doing a PUT/.new.save"
it "should prepend a rest version number on the path (w00t)"
it "should ... on server side, .save should from_yaml, then foo.save(args) instead of just Foo.new.save(args)"
- it "should have a from_yaml class_method in the indirector (... default: yaml.load(data) => instance, but can be overridden)"
-end
-
-describe Puppet::Indirector, "stuff required by HTTP servers" do
- it "should provide the model with the ability to serialize to XML"
- it "should provide the model with the ability to deserialize from XML"
end