diff options
| author | Luke Kanies <luke@madstop.com> | 2008-07-26 22:46:26 -0500 |
|---|---|---|
| committer | Luke Kanies <luke@madstop.com> | 2008-07-29 00:51:21 -0500 |
| commit | e3350caeec3a662b0b92ec2dee372563a493fa11 (patch) | |
| tree | 26630fdca2c343d91b7e6b8fe8fa8e67b4b68a47 /spec/unit/network/http/mongrel | |
| parent | b3914c367f470dd37a846d01207228d6ded40c6d (diff) | |
| download | puppet-e3350caeec3a662b0b92ec2dee372563a493fa11.tar.gz puppet-e3350caeec3a662b0b92ec2dee372563a493fa11.tar.xz puppet-e3350caeec3a662b0b92ec2dee372563a493fa11.zip | |
Drastically simplifying the REST implementation tests.
Nearly all of the tests are now written just once, in
the Handler module. The Mongrel and Webrick tests just
validate that they provide the interface and how they do
so.
Signed-off-by: Luke Kanies <luke@madstop.com>
Diffstat (limited to 'spec/unit/network/http/mongrel')
| -rwxr-xr-x | spec/unit/network/http/mongrel/rest.rb | 452 |
1 files changed, 115 insertions, 337 deletions
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 |
