orientdb_client 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,10 @@
1
+ class ClassConfigurator
2
+ def initialize(class_name, node)
3
+ @class_name = class_name
4
+ @node = node
5
+ end
6
+
7
+ def property(property_name, type, options = {})
8
+ @node.create_property(@class_name, property_name, type, options)
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module OrientdbClient
2
+ class OrientdbError < StandardError
3
+ attr_reader :http_code, :response_body
4
+
5
+ def initialize(message = nil, http_code = nil, response_body = nil)
6
+ super(message)
7
+ @http_code = http_code
8
+ @response_body = response_body
9
+ end
10
+ end
11
+
12
+ class ConnectionError < OrientdbError; end
13
+
14
+ # ServerError: server has rejected your command/query because
15
+ # processing it would violate some invariant
16
+ class ServerError < OrientdbError; end
17
+ class TransactionException < ServerError; end
18
+ class DistributedTransactionException < TransactionException; end
19
+ class MVCCError < ServerError; end
20
+
21
+ # ClientError: you did something wrong
22
+ class ClientError < OrientdbError; end
23
+ class UnauthorizedError < ClientError; end
24
+ class IllegalArgumentException < ClientError; end
25
+ class CommandExecutionException < ClientError; end
26
+
27
+ # ConflictError: you tried to create something that already exists
28
+ class ConflictError < ClientError; end
29
+ class DuplicateRecordError < ConflictError; end
30
+ class DistributedDuplicateRecordError < DuplicateRecordError; end
31
+
32
+ class NotFoundError < OrientdbError; end
33
+
34
+ class NegativeArraySizeException < OrientdbError; end
35
+ end
@@ -0,0 +1,25 @@
1
+ module OrientdbClient
2
+ module HttpAdapters
3
+ SESSION_COOKIE_NAME = 'OSESSIONID'
4
+
5
+ class Base
6
+ attr_accessor :username, :password
7
+
8
+ def initialize
9
+ @username = nil
10
+ @password = nil
11
+ @session_id = nil
12
+ end
13
+
14
+ def reset_credentials
15
+ @username = nil
16
+ @password = nil
17
+ @session_id = nil
18
+ end
19
+
20
+ def request
21
+ raise NotImplementedError
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ require 'curb'
2
+
3
+ module OrientdbClient
4
+ module HttpAdapters
5
+ class CurbAdapter < Base
6
+
7
+ def initialize
8
+ super
9
+ @curl = Curl::Easy.new
10
+ end
11
+
12
+ def request(method, url, options = {})
13
+ req = prepare_request(method, url, options)
14
+ run_request(req, method)
15
+ req
16
+ end
17
+
18
+ private
19
+
20
+ def prepare_request(method, url, options)
21
+ username = options[:username] || @username
22
+ password = options[:password] || @password
23
+ @curl.url = url
24
+ @curl.http_auth_types = :basic
25
+ @curl.username = username
26
+ @curl.password = password
27
+ @curl
28
+ end
29
+
30
+ def run_request(request, method)
31
+ request.public_send(method)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,53 @@
1
+ require 'typhoeus'
2
+
3
+ module OrientdbClient
4
+ module HttpAdapters
5
+ class TyphoeusAdapter < Base
6
+
7
+ def request(method, url, options = {})
8
+ req = prepare_request(method, url, options)
9
+ run_request(req)
10
+ end
11
+
12
+ private
13
+
14
+ def prepare_request(method, url, options)
15
+ options = {
16
+ userpwd: authentication_string(options),
17
+ method: method
18
+ }.merge(options)
19
+ Typhoeus::Request.new(url, options)
20
+ end
21
+
22
+ def run_request(request)
23
+ request.run
24
+ response = request.response
25
+ if cookies = response.headers['Set-Cookie']
26
+ @session_id = extract_session_id(cookies)
27
+ end
28
+ # TODO hacky, replace with response adpater object probably
29
+ def response.content_type
30
+ headers['Content-Type']
31
+ end
32
+ response
33
+ end
34
+
35
+ def authentication_string(options)
36
+ username = options[:username] || @username
37
+ password = options[:password] || @password
38
+ "#{username}:#{password}"
39
+ end
40
+
41
+ def extract_session_id(cookies)
42
+ r = Regexp.new("#{SESSION_COOKIE_NAME}=([^\s;]+)")
43
+ if cookies.is_a?(Array)
44
+ return cookies.detect { |cookie| cookie.match(r) != nil }.
45
+ match(r)[1]
46
+ else
47
+ return cookies.match(r)[1]
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ module OrientdbClient
2
+ module Test
3
+ OrientdbClient::Test::DatabaseName = ENV['ORIENTDB_TEST_DATABASENAME'] || 'orientdb_client_rb_test'
4
+ OrientdbClient::Test::Username = ENV['ORIENTDB_TEST_USERNAME'] || 'test'
5
+ OrientdbClient::Test::Password = ENV['ORIENTDB_TEST_PASSWORD'] || 'test'
6
+ end
7
+ end
8
+
9
+ require 'orientdb_client/http_adapters/curb_adapter'
@@ -0,0 +1,3 @@
1
+ module OrientdbClient
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'orientdb_client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "orientdb_client"
8
+ spec.version = OrientdbClient::VERSION
9
+ spec.authors = ["Luke Rodgers"]
10
+ spec.email = ["lukeasrodgers@gmail.com"]
11
+ spec.summary = %q{Orientdb ruby client}
12
+ spec.description = %q{Orientdb ruby client aiming to be simple, fast, and provide good integration with Orientdb error messages.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "typhoeus", "~> 0.6"
22
+ spec.add_dependency "oj", "~> 2.0"
23
+ spec.add_dependency "rainbow", "> 1.99"
24
+
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "rspec", "~> 3.3"
28
+ spec.add_development_dependency "rspec-mocks", "~> 3.3"
29
+ spec.add_development_dependency "webmock", '~> 1.22'
30
+ spec.add_development_dependency "byebug"
31
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'support/shared_examples_for_http_adapter'
3
+
4
+ RSpec.describe OrientdbClient::HttpAdapters::CurbAdapter do
5
+ it_behaves_like 'http adapter' do
6
+ let(:adapter_klass) { OrientdbClient::HttpAdapters::CurbAdapter }
7
+ end
8
+ end
@@ -0,0 +1,703 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe OrientdbClient do
4
+ let(:client) do
5
+ c = OrientdbClient.client
6
+ c.logger.level = Logger::ERROR
7
+ c
8
+ end
9
+ let(:username) { OrientdbClient::Test::Username }
10
+ let(:valid_username) { OrientdbClient::Test::Username }
11
+ let(:password) { OrientdbClient::Test::Password }
12
+ let(:valid_password) { OrientdbClient::Test::Password }
13
+ let(:db) { OrientdbClient::Test::DatabaseName }
14
+ let(:temp_db_name) { "#{OrientdbClient::Test::DatabaseName}_temp" }
15
+
16
+ after(:each) do
17
+ if client.connected?
18
+ client.disconnect
19
+ end
20
+ if client.database_exists?(temp_db_name)
21
+ client.delete_database(temp_db_name, username: valid_username, password: valid_password)
22
+ end
23
+ end
24
+
25
+ describe 'integration specs', type: :integration do
26
+ describe '#connect' do
27
+ subject { client.connect(username: username, password: password, db: db) }
28
+
29
+ after(:each) do
30
+ if client.connected?
31
+ client.disconnect
32
+ end
33
+ end
34
+
35
+ context 'with valid credentials' do
36
+ let(:username) { OrientdbClient::Test::Username }
37
+ let(:password) { OrientdbClient::Test::Password }
38
+ let(:db) { OrientdbClient::Test::DatabaseName }
39
+
40
+ it 'connects to the database' do
41
+ subject
42
+ expect(client.connected?).to be true
43
+ end
44
+ end
45
+
46
+ context 'with invalid credentials' do
47
+ let(:username) { 'foo' }
48
+ let(:password) { 'bar' }
49
+ let(:db) { OrientdbClient::Test::DatabaseName }
50
+
51
+ it 'fails to connect' do
52
+ begin
53
+ subject
54
+ rescue
55
+ ensure
56
+ expect(client.connected?).to be false
57
+ end
58
+ end
59
+
60
+ it 'raises an UnauthorizedError' do
61
+ expect { subject }.to raise_exception(OrientdbClient::UnauthorizedError)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '#create_database' do
67
+ before(:each) do
68
+ if client.database_exists?(temp_db_name)
69
+ client.delete_database(temp_db_name, username: username, password: password)
70
+ end
71
+ end
72
+
73
+ context 'without existing connection' do
74
+ context 'with valid authentication options' do
75
+ it 'creates the database' do
76
+ client.create_database(temp_db_name, 'plocal', 'document', username: username, password: password)
77
+ expect(client.database_exists?(temp_db_name)).to be true
78
+ end
79
+ end
80
+
81
+ context 'with invalid authentication options' do
82
+ it 'raises UnauthorizedError' do
83
+ expect do
84
+ client.create_database(temp_db_name, 'plocal', 'document', username: 'foo', password: 'bar')
85
+ end.to raise_exception(OrientdbClient::UnauthorizedError)
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'with existing connection' do
91
+ before do
92
+ client.connect(username: username, password: password, db: db)
93
+ end
94
+
95
+ context 'with valid database parameters' do
96
+ it 'creates a database' do
97
+ client.create_database(temp_db_name, 'plocal', 'document')
98
+ expect(client.database_exists?(temp_db_name)).to be true
99
+ end
100
+ end
101
+
102
+ context 'with existing database' do
103
+ it 'creates a database' do
104
+ client.create_database(temp_db_name, 'plocal', 'document')
105
+ expect(client.database_exists?(temp_db_name)).to be true
106
+ expect do
107
+ client.create_database(temp_db_name, 'plocal', 'document')
108
+ end.to raise_exception(OrientdbClient::ConflictError)
109
+ end
110
+ end
111
+
112
+ context 'with invalid storage type' do
113
+ it 'raises a ClientError' do
114
+ expect do
115
+ client.create_database(temp_db_name, 'foo', 'document')
116
+ end.to raise_exception(OrientdbClient::ClientError, /OCommandExecutionException/)
117
+ end
118
+ end
119
+
120
+ context 'with invalid database type' do
121
+ it 'raises a ClientError' do
122
+ expect do
123
+ client.create_database(temp_db_name, 'memory', 'dog')
124
+ end.to raise_exception(ArgumentError)
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#delete_database' do
131
+ before(:each) do
132
+ if !client.database_exists?(temp_db_name)
133
+ client.create_database(temp_db_name, 'plocal', 'document', username: valid_username, password: valid_password)
134
+ end
135
+ end
136
+
137
+ context 'without existing connection' do
138
+ context 'with valid authentication options' do
139
+ it 'deletes the database' do
140
+ client.delete_database(temp_db_name, username: username, password: password)
141
+ expect(client.database_exists?(temp_db_name)).to be false
142
+ end
143
+ end
144
+
145
+ context 'with invalid authentication options' do
146
+ it 'raises UnauthorizedError' do
147
+ expect do
148
+ client.delete_database(temp_db_name, username: 'foo', password: 'bar')
149
+ end.to raise_exception(OrientdbClient::UnauthorizedError)
150
+ end
151
+
152
+ it 'does not delete the database' do
153
+ begin
154
+ client.delete_database(temp_db_name, username: 'foo', password: 'bar')
155
+ rescue
156
+ ensure
157
+ expect(client.database_exists?(temp_db_name)).to be true
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'with existing connection' do
164
+ before do
165
+ client.connect(username: username, password: password, db: db)
166
+ end
167
+
168
+ context 'with valid database parameters' do
169
+ it 'deletes the database' do
170
+ client.delete_database(temp_db_name)
171
+ expect(client.database_exists?(temp_db_name)).to be false
172
+ end
173
+ end
174
+
175
+ context 'with no matching database' do
176
+ it 'raises a ClientError' do
177
+ expect do
178
+ client.delete_database(temp_db_name + 'baz')
179
+ end.to raise_exception(OrientdbClient::ClientError, /OConfigurationException/)
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ describe '#query' do
186
+ context 'when connected' do
187
+ before(:each) do
188
+ client.connect(username: username, password: password, db: db)
189
+ end
190
+
191
+ context 'with valid query' do
192
+ it 'returns result' do
193
+ r = client.query('select * from OUser')
194
+ expect(r).to be
195
+ end
196
+ end
197
+
198
+ context 'with invalid query' do
199
+ it 'raises ClientError' do
200
+ expect { client.query('select * crumb') }.to raise_exception(OrientdbClient::ClientError, /OCommandSQLParsingException/)
201
+ end
202
+ end
203
+
204
+ context 'with non-idempotent query' do
205
+ it 'raises ClientError' do
206
+ expect { client.query('create class User') }.to raise_exception(OrientdbClient::ClientError, /OCommandExecutionException/)
207
+ end
208
+ end
209
+ end
210
+
211
+ context 'when not connected' do
212
+ it 'raises UnauthorizedError' do
213
+ expect { client.query('select * from OUser') }.to raise_exception(OrientdbClient::UnauthorizedError)
214
+ end
215
+ end
216
+ end
217
+
218
+ describe '#command' do
219
+ context 'when connected' do
220
+ before(:each) do
221
+ client.connect(username: username, password: password, db: db)
222
+ end
223
+
224
+ context 'with valid command' do
225
+ it 'returns result' do
226
+ r = client.command('select * from OUser')
227
+ expect(r).to be
228
+ end
229
+ end
230
+
231
+ context 'with invalid query' do
232
+ it 'returns result' do
233
+ expect { client.query('select * crumb') }.to raise_exception(OrientdbClient::ClientError, /OCommandSQLParsingException/)
234
+ end
235
+ end
236
+ end
237
+
238
+ context 'when not connected' do
239
+ it 'raises UnauthorizedError' do
240
+ expect { client.command('select * from OUser') }.to raise_exception(OrientdbClient::UnauthorizedError)
241
+ end
242
+ end
243
+ end
244
+
245
+ describe '#get_class' do
246
+ context 'when connected' do
247
+ before(:each) do
248
+ client.connect(username: username, password: password, db: db)
249
+ end
250
+
251
+ context 'with matching class' do
252
+ it 'returns result' do
253
+ r = client.get_class('OUser')
254
+ expect(r).to be
255
+ end
256
+ end
257
+
258
+ context 'with class that does not exist' do
259
+ it 'raises NotFoundError' do
260
+ expect { client.get_class('foobar') }.to raise_exception(OrientdbClient::NotFoundError)
261
+ end
262
+ end
263
+ end
264
+
265
+ context 'when not connected' do
266
+ it 'raises UnauthorizedError' do
267
+ expect { client.get_class('OUser') }.to raise_exception(OrientdbClient::UnauthorizedError)
268
+ end
269
+ end
270
+ end
271
+
272
+ describe '#list_databases' do
273
+ context 'when connected' do
274
+ before(:each) do
275
+ client.connect(username: username, password: password, db: db)
276
+ end
277
+
278
+ context 'with a database' do
279
+ before(:each) do
280
+ unless client.database_exists?(temp_db_name)
281
+ client.create_database(temp_db_name, 'plocal', 'document', username: valid_username, password: valid_password)
282
+ end
283
+ end
284
+
285
+ it 'returns an array including the database name' do
286
+ expect(client.list_databases).to include(temp_db_name)
287
+ end
288
+ end
289
+ end
290
+
291
+ context 'when not connected' do
292
+ it 'lists databases anyways' do
293
+ expect { client.list_databases }.not_to raise_exception
294
+ end
295
+ end
296
+ end
297
+
298
+ describe '#create_property' do
299
+ let(:class_name) { 'Member' }
300
+
301
+ context 'when connected' do
302
+ before(:each) do
303
+ client.connect(username: username, password: password, db: db)
304
+ end
305
+
306
+ context 'when class exists' do
307
+ before do
308
+ if (client.has_class?(class_name))
309
+ client.drop_class(class_name)
310
+ end
311
+ client.create_class(class_name)
312
+ end
313
+
314
+ after do
315
+ if (client.has_class?(class_name))
316
+ client.drop_class(class_name)
317
+ end
318
+ end
319
+
320
+ it 'can add string property to the class' do
321
+ client.create_property(class_name, 'member_name', 'string')
322
+ expect(client.get_class(class_name)['properties']).to include(hash_including({
323
+ 'name' => 'member_name',
324
+ 'type' => 'STRING',
325
+ 'mandatory' => false,
326
+ 'readonly' => false,
327
+ 'notNull' => false,
328
+ 'min' => nil,
329
+ 'max' => nil,
330
+ 'collate' => 'default'
331
+ }))
332
+ end
333
+
334
+ it 'accepts options for the new property' do
335
+ client.create_property(class_name, 'member_name', 'string', notnull: true, mandatory: true, min: 4, max: 10)
336
+ expect(client.get_class(class_name)['properties']).to include(hash_including({
337
+ 'name' => 'member_name',
338
+ 'type' => 'STRING',
339
+ 'mandatory' => true,
340
+ 'readonly' => false,
341
+ 'notNull' => true,
342
+ 'min' => '4',
343
+ 'max' => '10',
344
+ 'collate' => 'default'
345
+ }))
346
+ end
347
+
348
+ context 'when property already exists' do
349
+ before do
350
+ client.create_property(class_name, 'member_name', 'string')
351
+ end
352
+
353
+ it 'raises exception' do
354
+ expect do
355
+ client.create_property(class_name, 'member_name', 'string')
356
+ end.to raise_exception(OrientdbClient::CommandExecutionException, /OCommandExecutionException/)
357
+ end
358
+ end
359
+ end
360
+
361
+ context 'when class does not exist' do
362
+ it 'raises exception' do
363
+ expect do
364
+ client.create_property(class_name, 'member_name', 'string')
365
+ end.to raise_exception(OrientdbClient::CommandExecutionException, /OCommandExecutionException/)
366
+ end
367
+ end
368
+ end
369
+
370
+ context 'when not connected' do
371
+ it 'raises UnauthorizedError' do
372
+ expect do
373
+ client.create_property(class_name, 'member_name', 'string')
374
+ end.to raise_exception(OrientdbClient::UnauthorizedError)
375
+ end
376
+ end
377
+ end
378
+
379
+ describe '#alter_property' do
380
+ let(:class_name) { 'Member' }
381
+
382
+ context 'when connected' do
383
+ before(:each) do
384
+ client.connect(username: username, password: password, db: db)
385
+ end
386
+
387
+ context 'when class and property exist' do
388
+ before do
389
+ if (client.has_class?(class_name))
390
+ client.drop_class(class_name)
391
+ end
392
+ client.create_class(class_name) do |c|
393
+ c.property('member_name', 'string')
394
+ end
395
+ end
396
+
397
+ after do
398
+ if (client.has_class?(class_name))
399
+ client.drop_class(class_name)
400
+ end
401
+ end
402
+
403
+ it 'can change string to be notnull' do
404
+ client.alter_property(class_name, 'member_name', 'notnull', true)
405
+ expect(client.get_class(class_name)['properties']).to include(hash_including({
406
+ 'name' => 'member_name',
407
+ 'type' => 'STRING',
408
+ 'mandatory' => false,
409
+ 'readonly' => false,
410
+ 'notNull' => true,
411
+ 'min' => nil,
412
+ 'max' => nil,
413
+ 'collate' => 'default'
414
+ }))
415
+ end
416
+ end
417
+
418
+ context 'when class does not exist' do
419
+ before do
420
+ if (client.has_class?(class_name))
421
+ client.drop_class(class_name)
422
+ end
423
+ end
424
+
425
+ it 'raises exception' do
426
+ expect do
427
+ client.create_property(class_name, 'member_name', 'string')
428
+ end.to raise_exception(OrientdbClient::CommandExecutionException, /OCommandExecutionException/)
429
+ end
430
+ end
431
+
432
+ context 'when class exists but property does not' do
433
+ before do
434
+ if (client.has_class?(class_name))
435
+ client.drop_class(class_name)
436
+ end
437
+ client.create_class(class_name)
438
+ end
439
+
440
+ it 'raises exception' do
441
+ expect do
442
+ client.alter_property(class_name, 'member_name', 'notnull', true)
443
+ end.to raise_exception(OrientdbClient::CommandExecutionException, /OCommandExecutionException/)
444
+ end
445
+ end
446
+ end
447
+
448
+ context 'when not connected' do
449
+ it 'raises UnauthorizedError' do
450
+ expect do
451
+ client.create_property(class_name, 'member_name', 'string')
452
+ end.to raise_exception(OrientdbClient::UnauthorizedError)
453
+ end
454
+ end
455
+ end
456
+
457
+ describe '#create_class' do
458
+ let(:class_name) { 'Member' }
459
+
460
+ context 'when connected' do
461
+ before(:each) do
462
+ client.connect(username: username, password: password, db: db)
463
+
464
+ if (client.has_class?(class_name))
465
+ client.drop_class(class_name)
466
+ end
467
+ end
468
+
469
+ it 'creates the class' do
470
+ expect(client.create_class(class_name)).to be
471
+ expect(client.get_class(class_name)['name']).to eq(class_name)
472
+ end
473
+
474
+ it 'allows creation of classes that extend Vertex' do
475
+ client.create_class(class_name, extends: 'V')
476
+ expect(client.get_class(class_name)['superClass']).to eq('V')
477
+ end
478
+
479
+ it 'allows creation of abstract classes that extend Edge' do
480
+ client.create_class(class_name, extends: 'E', abstract: true)
481
+ expect(client.get_class(class_name)['superClass']).to eq('E')
482
+ expect(client.get_class(class_name)['abstract']).to be true
483
+ end
484
+
485
+ it 'raises exception on creation of classes that extend nothing' do
486
+ expect do
487
+ client.create_class(class_name, extends: 'VJk')
488
+ end.to raise_exception(OrientdbClient::ClientError, /OCommandSQLParsingException/)
489
+ end
490
+
491
+ describe 'with block' do
492
+ it 'creates properties on the class' do
493
+ client.create_class(class_name, extends: 'V') do |c|
494
+ c.property('member_name', 'string', notnull: true)
495
+ end
496
+ expect(client.get_class(class_name)['properties']).to include(hash_including({
497
+ 'name' => 'member_name',
498
+ 'type' => 'STRING',
499
+ 'mandatory' => false,
500
+ 'readonly' => false,
501
+ 'notNull' => true,
502
+ 'min' => nil,
503
+ 'max' => nil,
504
+ 'collate' => 'default'
505
+ }))
506
+ end
507
+ end
508
+
509
+ context 'with existing class of that name' do
510
+ it 'raises a ClientError' do
511
+ client.create_class(class_name)
512
+ expect do
513
+ client.create_class(class_name)
514
+ end.to raise_exception(OrientdbClient::ClientError, /OSchemaException/)
515
+ end
516
+ end
517
+ end
518
+
519
+
520
+ context 'when not connected' do
521
+ it 'raises UnauthorizedError' do
522
+ expect do
523
+ client.create_class(class_name)
524
+ end.to raise_exception(OrientdbClient::UnauthorizedError)
525
+ end
526
+ end
527
+ end
528
+
529
+ describe '#drop_class' do
530
+ let(:class_name) { 'Member' }
531
+
532
+ context 'when connected' do
533
+ before(:each) do
534
+ client.connect(username: username, password: password, db: db)
535
+ end
536
+
537
+ context 'with class' do
538
+ before(:each) do
539
+ unless client.has_class?(class_name)
540
+ client.create_class(class_name)
541
+ end
542
+ end
543
+
544
+ it 'deletes the class' do
545
+ client.drop_class(class_name)
546
+ expect(client.has_class?(class_name)).to be false
547
+ end
548
+ end
549
+
550
+ context 'without class' do
551
+ before(:each) do
552
+ if client.has_class?(class_name)
553
+ client.drop_class(class_name)
554
+ end
555
+ end
556
+
557
+ it 'returns nil' do
558
+ expect(client.drop_class(class_name)).to be_nil
559
+ end
560
+ end
561
+ end
562
+
563
+ context 'without connection' do
564
+ it 'raises UnauthorizedError' do
565
+ expect { client.drop_class(class_name) }.to raise_exception(OrientdbClient::UnauthorizedError)
566
+ end
567
+ end
568
+ end
569
+
570
+ describe '#get_database' do
571
+ context 'when connected' do
572
+ before(:each) do
573
+ client.connect(username: username, password: password, db: db)
574
+ end
575
+
576
+ context 'with db' do
577
+ it 'returns the database' do
578
+ expect(client.get_database(db)).to be
579
+ end
580
+ end
581
+
582
+ context 'without db' do
583
+ it 'raises NotFoundError' do
584
+ expect { client.get_database('foo') }.to raise_exception(OrientdbClient::NotFoundError, /not authorized/)
585
+ end
586
+ end
587
+ end
588
+
589
+ context 'without connection' do
590
+ it 'raises UnauthorizedError' do
591
+ expect { client.get_database(db) }.to raise_exception(OrientdbClient::UnauthorizedError)
592
+ end
593
+
594
+ context 'with option auth data' do
595
+ it 'returns the database' do
596
+ expect(client.get_database(db, {username: username, password: password})).to be
597
+ end
598
+ end
599
+ end
600
+ end
601
+
602
+ describe 'duplicate edge creation' do
603
+ before do
604
+ client.connect(username: username, password: password, db: db)
605
+ if client.has_class?('Person')
606
+ client.command('delete vertex Person')
607
+ client.drop_class('Person')
608
+ end
609
+ if client.has_class?('Friend')
610
+ client.drop_class('Friend')
611
+ end
612
+ end
613
+
614
+ after do
615
+ client.command('delete vertex Person')
616
+ client.drop_class('Person')
617
+ client.drop_class('Friend')
618
+ end
619
+
620
+ it 'raises DuplicateRecordError' do
621
+ client.create_class('Person', extends: 'V')
622
+ client.create_class('Friend', extends: 'E')
623
+ client.command('create property Friend.out link Person')
624
+ client.command('create property Friend.in link Person')
625
+ client.command('create index FollowIdx on Friend (out,in) unique')
626
+ client.command('create property Person.age integer')
627
+ jim = client.command('insert into Person CONTENT ' + Oj.dump({'name' => 'jim'}))
628
+ bob = client.command('insert into Person CONTENT ' + Oj.dump({'name' => 'bob'}))
629
+ jim_rid = jim['result'][0]['@rid']
630
+ bob_rid = bob['result'][0]['@rid']
631
+ client.command("create edge Friend from #{jim_rid} to #{bob_rid}")
632
+ expect do
633
+ client.command("create edge Friend from #{jim_rid} to #{bob_rid}")
634
+ end.to raise_exception(OrientdbClient::DuplicateRecordError, /found duplicated key/)
635
+ end
636
+ end
637
+
638
+ end
639
+
640
+ # These specs will sometimes fail, not too much we can do about that, depends
641
+ # on timing/threading in ruby and odb
642
+ describe 'mvcc handling', type: :integration do
643
+ let(:client) do
644
+ c = OrientdbClient.client
645
+ c.logger.level = Logger::ERROR
646
+ c
647
+ end
648
+ before do
649
+ client.connect(username: username, password: password, db: db)
650
+ if client.has_class?('Person')
651
+ client.command('delete vertex Person')
652
+ client.drop_class('Person')
653
+ end
654
+ if client.has_class?('Friend')
655
+ client.drop_class('Friend')
656
+ end
657
+ end
658
+
659
+ after do
660
+ client.command('delete vertex Person')
661
+ client.drop_class('Person')
662
+ client.drop_class('Friend')
663
+ end
664
+
665
+ it 'handles mvcc conflicts' do
666
+ client.create_class('Person', extends: 'V')
667
+ client.create_class('Friend', extends: 'E')
668
+ client.command('create property Friend.out link Person')
669
+ client.command('create property Friend.in link Person')
670
+ client.command('create index FollowIdx on Friend (out,in) unique')
671
+ client.command('create property Person.age integer')
672
+ jim = client.command('insert into Person CONTENT ' + Oj.dump({'name' => 'jim'}))
673
+ bob = client.command('insert into Person CONTENT ' + Oj.dump({'name' => 'bob'}))
674
+ jim_rid = jim['result'][0]['@rid']
675
+ bob_rid = bob['result'][0]['@rid']
676
+ thrs = []
677
+ expect do
678
+ thrs << Thread.new do
679
+ 100.times do
680
+ client.command("create edge Friend from #{jim_rid} to #{bob_rid}")
681
+ client.command("delete edge Friend from #{jim_rid} to #{bob_rid}")
682
+ end
683
+ end
684
+ thrs << Thread.new do
685
+ 100.times do |i|
686
+ client.command("update #{jim_rid} set age=#{i}")
687
+ client.command("update #{bob_rid} set age=#{i}")
688
+ end
689
+ end
690
+ thrs.each { |t| t.join }
691
+ end.to raise_exception(OrientdbClient::MVCCError, /OConcurrentModificationException/)
692
+ end
693
+ end
694
+
695
+ describe 'initialization' do
696
+ context 'with non-default adapter' do
697
+ it 'initializes specified adapter' do
698
+ client = OrientdbClient.client(adapter: 'CurbAdapter')
699
+ expect(client.http_client).to be_an_instance_of(OrientdbClient::HttpAdapters::CurbAdapter)
700
+ end
701
+ end
702
+ end
703
+ end