diff options
Diffstat (limited to 'spec/unit')
-rwxr-xr-x | spec/unit/indirector/indirection.rb | 12 | ||||
-rwxr-xr-x | spec/unit/indirector/node/memory.rb | 5 | ||||
-rwxr-xr-x | spec/unit/network/client/master.rb | 365 | ||||
-rw-r--r-- | spec/unit/network/http.rb | 29 | ||||
-rw-r--r-- | spec/unit/network/http/mongrel.rb | 124 | ||||
-rw-r--r-- | spec/unit/network/http/mongrel/rest.rb | 287 | ||||
-rw-r--r-- | spec/unit/network/http/mongrel/xmlrpc.rb | 0 | ||||
-rw-r--r-- | spec/unit/network/http/webrick.rb | 115 | ||||
-rw-r--r-- | spec/unit/network/http/webrick/rest.rb | 286 | ||||
-rw-r--r-- | spec/unit/network/http/webrick/xmlrpc.rb | 0 | ||||
-rw-r--r-- | spec/unit/network/rest_controller.rb | 65 | ||||
-rw-r--r-- | spec/unit/network/server.rb | 419 | ||||
-rwxr-xr-x | spec/unit/node/configuration.rb | 34 | ||||
-rwxr-xr-x | spec/unit/other/transbucket.rb | 12 | ||||
-rwxr-xr-x | spec/unit/other/transobject.rb | 147 | ||||
-rwxr-xr-x | spec/unit/parser/compile.rb | 100 |
16 files changed, 1676 insertions, 324 deletions
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/client/master.rb b/spec/unit/network/client/master.rb new file mode 100755 index 000000000..dca923994 --- /dev/null +++ b/spec/unit/network/client/master.rb @@ -0,0 +1,365 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-12. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/network/client/master' + +describe Puppet::Network::Client::Master, " when retrieving the configuration" do + before do + @master = mock 'master' + @client = Puppet::Network::Client.master.new( + :Master => @master + ) + @facts = {"one" => "two", "three" => "four"} + end + + it "should initialize the metadata store" do + @client.class.stubs(:facts).returns(@facts) + @client.expects(:dostorage) + @master.stubs(:getconfig).returns(nil) + @client.getconfig + end + + it "should collect facts to use for configuration retrieval" do + @client.stubs(:dostorage) + @client.class.expects(:facts).returns(@facts) + @master.stubs(:getconfig).returns(nil) + @client.getconfig + end + + it "should fail if no facts could be collected" do + @client.stubs(:dostorage) + @client.class.expects(:facts).returns({}) + @master.stubs(:getconfig).returns(nil) + proc { @client.getconfig }.should raise_error(Puppet::Network::ClientError) + end + + it "should use the cached configuration if it is up to date" do + file = "/path/to/cachefile" + @client.stubs(:cachefile).returns(file) + FileTest.expects(:exist?).with(file).returns(true) + @client.expects(:fresh?).with(@facts).returns true + @client.class.stubs(:facts).returns(@facts) + @client.expects(:use_cached_config).returns(true) + Puppet.stubs(:info) + + @client.getconfig + end + + it "should log that the configuration does not need a recompile" do + file = "/path/to/cachefile" + @client.stubs(:cachefile).returns(file) + FileTest.stubs(:exist?).with(file).returns(true) + @client.stubs(:fresh?).with(@facts).returns true + @client.stubs(:use_cached_config).returns(true) + @client.class.stubs(:facts).returns(@facts) + Puppet.expects(:info).with { |m| m.include?("up to date") } + + @client.getconfig + end + + it "should retrieve plugins if :pluginsync is enabled" do + file = "/path/to/cachefile" + @client.stubs(:cachefile).returns(file) + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + Puppet.settings.expects(:value).with(:pluginsync).returns(true) + @client.expects(:getplugins) + @client.stubs(:get_actual_config).returns(nil) + FileTest.stubs(:exist?).with(file).returns(true) + @client.stubs(:fresh?).with(@facts).returns true + @client.stubs(:use_cached_config).returns(true) + @client.class.stubs(:facts).returns(@facts) + @client.getconfig + end + + it "should use the cached configuration if no configuration could be retrieved" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).raises(ArgumentError.new("whev")) + @client.expects(:use_cached_config).with(true) + @client.getconfig + end + + it "should load the retrieved configuration using YAML" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).returns("myconfig") + + config = mock 'config' + YAML.expects(:load).with("myconfig").returns(config) + + @client.stubs(:setclasses) + + config.stubs(:classes) + config.stubs(:to_configuration).returns(config) + config.stubs(:host_config=) + config.stubs(:from_cache).returns(true) + + @client.getconfig + end + + it "should use the cached configuration if the retrieved configuration cannot be converted from YAML" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).returns("myconfig") + + YAML.expects(:load).with("myconfig").raises(ArgumentError) + + @client.expects(:use_cached_config).with(true) + + @client.getconfig + end + + it "should set the classes.txt file with the classes listed in the retrieved configuration" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).returns("myconfig") + + config = mock 'config' + YAML.expects(:load).with("myconfig").returns(config) + + config.expects(:classes).returns(:myclasses) + @client.expects(:setclasses).with(:myclasses) + + config.stubs(:to_configuration).returns(config) + config.stubs(:host_config=) + config.stubs(:from_cache).returns(true) + + @client.getconfig + end + + it "should convert the retrieved configuration to a RAL configuration" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).returns("myconfig") + + yamlconfig = mock 'yaml config' + YAML.stubs(:load).returns(yamlconfig) + + @client.stubs(:setclasses) + + config = mock 'config' + + yamlconfig.stubs(:classes) + yamlconfig.expects(:to_configuration).returns(config) + + config.stubs(:host_config=) + config.stubs(:from_cache).returns(true) + + @client.getconfig + end + + it "should use the cached configuration if the retrieved configuration cannot be converted to a RAL configuration" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).returns("myconfig") + + yamlconfig = mock 'yaml config' + YAML.stubs(:load).returns(yamlconfig) + + @client.stubs(:setclasses) + + config = mock 'config' + + yamlconfig.stubs(:classes) + yamlconfig.expects(:to_configuration).raises(ArgumentError) + + @client.expects(:use_cached_config).with(true) + + @client.getconfig + end + + it "should clear the failed configuration if using the cached configuration after failing to instantiate the retrieved configuration" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).returns("myconfig") + + yamlconfig = mock 'yaml config' + YAML.stubs(:load).returns(yamlconfig) + + @client.stubs(:setclasses) + + config = mock 'config' + + yamlconfig.stubs(:classes) + yamlconfig.stubs(:to_configuration).raises(ArgumentError) + + @client.stubs(:use_cached_config).with(true) + + @client.expects(:clear) + + @client.getconfig + end + + it "should cache the retrieved yaml configuration if it is not from the cache and is valid" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).returns("myconfig") + + yamlconfig = mock 'yaml config' + YAML.stubs(:load).returns(yamlconfig) + + @client.stubs(:setclasses) + + config = mock 'config' + + yamlconfig.stubs(:classes) + yamlconfig.expects(:to_configuration).returns(config) + + config.stubs(:host_config=) + + config.expects(:from_cache).returns(false) + + @client.expects(:cache).with("myconfig") + + @client.getconfig + end + + it "should mark the configuration as a host configuration" do + @client.stubs(:dostorage) + @client.class.stubs(:facts).returns(@facts) + @master.stubs(:getconfig).returns("myconfig") + + yamlconfig = mock 'yaml config' + YAML.stubs(:load).returns(yamlconfig) + + @client.stubs(:setclasses) + + config = mock 'config' + + yamlconfig.stubs(:classes) + yamlconfig.expects(:to_configuration).returns(config) + + config.stubs(:from_cache).returns(true) + + config.expects(:host_config=).with(true) + + @client.getconfig + end +end + +describe Puppet::Network::Client::Master, " when using the cached configuration" do + before do + @master = mock 'master' + @client = Puppet::Network::Client.master.new( + :Master => @master + ) + @facts = {"one" => "two", "three" => "four"} + end + + it "should return do nothing and true if there is already an in-memory configuration" do + @client.configuration = :whatever + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config.should be_true + end + end + + it "should return do nothing and false if it has been told there is a failure and :nocacheonfailure is enabled" do + Puppet.settings.expects(:value).with(:usecacheonfailure).returns(false) + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config(true).should be_false + end + end + + it "should return false if no cached configuration can be found" do + @client.expects(:retrievecache).returns(nil) + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config().should be_false + end + end + + it "should return false if the cached configuration cannot be instantiated" do + YAML.expects(:load).raises(ArgumentError) + @client.expects(:retrievecache).returns("whatever") + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config().should be_false + end + end + + it "should warn if the cached configuration cannot be instantiated" do + YAML.stubs(:load).raises(ArgumentError) + @client.stubs(:retrievecache).returns("whatever") + Puppet.expects(:warning).with { |m| m.include?("Could not load cache") } + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config().should be_false + end + end + + it "should clear the client if the cached configuration cannot be instantiated" do + YAML.stubs(:load).raises(ArgumentError) + @client.stubs(:retrievecache).returns("whatever") + @client.expects(:clear) + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config().should be_false + end + end + + it "should return true if the cached configuration can be instantiated" do + config = mock 'config' + YAML.stubs(:load).returns(config) + + ral_config = mock 'ral config' + ral_config.stubs(:from_cache=) + ral_config.stubs(:host_config=) + config.expects(:to_configuration).returns(ral_config) + + @client.stubs(:retrievecache).returns("whatever") + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config().should be_true + end + end + + it "should set the configuration instance variable if the cached configuration can be instantiated" do + config = mock 'config' + YAML.stubs(:load).returns(config) + + ral_config = mock 'ral config' + ral_config.stubs(:from_cache=) + ral_config.stubs(:host_config=) + config.expects(:to_configuration).returns(ral_config) + + @client.stubs(:retrievecache).returns("whatever") + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config() + end + + @client.configuration.should equal(ral_config) + end + + it "should mark the configuration as a host_config if valid" do + config = mock 'config' + YAML.stubs(:load).returns(config) + + ral_config = mock 'ral config' + ral_config.stubs(:from_cache=) + ral_config.expects(:host_config=).with(true) + config.expects(:to_configuration).returns(ral_config) + + @client.stubs(:retrievecache).returns("whatever") + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config() + end + + @client.configuration.should equal(ral_config) + end + + it "should mark the configuration as from the cache if valid" do + config = mock 'config' + YAML.stubs(:load).returns(config) + + ral_config = mock 'ral config' + ral_config.expects(:from_cache=).with(true) + ral_config.stubs(:host_config=) + config.expects(:to_configuration).returns(ral_config) + + @client.stubs(:retrievecache).returns("whatever") + Puppet::Network::Client::Master.publicize_methods :use_cached_config do + @client.use_cached_config() + end + + @client.configuration.should equal(ral_config) + end +end 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 diff --git a/spec/unit/node/configuration.rb b/spec/unit/node/configuration.rb index e9dc6b85d..5780d4fbb 100755 --- a/spec/unit/node/configuration.rb +++ b/spec/unit/node/configuration.rb @@ -301,9 +301,9 @@ end describe Puppet::Node::Configuration, " when functioning as a resource container" do before do @config = Puppet::Node::Configuration.new("host") - @one = stub 'resource1', :ref => "Me[you]", :configuration= => nil - @two = stub 'resource2', :ref => "Me[him]", :configuration= => nil - @dupe = stub 'resource3', :ref => "Me[you]", :configuration= => nil + @one = stub 'resource1', :ref => "Me[one]", :configuration= => nil + @two = stub 'resource2', :ref => "Me[two]", :configuration= => nil + @dupe = stub 'resource3', :ref => "Me[one]", :configuration= => nil end it "should provide a method to add one or more resources" do @@ -376,7 +376,7 @@ describe Puppet::Node::Configuration, " when functioning as a resource container it "should be able to find resources by reference or by type/title tuple" do @config.add_resource @one - @config.resource("me", "you").should equal(@one) + @config.resource("me", "one").should equal(@one) end it "should have a mechanism for removing resources" do @@ -386,6 +386,32 @@ describe Puppet::Node::Configuration, " when functioning as a resource container @config.resource(@one.ref).should be_nil @config.vertex?(@one).should be_false end + + it "should have a method for creating aliases for resources" do + @config.add_resource @one + @config.alias(@one, "other") + @config.resource("me", "other").should equal(@one) + end + + # This test is the same as the previous, but the behaviour should be explicit. + it "should alias using the class name from the resource reference, not the resource class name" do + @config.add_resource @one + @config.alias(@one, "other") + @config.resource("me", "other").should equal(@one) + end + + it "should fail to add an alias if the aliased name already exists" do + @config.add_resource @one + proc { @config.alias @two, "one" }.should raise_error(ArgumentError) + end + + it "should remove resource aliases when the target resource is removed" do + @config.add_resource @one + @config.alias(@one, "other") + @one.expects :remove + @config.remove_resource(@one) + @config.resource("me", "other").should be_nil + end end module ApplyingConfigurations diff --git a/spec/unit/other/transbucket.rb b/spec/unit/other/transbucket.rb index 8cb9abaa4..0da808460 100755 --- a/spec/unit/other/transbucket.rb +++ b/spec/unit/other/transbucket.rb @@ -102,6 +102,18 @@ describe Puppet::TransBucket, " when generating a configuration" do @top.to_configuration end + it "should set each TransObject's configuration before converting to a RAL resource" do + @middleobj.expects(:configuration=).with { |c| c.is_a?(Puppet::Node::Configuration) } + @top.to_configuration + end + + it "should set each TransBucket's configuration before converting to a RAL resource" do + # each bucket is seen twice in the loop, so we have to handle the case where the config + # is set twice + @bottom.expects(:configuration=).with { |c| c.is_a?(Puppet::Node::Configuration) }.at_least_once + @top.to_configuration + end + after do Puppet::Type.allclear end diff --git a/spec/unit/other/transobject.rb b/spec/unit/other/transobject.rb index 144940b7e..eaca855db 100755 --- a/spec/unit/other/transobject.rb +++ b/spec/unit/other/transobject.rb @@ -2,114 +2,73 @@ require File.dirname(__FILE__) + '/../../spec_helper' -describe Puppet::TransObject, " when building its search path" do -end - -describe Puppet::TransObject, " when building its search path" do -end -#!/usr/bin/env ruby - -$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ - -require 'puppet' require 'puppet/transportable' -require 'puppettest' -require 'puppettest/parsertesting' -require 'yaml' -class TestTransportable < Test::Unit::TestCase - include PuppetTest::ParserTesting - - def test_yamldumpobject - obj = mk_transobject - obj.to_yaml_properties - str = nil - assert_nothing_raised { - str = YAML.dump(obj) - } - - newobj = nil - assert_nothing_raised { - newobj = YAML.load(str) - } - - assert(newobj.name, "Object has no name") - assert(newobj.type, "Object has no type") +describe Puppet::TransObject, " when serializing" do + before do + @resource = Puppet::TransObject.new("/my/file", "file") + @resource["one"] = "test" + @resource["two"] = "other" end - def test_yamldumpbucket - objects = %w{/etc/passwd /etc /tmp /var /dev}.collect { |d| - mk_transobject(d) - } - bucket = mk_transbucket(*objects) - str = nil - assert_nothing_raised { - str = YAML.dump(bucket) - } - - newobj = nil - assert_nothing_raised { - newobj = YAML.load(str) - } - - assert(newobj.name, "Bucket has no name") - assert(newobj.type, "Bucket has no type") + it "should be able to be dumped to yaml" do + proc { YAML.dump(@resource) }.should_not raise_error end - # Verify that we correctly strip out collectable objects, since they should - # not be sent to the client. - def test_collectstrip - top = mk_transtree do |object, depth, width| - if width % 2 == 1 - object.collectable = true - end - end + it "should produce an equivalent yaml object" do + text = YAML.dump(@resource) - assert(top.flatten.find_all { |o| o.collectable }.length > 0, - "Could not find any collectable objects") + newresource = YAML.load(text) + newresource.name.should == "/my/file" + newresource.type.should == "file" + %w{one two}.each do |param| + newresource[param].should == @resource[param] + end + end +end - # Now strip out the collectable objects - top.collectstrip! +describe Puppet::TransObject, " when converting to a RAL resource" do + before do + @resource = Puppet::TransObject.new("/my/file", "file") + @resource["one"] = "test" + @resource["two"] = "other" + end - # And make sure they're actually gone - assert_equal(0, top.flatten.find_all { |o| o.collectable }.length, - "Still found collectable objects") + it "should use the resource type's :create method to create the resource" do + type = mock 'resource type' + type.expects(:create).with(@resource).returns(:myresource) + Puppet::Type.expects(:type).with("file").returns(type) + @resource.to_type.should == :myresource end - # Make sure our 'delve' command is working - def test_delve - top = mk_transtree do |object, depth, width| - if width % 2 == 1 - object.collectable = true - end - end + it "should convert to a component instance if the resource type cannot be found" do + Puppet::Type.expects(:type).with("file").returns(nil) + @resource.expects(:to_component).returns(:mycomponent) + @resource.to_type.should == :mycomponent + end +end - objects = [] - buckets = [] - collectable = [] +describe Puppet::TransObject, " when converting to a RAL component instance" do + before do + @resource = Puppet::TransObject.new("/my/file", "one::two") + @resource["one"] = "test" + @resource["noop"] = "other" + end - count = 0 - assert_nothing_raised { - top.delve do |object| - count += 1 - if object.is_a? Puppet::TransBucket - buckets << object - else - objects << object - if object.collectable - collectable << object - end - end - end - } + it "should use a new TransObject whose name is a resource reference of the type and title of the original TransObject" do + Puppet::Type::Component.expects(:create).with { |resource| resource.type == :component and resource.name == "One::Two[/my/file]" }.returns(:yay) + @resource.to_component.should == :yay + end - top.flatten.each do |obj| - assert(objects.include?(obj), "Missing obj %s[%s]" % [obj.type, obj.name]) - end + it "should pass the resource parameters on to the newly created TransObject" do + Puppet::Type::Component.expects(:create).with { |resource| resource["noop"] == "other" }.returns(:yay) + @resource.to_component.should == :yay + end - assert_equal(collectable.length, - top.flatten.find_all { |o| o.collectable }.length, - "Found incorrect number of collectable objects") + # LAK:FIXME This really isn't the design we want going forward, but it's + # good enough for now. + it "should not pass resource paramaters that are not metaparams" do + Puppet::Type::Component.expects(:create).with { |resource| resource["one"].nil? }.returns(:yay) + @resource.to_component.should == :yay end end - diff --git a/spec/unit/parser/compile.rb b/spec/unit/parser/compile.rb index 93c440417..5f239636b 100755 --- a/spec/unit/parser/compile.rb +++ b/spec/unit/parser/compile.rb @@ -141,3 +141,103 @@ describe Puppet::Parser::Compile, " when evaluating found classes" do @compile.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass} end end + +describe Puppet::Parser::Compile, " when evaluating AST nodes with no AST nodes present" do + before do + @node = stub 'node', :name => "foo" + @parser = stub 'parser', :version => "1.0", :nodes => {} + @compile = Puppet::Parser::Compile.new(@node, @parser) + end + + it "should do nothing" do + @compile.expects(:ast_nodes?).returns(false) + @compile.parser.expects(:nodes).never + Puppet::Parser::Resource.expects(:new).never + + @compile.send(:evaluate_ast_node) + end +end + +describe Puppet::Parser::Compile, " when evaluating AST nodes with AST nodes present" do + before do + @node = stub 'node', :name => "foo" + @parser = stub 'parser', :version => "1.0", :nodes => {} + @compile = Puppet::Parser::Compile.new(@node, @parser) + + @nodes = mock 'node_hash' + @compile.stubs(:ast_nodes?).returns(true) + @compile.parser.stubs(:nodes).returns(@nodes) + + # Set some names for our test + @node.stubs(:names).returns(%w{a b c}) + @nodes.stubs(:[]).with("a").returns(nil) + @nodes.stubs(:[]).with("b").returns(nil) + @nodes.stubs(:[]).with("c").returns(nil) + + # It should check this last, of course. + @nodes.stubs(:[]).with("default").returns(nil) + end + + it "should fail if the named node cannot be found" do + proc { @compile.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) + end + + it "should create a resource for the first node class matching the node name" do + node_class = stub 'node', :classname => "c" + @nodes.stubs(:[]).with("c").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil + Puppet::Parser::Resource.expects(:new).with { |args| args[:title] == "c" and args[:type] == "node" }.returns(node_resource) + + @compile.send(:evaluate_ast_node) + end + + it "should match the default node if no matching node can be found" do + node_class = stub 'node', :classname => "default" + @nodes.stubs(:[]).with("default").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil + Puppet::Parser::Resource.expects(:new).with { |args| args[:title] == "default" and args[:type] == "node" }.returns(node_resource) + + @compile.send(:evaluate_ast_node) + end + + it "should tag the configuration with the found node name" do + node_class = stub 'node', :classname => "c" + @nodes.stubs(:[]).with("c").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil + Puppet::Parser::Resource.stubs(:new).returns(node_resource) + + @compile.configuration.expects(:tag).with("c") + @compile.send(:evaluate_ast_node) + end + + it "should evaluate the node resource immediately rather than using lazy evaluation" do + node_class = stub 'node', :classname => "c" + @nodes.stubs(:[]).with("c").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[c]" + Puppet::Parser::Resource.stubs(:new).returns(node_resource) + + node_resource.expects(:evaluate) + + @compile.send(:evaluate_ast_node) + end + + it "should set the node's scope as the top scope" do + node_class = stub 'node', :classname => "c" + @nodes.stubs(:[]).with("c").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[c]" + Puppet::Parser::Resource.stubs(:new).returns(node_resource) + + # The #evaluate method normally does this. + @compile.class_set(node_class.classname, :my_node_scope) + node_resource.stubs(:evaluate) + + @compile.send(:evaluate_ast_node) + + @compile.topscope.should == :my_node_scope + end +end |