yax-fauna 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,102 @@
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 admin_client
27
+ fail 'Admin client not initialized' if @admin_client.nil?
28
+ @admin_client
29
+ end
30
+
31
+ def create_test_db
32
+ @db_ref = Fauna::Ref.new("faunadb-ruby-test-#{random_string}", Fauna::Native.databases)
33
+
34
+ root = root_client
35
+ root.query { create ref('databases'), name: @db_ref.id }
36
+
37
+ begin
38
+ server_key = root.query { create ref('keys'), database: @db_ref, role: 'server' }
39
+ admin_key = root.query { create ref('keys'), database: @db_ref, role: 'admin' }
40
+ rescue
41
+ root.query { delete @db_ref }
42
+ @db_ref = nil
43
+ raise
44
+ end
45
+
46
+ @server_secret = server_key[:secret]
47
+ @server_client = get_client secret: @server_secret
48
+ @admin_secret = admin_key[:secret]
49
+ @admin_client = get_client secret: @admin_secret
50
+ end
51
+
52
+ def destroy_test_db
53
+ root_client.query { delete @db_ref } unless @db_ref.nil?
54
+ end
55
+
56
+ def stub_client(method, url, response = nil, headers = {})
57
+ stubs = Faraday::Adapter::Test::Stubs.new
58
+ stubs.send(method, url) do |env|
59
+ if response.nil?
60
+ [200, headers, { resource: { method: env.method.to_s.upcase, body: JSON.load(env.body) } }.to_json]
61
+ else
62
+ [200, headers, response]
63
+ end
64
+ end
65
+ Fauna::Client.new(adapter: [:test, stubs])
66
+ end
67
+
68
+ def random_string
69
+ SecureRandom.hex(7)
70
+ end
71
+
72
+ def random_number
73
+ SecureRandom.random_number(1_000_000)
74
+ end
75
+
76
+ def random_bytes
77
+ SecureRandom.random_bytes(20)
78
+ end
79
+
80
+ def random_ref_string
81
+ "classes/#{random_string}/#{random_number}"
82
+ end
83
+
84
+ def to_json(value)
85
+ Fauna::FaunaJson.to_json(value)
86
+ end
87
+
88
+ def from_json(value)
89
+ Fauna::FaunaJson.json_load(value)
90
+ end
91
+
92
+ def wait_for_index(*refs)
93
+ sleep 1 until client.query { map(refs) { |ref| select([:active], get(ref)) } }.all? { |active| active }
94
+ end
95
+ end
96
+
97
+ # Shared classes
98
+ class DummyClass
99
+ def initialize(value)
100
+ @value = value
101
+ end
102
+ end
@@ -0,0 +1,161 @@
1
+ RSpec.describe Fauna::FaunaJson do
2
+ describe '#deserialize' do
3
+ it 'deserializes ref' do
4
+ ref = random_ref_string
5
+
6
+ data = { :@ref => { id: 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_string
14
+ terms = random_string
15
+
16
+ data = { :@set => { match: { :@ref => { id: 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 query' do
23
+ query = { lambda: 'a', expr: { add: [{ var: 'a' }, 1] } }
24
+ obj = Fauna::QueryV.new(query)
25
+
26
+ expect(Fauna::FaunaJson.deserialize(:@query => query)).to eq(obj)
27
+ end
28
+
29
+ it 'deserializes obj' do
30
+ obj = { a: random_string, b: random_string }
31
+ data = { :@obj => obj }
32
+
33
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
34
+ end
35
+
36
+ it 'deserializes ts' do
37
+ data = { :@ts => '1970-01-01T00:00:00.000000000Z' }
38
+ obj = Time.at(0).utc
39
+
40
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
41
+ end
42
+
43
+ it 'deserializes date' do
44
+ data = { :@date => '1970-01-01' }
45
+ obj = Date.new(1970, 1, 1)
46
+
47
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
48
+ end
49
+
50
+ it 'deserializes bytes' do
51
+ raw = random_bytes
52
+
53
+ data = { :@bytes => Base64.urlsafe_encode64(raw) }
54
+ obj = Fauna::Bytes.new(raw)
55
+
56
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
57
+ end
58
+
59
+ it 'recursively deserializes hashes' do
60
+ ref = random_ref_string
61
+
62
+ data = { test: { :@obj => { :@ref => { id: ref } } } }
63
+ obj = { test: Fauna::Ref.new(ref) }
64
+
65
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
66
+ end
67
+
68
+ it 'recursively deserializes arrays' do
69
+ ref1 = random_ref_string
70
+ ref2 = random_ref_string
71
+
72
+ data = [{ :@ref => { id: ref1 } }, { :@ref => { id: ref2 } }]
73
+ obj = [Fauna::Ref.new(ref1), Fauna::Ref.new(ref2)]
74
+
75
+ expect(Fauna::FaunaJson.deserialize(data)).to eq(obj)
76
+ end
77
+ end
78
+
79
+ describe '#to_json' do
80
+ it 'serializes ref' do
81
+ ref = random_ref_string
82
+
83
+ data = { :@ref => { id: ref } }
84
+ obj = Fauna::Ref.new(ref)
85
+
86
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
87
+ end
88
+
89
+ it 'serializes set' do
90
+ ref = random_ref_string
91
+
92
+ data = { :@ref => { id: ref } }
93
+ obj = Fauna::Ref.new(ref)
94
+
95
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
96
+ end
97
+
98
+ it 'serializes query' do
99
+ query = { lambda: 'a', expr: { add: [{ var: 'a' }, 1] } }
100
+ obj = Fauna::QueryV.new(query)
101
+
102
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(:@query => query)
103
+ end
104
+
105
+ it 'serializes expr' do
106
+ data = { a: random_string, b: random_number }
107
+ obj = Fauna::Query::Expr.new(data)
108
+
109
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
110
+ end
111
+
112
+ it 'serializes time' do
113
+ data = { :@ts => '1970-01-01T00:00:00.000000000Z' }
114
+ obj = Time.at(0).utc
115
+
116
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
117
+ end
118
+
119
+ it 'serializes date' do
120
+ data = { :@date => '1970-01-01' }
121
+ obj = Date.new(1970, 1, 1)
122
+
123
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
124
+ end
125
+
126
+ it 'serializes bytes' do
127
+ raw = random_bytes
128
+
129
+ data = { :@bytes => Base64.urlsafe_encode64(raw) }
130
+ obj = Fauna::Bytes.new(raw)
131
+
132
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
133
+ end
134
+
135
+ it 'recursively serializes hashes' do
136
+ ref = random_ref_string
137
+ terms = random_string
138
+
139
+ data = { a: { time: { :@ts => '1970-01-01T00:00:00.000000000Z' } }, b: { :@set => { match: { :@ref => { id: ref } }, terms: terms } } }
140
+ obj = { a: { time: Time.at(0).utc }, b: Fauna::SetRef.new(match: Fauna::Ref.new(ref), terms: terms) }
141
+
142
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
143
+ end
144
+
145
+ it 'recursively serializes arrays' do
146
+ ref1 = random_ref_string
147
+ ref2 = random_ref_string
148
+
149
+ data = [{ :@ref => { id: ref1 } }, { :@ref => { id: ref2 } }]
150
+ obj = [Fauna::Ref.new(ref1), Fauna::Ref.new(ref2)]
151
+
152
+ expect(Fauna::FaunaJson.serialize(obj)).to eq(data)
153
+ end
154
+
155
+ it 'fails on unserializable objects' do
156
+ obj = DummyClass.new(random_string)
157
+
158
+ expect { Fauna::FaunaJson.serialize(obj) }.to raise_error(Fauna::SerializationError)
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,357 @@
1
+ RSpec.describe Fauna::Page do
2
+ before(:all) do
3
+ create_test_db
4
+ @test_class = client.query { create ref('classes'), name: 'page_test' }[:ref]
5
+ @foreach_class = client.query { create ref('classes'), name: 'page_foreach' }[:ref]
6
+
7
+ index_refs = client.query { create ref('indexes'), name: 'page_refs', source: @test_class }
8
+ index_values = client.query { create ref('indexes'), name: 'page_values', source: @test_class, values: [{ field: %w(data value) }] }
9
+ index_foreach = client.query { create ref('indexes'), name: 'page_apply', source: @foreach_class }
10
+
11
+ wait_for_index(index_refs[:ref], index_values[:ref], index_foreach[:ref])
12
+
13
+ @refs_index = index_refs[:ref]
14
+ @values_index = index_values[:ref]
15
+ @foreach_index = index_foreach[:ref]
16
+
17
+ @instances = client.query { (1..6).collect { |x| create(@test_class, data: { value: x }) } }.sort_by { |inst| inst[:ref].id }
18
+ @instance_refs = @instances.collect { |instance| instance[:ref] }
19
+ @instance_values = @instances.collect { |instance| instance[:data][:value] }.sort
20
+
21
+ @refs_match = Fauna::Query.match(@refs_index)
22
+ @values_match = Fauna::Query.match(@values_index)
23
+ @foreach_match = Fauna::Query.match(@foreach_index)
24
+ end
25
+
26
+ after(:all) do
27
+ destroy_test_db
28
+ end
29
+
30
+ describe '#==' do
31
+ it 'equals identical page' do
32
+ page1 = Fauna::Page.new(client, @refs_match, size: 1)
33
+ page2 = Fauna::Page.new(client, @refs_match, size: 1)
34
+
35
+ expect(page1).to eq(page2)
36
+ end
37
+
38
+ it 'does not equal different page' do
39
+ page1 = Fauna::Page.new(client, @refs_match, size: 1234)
40
+ page2 = Fauna::Page.new(client, @refs_match, size: 4321)
41
+
42
+ expect(page1).not_to eq(page2)
43
+ end
44
+ end
45
+
46
+ it 'can\'t mutate params directly' do
47
+ page = client.paginate(@refs_match)
48
+
49
+ expect { page.params[:ts] = random_number }.to raise_error(RuntimeError, 'can\'t modify frozen Hash')
50
+
51
+ page = page.with_params(ts: random_number)
52
+
53
+ expect { page.params[:ts] = random_number }.to raise_error(RuntimeError, 'can\'t modify frozen Hash')
54
+ end
55
+
56
+ describe 'builders' do
57
+ def get_funcs(page)
58
+ page.instance_variable_get(:@fauna_funcs)
59
+ end
60
+
61
+ def get_postprocessing(page)
62
+ page.instance_variable_get(:@postprocessing_map)
63
+ end
64
+
65
+ describe '#with_params' do
66
+ let(:ref1) { random_ref_string }
67
+ let(:ref2) { random_ref_string }
68
+
69
+ it 'sets params on copy' do
70
+ ts1 = random_number
71
+ ts2 = random_number
72
+
73
+ page = client.paginate(@refs_match, ts: ts1)
74
+
75
+ expect(page.with_params(ts: ts2, sources: false).params).to eq(ts: ts2, sources: false)
76
+ expect(page.params).to eq(ts: ts1)
77
+ end
78
+
79
+ it 'reverses cursor' do
80
+ page = client.paginate(@refs_match, before: ref1)
81
+
82
+ expect(page.with_params(after: ref2).params).to eq(after: ref2)
83
+ expect(page.params).to eq(before: ref1)
84
+ end
85
+
86
+ it 'preserves nil' do
87
+ page = client.paginate(@refs_match, after: nil)
88
+
89
+ expect(page.with_params(before: nil).params).to eq(before: nil)
90
+ expect(page.params).to eq(after: nil)
91
+ end
92
+
93
+ it 'resets paging' do
94
+ page = client.paginate(@refs_match, size: 1)
95
+ page1 = page.page_after
96
+
97
+ page2 = page1.with_params(after: 0).page_after
98
+
99
+ expect(page2.data).to eq(page2.data)
100
+ end
101
+ end
102
+
103
+ describe '#map' do
104
+ it 'sets map on copy' do
105
+ page = client.paginate(@refs_match)
106
+
107
+ expect(get_funcs(page.map { |ref| get ref }).length).to be(1)
108
+ expect(get_funcs(page).length).to be(0)
109
+ end
110
+
111
+ it 'performs map when paging' do
112
+ page = client.paginate(@refs_match).map { |ref| get ref }
113
+
114
+ expect(page.all).to eq(@instances)
115
+ end
116
+ end
117
+
118
+ describe '#filter' do
119
+ it 'sets filter on copy' do
120
+ page = client.paginate(@values_match)
121
+
122
+ expect(get_funcs(page.filter { |value| equals(modulo(value, 2), 0) }).length).to be(1)
123
+ expect(get_funcs(page).length).to be(0)
124
+ end
125
+
126
+ it 'performs filter when paging' do
127
+ page = client.paginate(@values_match).filter { |value| equals(modulo(value, 2), 0) }
128
+
129
+ expect(page.all).to eq(@instance_values.find_all(&:even?))
130
+ end
131
+ end
132
+
133
+ describe '#postprocessing_map' do
134
+ it 'sets ruby map on copy' do
135
+ page = client.paginate(@refs_match)
136
+
137
+ expect(get_postprocessing(page.postprocessing_map(&:id))).to be_a(Proc)
138
+ expect(get_postprocessing(page)).to be_nil
139
+ end
140
+ end
141
+ end
142
+
143
+ describe '#load!' do
144
+ it 'explicitly loads page' do
145
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
146
+ expected = [@instance_refs[1]]
147
+
148
+ expect(page.instance_variable_get(:@data)).to be_nil
149
+ page.load!
150
+ expect(page.instance_variable_get(:@data)).to eq(expected)
151
+ end
152
+
153
+ it 'returns true when page was loaded' do
154
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
155
+
156
+ expect(page.instance_variable_get(:@populated)).to be(false)
157
+ expect(page.load!).to be(true)
158
+ expect(page.instance_variable_get(:@populated)).to be(true)
159
+ end
160
+
161
+ it 'returns false when page not loaded' do
162
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
163
+
164
+ page.load!
165
+ expect(page.instance_variable_get(:@populated)).to be(true)
166
+ expect(page.load!).to be(false)
167
+ end
168
+ end
169
+
170
+ describe '#data' do
171
+ it 'lazily loads page' do
172
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
173
+ expected = [@instance_refs[1]]
174
+
175
+ expect(page.instance_variable_get(:@data)).to be_nil
176
+ expect(page.data).to eq(expected)
177
+ expect(page.instance_variable_get(:@data)).to eq(expected)
178
+ end
179
+ end
180
+
181
+ describe '#before' do
182
+ it 'lazily loads page' do
183
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
184
+ expected = [@instance_refs[1]]
185
+
186
+ expect(page.instance_variable_get(:@before)).to be_nil
187
+ expect(page.before).to eq(expected)
188
+ expect(page.instance_variable_get(:@before)).to eq(expected)
189
+ end
190
+ end
191
+
192
+ describe '#after' do
193
+ it 'lazily loads page' do
194
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
195
+ expected = [@instance_refs[2]]
196
+
197
+ expect(page.instance_variable_get(:@after)).to be_nil
198
+ expect(page.after).to eq(expected)
199
+ expect(page.instance_variable_get(:@after)).to eq(expected)
200
+ end
201
+ end
202
+
203
+ describe '#page_after' do
204
+ it 'returns the page after' do
205
+ page = client.paginate(@refs_match, size: 1, after: 0)
206
+
207
+ @instance_refs.drop(1).each do |ref|
208
+ page = page.page_after
209
+ expect(page.data.first).to eq(ref)
210
+ end
211
+ end
212
+
213
+ it 'returns nil on last page' do
214
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs.last)
215
+
216
+ expect(page.data.first).to eq(@instance_refs.last)
217
+ expect(page.page_after).to be_nil
218
+ end
219
+
220
+ it 'is not affected by lazy loading' do
221
+ page = client.paginate(@refs_match, size: 1, after: 0)
222
+
223
+ expect(page.data.first).to eq(@instance_refs[0])
224
+ expect(page.page_after.data.first).to eq(@instance_refs[1])
225
+
226
+ page = client.paginate(@refs_match, size: 1, after: 0)
227
+
228
+ expect(page.page_after.data.first).to eq(@instance_refs[1])
229
+ end
230
+ end
231
+
232
+ describe '#page_before' do
233
+ it 'returns the page before' do
234
+ page = client.paginate(@refs_match, size: 1, before: nil)
235
+
236
+ @instance_refs.reverse.drop(1).each do |ref|
237
+ page = page.page_before
238
+ expect(page.data.first).to eq(ref)
239
+ end
240
+ end
241
+
242
+ it 'returns nil on last page' do
243
+ page = client.paginate(@refs_match, size: 1, before: @instance_refs.first)
244
+
245
+ expect(page.page_before).to be_nil
246
+ end
247
+
248
+ it 'is not affected by lazy loading' do
249
+ page = client.paginate(@refs_match, size: 1, before: nil)
250
+
251
+ expect(page.data.first).to eq(@instance_refs[-1])
252
+ expect(page.page_before.data.first).to eq(@instance_refs[-2])
253
+
254
+ page = client.paginate(@refs_match, size: 1, before: nil)
255
+
256
+ expect(page.page_before.data.first).to eq(@instance_refs[-2])
257
+ end
258
+ end
259
+
260
+ it 'pages both directions' do
261
+ page = client.paginate(@refs_match, size: 1, after: 0)
262
+ expect(page.data.first).to eq(@instance_refs[0])
263
+
264
+ page = page.page_after
265
+ expect(page.data.first).to eq(@instance_refs[1])
266
+
267
+ page = page.page_before
268
+ expect(page.data.first).to eq(@instance_refs[0])
269
+ end
270
+
271
+ describe '#each' do
272
+ it 'iterates the set in the after direction' do
273
+ page = client.paginate(@refs_match, size: 1)
274
+ refs = @instance_refs.collect { |ref| [ref] }
275
+
276
+ expect(page.each.collect { |ref| ref }).to eq(refs)
277
+ end
278
+
279
+ it 'is not affected by lazy loading' do
280
+ page = client.paginate(@refs_match, size: 1)
281
+ refs = @instance_refs.collect { |ref| [ref] }
282
+
283
+ expect(page.each.collect { |ref| ref }).to eq(refs)
284
+
285
+ page = client.paginate(@refs_match, size: 1)
286
+ refs = @instance_refs.collect { |ref| [ref] }
287
+
288
+ expect(page.data).to eq([@instance_refs.first])
289
+ expect(page.each.collect { |ref| ref }).to eq(refs)
290
+ end
291
+
292
+ context 'with fauna map' do
293
+ it 'iterates the set using the fauna map' do
294
+ page = client.paginate(@refs_match, size: 1) { |ref| get(ref) }
295
+ instances = @instances.collect { |inst| [inst] }
296
+
297
+ expect(page.each.collect { |inst| inst }).to eq(instances)
298
+ end
299
+
300
+ it 'chains multiple collection functions' do
301
+ page = client.paginate(@refs_match, size: 1)
302
+ # Map ref to value
303
+ page = page.map { |ref| select(['data', 'value'], get(ref)) }
304
+ # Filter out odd numbers
305
+ page = page.filter { |value| equals(modulo(value, 2), 0) }
306
+ # Map to double the value
307
+ page = page.map { |value| multiply(value, 2) }
308
+
309
+ # We are using the index on refs, not value, so we need to expect the values to be sorted by instance ref
310
+ expected = @instances.collect { |inst| inst[:data][:value] }.find_all(&:even?).collect { |v| v * 2 }
311
+
312
+ expect(page.all).to eq(expected)
313
+ end
314
+ end
315
+
316
+ context 'with ruby map' do
317
+ it 'iterates the set using the ruby map' do
318
+ page = client.paginate(@refs_match, size: 1).postprocessing_map(&:id)
319
+ ids = @instance_refs.collect { |ref| [ref.id] }
320
+
321
+ expect(page.each.collect { |id| id }).to eq(ids)
322
+ end
323
+ end
324
+ end
325
+
326
+ describe '#reverse_each' do
327
+ it 'iterates the set in the before direction' do
328
+ page = client.paginate(@refs_match, size: 1, before: nil)
329
+ refs = @instance_refs.reverse.collect { |ref| [ref] }
330
+
331
+ expect(page.reverse_each.collect { |ref| ref }).to eq(refs)
332
+ end
333
+ end
334
+
335
+ describe '#all' do
336
+ it 'returns full contents of the set' do
337
+ page = client.paginate(@refs_match, size: 1)
338
+
339
+ expect(page.all).to eq(@instance_refs)
340
+ end
341
+ end
342
+
343
+ describe '#foreach!' do
344
+ before(:each) do
345
+ @apply_refs = client.query { (1..3).collect { |x| select([:ref], create(@foreach_class, data: { value: x })) } }
346
+ end
347
+
348
+ it 'applies foreach to set' do
349
+ # Sanity
350
+ expect(client.query { map(@apply_refs) { |ref| exists ref } }).to eq(@apply_refs.collect { true })
351
+
352
+ client.paginate(@foreach_match, size: 1).foreach! { |ref| delete ref }
353
+
354
+ expect(client.query { map(@apply_refs) { |ref| exists ref } }).to eq(@apply_refs.collect { false })
355
+ end
356
+ end
357
+ end