fauna 1.3.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,144 @@
1
+ RSpec.describe 'Fauna Errors' do
2
+ RSpec::Matchers.define :raise_fauna_error do |exception, code, position = nil|
3
+ match do |block|
4
+ expect(&block).to raise_error(exception) do |ex|
5
+ expect(ex.errors.length).to be(1)
6
+ error = ex.errors.first
7
+ expect(error.code).to eq(code)
8
+ expect(error.position).to eq(position)
9
+ end
10
+ end
11
+
12
+ def supports_block_expectations?
13
+ true
14
+ end
15
+ end
16
+
17
+ before(:all) do
18
+ create_test_db
19
+ end
20
+
21
+ after(:all) do
22
+ destroy_test_db
23
+ end
24
+
25
+ # Create client with stub adapter responding to / with the given status and response
26
+ def stub_get(status_code, response)
27
+ stubs = Faraday::Adapter::Test::Stubs.new
28
+ stubs.get '/' do
29
+ [status_code, {}, response]
30
+ end
31
+ Fauna::Client.new(adapter: [:test, stubs])
32
+ end
33
+
34
+ it 'sets request result' do
35
+ expect { client.post '', foo: 'bar' }.to raise_error do |err|
36
+ expect(err).to be_a(Fauna::BadRequest)
37
+ expect(err.request_result.request_content).to eq(foo: 'bar')
38
+ end
39
+ end
40
+
41
+ it 'parses query errors' do
42
+ expect { client.query { add 1, :two } }.to raise_fauna_error(Fauna::BadRequest, 'invalid argument', [:add, 1])
43
+ end
44
+
45
+ it 'parses invalid data' do
46
+ expect { client.query { create ref('classes'), name: 123 } }.to raise_error(Fauna::BadRequest) do |err|
47
+ expect(err.errors.length).to eq(1)
48
+ error = err.errors.first
49
+
50
+ expect(error.code).to eq('validation failed')
51
+ expect(error.position).to eq([])
52
+ expect(error.failures.length).to eq(1)
53
+ failure = error.failures.first
54
+
55
+ expect(failure.code).to eq('invalid type')
56
+ expect(failure.field).to eq([:name])
57
+ end
58
+ end
59
+
60
+ describe Fauna::ErrorData do
61
+ it 'handles inspect' do
62
+ err = Fauna::ErrorData.new 'code', 'desc', nil, nil
63
+ expect(err.inspect).to eq('ErrorData("code", "desc", nil, nil)')
64
+ end
65
+
66
+ it 'handles inspect with failures' do
67
+ failure = Fauna::Failure.new 'code', 'desc', [:a, :b]
68
+ err = Fauna::ErrorData.new 'code', 'desc', [:pos], [failure]
69
+ expect(err.inspect).to eq('ErrorData("code", "desc", [:pos], [Failure("code", "desc", [:a, :b])])')
70
+ end
71
+ end
72
+
73
+ describe Fauna::BadRequest do
74
+ it 'is handled' do
75
+ expect { client.post '', foo: 'bar' }.to raise_error(Fauna::BadRequest)
76
+ end
77
+ end
78
+
79
+ describe Fauna::Unauthorized do
80
+ it 'is handled' do
81
+ bad_client = get_client secret: 'bad_key'
82
+ expect { bad_client.query { get @db_ref } }.to raise_error(Fauna::Unauthorized)
83
+ end
84
+ end
85
+
86
+ describe Fauna::PermissionDenied do
87
+ it 'is handled' do
88
+ key = root_client.query { create ref('keys'), database: @db_ref, role: :client }
89
+
90
+ expect { get_client(secret: key[:secret]).query { paginate ref('databases') } }.to raise_fauna_error(
91
+ Fauna::PermissionDenied, 'permission denied', [:paginate])
92
+ end
93
+ end
94
+
95
+ describe Fauna::NotFound do
96
+ it 'is handled' do
97
+ expect { client.get 'classes/no_class' }.to raise_fauna_error(Fauna::NotFound, 'not found')
98
+ end
99
+ end
100
+
101
+ describe Fauna::MethodNotAllowed do
102
+ it 'is handled' do
103
+ expect { client.delete 'classes' }.to raise_fauna_error(Fauna::MethodNotAllowed, 'method not allowed')
104
+ end
105
+ end
106
+
107
+ describe Fauna::InternalError do
108
+ it 'is handled' do
109
+ stub_client = stub_get 500,
110
+ '{"errors": [{"code": "internal server error", "description": "sample text", "stacktrace": []}]}'
111
+
112
+ expect { stub_client.get '' }.to raise_fauna_error(Fauna::InternalError, 'internal server error')
113
+ end
114
+ end
115
+
116
+ describe Fauna::UnavailableError do
117
+ it 'is handled' do
118
+ stub_client = stub_get 503, '{"errors": [{"code": "unavailable", "description": "on vacation"}]}'
119
+ expect { stub_client.get '' }.to raise_fauna_error(Fauna::UnavailableError, 'unavailable')
120
+ end
121
+ end
122
+
123
+ describe Fauna::UnexpectedError do
124
+ it 'raised for json error' do
125
+ expect { stub_get(200, 'I like fine wine').get('') }.to raise_error(Fauna::UnexpectedError, /json/i) do |err|
126
+ rr = err.request_result
127
+ expect(rr.response_content).to be_nil
128
+ expect(rr.response_raw).to eq('I like fine wine')
129
+ end
130
+ end
131
+
132
+ it 'raised for missing resource' do
133
+ expect { stub_get(200, '{"notaresource": 1}').get('') }.to raise_error(Fauna::UnexpectedError, /expected key/)
134
+ end
135
+
136
+ it 'raised for unexpected code' do
137
+ expect { stub_get(1337, '{"errors": []}').get('') }.to raise_error(Fauna::UnexpectedError, /status code/)
138
+ end
139
+
140
+ it 'raised for bad errors format' do
141
+ expect { stub_get(500, '{"errors": true}').get('') }.to raise_error(Fauna::UnexpectedError, /unexpected format/)
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,87 @@
1
+ require 'fauna'
2
+ require 'securerandom'
3
+
4
+ FAUNA_ROOT_KEY = ENV['FAUNA_ROOT_KEY']
5
+ FAUNA_DOMAIN = ENV['FAUNA_DOMAIN']
6
+ FAUNA_SCHEME = ENV['FAUNA_SCHEME']
7
+ FAUNA_PORT = ENV['FAUNA_PORT']
8
+
9
+ module FaunaTestHelpers
10
+ def get_client(params = {})
11
+ params = { domain: FAUNA_DOMAIN, scheme: FAUNA_SCHEME, port: FAUNA_PORT }.merge(params)
12
+ fail 'No secret provided' unless params.key? :secret
13
+ Fauna::Client.new params
14
+ end
15
+
16
+ def root_client
17
+ fail 'FAUNA_ROOT_KEY must be defined in your environment to run tests' unless FAUNA_ROOT_KEY
18
+ get_client secret: FAUNA_ROOT_KEY
19
+ end
20
+
21
+ def client
22
+ fail 'Server client not initialized' if @server_client.nil?
23
+ @server_client
24
+ end
25
+
26
+ def create_test_db
27
+ @db_ref = Fauna::Ref.new 'databases', "faunadb-ruby-test-#{random_string}"
28
+
29
+ root = root_client
30
+ root.query { create ref('databases'), name: @db_ref.id }
31
+
32
+ begin
33
+ server_key = root.query { create ref('keys'), database: @db_ref, role: 'server' }
34
+ rescue
35
+ root.query { delete @db_ref }
36
+ @db_ref = nil
37
+ raise
38
+ end
39
+
40
+ @server_secret = server_key[:secret]
41
+ @server_client = get_client secret: @server_secret
42
+ end
43
+
44
+ def destroy_test_db
45
+ root_client.query { delete @db_ref } unless @db_ref.nil?
46
+ end
47
+
48
+ def stub_client(method, url, response = nil, headers = {})
49
+ stubs = Faraday::Adapter::Test::Stubs.new
50
+ stubs.send(method, url) do |env|
51
+ if response.nil?
52
+ [200, headers, { resource: { method: env.method.to_s.upcase, body: JSON.load(env.body) } }.to_json]
53
+ else
54
+ [200, headers, response]
55
+ end
56
+ end
57
+ Fauna::Client.new(adapter: [:test, stubs])
58
+ end
59
+
60
+ def random_string
61
+ SecureRandom.hex(7)
62
+ end
63
+
64
+ def random_number
65
+ SecureRandom.random_number(1_000_000)
66
+ end
67
+
68
+ def random_ref
69
+ "classes/#{random_string}/#{random_number}"
70
+ end
71
+
72
+ def random_class_ref
73
+ "classes/#{random_string}"
74
+ end
75
+
76
+ def to_json(value)
77
+ Fauna::FaunaJson.to_json(value)
78
+ end
79
+
80
+ def from_json(value)
81
+ Fauna::FaunaJson.json_load(value)
82
+ end
83
+
84
+ def wait_for_active(ref)
85
+ sleep 1 until client.query { get(ref) }[:active]
86
+ end
87
+ end
@@ -0,0 +1,123 @@
1
+ RSpec.describe Fauna::FaunaJson do
2
+ describe '#deserialize' do
3
+ it 'deserializes ref' do
4
+ ref = random_ref
5
+
6
+ data = { :@ref => ref }
7
+ obj = Fauna::Ref.new(ref)
8
+
9
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
10
+ end
11
+
12
+ it 'deserializes set' do
13
+ ref = random_ref
14
+ terms = random_string
15
+
16
+ data = { :@set => { match: { :@ref => ref }, terms: terms } }
17
+ obj = Fauna::SetRef.new(match: Fauna::Ref.new(ref), terms: terms)
18
+
19
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
20
+ end
21
+
22
+ it 'deserializes obj' do
23
+ obj = { a: random_string, b: random_string }
24
+ data = { :@obj => obj }
25
+
26
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
27
+ end
28
+
29
+ it 'deserializes ts' do
30
+ data = { :@ts => '1970-01-01T00:00:00.000000000Z' }
31
+ obj = Time.at(0).utc
32
+
33
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
34
+ end
35
+
36
+ it 'deserializes date' do
37
+ data = { :@date => '1970-01-01' }
38
+ obj = Date.new(1970, 1, 1)
39
+
40
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
41
+ end
42
+
43
+ it 'recursively deserializes hashes' do
44
+ ref = random_ref
45
+
46
+ data = { test: { :@obj => { :@ref => ref } } }
47
+ obj = { test: Fauna::Ref.new(ref) }
48
+
49
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
50
+ end
51
+
52
+ it 'recursively deserializes arrays' do
53
+ ref1 = random_ref
54
+ ref2 = random_ref
55
+
56
+ data = [{ :@ref => ref1 }, { :@ref => ref2 }]
57
+ obj = [Fauna::Ref.new(ref1), Fauna::Ref.new(ref2)]
58
+
59
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
60
+ end
61
+ end
62
+
63
+ describe '#to_json' do
64
+ it 'serializes ref' do
65
+ ref = random_ref
66
+
67
+ data = { :@ref => ref }
68
+ obj = Fauna::Ref.new(ref)
69
+
70
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
71
+ end
72
+
73
+ it 'serializes set' do
74
+ ref = random_ref
75
+
76
+ data = { :@ref => ref }
77
+ obj = Fauna::Ref.new(ref)
78
+
79
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
80
+ end
81
+
82
+ it 'serializes expr' do
83
+ data = { a: random_string, b: random_number }
84
+ obj = Fauna::Query::Expr.new(data)
85
+
86
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
87
+ end
88
+
89
+ it 'serializes time' do
90
+ data = { :@ts => '1970-01-01T00:00:00.000000000Z' }
91
+ obj = Time.at(0).utc
92
+
93
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
94
+ end
95
+
96
+ it 'serializes date' do
97
+ data = { :@date => '1970-01-01' }
98
+ obj = Date.new(1970, 1, 1)
99
+
100
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
101
+ end
102
+
103
+ it 'recursively serializes hashes' do
104
+ ref = random_ref
105
+ terms = random_string
106
+
107
+ data = { a: { time: { :@ts => '1970-01-01T00:00:00.000000000Z' } }, b: { :@set => { match: { :@ref => ref }, terms: terms } } }
108
+ obj = { a: { time: Time.at(0).utc }, b: Fauna::SetRef.new(match: Fauna::Ref.new(ref), terms: terms) }
109
+
110
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
111
+ end
112
+
113
+ it 'recursively serializes arrays' do
114
+ ref1 = random_ref
115
+ ref2 = random_ref
116
+
117
+ data = [{ :@ref => ref1 }, { :@ref => ref2 }]
118
+ obj = [Fauna::Ref.new(ref1), Fauna::Ref.new(ref2)]
119
+
120
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,675 @@
1
+ RSpec.describe Fauna::Query do
2
+ before(:all) do
3
+ create_test_db
4
+ @test_class = client.query { create ref('classes'), name: 'query_test' }[:ref]
5
+
6
+ index_x = client.query do
7
+ create ref('indexes'), name: 'query_by_x', source: @test_class, terms: [{ path: 'data.x' }]
8
+ end
9
+ index_y = client.query do
10
+ create ref('indexes'), name: 'query_by_y', source: @test_class, terms: [{ path: 'data.y' }]
11
+ end
12
+
13
+ wait_for_active(index_x[:ref])
14
+ wait_for_active(index_y[:ref])
15
+
16
+ @test_by_x = index_x[:ref]
17
+ @test_by_y = index_y[:ref]
18
+ end
19
+
20
+ after(:all) do
21
+ destroy_test_db
22
+ end
23
+
24
+ # Alias for creating test class instance with provided data
25
+ def create_instance(data = {})
26
+ client.query { create(@test_class, data: data) }
27
+ end
28
+
29
+ # Helper to collect all the contents of a set
30
+ def get_set_data(set, params = {})
31
+ data = []
32
+
33
+ page = client.query { paginate(set, params) }
34
+ data += page[:data]
35
+ while page.key? :after
36
+ page = client.query { paginate(set, params.merge(after: page[:after])) }
37
+ data += page[:data]
38
+ end
39
+
40
+ data
41
+ end
42
+
43
+ describe Fauna::Query::Expr do
44
+ it 'converts to string' do
45
+ expr = Fauna::Query::Expr.new(
46
+ add: Fauna::Query::Expr.new(
47
+ [1, Fauna::Query::Expr.new(divide: Fauna::Query::Expr.new([4, 2]))]
48
+ )
49
+ )
50
+ as_string = 'Expr({:add=>Expr([1, Expr({:divide=>Expr([4, 2])})])})'
51
+
52
+ expect(expr.to_s).to eq(as_string)
53
+ end
54
+ end
55
+
56
+ describe '#expr' do
57
+ it 'maintains lexical scope' do
58
+ def test_method
59
+ 'foo'
60
+ end
61
+ test_var = 'bar'
62
+
63
+ expect(Fauna::Query.expr { test_method }).to eq('foo')
64
+ expect(Fauna::Query.expr { test_var }).to eq('bar')
65
+ end
66
+
67
+ it 'recursively wraps hashes' do
68
+ expr = Fauna::Query.expr { { x: 1, y: { foo: 2 }, z: add(1, 2) } }
69
+ query = { object: { x: 1, y: { object: { foo: 2 } }, z: { add: [1, 2] } } }
70
+
71
+ expect(to_json(expr)).to eq(to_json(query))
72
+ end
73
+
74
+ it 'recursively wraps special types' do
75
+ expr = Fauna::Query.expr { { x: { y: Time.at(0).utc } } }
76
+ query = { object: { x: { object: { y: { :@ts => '1970-01-01T00:00:00.000000000Z' } } } } }
77
+
78
+ expect(to_json(expr)).to eq(to_json(query))
79
+ end
80
+
81
+ it 'round-trips special types', skip: 'Support for auto-escaping of special types is deferred' do
82
+ expect(client.query { { '@ref' => 'foo' } }).to eq(:@ref => 'foo')
83
+ end
84
+ end
85
+
86
+ describe '#ref' do
87
+ it 'creates a ref' do
88
+ ref = random_ref
89
+ expect(Fauna::Query.ref(ref)).to eq(Fauna::Ref.new(ref))
90
+ end
91
+ end
92
+
93
+ describe '#object' do
94
+ it 'wraps fields in object' do
95
+ data = { a: random_string, b: random_number }
96
+ expect(Fauna::Query.object(data).raw).to eq(object: data)
97
+ end
98
+ end
99
+
100
+ describe '#let' do
101
+ it 'performs let with expression' do
102
+ x = random_number
103
+ expect(client.query { let({ x: x }, var(:x)) }).to eq(x)
104
+ end
105
+
106
+ it 'performs let with block' do
107
+ x = random_number
108
+ expect(client.query { let(x: x) { x } }).to eq(x)
109
+ end
110
+ end
111
+
112
+ describe '#var' do
113
+ it 'creates a var' do
114
+ name = random_string
115
+ expect(Fauna::Query.var(name).raw).to eq(var: name)
116
+ end
117
+ end
118
+
119
+ describe '#if_' do
120
+ it 'performs an if' do
121
+ expect(client.query { if_(true, 't', 'f') }).to eq('t')
122
+ expect(client.query { if_(false, 't', 'f') }).to eq('f')
123
+ end
124
+ end
125
+
126
+ describe '#do_' do
127
+ it 'performs do' do
128
+ instance = create_instance
129
+ expect(client.query { do_(delete(instance[:ref]), 1) }).to eq(1)
130
+ expect(client.query { exists instance[:ref] }).to be(false)
131
+ end
132
+ end
133
+
134
+ describe '#lambda' do
135
+ it 'raises when block takes no arguments' do
136
+ expect { Fauna::Query.lambda {} }.to raise_error(ArgumentError)
137
+ end
138
+
139
+ it 'raises when block takes splat argument' do
140
+ expect { Fauna::Query.lambda { |*vars| add(vars) } }.to raise_error(ArgumentError)
141
+ end
142
+
143
+ it 'performs lambda from single argument' do
144
+ expr = Fauna::Query.expr { lambda { |a| add(a, a) } }
145
+ query = { lambda: :a, expr: { add: [{ var: :a }, { var: :a }] } }
146
+
147
+ expect(to_json(expr)).to eq(to_json(query))
148
+ expect(client.query { map([1, 2, 3], expr) }).to eq([2, 4, 6])
149
+ end
150
+
151
+ it 'performs lambda from multiple arguments' do
152
+ expr = Fauna::Query.expr { lambda { |a, b| [b, a] } }
153
+ query = { lambda: [:a, :b], expr: [{ var: :b }, { var: :a }] }
154
+
155
+ expect(to_json(expr)).to eq(to_json(query))
156
+ expect(client.query { map([[1, 2], [3, 4]], expr) }).to eq([[2, 1], [4, 3]])
157
+ end
158
+ end
159
+
160
+ describe '#lambda_expr' do
161
+ it 'performs lambda from expression' do
162
+ expr = Fauna::Query.expr { lambda_expr(:a, add(var(:a), var(:a))) }
163
+ query = { lambda: :a, expr: { add: [{ var: :a }, { var: :a }] } }
164
+
165
+ expect(to_json(expr)).to eq(to_json(query))
166
+ expect(client.query { map([1, 2, 3], expr) }).to eq([2, 4, 6])
167
+ end
168
+
169
+ it 'destructures single element arrays' do
170
+ expr = Fauna::Query.expr { lambda_expr([:a], add(var(:a), var(:a))) }
171
+ query = { lambda: [:a], expr: { add: [{ var: :a }, { var: :a }] } }
172
+
173
+ expect(to_json(expr)).to eq(to_json(query))
174
+ expect(client.query { map([[1], [2], [3]], expr) }).to eq([2, 4, 6])
175
+ end
176
+ end
177
+
178
+ describe '#map' do
179
+ it 'performs map from expression' do
180
+ input = (1..3).collect { random_number }
181
+ output = input.collect { |x| 2 * x }
182
+
183
+ expect(client.query { map(input, lambda { |a| multiply 2, a }) }).to eq(output)
184
+ end
185
+
186
+ it 'performs map from block' do
187
+ input = (1..3).collect { random_number }
188
+ output = input.collect { |x| 2 * x }
189
+
190
+ expect(client.query { map(input) { |a| multiply 2, a } }).to eq(output)
191
+ end
192
+ end
193
+
194
+ describe '#foreach' do
195
+ before(:each) do
196
+ @refs = (1..3).collect { create_instance[:ref] }
197
+
198
+ # Sanity check
199
+ expect(client.query { @refs.collect { |ref| exists ref } }).to eq(@refs.collect { true })
200
+ end
201
+
202
+ it 'performs foreach from expression' do
203
+ client.query { foreach @refs, lambda { |a| delete a } }
204
+
205
+ expect(client.query { @refs.collect { |ref| exists ref } }).to eq(@refs.collect { false })
206
+ end
207
+
208
+ it 'performs foreach from block' do
209
+ client.query { foreach(@refs) { |a| delete a } }
210
+
211
+ expect(client.query { @refs.collect { |ref| exists ref } }).to eq(@refs.collect { false })
212
+ end
213
+ end
214
+
215
+ describe '#filter' do
216
+ it 'performs filter from expression' do
217
+ expect(client.query { filter([1, 2, 3, 4], lambda { |a| equals modulo(a, 2), 0 }) }).to eq([2, 4])
218
+ end
219
+
220
+ it 'performs filter from block' do
221
+ expect(client.query { filter([1, 2, 3, 4]) { |a| equals modulo(a, 2), 0 } }).to eq([2, 4])
222
+ end
223
+ end
224
+
225
+ describe '#take' do
226
+ it 'performs take' do
227
+ expect(client.query { take(1, [1, 2]) }).to eq([1])
228
+ expect(client.query { take(3, [1, 2]) }).to eq([1, 2])
229
+ expect(client.query { take(-1, [1, 2]) }).to eq([])
230
+ end
231
+ end
232
+
233
+ describe '#drop' do
234
+ it 'performs drop' do
235
+ expect(client.query { drop(1, [1, 2]) }).to eq([2])
236
+ expect(client.query { drop(3, [1, 2]) }).to eq([])
237
+ expect(client.query { drop(-1, [1, 2]) }).to eq([1, 2])
238
+ end
239
+ end
240
+
241
+ describe '#prepend' do
242
+ it 'performs prepend' do
243
+ expect(client.query { prepend([4, 5, 6], [1, 2, 3]) }).to eq([1, 2, 3, 4, 5, 6])
244
+ end
245
+ end
246
+
247
+ describe '#append' do
248
+ it 'performs append' do
249
+ expect(client.query { append([1, 2, 3], [4, 5, 6]) }).to eq([1, 2, 3, 4, 5, 6])
250
+ end
251
+ end
252
+
253
+ describe '#get' do
254
+ it 'performs get' do
255
+ instance = create_instance
256
+
257
+ expect(client.query { get instance[:ref] }).to eq(instance)
258
+ end
259
+ end
260
+
261
+ describe '#paginate' do
262
+ before do
263
+ @x_value = random_number
264
+ @x_refs = (1..3).collect { create_instance(x: @x_value)[:ref] }
265
+ end
266
+
267
+ it 'performs paginate' do
268
+ set = Fauna::Query.match(@test_by_x, @x_value)
269
+
270
+ expect(get_set_data(set, size: 1)).to eq(@x_refs)
271
+ end
272
+
273
+ it 'performs paginate with sources' do
274
+ response = {
275
+ data: @x_refs.collect do |ref|
276
+ { sources: [Fauna::SetRef.new(match: @test_by_x, terms: @x_value)], value: ref }
277
+ end
278
+ }
279
+
280
+ expect(client.query { paginate(match(@test_by_x, @x_value), sources: true) }).to eq(response)
281
+ end
282
+ end
283
+
284
+ describe '#exists' do
285
+ it 'performs exists' do
286
+ ref = create_instance[:ref]
287
+
288
+ expect(client.query { exists ref }).to be(true)
289
+ client.query { delete ref }
290
+ expect(client.query { exists ref }).to be(false)
291
+
292
+ # Sanity check
293
+ expect { client.query { get ref } }.to raise_error(Fauna::NotFound)
294
+ end
295
+ end
296
+
297
+ describe '#count' do
298
+ before do
299
+ @x_value = random_number
300
+ @x_refs = (1..3).collect { create_instance(x: @x_value)[:ref] }
301
+ end
302
+
303
+ it 'performs count' do
304
+ set = Fauna::Query.match(@test_by_x, @x_value)
305
+
306
+ # Count is only approximate; should be equal to @x_refs.length
307
+ expect(client.query { count set }).to be_a(Integer)
308
+ end
309
+ end
310
+
311
+ describe '#create' do
312
+ it 'performs create' do
313
+ instance = client.query { create(@test_class, {}) }
314
+
315
+ expect(instance[:class]).to eq(@test_class)
316
+ expect(client.query { exists instance[:ref] }).to be(true)
317
+ end
318
+ end
319
+
320
+ describe '#update' do
321
+ it 'performs update' do
322
+ x = random_number
323
+ y = random_number
324
+ ref = create_instance(x: x)[:ref]
325
+
326
+ instance = client.query { update(ref, data: { y: y }) }
327
+ expect(instance[:data]).to eq(x: x, y: y)
328
+ end
329
+ end
330
+
331
+ describe '#replace' do
332
+ it 'performs replace' do
333
+ x = random_number
334
+ y = random_number
335
+ ref = create_instance(x: x)[:ref]
336
+
337
+ instance = client.query { replace(ref, data: { y: y }) }
338
+ expect(instance[:data]).to eq(y: y)
339
+ end
340
+ end
341
+
342
+ describe '#delete' do
343
+ it 'performs delete' do
344
+ ref = create_instance[:ref]
345
+
346
+ client.query { delete ref }
347
+ expect(client.query { exists ref }).to be(false)
348
+ end
349
+ end
350
+
351
+ describe '#insert' do
352
+ it 'performs insert' do
353
+ instance = create_instance
354
+ ref = instance[:ref]
355
+ ts = instance[:ts]
356
+
357
+ prev_ts = ts - 1
358
+ value = random_number
359
+ client.query { insert(ref, prev_ts, :create, data: { x: value }) }
360
+
361
+ expect(client.query { get(ref, ts: prev_ts) }[:data]).to eq(x: value)
362
+ end
363
+ end
364
+
365
+ describe '#remove' do
366
+ it 'performs remove' do
367
+ # Create the instance
368
+ instance = create_instance
369
+ ref = instance[:ref]
370
+
371
+ # Change the instance
372
+ new_instance = client.query { replace(ref, data: { x: random_number }) }
373
+ expect(client.query { get(ref) }).to eq(new_instance)
374
+
375
+ # Delete the event
376
+ client.query { remove(ref, new_instance[:ts], :create) }
377
+
378
+ # Assert it changed
379
+ expect(client.query { get(ref) }).to eq(instance)
380
+ end
381
+ end
382
+
383
+ describe 'sets' do
384
+ before do
385
+ @x_value = random_number
386
+ @y_value = random_number
387
+
388
+ @ref_x = create_instance(x: @x_value)[:ref]
389
+ @ref_y = create_instance(y: @y_value)[:ref]
390
+ @ref_xy = create_instance(x: @x_value, y: @y_value)[:ref]
391
+ end
392
+
393
+ describe '#match' do
394
+ it 'performs match' do
395
+ set = Fauna::Query.expr { match(@test_by_x, @x_value) }
396
+ expect(get_set_data(set)).to contain_exactly(@ref_x, @ref_xy)
397
+ end
398
+ end
399
+
400
+ describe '#union' do
401
+ it 'performs union' do
402
+ set = Fauna::Query.expr { union(match(@test_by_x, @x_value), match(@test_by_y, @y_value)) }
403
+ expect(get_set_data(set)).to contain_exactly(@ref_x, @ref_y, @ref_xy)
404
+ end
405
+ end
406
+
407
+ describe '#intersection' do
408
+ it 'performs intersection' do
409
+ set = Fauna::Query.expr { intersection(match(@test_by_x, @x_value), match(@test_by_y, @y_value)) }
410
+ expect(get_set_data(set)).to contain_exactly(@ref_xy)
411
+ end
412
+ end
413
+
414
+ describe '#difference' do
415
+ it 'performs difference' do
416
+ set = Fauna::Query.expr { difference(match(@test_by_x, @x_value), match(@test_by_y, @y_value)) }
417
+ expect(get_set_data(set)).to contain_exactly(@ref_x)
418
+ end
419
+ end
420
+ end
421
+
422
+ describe '#distinct' do
423
+ before do
424
+ over_z = client.query do
425
+ create ref('indexes'), name: 'query_over_z', source: @test_class, values: [{ path: 'data.z' }]
426
+ end
427
+ wait_for_active(over_z[:ref])
428
+ @test_over_z = over_z[:ref]
429
+
430
+ @refs = []
431
+ @refs << client.query { create @test_class, data: { z: 0 } }[:ref]
432
+ @refs << client.query { create @test_class, data: { z: 1 } }[:ref]
433
+ @refs << client.query { create @test_class, data: { z: 1 } }[:ref]
434
+ end
435
+
436
+ it 'performs distinct' do
437
+ set = Fauna::Query.match(@test_over_z)
438
+ distinct = Fauna::Query.distinct(set)
439
+
440
+ expect(get_set_data(set)).to eq([0, 1, 1])
441
+ expect(get_set_data(distinct)).to eq([0, 1])
442
+ end
443
+ end
444
+
445
+ describe '#join' do
446
+ before do
447
+ @x_value = random_number
448
+ @join_refs = (1..3).collect { create_instance(x: @x_value)[:ref] }
449
+ @assoc_refs = @join_refs.collect { |ref| create_instance(y: ref)[:ref] }
450
+ end
451
+
452
+ context 'with expression' do
453
+ it 'performs join' do
454
+ source = Fauna::Query.match(@test_by_x, @x_value)
455
+ expect(get_set_data(source)).to eq(@join_refs)
456
+
457
+ # Get associated refs
458
+ set = Fauna::Query.expr { join(source, lambda { |a| match(@test_by_y, a) }) }
459
+ expect(get_set_data(set)).to eq(@assoc_refs)
460
+ end
461
+ end
462
+
463
+ context 'with block' do
464
+ it 'performs join' do
465
+ source = Fauna::Query.match(@test_by_x, @x_value)
466
+ expect(get_set_data(source)).to eq(@join_refs)
467
+
468
+ # Get associated refs
469
+ set = Fauna::Query.expr { join(source) { |a| match(@test_by_y, a) } }
470
+ expect(get_set_data(set)).to eq(@assoc_refs)
471
+ end
472
+ end
473
+ end
474
+
475
+ describe 'authentication' do
476
+ before do
477
+ @password = random_string
478
+ @user = client.query { create @test_class, credentials: { password: @password } }
479
+ end
480
+
481
+ describe '#login' do
482
+ it 'performs login' do
483
+ token = client.query { login @user[:ref], password: @password }
484
+ user_client = get_client secret: token[:secret]
485
+
486
+ expect(user_client.query { select(:ref, get(ref('tokens/self'))) }).to eq(token[:ref])
487
+ end
488
+ end
489
+
490
+ describe '#logout' do
491
+ it 'performs logout' do
492
+ token = client.query { login @user[:ref], password: @password }
493
+ user_client = get_client secret: token[:secret]
494
+
495
+ expect(user_client.query { logout true }).to be(true)
496
+ end
497
+ end
498
+
499
+ describe '#identify' do
500
+ it 'performs identify' do
501
+ expect(client.query { identify(@user[:ref], @password) }).to be(true)
502
+ end
503
+ end
504
+ end
505
+
506
+ describe '#concat' do
507
+ it 'performs concat' do
508
+ expect(client.query { concat ['a', 'b', 'c'] }).to eq('abc')
509
+ expect(client.query { concat [] }).to eq('')
510
+ end
511
+
512
+ it 'performs concat with separator' do
513
+ expect(client.query { concat(['a', 'b', 'c'], '.') }).to eq('a.b.c')
514
+ end
515
+ end
516
+
517
+ describe '#casefold' do
518
+ it 'performs casefold' do
519
+ expect(client.query { casefold 'Hen Wen' }).to eq('hen wen')
520
+ end
521
+ end
522
+
523
+ describe '#test' do
524
+ it 'performs time' do
525
+ # `.round 9` is necessary because MRI 1.9.3 stores with greater precision than just nanoseconds.
526
+ # This cuts it down to just nanoseconds so that the times compare as equal.
527
+ time = Time.at(0, 123_456.789).round 9
528
+ expect(client.query { time '1970-01-01T00:00:00.123456789Z' }).to eq(time)
529
+
530
+ # 'now' refers to the current time.
531
+ expect(client.query { time 'now' }).to be_a(Time)
532
+ end
533
+ end
534
+
535
+ describe '#epoch' do
536
+ it 'performs epoch for seconds' do
537
+ secs = random_number
538
+ expect(client.query { epoch(secs, 'second') }).to eq(Time.at(secs).utc)
539
+ end
540
+
541
+ it 'performs epoch for nanoseconds' do
542
+ nanos = random_number
543
+ expect(client.query { epoch(nanos, 'nanosecond') }).to eq(Time.at(Rational(nanos, 1_000_000_000)).utc)
544
+ end
545
+ end
546
+
547
+ describe '#date' do
548
+ it 'performs date' do
549
+ expect(client.query { date('1970-01-01') }).to eq(Date.new(1970, 1, 1))
550
+ end
551
+ end
552
+
553
+ describe '#equals' do
554
+ it 'performs equals' do
555
+ expect(client.query { equals(1, 1, 1) }).to be(true)
556
+ expect(client.query { equals(1, 1, 2) }).to be(false)
557
+ expect(client.query { equals 1 }).to be(true)
558
+ end
559
+ end
560
+
561
+ describe '#contains' do
562
+ it 'performs contains' do
563
+ obj = { a: { b: 1 } }
564
+
565
+ expect(client.query { contains([:a, :b], obj) }).to be(true)
566
+ expect(client.query { contains(:a, obj) }).to be(true)
567
+ expect(client.query { contains([:a, :c], obj) }).to be(false)
568
+ end
569
+ end
570
+
571
+ describe '#select' do
572
+ it 'performs select with hash' do
573
+ obj = { a: { b: 1 } }
574
+
575
+ expect(client.query { select(:a, obj) }).to eq(b: 1)
576
+ expect(client.query { select([:a, :b], obj) }).to eq(1)
577
+ expect(client.query { select(:c, obj, default: nil) }).to be_nil
578
+ expect { client.query { select(:c, obj) } }.to raise_error(Fauna::NotFound)
579
+ end
580
+
581
+ it 'performs select with array' do
582
+ arr = [1, 2, 3]
583
+
584
+ expect(client.query { select(2, arr) }).to eq(3)
585
+ expect { client.query { select(3, arr) } }.to raise_error(Fauna::NotFound)
586
+ end
587
+ end
588
+
589
+ describe '#add' do
590
+ it 'performs add' do
591
+ expect(client.query { add(2, 3, 5) }).to eq(10)
592
+ end
593
+ end
594
+
595
+ describe '#multiply' do
596
+ it 'performs multiply' do
597
+ expect(client.query { multiply(2, 3, 5) }).to eq(30)
598
+ end
599
+ end
600
+
601
+ describe '#subtract' do
602
+ it 'performs subtract' do
603
+ expect(client.query { subtract(2, 3, 5) }).to eq(-6)
604
+ expect(client.query { subtract(2) }).to eq(2)
605
+ end
606
+ end
607
+
608
+ describe '#divide' do
609
+ it 'performs divide' do
610
+ expect(client.query { divide(2.0, 3, 5) }).to eq(2.0 / 15)
611
+ expect(client.query { divide(2) }).to eq(2)
612
+ end
613
+ end
614
+
615
+ describe '#modulo' do
616
+ it 'performs modulo' do
617
+ expect(client.query { modulo(5, 2) }).to eq(1)
618
+ expect(client.query { modulo(15, 10, 2) }).to eq(1)
619
+ expect(client.query { modulo(2) }).to eq(2)
620
+ end
621
+ end
622
+
623
+ describe '#lt' do
624
+ it 'performs lt' do
625
+ expect(client.query { lt(1, 2) }).to be(true)
626
+ expect(client.query { lt(2, 2) }).to be(false)
627
+ end
628
+ end
629
+
630
+ describe '#lte' do
631
+ it 'performs lte' do
632
+ expect(client.query { lte(1, 1) }).to be(true)
633
+ expect(client.query { lte(2, 1) }).to be(false)
634
+ end
635
+ end
636
+
637
+ describe '#gt' do
638
+ it 'performs gt' do
639
+ expect(client.query { gt(2, 1) }).to be(true)
640
+ expect(client.query { gt(2, 2) }).to be(false)
641
+ end
642
+ end
643
+
644
+ describe '#gte' do
645
+ it 'performs gte' do
646
+ expect(client.query { gte(2, 2) }).to be(true)
647
+ expect(client.query { gte(2, 3) }).to be(false)
648
+ end
649
+ end
650
+
651
+ describe '#and_' do
652
+ it 'performs and' do
653
+ expect(client.query { and_(true, true, false) }).to be(false)
654
+ expect(client.query { and_(true, true, true) }).to be(true)
655
+ expect(client.query { and_(true) }).to be(true)
656
+ expect(client.query { and_(false) }).to be(false)
657
+ end
658
+ end
659
+
660
+ describe '#or_' do
661
+ it 'performs or' do
662
+ expect(client.query { or_(false, false, true) }).to be(true)
663
+ expect(client.query { or_(false, false, false) }).to be(false)
664
+ expect(client.query { or_(true) }).to be(true)
665
+ expect(client.query { or_(false) }).to be(false)
666
+ end
667
+ end
668
+
669
+ describe '#not_' do
670
+ it 'performs not' do
671
+ expect(client.query { not_(true) }).to be(false)
672
+ expect(client.query { not_(false) }).to be(true)
673
+ end
674
+ end
675
+ end