jsi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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