ronin-code-sql 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.document +4 -0
  3. data/.editorconfig +11 -0
  4. data/.github/workflows/ruby.yml +27 -0
  5. data/.gitignore +11 -0
  6. data/.mailmap +1 -0
  7. data/.rspec +1 -0
  8. data/.ruby-version +1 -0
  9. data/.yardopts +1 -0
  10. data/COPYING.txt +165 -0
  11. data/ChangeLog.md +104 -0
  12. data/Gemfile +28 -0
  13. data/README.md +212 -0
  14. data/Rakefile +30 -0
  15. data/gemspec.yml +25 -0
  16. data/lib/ronin/code/sql/binary_expr.rb +53 -0
  17. data/lib/ronin/code/sql/clause.rb +74 -0
  18. data/lib/ronin/code/sql/clauses.rb +310 -0
  19. data/lib/ronin/code/sql/emittable.rb +88 -0
  20. data/lib/ronin/code/sql/emitter.rb +406 -0
  21. data/lib/ronin/code/sql/field.rb +110 -0
  22. data/lib/ronin/code/sql/fields.rb +82 -0
  23. data/lib/ronin/code/sql/function.rb +53 -0
  24. data/lib/ronin/code/sql/functions.rb +1265 -0
  25. data/lib/ronin/code/sql/injection.rb +168 -0
  26. data/lib/ronin/code/sql/injection_expr.rb +113 -0
  27. data/lib/ronin/code/sql/literal.rb +40 -0
  28. data/lib/ronin/code/sql/literals.rb +83 -0
  29. data/lib/ronin/code/sql/operators.rb +384 -0
  30. data/lib/ronin/code/sql/statement.rb +72 -0
  31. data/lib/ronin/code/sql/statement_list.rb +112 -0
  32. data/lib/ronin/code/sql/statements.rb +117 -0
  33. data/lib/ronin/code/sql/unary_expr.rb +38 -0
  34. data/lib/ronin/code/sql/version.rb +28 -0
  35. data/lib/ronin/code/sql.rb +96 -0
  36. data/ronin-code-sql.gemspec +62 -0
  37. data/spec/spec_helper.rb +3 -0
  38. data/spec/sql/binary_expr_examples.rb +25 -0
  39. data/spec/sql/binary_expr_spec.rb +5 -0
  40. data/spec/sql/clause_examples.rb +43 -0
  41. data/spec/sql/clause_spec.rb +31 -0
  42. data/spec/sql/clauses_spec.rb +47 -0
  43. data/spec/sql/emittable_spec.rb +41 -0
  44. data/spec/sql/emitter_spec.rb +533 -0
  45. data/spec/sql/field_spec.rb +103 -0
  46. data/spec/sql/fields_spec.rb +40 -0
  47. data/spec/sql/function_examples.rb +30 -0
  48. data/spec/sql/function_spec.rb +25 -0
  49. data/spec/sql/functions_spec.rb +113 -0
  50. data/spec/sql/injection_expr_spec.rb +98 -0
  51. data/spec/sql/injection_spec.rb +172 -0
  52. data/spec/sql/literal_spec.rb +5 -0
  53. data/spec/sql/literals_spec.rb +46 -0
  54. data/spec/sql/operators_spec.rb +44 -0
  55. data/spec/sql/statement_examples.rb +39 -0
  56. data/spec/sql/statement_list_spec.rb +48 -0
  57. data/spec/sql/statement_spec.rb +38 -0
  58. data/spec/sql/statements_spec.rb +22 -0
  59. data/spec/sql/unary_expr_examples.rb +20 -0
  60. data/spec/sql/unary_expr_spec.rb +5 -0
  61. data/spec/sql_spec.rb +18 -0
  62. metadata +157 -0
@@ -0,0 +1,533 @@
1
+ require 'spec_helper'
2
+ require 'ronin/code/sql/literal'
3
+ require 'ronin/code/sql/unary_expr'
4
+ require 'ronin/code/sql/binary_expr'
5
+ require 'ronin/code/sql/field'
6
+ require 'ronin/code/sql/statement'
7
+ require 'ronin/code/sql/statement_list'
8
+ require 'ronin/code/sql/emitter'
9
+
10
+ describe Ronin::Code::SQL::Emitter do
11
+ describe "#initialize" do
12
+ context "without options" do
13
+ it { expect(subject.space).to eq(' ') }
14
+ it { expect(subject.quotes).to eq(:single) }
15
+ end
16
+ end
17
+
18
+ describe "#emit_keyword" do
19
+ context "when passed an Array of Symbols" do
20
+ let(:keywords) { [:DROP, :TABLE] }
21
+
22
+ it "should join the keywords" do
23
+ expect(subject.emit_keyword(keywords)).to eq("DROP TABLE")
24
+ end
25
+
26
+ context "when :space is set" do
27
+ subject { described_class.new(space: '/**/') }
28
+
29
+ it "should join the keywords" do
30
+ expect(subject.emit_keyword(keywords)).to eq("DROP/**/TABLE")
31
+ end
32
+ end
33
+ end
34
+
35
+ context "when case is :upper" do
36
+ let(:keyword) { :select }
37
+
38
+ subject { described_class.new(case: :upper) }
39
+
40
+ it "should upcase the keyword" do
41
+ expect(subject.emit_keyword(keyword)).to eq('SELECT')
42
+ end
43
+ end
44
+
45
+ context "when case is :lower" do
46
+ let(:keyword) { :SELECT }
47
+
48
+ subject { described_class.new(case: :lower) }
49
+
50
+ it "should upcase the keyword" do
51
+ expect(subject.emit_keyword(keyword)).to eq('select')
52
+ end
53
+ end
54
+
55
+ context "when case is :random" do
56
+ let(:keyword) { :select }
57
+
58
+ subject { described_class.new(case: :random) }
59
+
60
+ it "should contain at least one upper-case character" do
61
+ expect(subject.emit_keyword(keyword)).to match(/[SELECT]/)
62
+ end
63
+ end
64
+
65
+ context "when case is nil" do
66
+ subject { described_class.new(case: nil) }
67
+
68
+ let(:keyword) { 'Select' }
69
+
70
+ it "should emit the keyword as is" do
71
+ expect(subject.emit_keyword(keyword)).to eq(keyword)
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#emit_operator" do
77
+ context "when the operator is a symbol" do
78
+ it "should emit a String" do
79
+ expect(subject.emit_operator(:"!=")).to eq('!=')
80
+ end
81
+ end
82
+
83
+ context "otherwise" do
84
+ subject { described_class.new(case: :lower) }
85
+
86
+ it "should emit a keyword" do
87
+ expect(subject.emit_operator(:AS)).to eq('as')
88
+ end
89
+ end
90
+ end
91
+
92
+ describe "#emit_null" do
93
+ it "should emit the NULL keyword" do
94
+ expect(subject.emit_null).to eq('NULL')
95
+ end
96
+ end
97
+
98
+ describe "#emit_false" do
99
+ it "should emit 1=0" do
100
+ expect(subject.emit_false).to eq('1=0')
101
+ end
102
+ end
103
+
104
+ describe "#emit_true" do
105
+ it "should emit 1=1" do
106
+ expect(subject.emit_true).to eq('1=1')
107
+ end
108
+ end
109
+
110
+ describe "#emit_integer" do
111
+ it "should emit a String" do
112
+ expect(subject.emit_integer(10)).to eq('10')
113
+ end
114
+ end
115
+
116
+ describe "#emit_decimal" do
117
+ it "should emit a String" do
118
+ expect(subject.emit_decimal(2.5)).to eq('2.5')
119
+ end
120
+ end
121
+
122
+ describe "#emit_string" do
123
+ it "should emit a String" do
124
+ expect(subject.emit_string("O'Brian")).to eq("'O''Brian'")
125
+ end
126
+
127
+ context "when :quotes is :double" do
128
+ subject { described_class.new(quotes: :double) }
129
+
130
+ it "should double quote Strings" do
131
+ expect(subject.emit_string("O'Brian")).to eq("\"O'Brian\"")
132
+ end
133
+ end
134
+ end
135
+
136
+ describe "#emit_field" do
137
+ subject { described_class.new(case: :upper) }
138
+
139
+ let(:field) { Ronin::Code::SQL::Field.new(:id) }
140
+
141
+ it "should emit the name as a keyword" do
142
+ expect(subject.emit_field(field)).to eq('ID')
143
+ end
144
+
145
+ context "when the field has a parent" do
146
+ let(:parent) { Ronin::Code::SQL::Field.new(:users) }
147
+ let(:field) { Ronin::Code::SQL::Field.new(:id,parent) }
148
+
149
+ it "should emit the parent then the field name" do
150
+ expect(subject.emit_field(field)).to eq('USERS.ID')
151
+ end
152
+ end
153
+ end
154
+
155
+ describe "#emit_list" do
156
+ it "should emit a ',' separated list" do
157
+ expect(subject.emit_list([1,2,3,'foo'])).to eq("(1,2,3,'foo')")
158
+ end
159
+ end
160
+
161
+ describe "#emit_assignments" do
162
+ let(:values) { {x: 1, y: 2} }
163
+
164
+ it "should emit a list of column names and values" do
165
+ expect(subject.emit_assignments(values)).to eq('x=1,y=2')
166
+ end
167
+ end
168
+
169
+ describe "#emit_argument" do
170
+ context "when the value is a Statement" do
171
+ let(:stmt) { Ronin::Code::SQL::Statement.new(:SELECT,1) }
172
+
173
+ it "should wrap the statement in ( )" do
174
+ expect(subject.emit_argument(stmt)).to eq('(SELECT 1)')
175
+ end
176
+ end
177
+
178
+ context "otherwise" do
179
+ let(:value) { 'hello' }
180
+
181
+ it "should emit the value" do
182
+ expect(subject.emit_argument(value)).to eq("'hello'")
183
+ end
184
+ end
185
+ end
186
+
187
+ describe "#emit_expression" do
188
+ context "when the expression is a BinaryExpr" do
189
+ context "when the operator is alphabetic" do
190
+ subject { described_class.new(case: :upper) }
191
+
192
+ let(:expr) { Ronin::Code::SQL::BinaryExpr.new(:id,:is,1) }
193
+
194
+ it "should emit the operands and operator as a keyword with spaces" do
195
+ expect(subject.emit_expression(expr)).to eq('ID IS 1')
196
+ end
197
+ end
198
+
199
+ context "when the operator is symbolic" do
200
+ let(:expr) { Ronin::Code::SQL::BinaryExpr.new(:id,:"=",1) }
201
+
202
+ it "should emit the operands and operator without spaces" do
203
+ expect(subject.emit_expression(expr)).to eq('id=1')
204
+ end
205
+ end
206
+
207
+ context "when the left-hand operand is a Statement" do
208
+ let(:expr) do
209
+ Ronin::Code::SQL::BinaryExpr.new(
210
+ Ronin::Code::SQL::Statement.new(:SELECT,1), :"=", 1
211
+ )
212
+ end
213
+
214
+ it "should wrap the left-hand operand in parenthesis" do
215
+ expect(subject.emit_expression(expr)).to eq('(SELECT 1)=1')
216
+ end
217
+ end
218
+
219
+ context "when the right-hand operand is a Statement" do
220
+ let(:expr) do
221
+ Ronin::Code::SQL::BinaryExpr.new(
222
+ 1, :"=", Ronin::Code::SQL::Statement.new(:SELECT,1)
223
+ )
224
+ end
225
+
226
+ it "should wrap the left-hand operand in parenthesis" do
227
+ expect(subject.emit_expression(expr)).to eq('1=(SELECT 1)')
228
+ end
229
+ end
230
+ end
231
+
232
+ context "when the expression is a UnaryExpr" do
233
+ context "when the operator is upper-case alpha" do
234
+ let(:expr) { Ronin::Code::SQL::UnaryExpr.new(:NOT,:admin) }
235
+
236
+ it "should emit the operand and operator with spaces" do
237
+ expect(subject.emit_expression(expr)).to eq('NOT admin')
238
+ end
239
+ end
240
+
241
+ context "when the operator is symbolic" do
242
+ let(:expr) { Ronin::Code::SQL::UnaryExpr.new(:"-",1) }
243
+
244
+ it "should emit the operand and operator without spaces" do
245
+ expect(subject.emit_expression(expr)).to eq('-1')
246
+ end
247
+ end
248
+
249
+ context "when the operand is a Statement" do
250
+ let(:expr) do
251
+ Ronin::Code::SQL::UnaryExpr.new(
252
+ :NOT, Ronin::Code::SQL::Statement.new(:SELECT,1)
253
+ )
254
+ end
255
+
256
+ it "should wrap the operand in parenthesis" do
257
+ expect(subject.emit_expression(expr)).to eq('NOT (SELECT 1)')
258
+ end
259
+ end
260
+ end
261
+ end
262
+
263
+ describe "#emit_function" do
264
+ let(:func) { Ronin::Code::SQL::Function.new(:NOW) }
265
+
266
+ it "should emit the function name as a keyword" do
267
+ expect(subject.emit_function(func)).to eq('NOW()')
268
+ end
269
+
270
+ context "with arguments" do
271
+ let(:func) { Ronin::Code::SQL::Function.new(:MAX,1,2) }
272
+
273
+ it "should emit the function arguments" do
274
+ expect(subject.emit_function(func)).to eq('MAX(1,2)')
275
+ end
276
+ end
277
+ end
278
+
279
+ describe "#emit" do
280
+ context "when passed nil" do
281
+ it "should emit the NULL keyword" do
282
+ expect(subject.emit(nil)).to eq('NULL')
283
+ end
284
+ end
285
+
286
+ context "when passed true" do
287
+ it "should emit true" do
288
+ expect(subject.emit(true)).to eq('1=1')
289
+ end
290
+ end
291
+
292
+ context "when passed false" do
293
+ it "should emit false" do
294
+ expect(subject.emit(false)).to eq('1=0')
295
+ end
296
+ end
297
+
298
+ context "when passed an Integer" do
299
+ it "should emit an integer" do
300
+ expect(subject.emit(10)).to eq('10')
301
+ end
302
+ end
303
+
304
+ context "when passed a Float" do
305
+ it "should emit a decimal" do
306
+ expect(subject.emit(2.5)).to eq('2.5')
307
+ end
308
+ end
309
+
310
+ context "when passed a String" do
311
+ it "should emit a string" do
312
+ expect(subject.emit("O'Brian")).to eq("'O''Brian'")
313
+ end
314
+ end
315
+
316
+ context "when passed a Literal" do
317
+ let(:literal) { Ronin::Code::SQL::Literal.new(42) }
318
+
319
+ it "should emit the value" do
320
+ expect(subject.emit(literal)).to eq('42')
321
+ end
322
+ end
323
+
324
+ context "when passed a Field" do
325
+ let(:table) { Ronin::Code::SQL::Field.new(:users) }
326
+ let(:column) { Ronin::Code::SQL::Field.new(:id,table) }
327
+
328
+ it "should emit a field" do
329
+ expect(subject.emit(column)).to eq('users.id')
330
+ end
331
+ end
332
+
333
+ context "when passed a Symbol" do
334
+ it "should emit a field" do
335
+ expect(subject.emit(:id)).to eq('id')
336
+ end
337
+ end
338
+
339
+ context "when passed an Array" do
340
+ it "should emit a list" do
341
+ expect(subject.emit([1,2,3,'foo'])).to eq("(1,2,3,'foo')")
342
+ end
343
+ end
344
+
345
+ context "when passed a Hash" do
346
+ it "should emit a list of assignments" do
347
+ expect(subject.emit(x: 1, y: 2)).to eq('x=1,y=2')
348
+ end
349
+ end
350
+
351
+ context "when passed a BinaryExpr" do
352
+ let(:expr) { Ronin::Code::SQL::BinaryExpr.new(:id,:"=",1) }
353
+
354
+ it "should emit an expression" do
355
+ expect(subject.emit(expr)).to eq('id=1')
356
+ end
357
+ end
358
+
359
+ context "when passed a UnaryExpr" do
360
+ let(:expr) { Ronin::Code::SQL::UnaryExpr.new(:NOT,:admin) }
361
+
362
+ it "should emit an expression" do
363
+ expect(subject.emit(expr)).to eq('NOT admin')
364
+ end
365
+ end
366
+
367
+ context "when passed a Function" do
368
+ let(:func) { Ronin::Code::SQL::Function.new(:MAX,1,2) }
369
+
370
+ it "should emit the function" do
371
+ expect(subject.emit(func)).to eq('MAX(1,2)')
372
+ end
373
+ end
374
+
375
+ context "when passed a Statment" do
376
+ let(:stmt) { Ronin::Code::SQL::Statement.new(:SELECT,1) }
377
+
378
+ it "should emit a statement" do
379
+ expect(subject.emit(stmt)).to eq('SELECT 1')
380
+ end
381
+ end
382
+
383
+ context "when the object responds to #to_sql" do
384
+ let(:object) { double(:sql_object) }
385
+ let(:sql) { "EXEC sp_configure 'xp_cmdshell', 0;" }
386
+
387
+ it "should call #to_sql" do
388
+ allow(object).to receive(:to_sql).and_return(sql)
389
+
390
+ expect(subject.emit(object)).to eq(sql)
391
+ end
392
+ end
393
+
394
+ context "otherwise" do
395
+ let(:object) { Object.new }
396
+
397
+ it "should raise an ArgumentError" do
398
+ expect {
399
+ subject.emit(object)
400
+ }.to raise_error(ArgumentError)
401
+ end
402
+ end
403
+ end
404
+
405
+ describe "#emit_clause" do
406
+ let(:clause) { Ronin::Code::SQL::Clause.new(:"NOT INDEXED") }
407
+
408
+ it "should emit the clause keyword" do
409
+ expect(subject.emit_clause(clause)).to eq("NOT INDEXED")
410
+ end
411
+
412
+ context "with an argument" do
413
+ let(:argument) { 100 }
414
+ let(:clause) { Ronin::Code::SQL::Clause.new(:LIMIT,argument) }
415
+
416
+ it "should also emit the clause argument" do
417
+ expect(subject.emit_clause(clause)).to eq("LIMIT #{argument}")
418
+ end
419
+ end
420
+
421
+ context "with custom :space" do
422
+ subject { described_class.new(space: '/**/') }
423
+
424
+ let(:clause) { Ronin::Code::SQL::Clause.new(:LIMIT,100) }
425
+
426
+ it "should emit the custom white-space deliminater" do
427
+ expect(subject.emit_clause(clause)).to eq('LIMIT/**/100')
428
+ end
429
+ end
430
+ end
431
+
432
+ describe "#emit_clauses" do
433
+ let(:clauses) do
434
+ [
435
+ Ronin::Code::SQL::Clause.new(:LIMIT, 100),
436
+ Ronin::Code::SQL::Clause.new(:OFFSET, 10)
437
+ ]
438
+ end
439
+
440
+ it "should emit multiple clauses" do
441
+ expect(subject.emit_clauses(clauses)).to eq('LIMIT 100 OFFSET 10')
442
+ end
443
+
444
+ context "with custom :space" do
445
+ subject { described_class.new(space: '/**/') }
446
+
447
+ it "should emit the custom white-space deliminater" do
448
+ expect(subject.emit_clauses(clauses)).to eq('LIMIT/**/100/**/OFFSET/**/10')
449
+ end
450
+ end
451
+ end
452
+
453
+ describe "#emit_statement" do
454
+ subject { described_class.new(case: :lower) }
455
+
456
+ context "without an argument" do
457
+ let(:stmt) { Ronin::Code::SQL::Statement.new(:SELECT) }
458
+
459
+ it "should emit the statment keyword" do
460
+ expect(subject.emit_statement(stmt)).to eq('select')
461
+ end
462
+ end
463
+
464
+ context "with an argument" do
465
+ let(:stmt) { Ronin::Code::SQL::Statement.new(:SELECT,1) }
466
+
467
+ it "should emit the statment argument" do
468
+ expect(subject.emit_statement(stmt)).to eq('select 1')
469
+ end
470
+
471
+ context "when the argument is an Array" do
472
+ let(:stmt) { Ronin::Code::SQL::Statement.new(:SELECT,[1,2,3]) }
473
+
474
+ it "should emit a list" do
475
+ expect(subject.emit_statement(stmt)).to eq('select (1,2,3)')
476
+ end
477
+
478
+ context "with only one element" do
479
+ let(:stmt) { Ronin::Code::SQL::Statement.new(:SELECT,[1]) }
480
+
481
+ it "should emit the element" do
482
+ expect(subject.emit_statement(stmt)).to eq('select 1')
483
+ end
484
+ end
485
+ end
486
+
487
+ context "with custom :space" do
488
+ subject { described_class.new(case: :lower, space: '/**/') }
489
+
490
+ it "should emit the custom white-space deliminater" do
491
+ expect(subject.emit_statement(stmt)).to eq('select/**/1')
492
+ end
493
+ end
494
+ end
495
+
496
+ context "with clauses" do
497
+ let(:stmt) { Ronin::Code::SQL::Statement.new(:SELECT,1).offset(1).limit(100) }
498
+
499
+ it "should emit the statment argument" do
500
+ expect(subject.emit_statement(stmt)).to eq('select 1 offset 1 limit 100')
501
+ end
502
+
503
+ context "with custom :space" do
504
+ subject { described_class.new(case: :lower, space: '/**/') }
505
+
506
+ it "should emit the custom white-space deliminater" do
507
+ expect(subject.emit_statement(stmt)).to eq('select/**/1/**/offset/**/1/**/limit/**/100')
508
+ end
509
+ end
510
+ end
511
+ end
512
+
513
+ describe "#emit_statement_list" do
514
+ let(:stmts) do
515
+ sql = Ronin::Code::SQL::StatementList.new
516
+ sql << Ronin::Code::SQL::Statement.new(:SELECT, 1)
517
+ sql << Ronin::Code::SQL::Statement.new([:DROP, :TABLE], :users)
518
+ sql
519
+ end
520
+
521
+ it "should emit multiple statements separated by '; '" do
522
+ expect(subject.emit_statement_list(stmts)).to eq('SELECT 1; DROP TABLE users')
523
+ end
524
+
525
+ context "with custom :space" do
526
+ subject { described_class.new(space: '/**/') }
527
+
528
+ it "should emit the custom white-space deliminater" do
529
+ expect(subject.emit_statement_list(stmts)).to eq('SELECT/**/1;/**/DROP/**/TABLE/**/users')
530
+ end
531
+ end
532
+ end
533
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+ require 'ronin/code/sql/field'
3
+
4
+ describe Ronin::Code::SQL::Field do
5
+ describe "#initialize" do
6
+ it "should convert name to a String" do
7
+ expect(described_class.new(:table).name).to eq('table')
8
+ end
9
+
10
+ it "should default parent to nil" do
11
+ expect(described_class.new('table').parent).to be_nil
12
+ end
13
+ end
14
+
15
+ describe "parse" do
16
+ context "when passed a single field" do
17
+ let(:name) { "column" }
18
+
19
+ subject { described_class.parse(name) }
20
+
21
+ it "should parse the field name" do
22
+ expect(subject.name).to eq(name)
23
+ end
24
+
25
+ it "should not have a parent" do
26
+ expect(subject.parent).to be_nil
27
+ end
28
+ end
29
+
30
+ context "when parsing multiple fields" do
31
+ let(:parent) { "table" }
32
+ let(:name) { "column" }
33
+
34
+ subject { described_class.parse("#{parent}.#{name}") }
35
+
36
+ it "should parse the field name" do
37
+ expect(subject.name).to eq(name)
38
+ end
39
+
40
+ it "should parse the parent field" do
41
+ expect(subject.parent.name).to eq(parent)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "#to_s" do
47
+ context "when parent is nil" do
48
+ let(:name) { "column" }
49
+
50
+ subject { described_class.new(name) }
51
+
52
+ it "should return the name" do
53
+ expect(subject.to_s).to eq(name)
54
+ end
55
+ end
56
+
57
+ context "when parent is set" do
58
+ let(:parent) { "table" }
59
+ let(:name) { "column" }
60
+
61
+ subject { described_class.new(name,described_class.new(parent)) }
62
+
63
+ it "should return the name" do
64
+ expect(subject.to_s).to eq("#{parent}.#{name}")
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "#method_missing" do
70
+ context "at depth 1" do
71
+ subject { described_class.new('table') }
72
+
73
+ it "should allow accessing sub-fields" do
74
+ expect(subject.column.name).to eq('column')
75
+ end
76
+ end
77
+
78
+ context "at depth 2" do
79
+ subject { described_class.new('table',described_class.new('db')) }
80
+
81
+ it "should allow accessing sub-fields" do
82
+ expect(subject.column.name).to eq('column')
83
+ end
84
+ end
85
+
86
+ context "at depth 3" do
87
+ subject do
88
+ described_class.new(
89
+ 'column',
90
+ described_class.new(
91
+ 'table', described_class.new('db')
92
+ )
93
+ )
94
+ end
95
+
96
+ it "should not allow accessing sub-fields" do
97
+ expect {
98
+ subject.column
99
+ }.to raise_error(NoMethodError)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'ronin/code/sql/fields'
3
+
4
+ describe Ronin::Code::SQL::Fields do
5
+ subject { Object.new.extend(described_class) }
6
+
7
+ describe "#respond_to_missing?" do
8
+ it "should return true" do
9
+ expect(subject).to respond_to(:foo)
10
+ end
11
+ end
12
+
13
+ it { expect(subject.to_ary).to be_nil }
14
+
15
+ describe "#method_missing" do
16
+ let(:name) { 'users' }
17
+
18
+ context "when called with no arguments and no block" do
19
+ it "should create a Field" do
20
+ expect(subject.send(name).name).to eq(name)
21
+ end
22
+ end
23
+
24
+ context "when called with arguments" do
25
+ it "should raise a NoMethodError" do
26
+ expect {
27
+ subject.sned(name,1,2,3)
28
+ }.to raise_error(NoMethodError)
29
+ end
30
+ end
31
+
32
+ context "when called with a block" do
33
+ it "should raise a NoMethodError" do
34
+ expect {
35
+ subject.sned(name) { 1 + 1 }
36
+ }.to raise_error(NoMethodError)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "Function" do |method,arguments=[],additional_arguments=[]|
4
+ describe "##{method}" do
5
+ let(:name) { method.upcase }
6
+ let(:func) { subject.send(method,*arguments) }
7
+
8
+ it "should create a #{method.upcase} function" do
9
+ expect(func.name).to eq(name)
10
+ end
11
+
12
+ unless arguments.empty?
13
+ it "should set the arguments" do
14
+ expect(func.arguments).to eq(arguments)
15
+ end
16
+ end
17
+
18
+ unless additional_arguments.empty?
19
+ context "when passed additional arguments" do
20
+ let(:func) { subject.send(method,*additional_arguments) }
21
+
22
+ it "should set the arguments" do
23
+ expect(func.arguments).to eq(additional_arguments)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+