1#!/usr/bin/env rspec
2
3require 'spec_helper'
4
5module MCollective
6  class Application
7    describe Puppet do
8      before do
9        application_file = File.join(File.dirname(__FILE__), '../../', 'application', 'puppet.rb')
10        @app = MCollective::Test::ApplicationTest.new('puppet', :application_file => application_file).plugin
11
12        client = mock
13        client.stubs(:stats).returns(RPC::Stats.new)
14        client.stubs(:progress=)
15        @app.stubs(:client).returns(client)
16        @app.stubs(:printrpc)
17        @app.stubs(:printrpcstats)
18        @app.stubs(:halt)
19      end
20
21      describe "#application_description" do
22        it "should have a descrption set" do
23          @app.should have_a_description
24        end
25      end
26
27      describe "#post_option_parser" do
28        it "should detect unsupported commands" do
29          ARGV << "rspec"
30          expect { @app.post_option_parser(@app.configuration) }.to raise_error(/Action must be/)
31        end
32
33        it "should get the concurrency for runall" do
34          ARGV << "runall"
35          ARGV << "1"
36
37          @app.post_option_parser(@app.configuration)
38          @app.configuration[:command].should == "runall"
39          @app.configuration[:concurrency].should == 1
40        end
41
42        it "should get the message for disable" do
43          ARGV << "disable"
44          ARGV << "rspec test"
45
46          @app.post_option_parser(@app.configuration)
47          @app.configuration[:message].should == "rspec test"
48        end
49
50        it "should detect when no command is given" do
51          ARGV.clear
52
53          @app.expects(:raise_message).with(2)
54          @app.post_option_parser(@app.configuration)
55        end
56      end
57
58      describe "#validate_configuration" do
59        it "should not allow the splay option when forcing" do
60          @app.configuration[:force] = true
61          @app.configuration[:splay] = true
62
63          @app.expects(:raise_message).with(3)
64          @app.validate_configuration(@app.configuration)
65        end
66
67        it "should not allow the splaylimit option when forcing" do
68          @app.configuration[:force] = true
69          @app.configuration[:splaylimit] = 60
70
71          @app.expects(:raise_message).with(4)
72          @app.validate_configuration(@app.configuration)
73        end
74
75        it "should ensure the runall command has a concurrency" do
76          @app.configuration[:command] = "runall"
77
78          @app.expects(:raise_message).with(5)
79          @app.validate_configuration(@app.configuration)
80        end
81
82        it "should make sure the concurrency is > 0" do
83          @app.configuration[:command] = "runall"
84          @app.configuration[:concurrency] = 0
85
86          @app.expects(:raise_message).with(7)
87          @app.validate_configuration(@app.configuration)
88
89          @app.configuration[:concurrency] = 1
90          @app.validate_configuration(@app.configuration)
91        end
92      end
93
94      describe "#shorten_number" do
95        it "should shorten numbers correctly" do
96          @app.shorten_number("9999999").should == "10.0m"
97          @app.shorten_number("8999999").should == "9.0m"
98          @app.shorten_number("9000").should == "9.0k"
99          @app.shorten_number("9").should == "9.0"
100          @app.shorten_number("wat").should == "NaN"
101        end
102      end
103
104      describe "#calculate_longest_hostname" do
105        it "should calculate the correct size" do
106          results = [{:sender => "a"}, {:sender => "abcdef"}, {:sender => "ab"}]
107          @app.calculate_longest_hostname(results).should == 6
108        end
109      end
110
111      describe "#display_results_single_field" do
112        it "should print succesful results correctly" do
113          result = [{:statuscode => 0, :sender => "rspec sender", :data => {:message => "rspec test"}}]
114          @app.expects(:puts).with("   rspec sender: rspec test")
115          @app.display_results_single_field(result, :message)
116        end
117
118        it "should print failed results correctly" do
119          result = [{:statuscode => 1, :sender => "rspec sender", :data => {:message => "rspec test"}, :statusmsg => "error"}]
120          Util.expects(:colorize).with(:red, "error").returns("error")
121          @app.expects(:puts).with("   rspec sender: error")
122
123          @app.display_results_single_field(result, :message)
124        end
125
126        it "should not fail for empty results" do
127          @app.display_results_single_field([], :message).should == false
128        end
129      end
130
131      describe "#sparkline_for_field" do
132        it "should correctly extract and draw the data" do
133          results = []
134
135          (10...22).each do |c|
136            results << {:statuscode => 0, :data => {:rspec => c}}
137          end
138
139          @app.expects(:spark).with([2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0]).returns("rspec")
140          @app.sparkline_for_field(results, :rspec, 11).should == "rspec  min: 10.0   avg: 15.5   max: 21.0  "
141        end
142
143        it "should return an empty string with bad data to extract from" do
144          results = []
145          (10...22).each do |c|
146            results << {:statuscode => 1, :data => {:rspec => c}}
147          end
148
149          @app.sparkline_for_field(results, :rspec).should == ''
150        end
151
152        it "should return an empty string with no data to extract" do
153          results = []
154          @app.sparkline_for_field(results, :rspec).should == ''
155        end
156
157        it "should correctly handle mixed agent versions where some fields might be missing from some results" do
158          results = []
159
160          (10...22).each do |c|
161            results << {:statuscode => 0, :data => {:rspec => c}}
162          end
163          results << {:statuscode => 0, :data => {}}
164
165          @app.expects(:spark).with([2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0]).returns("rspec")
166          @app.sparkline_for_field(results, :rspec, 11).should == "rspec  min: 10.0   avg: 15.5   max: 21.0  "
167        end
168      end
169
170      describe "#spark" do
171        it "should correctly draw all zeros" do
172          @app.spark([0,0,0,0,0], ["0", "1", "2", "3"]).should == "00000"
173        end
174
175        it "should draw non zero for small numbers" do
176          @app.spark([1,0,0,0,100], ["0", "1", "2", "3"]).should == "10003"
177        end
178      end
179
180      describe "#runonce_arguments" do
181        it "should set the correct arguments" do
182          @app.configuration[:force] = true
183          @app.configuration[:server] = "rspec:123"
184          @app.configuration[:noop] = true
185          @app.configuration[:environment] = "rspec"
186          @app.configuration[:splay] = true
187          @app.configuration[:splaylimit] = 60
188          @app.configuration[:tag] = ["one", "two"]
189          @app.configuration[:use_cached_catalog] = false
190          @app.configuration[:ignoreschedules] = true
191
192          @app.runonce_arguments.should == {:splaylimit=>60, :force=>true, :environment=>"rspec", :noop=>true, :server=>"rspec:123", :tags=>"one,two", :splay=>true, :use_cached_catalog=>false, :ignoreschedules=>true}
193        end
194      end
195
196      describe "runall_command" do
197        it "should use the Puppetrunner to schedule runs" do
198          runner = mock
199          runner.expects(:logger)
200          runner.expects(:runall)
201
202          @app.runall_command(runner)
203        end
204      end
205
206      describe "#summary_command" do
207        it "should gather the summaries and display it" do
208          @app.client.expects(:progress=).with(false)
209          @app.client.expects(:last_run_summary).returns([])
210          @app.expects(:halt)
211
212          [:total_resources, :out_of_sync_resources, :failed_resources, :changed_resources,
213           :config_retrieval_time, :total_time, :since_lastrun, :corrected_resources]. each do |field|
214              @app.expects(:sparkline_for_field).with([], field)
215           end
216
217          @app.summary_command
218        end
219      end
220
221      describe "#status_command" do
222        it "should display the :message result and stats" do
223          @app.client.expects(:status).returns("rspec")
224          @app.expects(:display_results_single_field).with("rspec", :message)
225          @app.expects(:printrpcstats).with(:summarize => true)
226          @app.expects(:halt)
227          @app.status_command
228        end
229      end
230
231      describe "#enable_command" do
232        it "should enable the daemons and print results" do
233          @app.client.expects(:enable)
234          @app.expects(:printrpcstats).with(:summarize => true)
235          @app.expects(:halt)
236          @app.enable_command
237        end
238      end
239
240      describe "#disable_command" do
241        before do
242          @app.expects(:printrpcstats).with(:summarize => true)
243          @app.expects(:halt)
244        end
245
246        it "should support disabling with a message" do
247          @app.configuration[:message] = "rspec test"
248          @app.client.expects(:disable).with(:message => "rspec test").returns("rspec")
249          @app.disable_command
250        end
251
252        it "should support disabling without a message" do
253          @app.client.expects(:disable).with({}).returns("rspec")
254          @app.disable_command
255        end
256      end
257
258      describe "#runonce_command" do
259        it "should run the agent along with any custom arguments" do
260          @app.configuration[:force] = true
261          @app.configuration[:server] = "rspec:123"
262          @app.configuration[:noop] = true
263          @app.configuration[:environment] = "rspec"
264          @app.configuration[:splay] = true
265          @app.configuration[:splaylimit] = 60
266          @app.configuration[:tag] = ["one", "two"]
267          @app.configuration[:use_cached_catalog] = false
268          @app.configuration[:ignoreschedules] = true
269
270          @app.client.expects(:runonce).with(:force => true,
271                                             :server => "rspec:123",
272                                             :noop => true,
273                                             :environment => "rspec",
274                                             :splay => true,
275                                             :splaylimit => 60,
276                                             :use_cached_catalog => false,
277                                             :ignoreschedules => true,
278                                             :tags => "one,two").returns("result")
279          @app.expects(:halt)
280          @app.runonce_command
281        end
282      end
283
284      describe "#count_command" do
285        it "should display the totals" do
286          @app.client.expects(:status)
287          @app.client.stats.expects(:okcount).returns(3)
288          @app.client.stats.stubs(:failcount).returns(1)
289          Util.expects(:colorize).with(:red, "Failed to retrieve status of 1 node").returns("Failed to retrieve status of 1 node")
290          @app.expects(:extract_values_from_aggregates).returns(:enabled => {"enabled" => 3},
291                                                                :applying => {true => 1, false => 2},
292                                                                :daemon_present => {"running" => 2},
293                                                                :idling => {true => 1})
294
295
296          @app.expects(:puts).with("Total Puppet nodes: 3")
297          @app.expects(:puts).with("          Nodes currently enabled: 3")
298          @app.expects(:puts).with("         Nodes currently disabled: 0")
299          @app.expects(:puts).with("Nodes currently doing puppet runs: 1")
300          @app.expects(:puts).with("          Nodes currently stopped: 2")
301          @app.expects(:puts).with("       Nodes with daemons started: 2")
302          @app.expects(:puts).with("    Nodes without daemons started: 0")
303          @app.expects(:puts).with("       Daemons started but idling: 1")
304          @app.expects(:puts).with("Failed to retrieve status of 1 node")
305
306          @app.count_command
307        end
308      end
309
310      describe "#main" do
311        it "should call the command if it exist" do
312          @app.expects(:count_command)
313          @app.configuration[:command] = "count"
314          @app.main
315        end
316
317        it "should fail gracefully when a command does not exist" do
318          @app.expects(:raise_message).with(6, "rspec")
319          @app.configuration[:command] = "rspec"
320          @app.main
321        end
322      end
323    end
324  end
325end
326