ml-puppetdb-terminus 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +202 -0
  3. data/NOTICE.txt +17 -0
  4. data/README.md +22 -0
  5. data/puppet/lib/puppet/application/storeconfigs.rb +4 -0
  6. data/puppet/lib/puppet/face/node/deactivate.rb +37 -0
  7. data/puppet/lib/puppet/face/node/status.rb +80 -0
  8. data/puppet/lib/puppet/face/storeconfigs.rb +193 -0
  9. data/puppet/lib/puppet/indirector/catalog/puppetdb.rb +400 -0
  10. data/puppet/lib/puppet/indirector/facts/puppetdb.rb +152 -0
  11. data/puppet/lib/puppet/indirector/facts/puppetdb_apply.rb +25 -0
  12. data/puppet/lib/puppet/indirector/node/puppetdb.rb +19 -0
  13. data/puppet/lib/puppet/indirector/resource/puppetdb.rb +108 -0
  14. data/puppet/lib/puppet/reports/puppetdb.rb +188 -0
  15. data/puppet/lib/puppet/util/puppetdb.rb +108 -0
  16. data/puppet/lib/puppet/util/puppetdb/char_encoding.rb +316 -0
  17. data/puppet/lib/puppet/util/puppetdb/command.rb +116 -0
  18. data/puppet/lib/puppet/util/puppetdb/command_names.rb +8 -0
  19. data/puppet/lib/puppet/util/puppetdb/config.rb +148 -0
  20. data/puppet/lib/puppet/util/puppetdb/http.rb +121 -0
  21. data/puppet/spec/README.markdown +8 -0
  22. data/puppet/spec/spec.opts +6 -0
  23. data/puppet/spec/spec_helper.rb +38 -0
  24. data/puppet/spec/unit/face/node/deactivate_spec.rb +28 -0
  25. data/puppet/spec/unit/face/node/status_spec.rb +43 -0
  26. data/puppet/spec/unit/face/storeconfigs_spec.rb +199 -0
  27. data/puppet/spec/unit/indirector/catalog/puppetdb_spec.rb +703 -0
  28. data/puppet/spec/unit/indirector/facts/puppetdb_apply_spec.rb +27 -0
  29. data/puppet/spec/unit/indirector/facts/puppetdb_spec.rb +347 -0
  30. data/puppet/spec/unit/indirector/node/puppetdb_spec.rb +61 -0
  31. data/puppet/spec/unit/indirector/resource/puppetdb_spec.rb +199 -0
  32. data/puppet/spec/unit/reports/puppetdb_spec.rb +249 -0
  33. data/puppet/spec/unit/util/puppetdb/char_encoding_spec.rb +212 -0
  34. data/puppet/spec/unit/util/puppetdb/command_spec.rb +98 -0
  35. data/puppet/spec/unit/util/puppetdb/config_spec.rb +227 -0
  36. data/puppet/spec/unit/util/puppetdb/http_spec.rb +138 -0
  37. data/puppet/spec/unit/util/puppetdb_spec.rb +33 -0
  38. metadata +115 -0
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'puppet/indirector/facts/puppetdb_apply'
3
+
4
+ describe Puppet::Node::Facts::PuppetdbApply do
5
+ describe "#save" do
6
+ let(:facts) { Puppet::Node::Facts.new('foo') }
7
+
8
+ def save
9
+ subject.save(Puppet::Node::Facts.indirection.request(:save, facts.name, facts))
10
+ end
11
+
12
+ it "should only submit commands once" do
13
+ subject.expects(:submit_command).once
14
+ save
15
+ save
16
+ save
17
+ end
18
+
19
+ end
20
+
21
+ describe "#find" do
22
+ it "always returns nil to force the cache to be skipped" do
23
+ subject.find('foo').should be_nil
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env rspec
2
+ require 'spec_helper'
3
+
4
+ require 'puppet/util/feature'
5
+ require 'puppet/indirector/facts/puppetdb'
6
+ require 'puppet/util/puppetdb'
7
+ require 'puppet/util/puppetdb/command_names'
8
+ require 'json'
9
+
10
+
11
+ describe Puppet::Node::Facts::Puppetdb do
12
+
13
+ CommandReplaceFacts = Puppet::Util::Puppetdb::CommandNames::CommandReplaceFacts
14
+
15
+ let(:http) {stub 'http'}
16
+
17
+ before :each do
18
+ Puppet::Util::Puppetdb.config.stubs(:server_urls).returns [URI("https://localhost:8282")]
19
+ Puppet::Node::Facts.indirection.stubs(:terminus).returns(subject)
20
+ Puppet::Network::HttpPool.stubs(:http_instance).returns(http)
21
+ create_environmentdir("my_environment")
22
+ end
23
+
24
+ describe "#save" do
25
+ let(:response) { Net::HTTPOK.new('1.1', 200, 'OK') }
26
+ let(:facts) { Puppet::Node::Facts.new('foo') }
27
+
28
+ let(:options) {{
29
+ :producer_timestamp => 'a test',
30
+ :environment => "my_environment",
31
+ }}
32
+
33
+ before :each do
34
+ response.stubs(:body).returns '{"uuid": "a UUID"}'
35
+ end
36
+
37
+ def save
38
+ subject.save(Puppet::Node::Facts.indirection.request(:save, facts.name, facts, options))
39
+ end
40
+
41
+ it "should POST the facts as a JSON string" do
42
+ Puppet::Util::Puppetdb.stubs(:puppet3compat?).returns(true)
43
+ f = {
44
+ "certname" => facts.name,
45
+ "values" => subject.maybe_strip_internal(facts),
46
+ "environment" => "my_environment",
47
+ "producer_timestamp" => "a test",
48
+ }
49
+
50
+ payload = {
51
+ :command => CommandReplaceFacts,
52
+ :version => 4,
53
+ :payload => f,
54
+ }.to_json
55
+
56
+ http.expects(:post).with do |uri, body, headers|
57
+ expect(body).to eq(payload)
58
+ end.returns response
59
+
60
+ save
61
+ end
62
+
63
+ it "should POST the trusted data we tell it to" do
64
+
65
+ if Puppet::Util::Puppetdb.puppet3compat?
66
+ Puppet[:trusted_node_data] = true
67
+ end
68
+
69
+ trusted_data = {"foo" => "foobar", "certname" => "testing_posting"}
70
+ subject.stubs(:get_trusted_info).returns trusted_data
71
+
72
+ f = {
73
+ "certname" => facts.name,
74
+ "values" => subject.maybe_strip_internal(facts).merge({"trusted" => trusted_data}),
75
+ "environment" => "my_environment",
76
+ "producer_timestamp" => "a test",
77
+ }
78
+
79
+ payload = {
80
+ :command => CommandReplaceFacts,
81
+ :version => 4,
82
+ :payload => f,
83
+ }.to_pson
84
+
85
+ http.expects(:post).with do |uri, body, headers|
86
+ expect(body).to eq(payload)
87
+ end.returns response
88
+
89
+ save
90
+ end
91
+
92
+
93
+ it "should retain integer type when submitting" do
94
+ facts.values['something'] = 100
95
+
96
+ sent_payload = nil
97
+ http.expects(:post).with do |uri, body, headers|
98
+ sent_payload = body
99
+ end.returns response
100
+
101
+ save
102
+
103
+ message = JSON.parse(sent_payload)
104
+ sent_facts = message['payload']
105
+
106
+ # We shouldn't modify the original instance
107
+ facts.values['something'].should == 100
108
+ sent_facts['values']['something'].should == 100
109
+ end
110
+ end
111
+
112
+ describe "#get_trusted_info" do
113
+ it 'should return trusted data' do
114
+ node = Puppet::Node.new('my_certname')
115
+ trusted = subject.get_trusted_info(node)
116
+ # Extra keys domain & hostname introduced by PUP-5097, Puppet 4.3.0
117
+ if trusted.has_key?("domain")
118
+ expect(trusted).to eq({'authenticated'=>'local', 'certname'=>'testing',
119
+ 'extensions'=>{}, 'hostname'=>'testing', 'domain'=>nil})
120
+ else
121
+ # Puppet 4.2.x and older
122
+ expect(trusted).to eq({'authenticated'=>'local', 'certname'=>'testing', 'extensions'=>{}})
123
+ end
124
+ end
125
+
126
+ it 'should return trusted data when falling back to the node' do
127
+ # This removes :trusted_information from the global context, triggering our fallback code.
128
+ if Puppet.methods.include? :rollback_context
129
+ Puppet.rollback_context('initial testing state')
130
+ else
131
+ Puppet.pop_context # puppet 3.5.1
132
+ end
133
+
134
+ node = Puppet::Node.new('my_certname', :parameters => {'clientcert' => 'trusted_certname'})
135
+ trusted = subject.get_trusted_info(node)
136
+
137
+ # Extra keys domainname & hostname introduced by PUP-5097, Puppet 4.3.0
138
+ if trusted.has_key?("domain")
139
+ expect(trusted).to eq({'authenticated'=>'local', 'certname'=>'trusted_certname',
140
+ 'extensions'=>{}, 'hostname'=>'trusted_certname', 'domain'=>nil})
141
+ else
142
+ # Puppet 4.2.x and older
143
+ expect(trusted).to eq({'authenticated'=>'local', 'certname'=>'trusted_certname', 'extensions'=>{}})
144
+ end
145
+
146
+ # Put the context back the way the test harness expects
147
+ Puppet.push_context({}, 'context to make the tests happy')
148
+ if Puppet.methods.include? :mark_context
149
+ Puppet.mark_context('initial testing state')
150
+ end
151
+ end
152
+ end
153
+
154
+ describe "#find" do
155
+ def find_facts()
156
+ Puppet::Node::Facts.indirection.find('some_node')
157
+ end
158
+
159
+ it "should return the facts if they're found" do
160
+ body = [{"certname" => "some_node", "environment" => "production", "name" => "a", "value" => "1"},
161
+ {"certname" => "some_node", "environment" => "production", "name" => "b", "value" => "2"}].to_json
162
+
163
+ response = Net::HTTPOK.new('1.1', 200, 'OK')
164
+ response.stubs(:body).returns body
165
+
166
+ http.stubs(:get).with("/pdb/query/v4/nodes/some_node/facts", subject.headers).returns response
167
+
168
+ result = find_facts
169
+ result.should be_a(Puppet::Node::Facts)
170
+ result.name.should == 'some_node'
171
+ result.values.should include('a' => '1', 'b' => '2')
172
+ end
173
+
174
+ it "should return nil if no facts are found" do
175
+ body = {"error" => "No information known about factset some_node"}.to_json
176
+
177
+ response = Net::HTTPNotFound.new('1.1', 404, 'NotFound')
178
+ response.stubs(:body).returns body
179
+
180
+ http.stubs(:get).with("/pdb/query/v4/nodes/some_node/facts", subject.headers).returns response
181
+
182
+ find_facts.should be_nil
183
+ end
184
+
185
+ it "should fail if an HTTP error code is returned" do
186
+ response = Net::HTTPForbidden.new('1.1', 403, "Forbidden")
187
+ response.stubs(:body).returns ''
188
+
189
+ http.stubs(:get).with("/pdb/query/v4/nodes/some_node/facts", subject.headers).returns response
190
+
191
+ expect {
192
+ find_facts
193
+ }.to raise_error Puppet::Error, /\[403 Forbidden\]/
194
+ end
195
+
196
+ it "should fail if an error occurs" do
197
+ http.stubs(:get).with("/pdb/query/v4/nodes/some_node/facts", subject.headers).raises Puppet::Error, "Everything is terrible!"
198
+
199
+ expect {
200
+ find_facts
201
+ }.to raise_error Puppet::Error, /Everything is terrible!/
202
+ end
203
+
204
+ it "should log a deprecation warning if one is returned from PuppetDB" do
205
+ response = Net::HTTPOK.new('1.1', 200, 'OK')
206
+ response['x-deprecation'] = "This is deprecated!"
207
+
208
+ body = [].to_json
209
+
210
+ response.stubs(:body).returns body
211
+
212
+ http.stubs(:get).with("/pdb/query/v4/nodes/some_node/facts", subject.headers).returns(response)
213
+
214
+ Puppet.expects(:deprecation_warning).with do |msg|
215
+ msg =~ /This is deprecated!/
216
+ end
217
+
218
+ find_facts
219
+ end
220
+ end
221
+
222
+ describe "#search" do
223
+ def search_facts(query)
224
+ Puppet::Node::Facts.indirection.search('facts', query)
225
+ end
226
+ let(:response) { Net::HTTPOK.new('1.1', 200, 'OK') }
227
+
228
+ it "should return the nodes from the response" do
229
+ args = {
230
+ 'facts.kernel.eq' => 'Linux',
231
+ }
232
+
233
+ response.stubs(:body).returns '["foo", "bar", "baz"]'
234
+ response.stubs(:body).returns '[{"name": "foo", "deactivated": null, "expired": null, "catalog_timestamp": null, "facts_timestamp": null, "report_timestamp": null},
235
+ {"name": "bar", "deactivated": null, "expired": null, "catalog_timestamp": null, "facts_timestamp": null, "report_timestamp": null},
236
+ {"name": "baz", "deactivated": null, "expired": null, "catalog_timestamp": null, "facts_timestamp": null, "report_timestamp": null}]'
237
+
238
+ query = CGI.escape("[\"and\",[\"=\",[\"fact\",\"kernel\"],\"Linux\"]]")
239
+ http.stubs(:get).with("/pdb/query/v4/nodes?query=#{query}", subject.headers).returns(response)
240
+
241
+ search_facts(args).should == ['foo', 'bar', 'baz']
242
+ end
243
+
244
+ it "should only allow searches against facts" do
245
+ args = {
246
+ 'facts.kernel.eq' => 'Linux',
247
+ 'wrong.kernel.eq' => 'Linux',
248
+ }
249
+
250
+ expect do
251
+ search_facts(args)
252
+ end.to raise_error(Puppet::Error, /Fact search against keys of type 'wrong' is unsupported/)
253
+ end
254
+
255
+ it "should combine multiple terms with 'and'" do
256
+ args = {
257
+ 'facts.kernel.eq' => 'Linux',
258
+ 'facts.uptime.eq' => '10 days',
259
+ }
260
+
261
+ query = CGI.escape(["and", ["=", ["fact", "kernel"], "Linux"],
262
+ ["=", ["fact", "uptime"], "10 days"]].to_json)
263
+
264
+ response.stubs(:body).returns '[]'
265
+
266
+ http.stubs(:get).with("/pdb/query/v4/nodes?query=#{query}", subject.headers).returns(response)
267
+
268
+ search_facts(args)
269
+ end
270
+
271
+ it "should add 'not' to a != query" do
272
+ args = {
273
+ 'facts.kernel.ne' => 'Linux',
274
+ }
275
+
276
+ query = CGI.escape(["and", ["not", ["=", ["fact", "kernel"], "Linux"]]].to_json)
277
+
278
+ response.stubs(:body).returns '[]'
279
+
280
+ http.stubs(:get).with("/pdb/query/v4/nodes?query=#{query}", subject.headers).returns(response)
281
+
282
+ search_facts(args)
283
+ end
284
+
285
+ it "should default the operator to = if one is not specified" do
286
+ args = {
287
+ 'facts.kernel' => 'Linux',
288
+ }
289
+
290
+ query = CGI.escape(["and", ["=", ["fact", "kernel"], "Linux"]].to_json)
291
+
292
+ response.stubs(:body).returns '[]'
293
+
294
+ http.stubs(:get).with("/pdb/query/v4/nodes?query=#{query}", subject.headers).returns(response)
295
+
296
+ search_facts(args)
297
+ end
298
+
299
+ {
300
+ 'gt' => '>',
301
+ 'lt' => '<',
302
+ 'ge' => '>=',
303
+ 'le' => '<='
304
+ }.each do |name, operator|
305
+ it "should map '#{name}' to #{operator}" do
306
+ args = {
307
+ "facts.kernel.#{name}" => 'Linux',
308
+ }
309
+
310
+ query = CGI.escape(["and", [operator, ["fact", "kernel"], "Linux"]].to_json)
311
+
312
+ response.stubs(:body).returns '[]'
313
+
314
+ http.stubs(:get).with("/pdb/query/v4/nodes?query=#{query}", subject.headers).returns(response)
315
+
316
+ search_facts(args)
317
+ end
318
+ end
319
+
320
+ it "should raise an error if a failure occurs" do
321
+ response = Net::HTTPBadRequest.new('1.1', 400, 'Bad Request')
322
+ response.stubs(:body).returns 'Something bad happened!'
323
+
324
+ query = CGI.escape(["and"].to_json)
325
+ http.stubs(:get).with("/pdb/query/v4/nodes?query=#{query}", subject.headers).returns(response)
326
+
327
+ expect do
328
+ search_facts(nil)
329
+ end.to raise_error(Puppet::Error, /\[400 Bad Request\] Something bad happened!/)
330
+
331
+ end
332
+
333
+ it "should log a deprecation warning if one is returned from PuppetDB" do
334
+ response['x-deprecation'] = "This is deprecated!"
335
+ response.stubs(:body).returns '[]'
336
+
337
+ query = CGI.escape(["and"].to_json)
338
+ http.stubs(:get).with("/pdb/query/v4/nodes?query=#{query}", subject.headers).returns(response)
339
+
340
+ Puppet.expects(:deprecation_warning).with do |msg|
341
+ msg =~ /This is deprecated!/
342
+ end
343
+
344
+ search_facts(nil)
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env rspec
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'puppet/indirector/node/puppetdb'
6
+ require 'puppet/util/puppetdb/command_names'
7
+ require 'json'
8
+
9
+ describe Puppet::Node::Puppetdb do
10
+
11
+ CommandDeactivateNode = Puppet::Util::Puppetdb::CommandNames::CommandDeactivateNode
12
+
13
+ before :each do
14
+ Puppet::Node.indirection.stubs(:terminus).returns(subject)
15
+ end
16
+
17
+ let(:node) { "something.example.com" }
18
+ let(:producer_timestamp) { Time.now.iso8601(5) }
19
+
20
+ def destroy
21
+ Puppet::Node.indirection.destroy(node, {:producer_timestamp => producer_timestamp})
22
+ end
23
+
24
+ describe "#destroy" do
25
+ let(:response) { Net::HTTPOK.new('1.1', 200, 'OK') }
26
+ let(:http) { mock 'http' }
27
+ before :each do
28
+ Puppet::Network::HttpPool.expects(:http_instance).returns http
29
+ end
30
+
31
+ it "should POST a '#{CommandDeactivateNode}' command" do
32
+ response.stubs(:body).returns '{"uuid": "a UUID"}'
33
+
34
+ payload = {
35
+ :command => CommandDeactivateNode,
36
+ :version => 3,
37
+ :payload => { :certname => node,
38
+ :producer_timestamp => producer_timestamp },
39
+ }.to_json
40
+
41
+ http.expects(:post).with do |uri,body,headers|
42
+ expect(body).to eq(payload)
43
+ end.returns response
44
+
45
+ destroy
46
+ end
47
+
48
+ it "should log a deprecation warning if one is returned from PuppetDB" do
49
+ response['x-deprecation'] = 'A horrible deprecation warning!'
50
+ response.stubs(:body).returns '{"uuid": "a UUID"}'
51
+
52
+ Puppet.expects(:deprecation_warning).with do |msg|
53
+ msg =~ /A horrible deprecation warning!/
54
+ end
55
+
56
+ http.stubs(:post).returns response
57
+
58
+ destroy
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env rspec
2
+ require 'spec_helper'
3
+
4
+ require 'puppet/indirector/resource/puppetdb'
5
+ require 'json'
6
+
7
+ describe Puppet::Resource::Puppetdb do
8
+ before :each do
9
+ Puppet::Util::Puppetdb.stubs(:server).returns 'localhost'
10
+ Puppet::Util::Puppetdb.stubs(:port).returns 0
11
+ Puppet::Resource.indirection.stubs(:terminus).returns(subject)
12
+ end
13
+
14
+ describe "#search" do
15
+ let(:host) { 'default.local' }
16
+
17
+ def search(type)
18
+ # The API for creating scope objects is different between Puppet 2.7 and
19
+ # 3.0. The scope here isn't really used for anything relevant, so it's
20
+ # easiest to make it a stub to run against both versions of Puppet.
21
+ scope = stub('scope', :source => nil)
22
+ args = { :host => host, :filter => nil, :scope => scope }
23
+ Puppet::Resource.indirection.search(type, args)
24
+ end
25
+
26
+ it "should return an empty array if no resources match" do
27
+ response = Net::HTTPOK.new('1.1', 200, 'OK')
28
+ response.stubs(:body).returns '[]'
29
+
30
+ query = CGI.escape(["and", ["=", "type", "exec"], ["=", "exported", true], ["not", ["=", "certname", "default.local"]]].to_json)
31
+ http = stub 'http'
32
+ Puppet::Network::HttpPool.stubs(:http_instance).returns(http)
33
+ http.stubs(:get).with("/pdb/query/v4/resources?query=#{query}", subject.headers).returns response
34
+
35
+ search("exec").should == []
36
+ end
37
+
38
+ it "should log a deprecation warning if one is returned from PuppetDB" do
39
+ response = Net::HTTPOK.new('1.1', 200, 'OK')
40
+ response['x-deprecation'] = "Deprecated, yo."
41
+
42
+ response.stubs(:body).returns '[]'
43
+
44
+ query = CGI.escape(["and", ["=", "type", "exec"], ["=", "exported", true], ["not", ["=", "certname", "default.local"]]].to_json)
45
+ http = stub 'http'
46
+ Puppet::Network::HttpPool.stubs(:http_instance).returns(http)
47
+ http.stubs(:get).with("/pdb/query/v4/resources?query=#{query}", subject.headers).returns response
48
+
49
+ Puppet.expects(:deprecation_warning).with do |msg|
50
+ msg =~ /Deprecated, yo\./
51
+ end
52
+
53
+ search("exec")
54
+ end
55
+
56
+ it "should fail it can't connect to the PuppetDB server" do
57
+ expect { search("user") }.to raise_error(Puppet::Error, /Could not retrieve resources/)
58
+ end
59
+
60
+ describe "with a matching resource" do
61
+
62
+ let (:query) {
63
+ ['and',
64
+ ['=', 'type', 'File'],
65
+ ['=', 'exported', true],
66
+ ['not', ['=', 'certname', host]]]
67
+ }
68
+
69
+ def make_resource_hash(name, certname="localhost", exported=true)
70
+ metadata = { :file => '/etc/puppet/manifests/site.pp',
71
+ :line => 10,
72
+ :exported => exported,
73
+ :certname => certname,
74
+ :hash => 'foobarbaz', }
75
+
76
+ res = Puppet::Type.type(:file).new(:title => File.expand_path(name),
77
+ :ensure => :present,
78
+ :mode => "0777")
79
+ params = res.to_hash
80
+ res_hash = metadata.merge(:type => res.type, :title => res.title)
81
+ res_hash.merge(:parameters => params)
82
+ end
83
+
84
+ def stub_response(resource_hashes)
85
+ body = resource_hashes.to_json
86
+
87
+ response = Net::HTTPOK.new('1.1', 200, 'OK')
88
+ response.stubs(:body).returns body
89
+
90
+ http = stub 'http'
91
+ Puppet::Network::HttpPool.stubs(:http_instance).returns(http)
92
+ http.stubs(:get).with("/pdb/query/v4/resources?query=#{CGI.escape(query.to_json)}", subject.headers).returns response
93
+ end
94
+
95
+ context "with resources from a single host" do
96
+ before :each do
97
+ stub_response([make_resource_hash('foo'), make_resource_hash('bar')])
98
+ end
99
+
100
+
101
+ it "should return a list of parser resources if any resources are found" do
102
+ found = search('File')
103
+ found.length.should == 2
104
+ found.each do |item|
105
+ item.should be_a(Puppet::Parser::Resource)
106
+ item.type.should == 'File'
107
+ item[:ensure].should == 'present'
108
+ item[:mode].should == '777'
109
+ end
110
+ end
111
+
112
+
113
+ it "should not filter resources that have been found before" do
114
+ search('File').should == search('File')
115
+ end
116
+ end
117
+
118
+ context "with resources from multiple hosts" do
119
+ before :each do
120
+ stub_response([make_resource_hash('foo', 'localhost'), make_resource_hash('foo', 'remotehost')])
121
+ end
122
+
123
+ it "should supply unique collector_id vals for resources collected from different hosts" do
124
+ found = search('File')
125
+ found.length.should == 2
126
+ found[0].collector_id.should_not == found[1].collector_id
127
+ end
128
+ end
129
+
130
+
131
+ end
132
+ end
133
+
134
+ describe "#build_expression" do
135
+ it "should return nil if there is no filter" do
136
+ subject.build_expression(nil).should == nil
137
+ end
138
+
139
+ it "should fail if the filter uses an illegal operator" do
140
+ expect do
141
+ subject.build_expression(['foo', 'xor', 'bar'])
142
+ end.to raise_error(Puppet::Error, /not supported/)
143
+ end
144
+
145
+ it "should return an equal query if the operator is '='" do
146
+ subject.build_expression(['param','==','value']).should == ['=',['parameter','param'],'value']
147
+ end
148
+
149
+ it "should return a not-equal query if the operator is '!='" do
150
+ subject.build_expression(['param','!=','value']).should == ['not', ['=', ['parameter','param'],'value']]
151
+ end
152
+
153
+ it "should handle title correctly" do
154
+ subject.build_expression(['title','==','value']).should == ['=', 'title', 'value']
155
+ end
156
+
157
+ it "should preserve the case of title queries" do
158
+ subject.build_expression(['title','==','VALUE']).should == ['=', 'title', 'VALUE']
159
+ end
160
+
161
+ it "should handle tag correctly" do
162
+ subject.build_expression(['tag','==','value']).should == ['=', 'tag', 'value']
163
+ end
164
+
165
+ it "should generate lowercase tag queries for case-insensitivity" do
166
+ subject.build_expression(['tag','==','VALUE']).should == ['=', 'tag', 'value']
167
+ end
168
+
169
+ it "should conjoin 'and' queries with 'and'" do
170
+ query = [['tag', '==', 'one'], 'and', ['tag', '==', 'two']]
171
+ subject.build_expression(query).should == ['and',
172
+ ['=', 'tag', 'one'],
173
+ ['=', 'tag', 'two']]
174
+ end
175
+
176
+ it "should conjoin 'or' queries with 'or'" do
177
+ query = [['tag', '==', 'one'], 'or', ['tag', '==', 'two']]
178
+ subject.build_expression(query).should == ['or',
179
+ ['=', 'tag', 'one'],
180
+ ['=', 'tag', 'two']]
181
+ end
182
+
183
+ it "should construct complex, nested queries" do
184
+ query = [[['tag', '==', 'one'], 'and', ['tag', '==', 'two']], 'or', ['tag', '!=', 'three']]
185
+ subject.build_expression(query).should == ['or',
186
+ ['and',
187
+ ['=', 'tag', 'one'],
188
+ ['=', 'tag', 'two']],
189
+ ['not',
190
+ ['=', 'tag', 'three']]]
191
+ end
192
+ end
193
+
194
+ describe "#headers" do
195
+ it "should accept the correct mime type" do
196
+ subject.headers['Accept'].should == 'application/json'
197
+ end
198
+ end
199
+ end