summaryrefslogtreecommitdiffstats
path: root/spec/unit
diff options
context:
space:
mode:
Diffstat (limited to 'spec/unit')
-rwxr-xr-xspec/unit/indirector/indirection.rb12
-rwxr-xr-xspec/unit/indirector/node/memory.rb5
-rwxr-xr-xspec/unit/network/client/master.rb365
-rw-r--r--spec/unit/network/http.rb29
-rw-r--r--spec/unit/network/http/mongrel.rb124
-rw-r--r--spec/unit/network/http/mongrel/rest.rb287
-rw-r--r--spec/unit/network/http/mongrel/xmlrpc.rb0
-rw-r--r--spec/unit/network/http/webrick.rb115
-rw-r--r--spec/unit/network/http/webrick/rest.rb286
-rw-r--r--spec/unit/network/http/webrick/xmlrpc.rb0
-rw-r--r--spec/unit/network/rest_controller.rb65
-rw-r--r--spec/unit/network/server.rb419
-rwxr-xr-xspec/unit/node/configuration.rb34
-rwxr-xr-xspec/unit/other/transbucket.rb12
-rwxr-xr-xspec/unit/other/transobject.rb147
-rwxr-xr-xspec/unit/parser/compile.rb100
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