3
require File.dirname(__FILE__) + '/../../spec_helper'
5
describe Puppet::Resource::Catalog, "when compiling" do
6
it "should be an Expirer" do
7
Puppet::Resource::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer)
10
it "should always be expired if it's not applying" do
11
@catalog = Puppet::Resource::Catalog.new("host")
12
@catalog.expects(:applying?).returns false
13
@catalog.should be_dependent_data_expired(Time.now)
16
it "should not be expired if it's applying and the timestamp is late enough" do
17
@catalog = Puppet::Resource::Catalog.new("host")
19
@catalog.expects(:applying?).returns true
20
@catalog.should_not be_dependent_data_expired(Time.now)
23
it "should be able to write its list of classes to the class file" do
24
@catalog = Puppet::Resource::Catalog.new("host")
26
@catalog.add_class "foo", "bar"
28
Puppet.settings.expects(:value).with(:classfile).returns "/class/file"
30
fh = mock 'filehandle'
31
File.expects(:open).with("/class/file", "w").yields fh
33
fh.expects(:puts).with "foo\nbar"
35
@catalog.write_class_file
38
it "should have a client_version attribute" do
39
@catalog = Puppet::Resource::Catalog.new("host")
40
@catalog.client_version = 5
41
@catalog.client_version.should == 5
44
it "should have a server_version attribute" do
45
@catalog = Puppet::Resource::Catalog.new("host")
46
@catalog.server_version = 5
47
@catalog.server_version.should == 5
50
describe "when compiling" do
51
it "should accept tags" do
52
config = Puppet::Resource::Catalog.new("mynode")
54
config.tags.should == %w{one}
57
it "should accept multiple tags at once" do
58
config = Puppet::Resource::Catalog.new("mynode")
59
config.tag("one", "two")
60
config.tags.should == %w{one two}
63
it "should convert all tags to strings" do
64
config = Puppet::Resource::Catalog.new("mynode")
65
config.tag("one", :two)
66
config.tags.should == %w{one two}
69
it "should tag with both the qualified name and the split name" do
70
config = Puppet::Resource::Catalog.new("mynode")
71
config.tag("one::two")
72
config.tags.include?("one").should be_true
73
config.tags.include?("one::two").should be_true
76
it "should accept classes" do
77
config = Puppet::Resource::Catalog.new("mynode")
78
config.add_class("one")
79
config.classes.should == %w{one}
80
config.add_class("two", "three")
81
config.classes.should == %w{one two three}
84
it "should tag itself with passed class names" do
85
config = Puppet::Resource::Catalog.new("mynode")
86
config.add_class("one")
87
config.tags.should == %w{one}
91
describe "when extracting" do
92
it "should return extraction result as the method result" do
93
config = Puppet::Resource::Catalog.new("mynode")
94
config.expects(:extraction_format).returns(:whatever)
95
config.expects(:extract_to_whatever).returns(:result)
96
config.extract.should == :result
100
describe "when extracting transobjects" do
103
@parser = Puppet::Parser::Parser.new :Code => ""
104
@node = Puppet::Node.new("mynode")
105
@compiler = Puppet::Parser::Compiler.new(@node, @parser)
107
# XXX This is ridiculous.
108
@compiler.send(:evaluate_main)
109
@scope = @compiler.topscope
112
def mkresource(type, name)
113
Puppet::Parser::Resource.new(:type => type, :title => name, :source => @source, :scope => @scope)
116
it "should always create a TransBucket for the 'main' class" do
117
config = Puppet::Resource::Catalog.new("mynode")
120
@source = mock 'source'
122
main = mkresource("class", :main)
123
config.add_vertex(main)
125
bucket = stub 'bucket', :file= => nil, :line= => nil, :classes= => nil
126
bucket.expects(:type=).with("Class")
127
bucket.expects(:name=).with(:main)
128
main.stubs(:builtin?).returns(false)
130
Puppet::TransBucket.expects(:new).returns bucket
132
config.extract_to_transportable.should equal(bucket)
135
# Now try it with a more complicated graph -- a three tier graph, each tier
136
it "should transform arbitrarily deep graphs into isomorphic trees" do
137
config = Puppet::Resource::Catalog.new("mynode")
140
@scope.stubs(:tags).returns([])
141
@source = mock 'source'
144
top = mkresource "class", :main
146
topbucket.expects(:classes=).with([])
147
top.expects(:to_trans).returns(topbucket)
148
topres = mkresource "file", "/top"
149
topres.expects(:to_trans).returns(:topres)
150
config.add_edge top, topres
152
middle = mkresource "class", "middle"
153
middle.expects(:to_trans).returns([])
154
config.add_edge top, middle
155
midres = mkresource "file", "/mid"
156
midres.expects(:to_trans).returns(:midres)
157
config.add_edge middle, midres
159
bottom = mkresource "class", "bottom"
160
bottom.expects(:to_trans).returns([])
161
config.add_edge middle, bottom
162
botres = mkresource "file", "/bot"
163
botres.expects(:to_trans).returns(:botres)
164
config.add_edge bottom, botres
166
toparray = config.extract_to_transportable
168
# This is annoying; it should look like:
169
# [[[:botres], :midres], :topres]
170
# but we can't guarantee sort order.
171
toparray.include?(:topres).should be_true
173
midarray = toparray.find { |t| t.is_a?(Array) }
174
midarray.include?(:midres).should be_true
175
botarray = midarray.find { |t| t.is_a?(Array) }
176
botarray.include?(:botres).should be_true
180
describe " when converting to a Puppet::Resource catalog" do
182
@original = Puppet::Resource::Catalog.new("mynode")
183
@original.tag(*%w{one two three})
184
@original.add_class *%w{four five six}
186
@top = Puppet::TransObject.new 'top', "class"
187
@topobject = Puppet::TransObject.new '/topobject', "file"
188
@middle = Puppet::TransObject.new 'middle', "class"
189
@middleobject = Puppet::TransObject.new '/middleobject', "file"
190
@bottom = Puppet::TransObject.new 'bottom', "class"
191
@bottomobject = Puppet::TransObject.new '/bottomobject', "file"
193
@resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject]
195
@original.add_resource(*@resources)
197
@original.add_edge(@top, @topobject)
198
@original.add_edge(@top, @middle)
199
@original.add_edge(@middle, @middleobject)
200
@original.add_edge(@middle, @bottom)
201
@original.add_edge(@bottom, @bottomobject)
203
@catalog = @original.to_resource
206
it "should copy over the version" do
207
@original.version = "foo"
208
@original.to_resource.version.should == "foo"
211
it "should add all resources as Puppet::Resource instances" do
212
@resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Resource) }
215
it "should copy the tag list to the new catalog" do
216
@catalog.tags.sort.should == @original.tags.sort
219
it "should copy the class list to the new catalog" do
220
@catalog.classes.should == @original.classes
223
it "should duplicate the original edges" do
224
@original.edges.each do |edge|
225
@catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true
229
it "should set itself as the catalog for each converted resource" do
230
@catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) }
234
describe "when converting to a RAL catalog" do
236
@original = Puppet::Resource::Catalog.new("mynode")
237
@original.tag(*%w{one two three})
238
@original.add_class *%w{four five six}
240
@top = Puppet::Resource.new :class, 'top'
241
@topobject = Puppet::Resource.new :file, '/topobject'
242
@middle = Puppet::Resource.new :class, 'middle'
243
@middleobject = Puppet::Resource.new :file, '/middleobject'
244
@bottom = Puppet::Resource.new :class, 'bottom'
245
@bottomobject = Puppet::Resource.new :file, '/bottomobject'
247
@resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject]
249
@original.add_resource(*@resources)
251
@original.add_edge(@top, @topobject)
252
@original.add_edge(@top, @middle)
253
@original.add_edge(@middle, @middleobject)
254
@original.add_edge(@middle, @bottom)
255
@original.add_edge(@bottom, @bottomobject)
257
@catalog = @original.to_ral
260
it "should add all resources as RAL instances" do
261
@resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) }
264
it "should copy the tag list to the new catalog" do
265
@catalog.tags.sort.should == @original.tags.sort
268
it "should copy the class list to the new catalog" do
269
@catalog.classes.should == @original.classes
272
it "should duplicate the original edges" do
273
@original.edges.each do |edge|
274
@catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true
278
it "should set itself as the catalog for each converted resource" do
279
@catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) }
283
it "should not lose track of resources whose names vary" do
284
changer = Puppet::TransObject.new 'changer', 'test'
286
config = Puppet::Resource::Catalog.new('test')
287
config.add_resource(changer)
288
config.add_resource(@top)
290
config.add_edge(@top, changer)
292
resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil
294
#changer is going to get duplicated as part of a fix for aliases 1094
295
changer.expects(:dup).returns(changer)
296
changer.expects(:to_ral).returns(resource)
300
proc { @catalog = config.to_ral }.should_not raise_error
301
@catalog.resource("Test[changer2]").should equal(resource)
305
# Remove all resource instances.
310
describe "when filtering" do
312
@original = Puppet::Resource::Catalog.new("mynode")
313
@original.tag(*%w{one two three})
314
@original.add_class *%w{four five six}
316
@r1 = stub_everything 'r1', :ref => "File[/a]"
317
@r1.stubs(:respond_to?).with(:ref).returns(true)
318
@r1.stubs(:dup).returns(@r1)
319
@r1.stubs(:is_a?).returns(Puppet::Resource).returns(true)
321
@r2 = stub_everything 'r2', :ref => "File[/b]"
322
@r2.stubs(:respond_to?).with(:ref).returns(true)
323
@r2.stubs(:dup).returns(@r2)
324
@r2.stubs(:is_a?).returns(Puppet::Resource).returns(true)
326
@resources = [@r1,@r2]
328
@original.add_resource(@r1,@r2)
331
it "should transform the catalog to a resource catalog" do
332
@original.expects(:to_catalog).with { |h,b| h == :to_resource }
337
it "should scan each catalog resource in turn and apply filtering block" do
338
@resources.each { |r| r.expects(:test?) }
339
@original.filter do |r|
344
it "should filter out resources which produce true when the filter block is evaluated" do
345
@original.filter do |r|
347
end.resource("File[/a]").should be_nil
350
it "should not consider edges against resources that were filtered out" do
351
@original.add_edge(@r1,@r2)
352
@original.filter do |r|
354
end.edge(@r1,@r2).should be_empty
358
describe "when functioning as a resource container" do
360
@catalog = Puppet::Resource::Catalog.new("host")
361
@one = Puppet::Type.type(:notify).new :name => "one"
362
@two = Puppet::Type.type(:notify).new :name => "two"
363
@dupe = Puppet::Type.type(:notify).new :name => "one"
366
it "should provide a method to add one or more resources" do
367
@catalog.add_resource @one, @two
368
@catalog.resource(@one.ref).should equal(@one)
369
@catalog.resource(@two.ref).should equal(@two)
372
it "should add resources to the relationship graph if it exists" do
373
relgraph = @catalog.relationship_graph
374
@catalog.add_resource @one
375
relgraph.should be_vertex(@one)
378
it "should yield added resources if a block is provided" do
380
@catalog.add_resource(@one, @two) { |r| yielded << r }
381
yielded.length.should == 2
384
it "should set itself as the resource's catalog if it is not a relationship graph" do
385
@one.expects(:catalog=).with(@catalog)
386
@catalog.add_resource @one
389
it "should make all vertices available by resource reference" do
390
@catalog.add_resource(@one)
391
@catalog.resource(@one.ref).should equal(@one)
392
@catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one)
395
it "should canonize how resources are referred to during retrieval when both type and title are provided" do
396
@catalog.add_resource(@one)
398
@catalog.resource("notify", "one").should equal(@one)
401
it "should canonize how resources are referred to during retrieval when just the title is provided" do
402
@catalog.add_resource(@one)
404
@catalog.resource("notify[one]", nil).should equal(@one)
407
it "should not allow two resources with the same resource reference" do
408
@catalog.add_resource(@one)
410
proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError)
413
it "should not store objects that do not respond to :ref" do
414
proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError)
417
it "should remove all resources when asked" do
418
@catalog.add_resource @one
419
@catalog.add_resource @two
425
it "should support a mechanism for finishing resources" do
428
@catalog.add_resource @one
429
@catalog.add_resource @two
434
it "should make default resources when finalizing" do
435
@catalog.expects(:make_default_resources)
439
it "should add default resources to the catalog upon creation" do
440
@catalog.make_default_resources
441
@catalog.resource(:schedule, "daily").should_not be_nil
444
it "should optionally support an initialization block and should finalize after such blocks" do
447
config = Puppet::Resource::Catalog.new("host") do |conf|
448
conf.add_resource @one
449
conf.add_resource @two
453
it "should inform the resource that it is the resource's catalog" do
454
@one.expects(:catalog=).with(@catalog)
455
@catalog.add_resource @one
458
it "should be able to find resources by reference" do
459
@catalog.add_resource @one
460
@catalog.resource(@one.ref).should equal(@one)
463
it "should be able to find resources by reference or by type/title tuple" do
464
@catalog.add_resource @one
465
@catalog.resource("notify", "one").should equal(@one)
468
it "should have a mechanism for removing resources" do
469
@catalog.add_resource @one
471
@catalog.remove_resource(@one)
472
@catalog.resource(@one.ref).should be_nil
473
@catalog.vertex?(@one).should be_false
476
it "should have a method for creating aliases for resources" do
477
@catalog.add_resource @one
478
@catalog.alias(@one, "other")
479
@catalog.resource("notify", "other").should equal(@one)
482
it "should ignore conflicting aliases that point to the aliased resource" do
483
@catalog.alias(@one, "other")
484
lambda { @catalog.alias(@one, "other") }.should_not raise_error
487
it "should create aliases for resources isomorphic resources whose names do not match their titles" do
488
resource = Puppet::Type::File.new(:title => "testing", :path => "/something")
490
@catalog.add_resource(resource)
492
@catalog.resource(:file, "/something").should equal(resource)
495
it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do
496
resource = Puppet::Type.type(:exec).new(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin})
498
@catalog.add_resource(resource)
500
# Yay, I've already got a 'should' method
501
@catalog.resource(:exec, "echo").object_id.should == nil.object_id
504
# This test is the same as the previous, but the behaviour should be explicit.
505
it "should alias using the class name from the resource reference, not the resource class name" do
506
@catalog.add_resource @one
507
@catalog.alias(@one, "other")
508
@catalog.resource("notify", "other").should equal(@one)
511
it "should ignore conflicting aliases that point to the aliased resource" do
512
@catalog.alias(@one, "other")
513
lambda { @catalog.alias(@one, "other") }.should_not raise_error
516
it "should fail to add an alias if the aliased name already exists" do
517
@catalog.add_resource @one
518
proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError)
521
it "should not fail when a resource has duplicate aliases created" do
522
@catalog.add_resource @one
523
proc { @catalog.alias @one, "one" }.should_not raise_error
526
it "should not create aliases that point back to the resource" do
527
@catalog.alias(@one, "one")
528
@catalog.resource(:notify, "one").should be_nil
531
it "should be able to look resources up by their aliases" do
532
@catalog.add_resource @one
533
@catalog.alias @one, "two"
534
@catalog.resource(:notify, "two").should equal(@one)
537
it "should remove resource aliases when the target resource is removed" do
538
@catalog.add_resource @one
539
@catalog.alias(@one, "other")
541
@catalog.remove_resource(@one)
542
@catalog.resource("notify", "other").should be_nil
545
it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do
546
resource = Puppet::Type.type(:file).new :path => "/something", :title => "other", :content => "blah"
547
resource.expects(:isomorphic?).returns(true)
548
@catalog.add_resource(resource)
549
@catalog.resource(:file, "other").should equal(resource)
550
@catalog.resource(:file, "/something").ref.should == resource.ref
553
it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do
554
resource = Puppet::Type.type(:file).new :path => "/something", :title => "other", :content => "blah"
555
resource.expects(:isomorphic?).returns(false)
556
@catalog.add_resource(resource)
557
@catalog.resource(:file, resource.title).should equal(resource)
558
# We can't use .should here, because the resources respond to that method.
559
if @catalog.resource(:file, resource.name)
560
raise "Aliased non-isomorphic resource"
564
it "should provide a method to create additional resources that also registers the resource" do
565
args = {:name => "/yay", :ensure => :file}
566
resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay"
567
Puppet::Type.type(:file).expects(:new).with(args).returns(resource)
568
@catalog.create_resource :file, args
569
@catalog.resource("File[/yay]").should equal(resource)
573
describe "when applying" do
575
@catalog = Puppet::Resource::Catalog.new("host")
577
@catalog.retrieval_duration = Time.now
578
@transaction = mock 'transaction'
579
Puppet::Transaction.stubs(:new).returns(@transaction)
580
@transaction.stubs(:evaluate)
581
@transaction.stubs(:cleanup)
582
@transaction.stubs(:addtimes)
585
it "should create and evaluate a transaction" do
586
@transaction.expects(:evaluate)
590
it "should provide the catalog time to the transaction" do
591
@transaction.expects(:addtimes).with do |arg|
592
arg[:config_retrieval].should be_instance_of(Time)
598
it "should clean up the transaction" do
599
@transaction.expects :cleanup
603
it "should return the transaction" do
604
@catalog.apply.should equal(@transaction)
607
it "should yield the transaction if a block is provided" do
608
@catalog.apply do |trans|
609
trans.should equal(@transaction)
613
it "should default to not being a host catalog" do
614
@catalog.host_config.should be_nil
617
it "should pass supplied tags on to the transaction" do
618
@transaction.expects(:tags=).with(%w{one two})
619
@catalog.apply(:tags => %w{one two})
622
it "should set ignoreschedules on the transaction if specified in apply()" do
623
@transaction.expects(:ignoreschedules=).with(true)
624
@catalog.apply(:ignoreschedules => true)
627
it "should remove resources created mid-transaction" do
628
args = {:name => "/yay", :ensure => :file}
629
resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay"
630
@transaction = mock 'transaction'
631
Puppet::Transaction.stubs(:new).returns(@transaction)
632
@transaction.stubs(:evaluate)
633
@transaction.stubs(:cleanup)
634
@transaction.stubs(:addtimes)
635
Puppet::Type.type(:file).expects(:new).with(args).returns(resource)
636
resource.expects :remove
637
@catalog.apply do |trans|
638
@catalog.create_resource :file, args
639
@catalog.resource("File[/yay]").should equal(resource)
641
@catalog.resource("File[/yay]").should be_nil
644
it "should remove resources added mid-transaction" do
645
@transaction = mock 'transaction'
646
Puppet::Transaction.stubs(:new).returns(@transaction)
647
@transaction.stubs(:evaluate)
648
@transaction.stubs(:cleanup)
649
@transaction.stubs(:addtimes)
650
file = Puppet::Type.type(:file).new(:name => "/yay", :ensure => :file)
651
@catalog.apply do |trans|
652
@catalog.add_resource file
653
@catalog.resource("File[/yay]").should_not be_nil
655
@catalog.resource("File[/yay]").should be_nil
658
it "should expire cached data in the resources both before and after the transaction" do
659
@catalog.expects(:expire).times(2)
663
describe "host catalogs" do
665
# super() doesn't work in the setup method for some reason
667
@catalog.host_config = true
668
Puppet::Util::Storage.stubs(:store)
671
it "should send a report if reporting is enabled" do
672
Puppet[:report] = true
673
@transaction.expects :send_report
674
@transaction.stubs :any_failed? => false
678
it "should send a report if report summaries are enabled" do
679
Puppet[:summarize] = true
680
@transaction.expects :send_report
681
@transaction.stubs :any_failed? => false
685
it "should initialize the state database before applying a catalog" do
686
Puppet::Util::Storage.expects(:load)
688
# Short-circuit the apply, so we know we're loading before the transaction
689
Puppet::Transaction.expects(:new).raises ArgumentError
690
proc { @catalog.apply }.should raise_error(ArgumentError)
693
it "should sync the state database after applying" do
694
Puppet::Util::Storage.expects(:store)
695
@transaction.stubs :any_failed? => false
699
after { Puppet.settings.clear }
702
describe "non-host catalogs" do
705
@catalog.host_config = false
708
it "should never send reports" do
709
Puppet[:report] = true
710
Puppet[:summarize] = true
711
@transaction.expects(:send_report).never
715
it "should never modify the state database" do
716
Puppet::Util::Storage.expects(:load).never
717
Puppet::Util::Storage.expects(:store).never
721
after { Puppet.settings.clear }
725
describe "when creating a relationship graph" do
727
Puppet::Type.type(:component)
728
@catalog = Puppet::Resource::Catalog.new("host")
729
@compone = Puppet::Type::Component.new :name => "one"
730
@comptwo = Puppet::Type::Component.new :name => "two", :require => "Class[one]"
731
@file = Puppet::Type.type(:file)
732
@one = @file.new :path => "/one"
733
@two = @file.new :path => "/two"
734
@sub = @file.new :path => "/two/subdir"
735
@catalog.add_edge @compone, @one
736
@catalog.add_edge @comptwo, @two
738
@three = @file.new :path => "/three"
739
@four = @file.new :path => "/four", :require => "File[/three]"
740
@five = @file.new :path => "/five"
741
@catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub
743
@relationships = @catalog.relationship_graph
746
it "should be able to create a relationship graph" do
747
@relationships.should be_instance_of(Puppet::SimpleGraph)
750
it "should not have any components" do
751
@relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil
754
it "should have all non-component resources from the catalog" do
755
# The failures print out too much info, so i just do a class comparison
756
@relationships.vertex?(@five).should be_true
759
it "should have all resource relationships set as edges" do
760
@relationships.edge?(@three, @four).should be_true
763
it "should copy component relationships to all contained resources" do
764
@relationships.edge?(@one, @two).should be_true
767
it "should add automatic relationships to the relationship graph" do
768
@relationships.edge?(@two, @sub).should be_true
771
it "should get removed when the catalog is cleaned up" do
772
@relationships.expects(:clear)
774
@catalog.instance_variable_get("@relationship_graph").should be_nil
777
it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do
779
graph = Puppet::SimpleGraph.new
780
Puppet::SimpleGraph.expects(:new).returns graph
782
graph.expects(:write_graph).with(:relationships)
783
graph.expects(:write_graph).with(:expanded_relationships)
785
@catalog.host_config = true
787
@catalog.relationship_graph
790
it "should not write graph files if the catalog is not a host catalog" do
792
graph = Puppet::SimpleGraph.new
793
Puppet::SimpleGraph.expects(:new).returns graph
795
graph.expects(:write_graph).never
797
@catalog.host_config = false
799
@catalog.relationship_graph
802
it "should create a new relationship graph after clearing the old one" do
803
@relationships.expects(:clear)
805
@catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph)
808
it "should remove removed resources from the relationship graph if it exists" do
809
@catalog.remove_resource(@one)
810
@catalog.relationship_graph.vertex?(@one).should be_false
814
describe "when writing dot files" do
816
@catalog = Puppet::Resource::Catalog.new("host")
818
@file = File.join(Puppet[:graphdir], @name.to_s + ".dot")
821
it "should only write when it is a host catalog" do
822
File.expects(:open).with(@file).never
823
@catalog.host_config = false
824
Puppet[:graph] = true
825
@catalog.write_graph(@name)
829
Puppet.settings.clear
833
describe "when indirecting" do
835
@indirection = stub 'indirection', :name => :catalog
837
Puppet::Util::Cacher.expire
840
it "should redirect to the indirection for retrieval" do
841
Puppet::Resource::Catalog.stubs(:indirection).returns(@indirection)
842
@indirection.expects(:find)
843
Puppet::Resource::Catalog.find(:myconfig)
846
it "should default to the 'compiler' terminus" do
847
Puppet::Resource::Catalog.indirection.terminus_class.should == :compiler
851
Puppet::Util::Cacher.expire
855
describe "when converting to yaml" do
857
@catalog = Puppet::Resource::Catalog.new("me")
858
@catalog.add_edge("one", "two")
861
it "should be able to be dumped to yaml" do
862
YAML.dump(@catalog).should be_instance_of(String)
866
describe "when converting from yaml" do
868
@catalog = Puppet::Resource::Catalog.new("me")
869
@catalog.add_edge("one", "two")
871
text = YAML.dump(@catalog)
872
@newcatalog = YAML.load(text)
875
it "should get converted back to a catalog" do
876
@newcatalog.should be_instance_of(Puppet::Resource::Catalog)
879
it "should have all vertices" do
880
@newcatalog.vertex?("one").should be_true
881
@newcatalog.vertex?("two").should be_true
884
it "should have all edges" do
885
@newcatalog.edge?("one", "two").should be_true
890
describe Puppet::Resource::Catalog, "when converting to pson" do
891
confine "Missing 'pson' library" => Puppet.features.pson?
894
@catalog = Puppet::Resource::Catalog.new("myhost")
897
def pson_output_should
898
@catalog.class.expects(:pson_create).with { |hash| yield hash }.returns(:something)
901
# LAK:NOTE For all of these tests, we convert back to the resource so we can
902
# trap the actual data structure then.
903
it "should set its document_type to 'Catalog'" do
904
pson_output_should { |hash| hash['document_type'] == "Catalog" }
906
PSON.parse @catalog.to_pson
909
it "should set its data as a hash" do
910
pson_output_should { |hash| hash['data'].is_a?(Hash) }
911
PSON.parse @catalog.to_pson
914
[:name, :version, :tags, :classes].each do |param|
915
it "should set its #{param} to the #{param} of the resource" do
916
@catalog.send(param.to_s + "=", "testing") unless @catalog.send(param)
918
pson_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) }
919
PSON.parse @catalog.to_pson
923
it "should convert its resources to a PSON-encoded array and store it as the 'resources' data" do
924
one = stub 'one', :to_pson_data_hash => "one_resource", :ref => "Foo[one]"
925
two = stub 'two', :to_pson_data_hash => "two_resource", :ref => "Foo[two]"
927
@catalog.add_resource(one)
928
@catalog.add_resource(two)
930
# TODO this should really guarantee sort order
931
PSON.parse(@catalog.to_pson,:create_additions => false)['data']['resources'].sort.should == ["one_resource", "two_resource"].sort
935
it "should convert its edges to a PSON-encoded array and store it as the 'edges' data" do
936
one = stub 'one', :to_pson_data_hash => "one_resource", :ref => 'Foo[one]'
937
two = stub 'two', :to_pson_data_hash => "two_resource", :ref => 'Foo[two]'
938
three = stub 'three', :to_pson_data_hash => "three_resource", :ref => 'Foo[three]'
940
@catalog.add_edge(one, two)
941
@catalog.add_edge(two, three)
943
@catalog.edge(one, two ).expects(:to_pson_data_hash).returns "one_two_pson"
944
@catalog.edge(two, three).expects(:to_pson_data_hash).returns "two_three_pson"
946
PSON.parse(@catalog.to_pson,:create_additions => false)['data']['edges'].sort.should == %w{one_two_pson two_three_pson}.sort
950
describe Puppet::Resource::Catalog, "when converting from pson" do
951
confine "Missing 'pson' library" => Puppet.features.pson?
953
def pson_result_should
954
Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash }
962
'document_type' => 'Puppet::Resource::Catalog',
967
@catalog = Puppet::Resource::Catalog.new("myhost")
968
Puppet::Resource::Catalog.stubs(:new).returns @catalog
971
it "should be extended with the PSON utility module" do
972
Puppet::Resource::Catalog.metaclass.ancestors.should be_include(Puppet::Util::Pson)
975
it "should create it with the provided name" do
976
Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog
977
PSON.parse @pson.to_pson
980
it "should set the provided version on the catalog if one is set" do
981
@data['version'] = 50
982
PSON.parse @pson.to_pson
983
@catalog.version.should == @data['version']
986
it "should set any provided tags on the catalog" do
987
@data['tags'] = %w{one two}
988
PSON.parse @pson.to_pson
989
@catalog.tags.should == @data['tags']
992
it "should set any provided classes on the catalog" do
993
@data['classes'] = %w{one two}
994
PSON.parse @pson.to_pson
995
@catalog.classes.should == @data['classes']
998
it 'should convert the resources list into resources and add each of them' do
999
@data['resources'] = [Puppet::Resource.new(:file, "/foo"), Puppet::Resource.new(:file, "/bar")]
1001
@catalog.expects(:add_resource).times(2).with { |res| res.type == "File" }
1002
PSON.parse @pson.to_pson
1005
it 'should convert resources even if they do not include "type" information' do
1006
@data['resources'] = [Puppet::Resource.new(:file, "/foo")]
1008
@data['resources'][0].expects(:to_pson).returns '{"title":"/foo","tags":["file"],"type":"File"}'
1010
@catalog.expects(:add_resource).with { |res| res.type == "File" }
1012
PSON.parse @pson.to_pson
1015
it 'should convert the edges list into edges and add each of them' do
1016
one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh")
1017
two = Puppet::Relationship.new("tsource", "ttarget", :event => "two", :callback => "refresh")
1019
@data['edges'] = [one, two]
1021
@catalog.stubs(:resource).returns("eh")
1023
@catalog.expects(:add_edge).with { |edge| edge.event == "one" }
1024
@catalog.expects(:add_edge).with { |edge| edge.event == "two" }
1026
PSON.parse @pson.to_pson
1029
it "should be able to convert relationships that do not include 'type' information" do
1030
one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh")
1031
one.expects(:to_pson).returns "{\"event\":\"one\",\"callback\":\"refresh\",\"source\":\"osource\",\"target\":\"otarget\"}"
1033
@data['edges'] = [one]
1035
@catalog.stubs(:resource).returns("eh")
1037
@catalog.expects(:add_edge).with { |edge| edge.event == "one" }
1039
PSON.parse @pson.to_pson
1042
it "should set the source and target for each edge to the actual resource" do
1043
edge = Puppet::Relationship.new("source", "target")
1045
@data['edges'] = [edge]
1047
@catalog.expects(:resource).with("source").returns("source_resource")
1048
@catalog.expects(:resource).with("target").returns("target_resource")
1050
@catalog.expects(:add_edge).with { |edge| edge.source == "source_resource" and edge.target == "target_resource" }
1052
PSON.parse @pson.to_pson
1055
it "should fail if the source resource cannot be found" do
1056
edge = Puppet::Relationship.new("source", "target")
1058
@data['edges'] = [edge]
1060
@catalog.expects(:resource).with("source").returns(nil)
1061
@catalog.stubs(:resource).with("target").returns("target_resource")
1063
lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError)
1066
it "should fail if the target resource cannot be found" do
1067
edge = Puppet::Relationship.new("source", "target")
1069
@data['edges'] = [edge]
1071
@catalog.stubs(:resource).with("source").returns("source_resource")
1072
@catalog.expects(:resource).with("target").returns(nil)
1074
lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError)