ruby-nmap 0.10.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.document +1 -0
  3. data/.editorconfig +11 -0
  4. data/.github/workflows/ruby.yml +31 -0
  5. data/ChangeLog.md +118 -71
  6. data/Gemfile +11 -5
  7. data/LICENSE.txt +1 -1
  8. data/README.md +88 -50
  9. data/Rakefile +5 -0
  10. data/UPGRADING.md +47 -0
  11. data/gemspec.yml +5 -5
  12. data/lib/nmap/command.rb +765 -0
  13. data/lib/nmap/version.rb +1 -1
  14. data/lib/nmap/xml/address.rb +38 -0
  15. data/lib/nmap/xml/cpe/url.rb +80 -0
  16. data/lib/nmap/xml/cpe.rb +47 -0
  17. data/lib/nmap/xml/hop.rb +22 -0
  18. data/lib/nmap/xml/host.rb +546 -0
  19. data/lib/nmap/xml/host_script.rb +26 -0
  20. data/lib/nmap/xml/hostname.rb +44 -0
  21. data/lib/nmap/xml/ip_id_sequence.rb +26 -0
  22. data/lib/nmap/xml/os.rb +131 -0
  23. data/lib/nmap/xml/os_class.rb +86 -0
  24. data/lib/nmap/xml/os_match.rb +22 -0
  25. data/lib/nmap/xml/port.rb +114 -0
  26. data/lib/nmap/xml/postscript.rb +26 -0
  27. data/lib/nmap/xml/prescript.rb +26 -0
  28. data/lib/nmap/xml/run_stat.rb +22 -0
  29. data/lib/nmap/xml/scan.rb +38 -0
  30. data/lib/nmap/xml/scan_task.rb +55 -0
  31. data/lib/nmap/xml/scanner.rb +22 -0
  32. data/lib/nmap/xml/script.rb +110 -0
  33. data/lib/nmap/xml/scripts.rb +33 -0
  34. data/lib/nmap/xml/sequence.rb +52 -0
  35. data/lib/nmap/xml/service.rb +172 -0
  36. data/lib/nmap/xml/status.rb +22 -0
  37. data/lib/nmap/xml/tcp_sequence.rb +48 -0
  38. data/lib/nmap/xml/tcp_ts_sequence.rb +26 -0
  39. data/lib/nmap/xml/traceroute.rb +73 -0
  40. data/lib/nmap/xml/uptime.rb +22 -0
  41. data/lib/nmap/xml.rb +31 -44
  42. data/spec/command_spec.rb +726 -0
  43. data/spec/fixtures/down_host_scan.xml +16 -0
  44. data/spec/{address_spec.rb → xml/address_spec.rb} +2 -2
  45. data/spec/{cpe → xml/cpe}/url_spec.rb +1 -1
  46. data/spec/{cpe_examples.rb → xml/cpe_examples.rb} +1 -1
  47. data/spec/{hop_spec.rb → xml/hop_spec.rb} +2 -2
  48. data/spec/{host_script_spec.rb → xml/host_script_spec.rb} +2 -2
  49. data/spec/{host_spec.rb → xml/host_spec.rb} +8 -8
  50. data/spec/{hostname_spec.rb → xml/hostname_spec.rb} +2 -2
  51. data/spec/{ip_id_sequence_spec.rb → xml/ip_id_sequence_spec.rb} +3 -3
  52. data/spec/{os_class_spec.rb → xml/os_class_spec.rb} +3 -3
  53. data/spec/{os_match_spec.rb → xml/os_match_spec.rb} +2 -2
  54. data/spec/{os_spec.rb → xml/os_spec.rb} +3 -3
  55. data/spec/{port_spec.rb → xml/port_spec.rb} +4 -5
  56. data/spec/{postscript_spec.rb → xml/postscript_spec.rb} +2 -2
  57. data/spec/{prescript_spec.rb → xml/prescript_spec.rb} +2 -2
  58. data/spec/{run_stat_spec.rb → xml/run_stat_spec.rb} +2 -2
  59. data/spec/{scan_spec.rb → xml/scan_spec.rb} +2 -2
  60. data/spec/{scan_task_spec.rb → xml/scan_task_spec.rb} +6 -6
  61. data/spec/{scanner_spec.rb → xml/scanner_spec.rb} +3 -3
  62. data/spec/xml/script_spec.rb +137 -0
  63. data/spec/xml/scripts_examples.rb +19 -0
  64. data/spec/{sequence_examples.rb → xml/sequence_examples.rb} +1 -0
  65. data/spec/{service_spec.rb → xml/service_spec.rb} +31 -5
  66. data/spec/{status_spec.rb → xml/status_spec.rb} +2 -2
  67. data/spec/{tcp_sequence_spec.rb → xml/tcp_sequence_spec.rb} +3 -3
  68. data/spec/{tcp_ts_sequence_spec.rb → xml/tcp_ts_sequence_spec.rb} +3 -3
  69. data/spec/{traceroute_spec.rb → xml/traceroute_spec.rb} +3 -3
  70. data/spec/{uptime_spec.rb → xml/uptime_spec.rb} +2 -2
  71. data/spec/xml_spec.rb +73 -44
  72. metadata +72 -66
  73. data/.travis.yml +0 -16
  74. data/lib/nmap/address.rb +0 -34
  75. data/lib/nmap/cpe/url.rb +0 -78
  76. data/lib/nmap/cpe.rb +0 -45
  77. data/lib/nmap/hop.rb +0 -20
  78. data/lib/nmap/host.rb +0 -587
  79. data/lib/nmap/host_script.rb +0 -18
  80. data/lib/nmap/hostname.rb +0 -42
  81. data/lib/nmap/ip_id_sequence.rb +0 -24
  82. data/lib/nmap/os.rb +0 -127
  83. data/lib/nmap/os_class.rb +0 -82
  84. data/lib/nmap/os_match.rb +0 -18
  85. data/lib/nmap/port.rb +0 -110
  86. data/lib/nmap/postscript.rb +0 -16
  87. data/lib/nmap/prescript.rb +0 -16
  88. data/lib/nmap/program.rb +0 -102
  89. data/lib/nmap/run_stat.rb +0 -20
  90. data/lib/nmap/scan.rb +0 -34
  91. data/lib/nmap/scan_task.rb +0 -53
  92. data/lib/nmap/scanner.rb +0 -18
  93. data/lib/nmap/scripts.rb +0 -71
  94. data/lib/nmap/sequence.rb +0 -50
  95. data/lib/nmap/service.rb +0 -170
  96. data/lib/nmap/status.rb +0 -18
  97. data/lib/nmap/task.rb +0 -387
  98. data/lib/nmap/tcp_sequence.rb +0 -46
  99. data/lib/nmap/tcp_ts_sequence.rb +0 -22
  100. data/lib/nmap/traceroute.rb +0 -71
  101. data/lib/nmap/uptime.rb +0 -20
  102. data/spec/scripts_examples.rb +0 -35
  103. data/spec/task_spec.rb +0 -150
@@ -0,0 +1,726 @@
1
+ require 'spec_helper'
2
+ require 'nmap/command'
3
+
4
+ describe Nmap::Command do
5
+ describe described_class::Port do
6
+ describe "#validate" do
7
+ context "when given an Integer" do
8
+ let(:value) { 443 }
9
+
10
+ it "must return true" do
11
+ expect(subject.validate(value)).to be(true)
12
+ end
13
+
14
+ context "but it's less than 1" do
15
+ let(:value) { 0 }
16
+
17
+ it "must return [false, \"(...) not within the range of acceptable values (1..65535)\"]" do
18
+ expect(subject.validate(value)).to eq(
19
+ [false, "(#{value.inspect}) not within the range of acceptable values (1..65535)"]
20
+ )
21
+ end
22
+ end
23
+
24
+ context "but it's greater than 65535" do
25
+ let(:value) { 65536 }
26
+
27
+ it "must return [false, \"(...) not within the range of acceptable values (1..65535)\"]" do
28
+ expect(subject.validate(value)).to eq(
29
+ [false, "(#{value.inspect}) not within the range of acceptable values (1..65535)"]
30
+ )
31
+ end
32
+ end
33
+ end
34
+
35
+ context "when given a String" do
36
+ context "and it's a number" do
37
+ let(:value) { '443' }
38
+
39
+ it "must return true" do
40
+ expect(subject.validate(value)).to be(true)
41
+ end
42
+
43
+ context "but it's less than 1" do
44
+ let(:value) { '0' }
45
+
46
+ it "must return [false, \"(...) not within the range of acceptable values (1..65535)\"]" do
47
+ expect(subject.validate(value)).to eq(
48
+ [false, "(#{value.inspect}) not within the range of acceptable values (1..65535)"]
49
+ )
50
+ end
51
+ end
52
+
53
+ context "but it's greater than 65535" do
54
+ let(:value) { '65536' }
55
+
56
+ it "must return [false, \"(...) not within the range of acceptable values (1..65535)\"]" do
57
+ expect(subject.validate(value)).to eq(
58
+ [false, "(#{value.inspect}) not within the range of acceptable values (1..65535)"]
59
+ )
60
+ end
61
+ end
62
+ end
63
+
64
+ context "and it's a service name" do
65
+ let(:value) { "http" }
66
+
67
+ it "must return true" do
68
+ expect(subject.validate(value)).to be(true)
69
+ end
70
+
71
+ context "and it ends with a '*' character" do
72
+ let(:value) { "http*" }
73
+
74
+ it "must return true" do
75
+ expect(subject.validate(value)).to be(true)
76
+ end
77
+ end
78
+
79
+ context "and it contains uppercase letters" do
80
+ let(:value) { "XmlIpcRegSvc" }
81
+
82
+ it "must return true" do
83
+ expect(subject.validate(value)).to be(true)
84
+ end
85
+ end
86
+
87
+ context "and it starts with digits" do
88
+ let(:value) { "1ci-smcs" }
89
+
90
+ it "must return true" do
91
+ expect(subject.validate(value)).to be(true)
92
+ end
93
+ end
94
+
95
+ context "and it contains digits" do
96
+ let(:value) { "neo4j" }
97
+
98
+ it "must return true" do
99
+ expect(subject.validate(value)).to be(true)
100
+ end
101
+ end
102
+
103
+ context "and it contains a '-' character" do
104
+ let(:value) { "3gpp-cbsp" }
105
+
106
+ it "must return true" do
107
+ expect(subject.validate(value)).to be(true)
108
+ end
109
+ end
110
+
111
+ context "but it starts with a '-' character" do
112
+ let(:value) { "-foo" }
113
+
114
+ it "must return [false, \"must be a port number or service name (...)\"]" do
115
+ expect(subject.validate(value)).to eq(
116
+ [false, "must be a port number or service name (#{value.inspect})"]
117
+ )
118
+ end
119
+ end
120
+
121
+ context "but it ends with a '-' character" do
122
+ let(:value) { "foo-" }
123
+
124
+ it "must return [false, \"must be a port number or service name (...)\"]" do
125
+ expect(subject.validate(value)).to eq(
126
+ [false, "must be a port number or service name (#{value.inspect})"]
127
+ )
128
+ end
129
+ end
130
+
131
+ context "and it contains a '_' character" do
132
+ let(:value) { "kerberos_master" }
133
+
134
+ it "must return true" do
135
+ expect(subject.validate(value)).to be(true)
136
+ end
137
+ end
138
+
139
+ context "but it starts with a '_' character" do
140
+ let(:value) { "_foo" }
141
+
142
+ it "must return [false, \"must be a port number or service name (...)\"]" do
143
+ expect(subject.validate(value)).to eq(
144
+ [false, "must be a port number or service name (#{value.inspect})"]
145
+ )
146
+ end
147
+ end
148
+
149
+ context "but it ends with a '_' character" do
150
+ let(:value) { "foo_" }
151
+
152
+ it "must return [false, \"must be a port number or service name (...)\"]" do
153
+ expect(subject.validate(value)).to eq(
154
+ [false, "must be a port number or service name (#{value.inspect})"]
155
+ )
156
+ end
157
+ end
158
+
159
+ context "and it contain's a '/' character" do
160
+ let(:value) { "cl/1" }
161
+
162
+ it "must return true" do
163
+ expect(subject.validate(value)).to be(true)
164
+ end
165
+ end
166
+
167
+ context "but it starts with a '/' character" do
168
+ let(:value) { "/foo" }
169
+
170
+ it "must return [false, \"must be a port number or service name (...)\"]" do
171
+ expect(subject.validate(value)).to eq(
172
+ [false, "must be a port number or service name (#{value.inspect})"]
173
+ )
174
+ end
175
+ end
176
+
177
+ context "but it starts with a '/' character" do
178
+ let(:value) { "foo/" }
179
+
180
+ it "must return [false, \"must be a port number or service name (...)\"]" do
181
+ expect(subject.validate(value)).to eq(
182
+ [false, "must be a port number or service name (#{value.inspect})"]
183
+ )
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ describe described_class::PortRange do
192
+ describe "#validate" do
193
+ context "when given an Integer value" do
194
+ end
195
+
196
+ context "when given a String value" do
197
+ end
198
+
199
+ context "when given a Range of port numbers" do
200
+ let(:value) { (1..1024) }
201
+
202
+ it "must return true" do
203
+ expect(subject.validate(value)).to be(true)
204
+ end
205
+ end
206
+ end
207
+
208
+ describe "#format" do
209
+ context "when given a single port number" do
210
+ let(:value) { 443 }
211
+
212
+ it "must return the formatted port number" do
213
+ expect(subject.format(value)).to eq(value.to_s)
214
+ end
215
+ end
216
+
217
+ context "when given a Range of port numbers" do
218
+ let(:value) { 1..1024 }
219
+
220
+ it "must return the formatted port number range (ex: 1-102)" do
221
+ expect(subject.format(value)).to eq("#{value.begin}-#{value.end}")
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ describe described_class::PortRangeList do
228
+ describe "#validate" do
229
+ context "when given a single port number" do
230
+ let(:value) { 443 }
231
+
232
+ it "must return true" do
233
+ expect(subject.validate(value)).to be(true)
234
+ end
235
+ end
236
+
237
+ context "when given a Range of port numbers" do
238
+ let(:value) { (1..1024) }
239
+
240
+ it "must return true" do
241
+ expect(subject.validate(value)).to be(true)
242
+ end
243
+ end
244
+
245
+ context "when given an Array of port numbers" do
246
+ let(:value) { [80, 443] }
247
+
248
+ it "must return true" do
249
+ expect(subject.validate(value)).to be(true)
250
+ end
251
+
252
+ context "but the Array is empty" do
253
+ let(:value) { [] }
254
+
255
+ it "must return [false, \"cannot be empty\"]" do
256
+ expect(subject.validate(value)).to eq(
257
+ [false, "cannot be empty"]
258
+ )
259
+ end
260
+ end
261
+
262
+ context "and the Array contains Ranges" do
263
+ let(:value) { [80, (1..42), 443] }
264
+
265
+ it "must return true" do
266
+ expect(subject.validate(value)).to be(true)
267
+ end
268
+ end
269
+ end
270
+
271
+ context "when given a Hash of protocols and port numbers" do
272
+ let(:value) do
273
+ {tcp: [1,2,3,4], udp: [1,2,3,4], sctp: [1,2,3,4]}
274
+ end
275
+
276
+ it "must return true" do
277
+ expect(subject.validate(value)).to be(true)
278
+ end
279
+
280
+ context "but the Hash is empty" do
281
+ let(:value) { {} }
282
+
283
+ it "must return [false, \"cannot be empty\"]" do
284
+ expect(subject.validate(value)).to eq(
285
+ [false, "cannot be empty"]
286
+ )
287
+ end
288
+ end
289
+
290
+ context "but one of the keys is not a known protocol" do
291
+ let(:protocol) { :foo }
292
+ let(:value) do
293
+ {tcp: [1,2,3,4], protocol => [1,2,3,4]}
294
+ end
295
+
296
+ it "must return [false, \"unknown protocol (...) must be :tcp, :udp, :sctp\"]" do
297
+ expect(subject.validate(value)).to eq(
298
+ [false, "unknown protocol (#{protocol.inspect}) must be :tcp, :udp, or :sctp"]
299
+ )
300
+ end
301
+ end
302
+
303
+ context "but one of the Hash values is not a valid port list class" do
304
+ let(:bad_port_list) { Object.new }
305
+ let(:value) do
306
+ {tcp: [1,2,3,4], udp: bad_port_list, sctp: [1,2,3,4]}
307
+ end
308
+
309
+ it "must return [false, \"cannot be converted into an Integer\"]" do
310
+ expect(subject.validate(value)).to eq(
311
+ [false, "element cannot be converted into an Integer (#{bad_port_list.inspect})"]
312
+ )
313
+ end
314
+ end
315
+
316
+ context "but one of the elements within the port list is not a port" do
317
+ let(:bad_port) { "" }
318
+ let(:value) do
319
+ {tcp: [1,2,3,4], udp: [1, bad_port, 3], sctp: [1,2,3,4]}
320
+ end
321
+
322
+ it "must return [false, \"element not a valid port range (...)\"]" do
323
+ expect(subject.validate(value)).to eq(
324
+ [false, "element not a valid port range (#{bad_port.inspect})"]
325
+ )
326
+ end
327
+ end
328
+ end
329
+ end
330
+
331
+ describe "#format" do
332
+ context "when given a single port number" do
333
+ let(:value) { 443 }
334
+
335
+ it "must return the formatted port number" do
336
+ expect(subject.format(value)).to eq(value.to_s)
337
+ end
338
+ end
339
+
340
+ context "when given a Range of port numbers" do
341
+ let(:value) { (1..1024) }
342
+
343
+ it "must return the formatted port number range (ex: 1-102)" do
344
+ expect(subject.format(value)).to eq("#{value.begin}-#{value.end}")
345
+ end
346
+ end
347
+
348
+ context "when given an Array of port numbers" do
349
+ let(:value) { [80, 443] }
350
+
351
+ it "must return the formatted list of port numbers" do
352
+ expect(subject.format(value)).to eq(value.join(','))
353
+ end
354
+
355
+ context "and the Array contains Ranges" do
356
+ let(:value) { [80, (1..42), 443] }
357
+
358
+ it "must return the formatted list of port numbers and port ranges" do
359
+ expect(subject.format(value)).to eq("#{value[0]},#{value[1].begin}-#{value[1].end},#{value[2]}")
360
+ end
361
+ end
362
+ end
363
+
364
+ context "when given a Hash of protocols and port numbers" do
365
+ let(:value) do
366
+ {tcp: [1,2,3,4], udp: [1,2,3,4], sctp: [1,2,3,4]}
367
+ end
368
+
369
+ it "must convert the keys to capital protocol letters and format the values" do
370
+ expect(subject.format(value)).to eq(
371
+ "T:#{subject.format(value[:tcp])},U:#{subject.format(value[:udp])},S:#{subject.format(value[:sctp])}"
372
+ )
373
+ end
374
+ end
375
+ end
376
+ end
377
+
378
+ describe described_class::Time do
379
+ describe "#validate" do
380
+ context "when given nil" do
381
+ let(:value) { nil }
382
+
383
+ it "must return [false, \"cannot be nil\"]" do
384
+ expect(subject.validate(value)).to eq(
385
+ [false, "cannot be nil"]
386
+ )
387
+ end
388
+ end
389
+
390
+ context "when given an Integer" do
391
+ let(:value) { 10 }
392
+
393
+ it "must return true" do
394
+ expect(subject.validate(value)).to be(true)
395
+ end
396
+ end
397
+
398
+ context "when given a String" do
399
+ context "when given an empty String" do
400
+ let(:value) { "" }
401
+
402
+ it "must return [false, \"does not allow an empty value\"]" do
403
+ expect(subject.validate(value)).to eq(
404
+ [false, "does not allow an empty value"]
405
+ )
406
+ end
407
+ end
408
+
409
+ context "when given a number" do
410
+ let(:value) { "10" }
411
+
412
+ it "must return true" do
413
+ expect(subject.validate(value)).to be(true)
414
+ end
415
+ end
416
+
417
+ context "when given a number that ends with 'ms'" do
418
+ let(:value) { "10ms" }
419
+
420
+ it "must return true" do
421
+ expect(subject.validate(value)).to be(true)
422
+ end
423
+ end
424
+
425
+ context "when given a number that ends with 's'" do
426
+ let(:value) { "10s" }
427
+
428
+ it "must return true" do
429
+ expect(subject.validate(value)).to be(true)
430
+ end
431
+ end
432
+
433
+ context "when given a number that ends with 'm'" do
434
+ let(:value) { "10m" }
435
+
436
+ it "must return true" do
437
+ expect(subject.validate(value)).to be(true)
438
+ end
439
+ end
440
+
441
+ context "when given a number that ends with 'h'" do
442
+ let(:value) { "10h" }
443
+
444
+ it "must return true" do
445
+ expect(subject.validate(value)).to be(true)
446
+ end
447
+ end
448
+
449
+ context "when given a number that ends with an unrecognized unit" do
450
+ let(:value) { "10x" }
451
+
452
+ it "must return [false, \"must be a number and end with 'ms', 's', 'm', or 'h'\"]" do
453
+ expect(subject.validate(value)).to eq(
454
+ [false, "must be a number and end with 'ms', 's', 'm', or 'h'"]
455
+ )
456
+ end
457
+ end
458
+ end
459
+ end
460
+ end
461
+
462
+ describe described_class::HexString do
463
+ describe "#validate" do
464
+ context "when given nil" do
465
+ let(:value) { nil }
466
+
467
+ it "must return [false, \"cannot be nil\"]" do
468
+ expect(subject.validate(value)).to eq(
469
+ [false, "cannot be nil"]
470
+ )
471
+ end
472
+ end
473
+
474
+ context "when given a String" do
475
+ context "but it's empty" do
476
+ let(:value) { '' }
477
+
478
+ it "must return [false, \"does not allow an empty value\"]" do
479
+ expect(subject.validate(value)).to eq(
480
+ [false, "does not allow an empty value"]
481
+ )
482
+ end
483
+ end
484
+
485
+ context "and it's of the format 0xAABBCCDDEEFF" do
486
+ let(:value) { "0xAABBCCDDEEFF" }
487
+
488
+ it "must return true" do
489
+ expect(subject.validate(value)).to be(true)
490
+ end
491
+ end
492
+
493
+ context "and it's of the format AABBCCDDEEFF" do
494
+ let(:value) { "AABBCCDDEEFF" }
495
+
496
+ it "must return true" do
497
+ expect(subject.validate(value)).to be(true)
498
+ end
499
+ end
500
+
501
+ context "and it's of the format \\xAA\\xBB\\xCC\\xDD\\xEE\\xFF" do
502
+ let(:value) { "\\xAA\\xBB\\xCC\\xDD\\xEE\\xFF" }
503
+
504
+ it "must return true" do
505
+ expect(subject.validate(value)).to be(true)
506
+ end
507
+ end
508
+
509
+ context "but it contains non-hex characters" do
510
+ let(:value) { "abcxyz123" }
511
+
512
+ it "must return [false, \"must be of the format 0xAABBCCDDEEFF..., AABBCCDDEEFF..., or \\\\xAA\\\\xBB\\\\xCC\\\\xDD\\\\xEE\\\\xFF...\"]" do
513
+ expect(subject.validate(value)).to eq(
514
+ [false, "must be of the format 0xAABBCCDDEEFF..., AABBCCDDEEFF..., or \\xAA\\xBB\\xCC\\xDD\\xEE\\xFF..."]
515
+ )
516
+ end
517
+ end
518
+ end
519
+ end
520
+ end
521
+
522
+ describe described_class::ScanFlags do
523
+ describe "#validate" do
524
+ context "when given nil" do
525
+ let(:value) { nil }
526
+
527
+ it "must return [false, \"cannot be nil\"]" do
528
+ expect(subject.validate(value)).to eq(
529
+ [false, "cannot be nil"]
530
+ )
531
+ end
532
+ end
533
+
534
+ context "when given a String" do
535
+ context "but it's empty" do
536
+ let(:value) { '' }
537
+
538
+ it "must return [false, \"does not allow an empty value\"]" do
539
+ expect(subject.validate(value)).to eq(
540
+ [false, "does not allow an empty value"]
541
+ )
542
+ end
543
+ end
544
+
545
+ context "and it's made up of 'URG', 'ACK', 'PSH', 'RST', 'SYN', and 'FIN'" do
546
+ let(:value) { 'URGACKPSHRSTSYNFIN' }
547
+
548
+ it "must return true" do
549
+ expect(subject.validate(value)).to be(true)
550
+ end
551
+ end
552
+
553
+ context "but it contains other sub-strings besides 'URG', 'ACK', 'PSH', 'RST', 'SYN', and 'FIN'" do
554
+ let(:value) { 'URGACKPSHRSTSYNFINXXX' }
555
+
556
+ it "must return [false, \"must only contain URG, ACK, PSH, RST, SYN, or FIN\"]" do
557
+ expect(subject.validate(value)).to eq(
558
+ [false, "must only contain URG, ACK, PSH, RST, SYN, or FIN"]
559
+ )
560
+ end
561
+ end
562
+
563
+ context "and it's numeric" do
564
+ let(:value) { '9' }
565
+
566
+ it "must return true" do
567
+ expect(subject.validate(value)).to be(true)
568
+ end
569
+ end
570
+ end
571
+
572
+ context "when given an Integer" do
573
+ let(:value) { 9 }
574
+
575
+ it "must return true" do
576
+ expect(subject.validate(value)).to be(true)
577
+ end
578
+ end
579
+
580
+ context "when given an Array" do
581
+ context "but it's empty" do
582
+ let(:value) { [] }
583
+
584
+ it "must return [false, \"Array value cannot be empty\"]" do
585
+ expect(subject.validate(value)).to eq(
586
+ [false, "Array value cannot be empty"]
587
+ )
588
+ end
589
+ end
590
+
591
+ context "and it contains :urg, :ack, :psh, :rst, :syn, or :fin" do
592
+ let(:value) { [:urg, :ack, :psh, :rst, :syn, :fin] }
593
+
594
+ it "must return true" do
595
+ expect(subject.validate(value)).to be(true)
596
+ end
597
+ end
598
+
599
+ context "but it contains other values besides :urg, :ack, :psh, :rst, :syn, and :fin" do
600
+ let(:other) { :foo }
601
+ let(:value) { [:urg, :ack, :psh, :rst, :syn, :fin, other] }
602
+
603
+ it "must return true" do
604
+ expect(subject.validate(value)).to eq(
605
+ [false, "Array must only contain the values :urg, :ack, :psh, :rst, :syn, or :fin"]
606
+ )
607
+ end
608
+ end
609
+ end
610
+
611
+ context "when given a Hash" do
612
+ context "but it's empty" do
613
+ let(:value) { {} }
614
+
615
+ it "must return [false, \"Hash value cannot be empty\"]" do
616
+ expect(subject.validate(value)).to eq(
617
+ [false, "Hash value cannot be empty"]
618
+ )
619
+ end
620
+ end
621
+
622
+ context "and it's keys contain :urg, :ack, :psh, :rst, :syn, or :fin" do
623
+ context "and it's values are all Boolean" do
624
+ let(:value) do
625
+ {
626
+ urg: true,
627
+ ack: true,
628
+ psh: false,
629
+ rst: nil,
630
+ syn: true,
631
+ fin: true
632
+ }
633
+ end
634
+
635
+ it "must return true" do
636
+ expect(subject.validate(value)).to be(true)
637
+ end
638
+ end
639
+
640
+ context "but one of it's values is not a Boolean value" do
641
+ let(:value) do
642
+ {
643
+ urg: true,
644
+ ack: true,
645
+ psh: false,
646
+ rst: nil,
647
+ syn: true,
648
+ fin: "foo"
649
+ }
650
+ end
651
+
652
+ it "must return [false, \"must only contain the Boolean values\"]" do
653
+ expect(subject.validate(value)).to eq(
654
+ [false, "Hash must only contain the values true, false, or nil"]
655
+ )
656
+ end
657
+ end
658
+ end
659
+
660
+ context "but it's keys contains other values besides :urg, :ack, :psh, :rst, :syn, and :fin" do
661
+ let(:other) { :foo }
662
+ let(:value) do
663
+ {
664
+ urg: true,
665
+ ack: true,
666
+ psh: true,
667
+ rst: true,
668
+ syn: true,
669
+ fin: true,
670
+ other => true
671
+ }
672
+ end
673
+
674
+ it "must return [false, \"must only contain the keys :urg, :ack, :psh, :rst, :syn, or :fin\"]" do
675
+ expect(subject.validate(value)).to eq(
676
+ [false, "Hash must only contain the keys :urg, :ack, :psh, :rst, :syn, or :fin"]
677
+ )
678
+ end
679
+ end
680
+ end
681
+ end
682
+
683
+ describe "#format" do
684
+ context "when given a String" do
685
+ let(:value) { 'PSHFIN' }
686
+
687
+ it "must return the String" do
688
+ expect(subject.format(value)).to eq(value)
689
+ end
690
+ end
691
+
692
+ context "when given an Integer" do
693
+ let(:value) { 9 }
694
+
695
+ it "must return the String version of the Integer" do
696
+ expect(subject.format(value)).to eq(value.to_s)
697
+ end
698
+ end
699
+
700
+ context "when given an Array" do
701
+ let(:value) { [:urg, :ack, :psh, :rst, :syn, :fin] }
702
+
703
+ it "must map each Symbol to their flag names value and join them together" do
704
+ expect(subject.format(value)).to eq("URGACKPSHRSTSYNFIN")
705
+ end
706
+ end
707
+
708
+ context "when given a Hash" do
709
+ let(:value) do
710
+ {
711
+ urg: true,
712
+ ack: true,
713
+ psh: false,
714
+ rst: nil,
715
+ syn: true,
716
+ fin: true
717
+ }
718
+ end
719
+
720
+ it "must map the keys with true values to their flag names and join them together" do
721
+ expect(subject.format(value)).to eq("URGACKSYNFIN")
722
+ end
723
+ end
724
+ end
725
+ end
726
+ end