fauna 1.3.4 → 2.0.0

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,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