orientdb_client 0.0.1

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