rspec-networking 0.1.0 → 0.1.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.
@@ -1,14 +1,14 @@
1
+ 000039
2
+ 000600
1
3
  00080D
2
4
  000E7B
3
5
  0015B7
4
6
  001C7E
7
+ 002318
5
8
  005FBF
6
9
  20B780
7
- 2318
8
10
  242FFA
9
11
  28FFB2
10
- 39
11
- 600
12
12
  6845F1
13
13
  78D6B2
14
14
  887384
@@ -1,14 +1,14 @@
1
- 0
1
+ 000000
2
+ 000001
3
+ 000002
4
+ 000003
5
+ 000004
6
+ 000005
7
+ 000006
8
+ 000007
9
+ 000008
10
+ 000009
2
11
  0000AA
3
- 1
4
- 2
5
- 3
6
- 4
7
- 5
8
- 6
9
- 7
10
- 8
11
- 80072
12
- 9
12
+ 080072
13
13
  9C934E
14
14
  E84DEC
@@ -2,13 +2,17 @@
2
2
  00C30A
3
3
  00EC0A
4
4
  04106B
5
+ 046761
5
6
  047A0B
7
+ 04AE47
6
8
  04B167
7
9
  04C807
8
10
  04CF8C
9
11
  04D13A
10
12
  04E598
11
13
  081C6E
14
+ 082525
15
+ 08B339
12
16
  0C07DF
13
17
  0C1DAF
14
18
  0C9838
@@ -17,25 +21,33 @@
17
21
  0CF346
18
22
  102AB3
19
23
  103F44
24
+ 10C197
20
25
  1449D4
26
+ 14907A
21
27
  14993E
28
+ 14D881
22
29
  14F65A
23
30
  1801F1
24
31
  185936
25
32
  188740
26
33
  18F0E4
34
+ 1C2575
27
35
  1C2AB0
28
36
  1C8BEF
29
37
  1CCCD6
30
38
  1CEAAC
39
+ 2025CC
40
+ 203462
31
41
  2034FB
32
42
  203B34
33
43
  2047DA
34
44
  2072A9
35
45
  2082C0
46
+ 209952
36
47
  20A60C
37
48
  20F478
38
49
  241145
50
+ 246477
39
51
  24CF24
40
52
  24D337
41
53
  28167F
@@ -44,58 +56,83 @@
44
56
  28D127
45
57
  28E31F
46
58
  2C0B97
59
+ 2C0DCF
47
60
  2C195C
48
61
  2CD066
49
62
  2CFE4F
63
+ 3007EB
50
64
  3050CE
51
65
  341CF0
66
+ 343932
52
67
  3480B3
53
68
  34B98D
54
69
  34CE00
70
+ 34F015
71
+ 34FA1C
55
72
  38A4ED
73
+ 38C1ED
56
74
  38C6BD
57
75
  38E60A
58
76
  3C135A
59
77
  3C2CA6
60
78
  3C3824
79
+ 3C7F6E
61
80
  3CAFB7
62
81
  3CBD3E
63
82
  3CCD57
83
+ 400877
64
84
  40313C
65
85
  44237C
86
+ 444A37
87
+ 44579F
66
88
  447147
89
+ 44BDC8
90
+ 44CBAD
67
91
  44DF65
92
+ 44E213
68
93
  44F770
69
94
  482CA0
70
95
  488759
71
96
  48FDA3
72
97
  4C0220
73
98
  4C49E3
99
+ 4C55B2
74
100
  4C6371
101
+ 4C8E19
75
102
  4CC64C
76
103
  4CE0DB
104
+ 4CE20F
77
105
  4CF202
78
106
  503DC6
107
+ 504F3B
79
108
  50642B
80
109
  508811
110
+ 508E49
81
111
  508F4C
112
+ 50926A
82
113
  509839
83
114
  50A009
84
115
  50D2F5
85
116
  50DAD6
86
117
  50EC50
118
+ 50FE39
119
+ 5448E6
87
120
  582059
88
121
  584498
89
122
  58B623
90
123
  58EA1F
91
124
  5C0214
125
+ 5C4071
92
126
  5CD06E
93
127
  5CE50C
94
128
  606EE8
95
129
  60AB67
130
+ 60F723
96
131
  640980
132
+ 646306
97
133
  64644A
98
134
  6490C1
135
+ 649E31
99
136
  64A200
100
137
  64B473
101
138
  64CC2E
@@ -105,11 +142,14 @@
105
142
  68B8BB
106
143
  68C44C
107
144
  68DFDD
145
+ 6C0699
108
146
  6C0DC4
109
147
  6C483F
110
148
  6CF784
149
+ 70217F
111
150
  703A51
112
151
  705FA3
152
+ 709751
113
153
  70BBE9
114
154
  741575
115
155
  742344
@@ -123,6 +163,7 @@
123
163
  78D840
124
164
  7C035E
125
165
  7C03AB
166
+ 7C0CF1
126
167
  7C1DD9
127
168
  7C2ADB
128
169
  7C49EB
@@ -131,12 +172,16 @@
131
172
  7CD661
132
173
  7CFD6B
133
174
  8035C1
175
+ 808F97
134
176
  80AD16
135
- 82525
177
+ 80E63C
136
178
  844693
179
+ 84AEDE
180
+ 882F92
137
181
  884604
138
182
  8852EB
139
183
  886C60
184
+ 88B951
140
185
  88C397
141
186
  8C53C3
142
187
  8C5AF8
@@ -150,9 +195,16 @@
150
195
  9078B2
151
196
  90FB5D
152
197
  941700
198
+ 94626D
153
199
  947BAE
200
+ 9487E0
154
201
  948B93
155
202
  94D331
203
+ 9812E0
204
+ 98171A
205
+ 987552
206
+ 98E081
207
+ 98EE94
156
208
  98F621
157
209
  98FAE3
158
210
  9C28F7
@@ -169,21 +221,31 @@ A44BD5
169
221
  A45046
170
222
  A45590
171
223
  A4A930
224
+ A4BA70
225
+ A4C3BE
226
+ A4C788
172
227
  A4CCB3
173
228
  A4E287
229
+ A4FF9F
230
+ A82BD5
174
231
  A86A86
175
232
  A89CED
176
233
  AC1E9E
177
234
  AC8C46
178
235
  ACC1EE
236
+ ACE4D8
179
237
  ACF7F3
238
+ B00BE2
180
239
  B09C63
181
240
  B0E235
182
241
  B405A1
183
242
  B460ED
243
+ B47E9F
184
244
  B4C4FC
185
245
  B83BCC
186
246
  B850D8
247
+ B852E0
248
+ B85384
187
249
  B894E7
188
250
  B8EA98
189
251
  BC6193
@@ -192,12 +254,15 @@ BC7FA4
192
254
  C01693
193
255
  C05B44
194
256
  C40BCB
257
+ C46940
195
258
  C46AB7
259
+ C4710F
196
260
  C493BB
197
261
  C82832
198
262
  C83DDC
199
263
  C85CCC
200
264
  C8BF4C
265
+ CC033D
201
266
  CC4210
202
267
  CC4D75
203
268
  CCB5D1
@@ -206,9 +271,12 @@ CCDA20
206
271
  CCEB5E
207
272
  D09C7A
208
273
  D0AE05
274
+ D0C1BF
209
275
  D0CEC0
210
276
  D41761
211
277
  D43538
278
+ D4438A
279
+ D4532A
212
280
  D45EEC
213
281
  D4970B
214
282
  D4A365
@@ -216,8 +284,11 @@ D4DA21
216
284
  D4F0EA
217
285
  D832E3
218
286
  D86375
287
+ D893D4
219
288
  D8B053
220
289
  D8CE3A
290
+ D8E374
291
+ D8EDA8
221
292
  DC6AE7
222
293
  DCB72E
223
294
  DCED83
@@ -227,18 +298,24 @@ E0806B
227
298
  E0B655
228
299
  E0CCF8
229
300
  E0DCFF
301
+ E41B43
230
302
  E44519
231
303
  E446DA
232
304
  E484D3
233
305
  E4AAE4
234
306
  E4BCAA
307
+ E4D398
235
308
  E4DB6D
309
+ E4FE43
236
310
  E84A54
237
311
  E85A8B
238
312
  E85FB4
313
+ E87EEF
239
314
  E88843
240
315
  E89847
316
+ E8BA17
241
317
  E8F791
318
+ EC1055
242
319
  EC30B3
243
320
  EC4118
244
321
  EC4D3E
@@ -251,12 +328,16 @@ F4308B
251
328
  F460E2
252
329
  F48B32
253
330
  F4F5DB
331
+ F843EF
254
332
  F8710C
333
+ F88306
255
334
  F8A45F
256
335
  F8AB82
257
336
  FC0296
258
337
  FC1999
338
+ FC4345
259
339
  FC5B8C
260
340
  FC64BA
261
341
  FCA9F5
342
+ FCC17D
262
343
  FCD908
@@ -1,18 +1,29 @@
1
+ 001349
1
2
  0019CB
2
3
  0023F8
3
4
  00A0C5
5
+ 042336
4
6
  04BF6D
7
+ 082697
5
8
  1071B3
6
- 1349
9
+ 107BEF
10
+ 10C5FA
7
11
  143375
8
12
  14360E
13
+ 14C7C4
9
14
  1C740D
10
15
  28285D
16
+ 301577
17
+ 30BD13
18
+ 3CF75D
11
19
  404A03
20
+ 445E82
21
+ 4463C2
12
22
  48EDE6
13
23
  4C9EFF
14
24
  4CC53E
15
25
  5067F0
26
+ 50E039
16
27
  54833A
17
28
  588BF3
18
29
  5C648E
@@ -20,12 +31,14 @@
20
31
  5CE28C
21
32
  5CF4AB
22
33
  603197
34
+ 64DD68
35
+ 6C4F89
23
36
  7049A2
24
37
  78C57D
25
38
  7C7716
26
39
  80EA0B
27
- 82697
28
40
  88ACC0
41
+ 88F3D5
29
42
  8C5973
30
43
  909F22
31
44
  90EF68
@@ -34,9 +47,12 @@ A0E4CB
34
47
  B0B2DC
35
48
  B8D526
36
49
  B8ECA3
50
+ BC7EC3
37
51
  BC9911
38
52
  BCCF4F
39
- BEF 107.00
53
+ C02E5F
54
+ C49A31
55
+ C83374
40
56
  C8544B
41
57
  C86C87
42
58
  CC5D4E
@@ -52,4 +68,5 @@ F08756
52
68
  F44D5C
53
69
  F80DA9
54
70
  FC22F4
71
+ FC9F2A
55
72
  FCF528
@@ -1,34 +1,48 @@
1
- require 'rspec/networking/mac_address_manufacturers'
1
+ require "rspec/networking/mac_address_manufacturers"
2
+
3
+ module RSpec
4
+ module Networking
5
+ module MacAddress
6
+ # MM:MM:MM:SS:SS:SS, MM-MM-MM-SS-SS-SS, MMMM.MMSS.SSSS
7
+ EUI48 = /\A\h{2}([:-])(\h{2}\1){4}\h{2}\z|\A\h{4}(\.\h{4}){2}\z/
8
+
9
+ # MM:MM:MM:FF:FE:SS:SS:SS, MMMM:MMFF:FESS:SSSS
10
+ EUI64 = /\A\h{2}([:-])(\h{2}\1){6}\h{2}\z|\A\h{4}(:\h{4}){3}\z/
11
+
12
+ # half an EUI-48: MM:MM:MM or SS-SS-SS
13
+ HALF = /\A\h{2}([:-])\h{2}\1\h{2}\z/
14
+
15
+ def self.normalize(str)
16
+ str.delete(".:-").upcase
17
+ end
18
+ end
19
+ end
20
+ end
2
21
 
3
22
  RSpec::Matchers.define :be_a_mac_address do
23
+ mac = RSpec::Networking::MacAddress
24
+
4
25
  chain :from do |manufacturer|
5
- @manufacturer_str = manufacturer
26
+ @manufacturer = manufacturer
6
27
 
7
- if manufacturer.match?(/^\h{2}([:-])\h{2}\1\h{2}$/) || manufacturer.match?(/^\h{3}\.\h{3}$/)
8
- # exact match
9
- @manufacturer = manufacturer.delete('.:-').upcase
28
+ @prefixes = if mac::HALF.match?(manufacturer.to_s)
29
+ Set[mac.normalize(manufacturer)]
10
30
  else
11
- # manufacturer lookup
12
- @manufacturer = manufacturer.to_s.downcase.tr(' ', '_')
13
-
14
- unless RSpec::Networking::MANUFACTURERS.key?(@manufacturer)
31
+ RSpec::Networking::MANUFACTURERS.fetch(manufacturer.to_s.downcase.tr(" ", "_")) do
15
32
  raise ArgumentError, "unknown manufacturer: #{manufacturer}"
16
33
  end
17
34
  end
18
35
  end
19
36
 
20
37
  chain :for do |device|
21
- unless device.match?(/^\h{2}([:-])\h{2}\1\h{2}$/) || device.match?(/^\h{3}\.\h{3}$/)
22
- raise ArgumentError, "invalid device: #{device}"
23
- end
38
+ raise ArgumentError, "invalid device: #{device}" unless mac::HALF.match?(device.to_s)
24
39
 
25
40
  @device = device
41
+ @suffix = mac.normalize(device)
26
42
  end
27
43
 
28
44
  chain :bits do |bits|
29
- unless [ 48, 64 ].include?(bits)
30
- raise ArgumentError, "invalid bit length: #{bits}"
31
- end
45
+ raise ArgumentError, "invalid bit length: #{bits}" unless [48, 64].include?(bits)
32
46
 
33
47
  @bits = bits
34
48
  end
@@ -46,85 +60,46 @@ RSpec::Matchers.define :be_a_mac_address do
46
60
  end
47
61
 
48
62
  match do |actual|
49
- @actual = actual
50
-
51
63
  return false unless actual.is_a?(String)
52
64
 
53
- hex = nil
54
-
55
- if actual.match?(/^\h{2}(:\h{2}){7}$/) || actual.match?(/^\h{4}(:\h{4}){3}$/)
56
- # 64 bits
57
- # 00:00:00:FF:FE:00:00:00
58
- # 0000:00FF:FE00:0000
59
-
60
- hex = actual.delete(':').upcase
61
- return false unless hex.slice(6, 4) == 'FFFE'
65
+ if mac::EUI64.match?(actual)
62
66
  return false if @bits == 48
63
67
 
64
- # normalize to 48 bits
65
- hex.slice!(6, 4)
66
- elsif actual.match?(/^\h{2}([:-])(\h{2}\1){4}\h{2}$/) || actual.match?(/^\h{3}(\.\h{3}){3}$/)
67
- # 48 bits
68
- # MM:MM:MM:SS:SS:SS
69
- # MM-MM-MM-SS-SS-SS
70
- # MMM.MMM.SSS.SSS
68
+ hex = mac.normalize(actual)
69
+ return false unless hex[6, 4] == "FFFE"
71
70
 
71
+ # normalize to 48 bits
72
+ hex = hex[0, 6] + hex[10, 6]
73
+ elsif mac::EUI48.match?(actual)
72
74
  return false if @bits == 64
73
75
 
74
- hex = actual.delete('.:-').upcase
76
+ hex = mac.normalize(actual)
75
77
  else
76
78
  return false
77
79
  end
78
80
 
79
- if @broadcast
80
- return false unless hex == 'FF' * 6
81
- end
82
-
83
- if @cast
84
- bit = @cast == :multicast ? 1 : 0
85
-
86
- return false unless hex[1].to_i(16) & 1 == bit
87
- end
88
-
89
- matches = true
81
+ return false if @broadcast && hex != "FF" * 6
90
82
 
91
- if @manufacturer
92
- prefix = hex.slice(0, 6)
83
+ # the I/G bit: least significant bit of the first octet
84
+ return false if @cast && hex[0, 2].to_i(16).odd? != (@cast == :multicast)
93
85
 
94
- matches &&= if @manufacturer_str.match?(/[.:-]/)
95
- # exact match
96
- prefix == @manufacturer
97
- else
98
- # manufacturer lookup
99
- RSpec::Networking::MANUFACTURERS[@manufacturer].include?(prefix)
100
- end
101
- end
86
+ return false if @prefixes && !@prefixes.include?(hex[0, 6])
87
+ return false if @suffix && @suffix != hex[6, 6]
102
88
 
103
- if @device
104
- suffix = hex.slice(-6, 6)
105
- matches &&= suffix == @device.delete('.:-').upcase
106
- end
107
-
108
- matches
89
+ true
109
90
  end
110
91
 
111
92
  description do
112
- from_s = @manufacturer ? " from #{@manufacturer_str}" : ""
113
- for_s = @device ? " for #{@device}" : ""
114
- bits_s = @bits ? " with #{@bits} bits" : ""
115
- broadcast_s = @broadcast ? " broadcast" : ""
116
- cast_s = @cast ? " #{@cast}" : ""
117
-
118
- "a MAC address#{bits_s}#{from_s}#{for_s}#{broadcast_s}#{cast_s}"
119
- end
120
-
121
- failure_message do
122
- "expected '#{@actual}' to be #{description}"
123
- end
124
-
125
- failure_message_when_negated do
126
- "expected '#{@actual}' not to be #{description}"
93
+ [
94
+ "be a",
95
+ ("broadcast" if @broadcast),
96
+ @cast,
97
+ "MAC address",
98
+ ("with #{@bits} bits" if @bits),
99
+ ("from #{@manufacturer}" if @manufacturer),
100
+ ("for #{@device}" if @device),
101
+ ].compact.join(" ")
127
102
  end
128
103
  end
129
104
 
130
- RSpec::Matchers.alias_matcher :a_mac_address, :be_a_mac_address
105
+ RSpec::Matchers.alias_matcher(:a_mac_address, :be_a_mac_address) { |desc| desc.delete_prefix("be ") }
@@ -1,19 +1,10 @@
1
- require 'set'
1
+ require "set"
2
2
 
3
3
  module RSpec
4
4
  module Networking
5
- MANUFACTURERS = {
6
- "xerox" => Set.new(['00:00:00'.delete(':')]),
7
- }
8
-
9
- # load manufacturers
10
- Dir.glob("#{__dir__}/mac_address/*.txt").each do |file|
11
- manufacturer = File.basename(file, '.txt').downcase
12
-
13
- File.readlines(file).map(&:chomp).each do |entry|
14
- MANUFACTURERS[manufacturer] ||= Set.new
15
- MANUFACTURERS[manufacturer] << entry.upcase
16
- end
17
- end
5
+ # generated by script/update_manufacturers
6
+ MANUFACTURERS = Dir["#{__dir__}/mac_address/*.txt"].to_h do |file|
7
+ [File.basename(file, ".txt"), Set.new(File.readlines(file, chomp: true))]
8
+ end.freeze
18
9
  end
19
10
  end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module Networking
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -1,22 +1,25 @@
1
1
  require_relative "lib/rspec/networking/version"
2
- package = RSpec::Networking
3
- package_name = "rspec-networking"
4
2
 
5
3
  Gem::Specification.new do |s|
6
4
  s.authors = ["Daniel Pepper"]
7
- s.description = "RSpec matchers for IP and MAC Addresses"
5
+ s.description = "RSpec matchers for IP and MAC addresses"
8
6
  s.files = `git ls-files * ':!:spec'`.split("\n")
9
- s.homepage = "https://github.com/dpep/#{package_name}"
7
+ s.homepage = "https://github.com/dpep/rspec-networking"
10
8
  s.license = "MIT"
11
- s.name = package_name
12
- s.summary = package.to_s
13
- s.version = package.const_get "VERSION"
9
+ s.name = "rspec-networking"
10
+ s.summary = "RSpec matchers for IP and MAC addresses"
11
+ s.version = RSpec::Networking::VERSION
12
+
13
+ s.metadata = {
14
+ "changelog_uri" => "https://github.com/dpep/rspec-networking/blob/main/CHANGELOG.md",
15
+ "rubygems_mfa_required" => "true",
16
+ }
14
17
 
15
18
  s.required_ruby_version = ">= 3"
16
19
 
17
20
  s.add_dependency "rspec-expectations", ">= 3"
18
21
 
19
- s.add_development_dependency "byebug"
22
+ s.add_development_dependency "debug"
20
23
  s.add_development_dependency "faker"
21
24
  s.add_development_dependency "rspec"
22
25
  s.add_development_dependency "simplecov"
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # Regenerates lib/rspec/networking/mac_address/*.txt from the IEEE OUI registry.
3
+ #
4
+ # usage: script/update_manufacturers [path/to/oui.csv]
5
+
6
+ require "csv"
7
+ require "net/http"
8
+
9
+ URL = "https://standards-oui.ieee.org/oui/oui.csv"
10
+ DIR = File.expand_path("../lib/rspec/networking/mac_address", __dir__)
11
+
12
+ # matched against the registry's "Organization Name"
13
+ MANUFACTURERS = {
14
+ "apple" => /\AApple, Inc/,
15
+ "eero" => /\Aeero/i,
16
+ "google" => /\AGoogle/,
17
+ "microsoft" => /\AMicrosoft/i,
18
+ "nokia" => /\ANokia/,
19
+ "oracle" => /\AOracle/,
20
+ "sony" => /\ASony/i,
21
+ "texas_instruments" => /\ATexas Instruments/,
22
+ "toshiba" => /Toshiba/i,
23
+ "xerox" => /\AXerox/i,
24
+ "xiaomi" => /Xiaomi/i,
25
+ "zyxel" => /Zyxel/i,
26
+ }
27
+
28
+ csv = ARGV[0] ? File.read(ARGV[0]) : Net::HTTP.get(URI(URL))
29
+
30
+ # only MA-L assignments are full 24-bit OUIs
31
+ rows = CSV.parse(csv, headers: true).select { |row| row["Registry"] == "MA-L" }
32
+
33
+ MANUFACTURERS.each do |name, pattern|
34
+ ouis = rows.select { |row| row["Organization Name"].to_s.strip.match?(pattern) }
35
+ .map { |row| row["Assignment"].upcase }
36
+ .sort
37
+ .uniq
38
+
39
+ abort "no OUIs found for #{name}" if ouis.empty?
40
+
41
+ File.write("#{DIR}/#{name}.txt", ouis.join("\n") + "\n")
42
+ puts "#{name}: #{ouis.size}"
43
+ end