berkeley_library-util 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +18 -0
  3. data/.gitignore +240 -0
  4. data/.idea/.gitignore +8 -0
  5. data/.idea/inspectionProfiles/Project_Default.xml +21 -0
  6. data/.idea/misc.xml +6 -0
  7. data/.idea/modules.xml +8 -0
  8. data/.idea/util.iml +123 -0
  9. data/.idea/vcs.xml +6 -0
  10. data/.rubocop.yml +334 -0
  11. data/.ruby-version +1 -0
  12. data/.simplecov +8 -0
  13. data/.yardopts +1 -0
  14. data/CHANGES.md +3 -0
  15. data/Gemfile +3 -0
  16. data/LICENSE.md +21 -0
  17. data/README.md +21 -0
  18. data/Rakefile +20 -0
  19. data/berkeley_library-util.gemspec +42 -0
  20. data/lib/berkeley_library/util/arrays.rb +178 -0
  21. data/lib/berkeley_library/util/module_info.rb +16 -0
  22. data/lib/berkeley_library/util/paths.rb +111 -0
  23. data/lib/berkeley_library/util/stringios.rb +30 -0
  24. data/lib/berkeley_library/util/strings.rb +42 -0
  25. data/lib/berkeley_library/util/sys_exits.rb +15 -0
  26. data/lib/berkeley_library/util/times.rb +22 -0
  27. data/lib/berkeley_library/util/uris/appender.rb +162 -0
  28. data/lib/berkeley_library/util/uris/requester.rb +62 -0
  29. data/lib/berkeley_library/util/uris/validator.rb +32 -0
  30. data/lib/berkeley_library/util/uris.rb +44 -0
  31. data/lib/berkeley_library/util.rb +1 -0
  32. data/rakelib/bundle.rake +8 -0
  33. data/rakelib/coverage.rake +11 -0
  34. data/rakelib/gem.rake +54 -0
  35. data/rakelib/rubocop.rake +18 -0
  36. data/rakelib/spec.rake +2 -0
  37. data/spec/.rubocop.yml +40 -0
  38. data/spec/berkeley_library/util/arrays_spec.rb +340 -0
  39. data/spec/berkeley_library/util/paths_spec.rb +90 -0
  40. data/spec/berkeley_library/util/stringios_spec.rb +34 -0
  41. data/spec/berkeley_library/util/strings_spec.rb +59 -0
  42. data/spec/berkeley_library/util/times_spec.rb +39 -0
  43. data/spec/berkeley_library/util/uris/requester_spec.rb +75 -0
  44. data/spec/berkeley_library/util/uris/validator_spec.rb +51 -0
  45. data/spec/berkeley_library/util/uris_spec.rb +133 -0
  46. data/spec/spec_helper.rb +30 -0
  47. metadata +321 -0
@@ -0,0 +1,340 @@
1
+ require 'spec_helper'
2
+
3
+ require 'berkeley_library/util/arrays'
4
+
5
+ module BerkeleyLibrary::Util
6
+ describe Arrays do
7
+ describe :ordered_superset do
8
+ let(:sup) { %w[a b c d e] }
9
+
10
+ it 'returns true for an identical subset' do
11
+ expect(Arrays.ordered_superset?(superset: sup, subset: sup.dup)).to eq(true)
12
+ end
13
+
14
+ it 'returns true for an empty subset' do
15
+ expect(Arrays.ordered_superset?(superset: sup, subset: [])).to eq(true)
16
+ end
17
+
18
+ it 'returns true for an exact sublist' do
19
+ subs = [
20
+ %w[a b c],
21
+ %w[b c d],
22
+ %w[c d e]
23
+ ]
24
+ subs.each do |sub|
25
+ expect(Arrays.ordered_superset?(superset: sup, subset: sub)).to eq(true)
26
+ end
27
+ end
28
+
29
+ it 'returns true when the superset interpolates extra elements' do
30
+ subs = [
31
+ %w[a c e],
32
+ %w[b d],
33
+ %w[a b d e]
34
+ ]
35
+ subs.each do |sub|
36
+ expect(Arrays.ordered_superset?(superset: sup, subset: sub)).to eq(true)
37
+ end
38
+ end
39
+
40
+ it 'returns false for a too-large subset' do
41
+ sub = %w[a b c d e f g]
42
+ expect(Arrays.ordered_superset?(superset: sup, subset: sub)).to eq(false)
43
+ end
44
+
45
+ it 'returns false when extra elements are present' do
46
+ subs = [
47
+ %w[a b c x],
48
+ %w[x b c d],
49
+ %w[c d x e]
50
+ ]
51
+ subs.each do |sub|
52
+ expect(Arrays.ordered_superset?(superset: sup, subset: sub)).to eq(false)
53
+ end
54
+ end
55
+ end
56
+
57
+ describe :count_while do
58
+ it 'returns the count of matching elements' do
59
+ a = [1, 3, 5, 2, 4, 6, 7, 11, 13]
60
+ expect(Arrays.count_while(values: a, &:odd?)).to eq(3)
61
+ end
62
+
63
+ it 'returns 0 if the first element does not match' do
64
+ a = [2, 4, 6, 7, 11, 13]
65
+ expect(Arrays.count_while(values: a, &:odd?)).to eq(0)
66
+ end
67
+
68
+ it 'returns an enumerator if not passed a block' do
69
+ a = [1, 3, 5, 2, 4, 6, 7, 11, 13]
70
+ e = Arrays.count_while(values: a)
71
+ expect(e.each(&:odd?)).to eq(3)
72
+ end
73
+
74
+ it 'works on non-arrays' do
75
+ a = [1, 3, 5, 2, 4, 6, 7, 11, 13]
76
+ e = Enumerator.new { |y| a.each { |x| y << x } }
77
+ expect(Arrays.count_while(values: e, &:odd?)).to eq(3)
78
+ end
79
+ end
80
+
81
+ describe :find_indices do
82
+ let(:target) { %w[a b c d e] }
83
+
84
+ it 'returns identity indices for an identical subset' do
85
+ expect(Arrays.find_indices(for_array: target.dup, in_array: target)).to eq([0, 1, 2, 3, 4])
86
+ end
87
+
88
+ it 'returns an empty array for an empty subset' do
89
+ expect(Arrays.find_indices(for_array: [], in_array: target)).to eq([])
90
+ end
91
+
92
+ it 'returns the expected subindices for an exact sublist' do
93
+ sources = {
94
+ %w[a b c] => [0, 1, 2],
95
+ %w[b c d] => [1, 2, 3],
96
+ %w[c d e] => [2, 3, 4]
97
+ }
98
+ sources.each do |source, expected|
99
+ expect(Arrays.find_indices(for_array: source, in_array: target)).to eq(expected)
100
+ end
101
+ end
102
+
103
+ it 'returns nil for a too-large subset' do
104
+ source = %w[a b c d e f g]
105
+ expect(Arrays.find_indices(for_array: source, in_array: target)).to be_nil
106
+ end
107
+
108
+ it 'returns nil when extra elements are present' do
109
+ sources = [
110
+ %w[a b c x],
111
+ %w[x b c d],
112
+ %w[c d x e]
113
+ ]
114
+ sources.each do |source|
115
+ expect(Arrays.find_indices(for_array: source, in_array: target)).to be_nil
116
+ end
117
+ end
118
+
119
+ it 'takes a comparison block' do
120
+ sub = %i[a c e]
121
+ expect(Arrays.find_indices(for_array: sub, in_array: target) { |source, target| target == source.to_s }).to eq([0, 2, 4])
122
+ end
123
+ end
124
+
125
+ describe :find_index do
126
+ let(:arr) { [0, 2, 4, 6, 4] }
127
+
128
+ it 'finds an index based on a value' do
129
+ expect(Arrays.find_index(4, in_array: arr)).to eq(2)
130
+ expect(Arrays.find_index(4, in_array: arr, start_index: 3)).to eq(4)
131
+ end
132
+
133
+ it 'finds an index based on a block' do
134
+ expect(Arrays.find_index(in_array: arr) { |x| x > 3 }).to eq(2)
135
+ expect(Arrays.find_index(in_array: arr, start_index: 3) { |x| x < 5 }).to eq(4)
136
+ end
137
+
138
+ it 'returns nil if no equal value found' do
139
+ expect(Arrays.find_index(7, in_array: arr)).to be_nil
140
+ expect(Arrays.find_index(2, in_array: arr, start_index: 2)).to be_nil
141
+ end
142
+
143
+ it 'returns nil if no matching value found' do
144
+ expect(Arrays.find_index(in_array: arr, &:odd?)).to be_nil
145
+ expect(Arrays.find_index(in_array: arr, start_index: 2) { |x| x < 4 }).to be_nil
146
+ end
147
+
148
+ # rubocop:disable Lint/Void
149
+ it 'returns an enumerator if given no arguments' do
150
+ e = Arrays.find_index(in_array: arr)
151
+ expect(e.each { |x| x > 3 }).to eq(2)
152
+
153
+ e = Arrays.find_index(in_array: arr, start_index: 3)
154
+ expect(e.each { |x| x < 5 }).to eq(4)
155
+ end
156
+ # rubocop:enable Lint/Void
157
+ end
158
+
159
+ describe :merge do
160
+ it 'merges two arrays' do
161
+ a1 = [1, 2, 3]
162
+ a2 = [2, 3, 4]
163
+ expect(Arrays.merge(a1, a2)).to eq([1, 2, 3, 4])
164
+ expect(Arrays.merge(a2, a1)).to eq([1, 2, 3, 4])
165
+ end
166
+
167
+ it 'merges disjoint arrays' do
168
+ a1 = [1, 3, 5]
169
+ a2 = [2, 4, 6]
170
+ expect(Arrays.merge(a1, a2)).to eq([1, 3, 5, 2, 4, 6])
171
+ end
172
+
173
+ it 'preserves duplicates' do
174
+ a1 = [1, 2, 2, 3, 4, 5]
175
+ a2 = [2, 4, 4, 5, 5, 6]
176
+
177
+ merged = Arrays.merge(a1, a2)
178
+ [a1, a2].each do |a|
179
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
180
+ end
181
+
182
+ expected = [1, 2, 2, 3, 4, 4, 5, 5, 6]
183
+ expect(merged.size).to eq(expected.size)
184
+ expect(merged).to eq(expected)
185
+ end
186
+
187
+ it 'merges gappy arrays' do
188
+ a1 = [1, 4, 5, 7, 9]
189
+ a2 = [2, 3, 4, 7, 8, 9]
190
+
191
+ merged = Arrays.merge(a1, a2)
192
+ [a1, a2].each do |a|
193
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
194
+ end
195
+
196
+ expected = [1, 2, 3, 4, 5, 7, 8, 9]
197
+ expect(merged.size).to eq(expected.size)
198
+ expect(merged).to eq(expected)
199
+ end
200
+
201
+ it 'preserves order when merging arrays with duplicates' do
202
+ a1 = [1, 3, 2, 2, 4]
203
+ a2 = [1, 2, 3, 2, 4]
204
+
205
+ merged = Arrays.merge(a1, a2)
206
+ [a1, a2].each do |a|
207
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
208
+ end
209
+
210
+ expected = [1, 2, 3, 2, 2, 4]
211
+ expect(merged.size).to eq(expected.size)
212
+ expect(merged).to eq(expected)
213
+ end
214
+
215
+ it 'preserves nil' do
216
+ a1 = [1, 3, nil, nil, 4]
217
+ a2 = [1, nil, 3, nil, 4]
218
+
219
+ merged = Arrays.merge(a1, a2)
220
+ [a1, a2].each do |a|
221
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
222
+ end
223
+
224
+ expected = [1, nil, 3, nil, nil, 4]
225
+ expect(merged.size).to eq(expected.size)
226
+ expect(merged).to eq(expected)
227
+ end
228
+
229
+ it 'works with non-comparable types' do
230
+ a1 = [1, 3, 'two', 'two', 4]
231
+ a2 = [1, 'two', 3, 'two', 4]
232
+
233
+ merged = Arrays.merge(a1, a2)
234
+ [a1, a2].each do |a|
235
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
236
+ end
237
+
238
+ expected = [1, 'two', 3, 'two', 'two', 4]
239
+ expect(merged.size).to eq(expected.size)
240
+ expect(merged).to eq(expected)
241
+ end
242
+
243
+ it 'returns the larger array if the smaller is already a subarray' do
244
+ a1 = [2, 3, 4]
245
+ a2 = [1, 2, 3, 4, 5]
246
+
247
+ merged = Arrays.merge(a1, a2)
248
+ [a1, a2].each do |a|
249
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
250
+ end
251
+
252
+ expected = a2
253
+ expect(merged.size).to eq(expected.size)
254
+ expect(merged).to eq(expected)
255
+
256
+ expect(Arrays.merge(a2, a1)).to eq(expected)
257
+ end
258
+
259
+ it 'sorts where sorting preserves order' do
260
+ a1 = [1, 2, 3, 4, 5]
261
+ a2 = [2, 3, 6, 9]
262
+
263
+ merged = Arrays.merge(a1, a2)
264
+ [a1, a2].each do |a|
265
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
266
+ end
267
+
268
+ expected = [1, 2, 3, 4, 5, 6, 9]
269
+ expect(merged.size).to eq(expected.size)
270
+ expect(merged).to eq(expected)
271
+
272
+ expect(Arrays.merge(a2, a1)).to eq(expected)
273
+ end
274
+
275
+ it "doesn't muck up partial matches" do
276
+ a1 = [1, 2, 3, 4, 5]
277
+ a2 = [6, 9, 2, 3]
278
+
279
+ merged = Arrays.merge(a1, a2)
280
+ [a1, a2].each do |a|
281
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
282
+ end
283
+
284
+ expected = [1, 6, 9, 2, 3, 4, 5]
285
+ expect(merged.size).to eq(expected.size)
286
+ expect(merged).to eq(expected)
287
+
288
+ expect(Arrays.merge(a2, a1)).to eq(expected)
289
+ end
290
+
291
+ it "doesn't much up disjoints" do
292
+ a1 = [1, 2, 3, 4, 5]
293
+ a2 = [6, 9]
294
+
295
+ merged = Arrays.merge(a1, a2)
296
+ [a1, a2].each do |a|
297
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
298
+ end
299
+
300
+ expected = [1, 2, 3, 4, 5, 6, 9]
301
+ expect(merged.size).to eq(expected.size)
302
+ expect(merged).to eq(expected)
303
+
304
+ expect(Arrays.merge(a2, a1)).to eq(expected)
305
+ end
306
+
307
+ it 'works on a selection of random values' do
308
+ next_int = ->(n) { (n * rand).to_i }
309
+ rand_array = -> { (0...next_int.call(10)).map { next_int.call(10) } }
310
+ aggregate_failures 'random values' do
311
+ 100.times do
312
+ a1 = rand_array.call
313
+ a2 = rand_array.call
314
+
315
+ merged = Arrays.merge(a1, a2)
316
+ [a1, a2].each do |a|
317
+ expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ end
324
+
325
+ describe :invert do
326
+ it 'inverts an array of ints' do
327
+ expect(Arrays.invert([0, 2, 3])).to eq([0, nil, 1, 2])
328
+ end
329
+
330
+ it 'fails if values are not ints' do
331
+ # noinspection RubyYardParamTypeMatch
332
+ expect { Arrays.invert(%i[a b c]) }.to raise_error(TypeError)
333
+ end
334
+
335
+ it 'fails if given duplicate values' do
336
+ expect { Arrays.invert([1, 2, 3, 2]) }.to raise_error(ArgumentError)
337
+ end
338
+ end
339
+ end
340
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+ require 'berkeley_library/util/paths'
3
+
4
+ module BerkeleyLibrary::Util
5
+ describe Paths do
6
+ describe :clean do
7
+ {
8
+ # nil
9
+ nil => nil,
10
+
11
+ # Already clean
12
+ '' => '.',
13
+ 'abc' => 'abc',
14
+ 'abc/def' => 'abc/def',
15
+ 'a/b/c' => 'a/b/c',
16
+ '.' => '.',
17
+ '..' => '..',
18
+ '../..' => '../..',
19
+ '../../abc' => '../../abc',
20
+ '/abc' => '/abc',
21
+ '/' => '/',
22
+
23
+ # Remove trailing slash
24
+ 'abc/' => 'abc',
25
+ 'abc/def/' => 'abc/def',
26
+ 'a/b/c/' => 'a/b/c',
27
+ './' => '.',
28
+ '../' => '..',
29
+ '../../' => '../..',
30
+ '/abc/' => '/abc',
31
+
32
+ # Remove doubled slash
33
+ 'abc//def//ghi' => 'abc/def/ghi',
34
+ '//abc' => '/abc',
35
+ '///abc' => '/abc',
36
+ '//abc//' => '/abc',
37
+ 'abc//' => 'abc',
38
+
39
+ # Remove . elements
40
+ 'abc/./def' => 'abc/def',
41
+ '/./abc/def' => '/abc/def',
42
+ 'abc/.' => 'abc',
43
+
44
+ # Remove .. elements
45
+ 'abc/def/ghi/../jkl' => 'abc/def/jkl',
46
+ 'abc/def/../ghi/../jkl' => 'abc/jkl',
47
+ 'abc/def/..' => 'abc',
48
+ 'abc/def/../..' => '.',
49
+ '/abc/def/../..' => '/',
50
+ 'abc/def/../../..' => '..',
51
+ '/abc/def/../../..' => '/',
52
+ 'abc/def/../../../ghi/jkl/../../../mno' => '../../mno',
53
+
54
+ # Combinations
55
+ 'abc/./../def' => 'def',
56
+ 'abc//./../def' => 'def',
57
+ 'abc/../../././../def' => '../../def'
58
+ }.each do |orig, expected|
59
+ it "clean(#{orig.inspect}) -> #{expected.inspect}" do
60
+ expect(Paths.clean(orig)).to eq(expected)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe :join do
66
+ {
67
+ # zero parameters
68
+ [] => '',
69
+
70
+ # one parameter
71
+ [''] => '',
72
+ ['a'] => 'a',
73
+
74
+ # two parameters
75
+ ['a', 'b'] => 'a/b',
76
+ ['a', ''] => 'a',
77
+ ['', 'b'] => 'b',
78
+ ['/', 'a'] => '/a',
79
+ ['/', ''] => '/',
80
+ ['a/', 'b'] => 'a/b',
81
+ ['a/', ''] => 'a',
82
+ ['', ''] => ''
83
+ }.each do |orig, expected|
84
+ it "join(#{orig.map(&:inspect).join(', ')}) -> #{expected.inspect}" do
85
+ expect(Paths.join(*orig)).to eq(expected)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ require 'berkeley_library/util/stringios'
4
+
5
+ module BerkeleyLibrary
6
+ module Util
7
+ describe StringIOs do
8
+ describe :getbyte do
9
+ let(:s) { '祇園精舎の鐘の声、諸行無常の響きあり。' }
10
+ let(:bytes) { s.bytes }
11
+ let(:sio) { StringIO.new(s) }
12
+
13
+ it 'gets the byte at the specified byte index' do
14
+ bytes.each_with_index do |b, i|
15
+ expect(StringIOs.getbyte(sio, i)).to eq(b)
16
+ end
17
+ end
18
+
19
+ it 'resets the current offset' do
20
+ StringIOs.getbyte(sio, bytes.size / 2)
21
+ expect(sio.pos).to eq(0)
22
+ end
23
+
24
+ it 'returns nil for a too-large positive offset' do
25
+ expect(StringIOs.getbyte(s, bytes.size)).to be_nil
26
+ end
27
+
28
+ it 'returns nil for a too-large negative offset' do
29
+ expect(StringIOs.getbyte(s, -(1 + bytes.size))).to be_nil
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ describe Strings do
6
+ describe :ascii_numeric do
7
+ it 'returns true for ASCII numeric strings' do
8
+ str = '8675309'
9
+ expect(Strings.ascii_numeric?(str)).to eq(true)
10
+ end
11
+
12
+ it 'returns false for non-ASCII numeric strings' do
13
+ strs = %w[
14
+ ٨٦٧٥٣٠٩
15
+ 八六七五三〇九
16
+ ]
17
+ aggregate_failures 'non-ASCII numeric strings' do
18
+ strs.each do |str|
19
+ expect(Strings.ascii_numeric?(str)).to eq(false), "Expected #{str.inspect} to be non-ASCII-numeric"
20
+ end
21
+ end
22
+ end
23
+
24
+ it 'returns false for mixed ASCII numeric and non-numeric strings' do
25
+ strs = [
26
+ '867-5309',
27
+ '867 5309',
28
+ ' 8675309 '
29
+ ]
30
+ aggregate_failures 'ASCII mixed numeric and non-numeric strings' do
31
+ strs.each do |str|
32
+ expect(Strings.ascii_numeric?(str)).to eq(false), "Expected #{str.inspect} to be non-ASCII-numeric"
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ describe :diff_index do
39
+ it 'returns nil for identical strings' do
40
+ s = 'elvis'
41
+ expect(Strings.diff_index(s, s)).to be_nil
42
+ end
43
+
44
+ it 'returns the index for different strings' do
45
+ s1 = 'elvis aaron presley'
46
+ s2 = 'elvis nikita presley'
47
+ expect(Strings.diff_index(s1, s2)).to eq(6)
48
+ end
49
+
50
+ it 'returns the length of the shorter string for prefixes' do
51
+ s1 = 'elvis'
52
+ s2 = 'elvis aaron presley'
53
+ expect(Strings.diff_index(s1, s2)).to eq(5)
54
+ expect(Strings.diff_index(s2, s1)).to eq(5)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'berkeley_library/util/times'
3
+
4
+ module BerkeleyLibrary
5
+ module Util
6
+ describe Times do
7
+ describe :ensure_utc do
8
+ it 'returns a UTC time unchanged' do
9
+ time = Time.parse('2021-02-05 16:19:11.37707 -0800')
10
+ utc_time = time.getutc
11
+ expect(Times.ensure_utc(utc_time)).to be(utc_time)
12
+ end
13
+
14
+ it 'converts a non-UTC time' do
15
+ time = Time.parse('2021-02-06 08:19:11.37707 +0800')
16
+ expect(Times.ensure_utc(time)).to eq(time.getutc)
17
+ expect(time.gmt_offset).to eq(28_800), 'Times.ensure_utc() should not modify its argument'
18
+ end
19
+
20
+ it 'accepts a Date' do
21
+ date = Date.new(2021, 2, 6)
22
+ utc_time = Time.new(2021, 2, 6).getutc
23
+ expect(Times.ensure_utc(date)).to eq(utc_time)
24
+ end
25
+
26
+ it 'accepts a Datetime' do
27
+ datetime = DateTime.parse('2021-02-05 16:19:11.37707 -0800')
28
+ utc_time = Time.parse('2021-02-06 00:19:11.37707 UTC')
29
+ expect(Times.ensure_utc(datetime)).to eq(utc_time)
30
+ end
31
+
32
+ it 'rejects non-date/time objects' do
33
+ # noinspection RubyYardParamTypeMatch
34
+ expect { Times.ensure_utc(Object.new) }.to raise_error(ArgumentError)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module URIs
6
+ describe Requester do
7
+ describe :get do
8
+ it 'returns an HTTP response body for a URL string' do
9
+ url = 'https://example.org/'
10
+ expected_body = 'Help! I am trapped in a unit test'
11
+ stub_request(:get, url).to_return(body: expected_body)
12
+
13
+ result = Requester.get(url)
14
+ expect(result).to eq(expected_body)
15
+ end
16
+
17
+ it 'returns an HTTP response body for a URI' do
18
+ uri = URI.parse('https://example.org/')
19
+ expected_body = 'Help! I am trapped in a unit test'
20
+ stub_request(:get, uri).to_return(body: expected_body)
21
+
22
+ result = Requester.get(uri)
23
+ expect(result).to eq(expected_body)
24
+ end
25
+
26
+ it 'appends query parameters' do
27
+ url = 'https://example.org/'
28
+ params = { p1: 1, p2: 2 }
29
+ url_with_query = "#{url}?#{URI.encode_www_form(params)}"
30
+ expected_body = 'Help! I am trapped in a unit test'
31
+ stub_request(:get, url_with_query).to_return(body: expected_body)
32
+
33
+ result = Requester.get(url, params: params)
34
+ expect(result).to eq(expected_body)
35
+ end
36
+
37
+ it 'sends request headers' do
38
+ url = 'https://example.org/'
39
+ headers = { 'X-help' => 'I am trapped in a unit test' }
40
+ expected_body = 'Help! I am trapped in a unit test'
41
+ stub_request(:get, url).with(headers: headers).to_return(body: expected_body)
42
+
43
+ result = Requester.get(url, headers: headers)
44
+ expect(result).to eq(expected_body)
45
+ end
46
+
47
+ it 'raises an error for a failure status' do
48
+ url = 'https://example.org/'
49
+ stub_request(:get, url).to_return(status: 404)
50
+
51
+ expect { Requester.get(url) }.to raise_error(RestClient::RequestFailed)
52
+ end
53
+
54
+ it 'raises an error for a weird non-failure status' do
55
+ url = 'https://example.org/'
56
+ stub_request(:get, url).to_return(status: 207)
57
+
58
+ expect { Requester.get(url) }.to raise_error(RestClient::RequestFailed)
59
+ end
60
+
61
+ it 'handles redirects' do
62
+ url1 = 'https://example.org/'
63
+ url2 = 'https://example.edu/'
64
+ stub_request(:get, url1).to_return(status: 302, headers: { 'Location' => url2 })
65
+ expected_body = 'Help! I am trapped in a unit test'
66
+ stub_request(:get, url2).to_return(body: expected_body)
67
+
68
+ result = Requester.get(url1)
69
+ expect(result).to eq(expected_body)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module URIs
6
+ describe Validator do
7
+ describe :uri_or_nil do
8
+ it 'returns a URI unchanged' do
9
+ uri = URI.parse('http://example.org/')
10
+ expect(Validator.uri_or_nil(uri)).to be(uri)
11
+ end
12
+
13
+ it 'converts a string to a URI' do
14
+ url = 'http://example.org/'
15
+ expect(Validator.uri_or_nil(url)).to eq(URI.parse(url))
16
+ end
17
+
18
+ it 'returns nil for nil' do
19
+ expect(Validator.uri_or_nil(nil)).to be_nil
20
+ end
21
+
22
+ it 'raises an error for invalid URL strings' do
23
+ bad_url = 'not a uri'
24
+ expect { Validator.uri_or_nil(bad_url) }.to raise_error(URI::InvalidURIError)
25
+ end
26
+ end
27
+
28
+ describe :url_str_or_nil do
29
+ it 'returns a URL string for a URI' do
30
+ uri = URI.parse('http://example.org/')
31
+ expect(Validator.url_str_or_nil(uri)).to eq(uri.to_s)
32
+ end
33
+
34
+ it 'returns a URL string' do
35
+ url = 'http://example.org/'
36
+ expect(Validator.url_str_or_nil(url)).to eq(url)
37
+ end
38
+
39
+ it 'returns nil for nil' do
40
+ expect(Validator.url_str_or_nil(nil)).to be_nil
41
+ end
42
+
43
+ it 'raises an error for invalid URL strings' do
44
+ bad_url = 'not a uri'
45
+ expect { Validator.url_str_or_nil(bad_url) }.to raise_error(URI::InvalidURIError)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end