metasploit-model 0.25.7 → 0.26.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 (26) hide show
  1. checksums.yaml +8 -8
  2. data/app/models/metasploit/model/search/operation/association.rb +57 -0
  3. data/app/models/metasploit/model/search/operator/association.rb +24 -14
  4. data/app/models/metasploit/model/search/operator/base.rb +19 -3
  5. data/app/models/metasploit/model/search/operator/single.rb +21 -1
  6. data/app/models/metasploit/model/search/query.rb +20 -1
  7. data/lib/metasploit/model/association/tree.rb +130 -0
  8. data/lib/metasploit/model/search.rb +61 -27
  9. data/lib/metasploit/model/search/association.rb +152 -8
  10. data/lib/metasploit/model/search/attribute.rb +112 -22
  11. data/lib/metasploit/model/search/operator.rb +53 -1
  12. data/lib/metasploit/model/search/operator/help.rb +39 -1
  13. data/lib/metasploit/model/search/with.rb +44 -1
  14. data/lib/metasploit/model/version.rb +2 -2
  15. data/spec/app/models/metasploit/model/search/operation/association_spec.rb +67 -0
  16. data/spec/app/models/metasploit/model/search/operator/association_spec.rb +76 -76
  17. data/spec/app/models/metasploit/model/search/operator/deprecated/author_spec.rb +54 -18
  18. data/spec/app/models/metasploit/model/search/operator/deprecated/authority_spec.rb +20 -8
  19. data/spec/app/models/metasploit/model/search/operator/deprecated/platform_spec.rb +20 -8
  20. data/spec/app/models/metasploit/model/search/operator/deprecated/ref_spec.rb +86 -26
  21. data/spec/app/models/metasploit/model/search/operator/deprecated/text_spec.rb +63 -21
  22. data/spec/lib/metasploit/model/search/association/tree_spec.rb +385 -0
  23. data/spec/lib/metasploit/model/search/association_spec.rb +99 -10
  24. data/spec/lib/metasploit/model/search_spec.rb +48 -107
  25. data/spec/support/shared/examples/search/query/metasploit/model/search/operator/deprecated/authority.rb +19 -7
  26. metadata +7 -1
@@ -55,8 +55,8 @@ describe Metasploit::Model::Search::Operator::Deprecated::Platform do
55
55
  let(:platforms_fully_qualified_name_operator) do
56
56
  Metasploit::Model::Search::Operator::Association.new(
57
57
  :association => :platforms,
58
- :attribute_operator => platform_fully_qualified_name_operator,
59
- :klass => klass
58
+ :klass => klass,
59
+ :source_operator => platform_fully_qualified_name_operator
60
60
  )
61
61
  end
62
62
 
@@ -75,8 +75,8 @@ describe Metasploit::Model::Search::Operator::Deprecated::Platform do
75
75
  let(:targets_name_operator) do
76
76
  Metasploit::Model::Search::Operator::Association.new(
77
77
  :association => :targets,
78
- :attribute_operator => target_name_operator,
79
- :klass => klass
78
+ :klass => klass,
79
+ :source_operator => target_name_operator
80
80
  )
81
81
  end
82
82
 
@@ -94,8 +94,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Platform do
94
94
  child('platforms.fully_qualified_name')
95
95
  end
96
96
 
97
- it 'should use formatted value for value' do
98
- operation.value.should == formatted_value
97
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
98
+ subject(:source_operation) {
99
+ operation.source_operation
100
+ }
101
+
102
+ it 'uses formatted value for value' do
103
+ expect(source_operation.value).to eq(formatted_value)
104
+ end
99
105
  end
100
106
  end
101
107
 
@@ -104,8 +110,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Platform do
104
110
  child('targets.name')
105
111
  end
106
112
 
107
- it 'should use formatted value for value' do
108
- operation.value.should == formatted_value
113
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
114
+ subject(:source_operation) {
115
+ operation.source_operation
116
+ }
117
+
118
+ it 'uses formatted value for value' do
119
+ expect(source_operation.value).to eq(formatted_value)
120
+ end
109
121
  end
110
122
  end
111
123
  end
@@ -29,8 +29,8 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
29
29
  let(:authorities_abbreviation_operator) do
30
30
  Metasploit::Model::Search::Operator::Association.new(
31
31
  :association => :authorities,
32
- :attribute_operator => abbreviation_operator,
33
- :klass => klass
32
+ :klass => klass,
33
+ :source_operator => abbreviation_operator
34
34
  )
35
35
  end
36
36
 
@@ -49,16 +49,16 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
49
49
  let(:references_designation_operator) do
50
50
  Metasploit::Model::Search::Operator::Association.new(
51
51
  :association => :references,
52
- :attribute_operator => designation_operator,
53
- :klass => klass
52
+ :klass => klass,
53
+ :source_operator => designation_operator
54
54
  )
55
55
  end
56
56
 
57
57
  let(:references_url_operator) do
58
58
  Metasploit::Model::Search::Operator::Association.new(
59
59
  :association => :references,
60
- :attribute_operator => url_operator,
61
- :klass => klass
60
+ :klass => klass,
61
+ :source_operator => url_operator
62
62
  )
63
63
  end
64
64
 
@@ -124,8 +124,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
124
124
  child('references.url')
125
125
  end
126
126
 
127
- it "should use portion of formatted value after 'URL-' for value" do
128
- operation.value.should == tail
127
+ context 'Metasploit::Model::Search::Operation::Association' do
128
+ subject(:source_operation) {
129
+ operation.source_operation
130
+ }
131
+
132
+ it "uses portion of formatted value after 'URL-' for value" do
133
+ expect(source_operation.value).to eq(tail)
134
+ end
129
135
  end
130
136
  end
131
137
  end
@@ -160,8 +166,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
160
166
  child('references.designation')
161
167
  end
162
168
 
163
- it "should use portion of formatted value after '-' for value" do
164
- operation.value.should == tail
169
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
170
+ subject(:source_operation) {
171
+ operation.source_operation
172
+ }
173
+
174
+ it "uses portion of formatted value after '-' for value" do
175
+ expect(source_operation.value).to eq(tail)
176
+ end
165
177
  end
166
178
  end
167
179
 
@@ -182,8 +194,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
182
194
  child('authorities.abbreviation')
183
195
  end
184
196
 
185
- it "should use portion of formatted value before '-' as value" do
186
- operation.value.should == head
197
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
198
+ subject(:source_operation) {
199
+ operation.source_operation
200
+ }
201
+
202
+ it "uses portion of formatted value before '-' as value" do
203
+ expect(source_operation.value).to eq(head)
204
+ end
187
205
  end
188
206
  end
189
207
 
@@ -192,8 +210,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
192
210
  child('references.designation')
193
211
  end
194
212
 
195
- it "should use portion of formatted value after '-' as value" do
196
- operation.value.should == tail
213
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
214
+ subject(:source_operation) {
215
+ operation.source_operation
216
+ }
217
+
218
+ it "uses portion of formatted value after '-' as value" do
219
+ expect(source_operation.value).to eq(tail)
220
+ end
197
221
  end
198
222
  end
199
223
 
@@ -218,8 +242,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
218
242
  child('authorities.abbreviation')
219
243
  end
220
244
 
221
- it "should use portion of formatted value before '-' for value" do
222
- operation.value.should == head
245
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
246
+ subject(:source_operation) {
247
+ operation.source_operation
248
+ }
249
+
250
+ it "use portion of formatted value before '-' for value" do
251
+ expect(source_operation.value).to eq(head)
252
+ end
223
253
  end
224
254
  end
225
255
 
@@ -250,8 +280,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
250
280
  child('authorities.abbreviation')
251
281
  end
252
282
 
253
- it "should use portion of format value before '-' for value" do
254
- operation.value.should == head
283
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
284
+ subject(:source_operation) {
285
+ operation.source_operation
286
+ }
287
+
288
+ it "uses portion of format value before '-' for value" do
289
+ expect(source_operation.value).to eq(head)
290
+ end
255
291
  end
256
292
  end
257
293
 
@@ -260,8 +296,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
260
296
  child('references.designation')
261
297
  end
262
298
 
263
- it "should use portion of format value after '-' for value" do
264
- operation.value.should == tail
299
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
300
+ subject(:source_operation) {
301
+ operation.source_operation
302
+ }
303
+
304
+ it "uses portion of format value after '-' for value" do
305
+ expect(source_operation.value).to eq(tail)
306
+ end
265
307
  end
266
308
  end
267
309
 
@@ -297,8 +339,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
297
339
  child('authorities.abbreviation')
298
340
  end
299
341
 
300
- it 'should use formatted value for value' do
301
- operation.value.should == formatted_value
342
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
343
+ subject(:source_operation) {
344
+ operation.source_operation
345
+ }
346
+
347
+ it 'uses formatted value for value' do
348
+ expect(source_operation.value).to eq(formatted_value)
349
+ end
302
350
  end
303
351
  end
304
352
 
@@ -307,8 +355,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
307
355
  child('references.designation')
308
356
  end
309
357
 
310
- it 'should use formatted value for value' do
311
- operation.value.should == formatted_value
358
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
359
+ subject(:source_operation) {
360
+ operation.source_operation
361
+ }
362
+
363
+ it 'uses formatted value for value' do
364
+ expect(source_operation.value).to eq(formatted_value)
365
+ end
312
366
  end
313
367
  end
314
368
 
@@ -317,8 +371,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Ref do
317
371
  child('references.url')
318
372
  end
319
373
 
320
- it 'should use formatted value for value' do
321
- operation.value.should == formatted_value
374
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
375
+ subject(:source_operation) {
376
+ operation.source_operation
377
+ }
378
+
379
+ it 'uses formatted value for value' do
380
+ expect(source_operation.value).to eq(formatted_value)
381
+ end
322
382
  end
323
383
  end
324
384
  end
@@ -54,7 +54,7 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
54
54
  let(:actions_name_operator) do
55
55
  Metasploit::Model::Search::Operator::Association.new(
56
56
  :association => :actions,
57
- :attribute_operator => action_name_operator,
57
+ :source_operator => action_name_operator,
58
58
  :klass => klass
59
59
  )
60
60
  end
@@ -74,7 +74,7 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
74
74
  let(:architectures_abbreviation_operator) do
75
75
  Metasploit::Model::Search::Operator::Association.new(
76
76
  :association => :architectures,
77
- :attribute_operator => architecture_abbreviation_operator,
77
+ :source_operator => architecture_abbreviation_operator,
78
78
  :klass => klass
79
79
  )
80
80
  end
@@ -94,7 +94,7 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
94
94
  let(:authorities_abbreviation_operator) do
95
95
  Metasploit::Model::Search::Operator::Association.new(
96
96
  :association => :authorities,
97
- :attribute_operator => authority_abbreviation_operator,
97
+ :source_operator => authority_abbreviation_operator,
98
98
  :klass => klass
99
99
  )
100
100
  end
@@ -141,7 +141,7 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
141
141
  let(:platforms_fully_qualified_name_operator) do
142
142
  Metasploit::Model::Search::Operator::Association.new(
143
143
  :association => :platforms,
144
- :attribute_operator => platform_fully_qualified_name_operator,
144
+ :source_operator => platform_fully_qualified_name_operator,
145
145
  :klass => klass
146
146
  )
147
147
  end
@@ -167,7 +167,7 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
167
167
  let(:references_designation_operator) do
168
168
  Metasploit::Model::Search::Operator::Association.new(
169
169
  :association => :references,
170
- :attribute_operator => reference_designation_operator,
170
+ :source_operator => reference_designation_operator,
171
171
  :klass => klass
172
172
  )
173
173
  end
@@ -183,7 +183,7 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
183
183
  let(:references_url_operator) do
184
184
  Metasploit::Model::Search::Operator::Association.new(
185
185
  :association => :references,
186
- :attribute_operator => reference_url_operator,
186
+ :source_operator => reference_url_operator,
187
187
  :klass => klass
188
188
  )
189
189
  end
@@ -203,7 +203,7 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
203
203
  let(:targets_name_operator) do
204
204
  Metasploit::Model::Search::Operator::Association.new(
205
205
  :association => :targets,
206
- :attribute_operator => target_name_operator,
206
+ :source_operator => target_name_operator,
207
207
  :klass => klass
208
208
  )
209
209
  end
@@ -253,8 +253,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
253
253
  child('actions.name')
254
254
  end
255
255
 
256
- it 'should use formatted value for value' do
257
- operation.value.should == formatted_value
256
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
257
+ subject(:source_operation) {
258
+ operation.source_operation
259
+ }
260
+
261
+ it 'should use formatted value for value' do
262
+ expect(source_operation.value).to eq(formatted_value)
263
+ end
258
264
  end
259
265
  end
260
266
 
@@ -263,8 +269,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
263
269
  child('architectures.abbreviation')
264
270
  end
265
271
 
266
- it 'should use formatted value for value' do
267
- operation.value.should == formatted_value
272
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
273
+ subject(:source_operation) {
274
+ operation.source_operation
275
+ }
276
+
277
+ it 'should use formatted value for value' do
278
+ expect(source_operation.value).to eq(formatted_value)
279
+ end
268
280
  end
269
281
  end
270
282
 
@@ -289,8 +301,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
289
301
  grandchild('platforms.fully_qualified_name')
290
302
  end
291
303
 
292
- it 'should use formatted value for value' do
293
- grandchild_operation.value.should == formatted_value
304
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
305
+ subject(:source_operation) {
306
+ grandchild_operation.source_operation
307
+ }
308
+
309
+ it 'should use formatted value for value' do
310
+ expect(source_operation.value).to eq(formatted_value)
311
+ end
294
312
  end
295
313
  end
296
314
 
@@ -299,8 +317,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
299
317
  grandchild('targets.name')
300
318
  end
301
319
 
302
- it 'should use formatted value for value' do
303
- grandchild_operation.value.should == formatted_value
320
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
321
+ subject(:source_operation) {
322
+ grandchild_operation.source_operation
323
+ }
324
+
325
+ it 'should use formatted value for value' do
326
+ expect(source_operation.value).to eq(formatted_value)
327
+ end
304
328
  end
305
329
  end
306
330
  end
@@ -327,8 +351,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
327
351
  grandchild('authorities.abbreviation')
328
352
  end
329
353
 
330
- it 'should use formatted value for value' do
331
- grandchild_operation.value.should == formatted_value
354
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
355
+ subject(:source_operation) {
356
+ grandchild_operation.source_operation
357
+ }
358
+
359
+ it 'should use formatted value for value' do
360
+ expect(source_operation.value).to eq(formatted_value)
361
+ end
332
362
  end
333
363
  end
334
364
 
@@ -337,8 +367,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
337
367
  grandchild('references.designation')
338
368
  end
339
369
 
340
- it 'should use formatted value for value' do
341
- grandchild_operation.value.should == formatted_value
370
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
371
+ subject(:source_operation) {
372
+ grandchild_operation.source_operation
373
+ }
374
+
375
+ it 'should use formatted value for value' do
376
+ expect(source_operation.value).to eq(formatted_value)
377
+ end
342
378
  end
343
379
  end
344
380
 
@@ -347,8 +383,14 @@ describe Metasploit::Model::Search::Operator::Deprecated::Text do
347
383
  grandchild('references.url')
348
384
  end
349
385
 
350
- it 'should use formatted value for value' do
351
- grandchild_operation.value.should == formatted_value
386
+ context 'Metasploit::Model::Search::Operation::Association#source_operation' do
387
+ subject(:source_operation) {
388
+ grandchild_operation.source_operation
389
+ }
390
+
391
+ it 'should use formatted value for value' do
392
+ expect(source_operation.value).to eq(formatted_value)
393
+ end
352
394
  end
353
395
  end
354
396
  end
@@ -0,0 +1,385 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metasploit::Model::Association::Tree do
4
+ context 'expand' do
5
+ subject(:expand) {
6
+ described_class.expand(associations)
7
+ }
8
+
9
+ context 'with Array<Hash>' do
10
+ let(:associations) {
11
+ [
12
+ {
13
+ first_parent: :first_child
14
+ },
15
+ {
16
+ second_parent: :second_child
17
+ }
18
+ ]
19
+ }
20
+
21
+ it 'merges hashes' do
22
+ expect(expand).to have_key(:first_parent)
23
+
24
+ first_child_tree = expand[:first_parent]
25
+
26
+ expect(first_child_tree).to have_key(:first_child)
27
+ expect(first_child_tree[:first_child]).to be_nil
28
+
29
+ expect(expand).to have_key(:second_parent)
30
+
31
+ second_child_tree = expand[:second_parent]
32
+
33
+ expect(second_child_tree).to have_key(:second_child)
34
+ expect(second_child_tree[:second_child]).to be_nil
35
+ end
36
+ end
37
+
38
+
39
+ context 'with Array<Symbol>' do
40
+ let(:associations) {
41
+ [
42
+ :first,
43
+ :second
44
+ ]
45
+ }
46
+
47
+ it 'expands to Hash{Symbol => nil}' do
48
+ expect(expand).to have_key(:first)
49
+ expect(expand[:first]).to be_nil
50
+
51
+ expect(expand).to have_key(:second)
52
+ expect(expand[:second]).to be_nil
53
+ end
54
+ end
55
+
56
+ context 'with Hash<Symbol>' do
57
+ let(:associations) {
58
+ {
59
+ parent: :child
60
+ }
61
+ }
62
+
63
+ it 'expands to Hash{Symbol => Hash{Symbol => nil}}' do
64
+ expect(expand).to have_key(:parent)
65
+
66
+ child_tree = expand[:parent]
67
+
68
+ expect(child_tree).to have_key(:child)
69
+ expect(child_tree[:child]).to be_nil
70
+ end
71
+ end
72
+
73
+ context 'with Symbol' do
74
+ let(:associations) {
75
+ :symbol
76
+ }
77
+
78
+ it 'expands to Hash{Symbol => nil}' do
79
+ expect(expand).to have_key(:symbol)
80
+ expect(expand[:symbol]).to be_nil
81
+ end
82
+ end
83
+ end
84
+
85
+ context 'merge' do
86
+ subject(:merge) {
87
+ described_class.merge(first, second)
88
+ }
89
+
90
+ context 'first' do
91
+ context 'with nil' do
92
+ let(:first) {
93
+ nil
94
+ }
95
+
96
+ context 'second' do
97
+ context 'with nil' do
98
+ let(:second) {
99
+ nil
100
+ }
101
+
102
+ it { should be_nil }
103
+ end
104
+
105
+ context 'without nil' do
106
+ let(:second) {
107
+ double('second')
108
+ }
109
+
110
+ it 'returns second' do
111
+ expect(merge).to eq(second)
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ context 'without nil' do
118
+ let(:first) {
119
+ {
120
+ common: {
121
+ first_common_child: nil
122
+ },
123
+ first: {
124
+ first_child: nil
125
+ }
126
+ }
127
+ }
128
+
129
+ context 'second' do
130
+ context 'with nil' do
131
+ let(:second) {
132
+ nil
133
+ }
134
+
135
+ it 'returns first' do
136
+ expect(merge).to eq(first)
137
+ end
138
+ end
139
+
140
+ context 'without nil' do
141
+ let(:second) {
142
+ {
143
+ common: {
144
+ second_common_child: nil
145
+ },
146
+ second: {
147
+ second_child: nil
148
+ }
149
+ }
150
+ }
151
+
152
+ it 'merges trees under common keys' do
153
+ expect(merge).to have_key(:common)
154
+
155
+ common_tree = merge[:common]
156
+
157
+ expect(common_tree).to have_key(:first_common_child)
158
+ expect(common_tree[:first_common_child]).to be_nil
159
+ expect(common_tree).to have_key(:second_common_child)
160
+ expect(common_tree[:second_common_child]).to be_nil
161
+ end
162
+
163
+ it 'reuses uncommon keys' do
164
+ expect(merge[:first]).to eq(first[:first])
165
+ expect(merge[:second]).to eq(second[:second])
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ context 'operators' do
174
+ subject(:operators) {
175
+ described_class.operators(
176
+ expanded,
177
+ class: klass
178
+ )
179
+ }
180
+
181
+ let(:near_class) {
182
+ Class.new {
183
+ include Metasploit::Model::Search
184
+
185
+ search_attribute :near_boolean,
186
+ type: :boolean
187
+ search_attribute :near_string,
188
+ type: :string
189
+ }.tap { |klass|
190
+ stub_const('NearClass', klass)
191
+ }
192
+ }
193
+
194
+ let(:klass) {
195
+ near_class = self.near_class
196
+
197
+ Class.new do
198
+ include Metasploit::Model::Association
199
+
200
+ association :near_classes,
201
+ class_name: near_class.name
202
+ end
203
+ }
204
+
205
+ context 'with Hash{Symbol => nil}' do
206
+ let(:expanded) {
207
+ {
208
+ near_classes: nil
209
+ }
210
+ }
211
+
212
+ it 'includes a Metasploit::Model::Search::Operator::Association for each non-association operator on the associated class' do
213
+ near_classes_near_boolean = operators.find { |o| o.name == :'near_classes.near_boolean' }
214
+
215
+ expect(near_classes_near_boolean).to be_a Metasploit::Model::Search::Operator::Association
216
+ expect(near_classes_near_boolean.association).to eq(:near_classes)
217
+ expect(near_classes_near_boolean.klass).to eq(klass)
218
+
219
+ near_boolean = near_classes_near_boolean.source_operator
220
+
221
+ expect(near_boolean).to eq(near_class.search_operator_by_name.fetch(:near_boolean))
222
+
223
+ near_classes_near_string = operators.find { |o| o.name == :'near_classes.near_string' }
224
+
225
+ expect(near_classes_near_string).to be_a Metasploit::Model::Search::Operator::Association
226
+ expect(near_classes_near_string.association).to eq(:near_classes)
227
+ expect(near_classes_near_string.klass).to eq(klass)
228
+
229
+ near_string = near_classes_near_string.source_operator
230
+
231
+ expect(near_string).to eq(near_class.search_operator_by_name.fetch(:near_string))
232
+ end
233
+ end
234
+
235
+ context 'with Hash{Symbol => Hash}' do
236
+ let(:expanded) {
237
+ {
238
+ near_classes: {
239
+ far_class: nil
240
+ }
241
+ }
242
+ }
243
+
244
+ let(:far_class) {
245
+ Class.new {
246
+ include Metasploit::Model::Search
247
+
248
+ search_attribute :far_integer,
249
+ type: :integer
250
+ }.tap { |klass|
251
+ stub_const('FarClass', klass)
252
+ }
253
+ }
254
+
255
+ let(:near_class) {
256
+ super().tap { |klass|
257
+ far_class = self.far_class
258
+
259
+ klass.class_eval do
260
+ include Metasploit::Model::Association
261
+
262
+ association :far_class,
263
+ class_name: far_class.name
264
+ end
265
+ }
266
+ }
267
+
268
+ it 'includes a Metasploit::Model::Search::Operator::Association for each non-association operator on the near class' do
269
+ near_classes_near_boolean = operators.find { |o| o.name == :'near_classes.near_boolean' }
270
+
271
+ expect(near_classes_near_boolean).to be_a Metasploit::Model::Search::Operator::Association
272
+ expect(near_classes_near_boolean.association).to eq(:near_classes)
273
+ expect(near_classes_near_boolean.klass).to eq(klass)
274
+
275
+ near_boolean = near_classes_near_boolean.source_operator
276
+
277
+ expect(near_boolean).to eq(near_class.search_operator_by_name.fetch(:near_boolean))
278
+
279
+ near_classes_near_string = operators.find { |o| o.name == :'near_classes.near_string' }
280
+
281
+ expect(near_classes_near_string).to be_a Metasploit::Model::Search::Operator::Association
282
+ expect(near_classes_near_string.association).to eq(:near_classes)
283
+ expect(near_classes_near_string.klass).to eq(klass)
284
+
285
+ near_string = near_classes_near_string.source_operator
286
+
287
+ expect(near_string).to eq(near_class.search_operator_by_name.fetch(:near_string))
288
+ end
289
+
290
+ it 'includes Metasploit::Model::Search::Operator::Association for each non-association operator on the far class' do
291
+ near_classes_far_class_far_integer = operators.find { |o| o.name == :'near_classes.far_class.far_integer' }
292
+
293
+ expect(near_classes_far_class_far_integer).to be_a Metasploit::Model::Search::Operator::Association
294
+ expect(near_classes_far_class_far_integer.association).to eq(:near_classes)
295
+ expect(near_classes_far_class_far_integer.klass).to eq(klass)
296
+
297
+ far_class_far_integer = near_classes_far_class_far_integer.source_operator
298
+
299
+ expect(far_class_far_integer).to be_a Metasploit::Model::Search::Operator::Association
300
+ expect(far_class_far_integer.association).to eq(:far_class)
301
+ expect(far_class_far_integer.klass).to eq(near_class)
302
+
303
+ far_integer = far_class_far_integer.source_operator
304
+
305
+ expect(far_integer).to eq(far_class.search_operator_by_name.fetch(:far_integer))
306
+ end
307
+ end
308
+
309
+ context 'with nil' do
310
+ let(:expanded) {
311
+ nil
312
+ }
313
+
314
+ it { should == [] }
315
+ end
316
+ end
317
+
318
+
319
+ context 'reflect_on_association_on_class' do
320
+ subject(:reflect_on_association_on_class) {
321
+ described_class.reflect_on_association_on_class(association, klass)
322
+ }
323
+
324
+ let(:association) {
325
+ :associated_things
326
+ }
327
+
328
+ let(:klass) {
329
+ Class.new
330
+ }
331
+
332
+ context 'klass' do
333
+ context 'responds to reflect_on_association' do
334
+ let(:klass) {
335
+ super().tap { |klass|
336
+ klass.send(:include, Metasploit::Model::Association)
337
+ }
338
+ }
339
+
340
+ context 'with association' do
341
+ #
342
+ # lets
343
+ #
344
+
345
+ let(:associated_class) {
346
+ Class.new.tap { |klass|
347
+ stub_const('AssociatedThing', klass)
348
+ }
349
+ }
350
+
351
+ let(:klass) {
352
+ super().tap { |klass|
353
+ klass.association association, class_name: associated_class.name
354
+ }
355
+ }
356
+
357
+ it 'returns reflection with associated class as klass' do
358
+ expect(reflect_on_association_on_class.klass).to eq(associated_class)
359
+ end
360
+ end
361
+
362
+ context 'without association' do
363
+ it 'raises a Metasploit::Model::Association::Error on association and klass' do
364
+ expect {
365
+ reflect_on_association_on_class
366
+ }.to raise_error(Metasploit::Model::Association::Error) { |error|
367
+ expect(error.model).to eq(klass)
368
+ expect(error.name).to eq(association)
369
+ }
370
+ end
371
+ end
372
+ end
373
+
374
+ context 'does not respond to reflect_on_association' do
375
+ it 'raises NameError with instructions for using Metasploit::Model::Association' do
376
+ expect {
377
+ reflect_on_association_on_class
378
+ }.to raise_error(NameError) { |error|
379
+ expect(error.message).to include 'Metasploit::Model::Association'
380
+ }
381
+ end
382
+ end
383
+ end
384
+ end
385
+ end