1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
|
require 'spec_helper'
require 'puppet/defaults'
require 'puppet/indirector'
require 'puppet/indirector/memory'
describe Puppet::Indirector::Terminus do
before :all do
class Puppet::AbstractConcept
extend Puppet::Indirector
indirects :abstract_concept
attr_accessor :name
def initialize(name = "name")
@name = name
end
end
class Puppet::AbstractConcept::Freedom < Puppet::Indirector::Code
end
end
after :all do
# Remove the class, unlinking it from the rest of the system.
Puppet.send(:remove_const, :AbstractConcept) if Puppet.const_defined?(:AbstractConcept)
end
let :terminus_class do Puppet::AbstractConcept::Freedom end
let :terminus do terminus_class.new end
let :indirection do Puppet::AbstractConcept.indirection end
let :model do Puppet::AbstractConcept end
it "should provide a method for setting terminus class documentation" do
expect(terminus_class).to respond_to(:desc)
end
it "should support a class-level name attribute" do
expect(terminus_class).to respond_to(:name)
end
it "should support a class-level indirection attribute" do
expect(terminus_class).to respond_to(:indirection)
end
it "should support a class-level terminus-type attribute" do
expect(terminus_class).to respond_to(:terminus_type)
end
it "should support a class-level model attribute" do
expect(terminus_class).to respond_to(:model)
end
it "should accept indirection instances as its indirection" do
# The test is that this shouldn't raise, and should preserve the object
# instance exactly, hence "equal", not just "==".
terminus_class.indirection = indirection
expect(terminus_class.indirection).to equal indirection
end
it "should look up indirection instances when only a name has been provided" do
terminus_class.indirection = :abstract_concept
expect(terminus_class.indirection).to equal indirection
end
it "should fail when provided a name that does not resolve to an indirection" do
expect {
terminus_class.indirection = :exploding_whales
}.to raise_error(ArgumentError, /Could not find indirection instance/)
# We should still have the default indirection.
expect(terminus_class.indirection).to equal indirection
end
describe "when a terminus instance" do
it "should return the class's name as its name" do
expect(terminus.name).to eq(:freedom)
end
it "should return the class's indirection as its indirection" do
expect(terminus.indirection).to equal indirection
end
it "should set the instances's type to the abstract terminus type's name" do
expect(terminus.terminus_type).to eq(:code)
end
it "should set the instances's model to the indirection's model" do
expect(terminus.model).to equal indirection.model
end
end
describe "when managing terminus classes" do
it "should provide a method for registering terminus classes" do
expect(Puppet::Indirector::Terminus).to respond_to(:register_terminus_class)
end
it "should provide a method for returning terminus classes by name and type" do
terminus = double('terminus_type', :name => :abstract, :indirection_name => :whatever)
Puppet::Indirector::Terminus.register_terminus_class(terminus)
expect(Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract)).to equal(terminus)
end
it "should set up autoloading for any terminus class types requested" do
expect(Puppet::Indirector::Terminus).to receive(:instance_load).with(:test2, "puppet/indirector/test2")
Puppet::Indirector::Terminus.terminus_class(:test2, :whatever)
end
it "should load terminus classes that are not found" do
# Set up instance loading; it would normally happen automatically
Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1"
expect(Puppet::Indirector::Terminus.instance_loader(:test1)).to receive(:load).with(:yay, anything)
Puppet::Indirector::Terminus.terminus_class(:test1, :yay)
end
it "should fail when no indirection can be found" do
expect(Puppet::Indirector::Indirection).to receive(:instance).with(:abstract_concept).and_return(nil)
expect {
class Puppet::AbstractConcept::Physics < Puppet::Indirector::Code
end
}.to raise_error(ArgumentError, /Could not find indirection instance/)
end
it "should register the terminus class with the terminus base class" do
expect(Puppet::Indirector::Terminus).to receive(:register_terminus_class) do |type|
expect(type.indirection_name).to eq(:abstract_concept)
expect(type.name).to eq(:intellect)
end
begin
class Puppet::AbstractConcept::Intellect < Puppet::Indirector::Code
end
ensure
Puppet::AbstractConcept.send(:remove_const, :Intellect) rescue nil
end
end
end
describe "when parsing class constants for indirection and terminus names" do
before :each do
allow(Puppet::Indirector::Terminus).to receive(:register_terminus_class)
end
let :subclass do
subclass = double('subclass')
allow(subclass).to receive(:to_s).and_return("TestInd::OneTwo")
allow(subclass).to receive(:mark_as_abstract_terminus)
subclass
end
it "should fail when anonymous classes are used" do
expect {
Puppet::Indirector::Terminus.inherited(Class.new)
}.to raise_error(Puppet::DevError, /Terminus subclasses must have associated constants/)
end
it "should use the last term in the constant for the terminus class name" do
expect(subclass).to receive(:name=).with(:one_two)
allow(subclass).to receive(:indirection=)
Puppet::Indirector::Terminus.inherited(subclass)
end
it "should convert the terminus name to a downcased symbol" do
expect(subclass).to receive(:name=).with(:one_two)
allow(subclass).to receive(:indirection=)
Puppet::Indirector::Terminus.inherited(subclass)
end
it "should use the second to last term in the constant for the indirection name" do
expect(subclass).to receive(:indirection=).with(:test_ind)
allow(subclass).to receive(:name=)
allow(subclass).to receive(:terminus_type=)
Puppet::Indirector::Memory.inherited(subclass)
end
it "should convert the indirection name to a downcased symbol" do
expect(subclass).to receive(:indirection=).with(:test_ind)
allow(subclass).to receive(:name=)
allow(subclass).to receive(:terminus_type=)
Puppet::Indirector::Memory.inherited(subclass)
end
it "should convert camel case to lower case with underscores as word separators" do
expect(subclass).to receive(:name=).with(:one_two)
allow(subclass).to receive(:indirection=)
Puppet::Indirector::Terminus.inherited(subclass)
end
end
describe "when creating terminus class types" do
before :each do
allow(Puppet::Indirector::Terminus).to receive(:register_terminus_class)
class Puppet::Indirector::Terminus::TestTerminusType < Puppet::Indirector::Terminus
end
end
after :each do
Puppet::Indirector::Terminus.send(:remove_const, :TestTerminusType)
end
let :subclass do
Puppet::Indirector::Terminus::TestTerminusType
end
it "should set the name of the abstract subclass to be its class constant" do
expect(subclass.name).to eq(:test_terminus_type)
end
it "should mark abstract terminus types as such" do
expect(subclass).to be_abstract_terminus
end
it "should not allow instances of abstract subclasses to be created" do
expect { subclass.new }.to raise_error(Puppet::DevError)
end
end
describe "when listing terminus classes" do
it "should list the terminus files available to load" do
allow_any_instance_of(Puppet::Util::Autoload).to receive(:files_to_load).and_return(["/foo/bar/baz", "/max/runs/marathon"])
expect(Puppet::Indirector::Terminus.terminus_classes('my_stuff')).to eq([:baz, :marathon])
end
end
describe "when validating a request" do
let :request do
Puppet::Indirector::Request.new(indirection.name, :find, "the_key", instance)
end
describe "`instance.name` does not match the key in the request" do
let(:instance) { model.new("wrong_key") }
it "raises an error " do
expect {
terminus.validate(request)
}.to raise_error(
Puppet::Indirector::ValidationError,
/Instance name .* does not match requested key/
)
end
end
describe "`instance` is not an instance of the model class" do
let(:instance) { double("instance") }
it "raises an error" do
expect {
terminus.validate(request)
}.to raise_error(
Puppet::Indirector::ValidationError,
/Invalid instance type/
)
end
end
describe "the instance key and class match the request key and model class" do
let(:instance) { model.new("the_key") }
it "passes" do
terminus.validate(request)
end
end
end
end
|