tentd 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.
Files changed (101) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +9 -0
  5. data/Guardfile +6 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +49 -0
  8. data/Rakefile +8 -0
  9. data/bin/tent-server +3 -0
  10. data/lib/tentd.rb +31 -0
  11. data/lib/tentd/api.rb +58 -0
  12. data/lib/tentd/api/apps.rb +196 -0
  13. data/lib/tentd/api/authentication_finalize.rb +12 -0
  14. data/lib/tentd/api/authentication_lookup.rb +27 -0
  15. data/lib/tentd/api/authentication_verification.rb +50 -0
  16. data/lib/tentd/api/authorizable.rb +21 -0
  17. data/lib/tentd/api/authorization.rb +14 -0
  18. data/lib/tentd/api/core_profile_data.rb +45 -0
  19. data/lib/tentd/api/followers.rb +218 -0
  20. data/lib/tentd/api/followings.rb +241 -0
  21. data/lib/tentd/api/groups.rb +161 -0
  22. data/lib/tentd/api/middleware.rb +32 -0
  23. data/lib/tentd/api/posts.rb +373 -0
  24. data/lib/tentd/api/profile.rb +78 -0
  25. data/lib/tentd/api/router.rb +123 -0
  26. data/lib/tentd/api/router/caching_headers.rb +49 -0
  27. data/lib/tentd/api/router/extract_params.rb +88 -0
  28. data/lib/tentd/api/router/serialize_response.rb +38 -0
  29. data/lib/tentd/api/user_lookup.rb +10 -0
  30. data/lib/tentd/core_ext/hash/slice.rb +29 -0
  31. data/lib/tentd/datamapper/array_property.rb +23 -0
  32. data/lib/tentd/datamapper/query.rb +19 -0
  33. data/lib/tentd/json_patch.rb +181 -0
  34. data/lib/tentd/model.rb +30 -0
  35. data/lib/tentd/model/app.rb +68 -0
  36. data/lib/tentd/model/app_authorization.rb +113 -0
  37. data/lib/tentd/model/follower.rb +105 -0
  38. data/lib/tentd/model/following.rb +100 -0
  39. data/lib/tentd/model/group.rb +24 -0
  40. data/lib/tentd/model/mention.rb +19 -0
  41. data/lib/tentd/model/notification_subscription.rb +56 -0
  42. data/lib/tentd/model/permissible.rb +227 -0
  43. data/lib/tentd/model/permission.rb +28 -0
  44. data/lib/tentd/model/post.rb +178 -0
  45. data/lib/tentd/model/post_attachment.rb +27 -0
  46. data/lib/tentd/model/post_version.rb +64 -0
  47. data/lib/tentd/model/profile_info.rb +80 -0
  48. data/lib/tentd/model/random_public_id.rb +46 -0
  49. data/lib/tentd/model/serializable.rb +58 -0
  50. data/lib/tentd/model/type_properties.rb +36 -0
  51. data/lib/tentd/model/user.rb +39 -0
  52. data/lib/tentd/model/user_scoped.rb +14 -0
  53. data/lib/tentd/notifications.rb +13 -0
  54. data/lib/tentd/notifications/girl_friday.rb +30 -0
  55. data/lib/tentd/notifications/sidekiq.rb +50 -0
  56. data/lib/tentd/tent_type.rb +20 -0
  57. data/lib/tentd/tent_version.rb +41 -0
  58. data/lib/tentd/version.rb +3 -0
  59. data/spec/fabricators/app_authorizations_fabricator.rb +5 -0
  60. data/spec/fabricators/apps_fabricator.rb +11 -0
  61. data/spec/fabricators/followers_fabricator.rb +14 -0
  62. data/spec/fabricators/followings_fabricator.rb +17 -0
  63. data/spec/fabricators/groups_fabricator.rb +3 -0
  64. data/spec/fabricators/mentions_fabricator.rb +3 -0
  65. data/spec/fabricators/notification_subscriptions_fabricator.rb +4 -0
  66. data/spec/fabricators/permissions_fabricator.rb +1 -0
  67. data/spec/fabricators/post_attachments_fabricator.rb +8 -0
  68. data/spec/fabricators/post_versions_fabricator.rb +12 -0
  69. data/spec/fabricators/posts_fabricator.rb +12 -0
  70. data/spec/fabricators/profile_infos_fabricator.rb +30 -0
  71. data/spec/integration/api/apps_spec.rb +466 -0
  72. data/spec/integration/api/followers_spec.rb +535 -0
  73. data/spec/integration/api/followings_spec.rb +688 -0
  74. data/spec/integration/api/groups_spec.rb +207 -0
  75. data/spec/integration/api/posts_spec.rb +874 -0
  76. data/spec/integration/api/profile_spec.rb +285 -0
  77. data/spec/integration/api/router_spec.rb +102 -0
  78. data/spec/integration/model/app_authorization_spec.rb +59 -0
  79. data/spec/integration/model/app_spec.rb +63 -0
  80. data/spec/integration/model/follower_spec.rb +344 -0
  81. data/spec/integration/model/following_spec.rb +97 -0
  82. data/spec/integration/model/group_spec.rb +39 -0
  83. data/spec/integration/model/notification_subscription_spec.rb +145 -0
  84. data/spec/integration/model/post_spec.rb +658 -0
  85. data/spec/spec_helper.rb +37 -0
  86. data/spec/support/expect_server.rb +3 -0
  87. data/spec/support/json_request.rb +54 -0
  88. data/spec/support/with_constant.rb +23 -0
  89. data/spec/support/with_warnings.rb +6 -0
  90. data/spec/unit/api/authentication_finalize_spec.rb +45 -0
  91. data/spec/unit/api/authentication_lookup_spec.rb +65 -0
  92. data/spec/unit/api/authentication_verification_spec.rb +50 -0
  93. data/spec/unit/api/authorizable_spec.rb +50 -0
  94. data/spec/unit/api/authorization_spec.rb +44 -0
  95. data/spec/unit/api/caching_headers_spec.rb +121 -0
  96. data/spec/unit/core_profile_data_spec.rb +64 -0
  97. data/spec/unit/json_patch_spec.rb +407 -0
  98. data/spec/unit/tent_type_spec.rb +28 -0
  99. data/spec/unit/tent_version_spec.rb +68 -0
  100. data/tentd.gemspec +36 -0
  101. metadata +435 -0
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::API::CoreProfileData do
4
+ let(:tent_profile_type_uri) { "https://tent.io/types/info/core/v0.1.0" }
5
+ let(:entity_url) { "https://smith.example.com" }
6
+ let(:another_entity_url) { "https://alex.example.org" }
7
+ let(:data) do
8
+ {
9
+ "https://tent.io/types/info/core/v0.1.5" => {
10
+ "licenses" => [],
11
+ "entity" => entity_url,
12
+ },
13
+ "https://tent.io/types/info/core/v0.1.1" => {
14
+ "licenses" => [],
15
+ "entity" => entity_url,
16
+ },
17
+ "https://tent.io/types/info/core/v0.2.0" => {
18
+ "licenses" => [],
19
+ "entity" => entity_url,
20
+ },
21
+ "https://tent.io/types/info/musci/v0.1.0" => {
22
+ }
23
+ }
24
+ end
25
+ describe '#expected_version' do
26
+ it 'should return TentVersion representing sever tent profile type uri version' do
27
+ with_constants "TentD::Model::ProfileInfo::TENT_PROFILE_TYPE_URI" => tent_profile_type_uri do
28
+ expect(described_class.new(data).expected_version).to eq(TentD::TentVersion.new('0.1.0'))
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '#versions' do
34
+ it 'should return array of TentVersions found in data matching core info type' do
35
+ expect(described_class.new(data).versions).to eq([
36
+ TentD::TentVersion.new('0.1.1'),
37
+ TentD::TentVersion.new('0.1.5'),
38
+ TentD::TentVersion.new('0.2.0')
39
+ ])
40
+ end
41
+ end
42
+
43
+ describe '#version' do
44
+ it 'should return closest compatible version' do
45
+ expect(described_class.new(data).version).to eq(TentD::TentVersion.new('0.1.1'))
46
+ end
47
+ end
48
+
49
+ describe '#version_key' do
50
+ it 'should return full url of version' do
51
+ expect(described_class.new(data).version_key).to eq("https://tent.io/types/info/core/v0.1.1")
52
+ end
53
+ end
54
+
55
+ describe '#entity?(entity)' do
56
+ it 'should return true if entity matches' do
57
+ expect(described_class.new(data).entity?(entity_url)).to be_true
58
+ end
59
+
60
+ it 'should return false if entity does not match' do
61
+ expect(described_class.new(data).entity?(another_entity_url)).to be_false
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,407 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::JsonPatch::HashPointer do
4
+ context 'when finding pointer' do
5
+ it 'should find nested hash' do
6
+ hash = { "a" => { "b" => { "c" => "foo" } } }
7
+ pointer = described_class.new(hash, "a/b/c")
8
+ expect(pointer.value).to eq("foo")
9
+ end
10
+
11
+ it 'should find position in array' do
12
+ hash = { "a" => ["foo", "bar", "baz"] }
13
+ pointer = described_class.new(hash, "a/1")
14
+ expect(pointer.value).to eq("bar")
15
+ end
16
+
17
+ it "should throw exception if key doesn't exist" do
18
+ hash = { "a" => "foo" }
19
+ pointer = described_class.new(hash, "a/b/c")
20
+ expect(lambda { pointer.value }).to raise_error(described_class::InvalidPointer)
21
+ end
22
+
23
+ it "should throw exception if array position is outsize index range" do
24
+ hash = { "a" => ["foo", "bar", "baz"] }
25
+ pointer = described_class.new(hash, "a/3")
26
+ expect(lambda { pointer.value }).to raise_error(described_class::InvalidPointer)
27
+ end
28
+ end
29
+
30
+ context 'when setting pointer' do
31
+ it "should set nested hash key" do
32
+ pointer = described_class.new({}, "a/b/c")
33
+ pointer.value = "foo"
34
+ expect(pointer.value).to eq("foo")
35
+ end
36
+
37
+ it 'should set value at array index' do
38
+ hash = { "a" => ["foo", "bar"] }
39
+ pointer = described_class.new(hash, "a/1")
40
+ pointer.value = "baz"
41
+ expect(hash["a"]).to be_an(Array)
42
+ expect(pointer.value).to eq("baz")
43
+ expect(described_class.new(hash, "a/2").value).to eq("bar")
44
+
45
+ hash = { "a" => { "b" => ["foo", "baz"] } }
46
+ pointer = described_class.new(hash, "a/b/1")
47
+ pointer.value = "bar"
48
+ expect(hash["a"]["b"]).to be_an(Array)
49
+ expect(pointer.value).to eq("bar")
50
+ end
51
+ end
52
+
53
+ context 'when deleting pointer' do
54
+ it 'should delete key from object' do
55
+ hash = { "a" => { "b" => { "c" => "foo" } } }
56
+ pointer = described_class.new(hash, "a/b/c")
57
+ pointer.delete
58
+ expect(hash).to eq({ "a" => { "b" => {} } })
59
+ end
60
+
61
+ it 'should delete index from array' do
62
+ hash = { "a" => { "b" => ["foo", "baz", "bar"] } }
63
+ pointer = described_class.new(hash, "a/b/1")
64
+ pointer.delete
65
+ expect(hash).to eq({ "a" => { "b" => ["foo", "bar"] } })
66
+ end
67
+
68
+ it 'should throw exception if key does not exist' do
69
+ hash = {}
70
+ pointer = described_class.new(hash, "a")
71
+ expect( lambda { pointer.delete }).to raise_error(described_class::InvalidPointer)
72
+ expect(hash).to eq({})
73
+ end
74
+
75
+ it 'should throw exception if index of array does not exist' do
76
+ hash = { "a" => { "b" => ["foo", "bar"] } }
77
+ pointer = described_class.new(hash, "a/b/2")
78
+ expect( lambda { pointer.delete }).to raise_error(described_class::InvalidPointer)
79
+ expect(hash).to eq({ "a" => { "b" => ["foo", "bar"] } })
80
+ end
81
+ end
82
+
83
+ context 'when moving pointer' do
84
+ it 'should move key to another key' do
85
+ hash = { "a" => { "b" => { "c" => "foo" } } }
86
+ pointer = described_class.new(hash, "a/b")
87
+ pointer.move_to "/b"
88
+ expect(hash).to eq({ "a" => {}, "b" => { "c" => "foo" } })
89
+ end
90
+
91
+ it 'should move array index to another index' do
92
+ hash = { "a" => { "b" => ["foo", "bar"] } }
93
+ pointer = described_class.new(hash, "a/b/0")
94
+ pointer.move_to "a/b/1"
95
+ expect(hash).to eq({ "a" => { "b" => ["bar", "foo"] } })
96
+ end
97
+
98
+ it 'should throw exception if to would overwrite another key' do
99
+ hash = { "a" => { "b" => ["foo", "bar"] } }
100
+ pointer = described_class.new(hash, "a/b/0")
101
+ expect( lambda { pointer.move_to "a/b/c" } ).
102
+ to raise_error(described_class::InvalidPointer)
103
+ expect(hash).to eq({ "a" => { "b" => ["foo", "bar"] } })
104
+ end
105
+ end
106
+
107
+ describe '#exists?' do
108
+ it 'should return false if key not in hash' do
109
+ hash = { "a" => { "b" => { "foo" => "bar" } } }
110
+ pointer = described_class.new(hash, "a/b/c")
111
+ expect(pointer.exists?).to be_false
112
+ end
113
+
114
+ it 'should return false if index not in array' do
115
+ hash = { "a" => { "b" => ["foo", "bar"] } }
116
+ pointer = described_class.new(hash, "a/b/2")
117
+ expect(pointer.exists?).to be_false
118
+ end
119
+
120
+ it 'should return true if key in hash' do
121
+ hash = { "a" => { "b" => { "foo" => "bar" } } }
122
+ pointer = described_class.new(hash, "a/b")
123
+ expect(pointer.exists?).to be_true
124
+
125
+ hash = { "a" => "baz" }
126
+ pointer = described_class.new(hash, "a/b")
127
+ expect(pointer.exists?).to be_true
128
+ end
129
+
130
+ it 'should return true if index in array' do
131
+ hash = { "a" => { "b" => ["foo", "bar"] } }
132
+ pointer = described_class.new(hash, "a/b/1")
133
+ expect(pointer.exists?).to be_true
134
+ end
135
+ end
136
+ end
137
+
138
+ describe TentD::JsonPatch do
139
+ describe 'add' do
140
+ it 'should add new value at specified location' do
141
+ object = {}
142
+ patch_object = [{ "add" => "a/b/c", "value" => ["foo", "bar", "baz"] }]
143
+ TentD::JsonPatch.merge(object, patch_object)
144
+ expect(object).to be_a(Hash)
145
+ expect(object["a"]["b"]["c"]).to eq(["foo", "bar", "baz"])
146
+
147
+ object = { "a" => {} }
148
+ patch_object = [{ "add" => "a/b/c", "value" => ["foo", "bar", "baz"] }]
149
+ TentD::JsonPatch.merge(object, patch_object)
150
+ expect(object).to be_a(Hash)
151
+ expect(object["a"]["b"]["c"]).to eq(["foo", "bar", "baz"])
152
+
153
+ object = { "a" => { "b" => {} } }
154
+ patch_object = [{ "add" => "a/b/c", "value" => ["foo", "bar", "baz"] }]
155
+ TentD::JsonPatch.merge(object, patch_object)
156
+ expect(object).to be_a(Hash)
157
+ expect(object["a"]["b"]["c"]).to eq(["foo", "bar", "baz"])
158
+ end
159
+
160
+ it 'should throw exception if specified location exists' do
161
+ object = { "a" => "foo" }
162
+ patch_object = [{ "add" => "a/b/c", "value" => ["foo", "bar", "baz"] }]
163
+ expect( lambda { TentD::JsonPatch.merge(object, patch_object) } ).
164
+ to raise_error(described_class::ObjectExists)
165
+ expect(object["a"]).to eq("foo")
166
+
167
+ object = { "a" => { "b" => "foo" } }
168
+ patch_object = [{ "add" => "a/b/c", "value" => ["foo", "bar", "baz"] }]
169
+ expect( lambda { TentD::JsonPatch.merge(object, patch_object) } ).
170
+ to raise_error(described_class::ObjectExists)
171
+ expect(object["a"]).to eq("b" => "foo")
172
+
173
+ object = { "a" => { "b" => { "c" => "foo" } } }
174
+ patch_object = [{ "add" => "a/b/c", "value" => ["foo", "bar", "baz"] }]
175
+ expect( lambda { TentD::JsonPatch.merge(object, patch_object) } ).
176
+ to raise_error(described_class::ObjectExists)
177
+ expect(object["a"]).to eq("b" => { "c" => "foo" })
178
+
179
+ object = { "a" => { "b" => { "c" => "foo" } } }
180
+ patch_object = [{ "add" => "a/b/c/d/e/f/g", "value" => ["foo", "bar", "baz"] }]
181
+ expect( lambda { TentD::JsonPatch.merge(object, patch_object) } ).
182
+ to raise_error(described_class::ObjectExists)
183
+ expect(object["a"]).to eq("b" => { "c" => "foo" })
184
+
185
+ object = { "a" => { "b" => ["foo", "bar"] } }
186
+ patch_object = [{ "add" => "a/b/c/d/e/f/g", "value" => ["foo", "bar", "baz"] }]
187
+ expect( lambda { TentD::JsonPatch.merge(object, patch_object) } ).
188
+ to raise_error(described_class::ObjectExists)
189
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
190
+ end
191
+
192
+ context 'when pointer is an array' do
193
+ it 'should add new value at specified index' do
194
+ object = { "a" => ["foo", "bar"] }
195
+ patch_object = [{ "add" => "a/1", "value" => "baz" }]
196
+ TentD::JsonPatch.merge(object, patch_object)
197
+ expect(object["a"]).to eq(["foo", "baz", "bar"])
198
+ end
199
+ end
200
+ end
201
+
202
+ describe 'remove' do
203
+ it 'should remove specified key' do
204
+ object = { "a" => { "b" => { "c" => "foo" } } }
205
+ patch_object = [{ "remove" => "a/b/c" }]
206
+ described_class.merge(object, patch_object)
207
+ expect(object).to eq({ "a" => { "b" => {} } })
208
+ end
209
+
210
+ it 'should throw exception if specified key does not exist' do
211
+ object = { "a" => { "b" => {} } }
212
+ patch_object = [{ "remove" => "a/b/c" }]
213
+ expect( lambda { described_class.merge(object, patch_object) } ).
214
+ to raise_error(described_class::ObjectNotFound)
215
+ expect(object).to eq({ "a" => { "b" => {} } })
216
+ end
217
+
218
+ context 'when pointer is an array' do
219
+ it 'should remove specified index' do
220
+ object = { "a" => { "b" => ["foo", "baz", "bar"] } }
221
+ patch_object = [{ "remove" => "a/b/1" }]
222
+ described_class.merge(object, patch_object)
223
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
224
+ end
225
+
226
+ it 'should throw exception if specified index does not exist' do
227
+ object = { "a" => { "b" => ["foo"] } }
228
+ patch_object = [{ "remove" => "a/b/1" }]
229
+ expect( lambda { described_class.merge(object, patch_object) } ).
230
+ to raise_error(described_class::ObjectNotFound)
231
+ expect(object).to eq({ "a" => { "b" => ["foo"] } })
232
+ end
233
+ end
234
+ end
235
+
236
+ describe 'replace' do
237
+ it 'should replace specified key value' do
238
+ object = { "a" => { "b" => { "c" => "foo" } } }
239
+ patch_object = [{ "replace" => "a", "value" => ["foo", "bar"] }]
240
+ described_class.merge(object, patch_object)
241
+ expect(object).to eq({ "a" => ["foo", "bar"] })
242
+ end
243
+
244
+ it 'should replace specified index of array' do
245
+ object = { "a" => { "b" => ["foo", "baz"] } }
246
+ patch_object = [{ "replace" => "a/b/1", "value" => "bar" }]
247
+ described_class.merge(object, patch_object)
248
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
249
+ end
250
+
251
+ it 'should throw exception if specified key does not exist' do
252
+ object = { "a" => { "b" => {} } }
253
+ patch_object = [{ "replace" => "a/b/c", "value" => "foo" }]
254
+ expect(lambda { described_class.merge(object, patch_object) }).
255
+ to raise_error(described_class::ObjectNotFound)
256
+ expect(object).to eq({ "a" => { "b" => {} } })
257
+ end
258
+
259
+ it 'should throw exception if specified index does not exist' do
260
+ object = { "a" => [] }
261
+ patch_object = [{ "replace" => "a/1", "value" => "bar" }]
262
+ expect(lambda { described_class.merge(object, patch_object) }).
263
+ to raise_error(described_class::ObjectNotFound)
264
+ expect(object).to eq({ "a" => [] })
265
+ end
266
+ end
267
+
268
+ describe 'move' do
269
+ it 'should move specified key to new key' do
270
+ object = { "a" => { "b" => { "c" => "foo" } } }
271
+ patch_object = [{ "move" => "a/b", "to" => "/b" }]
272
+ described_class.merge(object, patch_object)
273
+ expect(object).to eq({ "a" => {}, "b" => { "c" => "foo" } })
274
+ end
275
+
276
+ it 'should move specified index to new index' do
277
+ object = { "a" => { "b" => ["foo", "bar"] } }
278
+ patch_object = [{ "move" => "a/b/0", "to" => "a/b/1" }]
279
+ described_class.merge(object, patch_object)
280
+ expect(object).to eq({ "a" => { "b" => ["bar", "foo"] } })
281
+ end
282
+
283
+ it 'should throw exception if specified key does not exist' do
284
+ object = { "a" => { "b" => { "c" => "foo" } } }
285
+ patch_object = [{ "move" => "a/b/c/d", "to" => "/b" }]
286
+ expect(lambda { described_class.merge(object, patch_object) }).
287
+ to raise_error(described_class::ObjectNotFound)
288
+ expect(object).to eq({ "a" => { "b" => { "c" => "foo" } } })
289
+
290
+ object = { "a" => { "b" => "foo" } }
291
+ patch_object = [{ "move" => "a/b/c", "to" => "a" }]
292
+ expect(lambda { described_class.merge(object, patch_object) }).
293
+ to raise_error(described_class::ObjectNotFound)
294
+ expect(object).to eq({ "a" => { "b" => "foo" } })
295
+ end
296
+
297
+ it 'should throw exception if specified index does not exist' do
298
+ object = { "a" => { "b" => ["foo", "bar"] } }
299
+ patch_object = [{ "move" => "a/b/2", "to" => "a/b/0" }]
300
+ expect(lambda { described_class.merge(object, patch_object) }).
301
+ to raise_error(described_class::ObjectNotFound)
302
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
303
+ end
304
+
305
+ it 'should throw exception if to key would overwrite another' do
306
+ object = { "a" => { "b" => ["foo", "bar"] } }
307
+ patch_object = [{ "move" => "a/b/0", "to" => "a/b/c" }]
308
+ expect(lambda { described_class.merge(object, patch_object) }).
309
+ to raise_error(described_class::ObjectNotFound)
310
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
311
+ end
312
+ end
313
+
314
+ describe 'copy' do
315
+ it 'should copy specified key value to new key' do
316
+ object = { "a" => { "b" => { "c" => "foo" } } }
317
+ patch_object = [{ "copy" => "a/b/c", "to" => "/c" }]
318
+ described_class.merge(object, patch_object)
319
+ expect(object).to eq({ "a" => { "b" => { "c" => "foo" } }, "c" => "foo" })
320
+ end
321
+
322
+ it 'should copy specified index value to new index' do
323
+ object = { "a" => { "b" => ["foo", "bar"] } }
324
+ patch_object = [{ "copy" => "a/b/0", "to" => "a/b/2" }]
325
+ described_class.merge(object, patch_object)
326
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar", "foo"] } })
327
+ end
328
+
329
+ it 'should throw exception if specified key does not exist' do
330
+ object = { "a" => { "b" => { "c" => "foo" } } }
331
+ patch_object = [{ "copy" => "a/b/c/d", "to" => "/b" }]
332
+ expect(lambda { described_class.merge(object, patch_object) }).
333
+ to raise_error(described_class::ObjectNotFound)
334
+ expect(object).to eq({ "a" => { "b" => { "c" => "foo" } } })
335
+
336
+ object = { "a" => { "b" => "foo" } }
337
+ patch_object = [{ "copy" => "a/b/c", "to" => "a" }]
338
+ expect(lambda { described_class.merge(object, patch_object) }).
339
+ to raise_error(described_class::ObjectNotFound)
340
+ expect(object).to eq({ "a" => { "b" => "foo" } })
341
+ end
342
+
343
+ it 'should throw exception if specified index does not exist' do
344
+ object = { "a" => { "b" => ["foo", "bar"] } }
345
+ patch_object = [{ "copy" => "a/b/2", "to" => "a/b/0" }]
346
+ expect(lambda { described_class.merge(object, patch_object) }).
347
+ to raise_error(described_class::ObjectNotFound)
348
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
349
+ end
350
+
351
+ it 'should throw exception if to key would overwrite another' do
352
+ object = { "a" => { "b" => ["foo", "bar"] } }
353
+ patch_object = [{ "copy" => "a/b/0", "to" => "a/b/c" }]
354
+ expect(lambda { described_class.merge(object, patch_object) }).
355
+ to raise_error(described_class::ObjectExists)
356
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
357
+ end
358
+ end
359
+
360
+ describe 'test' do
361
+ it 'should throw exception if specified key does not exist' do
362
+ object = { "a" => { "b" => ["foo", "bar"] } }
363
+ patch_object = [{ "test" => "a/c" }]
364
+ expect(lambda { described_class.merge(object, patch_object) }).
365
+ to raise_error(described_class::ObjectNotFound)
366
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
367
+ end
368
+
369
+ it 'should throw exception if specified key does not equal specified value' do
370
+ object = { "a" => { "b" => ["foo", "bar"] } }
371
+ patch_object = [{ "test" => "a/b/0", "value" => "chunkybacon" }]
372
+ expect(lambda { described_class.merge(object, patch_object) }).
373
+ to raise_error(described_class::ObjectNotFound)
374
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
375
+ end
376
+
377
+ it 'should throw exception if specified index does not exist' do
378
+ object = { "a" => { "b" => ["foo", "bar"] } }
379
+ patch_object = [{ "test" => "a/b/3" }]
380
+ expect(lambda { described_class.merge(object, patch_object) }).
381
+ to raise_error(described_class::ObjectNotFound)
382
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
383
+ end
384
+
385
+ it 'should throw exception if specified index does not equal specified value' do
386
+ object = { "a" => { "b" => ["foo", "bar"] } }
387
+ patch_object = [{ "test" => "a/b/0", "value" => "chunkybacon" }]
388
+ expect(lambda { described_class.merge(object, patch_object) }).
389
+ to raise_error(described_class::ObjectNotFound)
390
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
391
+ end
392
+
393
+ it 'should not do anything if key exists and matches specified value' do
394
+ object = { "a" => { "b" => ["foo", "bar"] } }
395
+ patch_object = [{ "test" => "a/b", "value" => ["foo", "bar"] }]
396
+ described_class.merge(object, patch_object)
397
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
398
+ end
399
+
400
+ it 'should not do anything if index exists and matches specified value' do
401
+ object = { "a" => { "b" => ["foo", "bar"] } }
402
+ patch_object = [{ "test" => "a/b/0", "value" => "foo" }]
403
+ described_class.merge(object, patch_object)
404
+ expect(object).to eq({ "a" => { "b" => ["foo", "bar"] } })
405
+ end
406
+ end
407
+ end