jsi 0.0.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.
@@ -0,0 +1,133 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe JSI::JSON::ArrayNode do
4
+ # document of the node being tested
5
+ let(:document) { ['a', ['b', 'q'], {'c' => {'d' => 'e'}}] }
6
+ # by default the node is the whole document
7
+ let(:path) { [] }
8
+ # the node being tested
9
+ let(:node) { JSI::JSON::Node.new_by_type(document, path) }
10
+
11
+ describe '#[] bad index' do
12
+ it 'improves TypeError for Array subsript' do
13
+ err = assert_raises(TypeError) do
14
+ node[:x]
15
+ end
16
+ assert_match(/^subscripting with :x \(Symbol\) from Array. self is: #\[<JSI::JSON::ArrayNode fragment="#">/, err.message)
17
+ end
18
+ end
19
+ describe '#each' do
20
+ it 'iterates, one argument' do
21
+ out = []
22
+ node.each do |arg|
23
+ out << arg
24
+ end
25
+ assert_instance_of(JSI::JSON::ArrayNode, node[1])
26
+ assert_instance_of(JSI::JSON::HashNode, node[2])
27
+ assert_equal(['a', node[1], node[2]], out)
28
+ end
29
+ it 'returns self' do
30
+ assert_equal(node.each { }.object_id, node.object_id)
31
+ end
32
+ it 'returns an enumerator when called with no block' do
33
+ enum = node.each
34
+ assert_instance_of(Enumerator, enum)
35
+ assert_equal(['a', node[1], node[2]], enum.to_a)
36
+ end
37
+ end
38
+ describe '#to_ary' do
39
+ it 'returns a Array with Nodes in' do
40
+ assert_instance_of(Array, node.to_ary)
41
+ assert_equal(['a', node[1], node[2]], node.to_ary)
42
+ end
43
+ end
44
+ describe '#as_json' do
45
+ let(:document) { ['a', 'b'] }
46
+ it '#as_json' do
47
+ assert_equal(['a', 'b'], node.as_json)
48
+ assert_equal(['a', 'b'], node.as_json(some_option: false))
49
+ end
50
+ end
51
+ # these methods just delegate to Array so not going to test excessively
52
+ describe 'index only methods' do
53
+ it('#each_index') { assert_equal([0, 1, 2], node.each_index.to_a) }
54
+ it('#empty?') { assert_equal(false, node.empty?) }
55
+ it('#length') { assert_equal(3, node.length) }
56
+ it('#size') { assert_equal(3, node.size) }
57
+ end
58
+ describe 'index + element methods' do
59
+ it('#|') { assert_equal(['a', node[1], node[2], 0], node | [0]) }
60
+ it('#&') { assert_equal(['a'], node & ['a']) }
61
+ it('#*') { assert_equal(node.to_a, node * 1) }
62
+ it('#+') { assert_equal(node.to_a, node + []) }
63
+ it('#-') { assert_equal([node[1], node[2]], node - ['a']) }
64
+ it('#<=>') { assert_equal(1, node <=> []) }
65
+ it('#<=>') { assert_equal(-1, [] <=> node) }
66
+ require 'abbrev'
67
+ it('#abbrev') { assert_equal({'a' => 'a'}, JSI::JSON::Node.new_by_type(['a'], []).abbrev) }
68
+ it('#assoc') { assert_equal(['b', 'q'], node.assoc('b')) }
69
+ it('#at') { assert_equal('a', node.at(0)) }
70
+ it('#bsearch') { assert_equal(nil, node.bsearch { false }) }
71
+ it('#bsearch_index') { assert_equal(nil, node.bsearch_index { false }) } if [].respond_to?(:bsearch_index)
72
+ it('#combination') { assert_equal([['a'], [node[1]], [node[2]]], node.combination(1).to_a) }
73
+ it('#count') { assert_equal(1, node.count('a')) }
74
+ it('#cycle') { assert_equal(node.to_a, node.cycle(1).to_a) }
75
+ it('#dig') { assert_equal('e', node.dig(2, 'c', 'd')) } if [].respond_to?(:dig)
76
+ it('#drop') { assert_equal([node[2]], node.drop(2)) }
77
+ it('#drop_while') { assert_equal([node[1], node[2]], node.drop_while { |e| e == 'a' }) }
78
+ it('#fetch') { assert_equal('a', node.fetch(0)) }
79
+ it('#find_index') { assert_equal(0, node.find_index { true }) }
80
+ it('#first') { assert_equal('a', node.first) }
81
+ it('#include?') { assert_equal(true, node.include?('a')) }
82
+ it('#index') { assert_equal(0, node.index('a')) }
83
+ it('#join') { assert_equal('a b', JSI::JSON::Node.new_by_type(['a', 'b'], []).join(' ')) }
84
+ it('#last') { assert_equal(node[2], node.last) }
85
+ it('#pack') { assert_equal(' ', JSI::JSON::Node.new_by_type([32], []).pack('c')) }
86
+ it('#permutation') { assert_equal([['a'], [node[1]], [node[2]]], node.permutation(1).to_a) }
87
+ it('#product') { assert_equal([], node.product([])) }
88
+ # due to differences in implementation between #assoc and #rassoc, the reason for which
89
+ # I cannot begin to fathom, assoc works but rassoc does not because rassoc has different
90
+ # type checking than assoc for the array(like) array elements.
91
+ # compare:
92
+ # assoc: https://github.com/ruby/ruby/blob/v2_5_0/array.c#L3780-L3813
93
+ # rassoc: https://github.com/ruby/ruby/blob/v2_5_0/array.c#L3815-L3847
94
+ # for this reason, rassoc is NOT defined on Arraylike and #content must be called.
95
+ it('#rassoc') { assert_equal(['b', 'q'], node.content.rassoc('q')) }
96
+ it('#repeated_combination') { assert_equal([[]], node.repeated_combination(0).to_a) }
97
+ it('#repeated_permutation') { assert_equal([[]], node.repeated_permutation(0).to_a) }
98
+ it('#reverse') { assert_equal([node[2], node[1], 'a'], node.reverse) }
99
+ it('#reverse_each') { assert_equal([node[2], node[1], 'a'], node.reverse_each.to_a) }
100
+ it('#rindex') { assert_equal(0, node.rindex('a')) }
101
+ it('#rotate') { assert_equal([node[1], node[2], 'a'], node.rotate) }
102
+ it('#sample') { assert_equal('a', JSI::JSON::Node.new_by_type(['a'], []).sample) }
103
+ it('#shelljoin') { assert_equal('a', JSI::JSON::Node.new_by_type(['a'], []).shelljoin) } if [].respond_to?(:shelljoin)
104
+ it('#shuffle') { assert_equal(3, node.shuffle.size) }
105
+ it('#slice') { assert_equal(['a'], node.slice(0, 1)) }
106
+ it('#sort') { assert_equal(['a'], JSI::JSON::Node.new_by_type(['a'], []).sort) }
107
+ it('#take') { assert_equal(['a'], node.take(1)) }
108
+ it('#take_while') { assert_equal([], node.take_while { false }) }
109
+ it('#transpose') { assert_equal([], JSI::JSON::Node.new_by_type([], []).transpose) }
110
+ it('#uniq') { assert_equal(node.to_a, node.uniq) }
111
+ it('#values_at') { assert_equal(['a'], node.values_at(0)) }
112
+ it('#zip') { assert_equal([['a', 'a'], [node[1], node[1]], [node[2], node[2]]], node.zip(node)) }
113
+ end
114
+ describe 'modified copy methods' do
115
+ it('#reject') { assert_equal(JSI::JSON::Node.new_by_type(['a'], []), node.reject { |e| e != 'a' }) }
116
+ it('#select') { assert_equal(JSI::JSON::Node.new_by_type(['a'], []), node.select { |e| e == 'a' }) }
117
+ it('#compact') { assert_equal(node, node.compact) }
118
+ describe 'at a depth' do
119
+ let(:document) { [['b', 'q'], {'c' => ['d', 'e']}] }
120
+ let(:path) { ['1', 'c'] }
121
+ it('#select') do
122
+ selected = node.select { |e| e == 'd' }
123
+ equivalent = JSI::JSON::Node.new_by_type([['b', 'q'], {'c' => ['d']}], ['1', 'c'])
124
+ assert_equal(equivalent, selected)
125
+ end
126
+ end
127
+ end
128
+ JSI::Arraylike::DESTRUCTIVE_METHODS.each do |destructive_method_name|
129
+ it("does not respond to destructive method #{destructive_method_name}") do
130
+ assert(!node.respond_to?(destructive_method_name))
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,117 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe JSI::JSON::HashNode do
4
+ # document of the node being tested
5
+ let(:document) { {'a' => 'b', 'c' => {'d' => 'e'}} }
6
+ # by default the node is the whole document
7
+ let(:path) { [] }
8
+ # the node being tested
9
+ let(:node) { JSI::JSON::Node.new_by_type(document, path) }
10
+
11
+ describe '#each' do
12
+ it 'iterates, one argument' do
13
+ out = []
14
+ node.each do |arg|
15
+ out << arg
16
+ end
17
+ assert_instance_of(JSI::JSON::HashNode, node['c'])
18
+ assert_equal([['a', 'b'], ['c', node['c']]], out)
19
+ end
20
+ it 'iterates, two arguments' do
21
+ out = []
22
+ node.each do |k, v|
23
+ out << [k, v]
24
+ end
25
+ assert_instance_of(JSI::JSON::HashNode, node['c'])
26
+ assert_equal([['a', 'b'], ['c', node['c']]], out)
27
+ end
28
+ it 'returns self' do
29
+ assert_equal(node.each { }.object_id, node.object_id)
30
+ end
31
+ it 'returns an enumerator when called with no block' do
32
+ enum = node.each
33
+ assert_instance_of(Enumerator, enum)
34
+ assert_equal([['a', 'b'], ['c', node['c']]], enum.to_a)
35
+ end
36
+ end
37
+ describe '#to_hash' do
38
+ it 'returns a Hash with Nodes in' do
39
+ assert_instance_of(Hash, node.to_hash)
40
+ assert_equal({'a' => 'b', 'c' => node['c']}, node.to_hash)
41
+ end
42
+ end
43
+ describe '#merge' do
44
+ let(:document) { {'a' => {'b' => 0}, 'c' => {'d' => 'e'}} }
45
+ # testing the node at 'c' here, merging a hash at a path within a document.
46
+ let(:path) { ['c'] }
47
+ it 'merges' do
48
+ merged = node.merge('x' => 'y')
49
+ # check the content at 'c' was merged with the remainder of the document intact (at 'a')
50
+ assert_equal({'a' => {'b' => 0}, 'c' => {'d' => 'e', 'x' => 'y'}}, merged.document)
51
+ # check the original node retains its original document
52
+ assert_equal({'a' => {'b' => 0}, 'c' => {'d' => 'e'}}, node.document)
53
+ # check that unnecessary copies of unaffected parts of the document were not made
54
+ assert_equal(node.document['a'].object_id, merged.document['a'].object_id)
55
+ end
56
+ end
57
+ describe '#as_json' do
58
+ let(:document) { {'a' => 'b'} }
59
+ it '#as_json' do
60
+ assert_equal({'a' => 'b'}, node.as_json)
61
+ assert_equal({'a' => 'b'}, node.as_json(this_option: 'what?'))
62
+ end
63
+ end
64
+ # these methods just delegate to Hash so not going to test excessively
65
+ describe 'key only methods' do
66
+ it('#each_key') { assert_equal(['a', 'c'], node.each_key.to_a) }
67
+ it('#empty?') { assert_equal(false, node.empty?) }
68
+ it('#has_key?') { assert_equal(true, node.has_key?('a')) }
69
+ it('#include?') { assert_equal(false, node.include?('q')) }
70
+ it('#key?') { assert_equal(true, node.key?('c')) }
71
+ it('#keys') { assert_equal(['a', 'c'], node.keys) }
72
+ it('#length') { assert_equal(2, node.length) }
73
+ it('#member?') { assert_equal(false, node.member?(0)) }
74
+ it('#size') { assert_equal(2, node.size) }
75
+ end
76
+ describe 'key + value methods' do
77
+ it('#<') { assert_equal(true, node < {'a' => 'b', 'c' => node['c'], 'x' => 'y'}) } if {}.respond_to?(:<)
78
+ it('#<=') { assert_equal(true, node <= node) } if {}.respond_to?(:<=)
79
+ it('#>') { assert_equal(true, node > {}) } if {}.respond_to?(:>)
80
+ it('#>=') { assert_equal(false, node >= {'foo' => 'bar'}) } if {}.respond_to?(:>=)
81
+ it('#any?') { assert_equal(false, node.any? { |k, v| v == 3 }) }
82
+ it('#assoc') { assert_equal(['a', 'b'], node.assoc('a')) }
83
+ it('#dig') { assert_equal('e', node.dig('c', 'd')) } if {}.respond_to?(:dig)
84
+ it('#each_pair') { assert_equal([['a', 'b'], ['c', node['c']]], node.each_pair.to_a) }
85
+ it('#each_value') { assert_equal(['b', node['c']], node.each_value.to_a) }
86
+ it('#fetch') { assert_equal('b', node.fetch('a')) }
87
+ it('#fetch_values') { assert_equal(['b'], node.fetch_values('a')) } if {}.respond_to?(:fetch_values)
88
+ it('#has_value?') { assert_equal(true, node.has_value?('b')) }
89
+ it('#invert') { assert_equal({'b' => 'a', node['c'] => 'c'}, node.invert) }
90
+ it('#key') { assert_equal('a', node.key('b')) }
91
+ it('#rassoc') { assert_equal(['a', 'b'], node.rassoc('b')) }
92
+ it('#to_h') { assert_equal({'a' => 'b', 'c' => node['c']}, node.to_h) }
93
+ it('#to_proc') { assert_equal('b', node.to_proc.call('a')) } if {}.respond_to?(:to_proc)
94
+ if {}.respond_to?(:transform_values)
95
+ it('#transform_values') { assert_equal({'a' => nil, 'c' => nil}, node.transform_values { |_| nil}) }
96
+ end
97
+ it('#value?') { assert_equal(false, node.value?('0')) }
98
+ it('#values') { assert_equal(['b', node['c']], node.values) }
99
+ it('#values_at') { assert_equal(['b'], node.values_at('a')) }
100
+ end
101
+ describe 'modified copy methods' do
102
+ # I'm going to rely on the #merge test above to test the modified copy functionality and just do basic
103
+ # tests of all the modified copy methods here
104
+ it('#merge') { assert_equal(node, node.merge({})) }
105
+ it('#reject') { assert_equal(JSI::JSON::Node.new_by_type({}, []), node.reject { true }) }
106
+ it('#select') { assert_equal(JSI::JSON::Node.new_by_type({}, []), node.select { false }) }
107
+ # Hash#compact only available as of ruby 2.5.0
108
+ if {}.respond_to?(:compact)
109
+ it('#compact') { assert_equal(node, node.compact) }
110
+ end
111
+ end
112
+ JSI::Hashlike::DESTRUCTIVE_METHODS.each do |destructive_method_name|
113
+ it("does not respond to destructive method #{destructive_method_name}") do
114
+ assert(!node.respond_to?(destructive_method_name))
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,288 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe JSI::JSON::Node do
4
+ let(:path) { [] }
5
+ let(:node) { JSI::JSON::Node.new(document, path) }
6
+
7
+ describe 'initialization' do
8
+ it 'initializes' do
9
+ node = JSI::JSON::Node.new({'a' => 'b'}, [])
10
+ assert_equal({'a' => 'b'}, node.document)
11
+ assert_equal([], node.path)
12
+ end
13
+ end
14
+ describe 'initialization by .new_by_type' do
15
+ it 'initializes HashNode' do
16
+ node = JSI::JSON::Node.new_by_type({'a' => 'b'}, [])
17
+ assert_instance_of(JSI::JSON::HashNode, node)
18
+ assert_equal({'a' => 'b'}, node.document)
19
+ end
20
+ it 'initializes ArrayNode' do
21
+ node = JSI::JSON::Node.new_by_type(['a', 'b'], [])
22
+ assert_instance_of(JSI::JSON::ArrayNode, node)
23
+ assert_equal(['a', 'b'], node.document)
24
+ end
25
+ it 'initializes Node' do
26
+ object = Object.new
27
+ node = JSI::JSON::Node.new_by_type(object, [])
28
+ assert_instance_of(JSI::JSON::Node, node)
29
+ assert_equal(object, node.document)
30
+ end
31
+ end
32
+ describe '#pointer' do
33
+ it 'is a ::JSON::Schema::Pointer' do
34
+ assert_instance_of(::JSON::Schema::Pointer, JSI::JSON::Node.new({}, []).pointer)
35
+ end
36
+ end
37
+ describe '#content' do
38
+ it 'returns the content at the root' do
39
+ assert_equal({'a' => 'b'}, JSI::JSON::Node.new({'a' => 'b'}, []).content)
40
+ end
41
+ it 'returns the content from the deep' do
42
+ assert_equal('b', JSI::JSON::Node.new([0, {'x' => [{'a' => ['b']}]}], [1, 'x', 0, 'a', 0]).content)
43
+ end
44
+ end
45
+ describe '#deref' do
46
+ let(:document) do
47
+ {
48
+ 'foo' => {'bar' => ['baz']},
49
+ 'a' => {'$ref' => '#/foo'},
50
+ }
51
+ end
52
+ it 'follows a $ref' do
53
+ assert_equal({'bar' => ['baz']}, node['a'].deref.content)
54
+ end
55
+ it 'returns the node when there is no $ref to follow' do
56
+ assert_equal({'bar' => ['baz']}, node['foo'].deref.content)
57
+ end
58
+ describe "dealing with google's invalid $refs" do
59
+ let(:document) do
60
+ {
61
+ 'schemas' => {'bar' => {'description' => ['baz']}},
62
+ 'a' => {'$ref' => 'bar', 'foo' => 'bar'},
63
+ }
64
+ end
65
+ it 'subscripts a node consisting of a $ref WITHOUT following' do
66
+ subscripted = node['a']
67
+ assert_equal({'$ref' => 'bar', 'foo' => 'bar'}, subscripted.content)
68
+ assert_equal(['a'], subscripted.path)
69
+ end
70
+ it 'looks for a node in #/schemas with the name of the $ref' do
71
+ assert_equal({'description' => ['baz']}, node['a'].deref.content)
72
+ end
73
+ it 'follows a $ref when subscripting past it' do
74
+ subscripted = node['a']['description']
75
+ assert_equal(['baz'], subscripted.content)
76
+ assert_equal(['schemas', 'bar', 'description'], subscripted.path)
77
+ end
78
+ it 'does not follow a $ref when subscripting a key that is present' do
79
+ subscripted = node['a']['foo']
80
+ assert_equal('bar', subscripted)
81
+ end
82
+ end
83
+ describe "dealing with whatever this is" do
84
+ # I think google uses this style in some cases maybe. I don't remember.
85
+ let(:document) do
86
+ {
87
+ 'schemas' => {'bar' => {'id' => 'BarID', 'description' => 'baz'}},
88
+ 'a' => {'$ref' => 'BarID'},
89
+ }
90
+ end
91
+ it 'looks for a node in #/schemas with the name of the $ref' do
92
+ assert_equal({'id' => 'BarID', 'description' => 'baz'}, node['a'].deref.content)
93
+ end
94
+ end
95
+ end
96
+ describe '#[]' do
97
+ describe 'without dereferencing' do
98
+ let(:document) { [0, {'x' => [{'a' => ['b']}]}] }
99
+ it 'subscripts arrays and hashes' do
100
+ assert_equal('b', node[1]['x'][0]['a'][0])
101
+ end
102
+ it 'returns ArrayNode for an array' do
103
+ subscripted = node[1]['x']
104
+ assert_instance_of(JSI::JSON::ArrayNode, subscripted)
105
+ assert_equal([{'a' => ['b']}], subscripted.content)
106
+ assert_equal([1, 'x'], subscripted.path)
107
+ end
108
+ it 'returns HashNode for a Hash' do
109
+ subscripted = node[1]
110
+ assert_instance_of(JSI::JSON::HashNode, subscripted)
111
+ assert_equal({'x' => [{'a' => ['b']}]}, subscripted.content)
112
+ assert_equal([1], subscripted.path)
113
+ end
114
+ end
115
+ describe 'with dereferencing' do
116
+ let(:document) do
117
+ {
118
+ 'foo' => {'bar' => ['baz']},
119
+ 'a' => {'$ref' => '#/foo', 'description' => 'hi'}, # not sure a description is actually allowed here, whatever
120
+ }
121
+ end
122
+ it 'subscripts a node consisting of a $ref WITHOUT following' do
123
+ subscripted = node['a']
124
+ assert_equal({'$ref' => '#/foo', 'description' => 'hi'}, subscripted.content)
125
+ assert_equal(['a'], subscripted.path)
126
+ end
127
+ it 'follows a $ref when subscripting past it' do
128
+ subscripted = node['a']['bar']
129
+ assert_equal(['baz'], subscripted.content)
130
+ assert_equal(['foo', 'bar'], subscripted.path)
131
+ end
132
+ it 'does not follow a $ref when subscripting a key that is present' do
133
+ subscripted = node['a']['description']
134
+ assert_equal('hi', subscripted)
135
+ end
136
+ end
137
+ end
138
+ describe '#[]=' do
139
+ let(:document) { [0, {'x' => [{'a' => ['b']}]}] }
140
+ it 'assigns' do
141
+ node[0] = 'abcdefg'
142
+ assert_equal(['abcdefg', {'x' => [{'a' => ['b']}]}], document)
143
+ string_node = JSI::JSON::Node.new(document, [0])
144
+ string_node[0..2] = '0'
145
+ assert_equal(['0defg', {'x' => [{'a' => ['b']}]}], document)
146
+ node[0] = node[1]
147
+ assert_equal([{'x' => [{'a' => ['b']}]}, {'x' => [{'a' => ['b']}]}], document)
148
+ end
149
+ it 'assigns, deeper' do
150
+ node[1]['y'] = node[1]['x'][0]
151
+ assert_equal([0, {'x' => [{'a' => ['b']}], 'y' => {'a' => ['b']}}], document)
152
+ end
153
+ end
154
+ describe '#document_node' do
155
+ let(:document) { {'a' => {'b' => 3}} }
156
+ it 'has content that is the document' do
157
+ assert_equal({'a' => {'b' => 3}}, node['a'].document_node.content)
158
+ end
159
+ end
160
+ describe '#parent_node' do
161
+ let(:document) { {'a' => {'b' => []}} }
162
+ it 'finds a parent' do
163
+ sub = node['a']['b']
164
+ assert_equal(['a', 'b'], sub.path)
165
+ parent = sub.parent_node
166
+ assert_equal(['a'], parent.path)
167
+ assert_equal({'b' => []}, parent.content)
168
+ assert_equal(node['a'], parent)
169
+ root_from_sub = sub.parent_node.parent_node
170
+ assert_equal([], root_from_sub.path)
171
+ assert_equal({'a' => {'b' => []}}, root_from_sub.content)
172
+ assert_equal(node, root_from_sub)
173
+ err = assert_raises(::JSON::Schema::Pointer::ReferenceError) do
174
+ root_from_sub.parent_node
175
+ end
176
+ assert_match(/\Acannot access parent of root node: #\{<JSI::JSON::HashNode/, err.message)
177
+ end
178
+ end
179
+ describe '#pointer_path' do
180
+ let(:document) { {'a' => {'b' => 3}} }
181
+ it 'is empty' do
182
+ assert_equal('', node.pointer_path)
183
+ end
184
+ it 'is not empty' do
185
+ assert_equal('/a', node['a'].pointer_path)
186
+ end
187
+ describe 'containing an empty string and some slashes and tildes that need escaping' do
188
+ let(:document) { {'' => {'a/b~c!d#e[f]' => []}} }
189
+ it 'matches' do
190
+ assert_equal('//a~1b~0c!d#e[f]', node['']['a/b~c!d#e[f]'].pointer_path)
191
+ end
192
+ end
193
+ end
194
+ describe '#fragment' do
195
+ let(:document) { {'a' => {'b' => 3}} }
196
+ it 'is empty' do
197
+ assert_equal('#', node.fragment)
198
+ end
199
+ it 'is not empty' do
200
+ assert_equal('#/a', node['a'].fragment)
201
+ end
202
+ describe 'containing an empty string and some slashes and tildes that need escaping' do
203
+ let(:document) { {'' => {'a/b~c!d#e[f]' => []}} }
204
+ it 'matches' do
205
+ assert_equal('#//a~1b~0c!d#e%5Bf%5D', node['']['a/b~c!d#e[f]'].fragment)
206
+ end
207
+ end
208
+ end
209
+ describe '#modified_copy' do
210
+ let(:document) { [['b', 'q'], {'c' => ['d', 'e']}] }
211
+ let(:path) { ['1', 'c'] }
212
+ it 'returns a different object' do
213
+ # simplest thing
214
+ modified_dup = node.modified_copy(&:dup)
215
+ # it is equal - being a dup
216
+ assert_equal(modified_dup, node)
217
+ # but different object
218
+ refute_equal(node.object_id, modified_dup.object_id)
219
+ # the parents, obviously, are different
220
+ refute_equal(node.parent_node.content.object_id, modified_dup.parent_node.content.object_id)
221
+ refute_equal(node.parent_node.parent_node.content.object_id, modified_dup.parent_node.parent_node.content.object_id)
222
+ # but any untouched part(s) - in this case the ['b', 'q'] at document[0] - are untouched
223
+ assert_equal(node.document_node[0].content.object_id, modified_dup.document_node[0].content.object_id)
224
+ end
225
+ it 'returns the same object' do
226
+ unmodified_dup = node.modified_copy { |o| o }
227
+ assert_equal(unmodified_dup, node)
228
+ # same object, since the block just returned it
229
+ refute_equal(node.object_id, unmodified_dup.object_id)
230
+ # the parents are unchanged since the object is the same
231
+ assert_equal(node.parent_node.content.object_id, unmodified_dup.parent_node.content.object_id)
232
+ assert_equal(node.parent_node.parent_node.content.object_id, unmodified_dup.parent_node.parent_node.content.object_id)
233
+ # same as the other: any untouched part(s) - in this case the ['b', 'q'] at document[0] - are untouched
234
+ assert_equal(node.document_node[0].content.object_id, unmodified_dup.document_node[0].content.object_id)
235
+ end
236
+ it 'raises subscripting string from array' do
237
+ err = assert_raises(TypeError) { JSI::JSON::Node.new(document, ['x']).modified_copy(&:dup) }
238
+ assert_match(%r(\Abad subscript "x" with remaining subpath: \[\] for array: \[.*\]\z)m, err.message)
239
+ end
240
+ it 'raises subscripting from invalid subpath' do
241
+ err = assert_raises(TypeError) { JSI::JSON::Node.new(document, [0, 0, 'what']).modified_copy(&:dup) }
242
+ assert_match(%r(bad subscript: "what" with remaining subpath: \[\] for content: "b"\z)m, err.message)
243
+ end
244
+ end
245
+ describe '#inspect' do
246
+ let(:document) { {'a' => {'c' => ['d', 'e']}} }
247
+ let(:path) { ['a'] }
248
+ it 'inspects' do
249
+ assert_equal(%Q(#<JSI::JSON::Node fragment="#/a" {"c"=>["d", "e"]}>), node.inspect)
250
+ end
251
+ end
252
+ describe '#pretty_print' do
253
+ let(:document) { {'a' => {'c' => ['d', 'e']}} }
254
+ let(:path) { ['a'] }
255
+ it 'pretty prints' do
256
+ assert_equal(%Q(#<JSI::JSON::Node fragment="#/a" {"c"=>["d", "e"]}>), node.pretty_inspect.chomp)
257
+ end
258
+ end
259
+ describe '#fingerprint' do
260
+ it 'hashes consistently' do
261
+ assert_equal('x', {JSI::JSON::Node.new([0], []) => 'x'}[JSI::JSON::Node.new([0], [])])
262
+ end
263
+ it 'hashes consistently regardless of the Node being decorated as a subclass' do
264
+ assert_equal('x', {JSI::JSON::Node.new_by_type([0], []) => 'x'}[JSI::JSON::Node.new([0], [])])
265
+ assert_equal('x', {JSI::JSON::Node.new([0], []) => 'x'}[JSI::JSON::Node.new_by_type([0], [])])
266
+ end
267
+ it '==' do
268
+ assert_equal(JSI::JSON::Node.new([0], []), JSI::JSON::Node.new([0], []))
269
+ assert_equal(JSI::JSON::Node.new_by_type([0], []), JSI::JSON::Node.new([0], []))
270
+ assert_equal(JSI::JSON::Node.new([0], []), JSI::JSON::Node.new_by_type([0], []))
271
+ assert_equal(JSI::JSON::Node.new_by_type([0], []), JSI::JSON::Node.new_by_type([0], []))
272
+ end
273
+ it '!=' do
274
+ refute_equal(JSI::JSON::Node.new([0], []), JSI::JSON::Node.new({}, []))
275
+ refute_equal(JSI::JSON::Node.new_by_type([0], []), JSI::JSON::Node.new({}, []))
276
+ refute_equal(JSI::JSON::Node.new([0], []), JSI::JSON::Node.new_by_type({}, []))
277
+ refute_equal(JSI::JSON::Node.new_by_type([0], []), JSI::JSON::Node.new_by_type({}, []))
278
+ refute_equal({}, JSI::JSON::Node.new_by_type({}, []))
279
+ refute_equal(JSI::JSON::Node.new_by_type({}, []), {})
280
+ end
281
+ end
282
+ describe '#as_json' do
283
+ let(:document) { {'a' => 'b'} }
284
+ it '#as_json' do
285
+ assert_equal({'a' => 'b'}, node.as_json)
286
+ end
287
+ end
288
+ end