tentd 0.0.1

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