yax-fauna 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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