summaryrefslogtreecommitdiffstats
path: root/spec
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2007-11-08 19:48:21 -0600
committerLuke Kanies <luke@madstop.com>2007-11-08 19:48:21 -0600
commit59cc25d798f20bc01943ca341c04716cb037064a (patch)
tree8697db62d970cdff7736a6acb17e97872d42d536 /spec
parent3f583dc133ce50ae34bfc151474c6d4196f803ca (diff)
parent956daa5b4b1c61db9a5e1d7638ca819005fd7ef0 (diff)
downloadpuppet-59cc25d798f20bc01943ca341c04716cb037064a.tar.gz
puppet-59cc25d798f20bc01943ca341c04716cb037064a.tar.xz
puppet-59cc25d798f20bc01943ca341c04716cb037064a.zip
Merge branch 'routing' into test_routing
Diffstat (limited to 'spec')
-rw-r--r--spec/lib/autotest/discover.rb9
-rw-r--r--spec/lib/autotest/puppet_rspec.rb46
-rw-r--r--spec/lib/autotest/rspec.rb95
-rw-r--r--spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb30
-rw-r--r--spec/lib/spec/dsl/behaviour.rb3
-rw-r--r--spec/lib/spec/runner/behaviour_runner.rb2
-rw-r--r--spec/spec_helper.rb13
-rwxr-xr-xspec/unit/indirector/indirection.rb12
-rwxr-xr-xspec/unit/indirector/node/memory.rb5
-rw-r--r--spec/unit/network/http.rb29
-rw-r--r--spec/unit/network/http/mongrel.rb124
-rw-r--r--spec/unit/network/http/mongrel/rest.rb287
-rw-r--r--spec/unit/network/http/mongrel/xmlrpc.rb0
-rw-r--r--spec/unit/network/http/webrick.rb115
-rw-r--r--spec/unit/network/http/webrick/rest.rb286
-rw-r--r--spec/unit/network/http/webrick/xmlrpc.rb0
-rw-r--r--spec/unit/network/rest_controller.rb65
-rw-r--r--spec/unit/network/server.rb419
18 files changed, 1303 insertions, 237 deletions
diff --git a/spec/lib/autotest/discover.rb b/spec/lib/autotest/discover.rb
new file mode 100644
index 000000000..0ac563724
--- /dev/null
+++ b/spec/lib/autotest/discover.rb
@@ -0,0 +1,9 @@
+require 'autotest'
+
+Autotest.add_discovery do
+ "rspec"
+end
+
+Autotest.add_discovery do
+ "puppet"
+end
diff --git a/spec/lib/autotest/puppet_rspec.rb b/spec/lib/autotest/puppet_rspec.rb
new file mode 100644
index 000000000..8536f3912
--- /dev/null
+++ b/spec/lib/autotest/puppet_rspec.rb
@@ -0,0 +1,46 @@
+require 'autotest'
+require 'autotest/rspec'
+
+class Autotest::PuppetRspec < Autotest::Rspec
+ def initialize # :nodoc:
+ super
+ @test_mappings = {
+ # the libraries under lib/puppet
+ %r%^lib/puppet/(.*)\.rb$% => proc { |filename, m|
+ files_matching %r!spec/(unit|integration)/#{m[1]}.rb!
+ },
+
+ # the actual spec files themselves
+ %r%^spec/(unit|integration)/.*\.rb$% => proc { |filename, _|
+ filename
+ },
+
+ # force a complete re-run for all of these:
+
+ # main puppet lib
+ %r!^lib/puppet\.rb$! => proc { |filename, _|
+ files_matching %r!spec/(unit|integration)/.*\.rb!
+ },
+
+ # the spec_helper
+ %r!^spec/spec_helper\.rb$! => proc { |filename, _|
+ files_matching %r!spec/(unit|integration)/.*\.rb!
+ },
+
+ # the puppet test libraries
+ %r!^test/lib/puppettest/.*! => proc { |filename, _|
+ files_matching %r!spec/(unit|integration)/.*\.rb!
+ },
+
+ # the puppet spec libraries
+ %r!^spec/lib/spec.*! => proc { |filename, _|
+ files_matching %r!spec/(unit|integration)/.*\.rb!
+ },
+
+ # the monkey patches for rspec
+ %r!^spec/lib/monkey_patches/.*! => proc { |filename, _|
+ files_matching %r!spec/(unit|integration)/.*\.rb!
+ },
+ }
+ end
+end
diff --git a/spec/lib/autotest/rspec.rb b/spec/lib/autotest/rspec.rb
new file mode 100644
index 000000000..d4b77ea6b
--- /dev/null
+++ b/spec/lib/autotest/rspec.rb
@@ -0,0 +1,95 @@
+require 'autotest'
+
+class RspecCommandError < StandardError; end
+
+class Autotest::Rspec < Autotest
+
+ def initialize(kernel=Kernel, separator=File::SEPARATOR, alt_separator=File::ALT_SEPARATOR) # :nodoc:
+ super()
+ @kernel, @separator, @alt_separator = kernel, separator, alt_separator
+ @spec_command = spec_command
+
+ # watch out: Ruby bug (1.8.6):
+ # %r(/) != /\//
+ # since Ruby compares the REGEXP source, not the resulting pattern
+ @test_mappings = {
+ %r%^spec/.*\.rb$% => kernel.proc { |filename, _|
+ filename
+ },
+ %r%^lib/(.*)\.rb$% => kernel.proc { |_, m|
+ ["spec/#{m[1]}_spec.rb"]
+ },
+ %r%^spec/(spec_helper|shared/.*)\.rb$% => kernel.proc {
+ files_matching %r%^spec/.*_spec\.rb$%
+ }
+ }
+ end
+
+ def tests_for_file(filename)
+ super.select { |f| @files.has_key? f }
+ end
+
+ alias :specs_for_file :tests_for_file
+
+ def failed_results(results)
+ results.scan(/^\d+\)\n(?:\e\[\d*m)?(?:.*?Error in )?'([^\n]*)'(?: FAILED)?(?:\e\[\d*m)?\n(.*?)\n\n/m)
+ end
+
+ def handle_results(results)
+ @files_to_test = consolidate_failures failed_results(results)
+ unless @files_to_test.empty? then
+ hook :red
+ else
+ hook :green
+ end unless $TESTING
+ @tainted = true unless @files_to_test.empty?
+ end
+
+ def consolidate_failures(failed)
+ filters = Hash.new { |h,k| h[k] = [] }
+ failed.each do |spec, failed_trace|
+ @files.keys.select{|f| f =~ /spec\//}.each do |f|
+ if failed_trace =~ Regexp.new(f)
+ filters[f] << spec
+ break
+ end
+ end
+ end
+ return filters
+ end
+
+ def make_test_cmd(files_to_test)
+ return "#{ruby} -S #{@spec_command} #{add_options_if_present} #{files_to_test.keys.flatten.join(' ')}"
+ end
+
+ def add_options_if_present
+ File.exist?("spec/spec.opts") ? "-O spec/spec.opts " : ""
+ end
+
+ # Finds the proper spec command to use. Precendence
+ # is set in the lazily-evaluated method spec_commands. Alias + Override
+ # that in ~/.autotest to provide a different spec command
+ # then the default paths provided.
+ def spec_command
+ spec_commands.each do |command|
+ if File.exists?(command)
+ return @alt_separator ? (command.gsub @separator, @alt_separator) : command
+ end
+ end
+
+ raise RspecCommandError, "No spec command could be found!"
+ end
+
+ # Autotest will look for spec commands in the following
+ # locations, in this order:
+ #
+ # * bin/spec
+ # * default spec bin/loader installed in Rubygems
+ def spec_commands
+ [
+ File.join('bin', 'spec'),
+ File.join(Config::CONFIG['bindir'], 'spec')
+ ]
+ end
+
+end
diff --git a/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb b/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb
new file mode 100644
index 000000000..a2467eac8
--- /dev/null
+++ b/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb
@@ -0,0 +1,30 @@
+dir = File.expand_path(File.dirname(__FILE__))
+$LOAD_PATH.unshift("#{dir}/../../lib")
+$LOAD_PATH.unshift("#{dir}/../../../lib")
+$LOAD_PATH.unshift("#{dir}/../../../test/lib") # Add the old test dir, so that we can still find our local mocha and spec
+
+require 'spec'
+require 'puppettest'
+require 'puppettest/runnable_test'
+
+module Spec
+ module Runner
+ class BehaviourRunner
+ def run_behaviours
+ @behaviours.each do |behaviour|
+ # LAK:NOTE: this 'runnable' test is Puppet-specific.
+ next unless behaviour.runnable?
+ behaviour.run(@options.reporter, @options.dry_run, @options.reverse, @options.timeout)
+ end
+ end
+ end
+ end
+end
+
+module Spec
+ module DSL
+ class EvalModule < Module
+ include PuppetTest::RunnableTest
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/behaviour.rb b/spec/lib/spec/dsl/behaviour.rb
index 159a0ba7e..cc71ccffe 100644
--- a/spec/lib/spec/dsl/behaviour.rb
+++ b/spec/lib/spec/dsl/behaviour.rb
@@ -2,9 +2,6 @@ require(File.expand_path(File.dirname(__FILE__) + '../../../../../test/lib/puppe
module Spec
module DSL
- class EvalModule < Module;
- include PuppetTest::RunnableTest
- end
class Behaviour
extend BehaviourCallbacks
diff --git a/spec/lib/spec/runner/behaviour_runner.rb b/spec/lib/spec/runner/behaviour_runner.rb
index 078490e92..1ac891f3c 100644
--- a/spec/lib/spec/runner/behaviour_runner.rb
+++ b/spec/lib/spec/runner/behaviour_runner.rb
@@ -55,8 +55,6 @@ module Spec
def run_behaviours
@behaviours.each do |behaviour|
- # LAK:NOTE: this 'runnable' test is Puppet-specific.
- next unless behaviour.runnable?
behaviour.run(@options.reporter, @options.dry_run, @options.reverse, @options.timeout)
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 3017f272a..bfac9095f 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,12 +1,11 @@
dir = File.expand_path(File.dirname(__FILE__))
-$:.unshift("#{dir}/lib")
-$:.unshift("#{dir}/../lib")
+$LOAD_PATH.unshift("#{dir}/lib")
+$LOAD_PATH.unshift("#{dir}/../lib")
+$LOAD_PATH.unshift("#{dir}/../test/lib") # Add the old test dir, so that we can still find our local mocha and spec
-# Add the old test dir, so that we can still find mocha and spec
-$:.unshift("#{dir}/../test/lib")
-
-require 'mocha'
require 'puppettest'
+require 'puppettest/runnable_test'
+require 'mocha'
require 'spec'
Spec::Runner.configure do |config|
@@ -19,3 +18,5 @@ Spec::Runner.configure do |config|
teardown() if respond_to? :teardown
end
end
+
+require "#{dir}/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl"
diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb
index 0ac2356d6..5d8453905 100755
--- a/spec/unit/indirector/indirection.rb
+++ b/spec/unit/indirector/indirection.rb
@@ -156,11 +156,21 @@ describe Puppet::Indirector::Indirection, " when managing indirection instances"
@indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
Puppet::Indirector::Indirection.instance(:test).should equal(@indirection)
end
-
+
it "should return nil when the named indirection has not been created" do
Puppet::Indirector::Indirection.instance(:test).should be_nil
end
+ it "should allow an indirection's model to be retrieved by name" do
+ mock_model = mock('model')
+ @indirection = Puppet::Indirector::Indirection.new(mock_model, :test)
+ Puppet::Indirector::Indirection.model(:test).should equal(mock_model)
+ end
+
+ it "should return nil when no model matches the requested name" do
+ Puppet::Indirector::Indirection.model(:test).should be_nil
+ end
+
after do
@indirection.delete if defined? @indirection
end
diff --git a/spec/unit/indirector/node/memory.rb b/spec/unit/indirector/node/memory.rb
index f57cae818..a924c6209 100755
--- a/spec/unit/indirector/node/memory.rb
+++ b/spec/unit/indirector/node/memory.rb
@@ -4,9 +4,8 @@ require File.dirname(__FILE__) + '/../../../spec_helper'
require 'puppet/indirector/node/memory'
-# All of our behaviour is described here, so we always have to
-# include it.
-require 'unit/indirector/memory'
+# All of our behaviour is described here, so we always have to include it.
+require File.dirname(__FILE__) + '/../memory'
describe Puppet::Node::Memory do
before do
diff --git a/spec/unit/network/http.rb b/spec/unit/network/http.rb
new file mode 100644
index 000000000..79a0a88d4
--- /dev/null
+++ b/spec/unit/network/http.rb
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+#
+# Created by Rick Bradley on 2007-10-03.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/network/http'
+
+describe Puppet::Network::HTTP do
+ it "should return the webrick HTTP server class when asked for a webrick server" do
+ Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBrick)
+ end
+
+ if Puppet.features.mongrel?
+ it "should return the mongrel HTTP server class when asked for a mongrel server" do
+ Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel)
+ end
+ end
+
+ it "should fail to return the mongrel HTTP server class if mongrel is not available " do
+ Puppet.features.expects(:mongrel?).returns(false)
+ Proc.new { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError)
+ end
+
+ it "should return an error when asked for an unknown server" do
+ Proc.new { Puppet::Network::HTTP.server_class_by_type :foo }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb
new file mode 100644
index 000000000..b6ad07567
--- /dev/null
+++ b/spec/unit/network/http/mongrel.rb
@@ -0,0 +1,124 @@
+#!/usr/bin/env ruby
+#
+# Created by Rick Bradley on 2007-10-15.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+require 'puppet/network/http'
+
+describe Puppet::Network::HTTP::Mongrel, "after initializing" do
+ confine "Mongrel is not available" => Puppet.features.mongrel?
+
+ it "should not be listening" do
+ Puppet::Network::HTTP::Mongrel.new.should_not be_listening
+ end
+end
+
+describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do
+ confine "Mongrel is not available" => Puppet.features.mongrel?
+
+ before do
+ @server = Puppet::Network::HTTP::Mongrel.new
+ @mock_mongrel = mock('mongrel')
+ @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, :configuration ], :protocols => [ :rest, :xmlrpc ] }
+ end
+
+ it "should fail if already listening" do
+ @server.listen(@listen_params)
+ Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError)
+ end
+
+ it "should require at least one handler" do
+ Proc.new { @server.listen(@listen_params.delete_if {|k,v| :handlers == k}) }.should raise_error(ArgumentError)
+ end
+
+ it "should require at least one protocol" do
+ Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError)
+ end
+
+ it "should require a listening address to be specified" do
+ Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError)
+ end
+
+ it "should require a listening port to be specified" do
+ Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError)
+ end
+
+ it "should order a mongrel server to start" do
+ @mock_mongrel.expects(:run)
+ @server.listen(@listen_params)
+ end
+
+ it "should tell mongrel to listen on the specified address and port" do
+ Mongrel::HttpServer.expects(:new).with("127.0.0.1", 31337).returns(@mock_mongrel)
+ @server.listen(@listen_params)
+ end
+
+ it "should be listening" do
+ Mongrel::HttpServer.expects(:new).returns(@mock_mongrel)
+ @server.listen(@listen_params)
+ @server.should be_listening
+ end
+
+ 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)
+ 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]))
+ 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
+ confine "Mongrel is not available" => Puppet.features.mongrel?
+
+ before do
+ @mock_mongrel = mock('mongrel httpserver')
+ @mock_mongrel.stubs(:run)
+ @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, :configuration ], :protocols => [ :rest, :xmlrpc ] }
+ end
+
+ it "should fail unless listening" do
+ Proc.new { @server.unlisten }.should raise_error(RuntimeError)
+ end
+
+ it "should order mongrel server to stop" do
+ @server.listen(@listen_params)
+ @mock_mongrel.expects(:graceful_shutdown)
+ @server.unlisten
+ end
+
+ it "should not be listening" do
+ @server.listen(@listen_params)
+ @mock_mongrel.stubs(:graceful_shutdown)
+ @server.unlisten
+ @server.should_not be_listening
+ end
+end
diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb
new file mode 100644
index 000000000..9b2feb6ee
--- /dev/null
+++ b/spec/unit/network/http/mongrel/rest.rb
@@ -0,0 +1,287 @@
+#!/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::MongrelREST, "when initializing" do
+ confine "Mongrel is not available" => Puppet.features.mongrel?
+
+ before do
+ @mock_mongrel = mock('Mongrel server')
+ @mock_mongrel.stubs(:register)
+ @mock_model = mock('indirected model')
+ Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model)
+ @params = { :server => @mock_mongrel, :handler => :foo }
+ end
+
+ it "should require access to a Mongrel server" do
+ Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params.delete_if {|k,v| :server == k })}.should raise_error(ArgumentError)
+ end
+
+ it "should require an indirection name" do
+ Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params.delete_if {|k,v| :handler == k })}.should raise_error(ArgumentError)
+ end
+
+ it "should look up the indirection model from the indirection name" do
+ Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(@mock_model)
+ Puppet::Network::HTTP::MongrelREST.new(@params)
+ end
+
+ it "should fail if the indirection is not known" 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
+ confine "Mongrel is not available" => Puppet.features.mongrel?
+
+ before do
+ @mock_request = stub('mongrel http request')
+ @mock_head = stub('response head')
+ @mock_body = stub('response body', :write => true)
+ @mock_response = stub('mongrel http response')
+ @mock_response.stubs(:start).yields(@mock_head, @mock_body)
+ @mock_model_class = stub('indirected model class')
+ @mock_mongrel = stub('mongrel http server', :register => true)
+ Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class)
+ @handler = Puppet::Network::HTTP::MongrelREST.new(:server => @mock_mongrel, :handler => :foo)
+ end
+
+ def setup_find_request(params = {})
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET',
+ Mongrel::Const::REQUEST_PATH => '/foo/key',
+ 'QUERY_STRING' => ''}.merge(params))
+ @mock_model_class.stubs(:find)
+ end
+
+ def setup_search_request(params = {})
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET',
+ Mongrel::Const::REQUEST_PATH => '/foos',
+ 'QUERY_STRING' => '' }.merge(params))
+ @mock_model_class.stubs(:search).returns([])
+ end
+
+ def setup_destroy_request(params = {})
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE',
+ Mongrel::Const::REQUEST_PATH => '/foo/key',
+ 'QUERY_STRING' => '' }.merge(params))
+ @mock_model_class.stubs(:destroy)
+ end
+
+ def setup_save_request(params = {})
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT',
+ Mongrel::Const::REQUEST_PATH => '/foo',
+ '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)
+ end
+
+ def setup_bad_request
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'POST', Mongrel::Const::REQUEST_PATH => '/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', {})
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should call the model search method if the request represents a plural HTTP GET" do
+ setup_search_request
+ @mock_model_class.expects(:search).with({}).returns([])
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should call the model destroy method if the request represents an HTTP DELETE" do
+ setup_destroy_request
+ @mock_model_class.expects(:destroy).with('key', {})
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ 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')
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should fail if the HTTP method isn't supported" do
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'POST', Mongrel::Const::REQUEST_PATH => '/foo'})
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should fail if the request's pluralization is wrong" do
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foos/key'})
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foos/key'})
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should fail if the request is for an unknown path" do
+ @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET',
+ Mongrel::Const::REQUEST_PATH => '/bar/key',
+ 'QUERY_STRING' => '' })
+ @mock_response.expects(:start).with(404)
+ @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
+
+ 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
+
+ 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'
+ 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'
+ 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)
+ 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 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 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 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 if the request fails" do
+ setup_bad_request
+ @mock_response.expects(:start).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+end
diff --git a/spec/unit/network/http/mongrel/xmlrpc.rb b/spec/unit/network/http/mongrel/xmlrpc.rb
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/spec/unit/network/http/mongrel/xmlrpc.rb
diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb
new file mode 100644
index 000000000..3ed223e7e
--- /dev/null
+++ b/spec/unit/network/http/webrick.rb
@@ -0,0 +1,115 @@
+#!/usr/bin/env ruby
+#
+# Created by Rick Bradley on 2007-10-15.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+require 'puppet/network/http'
+
+describe Puppet::Network::HTTP::WEBrick, "after initializing" do
+ it "should not be listening" do
+ Puppet::Network::HTTP::WEBrick.new.should_not be_listening
+ end
+end
+
+describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do
+ before do
+ @mock_webrick = mock('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, :configuration ], :protocols => [ :rest, :xmlrpc ] }
+ end
+
+ it "should fail if already listening" do
+ @server.listen(@listen_params)
+ Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError)
+ end
+
+ it "should require at least one handler" do
+ Proc.new { @server.listen(@listen_params.delete_if {|k,v| :handlers == k}) }.should raise_error(ArgumentError)
+ end
+
+ it "should require at least one protocol" do
+ Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError)
+ end
+
+ it "should require a listening address to be specified" do
+ Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError)
+ end
+
+ it "should require a listening port to be specified" do
+ Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError)
+ end
+
+ it "should order a webrick server to start" do
+ @mock_webrick.expects(:start)
+ @server.listen(@listen_params)
+ end
+
+ it "should tell webrick to listen on the specified address and port" do
+ WEBrick::HTTPServer.expects(:new).with {|args|
+ args[:Port] == 31337 and args[:BindAddress] == "127.0.0.1"
+ }.returns(@mock_webrick)
+ @server.listen(@listen_params)
+ end
+
+ it "should be listening" do
+ @server.listen(@listen_params)
+ @server.should be_listening
+ end
+
+ 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_webrick and args[:handler] == handler
+ }.returns(mock_handler)
+ 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
+ @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)
+ end
+end
+
+describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do
+ before do
+ @mock_webrick = mock('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, :configuration ], :protocols => [ :rest, :xmlrpc ] }
+ end
+
+ it "should fail unless listening" do
+ Proc.new { @server.unlisten }.should raise_error(RuntimeError)
+ end
+
+ it "should order webrick server to stop" do
+ @mock_webrick.expects(:shutdown)
+ @server.listen(@listen_params)
+ @server.unlisten
+ end
+
+ it "should no longer be listening" do
+ @server.listen(@listen_params)
+ @server.unlisten
+ @server.should_not be_listening
+ end
+end
diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb
new file mode 100644
index 000000000..aa7a3d53a
--- /dev/null
+++ b/spec/unit/network/http/webrick/rest.rb
@@ -0,0 +1,286 @@
+#!/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_model = mock('indirected model')
+ Puppet::Indirector::Indirection.stubs(:model).returns(@mock_model)
+ @params = { :server => @mock_webrick, :handler => :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)
+ 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)
+ 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)
+ 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)
+ end
+end
+
+describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do
+ before 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)
+ Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class)
+ @handler = Puppet::Network::HTTP::WEBrickREST.new(:server => @mock_webrick, :handler => :foo)
+ end
+
+ def setup_find_request
+ @mock_request.stubs(:request_method).returns('GET')
+ @mock_request.stubs(:path).returns('/foo/key')
+ @mock_model_class.stubs(:find)
+ end
+
+ def setup_search_request
+ @mock_request.stubs(:request_method).returns('GET')
+ @mock_request.stubs(:path).returns('/foos')
+ @mock_model_class.stubs(:search).returns([])
+ end
+
+ def setup_destroy_request
+ @mock_request.stubs(:request_method).returns('DELETE')
+ @mock_request.stubs(:path).returns('/foo/key')
+ @mock_model_class.stubs(:destroy)
+ end
+
+ def setup_save_request
+ @mock_request.stubs(:request_method).returns('PUT')
+ @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)
+ end
+
+ def setup_bad_request
+ @mock_request.stubs(:request_method).returns('POST')
+ @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', {})
+ @handler.service(@mock_request, @mock_response)
+ end
+
+ it "should call the model search method if the request represents a plural HTTP GET" do
+ setup_search_request
+ @mock_model_class.expects(:search).returns([])
+ @handler.service(@mock_request, @mock_response)
+ end
+
+ it "should call the model destroy method if the request represents an HTTP DELETE" do
+ setup_destroy_request
+ @mock_model_class.expects(:destroy).with('key', {})
+ @handler.service(@mock_request, @mock_response)
+ end
+
+ 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)
+ @handler.service(@mock_request, @mock_response)
+ end
+
+ it "should fail if the HTTP method isn't supported" do
+ @mock_request.stubs(:request_method).returns('POST')
+ @mock_request.stubs(:path).returns('/foo')
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should fail if the 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)
+
+ @mock_request.stubs(:request_method).returns('PUT')
+ @mock_request.stubs(:path).returns('/foos/key')
+ @mock_response.expects(:status=).with(404)
+ @handler.process(@mock_request, @mock_response)
+ end
+
+ it "should fail if the request is for an unknown path" do
+ @mock_request.stubs(:request_method).returns('GET')
+ @mock_request.stubs(:path).returns('/bar/key')
+ @mock_response.expects(:status=).with(404)
+ @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
+ 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
+ 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
+ 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
+
+ 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)
+ 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)
+ @handler.process(@mock_request, @mock_response)
+ end
+end
diff --git a/spec/unit/network/http/webrick/xmlrpc.rb b/spec/unit/network/http/webrick/xmlrpc.rb
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/spec/unit/network/http/webrick/xmlrpc.rb
diff --git a/spec/unit/network/rest_controller.rb b/spec/unit/network/rest_controller.rb
deleted file mode 100644
index 0bcc0abf2..000000000
--- a/spec/unit/network/rest_controller.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/env ruby
-#
-# Created by Rick Bradley on 2007-10-03.
-# Copyright (c) 2007. All rights reserved.
-
-require File.dirname(__FILE__) + '/../../spec_helper'
-
-require 'puppet/network/rest_controller'
-
-describe Puppet::Network::RESTController, "in general" do
- it "should route GET requests on indirector's name to indirector find for the model class"
- it "should route GET requests on indirector's plural name to indirector search for the model class"
- it "should route DELETE requests on indirector's name to indirector destroy for the model class"
- it "should route POST requests on indirector's name to indirector save for the model class"
- it "should serialize result data when methods are handled"
- it "should serialize an error condition when indirection method call generates an exception"
-end
-
-__END__
-
-# possible implementation of the satisfying class
-
-class RESTController
- def initialize(klass)
- @klass = klass
- end
-
- # TODO: is it possible to distinguish from the request object the path which we were called by?
-
- def do_GET(request, response)
- return do_GETS(request, response) if asked_for_plural?(request)
- args = request.something
- result = @klass.find args
- return serialize(result)
- end
-
- def do_GETS(request, response)
- args = request.something
- result = @klass.search args
- return serialize(result)
- end
-
- def do_DELETE(request, response)
- args = request.something
- result = @klass.destroy args
- return serialize(result)
- end
-
- def do_PUT(request, response)
- args = request.something
- obj = @klass.new(args)
- result = obj.save
- return serialize(result)
- end
-
- def do_POST(request, response)
- do_PUT(request, response)
- end
-
- private
-
- def asked_for_plural?(request)
- # TODO: pick apart the request and see if this was trying to do a plural or singular GET
- end
-end
diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb
index 17ed336de..3e29807ad 100644
--- a/spec/unit/network/server.rb
+++ b/spec/unit/network/server.rb
@@ -4,181 +4,286 @@
# Copyright (c) 2007. All rights reserved.
require File.dirname(__FILE__) + '/../../spec_helper'
-
require 'puppet/network/server'
-# a fake server class, so we don't have to implement full autoloading etc. (or at least just yet) just to do testing
-class TestServer < Puppet::Network::Server
- def start_web_server
- end
-
- def stop_web_server
- end
-end
-
describe Puppet::Network::Server, "when initializing" do
- before do
- Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
- end
-
- it "should use the Puppet configurator to determine which HTTP server will be used to provide access to clients" do
- Puppet.expects(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new
- @server.server_type.should == :suparserver
- end
-
- it "should fail to initialize if there is no HTTP server known to the Puppet configurator" do
- Puppet.expects(:[]).with(:servertype).returns(nil)
- Proc.new { Puppet::Network::Server.new }.should raise_error
- end
-
- it "should use the Puppet Configurator to determine what style of service we will offer to clients (e.g., REST, XMLRPC, ...)"
- it "should fail to initialize if there is no style of service known to the Puppet configurator"
-
- it "should allow registering indirections" do
- @server = Puppet::Network::Server.new(:handlers => [ :foo, :bar, :baz])
- Proc.new { @server.unregister(:foo, :bar, :baz) }.should_not raise_error
- end
-
- it "should not be listening after initialization" do
- Puppet::Network::Server.new.should_not be_listening
- end
+ before do
+ @mock_http_server_class = mock('http server class')
+ Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
+ Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class)
+ end
+
+ it "should allow specifying a listening address" do
+ Puppet.stubs(:[]).with(:masterport).returns('')
+ @server = Puppet::Network::Server.new(:address => "127.0.0.1")
+ @server.address.should == "127.0.0.1"
+ end
+
+ it "should allow specifying a listening port" do
+ Puppet.stubs(:[]).with(:bindaddress).returns('')
+ @server = Puppet::Network::Server.new(:port => 31337)
+ @server.port.should == 31337
+ end
+
+ it "should use the Puppet configurator to find a default listening address" do
+ Puppet.stubs(:[]).with(:masterport).returns('')
+ Puppet.expects(:[]).with(:bindaddress).returns("10.0.0.1")
+ @server = Puppet::Network::Server.new
+ @server.address.should == "10.0.0.1"
+ end
+
+ it "should use the Puppet configurator to find a default listening port" do
+ Puppet.stubs(:[]).with(:bindaddress).returns('')
+ Puppet.expects(:[]).with(:masterport).returns(6667)
+ @server = Puppet::Network::Server.new
+ @server.port.should == 6667
+ end
+
+ it "should fail to initialize if no listening address can be found" do
+ Puppet.stubs(:[]).with(:masterport).returns(6667)
+ Puppet.stubs(:[]).with(:bindaddress).returns(nil)
+ Proc.new { Puppet::Network::Server.new }.should raise_error(ArgumentError)
+ end
+
+ it "should fail to initialize if no listening port can be found" do
+ Puppet.stubs(:[]).with(:bindaddress).returns("127.0.0.1")
+ Puppet.stubs(:[]).with(:masterport).returns(nil)
+ Proc.new { Puppet::Network::Server.new }.should raise_error(ArgumentError)
+ end
+
+ it "should use the Puppet configurator to determine which HTTP server will be used to provide access to clients" do
+ Puppet.expects(:[]).with(:servertype).returns(:suparserver)
+ @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337)
+ @server.server_type.should == :suparserver
+ end
+
+ it "should fail to initialize if there is no HTTP server known to the Puppet configurator" do
+ Puppet.expects(:[]).with(:servertype).returns(nil)
+ Proc.new { Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) }.should raise_error
+ end
+
+ it "should ask the Puppet::Network::HTTP class to fetch the proper HTTP server class" do
+ Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(@mock_http_server_class)
+ @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337)
+ end
+
+ it "should fail if the HTTP server class is unknown" do
+ Puppet::Network::HTTP.stubs(:server_class_by_type).returns(nil)
+ Proc.new { Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) }.should raise_error(ArgumentError)
+ end
+
+ it "should allow registering indirections" do
+ @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337, :handlers => [ :foo, :bar, :baz])
+ Proc.new { @server.unregister(:foo, :bar, :baz) }.should_not raise_error
+ end
+
+ it "should not be listening after initialization" do
+ Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337).should_not be_listening
+ end
end
describe Puppet::Network::Server, "in general" do
- before do
- Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
- Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new
- end
-
- it "should allow registering an indirection for client access by specifying its indirection name" do
- Proc.new { @server.register(:foo) }.should_not raise_error
- end
-
- it "should require at least one indirection name when registering indirections for client access" do
- Proc.new { @server.register }.should raise_error(ArgumentError)
- end
-
- it "should allow for numerous indirections to be registered at once for client access" do
- Proc.new { @server.register(:foo, :bar, :baz) }.should_not raise_error
- end
-
- it "should allow the use of indirection names to specify which indirections are to be no longer accessible to clients" do
- @server.register(:foo)
- Proc.new { @server.unregister(:foo) }.should_not raise_error
- end
-
- it "should leave other indirections accessible to clients when turning off indirections" do
- @server.register(:foo, :bar)
- @server.unregister(:foo)
- Proc.new { @server.unregister(:bar)}.should_not raise_error
- end
-
- it "should allow specifying numerous indirections which are to be no longer accessible to clients" do
- @server.register(:foo, :bar)
- Proc.new { @server.unregister(:foo, :bar) }.should_not raise_error
- end
-
- it "should not allow turning off unknown indirection names" do
- @server.register(:foo, :bar)
- Proc.new { @server.unregister(:baz) }.should raise_error(ArgumentError)
- end
-
- it "should disable client access immediately when turning off indirections" do
- @server.register(:foo, :bar)
- @server.unregister(:foo)
- Proc.new { @server.unregister(:foo) }.should raise_error(ArgumentError)
- end
-
- it "should allow turning off all indirections at once" do
- @server.register(:foo, :bar)
- @server.unregister
- [ :foo, :bar, :baz].each do |indirection|
- Proc.new { @server.unregister(indirection) }.should raise_error(ArgumentError)
- end
- end
-
- it "should provide a means of determining whether it is listening" do
- @server.should respond_to(:listening?)
- end
-
- it "should provide a means of determining which HTTP server will be used to provide access to clients" do
- @server.server_type.should == :suparserver
- end
-
- it "should provide a means of determining which style of service is being offered to clients"
-
- it "should allow for multiple configurations, each handling different indirections" do
- @server2 = Puppet::Network::Server.new
- @server.register(:foo, :bar)
- @server2.register(:foo, :xyzzy)
- @server.unregister(:foo, :bar)
- @server2.unregister(:foo, :xyzzy)
- Proc.new { @server.unregister(:xyzzy) }.should raise_error(ArgumentError)
- Proc.new { @server2.unregister(:bar) }.should raise_error(ArgumentError)
- end
-end
+ before 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)
+ end
+
+ it "should allow registering an indirection for client access by specifying its indirection name" do
+ Proc.new { @server.register(:foo) }.should_not raise_error
+ end
+
+ it "should require at least one indirection name when registering indirections for client access" do
+ Proc.new { @server.register }.should raise_error(ArgumentError)
+ end
+
+ it "should allow for numerous indirections to be registered at once for client access" do
+ Proc.new { @server.register(:foo, :bar, :baz) }.should_not raise_error
+ end
-describe Puppet::Network::Server, "when listening is turned off" do
- before do
- Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
- Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new
- end
+ it "should allow the use of indirection names to specify which indirections are to be no longer accessible to clients" do
+ @server.register(:foo)
+ Proc.new { @server.unregister(:foo) }.should_not raise_error
+ end
+
+ it "should leave other indirections accessible to clients when turning off indirections" do
+ @server.register(:foo, :bar)
+ @server.unregister(:foo)
+ Proc.new { @server.unregister(:bar)}.should_not raise_error
+ end
- it "should allow listening to be turned on" do
- Proc.new { @server.listen }.should_not raise_error
- end
+ it "should allow specifying numerous indirections which are to be no longer accessible to clients" do
+ @server.register(:foo, :bar)
+ Proc.new { @server.unregister(:foo, :bar) }.should_not raise_error
+ end
+
+ it "should not turn off any indirections if given unknown indirection names to turn off" do
+ @server.register(:foo, :bar)
+ Proc.new { @server.unregister(:foo, :bar, :baz) }.should raise_error(ArgumentError)
+ Proc.new { @server.unregister(:foo, :bar) }.should_not raise_error
+ end
- it "should not allow listening to be turned off" do
- Proc.new { @server.unlisten }.should raise_error(RuntimeError)
- end
+ it "should not allow turning off unknown indirection names" do
+ @server.register(:foo, :bar)
+ Proc.new { @server.unregister(:baz) }.should raise_error(ArgumentError)
+ end
- it "should indicate that it is not listening" do
- @server.should_not be_listening
- end
+ it "should disable client access immediately when turning off indirections" do
+ @server.register(:foo, :bar)
+ @server.unregister(:foo)
+ Proc.new { @server.unregister(:foo) }.should raise_error(ArgumentError)
+ end
- it "should cause the HTTP server to listen when listening is turned on" do
- @server.expects(:start_web_server)
- @server.listen
- end
+ it "should allow turning off all indirections at once" do
+ @server.register(:foo, :bar)
+ @server.unregister
+ [ :foo, :bar, :baz].each do |indirection|
+ Proc.new { @server.unregister(indirection) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "should provide a means of determining whether it is listening" do
+ @server.should respond_to(:listening?)
+ end
- it "should not route HTTP GET requests to a controller for the registered indirection"
- it "should not route HTTP DELETE requests to a controller for the registered indirection"
- it "should not route HTTP POST requests to a controller for the registered indirection"
+ it "should provide a means of determining which HTTP server will be used to provide access to clients" do
+ @server.server_type.should == :suparserver
+ end
+
+ it "should allow for multiple configurations, each handling different indirections" do
+ @server2 = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337)
+ @server.register(:foo, :bar)
+ @server2.register(:foo, :xyzzy)
+ @server.unregister(:foo, :bar)
+ @server2.unregister(:foo, :xyzzy)
+ Proc.new { @server.unregister(:xyzzy) }.should raise_error(ArgumentError)
+ Proc.new { @server2.unregister(:bar) }.should raise_error(ArgumentError)
+ end
- # TODO: FIXME write integrations which fire up actual webrick / mongrel servers and are thus webrick / mongrel specific?]
+ it "should provide a means of determining which style of service is being offered to clients" do
+ @server.protocols.should == []
+ end
+
+ it "should provide a means of determining the listening address" do
+ @server.address.should == "127.0.0.1"
+ end
+
+ it "should provide a means of determining the listening port" do
+ @server.port.should == 31337
+ end
end
-describe Puppet::Network::Server, "when listening is turned on" do
- before do
- Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
- Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new
- @server.listen
- end
-
- it "should allow listening to be turned off" do
- Proc.new { @server.unlisten }.should_not raise_error
- end
+describe Puppet::Network::Server, "when listening is off" do
+ before 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)
+ @mock_http_server = mock('http server')
+ @mock_http_server.stubs(:listen)
+ @server.stubs(:http_server).returns(@mock_http_server)
+ end
+
+ it "should indicate that it is not listening" do
+ @server.should_not be_listening
+ end
- it "should not allow listening to be turned on" do
- Proc.new { @server.listen }.should raise_error(RuntimeError)
- end
+ it "should not allow listening to be turned off" do
+ Proc.new { @server.unlisten }.should raise_error(RuntimeError)
+ end
- it "should indicate that listening is turned off" do
- @server.should be_listening
- end
+ it "should allow listening to be turned on" do
+ Proc.new { @server.listen }.should_not raise_error
+ end
+
+end
- it "should cause the HTTP server to stop listening when listening is turned off" do
- @server.expects(:stop_web_server)
- @server.unlisten
- end
+describe Puppet::Network::Server, "when listening is on" do
+ before 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)
+ @mock_http_server = mock('http server')
+ @mock_http_server.stubs(:listen)
+ @mock_http_server.stubs(:unlisten)
+ @server.stubs(:http_server).returns(@mock_http_server)
+ @server.listen
+ end
+
+ it "should indicate that listening is turned off" do
+ @server.should be_listening
+ end
+
+ it "should not allow listening to be turned on" do
+ Proc.new { @server.listen }.should raise_error(RuntimeError)
+ end
- it "should route HTTP GET requests to a controller for the registered indirection"
- it "should route HTTP DELETE requests to a controller for the registered indirection"
- it "should route HTTP POST requests to a controller for the registered indirection"
+ it "should allow listening to be turned off" do
+ Proc.new { @server.unlisten }.should_not raise_error
+ end
+end
+
+describe Puppet::Network::Server, "when listening is being turned on" do
+ before 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)
+ @mock_http_server = mock('http server')
+ @mock_http_server.stubs(:listen)
+ end
+
+ it "should fetch an instance of an HTTP server when listening is turned on" 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.listen
+ end
+
+ it "should cause the HTTP server to listen when listening is turned on" do
+ @mock_http_server.expects(:listen)
+ @server.expects(:http_server).returns(@mock_http_server)
+ @server.listen
+ end
+end
+
+describe Puppet::Network::Server, "when listening is being turned off" do
+ before 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)
+ @mock_http_server = mock('http server')
+ @mock_http_server.stubs(:listen)
+ @server.stubs(:http_server).returns(@mock_http_server)
+ @server.listen
+ end
+
+ it "should cause the HTTP server to stop listening when listening is turned off" do
+ @mock_http_server.expects(:unlisten)
+ @server.unlisten
+ end
+
+ it "should not allow for indirections to be turned off" do
+ @server.register(:foo)
+ Proc.new { @server.unregister(:foo) }.should raise_error(RuntimeError)
+ end
+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
- # TODO: FIXME [ write integrations which fire up actual webrick / mongrel servers and are thus webrick / mongrel specific?]
+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