summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/network/http/mongrel/rest.rb22
-rw-r--r--lib/puppet/network/http/webrick/rest.rb13
-rwxr-xr-xspec/unit/network/http/mongrel/rest.rb452
-rwxr-xr-xspec/unit/network/http/webrick/rest.rb364
4 files changed, 214 insertions, 637 deletions
diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb
index 520ad67f0..d6c2e4679 100644
--- a/lib/puppet/network/http/mongrel/rest.rb
+++ b/lib/puppet/network/http/mongrel/rest.rb
@@ -4,24 +4,28 @@ class Puppet::Network::HTTP::MongrelREST < Mongrel::HttpHandler
include Puppet::Network::HTTP::Handler
+ ACCEPT_HEADER = "HTTP_ACCEPT".freeze # yay, zed's a crazy-man
+
def initialize(args={})
super()
initialize_for_puppet(args)
end
- # Return the query params for this request. We had to expose this method for
- # testing purposes.
- def params(request)
- Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]).merge(client_info(request))
+ def accept_header(request)
+ request.params[ACCEPT_HEADER]
end
- private
-
# which HTTP verb was used in this request
def http_method(request)
request.params[Mongrel::Const::REQUEST_METHOD]
end
+ # Return the query params for this request. We had to expose this method for
+ # testing purposes.
+ def params(request)
+ Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]).merge(client_info(request))
+ end
+
# what path was requested?
def path(request)
# LAK:NOTE See http://snurl.com/21zf8 [groups_google_com]
@@ -39,8 +43,12 @@ class Puppet::Network::HTTP::MongrelREST < Mongrel::HttpHandler
request.body
end
+ def set_content_type(response, format)
+ response.header['Content-Type'] = format
+ end
+
# produce the body of the response
- def encode_result(request, response, result, status = 200)
+ def set_response(response, result, status = 200)
response.start(status) do |head, body|
body.write(result)
end
diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb
index a235fb4f3..c7cc06916 100644
--- a/lib/puppet/network/http/webrick/rest.rb
+++ b/lib/puppet/network/http/webrick/rest.rb
@@ -10,7 +10,7 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet
initialize_for_puppet(:server => server, :handler => handler)
end
- # We had to expose this method for testing purposes.
+ # Retrieve the request parameters, including authentication information.
def params(request)
result = request.query
result.merge(client_information(request))
@@ -21,7 +21,9 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet
process(request, response)
end
- private
+ def accept_header(request)
+ request[:accept]
+ end
def http_method(request)
request.request_method
@@ -41,7 +43,12 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet
request.body
end
- def encode_result(request, response, result, status = 200)
+ # Set the specified format as the content type of the response.
+ def set_content_type(response, format)
+ response[:content_type] = format
+ end
+
+ def set_response(response, result, status = 200)
response.status = status
response.body = result
end
diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb
index 9f35c199c..5435b0372 100755
--- a/spec/unit/network/http/mongrel/rest.rb
+++ b/spec/unit/network/http/mongrel/rest.rb
@@ -3,377 +3,155 @@
require File.dirname(__FILE__) + '/../../../../spec_helper'
require 'puppet/network/http'
-describe "Puppet::Network::HTTP::MongrelREST", "when initializing" do
+describe "Puppet::Network::HTTP::MongrelREST" do
confine "Mongrel is not available" => Puppet.features.mongrel?
- before do
- require 'puppet/network/http/mongrel/rest'
-
- @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
-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(:from_yaml).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, args| key == '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).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, args| key == '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)
- @handler.process(@mock_request, @mock_response)
+ it "should include the Puppet::Network::HTTP::Handler module" do
+ Puppet::Network::HTTP::MongrelREST.ancestors.should be_include(Puppet::Network::HTTP::Handler)
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(400)
- @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(400)
- @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(400)
- @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(400)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should serialize a controller exception if the request fails" do
- @mock_request.stubs(:params).raises(ArgumentError, "This is a failure")
- body = mock 'body'
- @mock_response.expects(:start).with(400).yields("head", body)
- body.expects(:write).with("This is a failure")
- @handler.process(@mock_request, @mock_response)
+ describe "when initializing" do
+ it "should call the Handler's initialization hook with its provided arguments as the server and handler" do
+ Puppet::Network::HTTP::MongrelREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments")
+ Puppet::Network::HTTP::MongrelREST.new(:server => "my", :handler => "arguments")
+ end
end
- describe "and determining the request parameters", :shared => true do
- confine "Mongrel is not available" => Puppet.features.mongrel?
-
+ describe "when receiving a request" do
before do
- @mock_request.stubs(:params).returns({})
- end
-
- it "should include the HTTP request parameters" do
- @mock_request.expects(:params).returns('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- result = @handler.params(@mock_request)
- result["foo"].should == "baz"
- result["bar"].should == "xyzzy"
- end
-
- it "should pass the client's ip address to model find" do
- @mock_request.stubs(:params).returns("REMOTE_ADDR" => "ipaddress")
- @handler.params(@mock_request)[:ip].should == "ipaddress"
- end
-
- it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do
- Puppet.settings.stubs(:value).returns "eh"
- Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader"
- @mock_request.stubs(:params).returns("myheader" => "/CN=host.domain.com")
- @handler.params(@mock_request)
- end
-
- it "should retrieve the hostname by matching the certificate parameter" do
- Puppet.settings.stubs(:value).returns "eh"
- Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader"
- @mock_request.stubs(:params).returns("myheader" => "/CN=host.domain.com")
- @handler.params(@mock_request)[:node].should == "host.domain.com"
- end
-
- it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do
- Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader"
- Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader"
- @mock_request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com")
- @handler.params(@mock_request)
- end
+ @params = {}
+ @request = stub('mongrel http request', :params => @params)
+
+ @head = stub('response head')
+ @body = stub('response body', :write => true)
+ @response = stub('mongrel http response')
+ @response.stubs(:start).yields(@head, @body)
+ @model_class = stub('indirected model class')
+ @mongrel = stub('mongrel http server', :register => true)
+ Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class)
+ @handler = Puppet::Network::HTTP::MongrelREST.new(:server => @mongrel, :handler => :foo)
+ end
+
+ describe "and using the HTTP Handler interface" do
+ it "should return the HTTP_ACCEPT parameter as the accept header" do
+ @params.expects(:[]).with("HTTP_ACCEPT").returns "myaccept"
+ @handler.accept_header(@request).should == "myaccept"
+ end
- it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do
- Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader"
- Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader"
- @mock_request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com")
- @handler.params(@mock_request)[:authenticated].should be_true
- end
+ it "should use the REQUEST_METHOD as the http method" do
+ @params.expects(:[]).with(Mongrel::Const::REQUEST_METHOD).returns "mymethod"
+ @handler.http_method(@request).should == "mymethod"
+ end
- it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do
- Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader"
- Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader"
- @mock_request.stubs(:params).returns("myheader" => "whatever", "certheader" => "/CN=host.domain.com")
- @handler.params(@mock_request)[:authenticated].should be_false
- end
+ it "should use the first part of the request path as the path" do
+ @params.expects(:[]).with(Mongrel::Const::REQUEST_PATH).returns "/foo/bar"
+ @handler.path(@request).should == "/foo"
+ end
- it "should consider the host unauthenticated if no certificate information is present" do
- Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader"
- Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader"
- @mock_request.stubs(:params).returns("myheader" => nil, "certheader" => "SUCCESS")
- @handler.params(@mock_request)[:authenticated].should be_false
- end
+ it "should use the second part of the request path as the request key" do
+ @params.expects(:[]).with(Mongrel::Const::REQUEST_PATH).returns "/foo/bar"
+ @handler.request_key(@request).should == "bar"
+ end
- it "should not pass a node name to model method if no certificate information is present" do
- Puppet.settings.stubs(:value).returns "eh"
- Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader"
- @mock_request.stubs(:params).returns("myheader" => nil)
- @handler.params(@mock_request).should_not be_include(:node)
- end
- end
+ it "should return the request body as the body" do
+ @request.expects(:body).returns "mybody"
+ @handler.body(@request).should == "mybody"
+ end
- describe "when finding a model instance" do |variable|
- confine "Mongrel is not available" => Puppet.features.mongrel?
+ it "should set the response's content-type header when setting the content type" do
+ @header = mock 'header'
+ @response.expects(:header).returns @header
+ @header.expects(:[]=).with('Content-Type', "mytype")
- 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(400)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should use a common method for determining the request parameters" do
- setup_find_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- @handler.expects(:params).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:find).with do |key, args|
- args[:foo] == :baz and args[:bar] == :xyzzy
+ @handler.set_content_type(@response, "mytype")
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 set the status and write the body when setting the response for a successful request" do
+ head = mock 'head'
+ body = mock 'body'
+ @response.expects(:start).with(200).yields(head, body)
- 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
+ body.expects(:write).with("mybody")
- 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(400)
- @handler.process(@mock_request, @mock_response)
- end
- end
+ @handler.set_response(@response, "mybody", 200)
+ end
- describe "when destroying a model instance" do |variable|
- confine "Mongrel is not available" => Puppet.features.mongrel?
+ it "should set the status and reason and write the body when setting the response for a successful request" do
+ head = mock 'head'
+ body = mock 'body'
+ @response.expects(:start).with(400, false, "mybody").yields(head, body)
- 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(400)
- @handler.process(@mock_request, @mock_response)
- end
+ body.expects(:write).with("mybody")
- it "should use a common method for determining the request parameters" do
- setup_destroy_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- @handler.expects(:params).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:destroy).with do |key, args|
- args[:foo] == :baz and args[:bar] == :xyzzy
+ @handler.set_response(@response, "mybody", 400)
end
- @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'
+ describe "and determining the request parameters", :shared => true do
+ before do
+ @request.stubs(:params).returns({})
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(400)
- @handler.process(@mock_request, @mock_response)
- end
- end
-
- describe "when saving a model instance" do |variable|
- confine "Mongrel is not available" => Puppet.features.mongrel?
-
- 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(400)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should use a common method for determining the request parameters" do
- setup_save_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- @handler.expects(:params).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_instance.expects(:save).with do |args|
- args[:foo] == :baz and args[:bar] == :xyzzy
+ it "should include the HTTP request parameters" do
+ @request.expects(:params).returns('QUERY_STRING' => 'foo=baz&bar=xyzzy')
+ result = @handler.params(@request)
+ result["foo"].should == "baz"
+ result["bar"].should == "xyzzy"
end
- @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 pass the client's ip address to model find" do
+ @request.stubs(:params).returns("REMOTE_ADDR" => "ipaddress")
+ @handler.params(@request)[:ip].should == "ipaddress"
+ 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(400)
- @handler.process(@mock_request, @mock_response)
- end
- end
+ it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do
+ Puppet.settings.stubs(:value).returns "eh"
+ Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader"
+ @request.stubs(:params).returns("myheader" => "/CN=host.domain.com")
+ @handler.params(@request)
+ end
- describe "when searching for model instances" do |variable|
- confine "Mongrel is not available" => Puppet.features.mongrel?
+ it "should retrieve the hostname by matching the certificate parameter" do
+ Puppet.settings.stubs(:value).returns "eh"
+ Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader"
+ @request.stubs(:params).returns("myheader" => "/CN=host.domain.com")
+ @handler.params(@request)[:node].should == "host.domain.com"
+ end
- it "should use a common method for determining the request parameters" do
- setup_search_request('QUERY_STRING' => 'foo=baz&bar=xyzzy')
- @handler.expects(:params).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:search).with do |args|
- args[:foo] == :baz and args[:bar] == :xyzzy
+ it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do
+ Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader"
+ Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader"
+ @request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com")
+ @handler.params(@request)
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 consider the host authenticated if the validity parameter contains 'SUCCESS'" do
+ Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader"
+ Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader"
+ @request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com")
+ @handler.params(@request)[:authenticated].should be_true
+ 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 consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do
+ Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader"
+ Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader"
+ @request.stubs(:params).returns("myheader" => "whatever", "certheader" => "/CN=host.domain.com")
+ @handler.params(@request)[:authenticated].should be_false
+ 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 consider the host unauthenticated if no certificate information is present" do
+ Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader"
+ Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader"
+ @request.stubs(:params).returns("myheader" => nil, "certheader" => "SUCCESS")
+ @handler.params(@request)[:authenticated].should be_false
+ 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(400)
- @handler.process(@mock_request, @mock_response)
+ it "should not pass a node name to model method if no certificate information is present" do
+ Puppet.settings.stubs(:value).returns "eh"
+ Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader"
+ @request.stubs(:params).returns("myheader" => nil)
+ @handler.params(@request).should_not be_include(:node)
+ end
end
- end
+ end
end
diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb
index 782f99045..b42053d53 100755
--- a/spec/unit/network/http/webrick/rest.rb
+++ b/spec/unit/network/http/webrick/rest.rb
@@ -4,322 +4,106 @@ require File.dirname(__FILE__) + '/../../../../spec_helper'
require 'puppet/network/http'
require 'puppet/network/http/webrick/rest'
-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 = [ @mock_webrick, :foo ]
+describe Puppet::Network::HTTP::WEBrickREST do
+ it "should include the Puppet::Network::HTTP::Handler module" do
+ Puppet::Network::HTTP::WEBrickREST.ancestors.should be_include(Puppet::Network::HTTP::Handler)
end
- it "should require access to a WEBrick server" do
- 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.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)
- 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
-end
-
-describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do
- before do
- @mock_request = stub('webrick http request', :query => {}, :peeraddr => %w{eh boo host ip}, :client_cert => nil)
- @mock_response = stub('webrick http response', :status= => true, :body= => true)
- @mock_model_class = stub('indirected model class')
- @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(@mock_webrick, :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(:from_yaml).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 delegate its :service method to its :process method" do
- @handler.expects(:process).with(@mock_request, @mock_response).returns "stuff"
- @handler.service(@mock_request, @mock_response).should == "stuff"
- 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, args| key == '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).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, args| key == '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)
- @handler.process(@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(400)
- @handler.process(@mock_request, @mock_response)
- end
-
- 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(400)
- @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(400)
- @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(400)
- @handler.process(@mock_request, @mock_response)
- end
-
- it "should set the response status to 400 and the body to the exception message if the request fails" do
- @mock_request.stubs(:request_method).raises(ArgumentError, "This is a failure")
- @mock_request.stubs(:path).returns('/foos')
- @mock_response.expects(:status=).with(400)
- @mock_response.expects(:body=).with("This is a failure")
- @handler.process(@mock_request, @mock_response)
- end
-
- describe "and determining the request parameters" do
- it "should include the HTTP request parameters" do
- @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
- result = @handler.params(@mock_request)
- result[:foo].should == :baz
- result[:bar].should == :xyzzy
- end
-
- it "should pass the client's ip address to model find" do
- @mock_request.stubs(:peeraddr).returns(%w{noidea dunno hostname ipaddress})
- @handler.params(@mock_request)[:ip].should == "ipaddress"
- end
-
- it "should set 'authenticated' to true if a certificate is present" do
- cert = stub 'cert', :subject => [%w{CN host.domain.com}]
- @mock_request.stubs(:client_cert).returns cert
- @handler.params(@mock_request)[:authenticated].should be_true
- end
-
- it "should set 'authenticated' to false if no certificate is present" do
- @mock_request.stubs(:client_cert).returns nil
- @handler.params(@mock_request)[:authenticated].should be_false
- end
-
- it "should pass the client's certificate name to model method if a certificate is present" do
- cert = stub 'cert', :subject => [%w{CN host.domain.com}]
- @mock_request.stubs(:client_cert).returns cert
- @handler.params(@mock_request)[:node].should == "host.domain.com"
- end
-
- it "should not pass a node name to model method if no certificate is present" do
- @mock_request.stubs(:client_cert).returns nil
- @handler.params(@mock_request).should_not be_include(:node)
+ describe "when initializing" do
+ it "should call the Handler's initialization hook with its provided arguments as the server and handler" do
+ Puppet::Network::HTTP::WEBrickREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments")
+ Puppet::Network::HTTP::WEBrickREST.new("my", "arguments")
end
end
- 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(400)
- @handler.process(@mock_request, @mock_response)
+ describe "when receiving a request" do
+ before do
+ @request = stub('webrick http request', :query => {}, :peeraddr => %w{eh boo host ip}, :client_cert => nil)
+ @response = stub('webrick http response', :status= => true, :body= => true)
+ @model_class = stub('indirected model class')
+ @webrick = stub('webrick http server', :mount => true, :[] => {})
+ Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class)
+ @handler = Puppet::Network::HTTP::WEBrickREST.new(@webrick, :foo)
end
- it "should use a common method for determining the request parameters" do
- setup_find_request
- @handler.stubs(:params).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:find).with do |key, args|
- args[:foo] == :baz and args[:bar] == :xyzzy
- end
- @handler.process(@mock_request, @mock_response)
+ it "should delegate its :service method to its :process method" do
+ @handler.expects(:process).with(@request, @response).returns "stuff"
+ @handler.service(@request, @response).should == "stuff"
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(400)
- @handler.process(@mock_request, @mock_response)
- end
- end
+ describe "when using the Handler interface" do
+ it "should use the 'accept' request parameter as the Accept header" do
+ @request.expects(:[]).with(:accept).returns "foobar"
+ @handler.accept_header(@request).should == "foobar"
+ end
- 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(400)
- @handler.process(@mock_request, @mock_response)
- end
+ it "should use the request method as the http method" do
+ @request.expects(:request_method).returns "FOO"
+ @handler.http_method(@request).should == "FOO"
+ end
- it "should use a common method for determining the request parameters" do
- setup_destroy_request
- @handler.stubs(:params).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:destroy).with do |key, args|
- args[:foo] == :baz and args[:bar] == :xyzzy
+ it "should return the first argument of the request path as the path" do
+ @request.expects(:path).returns "/foo/bar"
+ @handler.path(@request).should == "/foo"
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(:status=).with(200)
- @handler.process(@mock_request, @mock_response)
- end
+ it "should return the second field in the path as the request key" do
+ @request.expects(:path).returns "/foo/bar"
+ @handler.request_key(@request).should == "bar"
+ 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 the request body as the body" do
+ @request.expects(:body).returns "my body"
+ @handler.body(@request).should == "my body"
+ 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(400)
- @handler.process(@mock_request, @mock_response)
- end
- end
+ it "should set the response's :content_type header when setting the content type" do
+ @response.expects(:[]=).with(:content_type, "text/html")
+ @handler.set_content_type(@response, "text/html")
+ end
- 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(400)
- @handler.process(@mock_request, @mock_response)
- end
+ it "should set the status and body on the response when setting the response" do
+ @response.expects(:status=).with 200
+ @response.expects(:body=).with "mybody"
- it "should use a common method for determining the request parameters" do
- setup_save_request
- @handler.stubs(:params).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_instance.expects(:save).with do |args|
- args[:foo] == :baz and args[:bar] == :xyzzy
+ @handler.set_response(@response, "mybody", 200)
end
- @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
+ describe "and determining the request parameters" do
+ it "should include the HTTP request parameters" do
+ @request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy)
+ result = @handler.params(@request)
+ result[:foo].should == :baz
+ result[:bar].should == :xyzzy
+ 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(400)
- @handler.process(@mock_request, @mock_response)
- end
- end
+ it "should pass the client's ip address to model find" do
+ @request.stubs(:peeraddr).returns(%w{noidea dunno hostname ipaddress})
+ @handler.params(@request)[:ip].should == "ipaddress"
+ end
- describe "when searching for model instances" do
- it "should use a common method for determining the request parameters" do
- setup_search_request
- @handler.stubs(:params).returns(:foo => :baz, :bar => :xyzzy)
- @mock_model_class.expects(:search).with do |args|
- args[:foo] == :baz and args[:bar] == :xyzzy
+ it "should set 'authenticated' to true if a certificate is present" do
+ cert = stub 'cert', :subject => [%w{CN host.domain.com}]
+ @request.stubs(:client_cert).returns cert
+ @handler.params(@request)[:authenticated].should be_true
end
- @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 set 'authenticated' to false if no certificate is present" do
+ @request.stubs(:client_cert).returns nil
+ @handler.params(@request)[:authenticated].should be_false
+ 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 pass the client's certificate name to model method if a certificate is present" do
+ cert = stub 'cert', :subject => [%w{CN host.domain.com}]
+ @request.stubs(:client_cert).returns cert
+ @handler.params(@request)[:node].should == "host.domain.com"
+ 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(400)
- @handler.process(@mock_request, @mock_response)
+ it "should not pass a node name to model method if no certificate is present" do
+ @request.stubs(:client_cert).returns nil
+ @handler.params(@request).should_not be_include(:node)
+ end
end
end
end