berkeley_library-util 0.1.0

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 (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